mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 07:35:30 +00:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
c675aedb83 | |||
e32d139056 | |||
a790161168 | |||
2697ef0d76 | |||
5044aedbec | |||
44c90c1cb4 | |||
be79c68540 | |||
226a927df1 | |||
4e1b9bb3c2 | |||
8bc016cc08 | |||
5afbe10d01 | |||
8df07ca68d | |||
884bd2316e | |||
dadecce709 | |||
103935bdd5 | |||
62e6954949 | |||
3ac2d155cc | |||
6d8788c2a1 | |||
603dd01b87 | |||
8091508428 | |||
b38f63f077 | |||
d87539937e | |||
eb09cadded | |||
79e0c62321 | |||
ef38777129 | |||
313f081061 | |||
6108b05e0d | |||
f68601b45f |
@ -1,12 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(libdatachannel
|
||||
DESCRIPTION "WebRTC DataChannels Library"
|
||||
VERSION 0.6.3
|
||||
DESCRIPTION "WebRTC Data Channels Library"
|
||||
VERSION 0.6.4
|
||||
LANGUAGES CXX)
|
||||
|
||||
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
||||
option(USE_JUICE "Use libjuice instead of libnice" OFF)
|
||||
option(RTC_ENABLE_WEBSOCKET "Build WebSocket support" ON)
|
||||
option(USE_SRTP "Enable SRTP for media support" OFF)
|
||||
option(NO_WEBSOCKET "Disable WebSocket support" OFF)
|
||||
|
||||
if(USE_GNUTLS)
|
||||
option(USE_NETTLE "Use Nettle instead of OpenSSL in libjuice" ON)
|
||||
@ -82,7 +83,6 @@ set(TESTS_SOURCES
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(SRTP)
|
||||
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
|
||||
add_subdirectory(deps/plog)
|
||||
@ -99,7 +99,14 @@ endif()
|
||||
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
|
||||
add_library(Usrsctp::UsrsctpStatic ALIAS usrsctp-static)
|
||||
|
||||
if (RTC_ENABLE_WEBSOCKET)
|
||||
if (NO_WEBSOCKET)
|
||||
add_library(datachannel SHARED
|
||||
${LIBDATACHANNEL_SOURCES})
|
||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
|
||||
${LIBDATACHANNEL_SOURCES})
|
||||
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
|
||||
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
|
||||
else()
|
||||
add_library(datachannel SHARED
|
||||
${LIBDATACHANNEL_SOURCES}
|
||||
${LIBDATACHANNEL_WEBSOCKET_SOURCES})
|
||||
@ -108,13 +115,6 @@ if (RTC_ENABLE_WEBSOCKET)
|
||||
${LIBDATACHANNEL_WEBSOCKET_SOURCES})
|
||||
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
|
||||
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
|
||||
else()
|
||||
add_library(datachannel SHARED
|
||||
${LIBDATACHANNEL_SOURCES})
|
||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
|
||||
${LIBDATACHANNEL_SOURCES})
|
||||
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
|
||||
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
|
||||
endif()
|
||||
|
||||
set_target_properties(datachannel PROPERTIES
|
||||
@ -141,7 +141,8 @@ if(WIN32)
|
||||
target_link_libraries(datachannel-static PRIVATE wsock32 ws2_32) # winsock2
|
||||
endif()
|
||||
|
||||
if(SRTP_FOUND)
|
||||
if(USE_SRTP)
|
||||
find_package(SRTP REQUIRED)
|
||||
if(NOT TARGET SRTP::SRTP)
|
||||
add_library(SRTP::SRTP UNKNOWN IMPORTED)
|
||||
set_target_properties(SRTP::SRTP PROPERTIES
|
||||
|
2
Jamfile
2
Jamfile
@ -25,7 +25,7 @@ lib libdatachannel
|
||||
<library>/libdatachannel//usrsctp
|
||||
<library>/libdatachannel//juice
|
||||
<library>/libdatachannel//plog
|
||||
<gnutls>on:<library>gnutls
|
||||
<gnutls>on:<library>gnutls/<link>shared
|
||||
<gnutls>off:<library>ssl
|
||||
<gnutls>off:<library>crypto
|
||||
: # default build
|
||||
|
10
Makefile
10
Makefile
@ -38,22 +38,22 @@ else
|
||||
LIBS+=glib-2.0 gobject-2.0 nice
|
||||
endif
|
||||
|
||||
RTC_ENABLE_MEDIA ?= 0
|
||||
ifneq ($(RTC_ENABLE_MEDIA), 0)
|
||||
USE_SRTP ?= 0
|
||||
ifneq ($(USE_SRTP), 0)
|
||||
CPPFLAGS+=-DRTC_ENABLE_MEDIA=1
|
||||
LIBS+=srtp
|
||||
else
|
||||
CPPFLAGS+=-DRTC_ENABLE_MEDIA=0
|
||||
endif
|
||||
|
||||
RTC_ENABLE_WEBSOCKET ?= 1
|
||||
ifneq ($(RTC_ENABLE_WEBSOCKET), 0)
|
||||
|
||||
NO_WEBSOCKET ?= 0
|
||||
ifeq ($(NO_WEBSOCKET), 0)
|
||||
CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=1
|
||||
else
|
||||
CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=0
|
||||
endif
|
||||
|
||||
|
||||
INCLUDES+=$(shell pkg-config --cflags $(LIBS))
|
||||
LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS))
|
||||
|
||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: c6566d3c6f...92a2ed7d44
@ -49,6 +49,7 @@ public:
|
||||
bool ended() const;
|
||||
|
||||
void hintType(Type type);
|
||||
void setDataMid(string mid);
|
||||
void setFingerprint(string fingerprint);
|
||||
void setSctpPort(uint16_t port);
|
||||
void setMaxMessageSize(size_t size);
|
||||
|
@ -101,7 +101,7 @@ public:
|
||||
// Media
|
||||
bool hasMedia() const;
|
||||
void sendMedia(const binary &packet);
|
||||
void send(const byte *packet, size_t size);
|
||||
void sendMedia(const byte *packet, size_t size);
|
||||
|
||||
void onMedia(std::function<void(const binary &packet)> callback);
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -107,8 +108,9 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
|
||||
oss << left;
|
||||
mCandidate = oss.str();
|
||||
mIsResolved = true;
|
||||
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
|
||||
return mIsResolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +118,7 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
return false;
|
||||
return mIsResolved;
|
||||
}
|
||||
|
||||
bool Candidate::isResolved() const { return mIsResolved; }
|
||||
|
@ -163,6 +163,8 @@ void Description::hintType(Type type) {
|
||||
}
|
||||
}
|
||||
|
||||
void Description::setDataMid(string mid) { mData.mid = mid; }
|
||||
|
||||
void Description::setFingerprint(string fingerprint) {
|
||||
mFingerprint.emplace(std::move(fingerprint));
|
||||
}
|
||||
@ -216,17 +218,7 @@ string Description::generateSdp(const string &eol) const {
|
||||
sdp << " " << m.first; // mid
|
||||
sdp << " " << mData.mid << eol;
|
||||
|
||||
// Data
|
||||
const string dataDescription = "UDP/DTLS/SCTP webrtc-datachannel";
|
||||
sdp << "m=application" << ' ' << (!mMedia.empty() ? 0 : 9) << ' ' << dataDescription << eol;
|
||||
sdp << "c=IN IP4 0.0.0.0" << eol;
|
||||
if (!mMedia.empty())
|
||||
sdp << "a=bundle-only" << eol;
|
||||
sdp << "a=mid:" << mData.mid << eol;
|
||||
if (mData.sctpPort)
|
||||
sdp << "a=sctp-port:" << *mData.sctpPort << eol;
|
||||
if (mData.maxMessageSize)
|
||||
sdp << "a=max-message-size:" << *mData.maxMessageSize << eol;
|
||||
sdp << "a=msid-semantic: WMS" << eol;
|
||||
|
||||
// Non-data media
|
||||
if (!mMedia.empty()) {
|
||||
@ -248,12 +240,28 @@ string Description::generateSdp(const string &eol) const {
|
||||
}
|
||||
}
|
||||
|
||||
// Data
|
||||
const string dataDescription = "UDP/DTLS/SCTP webrtc-datachannel";
|
||||
sdp << "m=application" << ' ' << (!mMedia.empty() ? 0 : 9) << ' ' << dataDescription << eol;
|
||||
sdp << "c=IN IP4 0.0.0.0" << eol;
|
||||
if (!mMedia.empty())
|
||||
sdp << "a=bundle-only" << eol;
|
||||
sdp << "a=mid:" << mData.mid << eol;
|
||||
if (mData.sctpPort)
|
||||
sdp << "a=sctp-port:" << *mData.sctpPort << eol;
|
||||
if (mData.maxMessageSize)
|
||||
sdp << "a=max-message-size:" << *mData.maxMessageSize << eol;
|
||||
|
||||
|
||||
// Common
|
||||
sdp << "a=ice-options:trickle" << eol;
|
||||
if (!mEnded)
|
||||
sdp << "a=ice-options:trickle" << eol;
|
||||
|
||||
sdp << "a=ice-ufrag:" << mIceUfrag << eol;
|
||||
sdp << "a=ice-pwd:" << mIcePwd << eol;
|
||||
sdp << "a=setup:" << roleToString(mRole) << eol;
|
||||
sdp << "a=tls-id:1" << eol;
|
||||
|
||||
if (mFingerprint)
|
||||
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
||||
|
||||
|
@ -43,50 +43,86 @@ DtlsSrtpTransport::DtlsSrtpTransport(std::shared_ptr<IceTransport> lower,
|
||||
std::move(stateChangeCallback)),
|
||||
mSrtpRecvCallback(std::move(srtpRecvCallback)) { // distinct from Transport recv callback
|
||||
|
||||
PLOG_DEBUG << "Initializing SRTP transport";
|
||||
PLOG_DEBUG << "Initializing DTLS-SRTP transport";
|
||||
|
||||
#if USE_GNUTLS
|
||||
PLOG_DEBUG << "Initializing DTLS-SRTP transport (GnuTLS)";
|
||||
gnutls::check(gnutls_srtp_set_profile(mSession, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80),
|
||||
"Failed to set SRTP profile");
|
||||
#else
|
||||
PLOG_DEBUG << "Initializing DTLS-SRTP transport (OpenSSL)";
|
||||
openssl::check(SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"),
|
||||
"Failed to set SRTP profile");
|
||||
#endif
|
||||
if (srtp_err_status_t err = srtp_create(&mSrtpIn, nullptr)) {
|
||||
throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err)));
|
||||
}
|
||||
if (srtp_err_status_t err = srtp_create(&mSrtpOut, nullptr)) {
|
||||
srtp_dealloc(mSrtpIn);
|
||||
throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err)));
|
||||
}
|
||||
}
|
||||
|
||||
DtlsSrtpTransport::~DtlsSrtpTransport() {
|
||||
stop();
|
||||
|
||||
if (mCreated)
|
||||
srtp_dealloc(mSrtp);
|
||||
srtp_dealloc(mSrtpIn);
|
||||
srtp_dealloc(mSrtpOut);
|
||||
}
|
||||
|
||||
bool DtlsSrtpTransport::send(message_ptr message) {
|
||||
bool DtlsSrtpTransport::sendMedia(message_ptr message) {
|
||||
if (!message)
|
||||
return false;
|
||||
|
||||
if (!mInitDone) {
|
||||
PLOG_WARNING << "SRTP media sent before keys are derived";
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = message->size();
|
||||
PLOG_VERBOSE << "Send size=" << size;
|
||||
|
||||
// srtp_protect() assumes that it can write SRTP_MAX_TRAILER_LEN (for the authentication tag)
|
||||
// into the location in memory immediately following the RTP packet.
|
||||
// The RTP header has a minimum size of 12 bytes
|
||||
if (size < 12)
|
||||
throw std::runtime_error("RTP/RTCP packet too short");
|
||||
|
||||
// srtp_protect() and srtp_protect_rtcp() assume that they can write SRTP_MAX_TRAILER_LEN (for
|
||||
// the authentication tag) into the location in memory immediately following the RTP packet.
|
||||
message->resize(size + SRTP_MAX_TRAILER_LEN);
|
||||
if (srtp_err_status_t err = srtp_protect(mSrtp, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
throw std::runtime_error("SRTP packet is a replay");
|
||||
else
|
||||
throw std::runtime_error("SRTP protect error, status=" +
|
||||
to_string(static_cast<int>(err)));
|
||||
|
||||
uint8_t value2 = to_integer<uint8_t>(*(message->begin() + 1)) & 0x7F;
|
||||
PLOG_VERBOSE << "Demultiplexing SRTCP and SRTP with RTP payload type, value="
|
||||
<< unsigned(value2);
|
||||
|
||||
// RFC 5761 Multiplexing RTP and RTCP 4. Distinguishable RTP and RTCP Packets
|
||||
// It is RECOMMENDED to follow the guidelines in the RTP/AVP profile for the choice of RTP
|
||||
// payload type values, with the additional restriction that payload type values in the
|
||||
// range 64-95 MUST NOT be used. Specifically, dynamic RTP payload types SHOULD be chosen in
|
||||
// the range 96-127 where possible. Values below 64 MAY be used if that is insufficient
|
||||
// [...]
|
||||
if (value2 >= 64 && value2 <= 95) { // Range 64-95 (inclusive) MUST be RTCP
|
||||
if (srtp_err_status_t err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
throw std::runtime_error("SRTCP packet is a replay");
|
||||
else
|
||||
throw std::runtime_error("SRTCP protect error, status=" +
|
||||
to_string(static_cast<int>(err)));
|
||||
}
|
||||
PLOG_VERBOSE << "Protected SRTCP packet, size=" << size;
|
||||
} else {
|
||||
if (srtp_err_status_t err = srtp_protect(mSrtpOut, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
throw std::runtime_error("SRTP packet is a replay");
|
||||
else
|
||||
throw std::runtime_error("SRTP protect error, status=" +
|
||||
to_string(static_cast<int>(err)));
|
||||
}
|
||||
PLOG_VERBOSE << "Protected SRTP packet, size=" << size;
|
||||
}
|
||||
PLOG_VERBOSE << "Protected SRTP packet, size=" << size;
|
||||
|
||||
message->resize(size);
|
||||
outgoing(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::incoming(message_ptr message) {
|
||||
if (!mInitDone) {
|
||||
// Bypas
|
||||
DtlsTransport::incoming(message);
|
||||
return;
|
||||
}
|
||||
|
||||
int size = message->size();
|
||||
if (size == 0)
|
||||
return;
|
||||
@ -95,49 +131,80 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
|
||||
// The process for demultiplexing a packet is as follows. The receiver looks at the first byte
|
||||
// of the packet. [...] If the value is in between 128 and 191 (inclusive), then the packet is
|
||||
// RTP (or RTCP [...]). If the value is between 20 and 63 (inclusive), the packet is DTLS.
|
||||
uint8_t value = to_integer<uint8_t>(*message->begin());
|
||||
uint8_t value1 = to_integer<uint8_t>(*message->begin());
|
||||
PLOG_VERBOSE << "Demultiplexing DTLS and SRTP/SRTCP with first byte, value="
|
||||
<< unsigned(value1);
|
||||
|
||||
if (value >= 128 && value <= 192) {
|
||||
if (value1 >= 20 && value1 <= 63) {
|
||||
PLOG_VERBOSE << "Incoming DTLS packet, size=" << size;
|
||||
DtlsTransport::incoming(message);
|
||||
} else if (value >= 20 && value <= 64) {
|
||||
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
|
||||
|
||||
if (srtp_err_status_t err = srtp_unprotect(mSrtp, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
PLOG_WARNING << "Incoming SRTP packet is a replay";
|
||||
else
|
||||
PLOG_WARNING << "SRTP unprotect error, status=" << err;
|
||||
} else if (value1 >= 128 && value1 <= 191) {
|
||||
// The RTP header has a minimum size of 12 bytes
|
||||
if (size < 12) {
|
||||
PLOG_WARNING << "Incoming SRTP/SRTCP packet too short, size=" << size;
|
||||
return;
|
||||
}
|
||||
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
|
||||
|
||||
uint8_t value2 = to_integer<uint8_t>(*(message->begin() + 1)) & 0x7F;
|
||||
PLOG_VERBOSE << "Demultiplexing SRTCP and SRTP with RTP payload type, value="
|
||||
<< unsigned(value2);
|
||||
|
||||
// See RFC 5761 reference above
|
||||
if (value2 >= 64 && value2 <= 95) { // Range 64-95 (inclusive) MUST be RTCP
|
||||
PLOG_VERBOSE << "Incoming SRTCP packet, size=" << size;
|
||||
if (srtp_err_status_t err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
PLOG_WARNING << "Incoming SRTCP packet is a replay";
|
||||
else
|
||||
PLOG_WARNING << "SRTCP unprotect error, status=" << err;
|
||||
return;
|
||||
}
|
||||
PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
|
||||
} else {
|
||||
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
|
||||
if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
|
||||
if (err == srtp_err_status_replay_fail)
|
||||
PLOG_WARNING << "Incoming SRTP packet is a replay";
|
||||
else
|
||||
PLOG_WARNING << "SRTP unprotect error, status=" << err;
|
||||
return;
|
||||
}
|
||||
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
|
||||
}
|
||||
|
||||
message->resize(size);
|
||||
mSrtpRecvCallback(message);
|
||||
|
||||
} else {
|
||||
PLOG_WARNING << "Unknown packet type, value=" << value << ", size=" << size;
|
||||
PLOG_WARNING << "Unknown packet type, value=" << unsigned(value1) << ", size=" << size;
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::postCreation() {
|
||||
#if USE_GNUTLS
|
||||
PLOG_DEBUG << "Setting SRTP profile (GnuTLS)";
|
||||
gnutls::check(gnutls_srtp_set_profile(mSession, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80),
|
||||
"Failed to set SRTP profile");
|
||||
#else
|
||||
PLOG_DEBUG << "Setting SRTP profile (OpenSSL)";
|
||||
// returns 0 on success, 1 on error
|
||||
if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"), "Failed to set SRTP profile")
|
||||
throw std::runtime_error("Failed to set SRTP profile: " + openssl::error_string(ERR_get_error()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DtlsSrtpTransport::postHandshake() {
|
||||
if (mCreated)
|
||||
if (mInitDone)
|
||||
return;
|
||||
|
||||
srtp_policy_t inbound = {};
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
|
||||
inbound.ssrc.type = ssrc_any_inbound;
|
||||
|
||||
srtp_policy_t outbound = {};
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
|
||||
outbound.ssrc.type = ssrc_any_outbound;
|
||||
|
||||
const size_t materialLen = SRTP_AES_ICM_128_KEY_LEN_WSALT * 2;
|
||||
unsigned char material[materialLen];
|
||||
const unsigned char *clientKey, *clientSalt, *serverKey, *serverSalt;
|
||||
|
||||
#if USE_GNUTLS
|
||||
PLOG_INFO << "Deriving SRTP keying material (GnuTLS)";
|
||||
|
||||
gnutls_datum_t clientKeyDatum, clientSaltDatum, serverKeyDatum, serverSaltDatum;
|
||||
gnutls::check(gnutls_srtp_get_keys(mSession, material, materialLen, &clientKeyDatum,
|
||||
&clientSaltDatum, &serverKeyDatum, &serverSaltDatum),
|
||||
@ -160,18 +227,23 @@ void DtlsSrtpTransport::postHandshake() {
|
||||
serverKey = reinterpret_cast<const unsigned char *>(serverKeyDatum.data);
|
||||
serverSalt = reinterpret_cast<const unsigned char *>(serverSaltDatum.data);
|
||||
#else
|
||||
// This provides the client write master key, the server write master key, the client write
|
||||
// master salt and the server write master salt in that order.
|
||||
PLOG_INFO << "Deriving SRTP keying material (OpenSSL)";
|
||||
|
||||
// The extractor provides the client write master key, the server write master key, the client
|
||||
// write master salt and the server write master salt in that order.
|
||||
const string label = "EXTRACTOR-dtls_srtp";
|
||||
openssl::check(SSL_export_keying_material(mSsl, material, materialLen, label.c_str(),
|
||||
label.size(), nullptr, 0, 0),
|
||||
"Failed to derive SRTP keys");
|
||||
|
||||
// returns 1 on success, 0 or -1 on failure (OpenSSL API is a complete mess...)
|
||||
if (SSL_export_keying_material(mSsl, material, materialLen, label.c_str(), label.size(),
|
||||
nullptr, 0, 0) <= 0)
|
||||
throw std::runtime_error("Failed to derive SRTP keys: " +
|
||||
openssl::error_string(ERR_get_error()));
|
||||
|
||||
clientKey = material;
|
||||
clientSalt = clientKey + SRTP_AES_128_KEY_LEN;
|
||||
|
||||
serverKey = material + SRTP_AES_ICM_128_KEY_LEN_WSALT;
|
||||
serverSalt = serverSalt + SRTP_AES_128_KEY_LEN;
|
||||
serverSalt = serverKey + SRTP_AES_128_KEY_LEN;
|
||||
#endif
|
||||
|
||||
unsigned char clientSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
|
||||
@ -182,22 +254,31 @@ void DtlsSrtpTransport::postHandshake() {
|
||||
std::memcpy(serverSessionKey, serverKey, SRTP_AES_128_KEY_LEN);
|
||||
std::memcpy(serverSessionKey + SRTP_AES_128_KEY_LEN, serverSalt, SRTP_SALT_LEN);
|
||||
|
||||
if (mIsClient) {
|
||||
inbound.key = serverSessionKey;
|
||||
outbound.key = clientSessionKey;
|
||||
} else {
|
||||
inbound.key = clientSessionKey;
|
||||
outbound.key = serverSessionKey;
|
||||
}
|
||||
srtp_policy_t inbound = {};
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
|
||||
inbound.ssrc.type = ssrc_any_inbound;
|
||||
inbound.ssrc.value = 0;
|
||||
inbound.key = mIsClient ? serverSessionKey : clientSessionKey;
|
||||
inbound.next = nullptr;
|
||||
|
||||
srtp_policy_t *policies = &inbound;
|
||||
inbound.next = &outbound;
|
||||
if (srtp_err_status_t err = srtp_add_stream(mSrtpIn, &inbound))
|
||||
throw std::runtime_error("SRTP add inbound stream failed, status=" +
|
||||
to_string(static_cast<int>(err)));
|
||||
|
||||
srtp_policy_t outbound = {};
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
|
||||
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
|
||||
outbound.ssrc.type = ssrc_any_outbound;
|
||||
outbound.ssrc.value = 0;
|
||||
outbound.key = mIsClient ? clientSessionKey : serverSessionKey;
|
||||
outbound.next = nullptr;
|
||||
|
||||
if (srtp_err_status_t err = srtp_create(&mSrtp, policies))
|
||||
throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err)));
|
||||
if (srtp_err_status_t err = srtp_add_stream(mSrtpOut, &outbound))
|
||||
throw std::runtime_error("SRTP add outbound stream failed, status=" +
|
||||
to_string(static_cast<int>(err)));
|
||||
|
||||
mCreated = true;
|
||||
mInitDone = true;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
@ -38,16 +38,17 @@ public:
|
||||
state_callback stateChangeCallback);
|
||||
~DtlsSrtpTransport();
|
||||
|
||||
bool send(message_ptr message) override;
|
||||
bool sendMedia(message_ptr message);
|
||||
|
||||
private:
|
||||
void incoming(message_ptr message) override;
|
||||
void postCreation() override;
|
||||
void postHandshake() override;
|
||||
|
||||
message_callback mSrtpRecvCallback;
|
||||
|
||||
srtp_t mSrtp;
|
||||
bool mCreated = false;
|
||||
srtp_t mSrtpIn, mSrtpOut;
|
||||
bool mInitDone = false;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
@ -85,6 +85,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
|
||||
gnutls_transport_set_pull_function(mSession, ReadCallback);
|
||||
gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
|
||||
|
||||
postCreation();
|
||||
|
||||
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
||||
registerIncoming();
|
||||
|
||||
@ -137,6 +139,10 @@ void DtlsTransport::incoming(message_ptr message) {
|
||||
mIncomingQueue.push(message);
|
||||
}
|
||||
|
||||
void DtlsTransport::postCreation() {
|
||||
// Dummy
|
||||
}
|
||||
|
||||
void DtlsTransport::postHandshake() {
|
||||
// Dummy
|
||||
}
|
||||
@ -408,6 +414,10 @@ void DtlsTransport::incoming(message_ptr message) {
|
||||
mIncomingQueue.push(message);
|
||||
}
|
||||
|
||||
void DtlsTransport::postCreation() {
|
||||
// Dummy
|
||||
}
|
||||
|
||||
void DtlsTransport::postHandshake() {
|
||||
// Dummy
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void incoming(message_ptr message) override;
|
||||
virtual void postCreation();
|
||||
virtual void postHandshake();
|
||||
void runRecvLoop();
|
||||
|
||||
|
@ -227,7 +227,7 @@ void PeerConnection::sendMedia(const binary &packet) {
|
||||
outgoingMedia(make_message(packet.begin(), packet.end(), Message::Binary));
|
||||
}
|
||||
|
||||
void PeerConnection::send(const byte *packet, size_t size) {
|
||||
void PeerConnection::sendMedia(const byte *packet, size_t size) {
|
||||
outgoingMedia(make_message(packet, packet + size, Message::Binary));
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ void PeerConnection::outgoingMedia(message_ptr message) {
|
||||
if (!transport)
|
||||
throw std::runtime_error("PeerConnection is not open");
|
||||
|
||||
std::dynamic_pointer_cast<DtlsSrtpTransport>(transport)->send(message);
|
||||
std::dynamic_pointer_cast<DtlsSrtpTransport>(transport)->sendMedia(message);
|
||||
#else
|
||||
PLOG_WARNING << "Ignoring sent media (not compiled with SRTP support)";
|
||||
#endif
|
||||
@ -594,14 +594,20 @@ void PeerConnection::remoteCloseDataChannels() {
|
||||
|
||||
void PeerConnection::processLocalDescription(Description description) {
|
||||
std::optional<uint16_t> remoteSctpPort;
|
||||
if (auto remote = remoteDescription())
|
||||
remoteSctpPort = remote->sctpPort();
|
||||
std::optional<string> remoteDataMid;
|
||||
if (auto remote = remoteDescription()) {
|
||||
remoteDataMid = remote->dataMid();
|
||||
remoteSctpPort = remote->sctpPort();
|
||||
}
|
||||
|
||||
auto certificate = mCertificate.get(); // wait for certificate if not ready
|
||||
|
||||
{
|
||||
std::lock_guard lock(mLocalDescriptionMutex);
|
||||
mLocalDescription.emplace(std::move(description));
|
||||
if (remoteDataMid)
|
||||
mLocalDescription->setDataMid(*remoteDataMid);
|
||||
|
||||
mLocalDescription->setFingerprint(certificate->fingerprint());
|
||||
mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
|
||||
mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
|
||||
|
@ -194,6 +194,11 @@ SctpTransport::~SctpTransport() {
|
||||
}
|
||||
|
||||
bool SctpTransport::stop() {
|
||||
// Transport::stop() will unregister incoming() from the lower layer, therefore we need to make
|
||||
// sure the thread from lower layers is not blocked in incoming() by the WrittenOnce condition.
|
||||
mWrittenOnce = true;
|
||||
mWrittenCondition.notify_all();
|
||||
|
||||
if (!Transport::stop())
|
||||
return false;
|
||||
|
||||
|
@ -41,12 +41,17 @@ public:
|
||||
|
||||
virtual ~Transport() {
|
||||
stop();
|
||||
if (mLower)
|
||||
mLower->onRecv(nullptr); // doing it on stop could cause a deadlock
|
||||
}
|
||||
|
||||
virtual bool stop() {
|
||||
return !mShutdown.exchange(true);
|
||||
if (mShutdown.exchange(true))
|
||||
return false;
|
||||
|
||||
// We don't want incoming() to be called by the lower layer anymore
|
||||
if (mLower)
|
||||
mLower->onRecv(nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void registerIncoming() {
|
||||
|
Reference in New Issue
Block a user