dtls环境自动初始化,支持多线程

This commit is contained in:
ziyue 2021-03-27 10:03:19 +08:00
parent 0d4cc2fc65
commit d0a1c72fda
3 changed files with 84 additions and 81 deletions

View File

@ -10,6 +10,7 @@
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <cstdio> // std::sprintf(), std::fopen() #include <cstdio> // std::sprintf(), std::fopen()
#include <cstring> // std::memcpy(), std::strcmp() #include <cstring> // std::memcpy(), std::strcmp()
#include "Util/util.h"
#define LOG_OPENSSL_ERROR(desc) \ #define LOG_OPENSSL_ERROR(desc) \
do \ do \
@ -53,7 +54,6 @@ namespace RTC
// clang-format off // clang-format off
static constexpr int DtlsMtu{ 1350 }; static constexpr int DtlsMtu{ 1350 };
static constexpr int SslReadBufferSize{ 65536 };
// AES-HMAC: http://tools.ietf.org/html/rfc3711 // AES-HMAC: http://tools.ietf.org/html/rfc3711
static constexpr size_t SrtpMasterKeyLength{ 16 }; static constexpr size_t SrtpMasterKeyLength{ 16 };
static constexpr size_t SrtpMasterSaltLength{ 14 }; static constexpr size_t SrtpMasterSaltLength{ 14 };
@ -68,11 +68,6 @@ namespace RTC
// clang-format on // clang-format on
/* Class variables. */ /* Class variables. */
X509* DtlsTransport::certificate{ nullptr };
EVP_PKEY* DtlsTransport::privateKey{ nullptr };
SSL_CTX* DtlsTransport::sslCtx{ nullptr };
uint8_t DtlsTransport::sslReadBuffer[SslReadBufferSize];
// clang-format off // clang-format off
std::map<std::string, DtlsTransport::FingerprintAlgorithm> DtlsTransport::string2FingerprintAlgorithm = std::map<std::string, DtlsTransport::FingerprintAlgorithm> DtlsTransport::string2FingerprintAlgorithm =
{ {
@ -96,7 +91,6 @@ namespace RTC
{ "client", DtlsTransport::Role::CLIENT }, { "client", DtlsTransport::Role::CLIENT },
{ "server", DtlsTransport::Role::SERVER } { "server", DtlsTransport::Role::SERVER }
}; };
std::vector<DtlsTransport::Fingerprint> DtlsTransport::localFingerprints;
std::vector<DtlsTransport::SrtpCryptoSuiteMapEntry> DtlsTransport::srtpCryptoSuites = std::vector<DtlsTransport::SrtpCryptoSuiteMapEntry> DtlsTransport::srtpCryptoSuites =
{ {
{ RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM, "SRTP_AEAD_AES_256_GCM" }, { RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM, "SRTP_AEAD_AES_256_GCM" },
@ -106,13 +100,14 @@ namespace RTC
}; };
// clang-format on // clang-format on
INSTANCE_IMP(DtlsTransport::DtlsEnvironment);
/* Class methods. */ /* Class methods. */
void DtlsTransport::ClassInit() DtlsTransport::DtlsEnvironment::DtlsEnvironment()
{ {
MS_TRACE(); MS_TRACE();
// Generate a X509 certificate and private key (unless PEM files are provided). // Generate a X509 certificate and private key (unless PEM files are provided).
if (true /* if (true /*
Settings::configuration.dtlsCertificateFile.empty() || Settings::configuration.dtlsCertificateFile.empty() ||
@ -132,19 +127,19 @@ namespace RTC
GenerateFingerprints(); GenerateFingerprints();
} }
void DtlsTransport::ClassDestroy() DtlsTransport::DtlsEnvironment::~DtlsEnvironment()
{ {
MS_TRACE(); MS_TRACE();
if (DtlsTransport::privateKey) if (privateKey)
EVP_PKEY_free(DtlsTransport::privateKey); EVP_PKEY_free(privateKey);
if (DtlsTransport::certificate) if (certificate)
X509_free(DtlsTransport::certificate); X509_free(certificate);
if (DtlsTransport::sslCtx) if (sslCtx)
SSL_CTX_free(DtlsTransport::sslCtx); SSL_CTX_free(sslCtx);
} }
void DtlsTransport::GenerateCertificateAndPrivateKey() void DtlsTransport::DtlsEnvironment::GenerateCertificateAndPrivateKey()
{ {
MS_TRACE(); MS_TRACE();
@ -177,9 +172,9 @@ namespace RTC
} }
// Create a private key object. // Create a private key object.
DtlsTransport::privateKey = EVP_PKEY_new(); privateKey = EVP_PKEY_new();
if (!DtlsTransport::privateKey) if (!privateKey)
{ {
LOG_OPENSSL_ERROR("EVP_PKEY_new() failed"); LOG_OPENSSL_ERROR("EVP_PKEY_new() failed");
@ -187,7 +182,7 @@ namespace RTC
} }
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
ret = EVP_PKEY_assign_EC_KEY(DtlsTransport::privateKey, ecKey); ret = EVP_PKEY_assign_EC_KEY(privateKey, ecKey);
if (ret == 0) if (ret == 0)
{ {
@ -200,9 +195,9 @@ namespace RTC
ecKey = nullptr; ecKey = nullptr;
// Create the X509 certificate. // Create the X509 certificate.
DtlsTransport::certificate = X509_new(); certificate = X509_new();
if (!DtlsTransport::certificate) if (!certificate)
{ {
LOG_OPENSSL_ERROR("X509_new() failed"); LOG_OPENSSL_ERROR("X509_new() failed");
@ -210,19 +205,19 @@ namespace RTC
} }
// Set version 3 (note that 0 means version 1). // Set version 3 (note that 0 means version 1).
X509_set_version(DtlsTransport::certificate, 2); X509_set_version(certificate, 2);
// Set serial number (avoid default 0). // Set serial number (avoid default 0).
ASN1_INTEGER_set( ASN1_INTEGER_set(
X509_get_serialNumber(DtlsTransport::certificate), X509_get_serialNumber(certificate),
static_cast<uint64_t>(rand() % 999999 + 100000)); static_cast<uint64_t>(rand() % 999999 + 100000));
// Set valid period. // Set valid period.
X509_gmtime_adj(X509_get_notBefore(DtlsTransport::certificate), -315360000); // -10 years. X509_gmtime_adj(X509_get_notBefore(certificate), -315360000); // -10 years.
X509_gmtime_adj(X509_get_notAfter(DtlsTransport::certificate), 315360000); // 10 years. X509_gmtime_adj(X509_get_notAfter(certificate), 315360000); // 10 years.
// Set the public key for the certificate using the key. // Set the public key for the certificate using the key.
ret = X509_set_pubkey(DtlsTransport::certificate, DtlsTransport::privateKey); ret = X509_set_pubkey(certificate, privateKey);
if (ret == 0) if (ret == 0)
{ {
@ -232,7 +227,7 @@ namespace RTC
} }
// Set certificate fields. // Set certificate fields.
certName = X509_get_subject_name(DtlsTransport::certificate); certName = X509_get_subject_name(certificate);
if (!certName) if (!certName)
{ {
@ -247,7 +242,7 @@ namespace RTC
certName, "CN", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0); certName, "CN", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0);
// It is self-signed so set the issuer name to be the same as the subject. // It is self-signed so set the issuer name to be the same as the subject.
ret = X509_set_issuer_name(DtlsTransport::certificate, certName); ret = X509_set_issuer_name(certificate, certName);
if (ret == 0) if (ret == 0)
{ {
@ -257,7 +252,7 @@ namespace RTC
} }
// Sign the certificate with its own private key. // Sign the certificate with its own private key.
ret = X509_sign(DtlsTransport::certificate, DtlsTransport::privateKey, EVP_sha1()); ret = X509_sign(certificate, privateKey, EVP_sha1());
if (ret == 0) if (ret == 0)
{ {
@ -273,16 +268,16 @@ namespace RTC
if (ecKey) if (ecKey)
EC_KEY_free(ecKey); EC_KEY_free(ecKey);
if (DtlsTransport::privateKey) if (privateKey)
EVP_PKEY_free(DtlsTransport::privateKey); // NOTE: This also frees the EC key. EVP_PKEY_free(privateKey); // NOTE: This also frees the EC key.
if (DtlsTransport::certificate) if (certificate)
X509_free(DtlsTransport::certificate); X509_free(certificate);
MS_THROW_ERROR("DTLS certificate and private key generation failed"); MS_THROW_ERROR("DTLS certificate and private key generation failed");
} }
void DtlsTransport::ReadCertificateAndPrivateKeyFromFiles() void DtlsTransport::DtlsEnvironment::ReadCertificateAndPrivateKeyFromFiles()
{ {
#if 0 #if 0
MS_TRACE(); MS_TRACE();
@ -298,9 +293,9 @@ namespace RTC
goto error; goto error;
} }
DtlsTransport::certificate = PEM_read_X509(file, nullptr, nullptr, nullptr); certificate = PEM_read_X509(file, nullptr, nullptr, nullptr);
if (!DtlsTransport::certificate) if (!certificate)
{ {
LOG_OPENSSL_ERROR("PEM_read_X509() failed"); LOG_OPENSSL_ERROR("PEM_read_X509() failed");
@ -318,9 +313,9 @@ namespace RTC
goto error; goto error;
} }
DtlsTransport::privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr); privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr);
if (!DtlsTransport::privateKey) if (!privateKey)
{ {
LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed"); LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed");
@ -337,7 +332,7 @@ namespace RTC
#endif #endif
} }
void DtlsTransport::CreateSslCtx() void DtlsTransport::DtlsEnvironment::CreateSslCtx()
{ {
MS_TRACE(); MS_TRACE();
@ -347,16 +342,16 @@ namespace RTC
/* Set the global DTLS context. */ /* Set the global DTLS context. */
// Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0). // Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0).
DtlsTransport::sslCtx = SSL_CTX_new(DTLS_method()); sslCtx = SSL_CTX_new(DTLS_method());
if (!DtlsTransport::sslCtx) if (!sslCtx)
{ {
LOG_OPENSSL_ERROR("SSL_CTX_new() failed"); LOG_OPENSSL_ERROR("SSL_CTX_new() failed");
goto error; goto error;
} }
ret = SSL_CTX_use_certificate(DtlsTransport::sslCtx, DtlsTransport::certificate); ret = SSL_CTX_use_certificate(sslCtx, certificate);
if (ret == 0) if (ret == 0)
{ {
@ -365,7 +360,7 @@ namespace RTC
goto error; goto error;
} }
ret = SSL_CTX_use_PrivateKey(DtlsTransport::sslCtx, DtlsTransport::privateKey); ret = SSL_CTX_use_PrivateKey(sslCtx, privateKey);
if (ret == 0) if (ret == 0)
{ {
@ -374,7 +369,7 @@ namespace RTC
goto error; goto error;
} }
ret = SSL_CTX_check_private_key(DtlsTransport::sslCtx); ret = SSL_CTX_check_private_key(sslCtx);
if (ret == 0) if (ret == 0)
{ {
@ -385,31 +380,31 @@ namespace RTC
// Set options. // Set options.
SSL_CTX_set_options( SSL_CTX_set_options(
DtlsTransport::sslCtx, sslCtx,
SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE |
SSL_OP_NO_QUERY_MTU); SSL_OP_NO_QUERY_MTU);
// Don't use sessions cache. // Don't use sessions cache.
SSL_CTX_set_session_cache_mode(DtlsTransport::sslCtx, SSL_SESS_CACHE_OFF); SSL_CTX_set_session_cache_mode(sslCtx, SSL_SESS_CACHE_OFF);
// Read always as much into the buffer as possible. // Read always as much into the buffer as possible.
// NOTE: This is the default for DTLS, but a bug in non latest OpenSSL // NOTE: This is the default for DTLS, but a bug in non latest OpenSSL
// versions makes this call required. // versions makes this call required.
SSL_CTX_set_read_ahead(DtlsTransport::sslCtx, 1); SSL_CTX_set_read_ahead(sslCtx, 1);
SSL_CTX_set_verify_depth(DtlsTransport::sslCtx, 4); SSL_CTX_set_verify_depth(sslCtx, 4);
// Require certificate from peer. // Require certificate from peer.
SSL_CTX_set_verify( SSL_CTX_set_verify(
DtlsTransport::sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, onSslCertificateVerify); sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, onSslCertificateVerify);
// Set SSL info callback. // Set SSL info callback.
SSL_CTX_set_info_callback(DtlsTransport::sslCtx, [](const SSL* ssl, int where, int ret){ SSL_CTX_set_info_callback(sslCtx, [](const SSL* ssl, int where, int ret){
static_cast<RTC::DtlsTransport*>(SSL_get_ex_data(ssl, 0))->OnSslInfo(where, ret); static_cast<RTC::DtlsTransport*>(SSL_get_ex_data(ssl, 0))->OnSslInfo(where, ret);
}); });
// Set ciphers. // Set ciphers.
ret = SSL_CTX_set_cipher_list( ret = SSL_CTX_set_cipher_list(
DtlsTransport::sslCtx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK"); sslCtx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK");
if (ret == 0) if (ret == 0)
{ {
@ -424,7 +419,7 @@ namespace RTC
// NOTE: https://bugs.ruby-lang.org/issues/12324 // NOTE: https://bugs.ruby-lang.org/issues/12324
// For OpenSSL >= 1.0.2. // For OpenSSL >= 1.0.2.
SSL_CTX_set_ecdh_auto(DtlsTransport::sslCtx, 1); SSL_CTX_set_ecdh_auto(sslCtx, 1);
// Set the "use_srtp" DTLS extension. // Set the "use_srtp" DTLS extension.
for (auto it = DtlsTransport::srtpCryptoSuites.begin(); for (auto it = DtlsTransport::srtpCryptoSuites.begin();
@ -441,7 +436,7 @@ namespace RTC
MS_DEBUG_2TAGS(dtls, srtp, "setting SRTP cryptoSuites for DTLS: %s", dtlsSrtpCryptoSuites.c_str()); MS_DEBUG_2TAGS(dtls, srtp, "setting SRTP cryptoSuites for DTLS: %s", dtlsSrtpCryptoSuites.c_str());
// NOTE: This function returns 0 on success. // NOTE: This function returns 0 on success.
ret = SSL_CTX_set_tlsext_use_srtp(DtlsTransport::sslCtx, dtlsSrtpCryptoSuites.c_str()); ret = SSL_CTX_set_tlsext_use_srtp(sslCtx, dtlsSrtpCryptoSuites.c_str());
if (ret != 0) if (ret != 0)
{ {
@ -456,16 +451,16 @@ namespace RTC
error: error:
if (DtlsTransport::sslCtx) if (sslCtx)
{ {
SSL_CTX_free(DtlsTransport::sslCtx); SSL_CTX_free(sslCtx);
DtlsTransport::sslCtx = nullptr; sslCtx = nullptr;
} }
MS_THROW_ERROR("SSL context creation failed"); MS_THROW_ERROR("SSL context creation failed");
} }
void DtlsTransport::GenerateFingerprints() void DtlsTransport::DtlsEnvironment::GenerateFingerprints()
{ {
MS_TRACE(); MS_TRACE();
@ -505,7 +500,7 @@ namespace RTC
MS_THROW_ERROR("unknown algorithm"); MS_THROW_ERROR("unknown algorithm");
} }
ret = X509_digest(DtlsTransport::certificate, hashFunction, binaryFingerprint, &size); ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);
if (ret == 0) if (ret == 0)
{ {
@ -528,7 +523,7 @@ namespace RTC
fingerprint.algorithm = DtlsTransport::GetFingerprintAlgorithm(algorithmString); fingerprint.algorithm = DtlsTransport::GetFingerprintAlgorithm(algorithmString);
fingerprint.value = hexFingerprint; fingerprint.value = hexFingerprint;
DtlsTransport::localFingerprints.push_back(fingerprint); localFingerprints.push_back(fingerprint);
} }
} }
@ -537,10 +532,11 @@ namespace RTC
DtlsTransport::DtlsTransport(EventPoller::Ptr poller,Listener* listener) : poller(std::move(poller)), listener(listener) DtlsTransport::DtlsTransport(EventPoller::Ptr poller,Listener* listener) : poller(std::move(poller)), listener(listener)
{ {
MS_TRACE(); MS_TRACE();
env = DtlsEnvironment::Instance().shared_from_this();
/* Set SSL. */ /* Set SSL. */
this->ssl = SSL_new(DtlsTransport::sslCtx); this->ssl = SSL_new(env->sslCtx);
if (!this->ssl) if (!this->ssl)
{ {

View File

@ -60,6 +60,27 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
const char* name; const char* name;
}; };
class DtlsEnvironment : public std::enable_shared_from_this<DtlsEnvironment>
{
public:
using Ptr = std::shared_ptr<DtlsEnvironment>;
~DtlsEnvironment();
static DtlsEnvironment& Instance();
private:
DtlsEnvironment();
void GenerateCertificateAndPrivateKey();
void ReadCertificateAndPrivateKeyFromFiles();
void CreateSslCtx();
void GenerateFingerprints();
public:
X509* certificate{ nullptr };
EVP_PKEY* privateKey{ nullptr };
SSL_CTX* sslCtx{ nullptr };
std::vector<Fingerprint> localFingerprints;
};
public: public:
class Listener class Listener
{ {
@ -93,8 +114,6 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
}; };
public: public:
static void ClassInit();
static void ClassDestroy();
static Role StringToRole(const std::string& role) static Role StringToRole(const std::string& role)
{ {
auto it = DtlsTransport::string2Role.find(role); auto it = DtlsTransport::string2Role.find(role);
@ -132,20 +151,9 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
} }
private: private:
static void GenerateCertificateAndPrivateKey();
static void ReadCertificateAndPrivateKeyFromFiles();
static void CreateSslCtx();
static void GenerateFingerprints();
private:
static X509* certificate;
static EVP_PKEY* privateKey;
static SSL_CTX* sslCtx;
static uint8_t sslReadBuffer[];
static std::map<std::string, Role> string2Role; static std::map<std::string, Role> string2Role;
static std::map<std::string, FingerprintAlgorithm> string2FingerprintAlgorithm; static std::map<std::string, FingerprintAlgorithm> string2FingerprintAlgorithm;
static std::map<FingerprintAlgorithm, std::string> fingerprintAlgorithm2String; static std::map<FingerprintAlgorithm, std::string> fingerprintAlgorithm2String;
static std::vector<Fingerprint> localFingerprints;
static std::vector<SrtpCryptoSuiteMapEntry> srtpCryptoSuites; static std::vector<SrtpCryptoSuiteMapEntry> srtpCryptoSuites;
public: public:
@ -157,7 +165,7 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
void Run(Role localRole); void Run(Role localRole);
std::vector<Fingerprint>& GetLocalFingerprints() const std::vector<Fingerprint>& GetLocalFingerprints() const
{ {
return DtlsTransport::localFingerprints; return env->localFingerprints;
} }
bool SetRemoteFingerprint(Fingerprint fingerprint); bool SetRemoteFingerprint(Fingerprint fingerprint);
void ProcessDtlsData(const uint8_t* data, size_t len); void ProcessDtlsData(const uint8_t* data, size_t len);
@ -203,6 +211,7 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
void OnTimer(); void OnTimer();
private: private:
DtlsEnvironment::Ptr env;
EventPoller::Ptr poller; EventPoller::Ptr poller;
// Passed by argument. // Passed by argument.
Listener* listener{ nullptr }; Listener* listener{ nullptr };
@ -218,6 +227,8 @@ class DtlsTransport : public std::enable_shared_from_this<DtlsTransport>
bool handshakeDone{ false }; bool handshakeDone{ false };
bool handshakeDoneNow{ false }; bool handshakeDoneNow{ false };
std::string remoteCert; std::string remoteCert;
static constexpr int SslReadBufferSize{ 65536 };
uint8_t sslReadBuffer[SslReadBufferSize];
}; };
} // namespace RTC } // namespace RTC

View File

@ -3,10 +3,6 @@
#include "Rtcp/Rtcp.h" #include "Rtcp/Rtcp.h"
WebRtcTransport::WebRtcTransport() { WebRtcTransport::WebRtcTransport() {
static onceToken token([](){
RTC::DtlsTransport::ClassInit();
});
dtls_transport_ = std::make_shared<RTC::DtlsTransport>(EventPollerPool::Instance().getFirstPoller(), this); dtls_transport_ = std::make_shared<RTC::DtlsTransport>(EventPollerPool::Instance().getFirstPoller(), this);
ice_server_ = std::make_shared<RTC::IceServer>(this, makeRandStr(4), makeRandStr(24)); ice_server_ = std::make_shared<RTC::IceServer>(this, makeRandStr(4), makeRandStr(24));
} }