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