gint
gst_dtls_connection_send (GstDtlsConnection * self, gpointer data, gint len)
{
  int ret = 0;

  g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);

  g_return_val_if_fail (self->priv->ssl, 0);
  g_return_val_if_fail (self->priv->bio, 0);

  GST_TRACE_OBJECT (self, "locking @ send");
  g_mutex_lock (&self->priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ send");

  if (SSL_is_init_finished (self->priv->ssl)) {
    ret = SSL_write (self->priv->ssl, data, len);
    GST_DEBUG_OBJECT (self, "data sent: input was %d B, output is %d B", len,
        ret);
  } else {
    GST_WARNING_OBJECT (self,
        "tried to send data before handshake was complete");
    ret = 0;
  }

  GST_TRACE_OBJECT (self, "unlocking @ send");
  g_mutex_unlock (&self->priv->mutex);

  return ret;
}
static void
on_key_received (GstDtlsConnection * connection, gpointer key, guint cipher,
    guint auth, GstDtlsEnc * self)
{
  gpointer key_dup;
  gchar *key_str;

  g_return_if_fail (GST_IS_DTLS_ENC (self));
  g_return_if_fail (GST_IS_DTLS_CONNECTION (connection));

  self->srtp_cipher = cipher;
  self->srtp_auth = auth;

  key_dup = g_memdup (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);

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

  self->encoder_key =
      gst_buffer_new_wrapped (key_dup, GST_DTLS_SRTP_MASTER_KEY_LENGTH);

  key_str = g_base64_encode (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
  GST_INFO_OBJECT (self, "received key: %s", key_str);
  g_free (key_str);

  g_signal_emit (self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
}
static void
gst_dtls_connection_check_timeout_locked (GstDtlsConnection * self)
{
  GstDtlsConnectionPrivate *priv;
  struct timeval timeout;
  gint64 end_time, wait_time;

  g_return_if_fail (GST_IS_DTLS_CONNECTION (self));

  priv = self->priv;

  if (DTLSv1_get_timeout (priv->ssl, &timeout)) {
    wait_time = timeout.tv_sec * G_USEC_PER_SEC + timeout.tv_usec;

    GST_DEBUG_OBJECT (self, "waiting for %" G_GINT64_FORMAT " usec", wait_time);
    if (wait_time) {
      GstClock *system_clock = gst_system_clock_obtain ();
      GstClockID clock_id;
#ifndef G_DISABLE_ASSERT
      GstClockReturn clock_return;
#endif

      end_time = gst_clock_get_time (system_clock) + wait_time * GST_USECOND;

      clock_id = gst_clock_new_single_shot_id (system_clock, end_time);
#ifndef G_DISABLE_ASSERT
      clock_return =
#else
      (void)
#endif
          gst_clock_id_wait_async (clock_id, schedule_timeout_handling,
          g_object_ref (self), (GDestroyNotify) g_object_unref);
      g_assert (clock_return == GST_CLOCK_OK);
      gst_clock_id_unref (clock_id);
      gst_object_unref (system_clock);
    } else {
      if (self->priv->is_alive && !self->priv->timeout_pending) {
        self->priv->timeout_pending = TRUE;
        GST_TRACE_OBJECT (self, "Schedule timeout now");

        g_thread_pool_push (self->priv->thread_pool, GINT_TO_POINTER (0xc0ffee),
            NULL);
      }
    }
  } else {
    GST_DEBUG_OBJECT (self, "no timeout set");
  }
}
void
gst_dtls_connection_check_timeout (GstDtlsConnection * self)
{
  GstDtlsConnectionPrivate *priv;

  g_return_if_fail (GST_IS_DTLS_CONNECTION (self));

  priv = self->priv;

  GST_TRACE_OBJECT (self, "locking @ start_timeout");
  g_mutex_lock (&priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ start_timeout");
  gst_dtls_connection_check_timeout_locked (self);
  g_mutex_unlock (&priv->mutex);
  GST_TRACE_OBJECT (self, "unlocking @ start_timeout");
}
static int
openssl_verify_callback (int preverify_ok, X509_STORE_CTX * x509_ctx)
{
  GstDtlsConnection *self;
  SSL *ssl;
  BIO *bio;
  gchar *pem = NULL;
  gboolean accepted = FALSE;

  ssl =
      X509_STORE_CTX_get_ex_data (x509_ctx,
      SSL_get_ex_data_X509_STORE_CTX_idx ());
  self = SSL_get_ex_data (ssl, connection_ex_index);
  g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), FALSE);

  pem = _gst_dtls_x509_to_pem (X509_STORE_CTX_get0_cert (x509_ctx));

  if (!pem) {
    GST_WARNING_OBJECT (self,
        "failed to convert received certificate to pem format");
  } else {
    bio = BIO_new (BIO_s_mem ());
    if (bio) {
      gchar buffer[2048];
      gint len;

      len =
          X509_NAME_print_ex (bio,
          X509_get_subject_name (X509_STORE_CTX_get0_cert (x509_ctx)), 1,
          XN_FLAG_MULTILINE);
      BIO_read (bio, buffer, len);
      buffer[len] = '\0';
      GST_DEBUG_OBJECT (self, "Peer certificate received:\n%s", buffer);
      BIO_free (bio);
    } else {
      GST_DEBUG_OBJECT (self, "failed to create certificate print membio");
    }

    g_signal_emit (self, signals[SIGNAL_ON_PEER_CERTIFICATE], 0, pem,
        &accepted);
    g_free (pem);
  }

  return accepted;
}
gint
gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gint len)
{
  GstDtlsConnectionPrivate *priv;
  gint result;

  g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
  g_return_val_if_fail (self->priv->ssl, 0);
  g_return_val_if_fail (self->priv->bio, 0);

  priv = self->priv;

  GST_TRACE_OBJECT (self, "locking @ process");
  g_mutex_lock (&priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ process");

  g_warn_if_fail (!priv->bio_buffer);

  priv->bio_buffer = data;
  priv->bio_buffer_len = len;
  priv->bio_buffer_offset = 0;

  log_state (self, "process start");

  if (SSL_want_write (priv->ssl)) {
    openssl_poll (self);
    log_state (self, "process want write, after poll");
  }

  result = SSL_read (priv->ssl, data, len);

  log_state (self, "process after read");

  openssl_poll (self);

  log_state (self, "process after poll");

  GST_DEBUG_OBJECT (self, "read result: %d", result);

  GST_TRACE_OBJECT (self, "unlocking @ process");
  g_mutex_unlock (&priv->mutex);

  return result;
}
void
gst_dtls_connection_set_send_callback (GstDtlsConnection * self,
    GClosure * closure)
{
  g_return_if_fail (GST_IS_DTLS_CONNECTION (self));

  GST_TRACE_OBJECT (self, "locking @ set_send_callback");
  g_mutex_lock (&self->priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ set_send_callback");

  if (self->priv->send_closure) {
    g_closure_unref (self->priv->send_closure);
    self->priv->send_closure = NULL;
  }
  self->priv->send_closure = closure;

  if (closure && G_CLOSURE_NEEDS_MARSHAL (closure)) {
    g_closure_set_marshal (closure, g_cclosure_marshal_generic);
  }

  GST_TRACE_OBJECT (self, "unlocking @ set_send_callback");
  g_mutex_unlock (&self->priv->mutex);
}
void
gst_dtls_connection_close (GstDtlsConnection * self)
{
  g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
  g_return_if_fail (self->priv->ssl);
  g_return_if_fail (self->priv->bio);

  GST_DEBUG_OBJECT (self, "closing connection");

  GST_TRACE_OBJECT (self, "locking @ close");
  g_mutex_lock (&self->priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ close");

  if (self->priv->is_alive) {
    self->priv->is_alive = FALSE;
    g_cond_signal (&self->priv->condition);
  }

  GST_TRACE_OBJECT (self, "unlocking @ close");
  g_mutex_unlock (&self->priv->mutex);

  GST_DEBUG_OBJECT (self, "closed connection");
}
void
gst_dtls_connection_stop (GstDtlsConnection * self)
{
  g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
  g_return_if_fail (self->priv->ssl);
  g_return_if_fail (self->priv->bio);

  GST_DEBUG_OBJECT (self, "stopping connection");

  GST_TRACE_OBJECT (self, "locking @ stop");
  g_mutex_lock (&self->priv->mutex);
  GST_TRACE_OBJECT (self, "locked @ stop");

  self->priv->is_alive = FALSE;
  GST_TRACE_OBJECT (self, "signaling @ stop");
  g_cond_signal (&self->priv->condition);
  GST_TRACE_OBJECT (self, "signaled @ stop");

  GST_TRACE_OBJECT (self, "unlocking @ stop");
  g_mutex_unlock (&self->priv->mutex);

  GST_DEBUG_OBJECT (self, "stopped connection");
}