#define MS_CLASS "RTC::WebRtcServer"
#include "RTC/WebRtcServer.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);
}
WebRtcServer::WebRtcServer(const std::string& id, json& data) : id(id)
{
MS_TRACE();
auto jsonListenInfosIt = data.find("listenInfos");
if (jsonListenInfosIt == data.end())
MS_THROW_TYPE_ERROR("missing listenInfos");
else if (!jsonListenInfosIt->is_array())
MS_THROW_TYPE_ERROR("wrong listenInfos (not an array)");
else if (jsonListenInfosIt->empty())
MS_THROW_TYPE_ERROR("wrong listenInfos (empty array)");
else if (jsonListenInfosIt->size() > 8)
MS_THROW_TYPE_ERROR("wrong listenInfos (too many entries)");
std::vector<ListenInfo> listenInfos(jsonListenInfosIt->size());
for (size_t i{ 0 }; i < jsonListenInfosIt->size(); ++i)
{
auto& jsonListenInfo = (*jsonListenInfosIt)[i];
auto& listenInfo = listenInfos[i];
if (!jsonListenInfo.is_object())
MS_THROW_TYPE_ERROR("wrong listenInfo (not an object)");
auto jsonProtocolIt = jsonListenInfo.find("protocol");
if (jsonProtocolIt == jsonListenInfo.end())
MS_THROW_TYPE_ERROR("missing listenInfo.protocol");
else if (!jsonProtocolIt->is_string())
MS_THROW_TYPE_ERROR("wrong listenInfo.protocol (not an string");
std::string protocolStr = jsonProtocolIt->get<std::string>();
Utils::String::ToLowerCase(protocolStr);
if (protocolStr == "udp")
listenInfo.protocol = RTC::TransportTuple::Protocol::UDP;
else if (protocolStr == "tcp")
listenInfo.protocol = RTC::TransportTuple::Protocol::TCP;
else
MS_THROW_TYPE_ERROR("invalid listenInfo.protocol (must be 'udp' or 'tcp'");
auto jsonIpIt = jsonListenInfo.find("ip");
if (jsonIpIt == jsonListenInfo.end())
MS_THROW_TYPE_ERROR("missing listenInfo.ip");
else if (!jsonIpIt->is_string())
MS_THROW_TYPE_ERROR("wrong listenInfo.ip (not an string");
listenInfo.ip.assign(jsonIpIt->get<std::string>());
Utils::IP::NormalizeIp(listenInfo.ip);
auto jsonAnnouncedIpIt = jsonListenInfo.find("announcedIp");
if (jsonAnnouncedIpIt != jsonListenInfo.end())
{
if (!jsonAnnouncedIpIt->is_string())
MS_THROW_TYPE_ERROR("wrong listenInfo.announcedIp (not an string)");
listenInfo.announcedIp.assign(jsonAnnouncedIpIt->get<std::string>());
}
auto jsonPortIt = jsonListenInfo.find("port");
if (jsonPortIt == jsonListenInfo.end())
MS_THROW_TYPE_ERROR("missing listenInfo.port");
else if (!(jsonPortIt->is_number() && Utils::Json::IsPositiveInteger(*jsonPortIt)))
MS_THROW_TYPE_ERROR("wrong listenInfo.port (not a positive number)");
listenInfo.port = jsonPortIt->get<uint16_t>();
}
try
{
for (auto& listenInfo : listenInfos)
{
if (listenInfo.protocol == RTC::TransportTuple::Protocol::UDP)
{
auto* udpSocket = new RTC::UdpSocket(this, listenInfo.ip, listenInfo.port);
this->udpSocketOrTcpServers.emplace_back(udpSocket, nullptr, listenInfo.announcedIp);
}
else if (listenInfo.protocol == RTC::TransportTuple::Protocol::TCP)
{
auto* tcpServer = new RTC::TcpServer(this, this, listenInfo.ip, listenInfo.port);
this->udpSocketOrTcpServers.emplace_back(nullptr, tcpServer, listenInfo.announcedIp);
}
}
}
catch (const MediaSoupError& error)
{
for (auto& item : this->udpSocketOrTcpServers)
{
delete item.udpSocket;
item.udpSocket = nullptr;
delete item.tcpServer;
item.tcpServer = nullptr;
}
this->udpSocketOrTcpServers.clear();
throw;
}
}
WebRtcServer::~WebRtcServer()
{
MS_TRACE();
for (auto& item : this->udpSocketOrTcpServers)
{
delete item.udpSocket;
item.udpSocket = nullptr;
delete item.tcpServer;
item.tcpServer = nullptr;
}
this->udpSocketOrTcpServers.clear();
for (auto* webRtcTransport : this->webRtcTransports)
{
webRtcTransport->ListenServerClosed();
}
this->webRtcTransports.clear();
}
void WebRtcServer::FillJson(json& jsonObject) const
{
MS_TRACE();
jsonObject["id"] = this->id;
jsonObject["udpSockets"] = json::array();
auto jsonUdpSocketsIt = jsonObject.find("udpSockets");
jsonObject["tcpServers"] = json::array();
auto jsonTcpServersIt = jsonObject.find("tcpServers");
size_t udpSocketIdx{ 0 };
size_t tcpServerIdx{ 0 };
for (auto& item : this->udpSocketOrTcpServers)
{
if (item.udpSocket)
{
jsonUdpSocketsIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonUdpSocketsIt)[udpSocketIdx];
jsonEntry["ip"] = item.udpSocket->GetLocalIp();
jsonEntry["port"] = item.udpSocket->GetLocalPort();
++udpSocketIdx;
}
else if (item.tcpServer)
{
jsonTcpServersIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonTcpServersIt)[tcpServerIdx];
jsonEntry["ip"] = item.tcpServer->GetLocalIp();
jsonEntry["port"] = item.tcpServer->GetLocalPort();
++tcpServerIdx;
}
}
jsonObject["webRtcTransportIds"] = json::array();
auto jsonWebRtcTransportIdsIt = jsonObject.find("webRtcTransportIds");
for (auto* webRtcTransport : this->webRtcTransports)
{
jsonWebRtcTransportIdsIt->emplace_back(webRtcTransport->id);
}
size_t idx;
jsonObject["localIceUsernameFragments"] = json::array();
auto jsonLocalIceUsernamesIt = jsonObject.find("localIceUsernameFragments");
idx = 0;
for (auto& kv : this->mapLocalIceUsernameFragmentWebRtcTransport)
{
const auto& localIceUsernameFragment = kv.first;
const auto* webRtcTransport = kv.second;
jsonLocalIceUsernamesIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonLocalIceUsernamesIt)[idx];
jsonEntry["localIceUsernameFragment"] = localIceUsernameFragment;
jsonEntry["webRtcTransportId"] = webRtcTransport->id;
++idx;
}
jsonObject["tupleHashes"] = json::array();
auto jsonTupleHashesIt = jsonObject.find("tupleHashes");
idx = 0;
for (auto& kv : this->mapTupleWebRtcTransport)
{
const auto& tupleHash = kv.first;
const auto* webRtcTransport = kv.second;
jsonTupleHashesIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonTupleHashesIt)[idx];
jsonEntry["tupleHash"] = tupleHash;
jsonEntry["webRtcTransportId"] = webRtcTransport->id;
++idx;
}
}
void WebRtcServer::HandleRequest(Channel::ChannelRequest* request)
{
MS_TRACE();
switch (request->methodId)
{
case Channel::ChannelRequest::MethodId::WEBRTC_SERVER_DUMP:
{
json data = json::object();
FillJson(data);
request->Accept(data);
break;
}
default:
{
MS_THROW_ERROR("unknown method '%s'", request->method.c_str());
}
}
}
std::vector<RTC::IceCandidate> WebRtcServer::GetIceCandidates(
bool enableUdp, bool enableTcp, bool preferUdp, bool preferTcp)
{
MS_TRACE();
std::vector<RTC::IceCandidate> iceCandidates;
uint16_t iceLocalPreferenceDecrement{ 0 };
for (auto& item : this->udpSocketOrTcpServers)
{
if (item.udpSocket && enableUdp)
{
uint16_t iceLocalPreference = IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;
if (preferUdp)
iceLocalPreference += 1000;
uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);
if (item.announcedIp.empty())
iceCandidates.emplace_back(item.udpSocket, icePriority);
else
iceCandidates.emplace_back(item.udpSocket, icePriority, item.announcedIp);
}
else if (item.tcpServer && enableTcp)
{
uint16_t iceLocalPreference = IceCandidateDefaultLocalPriority - iceLocalPreferenceDecrement;
if (preferTcp)
iceLocalPreference += 1000;
uint32_t icePriority = generateIceCandidatePriority(iceLocalPreference);
if (item.announcedIp.empty())
iceCandidates.emplace_back(item.tcpServer, icePriority);
else
iceCandidates.emplace_back(item.tcpServer, icePriority, item.announcedIp);
}
iceLocalPreferenceDecrement += 100;
}
return iceCandidates;
}
inline std::string WebRtcServer::GetLocalIceUsernameFragmentFromReceivedStunPacket(
RTC::StunPacket* packet) const
{
MS_TRACE();
auto& username = packet->GetUsername();
size_t colonPos = username.find(":");
if (colonPos == std::string::npos)
return username;
return username.substr(0, colonPos);
}
inline void WebRtcServer::OnPacketReceived(RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
if (RTC::StunPacket::IsStun(data, len))
{
OnStunDataReceived(tuple, data, len);
}
else
{
OnNonStunDataReceived(tuple, data, len);
}
}
inline void WebRtcServer::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_TAG(ice, "ignoring wrong STUN packet received");
return;
}
auto it1 = this->mapTupleWebRtcTransport.find(tuple->hash);
if (it1 != this->mapTupleWebRtcTransport.end())
{
auto* webRtcTransport = it1->second;
webRtcTransport->ProcessStunPacketFromWebRtcServer(tuple, packet);
delete packet;
return;
}
auto key = GetLocalIceUsernameFragmentFromReceivedStunPacket(packet);
auto it2 = this->mapLocalIceUsernameFragmentWebRtcTransport.find(key);
if (it2 == this->mapLocalIceUsernameFragmentWebRtcTransport.end())
{
MS_WARN_TAG(ice, "ignoring received STUN packet with unknown remote ICE usernameFragment");
delete packet;
return;
}
auto* webRtcTransport = it2->second;
webRtcTransport->ProcessStunPacketFromWebRtcServer(tuple, packet);
delete packet;
}
inline void WebRtcServer::OnNonStunDataReceived(
RTC::TransportTuple* tuple, const uint8_t* data, size_t len)
{
MS_TRACE();
auto it = this->mapTupleWebRtcTransport.find(tuple->hash);
if (it == this->mapTupleWebRtcTransport.end())
{
MS_WARN_TAG(ice, "ignoring received non STUN data from unknown tuple");
return;
}
auto* webRtcTransport = it->second;
webRtcTransport->ProcessNonStunPacketFromWebRtcServer(tuple, data, len);
}
inline void WebRtcServer::OnWebRtcTransportCreated(RTC::WebRtcTransport* webRtcTransport)
{
MS_TRACE();
MS_ASSERT(
this->webRtcTransports.find(webRtcTransport) == this->webRtcTransports.end(),
"WebRtcTransport already handled");
this->webRtcTransports.insert(webRtcTransport);
}
inline void WebRtcServer::OnWebRtcTransportClosed(RTC::WebRtcTransport* webRtcTransport)
{
MS_TRACE();
MS_ASSERT(
this->webRtcTransports.find(webRtcTransport) != this->webRtcTransports.end(),
"WebRtcTransport not handled");
this->webRtcTransports.erase(webRtcTransport);
}
inline void WebRtcServer::OnWebRtcTransportLocalIceUsernameFragmentAdded(
RTC::WebRtcTransport* webRtcTransport, const std::string& usernameFragment)
{
MS_TRACE();
MS_ASSERT(
this->mapLocalIceUsernameFragmentWebRtcTransport.find(usernameFragment) ==
this->mapLocalIceUsernameFragmentWebRtcTransport.end(),
"local ICE username fragment already exists in the table");
this->mapLocalIceUsernameFragmentWebRtcTransport[usernameFragment] = webRtcTransport;
}
inline void WebRtcServer::OnWebRtcTransportLocalIceUsernameFragmentRemoved(
RTC::WebRtcTransport* webRtcTransport, const std::string& usernameFragment)
{
MS_TRACE();
MS_ASSERT(
this->mapLocalIceUsernameFragmentWebRtcTransport.find(usernameFragment) !=
this->mapLocalIceUsernameFragmentWebRtcTransport.end(),
"local ICE username fragment not found in the table");
this->mapLocalIceUsernameFragmentWebRtcTransport.erase(usernameFragment);
}
inline void WebRtcServer::OnWebRtcTransportTransportTupleAdded(
RTC::WebRtcTransport* webRtcTransport, RTC::TransportTuple* tuple)
{
MS_TRACE();
if (this->mapTupleWebRtcTransport.find(tuple->hash) != this->mapTupleWebRtcTransport.end())
{
MS_WARN_TAG(ice, "tuple hash already exists in the table");
return;
}
this->mapTupleWebRtcTransport[tuple->hash] = webRtcTransport;
}
inline void WebRtcServer::OnWebRtcTransportTransportTupleRemoved(
RTC::WebRtcTransport* webRtcTransport, RTC::TransportTuple* tuple)
{
MS_TRACE();
if (this->mapTupleWebRtcTransport.find(tuple->hash) == this->mapTupleWebRtcTransport.end())
{
MS_WARN_TAG(ice, "tuple hash not found in the table");
return;
}
this->mapTupleWebRtcTransport.erase(tuple->hash);
}
inline void WebRtcServer::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 WebRtcServer::OnRtcTcpConnectionClosed(
RTC::TcpServer* , RTC::TcpConnection* connection)
{
MS_TRACE();
RTC::TransportTuple tuple(connection);
auto it = this->mapTupleWebRtcTransport.find(tuple.hash);
if (it == this->mapTupleWebRtcTransport.end())
return;
auto* webRtcTransport = it->second;
webRtcTransport->RemoveTuple(&tuple);
}
inline void WebRtcServer::OnTcpConnectionPacketReceived(
RTC::TcpConnection* connection, const uint8_t* data, size_t len)
{
MS_TRACE();
RTC::TransportTuple tuple(connection);
OnPacketReceived(&tuple, data, len);
}
}