1 /**
2 * Copyright (c) 2019 Paul-Louis Ageneau
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "dtlstransport.hpp"
20 #include "icetransport.hpp"
21 #include "internals.hpp"
22
23 #include <chrono>
24 #include <cstring>
25 #include <exception>
26 #include <iostream>
27
28 #if !USE_GNUTLS
29 #ifdef _WIN32
30 #include <winsock2.h> // for timeval
31 #else
32 #include <sys/time.h> // for timeval
33 #endif
34 #endif
35
36 using namespace std::chrono;
37
38 namespace rtc::impl {
39
40 #if USE_GNUTLS
41
Init()42 void DtlsTransport::Init() {
43 gnutls_global_init(); // optional
44 }
45
Cleanup()46 void DtlsTransport::Cleanup() { gnutls_global_deinit(); }
47
DtlsTransport(shared_ptr<IceTransport> lower,certificate_ptr certificate,optional<size_t> mtu,verifier_callback verifierCallback,state_callback stateChangeCallback)48 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
49 optional<size_t> mtu, verifier_callback verifierCallback,
50 state_callback stateChangeCallback)
51 : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
52 mVerifierCallback(std::move(verifierCallback)),
53 mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
54
55 PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
56
57 if (!mCertificate)
58 throw std::invalid_argument("DTLS certificate is null");
59
60 gnutls_certificate_credentials_t creds = mCertificate->credentials();
61 gnutls_certificate_set_verify_function(creds, CertificateCallback);
62
63 unsigned int flags = GNUTLS_DATAGRAM | (mIsClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
64 gnutls::check(gnutls_init(&mSession, flags));
65
66 try {
67 // RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
68 // Therefore, the DTLS layer MUST NOT use any compression algorithm.
69 // See https://tools.ietf.org/html/rfc8261#section-5
70 const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
71 const char *err_pos = NULL;
72 gnutls::check(gnutls_priority_set_direct(mSession, priorities, &err_pos),
73 "Failed to set TLS priorities");
74
75 // RFC 8827: The DTLS-SRTP protection profile SRTP_AES128_CM_HMAC_SHA1_80 MUST be supported
76 // See https://tools.ietf.org/html/rfc8827#section-6.5
77 gnutls::check(gnutls_srtp_set_profile(mSession, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80),
78 "Failed to set SRTP profile");
79
80 gnutls::check(gnutls_credentials_set(mSession, GNUTLS_CRD_CERTIFICATE, creds));
81
82 gnutls_dtls_set_timeouts(mSession,
83 1000, // 1s retransmission timeout recommended by RFC 6347
84 30000); // 30s total timeout
85 gnutls_handshake_set_timeout(mSession, 30000);
86
87 gnutls_session_set_ptr(mSession, this);
88 gnutls_transport_set_ptr(mSession, this);
89 gnutls_transport_set_push_function(mSession, WriteCallback);
90 gnutls_transport_set_pull_function(mSession, ReadCallback);
91 gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
92
93 } catch (...) {
94 gnutls_deinit(mSession);
95 throw;
96 }
97 }
98
~DtlsTransport()99 DtlsTransport::~DtlsTransport() {
100 stop();
101
102 gnutls_deinit(mSession);
103 }
104
start()105 void DtlsTransport::start() {
106 Transport::start();
107
108 registerIncoming();
109
110 PLOG_DEBUG << "Starting DTLS recv thread";
111 mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
112 }
113
stop()114 bool DtlsTransport::stop() {
115 if (!Transport::stop())
116 return false;
117
118 PLOG_DEBUG << "Stopping DTLS recv thread";
119 mIncomingQueue.stop();
120 mRecvThread.join();
121 return true;
122 }
123
send(message_ptr message)124 bool DtlsTransport::send(message_ptr message) {
125 if (!message || state() != State::Connected)
126 return false;
127
128 PLOG_VERBOSE << "Send size=" << message->size();
129
130 ssize_t ret;
131 do {
132 std::lock_guard lock(mSendMutex);
133 mCurrentDscp = message->dscp;
134 ret = gnutls_record_send(mSession, message->data(), message->size());
135 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
136
137 if (ret == GNUTLS_E_LARGE_PACKET)
138 return false;
139
140 return gnutls::check(ret);
141 }
142
incoming(message_ptr message)143 void DtlsTransport::incoming(message_ptr message) {
144 if (!message) {
145 mIncomingQueue.stop();
146 return;
147 }
148
149 PLOG_VERBOSE << "Incoming size=" << message->size();
150 mIncomingQueue.push(message);
151 }
152
outgoing(message_ptr message)153 bool DtlsTransport::outgoing(message_ptr message) {
154 if (message->dscp == 0)
155 message->dscp = mCurrentDscp;
156
157 return Transport::outgoing(std::move(message));
158 }
159
postHandshake()160 void DtlsTransport::postHandshake() {
161 // Dummy
162 }
163
runRecvLoop()164 void DtlsTransport::runRecvLoop() {
165 const size_t bufferSize = 4096;
166
167 // Handshake loop
168 try {
169 changeState(State::Connecting);
170
171 size_t mtu = mMtu.value_or(DEFAULT_MTU) - 8 - 40; // UDP/IPv6
172 gnutls_dtls_set_mtu(mSession, static_cast<unsigned int>(mtu));
173 PLOG_VERBOSE << "SSL MTU set to " << mtu;
174
175 int ret;
176 do {
177 ret = gnutls_handshake(mSession);
178
179 if (ret == GNUTLS_E_LARGE_PACKET)
180 throw std::runtime_error("MTU is too low");
181
182 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ||
183 !gnutls::check(ret, "DTLS handshake failed"));
184
185 // RFC 8261: DTLS MUST support sending messages larger than the current path MTU
186 // See https://tools.ietf.org/html/rfc8261#section-5
187 gnutls_dtls_set_mtu(mSession, bufferSize + 1);
188
189 } catch (const std::exception &e) {
190 PLOG_ERROR << "DTLS handshake: " << e.what();
191 changeState(State::Failed);
192 return;
193 }
194
195 // Receive loop
196 try {
197 PLOG_INFO << "DTLS handshake finished";
198 postHandshake();
199 changeState(State::Connected);
200
201 char buffer[bufferSize];
202
203 while (true) {
204 ssize_t ret;
205 do {
206 ret = gnutls_record_recv(mSession, buffer, bufferSize);
207 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
208
209 // RFC 8827: Implementations MUST NOT implement DTLS renegotiation and MUST reject it
210 // with a "no_renegotiation" alert if offered.
211 // See https://tools.ietf.org/html/rfc8827#section-6.5
212 if (ret == GNUTLS_E_REHANDSHAKE) {
213 do {
214 std::lock_guard lock(mSendMutex);
215 ret = gnutls_alert_send(mSession, GNUTLS_AL_WARNING, GNUTLS_A_NO_RENEGOTIATION);
216 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
217 continue;
218 }
219
220 // Consider premature termination as remote closing
221 if (ret == GNUTLS_E_PREMATURE_TERMINATION) {
222 PLOG_DEBUG << "DTLS connection terminated";
223 break;
224 }
225
226 if (gnutls::check(ret)) {
227 if (ret == 0) {
228 // Closed
229 PLOG_DEBUG << "DTLS connection cleanly closed";
230 break;
231 }
232 auto *b = reinterpret_cast<byte *>(buffer);
233 recv(make_message(b, b + ret));
234 }
235 }
236
237 } catch (const std::exception &e) {
238 PLOG_ERROR << "DTLS recv: " << e.what();
239 }
240
241 gnutls_bye(mSession, GNUTLS_SHUT_RDWR);
242
243 PLOG_INFO << "DTLS closed";
244 changeState(State::Disconnected);
245 recv(nullptr);
246 }
247
CertificateCallback(gnutls_session_t session)248 int DtlsTransport::CertificateCallback(gnutls_session_t session) {
249 DtlsTransport *t = static_cast<DtlsTransport *>(gnutls_session_get_ptr(session));
250 try {
251 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
252 return GNUTLS_E_CERTIFICATE_ERROR;
253 }
254
255 unsigned int count = 0;
256 const gnutls_datum_t *array = gnutls_certificate_get_peers(session, &count);
257 if (!array || count == 0) {
258 return GNUTLS_E_CERTIFICATE_ERROR;
259 }
260
261 gnutls_x509_crt_t crt;
262 gnutls::check(gnutls_x509_crt_init(&crt));
263 int ret = gnutls_x509_crt_import(crt, &array[0], GNUTLS_X509_FMT_DER);
264 if (ret != GNUTLS_E_SUCCESS) {
265 gnutls_x509_crt_deinit(crt);
266 return GNUTLS_E_CERTIFICATE_ERROR;
267 }
268
269 string fingerprint = make_fingerprint(crt);
270 gnutls_x509_crt_deinit(crt);
271
272 bool success = t->mVerifierCallback(fingerprint);
273 return success ? GNUTLS_E_SUCCESS : GNUTLS_E_CERTIFICATE_ERROR;
274
275 } catch (const std::exception &e) {
276 PLOG_WARNING << e.what();
277 return GNUTLS_E_CERTIFICATE_ERROR;
278 }
279 }
280
WriteCallback(gnutls_transport_ptr_t ptr,const void * data,size_t len)281 ssize_t DtlsTransport::WriteCallback(gnutls_transport_ptr_t ptr, const void *data, size_t len) {
282 DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
283 try {
284 if (len > 0) {
285 auto b = reinterpret_cast<const byte *>(data);
286 t->outgoing(make_message(b, b + len));
287 }
288 gnutls_transport_set_errno(t->mSession, 0);
289 return ssize_t(len);
290
291 } catch (const std::exception &e) {
292 PLOG_WARNING << e.what();
293 gnutls_transport_set_errno(t->mSession, ECONNRESET);
294 return -1;
295 }
296 }
297
ReadCallback(gnutls_transport_ptr_t ptr,void * data,size_t maxlen)298 ssize_t DtlsTransport::ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_t maxlen) {
299 DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
300 try {
301 if (auto next = t->mIncomingQueue.pop()) {
302 message_ptr message = std::move(*next);
303 ssize_t len = std::min(maxlen, message->size());
304 std::memcpy(data, message->data(), len);
305 gnutls_transport_set_errno(t->mSession, 0);
306 return len;
307 }
308
309 // Closed
310 gnutls_transport_set_errno(t->mSession, 0);
311 return 0;
312
313 } catch (const std::exception &e) {
314 PLOG_WARNING << e.what();
315 gnutls_transport_set_errno(t->mSession, ECONNRESET);
316 return -1;
317 }
318 }
319
TimeoutCallback(gnutls_transport_ptr_t ptr,unsigned int ms)320 int DtlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms) {
321 DtlsTransport *t = static_cast<DtlsTransport *>(ptr);
322 try {
323 bool isReadable = t->mIncomingQueue.wait(
324 ms != GNUTLS_INDEFINITE_TIMEOUT ? std::make_optional(milliseconds(ms)) : nullopt);
325 return isReadable ? 1 : 0;
326
327 } catch (const std::exception &e) {
328 PLOG_WARNING << e.what();
329 return 1;
330 }
331 }
332
333 #else // USE_GNUTLS==0
334
335 BIO_METHOD *DtlsTransport::BioMethods = NULL;
336 int DtlsTransport::TransportExIndex = -1;
337 std::mutex DtlsTransport::GlobalMutex;
338
339 void DtlsTransport::Init() {
340 std::lock_guard lock(GlobalMutex);
341
342 openssl::init();
343
344 if (!BioMethods) {
345 BioMethods = BIO_meth_new(BIO_TYPE_BIO, "DTLS writer");
346 if (!BioMethods)
347 throw std::runtime_error("Failed to create BIO methods for DTLS writer");
348 BIO_meth_set_create(BioMethods, BioMethodNew);
349 BIO_meth_set_destroy(BioMethods, BioMethodFree);
350 BIO_meth_set_write(BioMethods, BioMethodWrite);
351 BIO_meth_set_ctrl(BioMethods, BioMethodCtrl);
352 }
353 if (TransportExIndex < 0) {
354 TransportExIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
355 }
356 }
357
358 void DtlsTransport::Cleanup() {
359 // Nothing to do
360 }
361
362 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
363 optional<size_t> mtu, verifier_callback verifierCallback,
364 state_callback stateChangeCallback)
365 : Transport(lower, std::move(stateChangeCallback)), mMtu(mtu), mCertificate(certificate),
366 mVerifierCallback(std::move(verifierCallback)),
367 mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
368 PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
369
370 if (!mCertificate)
371 throw std::invalid_argument("DTLS certificate is null");
372
373 try {
374 mCtx = SSL_CTX_new(DTLS_method());
375 if (!mCtx)
376 throw std::runtime_error("Failed to create SSL context");
377
378 // RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
379 // Therefore, the DTLS layer MUST NOT use any compression algorithm.
380 // See https://tools.ietf.org/html/rfc8261#section-5
381 // RFC 8827: Implementations MUST NOT implement DTLS renegotiation
382 // See https://tools.ietf.org/html/rfc8827#section-6.5
383 SSL_CTX_set_options(mCtx, SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_QUERY_MTU |
384 SSL_OP_NO_RENEGOTIATION);
385
386 SSL_CTX_set_min_proto_version(mCtx, DTLS1_VERSION);
387 SSL_CTX_set_read_ahead(mCtx, 1);
388 SSL_CTX_set_quiet_shutdown(mCtx, 1);
389 SSL_CTX_set_info_callback(mCtx, InfoCallback);
390
391 SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
392 CertificateCallback);
393 SSL_CTX_set_verify_depth(mCtx, 1);
394
395 openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
396 "Failed to set SSL priorities");
397
398 auto [x509, pkey] = mCertificate->credentials();
399 SSL_CTX_use_certificate(mCtx, x509);
400 SSL_CTX_use_PrivateKey(mCtx, pkey);
401
402 openssl::check(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed");
403
404 mSsl = SSL_new(mCtx);
405 if (!mSsl)
406 throw std::runtime_error("Failed to create SSL instance");
407
408 SSL_set_ex_data(mSsl, TransportExIndex, this);
409
410 if (mIsClient)
411 SSL_set_connect_state(mSsl);
412 else
413 SSL_set_accept_state(mSsl);
414
415 mInBio = BIO_new(BIO_s_mem());
416 mOutBio = BIO_new(BioMethods);
417 if (!mInBio || !mOutBio)
418 throw std::runtime_error("Failed to create BIO");
419
420 BIO_set_mem_eof_return(mInBio, BIO_EOF);
421 BIO_set_data(mOutBio, this);
422 SSL_set_bio(mSsl, mInBio, mOutBio);
423
424 auto ecdh = unique_ptr<EC_KEY, decltype(&EC_KEY_free)>(
425 EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);
426 SSL_set_options(mSsl, SSL_OP_SINGLE_ECDH_USE);
427 SSL_set_tmp_ecdh(mSsl, ecdh.get());
428
429 // RFC 8827: The DTLS-SRTP protection profile SRTP_AES128_CM_HMAC_SHA1_80 MUST be supported
430 // See https://tools.ietf.org/html/rfc8827#section-6.5 Warning:
431 // SSL_set_tlsext_use_srtp() returns 0 on success and 1 on error
432 if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"))
433 throw std::runtime_error("Failed to set SRTP profile: " +
434 openssl::error_string(ERR_get_error()));
435
436 } catch (...) {
437 if (mSsl)
438 SSL_free(mSsl);
439 if (mCtx)
440 SSL_CTX_free(mCtx);
441 throw;
442 }
443 }
444
445 DtlsTransport::~DtlsTransport() {
446 stop();
447
448 SSL_free(mSsl);
449 SSL_CTX_free(mCtx);
450 }
451
452 void DtlsTransport::start() {
453 Transport::start();
454
455 registerIncoming();
456
457 PLOG_DEBUG << "Starting DTLS recv thread";
458 mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
459 }
460
461 bool DtlsTransport::stop() {
462 if (!Transport::stop())
463 return false;
464
465 PLOG_DEBUG << "Stopping DTLS recv thread";
466 mIncomingQueue.stop();
467 mRecvThread.join();
468 SSL_shutdown(mSsl);
469 return true;
470 }
471
472 bool DtlsTransport::send(message_ptr message) {
473 if (!message || state() != State::Connected)
474 return false;
475
476 PLOG_VERBOSE << "Send size=" << message->size();
477
478 mCurrentDscp = message->dscp;
479 int ret = SSL_write(mSsl, message->data(), int(message->size()));
480 return openssl::check(mSsl, ret);
481 }
482
483 void DtlsTransport::incoming(message_ptr message) {
484 if (!message) {
485 mIncomingQueue.stop();
486 return;
487 }
488
489 PLOG_VERBOSE << "Incoming size=" << message->size();
490 mIncomingQueue.push(message);
491 }
492
493 bool DtlsTransport::outgoing(message_ptr message) {
494 if (message->dscp == 0) {
495 // DTLS handshake packet
496 if (state() != DtlsTransport::State::Connected) {
497 // Set recommended high-priority DSCP value
498 // See https://datatracker.ietf.org/doc/html/rfc8837#section-5
499 message->dscp = 18; // AF21(18), Assured Forwarding class 2, low drop probability
500 } else {
501 message->dscp = mCurrentDscp;
502 }
503 }
504 return Transport::outgoing(std::move(message));
505 }
506
507 void DtlsTransport::postHandshake() {
508 // Dummy
509 }
510
511 void DtlsTransport::runRecvLoop() {
512 const size_t bufferSize = 4096;
513 try {
514 changeState(State::Connecting);
515
516 size_t mtu = mMtu.value_or(DEFAULT_MTU) - 8 - 40; // UDP/IPv6
517 SSL_set_mtu(mSsl, static_cast<unsigned int>(mtu));
518 PLOG_VERBOSE << "SSL MTU set to " << mtu;
519
520 // Initiate the handshake
521 int ret = SSL_do_handshake(mSsl);
522 openssl::check(mSsl, ret, "Handshake failed");
523
524 byte buffer[bufferSize];
525 while (mIncomingQueue.running()) {
526 // Process pending messages
527 while (auto next = mIncomingQueue.tryPop()) {
528 message_ptr message = std::move(*next);
529 BIO_write(mInBio, message->data(), int(message->size()));
530
531 if (state() == State::Connecting) {
532 // Continue the handshake
533 ret = SSL_do_handshake(mSsl);
534 if (!openssl::check(mSsl, ret, "Handshake failed"))
535 break;
536
537 if (SSL_is_init_finished(mSsl)) {
538 // RFC 8261: DTLS MUST support sending messages larger than the current path
539 // MTU See https://tools.ietf.org/html/rfc8261#section-5
540 SSL_set_mtu(mSsl, bufferSize + 1);
541
542 PLOG_INFO << "DTLS handshake finished";
543 postHandshake();
544 changeState(State::Connected);
545 }
546 } else {
547 ret = SSL_read(mSsl, buffer, bufferSize);
548 if (!openssl::check(mSsl, ret))
549 break;
550
551 if (ret > 0)
552 recv(make_message(buffer, buffer + ret));
553 }
554 }
555
556 // No more messages pending, retransmit and rearm timeout if connecting
557 optional<milliseconds> duration;
558 if (state() == State::Connecting) {
559 // Warning: This function breaks the usual return value convention
560 ret = DTLSv1_handle_timeout(mSsl);
561 if (ret < 0) {
562 throw std::runtime_error("Handshake timeout"); // write BIO can't fail
563 } else if (ret > 0) {
564 LOG_VERBOSE << "OpenSSL did DTLS retransmit";
565 }
566
567 struct timeval timeout = {};
568 if (state() == State::Connecting && DTLSv1_get_timeout(mSsl, &timeout)) {
569 duration = milliseconds(timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
570 // Also handle handshake timeout manually because OpenSSL actually doesn't...
571 // OpenSSL backs off exponentially in base 2 starting from the recommended 1s
572 // so this allows for 5 retransmissions and fails after roughly 30s.
573 if (duration > 30s) {
574 throw std::runtime_error("Handshake timeout");
575 } else {
576 LOG_VERBOSE << "OpenSSL DTLS retransmit timeout is " << duration->count()
577 << "ms";
578 }
579 }
580 }
581
582 mIncomingQueue.wait(duration);
583 }
584 } catch (const std::exception &e) {
585 PLOG_ERROR << "DTLS recv: " << e.what();
586 }
587
588 if (state() == State::Connected) {
589 PLOG_INFO << "DTLS closed";
590 changeState(State::Disconnected);
591 recv(nullptr);
592 } else {
593 PLOG_ERROR << "DTLS handshake failed";
594 changeState(State::Failed);
595 }
596 }
597
598 int DtlsTransport::CertificateCallback(int /*preverify_ok*/, X509_STORE_CTX *ctx) {
599 SSL *ssl =
600 static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
601 DtlsTransport *t =
602 static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
603
604 X509 *crt = X509_STORE_CTX_get_current_cert(ctx);
605 string fingerprint = make_fingerprint(crt);
606
607 return t->mVerifierCallback(fingerprint) ? 1 : 0;
608 }
609
610 void DtlsTransport::InfoCallback(const SSL *ssl, int where, int ret) {
611 DtlsTransport *t =
612 static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
613
614 if (where & SSL_CB_ALERT) {
615 if (ret != 256) { // Close Notify
616 PLOG_ERROR << "DTLS alert: " << SSL_alert_desc_string_long(ret);
617 }
618 t->mIncomingQueue.stop(); // Close the connection
619 }
620 }
621
622 int DtlsTransport::BioMethodNew(BIO *bio) {
623 BIO_set_init(bio, 1);
624 BIO_set_data(bio, NULL);
625 BIO_set_shutdown(bio, 0);
626 return 1;
627 }
628
629 int DtlsTransport::BioMethodFree(BIO *bio) {
630 if (!bio)
631 return 0;
632 BIO_set_data(bio, NULL);
633 return 1;
634 }
635
636 int DtlsTransport::BioMethodWrite(BIO *bio, const char *in, int inl) {
637 if (inl <= 0)
638 return inl;
639 auto transport = reinterpret_cast<DtlsTransport *>(BIO_get_data(bio));
640 if (!transport)
641 return -1;
642 auto b = reinterpret_cast<const byte *>(in);
643 transport->outgoing(make_message(b, b + inl));
644 return inl; // can't fail
645 }
646
647 long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /*ptr*/) {
648 switch (cmd) {
649 case BIO_CTRL_FLUSH:
650 return 1;
651 case BIO_CTRL_DGRAM_QUERY_MTU:
652 return 0; // SSL_OP_NO_QUERY_MTU must be set
653 case BIO_CTRL_WPENDING:
654 case BIO_CTRL_PENDING:
655 return 0;
656 default:
657 break;
658 }
659 return 0;
660 }
661
662 #endif
663
664 } // namespace rtc::impl
665