• 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 <gst/gst.h>
31 
32 #include "gstdtlsconnection.h"
33 
34 #include "gstdtlsagent.h"
35 #include "gstdtlscertificate.h"
36 
37 #ifdef __APPLE__
38 # define __AVAILABILITYMACROS__
39 # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
40 #endif
41 
42 #include <openssl/err.h>
43 #include <openssl/ssl.h>
44 
45 #ifdef G_OS_WIN32
46 #include <winsock2.h>
47 #else
48 #include <string.h>
49 #include <errno.h>
50 #endif
51 
52 GST_DEBUG_CATEGORY_STATIC (gst_dtls_connection_debug);
53 #define GST_CAT_DEFAULT gst_dtls_connection_debug
54 
55 #define SRTP_KEY_LEN 16
56 #define SRTP_SALT_LEN 14
57 
58 enum
59 {
60   SIGNAL_ON_ENCODER_KEY,
61   SIGNAL_ON_DECODER_KEY,
62   SIGNAL_ON_PEER_CERTIFICATE,
63   NUM_SIGNALS
64 };
65 
66 static guint signals[NUM_SIGNALS];
67 
68 enum
69 {
70   PROP_0,
71   PROP_AGENT,
72   PROP_CONNECTION_STATE,
73   NUM_PROPERTIES
74 };
75 
76 static GParamSpec *properties[NUM_PROPERTIES];
77 
78 static int connection_ex_index;
79 
80 static void handle_timeout (gpointer data, gpointer user_data);
81 
82 struct _GstDtlsConnectionPrivate
83 {
84   SSL *ssl;
85   BIO *bio;
86 
87   gboolean is_client;
88   gboolean is_alive;
89   gboolean keys_exported;
90 
91   GstDtlsConnectionState connection_state;
92   gboolean sent_close_notify;
93   gboolean received_close_notify;
94 
95   GMutex mutex;
96   GCond condition;
97   gpointer bio_buffer;
98   gint bio_buffer_len;
99   gint bio_buffer_offset;
100 
101   GstDtlsConnectionSendCallback send_callback;
102   gpointer send_callback_user_data;
103   GDestroyNotify send_callback_destroy_notify;
104   GstFlowReturn syscall_flow_return;
105 
106   gboolean timeout_pending;
107   GThreadPool *thread_pool;
108 };
109 
110 G_DEFINE_TYPE_WITH_CODE (GstDtlsConnection, gst_dtls_connection, G_TYPE_OBJECT,
111     G_ADD_PRIVATE (GstDtlsConnection)
112     GST_DEBUG_CATEGORY_INIT (gst_dtls_connection_debug, "dtlsconnection", 0,
113         "DTLS Connection"));
114 
115 static void gst_dtls_connection_finalize (GObject * gobject);
116 static void gst_dtls_connection_set_property (GObject *, guint prop_id,
117     const GValue *, GParamSpec *);
118 static void gst_dtls_connection_get_property (GObject *, guint prop_id,
119     GValue *, GParamSpec *);
120 
121 static void log_state (GstDtlsConnection *, const gchar * str);
122 static gboolean export_srtp_keys (GstDtlsConnection *, GError ** err);
123 static GstFlowReturn openssl_poll (GstDtlsConnection *, gboolean * notify_state,
124     GError ** err);
125 static GstFlowReturn handle_error (GstDtlsConnection * self, int ret,
126     GstResourceError error_type, gboolean * notify_state, GError ** err);
127 static int openssl_verify_callback (int preverify_ok,
128     X509_STORE_CTX * x509_ctx);
129 
130 static BIO_METHOD *BIO_s_gst_dtls_connection (void);
131 static int bio_method_write (BIO *, const char *data, int size);
132 static int bio_method_read (BIO *, char *out_buffer, int size);
133 static long bio_method_ctrl (BIO *, int cmd, long arg1, void *arg2);
134 static int bio_method_new (BIO *);
135 static int bio_method_free (BIO *);
136 
137 static void
gst_dtls_connection_class_init(GstDtlsConnectionClass * klass)138 gst_dtls_connection_class_init (GstDtlsConnectionClass * klass)
139 {
140   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
141 
142   gobject_class->set_property = gst_dtls_connection_set_property;
143   gobject_class->get_property = gst_dtls_connection_get_property;
144 
145   connection_ex_index =
146       SSL_get_ex_new_index (0, (gpointer) "gstdtlsagent connection index", NULL,
147       NULL, NULL);
148 
149   signals[SIGNAL_ON_DECODER_KEY] =
150       g_signal_new ("on-decoder-key", G_TYPE_FROM_CLASS (klass),
151       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
152       G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT);
153 
154   signals[SIGNAL_ON_ENCODER_KEY] =
155       g_signal_new ("on-encoder-key", G_TYPE_FROM_CLASS (klass),
156       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
157       G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT);
158 
159   signals[SIGNAL_ON_PEER_CERTIFICATE] =
160       g_signal_new ("on-peer-certificate", G_TYPE_FROM_CLASS (klass),
161       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
162 
163   properties[PROP_AGENT] =
164       g_param_spec_object ("agent",
165       "DTLS Agent",
166       "Agent to use in creation of the connection",
167       GST_TYPE_DTLS_AGENT,
168       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
169 
170   properties[PROP_CONNECTION_STATE] =
171       g_param_spec_enum ("connection-state",
172       "Connection State",
173       "Current connection state",
174       GST_DTLS_TYPE_CONNECTION_STATE,
175       GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
176 
177   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
178 
179   _gst_dtls_init_openssl ();
180 
181   gobject_class->finalize = gst_dtls_connection_finalize;
182 }
183 
184 static void
gst_dtls_connection_init(GstDtlsConnection * self)185 gst_dtls_connection_init (GstDtlsConnection * self)
186 {
187   GstDtlsConnectionPrivate *priv;
188 
189   self->priv = priv = gst_dtls_connection_get_instance_private (self);
190 
191   priv->ssl = NULL;
192   priv->bio = NULL;
193 
194   priv->is_client = FALSE;
195   priv->is_alive = TRUE;
196   priv->keys_exported = FALSE;
197 
198   priv->bio_buffer = NULL;
199   priv->bio_buffer_len = 0;
200   priv->bio_buffer_offset = 0;
201 
202   g_mutex_init (&priv->mutex);
203   g_cond_init (&priv->condition);
204 
205   /* Thread pool for handling timeouts, we only need one thread for that
206    * really and share threads with all other thread pools around there as
207    * this is not going to happen very often */
208   priv->thread_pool = g_thread_pool_new (handle_timeout, self, 1, FALSE, NULL);
209   g_assert (priv->thread_pool);
210   priv->timeout_pending = FALSE;
211 }
212 
213 static void
gst_dtls_connection_finalize(GObject * gobject)214 gst_dtls_connection_finalize (GObject * gobject)
215 {
216   GstDtlsConnection *self = GST_DTLS_CONNECTION (gobject);
217   GstDtlsConnectionPrivate *priv = self->priv;
218 
219   g_thread_pool_free (priv->thread_pool, TRUE, TRUE);
220   priv->thread_pool = NULL;
221 
222   SSL_free (priv->ssl);
223   priv->ssl = NULL;
224 
225   if (priv->send_callback_destroy_notify)
226     priv->send_callback_destroy_notify (priv->send_callback_user_data);
227 
228   g_mutex_clear (&priv->mutex);
229   g_cond_clear (&priv->condition);
230 
231   GST_DEBUG_OBJECT (self, "finalized");
232 
233   G_OBJECT_CLASS (gst_dtls_connection_parent_class)->finalize (gobject);
234 }
235 
236 #if OPENSSL_VERSION_NUMBER < 0x10100001L
237 static void
BIO_set_data(BIO * bio,void * ptr)238 BIO_set_data (BIO * bio, void *ptr)
239 {
240   bio->ptr = ptr;
241 }
242 
243 static void *
BIO_get_data(BIO * bio)244 BIO_get_data (BIO * bio)
245 {
246   return bio->ptr;
247 }
248 
249 static void
BIO_set_shutdown(BIO * bio,int shutdown)250 BIO_set_shutdown (BIO * bio, int shutdown)
251 {
252   bio->shutdown = shutdown;
253 }
254 
255 static void
BIO_set_init(BIO * bio,int init)256 BIO_set_init (BIO * bio, int init)
257 {
258   bio->init = init;
259 }
260 
261 static X509 *
X509_STORE_CTX_get0_cert(X509_STORE_CTX * ctx)262 X509_STORE_CTX_get0_cert (X509_STORE_CTX * ctx)
263 {
264   return ctx->cert;
265 }
266 #endif
267 
268 static void
gst_dtls_connection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)269 gst_dtls_connection_set_property (GObject * object, guint prop_id,
270     const GValue * value, GParamSpec * pspec)
271 {
272   GstDtlsConnection *self = GST_DTLS_CONNECTION (object);
273   GstDtlsAgent *agent;
274   GstDtlsConnectionPrivate *priv = self->priv;
275   SSL_CTX *ssl_context;
276 
277   switch (prop_id) {
278     case PROP_AGENT:
279       g_return_if_fail (!priv->ssl);
280       agent = GST_DTLS_AGENT (g_value_get_object (value));
281       g_return_if_fail (GST_IS_DTLS_AGENT (agent));
282 
283       ssl_context = _gst_dtls_agent_peek_context (agent);
284 
285       priv->ssl = SSL_new (ssl_context);
286       g_return_if_fail (priv->ssl);
287 
288       priv->bio = BIO_new (BIO_s_gst_dtls_connection ());
289       g_return_if_fail (priv->bio);
290 
291       BIO_set_data (priv->bio, self);
292       SSL_set_bio (priv->ssl, priv->bio, priv->bio);
293 
294       SSL_set_verify (priv->ssl,
295           SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
296           openssl_verify_callback);
297       SSL_set_ex_data (priv->ssl, connection_ex_index, self);
298 
299       log_state (self, "connection created");
300       break;
301     default:
302       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
303   }
304 }
305 
306 static void
gst_dtls_connection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)307 gst_dtls_connection_get_property (GObject * object, guint prop_id,
308     GValue * value, GParamSpec * pspec)
309 {
310   GstDtlsConnection *self = GST_DTLS_CONNECTION (object);
311   GstDtlsConnectionPrivate *priv = self->priv;
312 
313   switch (prop_id) {
314     case PROP_CONNECTION_STATE:
315       g_mutex_lock (&priv->mutex);
316       g_value_set_enum (value, priv->connection_state);
317       g_mutex_unlock (&priv->mutex);
318       break;
319     default:
320       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
321   }
322 }
323 
324 gboolean
gst_dtls_connection_start(GstDtlsConnection * self,gboolean is_client,GError ** err)325 gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
326     GError ** err)
327 {
328   GstDtlsConnectionPrivate *priv;
329   gboolean ret;
330   gboolean notify_state = FALSE;
331 
332   priv = self->priv;
333 
334   g_return_val_if_fail (priv->send_callback, FALSE);
335   g_return_val_if_fail (priv->ssl, FALSE);
336   g_return_val_if_fail (priv->bio, FALSE);
337 
338   GST_TRACE_OBJECT (self, "locking @ start");
339   g_mutex_lock (&priv->mutex);
340   GST_TRACE_OBJECT (self, "locked @ start");
341 
342   priv->is_alive = TRUE;
343   priv->bio_buffer = NULL;
344   priv->bio_buffer_len = 0;
345   priv->bio_buffer_offset = 0;
346   priv->keys_exported = FALSE;
347 
348   priv->sent_close_notify = FALSE;
349   priv->received_close_notify = FALSE;
350 
351   /* Client immediately starts connecting, the server waits for a client to
352    * start the handshake process */
353   priv->is_client = is_client;
354   if (priv->is_client) {
355     priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTING;
356     notify_state = TRUE;
357     SSL_set_connect_state (priv->ssl);
358   } else {
359     if (priv->connection_state != GST_DTLS_CONNECTION_STATE_NEW) {
360       priv->connection_state = GST_DTLS_CONNECTION_STATE_NEW;
361       notify_state = TRUE;
362     }
363     SSL_set_accept_state (priv->ssl);
364   }
365   log_state (self, "initial state set");
366 
367   ret = openssl_poll (self, &notify_state, err);
368   if (ret == GST_FLOW_EOS && err) {
369     *err =
370         g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_WRITE,
371         "Connection closed");
372   }
373 
374   log_state (self, "first poll done");
375 
376   GST_TRACE_OBJECT (self, "unlocking @ start");
377   g_mutex_unlock (&priv->mutex);
378 
379   if (notify_state) {
380     g_object_notify_by_pspec (G_OBJECT (self),
381         properties[PROP_CONNECTION_STATE]);
382   }
383 
384   return ret == GST_FLOW_OK;
385 }
386 
387 static void
handle_timeout(gpointer data,gpointer user_data)388 handle_timeout (gpointer data, gpointer user_data)
389 {
390   GstDtlsConnection *self = user_data;
391   GstDtlsConnectionPrivate *priv;
392   gint ret;
393   gboolean notify_state = FALSE;
394 
395   priv = self->priv;
396 
397   g_mutex_lock (&priv->mutex);
398   priv->timeout_pending = FALSE;
399   if (priv->is_alive) {
400     ret = DTLSv1_handle_timeout (priv->ssl);
401 
402     GST_DEBUG_OBJECT (self, "handle timeout returned %d, is_alive: %d", ret,
403         priv->is_alive);
404 
405     if (ret < 0) {
406       GST_WARNING_OBJECT (self, "handling timeout failed");
407     } else if (ret > 0) {
408       log_state (self, "handling timeout before poll");
409       openssl_poll (self, &notify_state, NULL);
410       log_state (self, "handling timeout after poll");
411     }
412   }
413   g_mutex_unlock (&priv->mutex);
414 
415   if (notify_state) {
416     g_object_notify_by_pspec (G_OBJECT (self),
417         properties[PROP_CONNECTION_STATE]);
418   }
419 }
420 
421 static gboolean
schedule_timeout_handling(GstClock * clock,GstClockTime time,GstClockID id,gpointer user_data)422 schedule_timeout_handling (GstClock * clock, GstClockTime time, GstClockID id,
423     gpointer user_data)
424 {
425   GstDtlsConnection *self = user_data;
426 
427   g_mutex_lock (&self->priv->mutex);
428   if (self->priv->is_alive && !self->priv->timeout_pending) {
429     self->priv->timeout_pending = TRUE;
430 
431     GST_TRACE_OBJECT (self, "Schedule timeout now");
432     g_thread_pool_push (self->priv->thread_pool, GINT_TO_POINTER (0xc0ffee),
433         NULL);
434   }
435   g_mutex_unlock (&self->priv->mutex);
436 
437   return TRUE;
438 }
439 
440 static void
gst_dtls_connection_check_timeout_locked(GstDtlsConnection * self)441 gst_dtls_connection_check_timeout_locked (GstDtlsConnection * self)
442 {
443   GstDtlsConnectionPrivate *priv;
444   struct timeval timeout;
445   gint64 end_time, wait_time;
446 
447   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
448 
449   priv = self->priv;
450 
451   if (DTLSv1_get_timeout (priv->ssl, &timeout)) {
452     wait_time = timeout.tv_sec * G_USEC_PER_SEC + timeout.tv_usec;
453 
454     GST_DEBUG_OBJECT (self, "waiting for %" G_GINT64_FORMAT " usec", wait_time);
455     if (wait_time) {
456       GstClock *system_clock = gst_system_clock_obtain ();
457       GstClockID clock_id;
458 #ifndef G_DISABLE_ASSERT
459       GstClockReturn clock_return;
460 #endif
461 
462       end_time = gst_clock_get_time (system_clock) + wait_time * GST_USECOND;
463 
464       clock_id = gst_clock_new_single_shot_id (system_clock, end_time);
465 #ifndef G_DISABLE_ASSERT
466       clock_return =
467 #else
468       (void)
469 #endif
470           gst_clock_id_wait_async (clock_id, schedule_timeout_handling,
471           g_object_ref (self), (GDestroyNotify) g_object_unref);
472       g_assert (clock_return == GST_CLOCK_OK);
473       gst_clock_id_unref (clock_id);
474       gst_object_unref (system_clock);
475     } else {
476       if (self->priv->is_alive && !self->priv->timeout_pending) {
477         self->priv->timeout_pending = TRUE;
478         GST_TRACE_OBJECT (self, "Schedule timeout now");
479 
480         g_thread_pool_push (self->priv->thread_pool, GINT_TO_POINTER (0xc0ffee),
481             NULL);
482       }
483     }
484   } else {
485     GST_DEBUG_OBJECT (self, "no timeout set");
486   }
487 }
488 
489 void
gst_dtls_connection_check_timeout(GstDtlsConnection * self)490 gst_dtls_connection_check_timeout (GstDtlsConnection * self)
491 {
492   GstDtlsConnectionPrivate *priv;
493 
494   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
495 
496   priv = self->priv;
497 
498   GST_TRACE_OBJECT (self, "locking @ start_timeout");
499   g_mutex_lock (&priv->mutex);
500   GST_TRACE_OBJECT (self, "locked @ start_timeout");
501   gst_dtls_connection_check_timeout_locked (self);
502   g_mutex_unlock (&priv->mutex);
503   GST_TRACE_OBJECT (self, "unlocking @ start_timeout");
504 }
505 
506 void
gst_dtls_connection_stop(GstDtlsConnection * self)507 gst_dtls_connection_stop (GstDtlsConnection * self)
508 {
509   gboolean notify_state = FALSE;
510 
511   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
512   g_return_if_fail (self->priv->ssl);
513   g_return_if_fail (self->priv->bio);
514 
515   GST_DEBUG_OBJECT (self, "stopping connection");
516 
517   GST_TRACE_OBJECT (self, "locking @ stop");
518   g_mutex_lock (&self->priv->mutex);
519   GST_TRACE_OBJECT (self, "locked @ stop");
520 
521   self->priv->is_alive = FALSE;
522   if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
523       && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
524     self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
525     notify_state = TRUE;
526   }
527   GST_TRACE_OBJECT (self, "signaling @ stop");
528   g_cond_signal (&self->priv->condition);
529   GST_TRACE_OBJECT (self, "signaled @ stop");
530 
531   GST_TRACE_OBJECT (self, "unlocking @ stop");
532   g_mutex_unlock (&self->priv->mutex);
533 
534   GST_DEBUG_OBJECT (self, "stopped connection");
535 
536   if (notify_state) {
537     g_object_notify_by_pspec (G_OBJECT (self),
538         properties[PROP_CONNECTION_STATE]);
539   }
540 }
541 
542 void
gst_dtls_connection_close(GstDtlsConnection * self)543 gst_dtls_connection_close (GstDtlsConnection * self)
544 {
545   gboolean notify_state = FALSE;
546 
547   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
548   g_return_if_fail (self->priv->ssl);
549   g_return_if_fail (self->priv->bio);
550 
551   GST_DEBUG_OBJECT (self, "closing connection");
552 
553   GST_TRACE_OBJECT (self, "locking @ close");
554   g_mutex_lock (&self->priv->mutex);
555   GST_TRACE_OBJECT (self, "locked @ close");
556 
557   if (self->priv->is_alive) {
558     self->priv->is_alive = FALSE;
559     g_cond_signal (&self->priv->condition);
560   }
561 
562   if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
563       && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
564     self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
565     notify_state = TRUE;
566   }
567 
568   GST_TRACE_OBJECT (self, "unlocking @ close");
569   g_mutex_unlock (&self->priv->mutex);
570 
571   GST_DEBUG_OBJECT (self, "closed connection");
572 
573   if (notify_state) {
574     g_object_notify_by_pspec (G_OBJECT (self),
575         properties[PROP_CONNECTION_STATE]);
576   }
577 }
578 
579 void
gst_dtls_connection_set_send_callback(GstDtlsConnection * self,GstDtlsConnectionSendCallback callback,gpointer user_data,GDestroyNotify destroy_notify)580 gst_dtls_connection_set_send_callback (GstDtlsConnection * self,
581     GstDtlsConnectionSendCallback callback, gpointer user_data,
582     GDestroyNotify destroy_notify)
583 {
584   GstDtlsConnectionPrivate *priv;
585 
586   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
587 
588   priv = self->priv;
589 
590   GST_TRACE_OBJECT (self, "locking @ set_send_callback");
591   g_mutex_lock (&priv->mutex);
592   GST_TRACE_OBJECT (self, "locked @ set_send_callback");
593 
594   if (priv->send_callback_destroy_notify)
595     priv->send_callback_destroy_notify (priv->send_callback_user_data);
596   priv->send_callback = callback;
597   priv->send_callback_user_data = user_data;
598   priv->send_callback_destroy_notify = destroy_notify;
599 
600   GST_TRACE_OBJECT (self, "unlocking @ set_send_callback");
601   g_mutex_unlock (&priv->mutex);
602 }
603 
604 void
gst_dtls_connection_set_flow_return(GstDtlsConnection * self,GstFlowReturn flow_ret)605 gst_dtls_connection_set_flow_return (GstDtlsConnection * self,
606     GstFlowReturn flow_ret)
607 {
608   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
609   self->priv->syscall_flow_return = flow_ret;
610 }
611 
612 GstFlowReturn
gst_dtls_connection_process(GstDtlsConnection * self,gpointer data,gsize len,gsize * written,GError ** err)613 gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
614     gsize * written, GError ** err)
615 {
616   GstFlowReturn flow_ret = GST_FLOW_OK;
617   GstDtlsConnectionPrivate *priv;
618   int ret;
619   gboolean notify_state = FALSE;
620 
621   g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
622   g_return_val_if_fail (self->priv->ssl, 0);
623   g_return_val_if_fail (self->priv->bio, 0);
624 
625   priv = self->priv;
626 
627   GST_TRACE_OBJECT (self, "locking @ process");
628   g_mutex_lock (&priv->mutex);
629   GST_TRACE_OBJECT (self, "locked @ process");
630 
631   if (self->priv->received_close_notify
632       || self->priv->connection_state == GST_DTLS_CONNECTION_STATE_CLOSED) {
633     GST_DEBUG_OBJECT (self, "Already received close_notify");
634     g_mutex_unlock (&priv->mutex);
635     return GST_FLOW_EOS;
636   }
637 
638   if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_FAILED) {
639     GST_ERROR_OBJECT (self, "Had a fatal error before");
640     g_mutex_unlock (&priv->mutex);
641     if (err)
642       *err =
643           g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
644           "Had fatal error before");
645     return GST_FLOW_ERROR;
646   }
647 
648   g_warn_if_fail (!priv->bio_buffer);
649 
650   priv->bio_buffer = data;
651   priv->bio_buffer_len = len;
652   priv->bio_buffer_offset = 0;
653 
654   log_state (self, "process start");
655 
656   if (SSL_want_write (priv->ssl)) {
657     flow_ret = openssl_poll (self, &notify_state, err);
658     log_state (self, "process want write, after poll");
659     if (flow_ret != GST_FLOW_OK) {
660       g_mutex_unlock (&priv->mutex);
661       return flow_ret;
662     }
663   }
664 
665   /* If we're a server and were in new state then by receiving the first data
666    * we would start the connection process */
667   if (!priv->is_client) {
668     if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_NEW) {
669       priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTING;
670       notify_state = TRUE;
671     }
672   }
673 
674   ret = SSL_read (priv->ssl, data, len);
675   *written = ret >= 0 ? ret : 0;
676   GST_DEBUG_OBJECT (self, "read result: %d", ret);
677 
678   flow_ret =
679       handle_error (self, ret, GST_RESOURCE_ERROR_READ, &notify_state, err);
680   if (flow_ret == GST_FLOW_EOS) {
681     self->priv->received_close_notify = TRUE;
682     if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
683         && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
684       self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
685       notify_state = TRUE;
686     }
687     /* Notify about the connection being properly closed now if both
688      * sides did so */
689     if (self->priv->sent_close_notify && self->priv->send_callback)
690       self->priv->send_callback (self, NULL, 0, NULL);
691 
692     g_mutex_unlock (&priv->mutex);
693 
694     if (notify_state) {
695       g_object_notify_by_pspec (G_OBJECT (self),
696           properties[PROP_CONNECTION_STATE]);
697     }
698 
699     return flow_ret;
700   } else if (flow_ret != GST_FLOW_OK) {
701     g_mutex_unlock (&priv->mutex);
702 
703     if (notify_state) {
704       g_object_notify_by_pspec (G_OBJECT (self),
705           properties[PROP_CONNECTION_STATE]);
706     }
707 
708     return flow_ret;
709   }
710 
711   log_state (self, "process after read");
712 
713   flow_ret = openssl_poll (self, &notify_state, err);
714 
715   log_state (self, "process after poll");
716 
717   GST_TRACE_OBJECT (self, "unlocking @ process");
718   g_mutex_unlock (&priv->mutex);
719 
720   if (notify_state) {
721     g_object_notify_by_pspec (G_OBJECT (self),
722         properties[PROP_CONNECTION_STATE]);
723   }
724 
725   return flow_ret;
726 }
727 
728 GstFlowReturn
gst_dtls_connection_send(GstDtlsConnection * self,gconstpointer data,gsize len,gsize * written,GError ** err)729 gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
730     gsize len, gsize * written, GError ** err)
731 {
732   GstFlowReturn flow_ret;
733   int ret = 0;
734   gboolean notify_state = FALSE;
735 
736   g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
737 
738   g_return_val_if_fail (self->priv->ssl, 0);
739   g_return_val_if_fail (self->priv->bio, 0);
740 
741   GST_TRACE_OBJECT (self, "locking @ send");
742   g_mutex_lock (&self->priv->mutex);
743   GST_TRACE_OBJECT (self, "locked @ send");
744 
745   if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_FAILED) {
746     GST_ERROR_OBJECT (self, "Had a fatal error before");
747     g_mutex_unlock (&self->priv->mutex);
748     if (err)
749       *err =
750           g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_WRITE,
751           "Had fatal error before");
752     return GST_FLOW_ERROR;
753   }
754 
755   if (self->priv->sent_close_notify) {
756     len = 0;
757     GST_DEBUG_OBJECT (self, "Not sending new data after close_notify");
758   }
759 
760   if (len == 0) {
761     if (written)
762       *written = 0;
763     GST_DEBUG_OBJECT (self, "Sending close_notify");
764     ret = SSL_shutdown (self->priv->ssl);
765     if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED &&
766         self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
767       self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
768       notify_state = TRUE;
769     }
770     if (ret == 1) {
771       GST_LOG_OBJECT (self, "received peer close_notify already");
772       self->priv->received_close_notify = TRUE;
773       flow_ret = GST_FLOW_EOS;
774     } else if (ret == 0) {
775       GST_LOG_OBJECT (self, "did not receive peer close_notify yet");
776       flow_ret = GST_FLOW_OK;
777     } else {
778       flow_ret =
779           handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, &notify_state,
780           err);
781     }
782   } else if (SSL_is_init_finished (self->priv->ssl)) {
783     GST_DEBUG_OBJECT (self, "sending data of %" G_GSIZE_FORMAT " B", len);
784     ret = SSL_write (self->priv->ssl, data, len);
785     if (ret <= 0) {
786       if (written)
787         *written = 0;
788       flow_ret =
789           handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, &notify_state,
790           err);
791     } else {
792       if (written)
793         *written = ret;
794       flow_ret = GST_FLOW_OK;
795     }
796   } else {
797     if (written)
798       *written = ret;
799     GST_WARNING_OBJECT (self,
800         "tried to send data before handshake was complete");
801     if (err)
802       *err =
803           g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_WRITE,
804           "Tried to send data before handshake was complete");
805     flow_ret = GST_FLOW_ERROR;
806   }
807 
808   GST_TRACE_OBJECT (self, "unlocking @ send");
809   g_mutex_unlock (&self->priv->mutex);
810 
811   if (notify_state) {
812     g_object_notify_by_pspec (G_OBJECT (self),
813         properties[PROP_CONNECTION_STATE]);
814   }
815 
816   return flow_ret;
817 }
818 
819 /*
820      ######   #######  ##    ##
821     ##    ## ##     ## ###   ##
822     ##       ##     ## ####  ##
823     ##       ##     ## ## ## ##
824     ##       ##     ## ##  ####
825     ##    ## ##     ## ##   ###
826      ######   #######  ##    ##
827 */
828 
829 static void
log_state(GstDtlsConnection * self,const gchar * str)830 log_state (GstDtlsConnection * self, const gchar * str)
831 {
832   GstDtlsConnectionPrivate *priv = self->priv;
833   guint states = 0;
834 
835   states |= (! !SSL_is_init_finished (priv->ssl) << 0);
836   states |= (! !SSL_in_init (priv->ssl) << 4);
837   states |= (! !SSL_in_before (priv->ssl) << 8);
838   states |= (! !SSL_in_connect_init (priv->ssl) << 12);
839   states |= (! !SSL_in_accept_init (priv->ssl) << 16);
840   states |= (! !SSL_want_write (priv->ssl) << 20);
841   states |= (! !SSL_want_read (priv->ssl) << 24);
842 
843 #if OPENSSL_VERSION_NUMBER < 0x10100001L
844   GST_LOG_OBJECT (self, "%s: role=%s buf=(%d,%p:%d/%d) %x|%x %s",
845       str,
846       priv->is_client ? "client" : "server",
847       pqueue_size (priv->ssl->d1->sent_messages),
848       priv->bio_buffer,
849       priv->bio_buffer_offset,
850       priv->bio_buffer_len,
851       states, SSL_get_state (priv->ssl), SSL_state_string_long (priv->ssl));
852 #else
853   GST_LOG_OBJECT (self, "%s: role=%s buf=(%p:%d/%d) %x|%x %s",
854       str,
855       priv->is_client ? "client" : "server",
856       priv->bio_buffer,
857       priv->bio_buffer_offset,
858       priv->bio_buffer_len,
859       states, SSL_get_state (priv->ssl), SSL_state_string_long (priv->ssl));
860 #endif
861 }
862 
863 static gboolean
export_srtp_keys(GstDtlsConnection * self,GError ** err)864 export_srtp_keys (GstDtlsConnection * self, GError ** err)
865 {
866   typedef struct
867   {
868     guint8 v[SRTP_KEY_LEN];
869   } Key;
870 
871   typedef struct
872   {
873     guint8 v[SRTP_SALT_LEN];
874   } Salt;
875 
876   struct
877   {
878     Key client_key;
879     Key server_key;
880     Salt client_salt;
881     Salt server_salt;
882   } exported_keys;
883 
884   struct
885   {
886     Key key;
887     Salt salt;
888   } client_key, server_key;
889 
890   SRTP_PROTECTION_PROFILE *profile;
891   GstDtlsSrtpCipher cipher;
892   GstDtlsSrtpAuth auth;
893   gint success;
894 
895   static gchar export_string[] = "EXTRACTOR-dtls_srtp";
896 
897   success = SSL_export_keying_material (self->priv->ssl,
898       (gpointer) & exported_keys, 60, export_string, strlen (export_string),
899       NULL, 0, 0);
900 
901   if (!success) {
902     GST_WARNING_OBJECT (self, "Failed to export SRTP keys");
903     if (err)
904       *err =
905           g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
906           "Failed to export SRTP keys");
907     return FALSE;
908   }
909 
910   profile = SSL_get_selected_srtp_profile (self->priv->ssl);
911 
912   if (!profile) {
913     GST_WARNING_OBJECT (self,
914         "No SRTP capabilities negotiated during handshake");
915     if (err)
916       *err =
917           g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
918           "No SRTP capabilities negotiated during handshake");
919     return FALSE;
920   }
921 
922   GST_INFO_OBJECT (self, "keys received, profile is %s", profile->name);
923 
924   switch (profile->id) {
925     case SRTP_AES128_CM_SHA1_80:
926       cipher = GST_DTLS_SRTP_CIPHER_AES_128_ICM;
927       auth = GST_DTLS_SRTP_AUTH_HMAC_SHA1_80;
928       break;
929     case SRTP_AES128_CM_SHA1_32:
930       cipher = GST_DTLS_SRTP_CIPHER_AES_128_ICM;
931       auth = GST_DTLS_SRTP_AUTH_HMAC_SHA1_32;
932       break;
933     default:
934       GST_WARNING_OBJECT (self,
935           "Invalid/unsupported crypto suite set by handshake");
936       if (err)
937         *err =
938             g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
939             "Invalid/unsupported crypto suite set by handshake");
940       return FALSE;
941   }
942 
943   client_key.key = exported_keys.client_key;
944   server_key.key = exported_keys.server_key;
945   client_key.salt = exported_keys.client_salt;
946   server_key.salt = exported_keys.server_salt;
947 
948   if (self->priv->is_client) {
949     g_signal_emit (self, signals[SIGNAL_ON_ENCODER_KEY], 0, &client_key, cipher,
950         auth);
951     g_signal_emit (self, signals[SIGNAL_ON_DECODER_KEY], 0, &server_key,
952         cipher, auth);
953   } else {
954     g_signal_emit (self, signals[SIGNAL_ON_ENCODER_KEY], 0, &server_key,
955         cipher, auth);
956     g_signal_emit (self, signals[SIGNAL_ON_DECODER_KEY], 0, &client_key, cipher,
957         auth);
958   }
959 
960   self->priv->keys_exported = TRUE;
961 
962   return TRUE;
963 }
964 
965 static int
ssl_warn_cb(const char * str,size_t len,void * u)966 ssl_warn_cb (const char *str, size_t len, void *u)
967 {
968   GstDtlsConnection *self = u;
969   GST_WARNING_OBJECT (self, "ssl error: %s", str);
970   return 0;
971 }
972 
973 static int
ssl_err_cb(const char * str,size_t len,void * u)974 ssl_err_cb (const char *str, size_t len, void *u)
975 {
976   GstDtlsConnection *self = u;
977   GST_ERROR_OBJECT (self, "ssl error: %s", str);
978   return 0;
979 }
980 
981 static GstFlowReturn
handle_error(GstDtlsConnection * self,int ret,GstResourceError error_type,gboolean * notify_state,GError ** err)982 handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
983     gboolean * notify_state, GError ** err)
984 {
985   int error;
986 
987   error = SSL_get_error (self->priv->ssl, ret);
988 
989   switch (error) {
990     case SSL_ERROR_NONE:
991       GST_TRACE_OBJECT (self, "No error");
992       return GST_FLOW_OK;
993     case SSL_ERROR_SSL:
994       GST_ERROR_OBJECT (self, "Fatal SSL error");
995       if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
996         self->priv->connection_state = GST_DTLS_CONNECTION_STATE_FAILED;
997         *notify_state = TRUE;
998       }
999       ERR_print_errors_cb (ssl_err_cb, self);
1000       if (err)
1001         *err =
1002             g_error_new_literal (GST_RESOURCE_ERROR, error_type,
1003             "Fatal SSL error");
1004       return GST_FLOW_ERROR;
1005     case SSL_ERROR_ZERO_RETURN:
1006       GST_LOG_OBJECT (self, "Connection was closed");
1007       return GST_FLOW_EOS;
1008     case SSL_ERROR_WANT_READ:
1009       GST_LOG_OBJECT (self, "SSL wants read");
1010       return GST_FLOW_OK;
1011     case SSL_ERROR_WANT_WRITE:
1012       GST_LOG_OBJECT (self, "SSL wants write");
1013       return GST_FLOW_OK;
1014     case SSL_ERROR_SYSCALL:{
1015       GstFlowReturn rc = GST_FLOW_OK;
1016       /* OpenSSL shouldn't be making real system calls, so we can safely
1017        * ignore syscall errors. System interactions should happen through
1018        * our BIO.
1019        */
1020       if (error_type == GST_RESOURCE_ERROR_WRITE) {
1021         rc = self->priv->syscall_flow_return;
1022       }
1023       GST_DEBUG_OBJECT (self,
1024           "OpenSSL reported a syscall error. flow_return=%i", rc);
1025       return rc;
1026     }
1027     default:
1028       if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
1029         self->priv->connection_state = GST_DTLS_CONNECTION_STATE_FAILED;
1030         *notify_state = TRUE;
1031       }
1032       GST_ERROR_OBJECT (self, "Unknown SSL error: %d, ret: %d", error, ret);
1033       if (err)
1034         *err =
1035             g_error_new (GST_RESOURCE_ERROR, error_type,
1036             "Unknown SSL error: %d, ret: %d", error, ret);
1037       return GST_FLOW_ERROR;
1038   }
1039 }
1040 
1041 static GstFlowReturn
openssl_poll(GstDtlsConnection * self,gboolean * notify_state,GError ** err)1042 openssl_poll (GstDtlsConnection * self, gboolean * notify_state, GError ** err)
1043 {
1044   int ret;
1045   GstFlowReturn flow_ret;
1046 
1047   log_state (self, "poll: before handshake");
1048 
1049   ERR_clear_error ();
1050   ret = SSL_do_handshake (self->priv->ssl);
1051 
1052   log_state (self, "poll: after handshake");
1053 
1054   switch (ret) {
1055     case 1:
1056       if (!self->priv->keys_exported) {
1057         GST_INFO_OBJECT (self,
1058             "handshake just completed successfully, exporting keys");
1059 
1060         if (!export_srtp_keys (self, err))
1061           return GST_FLOW_ERROR;
1062 
1063         if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
1064             && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED
1065             && self->priv->connection_state !=
1066             GST_DTLS_CONNECTION_STATE_CONNECTED) {
1067           self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTED;
1068           *notify_state = TRUE;
1069         }
1070       } else {
1071         GST_INFO_OBJECT (self, "handshake is completed");
1072       }
1073       return GST_FLOW_OK;
1074     case 0:
1075       GST_DEBUG_OBJECT (self, "do_handshake encountered EOF");
1076       break;
1077     case -1:
1078       GST_DEBUG_OBJECT (self, "do_handshake encountered potential BIO error");
1079       break;
1080     default:
1081       GST_DEBUG_OBJECT (self, "do_handshake returned %d", ret);
1082       break;
1083   }
1084 
1085   flow_ret =
1086       handle_error (self, ret, GST_RESOURCE_ERROR_OPEN_WRITE, notify_state,
1087       err);
1088 
1089   ERR_print_errors_cb (ssl_warn_cb, self);
1090 
1091   return flow_ret;
1092 }
1093 
1094 static int
openssl_verify_callback(int preverify_ok,X509_STORE_CTX * x509_ctx)1095 openssl_verify_callback (int preverify_ok, X509_STORE_CTX * x509_ctx)
1096 {
1097   GstDtlsConnection *self;
1098   SSL *ssl;
1099   BIO *bio;
1100   gchar *pem = NULL;
1101   gboolean accepted = FALSE;
1102 
1103   ssl =
1104       X509_STORE_CTX_get_ex_data (x509_ctx,
1105       SSL_get_ex_data_X509_STORE_CTX_idx ());
1106   self = SSL_get_ex_data (ssl, connection_ex_index);
1107   g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), FALSE);
1108 
1109   pem = _gst_dtls_x509_to_pem (X509_STORE_CTX_get0_cert (x509_ctx));
1110 
1111   if (!pem) {
1112     GST_WARNING_OBJECT (self,
1113         "failed to convert received certificate to pem format");
1114   } else {
1115     bio = BIO_new (BIO_s_mem ());
1116     if (bio) {
1117       gchar buffer[2048];
1118       gint len;
1119 
1120       len =
1121           X509_NAME_print_ex (bio,
1122           X509_get_subject_name (X509_STORE_CTX_get0_cert (x509_ctx)), 1,
1123           XN_FLAG_MULTILINE);
1124       BIO_read (bio, buffer, len);
1125       buffer[len] = '\0';
1126       GST_DEBUG_OBJECT (self, "Peer certificate received:\n%s", buffer);
1127       BIO_free (bio);
1128     } else {
1129       GST_DEBUG_OBJECT (self, "failed to create certificate print membio");
1130     }
1131 
1132     g_signal_emit (self, signals[SIGNAL_ON_PEER_CERTIFICATE], 0, pem,
1133         &accepted);
1134     g_free (pem);
1135   }
1136 
1137   return accepted;
1138 }
1139 
1140 /*
1141     ########  ####  #######
1142     ##     ##  ##  ##     ##
1143     ##     ##  ##  ##     ##
1144     ########   ##  ##     ##
1145     ##     ##  ##  ##     ##
1146     ##     ##  ##  ##     ##
1147     ########  ####  #######
1148 */
1149 
1150 #if OPENSSL_VERSION_NUMBER < 0x10100001L
1151 static BIO_METHOD custom_bio_methods = {
1152   BIO_TYPE_BIO,
1153   "stream",
1154   bio_method_write,
1155   bio_method_read,
1156   NULL,
1157   NULL,
1158   bio_method_ctrl,
1159   bio_method_new,
1160   bio_method_free,
1161   NULL,
1162 };
1163 
1164 static BIO_METHOD *
BIO_s_gst_dtls_connection(void)1165 BIO_s_gst_dtls_connection (void)
1166 {
1167   return &custom_bio_methods;
1168 }
1169 #else
1170 static BIO_METHOD *custom_bio_methods;
1171 
1172 static BIO_METHOD *
BIO_s_gst_dtls_connection(void)1173 BIO_s_gst_dtls_connection (void)
1174 {
1175   if (custom_bio_methods != NULL)
1176     return custom_bio_methods;
1177 
1178   custom_bio_methods = BIO_meth_new (BIO_TYPE_BIO, "stream");
1179   if (custom_bio_methods == NULL
1180       || !BIO_meth_set_write (custom_bio_methods, bio_method_write)
1181       || !BIO_meth_set_read (custom_bio_methods, bio_method_read)
1182       || !BIO_meth_set_ctrl (custom_bio_methods, bio_method_ctrl)
1183       || !BIO_meth_set_create (custom_bio_methods, bio_method_new)
1184       || !BIO_meth_set_destroy (custom_bio_methods, bio_method_free)) {
1185     BIO_meth_free (custom_bio_methods);
1186     return NULL;
1187   }
1188 
1189   return custom_bio_methods;
1190 }
1191 #endif
1192 
1193 static int
bio_method_write(BIO * bio,const char * data,int size)1194 bio_method_write (BIO * bio, const char *data, int size)
1195 {
1196   GstDtlsConnection *self = GST_DTLS_CONNECTION (BIO_get_data (bio));
1197   gboolean ret = TRUE;
1198 
1199   GST_LOG_OBJECT (self, "BIO: writing %d", size);
1200   self->priv->syscall_flow_return = GST_FLOW_OK;
1201 
1202   if (self->priv->send_callback)
1203     ret = self->priv->send_callback (self, data, size,
1204         self->priv->send_callback_user_data);
1205 
1206   return ret ? size : -1;
1207 }
1208 
1209 static int
bio_method_read(BIO * bio,char * out_buffer,int size)1210 bio_method_read (BIO * bio, char *out_buffer, int size)
1211 {
1212   GstDtlsConnection *self = GST_DTLS_CONNECTION (BIO_get_data (bio));
1213   GstDtlsConnectionPrivate *priv = self->priv;
1214   guint internal_size;
1215   gint copy_size;
1216 
1217   internal_size = priv->bio_buffer_len - priv->bio_buffer_offset;
1218 
1219   if (!priv->bio_buffer) {
1220     GST_LOG_OBJECT (self, "BIO: EOF");
1221     return 0;
1222   }
1223 
1224   if (!out_buffer || size <= 0) {
1225     GST_WARNING_OBJECT (self, "BIO: read got invalid arguments");
1226     if (internal_size) {
1227       BIO_set_retry_read (bio);
1228     }
1229     return internal_size;
1230   }
1231 
1232   if (size > internal_size) {
1233     copy_size = internal_size;
1234   } else {
1235     copy_size = size;
1236   }
1237 
1238   GST_DEBUG_OBJECT (self,
1239       "reading %d/%d bytes %d at offset %d, output buff size is %d", copy_size,
1240       priv->bio_buffer_len, internal_size, priv->bio_buffer_offset, size);
1241 
1242   memcpy (out_buffer, (guint8 *) priv->bio_buffer + priv->bio_buffer_offset,
1243       copy_size);
1244   priv->bio_buffer_offset += copy_size;
1245 
1246   if (priv->bio_buffer_len == priv->bio_buffer_offset) {
1247     priv->bio_buffer = NULL;
1248   }
1249 
1250   return copy_size;
1251 }
1252 
1253 static long
bio_method_ctrl(BIO * bio,int cmd,long arg1,void * arg2)1254 bio_method_ctrl (BIO * bio, int cmd, long arg1, void *arg2)
1255 {
1256   GstDtlsConnection *self = GST_DTLS_CONNECTION (BIO_get_data (bio));
1257   GstDtlsConnectionPrivate *priv = self->priv;
1258 
1259   switch (cmd) {
1260     case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
1261     case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
1262       GST_LOG_OBJECT (self, "BIO: Timeout set");
1263       gst_dtls_connection_check_timeout_locked (self);
1264       return 1;
1265     case BIO_CTRL_RESET:
1266       priv->bio_buffer = NULL;
1267       priv->bio_buffer_len = 0;
1268       priv->bio_buffer_offset = 0;
1269       GST_LOG_OBJECT (self, "BIO: EOF reset");
1270       return 1;
1271     case BIO_CTRL_EOF:{
1272       gint eof = priv->is_alive == FALSE;
1273       GST_LOG_OBJECT (self, "BIO: EOF query returned %d", eof);
1274       return eof;
1275     }
1276     case BIO_CTRL_WPENDING:
1277       GST_LOG_OBJECT (self, "BIO: pending write");
1278       return 1;
1279     case BIO_CTRL_PENDING:{
1280       gint pending = priv->bio_buffer_len - priv->bio_buffer_offset;
1281       GST_LOG_OBJECT (self, "BIO: %d bytes pending", pending);
1282       return pending;
1283     }
1284     case BIO_CTRL_FLUSH:
1285       GST_LOG_OBJECT (self, "BIO: flushing");
1286       return 1;
1287     case BIO_CTRL_DGRAM_QUERY_MTU:
1288       GST_DEBUG_OBJECT (self, "BIO: MTU query, returning 0...");
1289       return 0;
1290     case BIO_CTRL_DGRAM_MTU_EXCEEDED:
1291       GST_WARNING_OBJECT (self, "BIO: MTU exceeded");
1292       return 0;
1293     default:
1294       GST_LOG_OBJECT (self, "BIO: unhandled ctrl, %d", cmd);
1295       return 0;
1296   }
1297 }
1298 
1299 static int
bio_method_new(BIO * bio)1300 bio_method_new (BIO * bio)
1301 {
1302   GST_LOG_OBJECT (NULL, "BIO: new");
1303 
1304   BIO_set_shutdown (bio, 0);
1305   BIO_set_init (bio, 1);
1306 
1307   return 1;
1308 }
1309 
1310 static int
bio_method_free(BIO * bio)1311 bio_method_free (BIO * bio)
1312 {
1313   if (!bio) {
1314     GST_LOG_OBJECT (NULL, "BIO free called with null bio");
1315     return 0;
1316   }
1317 
1318   GST_LOG_OBJECT (GST_DTLS_CONNECTION (BIO_get_data (bio)), "BIO free");
1319   return 0;
1320 }
1321 
1322 GType
gst_dtls_connection_state_get_type(void)1323 gst_dtls_connection_state_get_type (void)
1324 {
1325   static GType type = 0;
1326   static const GEnumValue values[] = {
1327     {GST_DTLS_CONNECTION_STATE_NEW, "New connection", "new"},
1328     {GST_DTLS_CONNECTION_STATE_CLOSED, "Closed connection on either side",
1329         "closed"},
1330     {GST_DTLS_CONNECTION_STATE_FAILED, "Failed connection", "failed"},
1331     {GST_DTLS_CONNECTION_STATE_CONNECTING, "Connecting", "connecting"},
1332     {GST_DTLS_CONNECTION_STATE_CONNECTED, "Successfully connected",
1333         "connected"},
1334     {0, NULL, NULL},
1335   };
1336 
1337   if (!type) {
1338     type = g_enum_register_static ("GstDtlsConnectionState", values);
1339   }
1340   return type;
1341 }
1342