mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 15:15:28 +00:00
Added OpenSSL as an alternative to GnuTLS
This commit is contained in:
@ -44,19 +44,10 @@ else()
|
||||
target_compile_options(usrsctp-static PRIVATE -Wno-error=address-of-packed-member -Wno-error=format-truncation)
|
||||
endif()
|
||||
|
||||
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
||||
|
||||
find_package(GnuTLS REQUIRED)
|
||||
find_package(LibNice REQUIRED)
|
||||
|
||||
if(NOT TARGET GnuTLS::GnuTLS)
|
||||
add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED)
|
||||
set_target_properties(GnuTLS::GnuTLS PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}"
|
||||
INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
add_library(datachannel SHARED ${LIBDATACHANNEL_SOURCES})
|
||||
set_target_properties(datachannel PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
@ -65,7 +56,7 @@ set_target_properties(datachannel PROPERTIES
|
||||
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_link_libraries(datachannel usrsctp-static GnuTLS::GnuTLS LibNice::LibNice)
|
||||
target_link_libraries(datachannel usrsctp-static LibNice::LibNice)
|
||||
|
||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
||||
set_target_properties(datachannel-static PROPERTIES
|
||||
@ -75,7 +66,29 @@ set_target_properties(datachannel-static PROPERTIES
|
||||
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_link_libraries(datachannel-static usrsctp-static GnuTLS::GnuTLS LibNice::LibNice)
|
||||
target_link_libraries(datachannel-static usrsctp-static LibNice::LibNice)
|
||||
|
||||
if (USE_GNUTLS)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
if(NOT TARGET GnuTLS::GnuTLS)
|
||||
add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED)
|
||||
set_target_properties(GnuTLS::GnuTLS PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}"
|
||||
INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
|
||||
endif()
|
||||
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=1)
|
||||
target_link_libraries(datachannel GnuTLS::GnuTLS)
|
||||
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=1)
|
||||
target_link_libraries(datachannel-static GnuTLS::GnuTLS)
|
||||
else()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=0)
|
||||
target_link_libraries(datachannel OpenSSL::SSL)
|
||||
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=0)
|
||||
target_link_libraries(datachannel-static OpenSSL::SSL)
|
||||
endif()
|
||||
|
||||
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
|
||||
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)
|
||||
|
6
Jamfile
6
Jamfile
@ -6,13 +6,15 @@ lib libdatachannel
|
||||
[ glob ./src/*.cpp ]
|
||||
: # requirements
|
||||
<include>./include/rtc
|
||||
<cxxflags>"`pkg-config --cflags gnutls glib-2.0 gobject-2.0 nice`"
|
||||
<define>USE_GNUTLS=0
|
||||
<cxxflags>"`pkg-config --cflags openssl glib-2.0 gobject-2.0 nice`"
|
||||
<library>/libdatachannel//usrsctp
|
||||
: # default build
|
||||
<link>static
|
||||
: # usage requirements
|
||||
<include>./include
|
||||
<linkflags>"`pkg-config --libs gnutls glib-2.0 gobject-2.0 nice`"
|
||||
<cxxflags>-pthread
|
||||
<linkflags>"`pkg-config --libs openssl glib-2.0 gobject-2.0 nice`"
|
||||
;
|
||||
|
||||
alias usrsctp
|
||||
|
15
Makefile
15
Makefile
@ -7,12 +7,21 @@ RM=rm -f
|
||||
CPPFLAGS=-O2 -pthread -fPIC -Wall -Wno-address-of-packed-member
|
||||
CXXFLAGS=-std=c++17
|
||||
LDFLAGS=-pthread
|
||||
LIBS=gnutls glib-2.0 gobject-2.0 nice
|
||||
LIBS=glib-2.0 gobject-2.0 nice
|
||||
USRSCTP_DIR=usrsctp
|
||||
|
||||
USE_GNUTLS ?= 0
|
||||
ifeq ($(USE_GNUTLS), 1)
|
||||
CPPFLAGS+= -DUSE_GNUTLS=1
|
||||
LIBS+= gnutls
|
||||
else
|
||||
CPPFLAGS+= -DUSE_GNUTLS=0
|
||||
LIBS+= openssl
|
||||
endif
|
||||
|
||||
LDLIBS= $(shell pkg-config --libs $(LIBS))
|
||||
INCLUDES=-Iinclude/rtc -I$(USRSCTP_DIR)/usrsctplib $(shell pkg-config --cflags $(LIBS))
|
||||
|
||||
USRSCTP_DIR:=usrsctp
|
||||
|
||||
SRCS=$(shell printf "%s " src/*.cpp)
|
||||
OBJS=$(subst .cpp,.o,$(SRCS))
|
||||
|
||||
|
@ -13,7 +13,7 @@ The library aims at fully implementing SCTP DataChannels ([draft-ietf-rtcweb-dat
|
||||
## Dependencies
|
||||
|
||||
- libnice: https://github.com/libnice/libnice
|
||||
- GnuTLS: https://www.gnutls.org/
|
||||
- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/
|
||||
|
||||
Submodules:
|
||||
- usrsctp: https://github.com/sctplab/usrsctp
|
||||
@ -24,7 +24,7 @@ Submodules:
|
||||
$ git submodule update --init --recursive
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ cmake -DUSE_GNUTLS=1 ..
|
||||
$ make
|
||||
```
|
||||
|
||||
|
@ -25,10 +25,13 @@
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
#if USE_GNUTLS
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -117,10 +120,10 @@ Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
|
||||
"Unable to set certificate and key pair in credentials");
|
||||
}
|
||||
|
||||
string Certificate::fingerprint() const { return mFingerprint; }
|
||||
|
||||
gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }
|
||||
|
||||
string Certificate::fingerprint() const { return mFingerprint; }
|
||||
|
||||
string make_fingerprint(gnutls_x509_crt_t crt) {
|
||||
const size_t size = 32;
|
||||
unsigned char buffer[size];
|
||||
@ -177,3 +180,120 @@ shared_ptr<Certificate> make_certificate(const string &commonName) {
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#else
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
namespace rtc {
|
||||
|
||||
Certificate::Certificate(string crt_pem, string key_pem) {
|
||||
BIO *bio;
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, crt_pem.data(), crt_pem.size());
|
||||
mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
|
||||
BIO_free(bio);
|
||||
if (!mX509)
|
||||
throw std::invalid_argument("Unable to import certificate PEM");
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, key_pem.data(), key_pem.size());
|
||||
mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
|
||||
BIO_free(bio);
|
||||
if (!mPKey)
|
||||
throw std::invalid_argument("Unable to import PEM key PEM");
|
||||
|
||||
mFingerprint = make_fingerprint(mX509.get());
|
||||
}
|
||||
|
||||
Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey) :
|
||||
mX509(std::move(x509)), mPKey(std::move(pkey))
|
||||
{
|
||||
mFingerprint = make_fingerprint(mX509.get());
|
||||
}
|
||||
|
||||
string Certificate::fingerprint() const { return mFingerprint; }
|
||||
|
||||
std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const { return {mX509.get(), mPKey.get()}; }
|
||||
|
||||
string make_fingerprint(X509 *x509) {
|
||||
const size_t size = 32;
|
||||
unsigned char buffer[size];
|
||||
unsigned int len = size;
|
||||
if (!X509_digest(x509, EVP_sha256(), buffer, &len))
|
||||
throw std::runtime_error("X509 fingerprint error");
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::uppercase << std::setfill('0');
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (i)
|
||||
oss << std::setw(1) << ':';
|
||||
oss << std::setw(2) << unsigned(buffer[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<Certificate> make_certificate(const string &commonName) {
|
||||
static std::unordered_map<string, shared_ptr<Certificate>> cache;
|
||||
static std::mutex cacheMutex;
|
||||
|
||||
std::lock_guard<std::mutex> lock(cacheMutex);
|
||||
if (auto it = cache.find(commonName); it != cache.end())
|
||||
return it->second;
|
||||
|
||||
if (cache.empty()) {
|
||||
// This is the first call to OpenSSL
|
||||
OPENSSL_init_ssl(0, NULL);
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
}
|
||||
|
||||
shared_ptr<X509> x509(X509_new(), X509_free);
|
||||
shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
|
||||
|
||||
unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
|
||||
unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
|
||||
unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
|
||||
unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);
|
||||
|
||||
if (!x509 || !pkey || !rsa || !exponent || !serial_number || !name)
|
||||
throw std::runtime_error("Unable allocate structures for certificate generation");
|
||||
|
||||
const int bits = 4096;
|
||||
const unsigned int e = 65537; // 2^16 + 1
|
||||
|
||||
if (!pkey || !rsa || !exponent || !BN_set_word(exponent.get(), e) ||
|
||||
!RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
|
||||
!EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) // the key will be freed when pkey is freed
|
||||
throw std::runtime_error("Unable to generate key pair");
|
||||
|
||||
const size_t serialSize = 16;
|
||||
const auto *commonNameBytes = reinterpret_cast<const unsigned char *>(commonName.c_str());
|
||||
|
||||
if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 3600 * -1) ||
|
||||
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 3600 * 24 * 365) ||
|
||||
!X509_set_version(x509.get(), 1) || !X509_set_pubkey(x509.get(), pkey.get()) ||
|
||||
!BN_pseudo_rand(serial_number.get(), serialSize, 0, 0) ||
|
||||
!BN_to_ASN1_INTEGER(serial_number.get(), X509_get_serialNumber(x509.get())) ||
|
||||
!X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, commonNameBytes, -1,
|
||||
-1, 0) ||
|
||||
!X509_set_subject_name(x509.get(), name.get()) ||
|
||||
!X509_set_issuer_name(x509.get(), name.get()))
|
||||
throw std::runtime_error("Unable to set certificate properties");
|
||||
|
||||
if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
|
||||
throw std::runtime_error("Unable to auto-sign certificate");
|
||||
|
||||
auto certificate = std::make_shared<Certificate>(x509, pkey);
|
||||
cache.emplace(std::make_pair(commonName, certificate));
|
||||
return certificate;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -21,24 +21,47 @@
|
||||
|
||||
#include "include.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#if USE_GNUTLS
|
||||
#include <gnutls/x509.h>
|
||||
#else
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
namespace rtc {
|
||||
|
||||
class Certificate {
|
||||
public:
|
||||
Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
|
||||
Certificate(string crt_pem, string key_pem);
|
||||
|
||||
string fingerprint() const;
|
||||
#if USE_GNUTLS
|
||||
Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
|
||||
gnutls_certificate_credentials_t credentials() const;
|
||||
#else
|
||||
Certificate(std::shared_ptr<X509> x509, std::shared_ptr<EVP_PKEY> pkey);
|
||||
std::tuple<X509 *, EVP_PKEY *> credentials() const;
|
||||
#endif
|
||||
|
||||
string fingerprint() const;
|
||||
|
||||
private:
|
||||
#if USE_GNUTLS
|
||||
std::shared_ptr<gnutls_certificate_credentials_t> mCredentials;
|
||||
#else
|
||||
std::shared_ptr<X509> mX509;
|
||||
std::shared_ptr<EVP_PKEY> mPKey;
|
||||
#endif
|
||||
|
||||
string mFingerprint;
|
||||
};
|
||||
|
||||
#if USE_GNUTLS
|
||||
string make_fingerprint(gnutls_x509_crt_t crt);
|
||||
#else
|
||||
string make_fingerprint(X509 *x509);
|
||||
#endif
|
||||
|
||||
std::shared_ptr<Certificate> make_certificate(const string &commonName);
|
||||
|
||||
} // namespace rtc
|
||||
|
@ -24,10 +24,14 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::weak_ptr;
|
||||
|
||||
#if USE_GNUTLS
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -44,8 +48,6 @@ static bool check_gnutls(int ret, const string &message = "GnuTLS error") {
|
||||
|
||||
namespace rtc {
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
|
||||
verifier_callback verifierCallback,
|
||||
state_callback stateChangeCallback)
|
||||
@ -61,7 +63,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
||||
// RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
|
||||
// Therefore, the DTLS layer MUST NOT use any compression algorithm.
|
||||
// See https://tools.ietf.org/html/rfc8261#section-5
|
||||
const char *priorities = "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-ARCFOUR-128:-COMP-ALL";
|
||||
const char *priorities = "SECURE128:-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL";
|
||||
const char *err_pos = NULL;
|
||||
check_gnutls(gnutls_priority_set_direct(mSession, priorities, &err_pos),
|
||||
"Unable to set TLS priorities");
|
||||
@ -95,7 +97,7 @@ DtlsTransport::~DtlsTransport() {
|
||||
DtlsTransport::State DtlsTransport::state() const { return mState; }
|
||||
|
||||
bool DtlsTransport::send(message_ptr message) {
|
||||
if (!message)
|
||||
if (!message || mState != State::Connected)
|
||||
return false;
|
||||
|
||||
ssize_t ret;
|
||||
@ -242,3 +244,238 @@ int DtlsTransport::TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms)
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#else
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
namespace {
|
||||
|
||||
const int BIO_EOF = -1;
|
||||
|
||||
string openssl_error_string(unsigned long err) {
|
||||
const size_t bufferSize = 256;
|
||||
char buffer[bufferSize];
|
||||
ERR_error_string_n(err, buffer, bufferSize);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
bool check_openssl(int success, const string &message = "OpenSSL error") {
|
||||
if (success)
|
||||
return true;
|
||||
else
|
||||
throw std::runtime_error(message + ": " + openssl_error_string(ERR_get_error()));
|
||||
}
|
||||
|
||||
bool check_openssl_ret(SSL *ssl, int ret, const string &message = "OpenSSL error") {
|
||||
if (ret == BIO_EOF)
|
||||
return true;
|
||||
|
||||
unsigned long err = SSL_get_error(ssl, ret);
|
||||
if (err == SSL_ERROR_NONE || err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||
return true;
|
||||
else if (err == SSL_ERROR_ZERO_RETURN)
|
||||
return false;
|
||||
else
|
||||
throw std::runtime_error(message + ": " + openssl_error_string(err));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace rtc {
|
||||
|
||||
int DtlsTransport::TransportExIndex = -1;
|
||||
std::mutex DtlsTransport::GlobalMutex;
|
||||
|
||||
void DtlsTransport::GlobalInit() {
|
||||
std::lock_guard<std::mutex> lock(GlobalMutex);
|
||||
if (TransportExIndex < 0) {
|
||||
TransportExIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
|
||||
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
||||
: Transport(lower), mCertificate(certificate), mState(State::Disconnected),
|
||||
mVerifierCallback(std::move(verifierCallback)),
|
||||
mStateChangeCallback(std::move(stateChangeCallback)) {
|
||||
|
||||
GlobalInit();
|
||||
|
||||
if (!(mCtx = SSL_CTX_new(DTLS_method())))
|
||||
throw std::runtime_error("Unable to create SSL context");
|
||||
|
||||
check_openssl(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
|
||||
"Unable to set SSL priorities");
|
||||
|
||||
// RFC 8261: SCTP performs segmentation and reassembly based on the path MTU.
|
||||
// Therefore, the DTLS layer MUST NOT use any compression algorithm.
|
||||
// See https://tools.ietf.org/html/rfc8261#section-5
|
||||
SSL_CTX_set_options(mCtx, SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
|
||||
SSL_CTX_set_min_proto_version(mCtx, DTLS1_VERSION);
|
||||
SSL_CTX_set_read_ahead(mCtx, 1);
|
||||
SSL_CTX_set_quiet_shutdown(mCtx, 1);
|
||||
SSL_CTX_set_info_callback(mCtx, InfoCallback);
|
||||
SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
CertificateCallback);
|
||||
SSL_CTX_set_verify_depth(mCtx, 1);
|
||||
|
||||
X509 *x509;
|
||||
EVP_PKEY *pkey;
|
||||
std::tie(x509, pkey) = mCertificate->credentials();
|
||||
SSL_CTX_use_certificate(mCtx, x509);
|
||||
SSL_CTX_use_PrivateKey(mCtx, pkey);
|
||||
|
||||
check_openssl(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed");
|
||||
|
||||
if (!(mSsl = SSL_new(mCtx)))
|
||||
throw std::runtime_error("Unable to create SSL instance");
|
||||
|
||||
SSL_set_ex_data(mSsl, TransportExIndex, this);
|
||||
SSL_set_mtu(mSsl, 1280 - 40 - 8); // min MTU over UDP/IPv6
|
||||
|
||||
if (lower->role() == Description::Role::Active)
|
||||
SSL_set_connect_state(mSsl);
|
||||
else
|
||||
SSL_set_accept_state(mSsl);
|
||||
|
||||
if (!(mInBio = BIO_new(BIO_s_mem())) || !(mOutBio = BIO_new(BIO_s_mem())))
|
||||
throw std::runtime_error("Unable to create BIO");
|
||||
|
||||
BIO_set_mem_eof_return(mInBio, BIO_EOF);
|
||||
BIO_set_mem_eof_return(mOutBio, BIO_EOF);
|
||||
SSL_set_bio(mSsl, mInBio, mOutBio);
|
||||
|
||||
auto ecdh = unique_ptr<EC_KEY, decltype(&EC_KEY_free)>(
|
||||
EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);
|
||||
SSL_set_options(mSsl, SSL_OP_SINGLE_ECDH_USE);
|
||||
SSL_set_tmp_ecdh(mSsl, ecdh.get());
|
||||
|
||||
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
||||
}
|
||||
|
||||
DtlsTransport::~DtlsTransport() {
|
||||
onRecv(nullptr); // unset recv callback
|
||||
|
||||
mIncomingQueue.stop();
|
||||
|
||||
if (mRecvThread.joinable())
|
||||
mRecvThread.join();
|
||||
|
||||
SSL_shutdown(mSsl);
|
||||
SSL_free(mSsl);
|
||||
SSL_CTX_free(mCtx);
|
||||
}
|
||||
|
||||
DtlsTransport::State DtlsTransport::state() const { return mState; }
|
||||
|
||||
bool DtlsTransport::send(message_ptr message) {
|
||||
const size_t bufferSize = 4096;
|
||||
byte buffer[bufferSize];
|
||||
|
||||
if (!message || mState != State::Connected)
|
||||
return false;
|
||||
|
||||
int ret = SSL_write(mSsl, message->data(), message->size());
|
||||
if (!check_openssl_ret(mSsl, ret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (BIO_ctrl_pending(mOutBio) > 0) {
|
||||
int ret = BIO_read(mOutBio, buffer, bufferSize);
|
||||
if (check_openssl_ret(mSsl, ret) && ret > 0)
|
||||
outgoing(make_message(buffer, buffer + ret));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DtlsTransport::incoming(message_ptr message) { mIncomingQueue.push(message); }
|
||||
|
||||
void DtlsTransport::changeState(State state) {
|
||||
if (mState.exchange(state) != state)
|
||||
mStateChangeCallback(state);
|
||||
}
|
||||
|
||||
void DtlsTransport::runRecvLoop() {
|
||||
const size_t bufferSize = 4096;
|
||||
byte buffer[bufferSize];
|
||||
|
||||
try {
|
||||
changeState(State::Connecting);
|
||||
|
||||
SSL_do_handshake(mSsl);
|
||||
while (BIO_ctrl_pending(mOutBio) > 0) {
|
||||
int ret = BIO_read(mOutBio, buffer, bufferSize);
|
||||
if (check_openssl_ret(mSsl, ret) && ret > 0)
|
||||
outgoing(make_message(buffer, buffer + ret));
|
||||
}
|
||||
|
||||
while (auto next = mIncomingQueue.pop()) {
|
||||
auto message = *next;
|
||||
BIO_write(mInBio, message->data(), message->size());
|
||||
int ret = SSL_read(mSsl, buffer, bufferSize);
|
||||
if (!check_openssl_ret(mSsl, ret))
|
||||
break;
|
||||
|
||||
auto decrypted = ret > 0 ? make_message(buffer, buffer + ret) : nullptr;
|
||||
|
||||
if (mState == State::Connecting) {
|
||||
if (unsigned long err = ERR_get_error())
|
||||
throw std::runtime_error("handshake failed: " + openssl_error_string(err));
|
||||
|
||||
while (BIO_ctrl_pending(mOutBio) > 0) {
|
||||
ret = BIO_read(mOutBio, buffer, bufferSize);
|
||||
if (check_openssl_ret(mSsl, ret) && ret > 0)
|
||||
outgoing(make_message(buffer, buffer + ret));
|
||||
}
|
||||
|
||||
if (SSL_is_init_finished(mSsl))
|
||||
changeState(State::Connected);
|
||||
}
|
||||
|
||||
if (decrypted)
|
||||
recv(decrypted);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "DTLS recv: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
if (mState == State::Connected) {
|
||||
changeState(State::Disconnected);
|
||||
recv(nullptr);
|
||||
} else {
|
||||
changeState(State::Failed);
|
||||
}
|
||||
}
|
||||
|
||||
int DtlsTransport::CertificateCallback(int preverify_ok, X509_STORE_CTX *ctx) {
|
||||
SSL *ssl =
|
||||
static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
|
||||
DtlsTransport *t =
|
||||
static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
|
||||
|
||||
X509 *crt = X509_STORE_CTX_get_current_cert(ctx);
|
||||
std::string fingerprint = make_fingerprint(crt);
|
||||
|
||||
return t->mVerifierCallback(fingerprint) ? 1 : 0;
|
||||
}
|
||||
|
||||
void DtlsTransport::InfoCallback(const SSL *ssl, int where, int ret) {
|
||||
DtlsTransport *t =
|
||||
static_cast<DtlsTransport *>(SSL_get_ex_data(ssl, DtlsTransport::TransportExIndex));
|
||||
|
||||
if (where & SSL_CB_ALERT) {
|
||||
if (ret != 256) // Close Notify
|
||||
std::cerr << "DTLS alert: " << SSL_alert_desc_string_long(ret) << std::endl;
|
||||
t->mIncomingQueue.stop(); // Close the connection
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -28,9 +28,14 @@
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#if USE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#else
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
namespace rtc {
|
||||
|
||||
@ -58,7 +63,6 @@ private:
|
||||
|
||||
const std::shared_ptr<Certificate> mCertificate;
|
||||
|
||||
gnutls_session_t mSession;
|
||||
Queue<message_ptr> mIncomingQueue;
|
||||
std::atomic<State> mState;
|
||||
std::thread mRecvThread;
|
||||
@ -66,10 +70,25 @@ private:
|
||||
verifier_callback mVerifierCallback;
|
||||
state_callback mStateChangeCallback;
|
||||
|
||||
#if USE_GNUTLS
|
||||
gnutls_session_t mSession;
|
||||
|
||||
static int CertificateCallback(gnutls_session_t session);
|
||||
static ssize_t WriteCallback(gnutls_transport_ptr_t ptr, const void *data, size_t len);
|
||||
static ssize_t ReadCallback(gnutls_transport_ptr_t ptr, void *data, size_t maxlen);
|
||||
static int TimeoutCallback(gnutls_transport_ptr_t ptr, unsigned int ms);
|
||||
#else
|
||||
SSL_CTX *mCtx;
|
||||
SSL *mSsl;
|
||||
BIO *mInBio, *mOutBio;
|
||||
|
||||
static int TransportExIndex;
|
||||
static std::mutex GlobalMutex;
|
||||
|
||||
static void GlobalInit();
|
||||
static int CertificateCallback(int preverify_ok, X509_STORE_CTX *ctx);
|
||||
static void InfoCallback(const SSL *ssl, int where, int ret);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
@ -132,7 +132,8 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
||||
|
||||
IceTransport::~IceTransport() {
|
||||
g_main_loop_quit(mMainLoop.get());
|
||||
mMainLoopThread.join();
|
||||
if (mMainLoopThread.joinable())
|
||||
mMainLoopThread.join();
|
||||
}
|
||||
|
||||
Description::Role IceTransport::role() const { return mRole; }
|
||||
|
@ -130,7 +130,8 @@ SctpTransport::~SctpTransport() {
|
||||
usrsctp_close(mSock);
|
||||
}
|
||||
|
||||
mSendThread.join();
|
||||
if (mSendThread.joinable())
|
||||
mSendThread.join();
|
||||
|
||||
usrsctp_deregister_address(this);
|
||||
GlobalCleanup();
|
||||
|
Reference in New Issue
Block a user