#define MS_CLASS "RTC::WebRtcTransport"
#include "RTC/WebRtcTransport.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Utils.hpp"
#include "Channel/ChannelNotifier.hpp"
#include <cmath>
namespace RTC
{
static constexpr uint16_t IceCandidateDefaultLocalPriority{ 10000 };
static constexpr uint16_t IceTypePreference{ 64 };
static constexpr uint16_t IceComponent{ 1 };
static inline uint32_t generateIceCandidatePriority(uint16_t localPreference)
{
MS_TRACE();
return std::pow(2, 24) * IceTypePreference + std::pow(2, 8) * localPreference +
std::pow(2, 0) * (256 - IceComponent);
}
WebRtcTransport::WebRtcTransport(const std::string& id, RTC::Transport::Listener* listener, json& data)
: RTC::Transport::Transport(id, listener, data)
{
MS_TRACE();
bool enableUdp{ true };
auto jsonEnableUdpIt = data.find("enableUdp");
if (jsonEnableUdpIt != data.end())
{
if (!jsonEnableUdpIt->is_boolean())
MS_THROW_TYPE_ERROR("wrong enableUdp (not a boolean)");
enableUdp = jsonEnableUdpIt->get<bool>();
}
bool enableTcp{ false };
auto jsonEnableTcpIt = data.find("enableTcp");
if (jsonEnableTcpIt != data.end())
{
if (!jsonEnableTcpIt->is_boolean())
MS_THROW_TYPE_ERROR("wrong enableTcp (not a boolean)");
enableTcp = jsonEnableTcpIt->get<bool>();
}
bool preferUdp{ false };
auto jsonPreferUdpIt = data.find("preferUdp");
if (jsonPreferUdpIt != data.end())
{
if (!jsonPreferUdpIt->is_boolean())
MS_THROW_TYPE_ERROR("wrong preferUdp (not a boolean)");
preferUdp = jsonPreferUdpIt->get<bool>();
}
bool preferTcp{ false };
auto jsonPreferTcpIt = data.find("preferTcp");
if (jsonPreferTcpIt != data.end())
{
if (!jsonPreferTcpIt->is_boolean())
MS_THROW_TYPE_ERROR("wrong preferTcp (not a boolean)");
preferTcp = jsonPreferTcpIt->get<bool>();
}
auto jsonListenIpsIt = data.find("listenIps");
if (jsonListenIpsIt == data.end())
MS_THROW_TYPE_ERROR("missing listenIps");
else if (!jsonListenIpsIt->is_array())
MS_THROW_TYPE_ERROR("wrong listenIps (not an array)");
else if (jsonListenIpsIt->empty())
MS_THROW_TYPE_ERROR("wrong listenIps (empty array)");
else if (jsonListenIpsIt->size() > 8)
MS_THROW_TYPE_ERROR("wrong listenIps (too many IPs)");
std::vector<ListenIp> listenIps(jsonListenIpsIt->size());
for (size_t i{ 0 }; i < jsonListenIpsIt->size(); ++i)
{
auto& jsonListenIp = (*jsonListenIpsIt)[i];
auto& listenIp = listenIps[i];
if (!jsonListenIp.is_object())
MS_THROW_TYPE_ERROR("wrong listenIp (not an object)");
auto jsonIpIt = jsonListenIp.find("ip");
if (jsonIpIt == jsonListenIp.end())
MS_THROW_TYPE_ERROR("missing listenIp.ip");
else if (!jsonIpIt->is_string())
MS_THROW_TYPE_ERROR("wrong listenIp.ip (not an string");
listenIp.ip.assign(jsonIpIt->get<std::string>());
Utils::IP::NormalizeIp(listenIp.ip);
auto jsonAnnouncedIpIt = jsonListenIp.find("announcedIp");
if (jsonAnnouncedIpIt != jsonListenIp.end())
{
if (!jsonAnnouncedIpIt->is_string())
MS_THROW_TYPE_ERROR("wrong listenIp.announcedIp (not an string)");
listenIp.announcedIp.assign(jsonAnnouncedIpIt->get<std::string>());
}
}
uint16_t port{ 0 };
auto jsonPortIt = data.find("port");
if (jsonPortIt != data.end())
{
if (!(jsonPortIt->is_number() && Utils::Json::IsPositiveInteger(*jsonPortIt)))
MS_THROW_TYPE_ERROR("wrong port (not a positive number)");
port = jsonPortIt->get<uint16_t>();
}
try
{
uint16_t iceLocalPreferenceDecrement{ 0 };
if (enableUdp && enableTcp)
this->iceCandidates.reserve(2 * jsonListenIpsIt->size());
else
this->iceCandidates.reserve(jsonListenIpsIt->size());
for (auto& listenIp : listenIps)
{
if (enableUdp)
{
uint16_t iceLocalPreference =
IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;
if (preferUdp)
iceLocalPreference += 1000;
uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);
RTC::UdpSocket* udpSocket;
if (port != 0)
udpSocket = new RTC::UdpSocket(this, listenIp.ip, port);
else
udpSocket = new RTC::UdpSocket(this, listenIp.ip);
this->udpSockets[udpSocket] = listenIp.announcedIp;
if (listenIp.announcedIp.empty())
this->iceCandidates.emplace_back(udpSocket, icePriority);
else
this->iceCandidates.emplace_back(udpSocket, icePriority, listenIp.announcedIp);
}
if (enableTcp)
{
uint16_t iceLocalPreference =
IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;
if (preferTcp)
iceLocalPreference += 1000;
uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);
RTC::TcpServer* tcpServer;
if (port != 0)
tcpServer = new RTC::TcpServer(this, this, listenIp.ip, port);
else
tcpServer = new RTC::TcpServer(this, this, listenIp.ip);
this->tcpServers[tcpServer] = listenIp.announcedIp;
if (listenIp.announcedIp.empty())
this->iceCandidates.emplace_back(tcpServer, icePriority);
else
this->iceCandidates.emplace_back(tcpServer, icePriority, listenIp.announcedIp);
}
iceLocalPreferenceDecrement += 100;
}
this->iceServer = new RTC::IceServer(
this, Utils::Crypto::GetRandomString(16), Utils::Crypto::GetRandomString(32));
this->dtlsTransport = new RTC::DtlsTransport(this);
}
catch (const MediaSoupError& error)
{
delete this->dtlsTransport;
this->dtlsTransport = nullptr;
delete this->iceServer;
this->iceServer = nullptr;
for (auto& kv : this->udpSockets)
{
auto* udpSocket = kv.first;
delete udpSocket;
}
this->udpSockets.clear();
for (auto& kv : this->tcpServers)
{
auto* tcpServer = kv.first;
delete tcpServer;
}
this->tcpServers.clear();
this->iceCandidates.clear();
throw;
}
}
WebRtcTransport::~WebRtcTransport()
{
MS_TRACE();
delete this->dtlsTransport;
this->dtlsTransport = nullptr;
delete this->iceServer;
this->iceServer = nullptr;
for (auto& kv : this->udpSockets)
{
auto* udpSocket = kv.first;
delete udpSocket;
}
this->udpSockets.clear();
for (auto& kv : this->tcpServers)
{
auto* tcpServer = kv.first;
delete tcpServer;
}
this->tcpServers.clear();
this->iceCandidates.clear();
delete this->srtpSendSession;
this->srtpSendSession = nullptr;
delete this->srtpRecvSession;
this->srtpRecvSession = nullptr;
}
void WebRtcTransport::FillJson(json& jsonObject) const
{
MS_TRACE();
RTC::Transport::FillJson(jsonObject);
jsonObject["iceRole"] = "controlled";
jsonObject["iceParameters"] = json::object();
auto jsonIceParametersIt = jsonObject.find("iceParameters");
(*jsonIceParametersIt)["usernameFragment"] = this->iceServer->GetUsernameFragment();
(*jsonIceParametersIt)["password"] = this->iceServer->GetPassword();
(*jsonIceParametersIt)["iceLite"] = true;
jsonObject["iceCandidates"] = json::array();
auto jsonIceCandidatesIt = jsonObject.find("iceCandidates");
for (size_t i{ 0 }; i < this->iceCandidates.size(); ++i)
{
jsonIceCandidatesIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonIceCandidatesIt)[i];
auto& iceCandidate = this->iceCandidates[i];
iceCandidate.FillJson(jsonEntry);
}
switch (this->iceServer->GetState())
{
case RTC::IceServer::IceState::NEW:
jsonObject["iceState"] = "new";
break;
case RTC::IceServer::IceState::CONNECTED:
jsonObject["iceState"] = "connected";
break;
case RTC::IceServer::IceState::COMPLETED:
jsonObject["iceState"] = "completed";
break;
case RTC::IceServer::IceState::DISCONNECTED:
jsonObject["iceState"] = "disconnected";
break;
}
if (this->iceServer->GetSelectedTuple())
this->iceServer->GetSelectedTuple()->FillJson(jsonObject["iceSelectedTuple"]);
jsonObject["dtlsParameters"] = json::object();
auto jsonDtlsParametersIt = jsonObject.find("dtlsParameters");
(*jsonDtlsParametersIt)["fingerprints"] = json::array();
auto jsonDtlsParametersFingerprintsIt = jsonDtlsParametersIt->find("fingerprints");
auto& fingerprints = this->dtlsTransport->GetLocalFingerprints();
for (size_t i{ 0 }; i < fingerprints.size(); ++i)
{
jsonDtlsParametersFingerprintsIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonDtlsParametersFingerprintsIt)[i];
auto& fingerprint = fingerprints[i];
jsonEntry["algorithm"] =
RTC::DtlsTransport::GetFingerprintAlgorithmString(fingerprint.algorithm);
jsonEntry["value"] = fingerprint.value;
}
switch (this->dtlsRole)
{
case RTC::DtlsTransport::Role::NONE:
(*jsonDtlsParametersIt)["role"] = "none";
break;
case RTC::DtlsTransport::Role::AUTO:
(*jsonDtlsParametersIt)["role"] = "auto";
break;
case RTC::DtlsTransport::Role::CLIENT:
(*jsonDtlsParametersIt)["role"] = "client";
break;
case RTC::DtlsTransport::Role::SERVER:
(*jsonDtlsParametersIt)["role"] = "server";
break;
}
switch (this->dtlsTransport->GetState())
{
case RTC::DtlsTransport::DtlsState::NEW:
jsonObject["dtlsState"] = "new";
break;
case RTC::DtlsTransport::DtlsState::CONNECTING:
jsonObject["dtlsState"] = "connecting";
break;
case RTC::DtlsTransport::DtlsState::CONNECTED:
jsonObject["dtlsState"] = "connected";
break;
case RTC::DtlsTransport::DtlsState::FAILED:
jsonObject["dtlsState"] = "failed";
break;
case RTC::DtlsTransport::DtlsState::CLOSED:
jsonObject["dtlsState"] = "closed";
break;
}
}
void WebRtcTransport::FillJsonStats(json& jsonArray)
{
MS_TRACE();
RTC::Transport::FillJsonStats(jsonArray);
auto& jsonObject = jsonArray[0];
jsonObject["type"] = "webrtc-transport";
jsonObject["iceRole"] = "controlled";
switch (this->iceServer->GetState())
{
case RTC::IceServer::IceState::NEW:
jsonObject["iceState"] = "new";
break;
case RTC::IceServer::IceState::CONNECTED:
jsonObject["iceState"] = "connected";
break;
case RTC::IceServer::IceState::COMPLETED:
jsonObject["iceState"] = "completed";
break;
case RTC::IceServer::IceState::DISCONNECTED:
jsonObject["iceState"] = "disconnected";
break;
}
if (this->iceServer->GetSelectedTuple())
{
this->iceServer->GetSelectedTuple()->FillJson(jsonObject["iceSelectedTuple"]);
}
switch (this->dtlsTransport->GetState())
{
case RTC::DtlsTransport::DtlsState::NEW:
jsonObject["dtlsState"] = "new";
break;
case RTC::DtlsTransport::DtlsState::CONNECTING:
jsonObject["dtlsState"] = "connecting";
break;
case RTC::DtlsTransport::DtlsState::CONNECTED:
jsonObject["dtlsState"] = "connected";
break;
case RTC::DtlsTransport::DtlsState::FAILED:
jsonObject["dtlsState"] = "failed";
break;
case RTC::DtlsTransport::DtlsState::CLOSED:
jsonObject["dtlsState"] = "closed";
break;
}
}
void WebRtcTransport::HandleRequest(Channel::ChannelRequest* request)
{
MS_TRACE();
switch (request->methodId)
{
case Channel::ChannelRequest::MethodId::TRANSPORT_CONNECT:
{
if (this->connectCalled)
MS_THROW_ERROR("connect() already called");
RTC::DtlsTransport::Fingerprint dtlsRemoteFingerprint;
RTC::DtlsTransport::Role dtlsRemoteRole;
auto jsonDtlsParametersIt = request->data.find("dtlsParameters");
if (jsonDtlsParametersIt == request->data.end() || !jsonDtlsParametersIt->is_object())
MS_THROW_TYPE_ERROR("missing dtlsParameters");
auto jsonFingerprintsIt = jsonDtlsParametersIt->find("fingerprints");
if (jsonFingerprintsIt == jsonDtlsParametersIt->end() || !jsonFingerprintsIt->is_array())
{
MS_THROW_TYPE_ERROR("missing dtlsParameters.fingerprints");
}
else if (jsonFingerprintsIt->empty())
{
MS_THROW_TYPE_ERROR("empty dtlsParameters.fingerprints array");
}
for (auto& jsonFingerprint : *jsonFingerprintsIt)
{
if (!jsonFingerprint.is_object())
MS_THROW_TYPE_ERROR("wrong entry in dtlsParameters.fingerprints (not an object)");
auto jsonAlgorithmIt = jsonFingerprint.find("algorithm");
if (jsonAlgorithmIt == jsonFingerprint.end())
MS_THROW_TYPE_ERROR("missing fingerprint.algorithm");
else if (!jsonAlgorithmIt->is_string())
MS_THROW_TYPE_ERROR("wrong fingerprint.algorithm (not a string)");
dtlsRemoteFingerprint.algorithm =
RTC::DtlsTransport::GetFingerprintAlgorithm(jsonAlgorithmIt->get<std::string>());
if (dtlsRemoteFingerprint.algorithm == RTC::DtlsTransport::FingerprintAlgorithm::NONE)
{
MS_THROW_TYPE_ERROR("invalid fingerprint.algorithm value");
}
auto jsonValueIt = jsonFingerprint.find("value");
if (jsonValueIt == jsonFingerprint.end())
MS_THROW_TYPE_ERROR("missing fingerprint.value");
else if (!jsonValueIt->is_string())
MS_THROW_TYPE_ERROR("wrong fingerprint.value (not a string)");
dtlsRemoteFingerprint.value = jsonValueIt->get<std::string>();
break;
}
auto jsonRoleIt = jsonDtlsParametersIt->find("role");
if (jsonRoleIt != jsonDtlsParametersIt->end())
{
if (!jsonRoleIt->is_string())
MS_THROW_TYPE_ERROR("wrong dtlsParameters.role (not a string)");
dtlsRemoteRole = RTC::DtlsTransport::StringToRole(jsonRoleIt->get<std::string>());
if (dtlsRemoteRole == RTC::DtlsTransport::Role::NONE)
MS_THROW_TYPE_ERROR("invalid dtlsParameters.role value");
}
else
{
dtlsRemoteRole = RTC::DtlsTransport::Role::AUTO;
}
switch (dtlsRemoteRole)
{
case RTC::DtlsTransport::Role::CLIENT:
{
this->dtlsRole = RTC::DtlsTransport::Role::SERVER;
break;
}
case RTC::DtlsTransport::Role::SERVER:
case RTC::DtlsTransport::Role::AUTO:
{
this->dtlsRole = RTC::DtlsTransport::Role::CLIENT;
break;
}
case RTC::DtlsTransport::Role::NONE:
{
MS_THROW_TYPE_ERROR("invalid remote DTLS role");
}
}
this->connectCalled = true;
if (this->dtlsTransport->SetRemoteFingerprint(dtlsRemoteFingerprint))
{
MayRunDtlsTransport();
}
json data = json::object();
switch (this->dtlsRole)
{
case RTC::DtlsTransport::Role::CLIENT:
data["dtlsLocalRole"] = "client";
break;
case RTC::DtlsTransport::Role::SERVER:
data["dtlsLocalRole"] = "server";
break;
default:
MS_ABORT("invalid local DTLS role");
}
request->Accept(data);
break;
}
case Channel::ChannelRequest::MethodId::TRANSPORT_RESTART_ICE:
{
std::string usernameFragment = Utils::Crypto::GetRandomString(16);
std::string password = Utils::Crypto::GetRandomString(32);
this->iceServer->SetUsernameFragment(usernameFragment);
this->iceServer->SetPassword(password);
MS_DEBUG_DEV(
"WebRtcTransport ICE usernameFragment and password changed [id:%s]", this->id.c_str());
json data = json::object();
data["iceParameters"] = json::object();
auto jsonIceParametersIt = data.find("iceParameters");
(*jsonIceParametersIt)["usernameFragment"] = this->iceServer->GetUsernameFragment();
(*jsonIceParametersIt)["password"] = this->iceServer->GetPassword();
(*jsonIceParametersIt)["iceLite"] = true;
request->Accept(data);
break;
}
default:
{
RTC::Transport::HandleRequest(request);
}
}
}
void WebRtcTransport::HandleNotification(PayloadChannel::Notification* notification)
{
MS_TRACE();
RTC::Transport::HandleNotification(notification);
}
inline bool WebRtcTransport::IsConnected() const
{
MS_TRACE();
return (
(
this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED ||
this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED
) &&
this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTED
);
}
void WebRtcTransport::MayRunDtlsTransport()
{
MS_TRACE();
if (this->dtlsTransport->GetLocalRole() == this->dtlsRole)
return;
switch (this->dtlsRole)
{
case RTC::DtlsTransport::Role::AUTO:
{
if (
this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED ||
this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED
)
{
MS_DEBUG_TAG(
dtls, "transition from DTLS local role 'auto' to 'server' and running DTLS transport");
this->dtlsRole = RTC::DtlsTransport::Role::SERVER;
this->dtlsTransport->Run(RTC::DtlsTransport::Role::SERVER);
}
break;
}
case RTC::DtlsTransport::Role::CLIENT:
{
if (
this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED ||
this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED
)
{
MS_DEBUG_TAG(dtls, "running DTLS transport in local role 'client'");
this->dtlsTransport->Run(RTC::DtlsTransport::Role::CLIENT);
}
break;
}
case RTC::DtlsTransport::Role::SERVER:
{
if (
this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED ||
this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED
)
{
MS_DEBUG_TAG(dtls, "running DTLS transport in local role 'server'");
this->dtlsTransport->Run(RTC::DtlsTransport::Role::SERVER);
}
break;
}
case RTC::DtlsTransport::Role::NONE:
{
MS_ABORT("local DTLS role not set");
}
}
}
void WebRtcTransport::SendRtpPacket(
RTC::Consumer* , RTC::RtpPacket* packet, RTC::Transport::onSendCallback* cb)
{
MS_TRACE();
if (!IsConnected())
{
if (cb)
{
(*cb)(false);
delete cb;
}
return;
}
if (!this->srtpSendSession)
{
MS_WARN_DEV("ignoring RTP packet due to non sending SRTP session");
if (cb)
{
(*cb)(false);
delete cb;
}
return;
}
const uint8_t* data = packet->GetData();
size_t len = packet->GetSize();
if (!this->srtpSendSession->EncryptRtp(&data, &len))
{
if (cb)
{
(*cb)(false);
delete cb;
}
return;
}
this->iceServer->GetSelectedTuple()->Send(data, len, cb);
RTC::Transport::DataSent(len);
}
void WebRtcTransport::SendRtcpPacket(RTC::RTCP::Packet* packet)
{
MS_TRACE();
if (!IsConnected())
return;
const uint8_t* data = packet->GetData();
size_t len = packet->GetSize();
if (!this->srtpSendSession)
{
MS_WARN_DEV("ignoring RTCP packet due to non sending SRTP session");
return;
}
if (!this->srtpSendSession->EncryptRtcp(&data, &len))
return;
this->iceServer->GetSelectedTuple()->Send(data, len);
RTC::Transport::DataSent(len);
}
void WebRtcTransport::SendRtcpCompoundPacket(RTC::RTCP::CompoundPacket* packet)
{
MS_TRACE();
if (!IsConnected())
return;
const uint8_t* data = packet->GetData();
size_t len = packet->GetSize();
if (!this->srtpSendSession)
{
MS_WARN_TAG(rtcp, "ignoring RTCP compound packet due to non sending SRTP session");
return;
}
if (!this->srtpSendSession->EncryptRtcp(&data, &len))
return;
this->iceServer->GetSelectedTuple()->Send(data, len);
RTC::Transport::DataSent(len);
}
void WebRtcTransport::SendMessage(
RTC::DataConsumer* dataConsumer, uint32_t ppid, const uint8_t* msg, size_t len, onQueuedCallback* cb)
{
MS_TRACE();
this->sctpAssociation->SendSctpMessage(dataConsumer, ppid, msg, len, cb);
}
void WebRtcTransport::SendSctpData(const uint8_t* data, size_t len)
{
MS_TRACE();
if (!IsConnected())
{
MS_WARN_TAG(sctp, "DTLS not connected, cannot send SCTP data");
return;
}
this->dtlsTransport->SendApplicationData(data, len);
}
void WebRtcTransport::RecvStreamClosed(uint32_t ssrc)
{
MS_TRACE();
if (this->srtpRecvSession)
{
this->srtpRecvSession->RemoveStream(ssrc);
}
}
void WebRtcTransport::SendStreamClosed(uint32_t ssrc)
{
MS_TRACE();
if (this->srtpSendSession)
{
this->srtpSendSession->RemoveStream(ssrc);
}
}
inline void WebRtcTransport::OnPacketReceived(
RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
RTC::Transport::DataReceived(len);
if (RTC::StunPacket::IsStun(data, len))
{
OnStunDataReceived(tuple, data, len);
}
else if (RTC::RTCP::Packet::IsRtcp(data, len))
{
OnRtcpDataReceived(tuple, data, len);
}
else if (RTC::RtpPacket::IsRtp(data, len))
{
OnRtpDataReceived(tuple, data, len);
}
else if (RTC::DtlsTransport::IsDtls(data, len))
{
OnDtlsDataReceived(tuple, data, len);
}
else
{
MS_WARN_DEV("ignoring received packet of unknown type");
}
}
inline void WebRtcTransport::OnStunDataReceived(
RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
RTC::StunPacket* packet = RTC::StunPacket::Parse(data, len);
if (!packet)
{
MS_WARN_DEV("ignoring wrong STUN packet received");
return;
}
this->iceServer->ProcessStunPacket(packet, tuple);
delete packet;
}
inline void WebRtcTransport::OnDtlsDataReceived(
const RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
if (!this->iceServer->IsValidTuple(tuple))
{
MS_WARN_TAG(dtls, "ignoring DTLS data coming from an invalid tuple");
return;
}
this->iceServer->ForceSelectedTuple(tuple);
if (
this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTING ||
this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTED)
{
MS_DEBUG_DEV("DTLS data received, passing it to the DTLS transport");
this->dtlsTransport->ProcessDtlsData(data, len);
}
else
{
MS_WARN_TAG(dtls, "Transport is not 'connecting' or 'connected', ignoring received DTLS data");
return;
}
}
inline void WebRtcTransport::OnRtpDataReceived(
RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
if (this->dtlsTransport->GetState() != RTC::DtlsTransport::DtlsState::CONNECTED)
{
MS_DEBUG_2TAGS(dtls, rtp, "ignoring RTP packet while DTLS not connected");
return;
}
if (!this->srtpRecvSession)
{
MS_DEBUG_TAG(srtp, "ignoring RTP packet due to non receiving SRTP session");
return;
}
if (!this->iceServer->IsValidTuple(tuple))
{
MS_WARN_TAG(rtp, "ignoring RTP packet coming from an invalid tuple");
return;
}
if (!this->srtpRecvSession->DecryptSrtp(const_cast<uint8_t*>(data), &len))
{
RTC::RtpPacket* packet = RTC::RtpPacket::Parse(data, len);
if (!packet)
{
MS_WARN_TAG(srtp, "DecryptSrtp() failed due to an invalid RTP packet");
}
else
{
MS_WARN_TAG(
srtp,
"DecryptSrtp() failed [ssrc:%" PRIu32 ", payloadType:%" PRIu8 ", seq:%" PRIu16 "]",
packet->GetSsrc(),
packet->GetPayloadType(),
packet->GetSequenceNumber());
delete packet;
}
return;
}
RTC::RtpPacket* packet = RTC::RtpPacket::Parse(data, len);
if (!packet)
{
MS_WARN_TAG(rtp, "received data is not a valid RTP packet");
return;
}
this->iceServer->ForceSelectedTuple(tuple);
RTC::Transport::ReceiveRtpPacket(packet);
}
inline void WebRtcTransport::OnRtcpDataReceived(
RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
if (this->dtlsTransport->GetState() != RTC::DtlsTransport::DtlsState::CONNECTED)
{
MS_DEBUG_2TAGS(dtls, rtcp, "ignoring RTCP packet while DTLS not connected");
return;
}
if (!this->srtpRecvSession)
{
MS_DEBUG_TAG(srtp, "ignoring RTCP packet due to non receiving SRTP session");
return;
}
if (!this->iceServer->IsValidTuple(tuple))
{
MS_WARN_TAG(rtcp, "ignoring RTCP packet coming from an invalid tuple");
return;
}
if (!this->srtpRecvSession->DecryptSrtcp(const_cast<uint8_t*>(data), &len))
return;
RTC::RTCP::Packet* packet = RTC::RTCP::Packet::Parse(data, len);
if (!packet)
{
MS_WARN_TAG(rtcp, "received data is not a valid RTCP compound or single packet");
return;
}
RTC::Transport::ReceiveRtcpPacket(packet);
}
inline void WebRtcTransport::OnUdpSocketPacketReceived(
RTC::UdpSocket* socket, const uint8_t* data, size_t len, const struct sockaddr* remoteAddr)
{
MS_TRACE();
RTC::TransportTuple tuple(socket, remoteAddr);
OnPacketReceived(&tuple, data, len);
}
inline void WebRtcTransport::OnRtcTcpConnectionClosed(
RTC::TcpServer* , RTC::TcpConnection* connection)
{
MS_TRACE();
RTC::TransportTuple tuple(connection);
this->iceServer->RemoveTuple(&tuple);
}
inline void WebRtcTransport::OnTcpConnectionPacketReceived(
RTC::TcpConnection* connection, const uint8_t* data, size_t len)
{
MS_TRACE();
RTC::TransportTuple tuple(connection);
OnPacketReceived(&tuple, data, len);
}
inline void WebRtcTransport::OnIceServerSendStunPacket(
const RTC::IceServer* , const RTC::StunPacket* packet, RTC::TransportTuple* tuple)
{
MS_TRACE();
tuple->Send(packet->GetData(), packet->GetSize());
RTC::Transport::DataSent(packet->GetSize());
}
inline void WebRtcTransport::OnIceServerSelectedTuple(
const RTC::IceServer* , RTC::TransportTuple* )
{
MS_TRACE();
MS_DEBUG_TAG(ice, "ICE selected tuple");
json data = json::object();
this->iceServer->GetSelectedTuple()->FillJson(data["iceSelectedTuple"]);
Channel::ChannelNotifier::Emit(this->id, "iceselectedtuplechange", data);
}
inline void WebRtcTransport::OnIceServerConnected(const RTC::IceServer* )
{
MS_TRACE();
MS_DEBUG_TAG(ice, "ICE connected");
json data = json::object();
data["iceState"] = "connected";
Channel::ChannelNotifier::Emit(this->id, "icestatechange", data);
MayRunDtlsTransport();
if (this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTED)
{
RTC::Transport::Connected();
}
}
inline void WebRtcTransport::OnIceServerCompleted(const RTC::IceServer* )
{
MS_TRACE();
MS_DEBUG_TAG(ice, "ICE completed");
json data = json::object();
data["iceState"] = "completed";
Channel::ChannelNotifier::Emit(this->id, "icestatechange", data);
MayRunDtlsTransport();
if (this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTED)
{
RTC::Transport::Connected();
}
}
inline void WebRtcTransport::OnIceServerDisconnected(const RTC::IceServer* )
{
MS_TRACE();
MS_DEBUG_TAG(ice, "ICE disconnected");
json data = json::object();
data["iceState"] = "disconnected";
Channel::ChannelNotifier::Emit(this->id, "icestatechange", data);
if (this->dtlsTransport->GetState() == RTC::DtlsTransport::DtlsState::CONNECTED)
{
RTC::Transport::Disconnected();
}
}
inline void WebRtcTransport::OnDtlsTransportConnecting(const RTC::DtlsTransport* )
{
MS_TRACE();
MS_DEBUG_TAG(dtls, "DTLS connecting");
json data = json::object();
data["dtlsState"] = "connecting";
Channel::ChannelNotifier::Emit(this->id, "dtlsstatechange", data);
}
inline void WebRtcTransport::OnDtlsTransportConnected(
const RTC::DtlsTransport* ,
RTC::SrtpSession::CryptoSuite srtpCryptoSuite,
uint8_t* srtpLocalKey,
size_t srtpLocalKeyLen,
uint8_t* srtpRemoteKey,
size_t srtpRemoteKeyLen,
std::string& remoteCert)
{
MS_TRACE();
MS_DEBUG_TAG(dtls, "DTLS connected");
delete this->srtpSendSession;
this->srtpSendSession = nullptr;
delete this->srtpRecvSession;
this->srtpRecvSession = nullptr;
try
{
this->srtpSendSession = new RTC::SrtpSession(
RTC::SrtpSession::Type::OUTBOUND, srtpCryptoSuite, srtpLocalKey, srtpLocalKeyLen);
}
catch (const MediaSoupError& error)
{
MS_ERROR("error creating SRTP sending session: %s", error.what());
}
try
{
this->srtpRecvSession = new RTC::SrtpSession(
RTC::SrtpSession::Type::INBOUND, srtpCryptoSuite, srtpRemoteKey, srtpRemoteKeyLen);
json data = json::object();
data["dtlsState"] = "connected";
data["dtlsRemoteCert"] = remoteCert;
Channel::ChannelNotifier::Emit(this->id, "dtlsstatechange", data);
RTC::Transport::Connected();
}
catch (const MediaSoupError& error)
{
MS_ERROR("error creating SRTP receiving session: %s", error.what());
delete this->srtpSendSession;
this->srtpSendSession = nullptr;
}
}
inline void WebRtcTransport::OnDtlsTransportFailed(const RTC::DtlsTransport* )
{
MS_TRACE();
MS_WARN_TAG(dtls, "DTLS failed");
json data = json::object();
data["dtlsState"] = "failed";
Channel::ChannelNotifier::Emit(this->id, "dtlsstatechange", data);
}
inline void WebRtcTransport::OnDtlsTransportClosed(const RTC::DtlsTransport* )
{
MS_TRACE();
MS_WARN_TAG(dtls, "DTLS remotely closed");
json data = json::object();
data["dtlsState"] = "closed";
Channel::ChannelNotifier::Emit(this->id, "dtlsstatechange", data);
RTC::Transport::Disconnected();
}
inline void WebRtcTransport::OnDtlsTransportSendData(
const RTC::DtlsTransport* , const uint8_t* data, size_t len)
{
MS_TRACE();
if (!this->iceServer->GetSelectedTuple())
{
MS_WARN_TAG(dtls, "no selected tuple set, cannot send DTLS packet");
return;
}
this->iceServer->GetSelectedTuple()->Send(data, len);
RTC::Transport::DataSent(len);
}
inline void WebRtcTransport::OnDtlsTransportApplicationDataReceived(
const RTC::DtlsTransport* , const uint8_t* data, size_t len)
{
MS_TRACE();
RTC::Transport::ReceiveSctpData(data, len);
}
}