- initial import
This commit is contained in:
35
src/libs/ice/CMakeLists.txt
Normal file
35
src/libs/ice/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
project (ice_stack)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set (ICE_STACK_SOURCES ICEAddress.cpp
|
||||
ICEAuthTransaction.cpp
|
||||
ICEBinding.cpp
|
||||
ICEBox.cpp
|
||||
ICEBoxImpl.cpp
|
||||
ICEByteBuffer.cpp
|
||||
ICECandidate.cpp
|
||||
ICECandidatePair.cpp
|
||||
ICECheckList.cpp
|
||||
ICECRC32.cpp
|
||||
ICEError.cpp
|
||||
ICELog.cpp
|
||||
ICEMD5.cpp
|
||||
ICENetworkHelper.cpp
|
||||
ICEPacketTimer.cpp
|
||||
ICEPlatform.cpp
|
||||
ICERelaying.cpp
|
||||
ICESession.cpp
|
||||
ICESHA1.cpp
|
||||
ICEStream.cpp
|
||||
ICEStunAttributes.cpp
|
||||
ICEStunConfig.cpp
|
||||
ICEStunMessage.cpp
|
||||
ICEStunTransaction.cpp
|
||||
ICESync.cpp
|
||||
ICETime.cpp
|
||||
ICETransactionList.cpp)
|
||||
|
||||
add_library(ice_stack ${ICE_STACK_SOURCES})
|
||||
42
src/libs/ice/ICEAction.h
Normal file
42
src/libs/ice/ICEAction.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_ACTION__H
|
||||
#define __ICE_ACTION__H
|
||||
|
||||
#include "ICESmartPtr.h"
|
||||
#include "ICECandidatePair.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class Transaction;
|
||||
struct Stream;
|
||||
class Logger;
|
||||
struct StackConfig;
|
||||
|
||||
class Action
|
||||
{
|
||||
protected:
|
||||
Stream& mStream;
|
||||
StackConfig& mConfig;
|
||||
|
||||
public:
|
||||
Action(Stream& stream, StackConfig& config)
|
||||
:mStream(stream), mConfig(config)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Action()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void finished(Transaction& transaction) = 0;
|
||||
};
|
||||
|
||||
typedef SmartPtr<Action> PAction;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
782
src/libs/ice/ICEAddress.cpp
Normal file
782
src/libs/ice/ICEAddress.cpp
Normal file
@@ -0,0 +1,782 @@
|
||||
/* Copyright(C) 2007-2018 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
# include <w32api.h>
|
||||
|
||||
# define WINVER WindowsVista
|
||||
# define _WIN32_WINDOWS WindowsVista
|
||||
# define _WIN32_WINNT WindowsVista
|
||||
#endif
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEAddress.h"
|
||||
#include "ICEError.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
# include <ws2tcpip.h>
|
||||
#else
|
||||
# include <netinet/in.h>
|
||||
# if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
# include <linux/in6.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
std::ostream& operator << (std::ostream& s, const ice::NetworkAddress& addr)
|
||||
{
|
||||
s << addr.toStdString().c_str();
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
std::wostream& operator << (std::wostream& s, const ice::NetworkAddress& addr)
|
||||
{
|
||||
s << addr.toStdWString();
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace ice;
|
||||
|
||||
NetworkAddress NetworkAddress::LoopbackAddress4("127.0.0.1", 1000);
|
||||
#ifdef TARGET_WIN
|
||||
static in_addr6 la6 = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
|
||||
#else
|
||||
static in6_addr la6 = { { { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 } } };
|
||||
#endif
|
||||
NetworkAddress NetworkAddress::LoopbackAddress6(la6, 1000);
|
||||
|
||||
|
||||
NetworkAddress NetworkAddress::parse(const std::string& s)
|
||||
{
|
||||
NetworkAddress result;
|
||||
result.mInitialized = !s.empty();
|
||||
if (result.mInitialized)
|
||||
{
|
||||
// Relayed or not
|
||||
result.mRelayed = s.find("relayed ") != std::string::npos;
|
||||
std::string::size_type ip4Pos = s.find("IPv4"), ip6Pos = s.find("IPv6");
|
||||
|
||||
if (ip4Pos == std::string::npos && ip6Pos == std::string::npos)
|
||||
{
|
||||
// Parse usual IP[:port] pair
|
||||
std::string::size_type cp = s.find(":");
|
||||
if (cp == std::string::npos)
|
||||
result.setIp(cp);
|
||||
else
|
||||
{
|
||||
result.setIp(s.substr(0, cp));
|
||||
result.setPort(atoi(s.substr(cp + 1).c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Family
|
||||
result.mAddr4.sin_family = ip4Pos != std::string::npos ? AF_INET : AF_INET6;
|
||||
|
||||
// IP:port
|
||||
std::string::size_type familyPos = ip4Pos != std::string::npos ? ip4Pos : ip6Pos;
|
||||
std::string addr = s.substr(familyPos + 5);
|
||||
|
||||
// Find IP substring and port
|
||||
std::string::size_type colonPos = addr.find_last_of(":");
|
||||
if (colonPos != std::string::npos)
|
||||
{
|
||||
int port = atoi(addr.substr(colonPos+1).c_str());
|
||||
result.setPort(port);
|
||||
result.setIp(addr.substr(0, colonPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NetworkAddress NetworkAddress::LoopbackAddress6("0:0:0:0:0:0:0:1", 1000);
|
||||
|
||||
NetworkAddress::NetworkAddress()
|
||||
:mInitialized(false), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
mAddr4.sin_family = AF_INET;
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(int stunType)
|
||||
:mInitialized(false), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
setStunType(stunType);
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const in6_addr& addr6, unsigned short port)
|
||||
:mInitialized(true), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
mAddr4.sin_family = AF_INET6;
|
||||
mAddr6.sin6_addr = addr6;
|
||||
mAddr6.sin6_port = htons(port);
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const in_addr& addr4, unsigned short port)
|
||||
:mInitialized(true), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
mAddr4.sin_family = AF_INET;
|
||||
mAddr4.sin_addr = addr4;
|
||||
mAddr4.sin_port = htons(port);
|
||||
}
|
||||
|
||||
unsigned char NetworkAddress::stunType() const
|
||||
{
|
||||
assert(mInitialized);
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return 1;
|
||||
|
||||
case AF_INET6:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NetworkAddress::setStunType(unsigned char st)
|
||||
{
|
||||
switch (st)
|
||||
{
|
||||
case 1:
|
||||
mAddr4.sin_family = AF_INET;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
mAddr6.sin6_family = AF_INET6;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const std::string& ip, unsigned short port)
|
||||
:mInitialized(true), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
setIp(ip);
|
||||
if (mAddr4.sin_family == AF_INET || mAddr4.sin_family == AF_INET6)
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const char *ip, unsigned short port)
|
||||
:mInitialized(true), mRelayed(false)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
setIp(ip);
|
||||
if (mAddr4.sin_family == AF_INET || mAddr4.sin_family == AF_INET6)
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const sockaddr& addr, unsigned addrLen)
|
||||
:mInitialized(true), mRelayed(false)
|
||||
{
|
||||
switch (addr.sa_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
memcpy(&mAddr6, &addr, addrLen);
|
||||
break;
|
||||
|
||||
case AF_INET:
|
||||
memset(&mAddr4, 0, sizeof(mAddr4));
|
||||
memcpy(&mAddr4, &addr, addrLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkAddress::NetworkAddress(const NetworkAddress& src)
|
||||
:mInitialized(src.mInitialized), mRelayed(src.mRelayed)
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
if (src.mAddr4.sin_family == AF_INET)
|
||||
memcpy(&mAddr4, &src.mAddr4, sizeof mAddr4);
|
||||
else
|
||||
memcpy(&mAddr6, &src.mAddr6, sizeof mAddr6);
|
||||
}
|
||||
|
||||
NetworkAddress::~NetworkAddress()
|
||||
{
|
||||
}
|
||||
|
||||
int NetworkAddress::family() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
|
||||
return this->mAddr4.sin_family;
|
||||
}
|
||||
|
||||
sockaddr* NetworkAddress::genericsockaddr() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return (sockaddr*)&mAddr4;
|
||||
|
||||
case AF_INET6:
|
||||
return (sockaddr*)&mAddr6;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned NetworkAddress::sockaddrLen() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return sizeof(mAddr4);
|
||||
|
||||
case AF_INET6:
|
||||
return sizeof(mAddr6);
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in* NetworkAddress::sockaddr4() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return (sockaddr_in*)&mAddr4;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sockaddr_in6* NetworkAddress::sockaddr6() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
return (sockaddr_in6*)&mAddr6;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NetworkAddress::setIp(NetworkAddress ipOnly)
|
||||
{
|
||||
// Save port
|
||||
mAddr4.sin_family = ipOnly.genericsockaddr()->sa_family;
|
||||
|
||||
switch (ipOnly.family())
|
||||
{
|
||||
case AF_INET:
|
||||
memcpy(&mAddr4.sin_addr, &ipOnly.sockaddr4()->sin_addr, 4);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
memcpy(&mAddr4.sin_addr, &ipOnly.sockaddr6()->sin6_addr, 16);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkAddress::setIp(const std::string& ip)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
int addrSize = sizeof(mAddr6);
|
||||
#endif
|
||||
|
||||
if (inet_addr(ip.c_str()) != INADDR_NONE)
|
||||
{
|
||||
mAddr4.sin_family = AF_INET;
|
||||
mAddr4.sin_addr.s_addr = inet_addr(ip.c_str());
|
||||
if (mAddr4.sin_port)
|
||||
mInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mAddr6.sin6_family = AF_INET6;
|
||||
#ifdef TARGET_WIN
|
||||
if (WSAStringToAddressA((char*)ip.c_str(), AF_INET6, NULL, (sockaddr*)&mAddr6, &addrSize) != 0)
|
||||
#else
|
||||
std::string ip2;
|
||||
if (ip.find('[') == 0 && ip.find(']') == ip.length()-1)
|
||||
ip2 = ip.substr(1, ip.length()-2);
|
||||
else
|
||||
ip2 = ip;
|
||||
|
||||
if (!inet_pton(AF_INET6, ip2.c_str(), &mAddr6.sin6_addr))
|
||||
#endif
|
||||
{
|
||||
mInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mAddr6.sin6_family = AF_INET6;
|
||||
if (mAddr6.sin6_port)
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NetworkAddress::setIp(unsigned long ip)
|
||||
{
|
||||
mAddr4.sin_family = AF_INET;
|
||||
mAddr4.sin_addr.s_addr = ip;
|
||||
|
||||
if (mAddr4.sin_port)
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void NetworkAddress::setIp(const in_addr& ip)
|
||||
{
|
||||
//memset(&mAddr4, 0, sizeof mAddr4);
|
||||
mAddr4.sin_family = AF_INET;
|
||||
mAddr4.sin_addr = ip;
|
||||
}
|
||||
|
||||
void NetworkAddress::setIp(const in6_addr& ip)
|
||||
{
|
||||
mAddr6.sin6_family = AF_INET6;
|
||||
mAddr6.sin6_addr = ip;
|
||||
}
|
||||
// 10.0.0.0 - 10.255.255.255
|
||||
// 172.16.0.0 - 172.31.255.255
|
||||
// 192.168.0.0 - 192.168.255.255
|
||||
|
||||
bool NetworkAddress::isSameLAN(const NetworkAddress& a1, const NetworkAddress& a2)
|
||||
{
|
||||
if (a1.family() != a2.family())
|
||||
return false;
|
||||
if (a1.family() == AF_INET)
|
||||
{
|
||||
sockaddr_in* s1 = a1.sockaddr4();
|
||||
sockaddr_in* s2 = a2.sockaddr4();
|
||||
#ifdef TARGET_WIN
|
||||
unsigned b1_0 = (s1->sin_addr.S_un.S_addr >> 0) & 0xFF;
|
||||
unsigned b1_1 = (s1->sin_addr.S_un.S_addr >> 8) & 0xFF;
|
||||
unsigned b2_0 = (s2->sin_addr.S_un.S_addr >> 0) & 0xFF;
|
||||
unsigned b2_1 = (s2->sin_addr.S_un.S_addr >> 8) & 0xFF;
|
||||
#else
|
||||
unsigned b1_0 = (s1->sin_addr.s_addr >> 0) & 0xFF;
|
||||
unsigned b1_1 = (s1->sin_addr.s_addr >> 8) & 0xFF;
|
||||
unsigned b2_0 = (s2->sin_addr.s_addr >> 0) & 0xFF;
|
||||
unsigned b2_1 = (s2->sin_addr.s_addr >> 8) & 0xFF;
|
||||
|
||||
if (b1_0 == b2_0 && b1_0 == 192 &&
|
||||
b1_1 == b2_1 && b1_1 == 168)
|
||||
return true;
|
||||
|
||||
if (b1_0 == b2_0 && b1_0 == 10)
|
||||
return true;
|
||||
|
||||
if (b1_0 == b2_0 && b1_0 == 172 &&
|
||||
b1_1 == b2_1 && (b1_1 < 32))
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkAddress::setPort(unsigned short port)
|
||||
{
|
||||
switch(mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
mAddr4.sin_port = htons(port);
|
||||
mInitialized = true;
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
mAddr6.sin6_port = htons(port);
|
||||
mInitialized = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::string NetworkAddress::ip() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
|
||||
char resultbuf[128], ip6[128]; resultbuf[0] = 0;
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
DWORD resultsize = sizeof(resultbuf);
|
||||
#endif
|
||||
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return inet_ntoa(mAddr4.sin_addr);
|
||||
|
||||
case AF_INET6:
|
||||
#if defined(TARGET_WIN)
|
||||
InetNtopA(AF_INET6, (PVOID)&mAddr6.sin6_addr, resultbuf, sizeof(resultbuf));
|
||||
#else
|
||||
inet_ntop(AF_INET6, &mAddr6.sin6_addr, resultbuf, sizeof(resultbuf));
|
||||
#endif
|
||||
sprintf(ip6, "[%s]", resultbuf);
|
||||
return ip6;
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
unsigned char* NetworkAddress::ipBytes() const
|
||||
{
|
||||
switch (family())
|
||||
{
|
||||
case AF_INET:
|
||||
#ifdef TARGET_WIN
|
||||
return (unsigned char*)&mAddr4.sin_addr.S_un.S_un_b;
|
||||
#else
|
||||
return (unsigned char*)&mAddr4.sin_addr.s_addr;
|
||||
#endif
|
||||
case AF_INET6:
|
||||
#ifdef TARGET_WIN
|
||||
return (unsigned char*)mAddr6.sin6_addr.u.Byte;
|
||||
#elif defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
return (unsigned char*)&mAddr6.sin6_addr.__u6_addr.__u6_addr8;
|
||||
#elif defined(TARGET_LINUX)
|
||||
return (unsigned char*)&mAddr6.sin6_addr.__in6_u.__u6_addr8;
|
||||
#elif defined(TARGET_ANDROID)
|
||||
return (unsigned char*)&mAddr6.sin6_addr.in6_u.u6_addr8;
|
||||
#endif
|
||||
}
|
||||
assert(0);
|
||||
return nullptr; // to avoid compiler warning
|
||||
}
|
||||
|
||||
unsigned short NetworkAddress::port() const
|
||||
{
|
||||
assert(mInitialized == true);
|
||||
|
||||
return ntohs(mAddr4.sin_port);
|
||||
}
|
||||
|
||||
std::string NetworkAddress::toStdString() const
|
||||
{
|
||||
if (!mInitialized)
|
||||
return "";
|
||||
|
||||
char temp[128];
|
||||
sprintf(temp, "%s%s %s:%u", mRelayed ? "relayed " : "", this->mAddr4.sin_family == AF_INET ? "IPv4" : "IPv6", ip().c_str(), (unsigned int)port());
|
||||
|
||||
return temp;
|
||||
}
|
||||
#ifdef WIN32
|
||||
std::wstring NetworkAddress::toStdWString() const
|
||||
{
|
||||
if (!mInitialized)
|
||||
return L"";
|
||||
|
||||
wchar_t temp[128];
|
||||
swprintf(temp, L"%s%s %S:%u", mRelayed ? L"relayed " : L"", this->mAddr4.sin_family == AF_INET ? L"IPv4" : L"IPv6", ip().c_str(), (unsigned int)port());
|
||||
|
||||
return temp;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const unsigned char localhost6[] =
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
bool NetworkAddress::isLoopback() const
|
||||
{
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return (mAddr4.sin_addr.s_addr == htonl(INADDR_LOOPBACK));
|
||||
|
||||
case AF_INET6:
|
||||
return memcmp(localhost6, &mAddr6.sin6_addr, sizeof mAddr6.sin6_addr) == 0;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isIp(const std::string& str)
|
||||
{
|
||||
if (inet_addr(str.c_str()) != INADDR_NONE)
|
||||
return true;
|
||||
const char* address = str.c_str();
|
||||
std::string temp;
|
||||
if (str.find('[') == 0 && str.find(']') == str.length()-1)
|
||||
{
|
||||
temp = str.substr(1, str.length()-2);
|
||||
address = temp.c_str();
|
||||
}
|
||||
sockaddr_in6 addr6;
|
||||
addr6.sin6_family = AF_INET6;
|
||||
|
||||
#ifdef _WIN32
|
||||
int addrSize = sizeof(addr6);
|
||||
if (WSAStringToAddressA((char*)address, AF_INET6, NULL, (sockaddr*)&addr6, &addrSize) != 0)
|
||||
#else
|
||||
if (!inet_pton(AF_INET6, address, &addr6))
|
||||
#endif
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isLAN() const
|
||||
{
|
||||
assert(mInitialized);
|
||||
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
unsigned char b1 = mAddr4.sin_addr.s_addr & 0xFF;
|
||||
unsigned char b2 = (mAddr4.sin_addr.s_addr >> 8) & 0xFF;
|
||||
if (b1 == 192 && b2 == 168)
|
||||
return true;
|
||||
if (b1 == 10)
|
||||
return true;
|
||||
if (b1 == 172 && (b2 >= 16 && b2 <= 31))
|
||||
return true;
|
||||
|
||||
// Link local addresses are not routable so report them here
|
||||
if (b1 == 169 && b2 == 254)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
case AF_INET6:
|
||||
return false;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isLinkLocal() const
|
||||
{
|
||||
assert(mInitialized);
|
||||
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
unsigned char b1 = mAddr4.sin_addr.s_addr & 0xFF;
|
||||
unsigned char b2 = (mAddr4.sin_addr.s_addr >> 8) & 0xFF;
|
||||
|
||||
// Link local addresses are not routable so report them here
|
||||
if (b1 == 169 && b2 == 254)
|
||||
return true;
|
||||
|
||||
//if (b1 == 100 && (b2 >= 64 && b2 <= 127))
|
||||
// return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
case AF_INET6:
|
||||
#ifdef WIN32
|
||||
return (mAddr6.sin6_addr.u.Byte[0] == 0xFE && mAddr6.sin6_addr.u.Byte[1] == 0x80);
|
||||
#else
|
||||
return IN6_IS_ADDR_LINKLOCAL(&mAddr6.sin6_addr);
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkAddress::clear()
|
||||
{
|
||||
memset(&mAddr6, 0, sizeof(mAddr6));
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isEmpty() const
|
||||
{
|
||||
return !mInitialized;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isZero() const
|
||||
{
|
||||
if (!mInitialized)
|
||||
return false;
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return mAddr4.sin_addr.s_addr == 0;
|
||||
|
||||
case AF_INET6:
|
||||
#ifdef WIN32
|
||||
return !mAddr6.sin6_addr.u.Word[0] && !mAddr6.sin6_addr.u.Word[1] &&
|
||||
!mAddr6.sin6_addr.u.Word[2] && !mAddr6.sin6_addr.u.Word[3] &&
|
||||
!mAddr6.sin6_addr.u.Word[4] && !mAddr6.sin6_addr.u.Word[5] &&
|
||||
!mAddr6.sin6_addr.u.Word[6] && !mAddr6.sin6_addr.u.Word[7];
|
||||
#else
|
||||
return IN6_IS_ADDR_UNSPECIFIED(&mAddr6.sin6_addr);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isV4() const
|
||||
{
|
||||
return family() == AF_INET;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isV6() const
|
||||
{
|
||||
return family() == AF_INET6;
|
||||
}
|
||||
|
||||
bool NetworkAddress::operator == (const NetworkAddress& rhs) const
|
||||
{
|
||||
return NetworkAddress::isSame(*this, rhs);
|
||||
}
|
||||
|
||||
bool NetworkAddress::operator != (const NetworkAddress& rhs) const
|
||||
{
|
||||
return !NetworkAddress::isSame(*this, rhs);
|
||||
}
|
||||
|
||||
bool NetworkAddress::operator < (const NetworkAddress& rhs) const
|
||||
{
|
||||
if (family() != rhs.family())
|
||||
return family() < rhs.family();
|
||||
|
||||
if (port() != rhs.port())
|
||||
return port() < rhs.port();
|
||||
|
||||
switch (family())
|
||||
{
|
||||
case AF_INET:
|
||||
return memcmp(sockaddr4(), rhs.sockaddr4(), sizeof(sockaddr_in)) < 0;
|
||||
|
||||
case AF_INET6:
|
||||
return memcmp(sockaddr6(), rhs.sockaddr6(), sizeof(sockaddr_in6)) < 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::operator > (const NetworkAddress& rhs) const
|
||||
{
|
||||
if (family() != rhs.family())
|
||||
return family() > rhs.family();
|
||||
|
||||
if (port() != rhs.port())
|
||||
return port() > rhs.port();
|
||||
|
||||
switch (family())
|
||||
{
|
||||
case AF_INET:
|
||||
return memcmp(sockaddr4(), rhs.sockaddr4(), sizeof(sockaddr_in)) > 0;
|
||||
|
||||
case AF_INET6:
|
||||
return memcmp(sockaddr6(), rhs.sockaddr6(), sizeof(sockaddr_in6)) > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isPublic() const
|
||||
{
|
||||
return !isLAN() && !isLinkLocal() && !isLoopback() && !isEmpty();
|
||||
}
|
||||
|
||||
void NetworkAddress::setRelayed(bool relayed)
|
||||
{
|
||||
mRelayed = relayed;
|
||||
}
|
||||
bool NetworkAddress::relayed() const
|
||||
{
|
||||
return mRelayed;
|
||||
}
|
||||
|
||||
bool NetworkAddress::hasHost(const std::vector<NetworkAddress>& hosts)
|
||||
{
|
||||
for (unsigned i=0; i<hosts.size(); i++)
|
||||
{
|
||||
const NetworkAddress& a = hosts[i];
|
||||
if (mAddr4.sin_family != a.mAddr4.sin_family)
|
||||
continue;
|
||||
|
||||
switch (mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
if (!memcmp(&mAddr4.sin_addr, &a.mAddr4.sin_addr, sizeof(mAddr4.sin_addr)))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (!memcmp(&mAddr6.sin6_addr, &a.mAddr6.sin6_addr, sizeof(mAddr6.sin6_addr)))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkAddress::isSameHost(const NetworkAddress& a1, const NetworkAddress& a2)
|
||||
{
|
||||
return (a1.ip() == a2.ip());
|
||||
}
|
||||
|
||||
bool NetworkAddress::isSame(const NetworkAddress& a1, const NetworkAddress& a2)
|
||||
{
|
||||
if (!a1.mInitialized || !a2.mInitialized)
|
||||
return false;
|
||||
|
||||
// Compare address families
|
||||
if (a1.mAddr4.sin_family != a2.mAddr4.sin_family)
|
||||
return false;
|
||||
|
||||
if (a1.mRelayed != a2.mRelayed)
|
||||
return false;
|
||||
|
||||
switch (a1.mAddr4.sin_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return a1.mAddr4.sin_addr.s_addr == a2.mAddr4.sin_addr.s_addr && a1.mAddr4.sin_port == a2.mAddr4.sin_port;
|
||||
|
||||
case AF_INET6:
|
||||
return memcmp(&a1.mAddr6.sin6_addr, &a2.mAddr6.sin6_addr, sizeof(a1.mAddr6.sin6_addr)) == 0 &&
|
||||
a1.mAddr6.sin6_port == a2.mAddr6.sin6_port;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
100
src/libs/ice/ICEAddress.h
Normal file
100
src/libs/ice/ICEAddress.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright(C) 2007-2018 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_ADDRESS_H
|
||||
#define __ICE_ADDRESS_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class NetworkAddress
|
||||
{
|
||||
public:
|
||||
static NetworkAddress LoopbackAddress4;
|
||||
static NetworkAddress LoopbackAddress6;
|
||||
static bool isSameLAN(const NetworkAddress& a1, const NetworkAddress& a2);
|
||||
static bool isIp(const std::string& str);
|
||||
static NetworkAddress parse(const std::string& s);
|
||||
|
||||
NetworkAddress();
|
||||
NetworkAddress(int stunType);
|
||||
NetworkAddress(const std::string& ip, unsigned short port);
|
||||
NetworkAddress(const char* ip, unsigned short port);
|
||||
NetworkAddress(const in6_addr& ip, unsigned short port);
|
||||
NetworkAddress(const in_addr& ip, unsigned short port);
|
||||
NetworkAddress(const sockaddr& addr, unsigned addrLen);
|
||||
NetworkAddress(const NetworkAddress& src);
|
||||
~NetworkAddress();
|
||||
|
||||
// Returns AF_INET or AF_INET6
|
||||
int family() const;
|
||||
|
||||
unsigned char stunType() const;
|
||||
void setStunType(unsigned char st);
|
||||
|
||||
sockaddr* genericsockaddr() const;
|
||||
sockaddr_in* sockaddr4() const;
|
||||
sockaddr_in6* sockaddr6() const;
|
||||
unsigned sockaddrLen() const;
|
||||
void setIp(const std::string& ip);
|
||||
void setIp(unsigned long ip);
|
||||
void setIp(const in_addr& ip);
|
||||
void setIp(const in6_addr& ip);
|
||||
void setIp(NetworkAddress ipOnly);
|
||||
|
||||
void setPort(unsigned short port);
|
||||
std::string ip() const;
|
||||
unsigned char* ipBytes() const;
|
||||
|
||||
unsigned short port() const;
|
||||
std::string toStdString() const;
|
||||
#ifdef WIN32
|
||||
std::wstring toStdWString() const;
|
||||
#endif
|
||||
bool isLoopback() const;
|
||||
bool isLAN() const;
|
||||
bool isLinkLocal() const;
|
||||
bool isPublic() const;
|
||||
bool isEmpty() const;
|
||||
bool isZero() const;
|
||||
bool isV4() const;
|
||||
bool isV6() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void setRelayed(bool relayed);
|
||||
bool relayed() const;
|
||||
bool hasHost(const std::vector<NetworkAddress>& hosts);
|
||||
|
||||
static bool isSameHost(const NetworkAddress& a1, const NetworkAddress& a2);
|
||||
|
||||
static bool isSame(const NetworkAddress& a1, const NetworkAddress& a2);
|
||||
|
||||
bool operator == (const NetworkAddress& rhs) const;
|
||||
bool operator != (const NetworkAddress& rhs) const;
|
||||
bool operator < (const NetworkAddress& rhs) const;
|
||||
bool operator > (const NetworkAddress& rhs) const;
|
||||
protected:
|
||||
union
|
||||
{
|
||||
sockaddr_in6 mAddr6;
|
||||
sockaddr_in mAddr4;
|
||||
};
|
||||
bool mRelayed;
|
||||
bool mInitialized;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& s, const ice::NetworkAddress& addr);
|
||||
#ifdef WIN32
|
||||
std::wostream& operator << (std::wostream& s, const ice::NetworkAddress& addr);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
251
src/libs/ice/ICEAuthTransaction.cpp
Normal file
251
src/libs/ice/ICEAuthTransaction.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEAuthTransaction.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICEMD5.h"
|
||||
#include "ICELog.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
AuthTransaction::AuthTransaction()
|
||||
:Transaction(), mActive(false), mComposed(false), mConformsToKeepaliveSchedule(true),
|
||||
mCredentialsEncoded(false)
|
||||
{
|
||||
}
|
||||
|
||||
AuthTransaction::~AuthTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
void AuthTransaction::init()
|
||||
{
|
||||
mConformsToKeepaliveSchedule = false;
|
||||
if (mRealm.size() && mNonce.size())
|
||||
buildAuthenticatedMsg();
|
||||
else
|
||||
{
|
||||
SmartPtr<StunMessage> msg(new StunMessage());
|
||||
msg->setTransactionId(mTransactionID);
|
||||
setInitialRequest(*msg);
|
||||
mComposed = false;
|
||||
mOutgoingMsgQueue.push_back(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthTransaction::buildAuthenticatedMsg()
|
||||
{
|
||||
// Setup key for MessageIntegrity
|
||||
std::string key = mUsername + ":" + mRealm + ":" + mPassword;
|
||||
md5Bin(key.c_str(), key.size(), mKey);
|
||||
|
||||
// Create new authenticated message
|
||||
SmartPtr<StunMessage> newMsg( new StunMessage() );
|
||||
|
||||
// Optional - generate new transaction ID
|
||||
// mTransactionID = StunMessage::TransactionID::GenerateNew();
|
||||
|
||||
newMsg->setTransactionId( mTransactionID );
|
||||
|
||||
// Add USERNAME
|
||||
newMsg->usernameAttr().setValue( mUsername );
|
||||
newMsg->realmAttr().setValue( mRealm );
|
||||
newMsg->nonceAttr().setValue( mNonce );
|
||||
|
||||
// Adjust message
|
||||
setAuthenticatedRequest( *newMsg );
|
||||
|
||||
// Ensure MessageIntegrity exists
|
||||
newMsg->messageIntegrityAttr().setValue( NULL );
|
||||
|
||||
// Enqueue msg
|
||||
mComposed = false;
|
||||
|
||||
mOutgoingMsgQueue.clear();
|
||||
mOutgoingMsgQueue.push_back( newMsg );
|
||||
}
|
||||
|
||||
bool AuthTransaction::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
if (msg.transactionId() != mTransactionID)
|
||||
return false;
|
||||
|
||||
// Check for 401 error code
|
||||
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
||||
{
|
||||
ErrorCode& ec = msg.errorCodeAttr();
|
||||
|
||||
// Get realm value - it must be long term credentials
|
||||
if (ec.errorCode() == 401 && (!mCredentialsEncoded || !mLongTerm))
|
||||
{
|
||||
if (!msg.hasAttribute(StunAttribute::Realm) || !msg.hasAttribute(StunAttribute::Nonce))
|
||||
return false;
|
||||
|
||||
Realm& realm = msg.realmAttr();
|
||||
|
||||
// Extract realm and nonce
|
||||
mRealm = realm.value();
|
||||
mNonce = msg.nonceAttr().value();
|
||||
|
||||
// Change transaction id
|
||||
if (mLongTerm)
|
||||
{
|
||||
mCredentialsEncoded = true;
|
||||
generateId();
|
||||
}
|
||||
ICELogDebug(<< "Server requested long term credentials for realm " << mRealm);
|
||||
buildAuthenticatedMsg();
|
||||
}
|
||||
else
|
||||
if (ec.errorCode() == 438 && (msg.hasAttribute(StunAttribute::Realm) || msg.hasAttribute(StunAttribute::Nonce)))
|
||||
{
|
||||
if (msg.hasAttribute(StunAttribute::Realm))
|
||||
mRealm = msg.realmAttr().value();
|
||||
if (msg.hasAttribute(StunAttribute::Nonce))
|
||||
mNonce = msg.nonceAttr().value();
|
||||
|
||||
ICELogDebug(<< "Returned error 438, using new nonce");
|
||||
if (msg.hasAttribute(StunAttribute::Realm) && msg.hasAttribute(StunAttribute::Nonce))
|
||||
{
|
||||
if (mLongTerm)
|
||||
{
|
||||
mCredentialsEncoded = true;
|
||||
generateId();
|
||||
}
|
||||
}
|
||||
buildAuthenticatedMsg();
|
||||
}
|
||||
else
|
||||
{
|
||||
mErrorCode = ec.errorCode();
|
||||
mErrorResponse = ec.errorPhrase();
|
||||
mState = Transaction::Failed;
|
||||
processError();
|
||||
|
||||
ICELogCritical(<<"Stack ID " << mStackID << ". Got error code " << mErrorCode << " for STUN transaction. Error message: " << ec.errorPhrase());
|
||||
}
|
||||
}
|
||||
else
|
||||
if (msg.messageClass() == StunMessage::ErrorClass)
|
||||
{
|
||||
mErrorCode = 0;
|
||||
mState = Transaction::Failed;
|
||||
processError();
|
||||
ICELogCritical(<<"Stack ID " << mStackID << ". Got ErrorClass response.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogDebug(<< "Process STUN success message");
|
||||
processSuccessMessage(msg, address);
|
||||
mState = Transaction::Success;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteBuffer* AuthTransaction::generateData(bool force)
|
||||
{
|
||||
if (!mActive)
|
||||
{
|
||||
init();
|
||||
mActive = true;
|
||||
}
|
||||
|
||||
if (!mComposed)
|
||||
{
|
||||
// Restart retransmission timer as new message will be built
|
||||
mRTOTimer.stop();
|
||||
mRTOTimer.start();
|
||||
|
||||
// Get message from outgoing queue
|
||||
if (!mOutgoingMsgQueue.empty())
|
||||
{
|
||||
// Clear buffer for raw data
|
||||
mOutgoingData.clear();
|
||||
|
||||
// Encode next message
|
||||
StunMessage& msg = *mOutgoingMsgQueue.front();
|
||||
|
||||
//ICELogDebug(<< "Stack ID " << mStackID << ". Build message " << msg.GetTransactionID().ToString() << " from transaction " << this->GetComment());
|
||||
|
||||
if (mShortTerm)
|
||||
msg.buildPacket(mOutgoingData, mPassword);
|
||||
else
|
||||
msg.buildPacket(mOutgoingData, std::string((char*)mKey, 16));
|
||||
|
||||
// Remove encoded message from msg queue
|
||||
mOutgoingMsgQueue.pop_front();
|
||||
}
|
||||
else
|
||||
ICELogDebug(<< "No outgoing message in queue");
|
||||
|
||||
mComposed = true;
|
||||
}
|
||||
|
||||
return Transaction::generateData(force);
|
||||
}
|
||||
|
||||
int AuthTransaction::errorCode()
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
|
||||
std::string AuthTransaction::errorResponse()
|
||||
{
|
||||
return mErrorResponse;
|
||||
}
|
||||
|
||||
void AuthTransaction::restart()
|
||||
{
|
||||
// Clear outgoing data
|
||||
mOutgoingData.clear();
|
||||
mOutgoingMsgQueue.clear();
|
||||
|
||||
mState = Transaction::Running;
|
||||
|
||||
// Mark "message is not built yet"
|
||||
mComposed = false;
|
||||
|
||||
// Mark "first request is not sent yet"
|
||||
mActive = false;
|
||||
|
||||
// Reset retransmission timer
|
||||
mRTOTimer.stop();
|
||||
mRTOTimer.start();
|
||||
mCredentialsEncoded = false;
|
||||
mConformsToKeepaliveSchedule = true;
|
||||
}
|
||||
|
||||
bool AuthTransaction::hasToRunNow()
|
||||
{
|
||||
bool result = Transaction::hasToRunNow();
|
||||
if (!result && keepalive())
|
||||
{
|
||||
result |= mState == /*TransactionState::*/Running && !mConformsToKeepaliveSchedule;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string AuthTransaction::realm() const
|
||||
{
|
||||
return mRealm;
|
||||
}
|
||||
|
||||
void AuthTransaction::setRealm(std::string realm)
|
||||
{
|
||||
mRealm = realm;
|
||||
}
|
||||
|
||||
std::string AuthTransaction::nonce() const
|
||||
{
|
||||
return mNonce;
|
||||
}
|
||||
|
||||
void AuthTransaction::setNonce(std::string nonce)
|
||||
{
|
||||
mNonce = nonce;
|
||||
}
|
||||
59
src/libs/ice/ICEAuthTransaction.h
Normal file
59
src/libs/ice/ICEAuthTransaction.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __AUTH_TRANSACTION_H
|
||||
#define __AUTH_TRANSACTION_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEStunTransaction.h"
|
||||
|
||||
namespace ice {
|
||||
|
||||
class AuthTransaction: public Transaction
|
||||
{
|
||||
public:
|
||||
AuthTransaction();
|
||||
virtual ~AuthTransaction();
|
||||
|
||||
// Creates initial request, calls SetInitialRequest. This method is called once from GenerateData.
|
||||
void init();
|
||||
|
||||
virtual void setInitialRequest(StunMessage& msg) = 0;
|
||||
virtual void setAuthenticatedRequest(StunMessage& msg) = 0;
|
||||
virtual void processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress) = 0;
|
||||
virtual void processError() {};
|
||||
|
||||
virtual bool processData(StunMessage& msg, NetworkAddress& address);
|
||||
virtual ByteBuffer* generateData(bool force = false);
|
||||
|
||||
int errorCode();
|
||||
std::string errorResponse();
|
||||
virtual void restart();
|
||||
virtual bool hasToRunNow();
|
||||
|
||||
std::string realm() const;
|
||||
void setRealm(std::string realm);
|
||||
|
||||
std::string nonce() const;
|
||||
void setNonce(std::string nonce);
|
||||
|
||||
protected:
|
||||
bool mActive;
|
||||
bool mComposed;
|
||||
int mErrorCode;
|
||||
std::string mErrorResponse;
|
||||
std::deque<SmartPtr<StunMessage> > mOutgoingMsgQueue;
|
||||
unsigned char mKey[16];
|
||||
bool mConformsToKeepaliveSchedule;
|
||||
std::string mRealm;
|
||||
std::string mNonce;
|
||||
bool mCredentialsEncoded;
|
||||
|
||||
void buildAuthenticatedMsg();
|
||||
};
|
||||
|
||||
} // end of namespace
|
||||
|
||||
#endif
|
||||
520
src/libs/ice/ICEBinding.cpp
Normal file
520
src/libs/ice/ICEBinding.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEBinding.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICETime.h"
|
||||
#include "ICELog.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace ice;
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
//----------------------- AuthClientBinding -----------------
|
||||
|
||||
ConnectivityCheck::ConnectivityCheck()
|
||||
{
|
||||
setComment("ConnectivityCheck");
|
||||
mErrorCode = 0;
|
||||
}
|
||||
|
||||
ConnectivityCheck::~ConnectivityCheck()
|
||||
{
|
||||
}
|
||||
|
||||
ByteBuffer* ConnectivityCheck::generateData(bool force)
|
||||
{
|
||||
if (!mComposed)
|
||||
{
|
||||
StunMessage msg;
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.setMessageType(StunMessage::Binding);
|
||||
msg.setTransactionId(mTransactionID);
|
||||
|
||||
if (mUseCandidate)
|
||||
msg.setAttribute(new ICEUseCandidate());
|
||||
|
||||
// Use message integrity attribute
|
||||
mMessageIntegrity = true;
|
||||
|
||||
// Use fingerprint attribute
|
||||
mFingerprint = true;
|
||||
|
||||
if (mEnablePriority)
|
||||
msg.icePriorityAttr().setPriority(mPriority);
|
||||
|
||||
// Copy comment to message object
|
||||
msg.setComment(mComment);
|
||||
|
||||
enqueueMessage(msg);
|
||||
|
||||
mComposed = true;
|
||||
}
|
||||
|
||||
return Transaction::generateData(force);
|
||||
}
|
||||
|
||||
void ConnectivityCheck::confirmTransaction(NetworkAddress& mapped)
|
||||
{
|
||||
// Save resolved address&ip
|
||||
mMappedAddress = mapped;
|
||||
|
||||
// Mark transaction as succeeded
|
||||
mState = Transaction::Success;
|
||||
|
||||
// Save source IP and port
|
||||
mResponseAddress = mapped;
|
||||
}
|
||||
|
||||
bool ConnectivityCheck::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
#ifdef ICE_TEST_VERYAGGRESSIVE
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Check if it is response
|
||||
if (msg.messageClass() != StunMessage::ErrorClass &&
|
||||
msg.messageClass() != StunMessage::SuccessClass)
|
||||
return false;
|
||||
|
||||
if (msg.transactionId() != mTransactionID)
|
||||
return false;
|
||||
|
||||
// Validate againgst password used to encrypt
|
||||
if (!msg.validatePacket(mPassword))
|
||||
return false;
|
||||
|
||||
// Check for ErrorCode attribute
|
||||
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
||||
{
|
||||
ErrorCode& ec = dynamic_cast<ErrorCode&>(msg.attribute(StunAttribute::ErrorCode));
|
||||
|
||||
//save error code and response
|
||||
mErrorCode = ec.errorCode();
|
||||
mErrorResponse = ec.errorPhrase();
|
||||
|
||||
//mark transaction as failed
|
||||
mState = Transaction::Failed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check if received empty ErrorClass response for poor servers
|
||||
if (msg.messageClass() == StunMessage::ErrorClass)
|
||||
{
|
||||
//mark transaction as failed
|
||||
mState = Transaction::Failed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check for mapped address attribute
|
||||
if (msg.hasAttribute(StunAttribute::MappedAddress))
|
||||
{
|
||||
// Save resolved address&ip
|
||||
mMappedAddress = msg.mappedAddressAttr().address();
|
||||
|
||||
// Mark transaction as succeeded
|
||||
mState = Transaction::Success;
|
||||
|
||||
// Save source IP and port
|
||||
mResponseAddress = address;
|
||||
}
|
||||
|
||||
//check for xor'ed mapped address attribute
|
||||
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
||||
{
|
||||
// Save resolved IP and port
|
||||
mMappedAddress = msg.xorMappedAddressAttr().address();
|
||||
|
||||
// Mark transaction as succeeded
|
||||
mState = Transaction::Success;
|
||||
|
||||
// Save source IP and port
|
||||
mResponseAddress = address;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NetworkAddress& ConnectivityCheck::mappedAddress()
|
||||
{
|
||||
return mMappedAddress;
|
||||
}
|
||||
NetworkAddress& ConnectivityCheck::responseAddress()
|
||||
{
|
||||
return mResponseAddress;
|
||||
}
|
||||
void ConnectivityCheck::addUseCandidate()
|
||||
{
|
||||
mUseCandidate = true;
|
||||
}
|
||||
int ConnectivityCheck::errorCode()
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
int ConnectivityCheck::resultError()
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
NetworkAddress& ConnectivityCheck::resultSource()
|
||||
{
|
||||
return mResponseAddress;
|
||||
}
|
||||
Transaction::State ConnectivityCheck::resultState()
|
||||
{
|
||||
return state();
|
||||
}
|
||||
NetworkAddress& ConnectivityCheck::resultLocal()
|
||||
{
|
||||
return mMappedAddress;
|
||||
}
|
||||
void ConnectivityCheck::useCandidate()
|
||||
{
|
||||
addUseCandidate();
|
||||
}
|
||||
unsigned ConnectivityCheck::resultPriority()
|
||||
{
|
||||
return priorityValue();
|
||||
}
|
||||
//---------------------------- ClientBindingTransaction ----------------------------
|
||||
|
||||
ClientBinding::ClientBinding()
|
||||
:Transaction(), mErrorCode(0), mUseCandidate(false)
|
||||
{
|
||||
setComment("ClientBinding");
|
||||
}
|
||||
|
||||
ClientBinding::~ClientBinding()
|
||||
{
|
||||
}
|
||||
|
||||
int ClientBinding::errorCode()
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
|
||||
std::string ClientBinding::errorResponse()
|
||||
{
|
||||
return mErrorResponse;
|
||||
}
|
||||
|
||||
NetworkAddress& ClientBinding::mappedAddress()
|
||||
{
|
||||
return mMappedAddress;
|
||||
}
|
||||
|
||||
NetworkAddress& ClientBinding::responseAddress()
|
||||
{
|
||||
return mResponseAddress;
|
||||
}
|
||||
|
||||
bool ClientBinding::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
// Check if it is response
|
||||
if (msg.messageClass() != StunMessage::ErrorClass &&
|
||||
msg.messageClass() != StunMessage::SuccessClass)
|
||||
return false;
|
||||
|
||||
if (msg.transactionId() != this->mTransactionID)
|
||||
return false;
|
||||
|
||||
// Check for ErrorCode attribute
|
||||
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
||||
{
|
||||
// Save error code and response
|
||||
mErrorCode = msg.errorCodeAttr().errorCode();
|
||||
mErrorResponse = msg.errorCodeAttr().errorPhrase();
|
||||
|
||||
// Mark transaction as failed
|
||||
mState = Transaction::Failed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if received empty ErrorClass response for poor servers
|
||||
if (msg.messageClass() == StunMessage::ErrorClass)
|
||||
{
|
||||
// Mark transaction as failed
|
||||
mState = Transaction::Failed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for mapped address attribute
|
||||
if (msg.hasAttribute(StunAttribute::MappedAddress))
|
||||
{
|
||||
// Save resolved address&ip
|
||||
mMappedAddress = msg.mappedAddressAttr().address();
|
||||
|
||||
// mMark transaction as succeeded
|
||||
mState = Transaction::Success;
|
||||
|
||||
// Save source IP and port
|
||||
mResponseAddress = address;
|
||||
}
|
||||
|
||||
//check for xor'ed mapped address attribute
|
||||
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
||||
{
|
||||
// Save resolved IP and port
|
||||
mMappedAddress = msg.xorMappedAddressAttr().address();
|
||||
|
||||
// Mark transaction as succeeded
|
||||
mState = Transaction::Success;
|
||||
|
||||
// Save source IP and port
|
||||
mResponseAddress = address;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteBuffer* ClientBinding::generateData(bool force)
|
||||
{
|
||||
if (!mComposed)
|
||||
{
|
||||
StunMessage msg;
|
||||
msg.setMessageType(ice::StunMessage::Binding);
|
||||
msg.setMessageClass(ice::StunMessage::RequestClass);
|
||||
msg.setTransactionId(mTransactionID);
|
||||
|
||||
if (mUseCandidate)
|
||||
msg.setAttribute(new ICEUseCandidate());
|
||||
|
||||
// Copy comment to message object
|
||||
msg.setComment(mComment);
|
||||
|
||||
enqueueMessage(msg);
|
||||
|
||||
mComposed = true;
|
||||
}
|
||||
|
||||
return Transaction::generateData(force);
|
||||
}
|
||||
|
||||
void ClientBinding::addUseCandidate()
|
||||
{
|
||||
mUseCandidate = true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------ ServerBindingTransaction ---------------------------
|
||||
ServerBinding::ServerBinding()
|
||||
{
|
||||
setComment("ServerBinding");
|
||||
|
||||
mGenerate400 = false;
|
||||
mGenerate487 = false;
|
||||
mRole = 0;
|
||||
}
|
||||
|
||||
ServerBinding::~ServerBinding()
|
||||
{
|
||||
}
|
||||
void ServerBinding::setLocalTieBreaker(std::string tieBreaker)
|
||||
{
|
||||
mLocalTieBreaker = tieBreaker;
|
||||
}
|
||||
|
||||
bool ServerBinding::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
if (msg.messageType() != StunMessage::Binding || msg.messageClass() != StunMessage::RequestClass)
|
||||
return false;
|
||||
ICELogDebug(<< "Received Binding request from " << address.toStdString().c_str());
|
||||
|
||||
// Save visible address
|
||||
mSourceAddress = address;
|
||||
|
||||
// Save transaction ID
|
||||
mTransactionID = msg.transactionId();
|
||||
|
||||
// Save Priority value
|
||||
if (msg.hasAttribute(StunAttribute::ICEPriority))
|
||||
{
|
||||
mEnablePriority = true;
|
||||
mPriority = msg.icePriorityAttr().priority();
|
||||
}
|
||||
|
||||
// Save UseCandidate flag
|
||||
mUseCandidate = msg.hasAttribute(StunAttribute::ICEUseCandidate);
|
||||
|
||||
// Check if auth credentials are needed
|
||||
mGenerate400 = false; //do not send 400 response by default
|
||||
mGenerate487 = false;
|
||||
|
||||
if (!msg.hasAttribute(StunAttribute::Username) || !msg.hasAttribute(StunAttribute::MessageIntegrity))
|
||||
{
|
||||
ICELogCritical(<< "There is no Username or MessageIntegrity attributes in Binding required. Error 400 will be generated.");
|
||||
// Send 400 error
|
||||
mGenerate400 = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for role
|
||||
if (msg.hasAttribute(StunAttribute::ControlledAttr))
|
||||
{
|
||||
mRemoteTieBreaker = msg.iceControlledAttr().tieBreaker();
|
||||
mRole = 1; // Session::Controlled;
|
||||
}
|
||||
else
|
||||
if (msg.hasAttribute(StunAttribute::ControllingAttr))
|
||||
{
|
||||
mRemoteTieBreaker = msg.iceControllingAttr().tieBreaker();
|
||||
mRole = 2;// Session::Controlling;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ByteBuffer* ServerBinding::generateData(bool force)
|
||||
{
|
||||
// Restart timer
|
||||
mRTOTimer.stop();
|
||||
mRTOTimer.start();
|
||||
|
||||
// See if remote password / username are set
|
||||
if (mPassword.empty() || mUsername.empty())
|
||||
return NULL;
|
||||
|
||||
StunMessage msg;
|
||||
|
||||
// Set transaction ID the same as processed incoming message
|
||||
msg.setTransactionId(mTransactionID);
|
||||
msg.setMessageType(StunMessage::Binding);
|
||||
|
||||
if (mGenerate400)
|
||||
{
|
||||
// Generate bad request error
|
||||
msg.setMessageClass(StunMessage::ErrorClass);
|
||||
|
||||
msg.errorCodeAttr().setErrorCode(400);
|
||||
msg.errorCodeAttr().setErrorPhrase("Bad request");
|
||||
}
|
||||
else
|
||||
if (mGenerate487)
|
||||
{
|
||||
// Generate 487 error
|
||||
msg.setMessageClass(StunMessage::ErrorClass);
|
||||
msg.errorCodeAttr().setErrorCode(487);
|
||||
msg.errorCodeAttr().setErrorPhrase("Role conflict");
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.setMessageClass(StunMessage::SuccessClass);
|
||||
|
||||
msg.mappedAddressAttr().address() = mSourceAddress;
|
||||
msg.xorMappedAddressAttr().address() = mSourceAddress;
|
||||
}
|
||||
|
||||
// Build message
|
||||
|
||||
// Clear outgoing buffer
|
||||
mOutgoingData.clear();
|
||||
|
||||
// Check if message should be secured by message integrity
|
||||
//std::string password;
|
||||
if (!mGenerate400 && !mGenerate487)
|
||||
{
|
||||
// Do not create username attribute here - response does not need it
|
||||
// msg.usernameAttr().setValue(mUsername);
|
||||
|
||||
//ICELogCritical(<< "Using password " << mPassword);
|
||||
|
||||
// Add message integrity attribute
|
||||
msg.setAttribute(new MessageIntegrity());
|
||||
|
||||
if (mFingerprint)
|
||||
msg.setAttribute(new Fingerprint());
|
||||
|
||||
// Add ICE-CONTROLLED attribute if needed
|
||||
if (mEnableControlled)
|
||||
msg.iceControlledAttr().setTieBreaker(mLocalTieBreaker);
|
||||
|
||||
// Add ICE-CONTROLLING attribute if needed
|
||||
if (mEnableControlling)
|
||||
msg.iceControllingAttr().setTieBreaker(mLocalTieBreaker);
|
||||
|
||||
// Add ICE-PRIORITY attribute if needed
|
||||
if (mEnablePriority)
|
||||
msg.icePriorityAttr().setPriority(mPriority);
|
||||
}
|
||||
|
||||
// Build packet
|
||||
msg.buildPacket(mOutgoingData, mPassword);
|
||||
|
||||
// Copy comment
|
||||
msg.setComment(mComment);
|
||||
|
||||
mComposed = true;
|
||||
|
||||
return new ByteBuffer(mOutgoingData);
|
||||
}
|
||||
|
||||
bool ServerBinding::hasUseCandidate()
|
||||
{
|
||||
return mUseCandidate;
|
||||
}
|
||||
|
||||
bool ServerBinding::gotRequest()
|
||||
{
|
||||
return !mGenerate400 && !mGenerate487;
|
||||
}
|
||||
|
||||
int ServerBinding::role()
|
||||
{
|
||||
return mRole;
|
||||
}
|
||||
|
||||
void ServerBinding::generate487()
|
||||
{
|
||||
mGenerate487 = true;
|
||||
}
|
||||
|
||||
std::string ServerBinding::remoteTieBreaker()
|
||||
{
|
||||
return mRemoteTieBreaker;
|
||||
}
|
||||
|
||||
void ServerBinding::restart()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------- BindingIndication ---------------
|
||||
BindingIndication::BindingIndication(unsigned int interval)
|
||||
:mInterval(interval)
|
||||
{
|
||||
setComment("BindingIndication");
|
||||
}
|
||||
|
||||
BindingIndication::~BindingIndication()
|
||||
{
|
||||
}
|
||||
|
||||
bool BindingIndication::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
return (msg.messageClass() == StunMessage::IndicationClass &&
|
||||
msg.messageType() == StunMessage::Binding);
|
||||
}
|
||||
|
||||
|
||||
ByteBuffer* BindingIndication::generateData(bool force)
|
||||
{
|
||||
if (!mComposed)
|
||||
{
|
||||
StunMessage msg;
|
||||
msg.setMessageClass(StunMessage::IndicationClass);
|
||||
msg.setMessageType(StunMessage::Binding);
|
||||
msg.setTransactionId(mTransactionID);
|
||||
msg.setComment(mComment);
|
||||
enqueueMessage(msg);
|
||||
mComposed = true;
|
||||
}
|
||||
|
||||
return Transaction::generateData(force);
|
||||
}
|
||||
|
||||
131
src/libs/ice/ICEBinding.h
Normal file
131
src/libs/ice/ICEBinding.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_BINDING_H
|
||||
#define __ICE_BINDING_H
|
||||
|
||||
#include "ICEStunTransaction.h"
|
||||
#include "ICEAuthTransaction.h"
|
||||
#include "ICEAddress.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class CheckResult
|
||||
{
|
||||
public:
|
||||
virtual Transaction::State resultState() = 0;
|
||||
virtual int resultError() = 0;
|
||||
virtual NetworkAddress& resultSource() = 0;
|
||||
virtual NetworkAddress& resultLocal() = 0;
|
||||
virtual void useCandidate() = 0;
|
||||
virtual unsigned resultPriority() = 0;
|
||||
};
|
||||
|
||||
class ConnectivityCheck: public Transaction, public CheckResult
|
||||
{
|
||||
public:
|
||||
ConnectivityCheck();
|
||||
virtual ~ConnectivityCheck();
|
||||
|
||||
ByteBuffer* generateData(bool force = false);
|
||||
bool processData(StunMessage& msg, NetworkAddress& sourceAddress);
|
||||
|
||||
NetworkAddress& mappedAddress(); //returns result from succesful transaction
|
||||
NetworkAddress& responseAddress(); //returns responses source address
|
||||
int errorCode();
|
||||
void addUseCandidate();
|
||||
void confirmTransaction(NetworkAddress& mapped);
|
||||
|
||||
// CheckResult interface
|
||||
int resultError();
|
||||
NetworkAddress& resultSource();
|
||||
NetworkAddress& resultLocal();
|
||||
State resultState();
|
||||
void useCandidate();
|
||||
unsigned resultPriority();
|
||||
protected:
|
||||
NetworkAddress mResponseAddress;
|
||||
NetworkAddress mMappedAddress;
|
||||
bool mUseCandidate;
|
||||
int mErrorCode;
|
||||
std::string mErrorResponse;
|
||||
};
|
||||
|
||||
class ClientBinding: public Transaction
|
||||
{
|
||||
public:
|
||||
ClientBinding();
|
||||
virtual ~ClientBinding();
|
||||
|
||||
int errorCode(); //returns error code from failed transaction
|
||||
std::string errorResponse(); //returns error msg from failed transaction
|
||||
NetworkAddress& mappedAddress(); //returns result from succesful transaction
|
||||
NetworkAddress& responseAddress(); //returns responses source address
|
||||
|
||||
bool processData(StunMessage& msg, NetworkAddress& address);
|
||||
ByteBuffer* generateData(bool force = false);
|
||||
void addUseCandidate();
|
||||
|
||||
protected:
|
||||
int mErrorCode;
|
||||
NetworkAddress mMappedAddress;
|
||||
std::string mErrorResponse;
|
||||
NetworkAddress mResponseAddress;
|
||||
bool mUseCandidate;
|
||||
};
|
||||
|
||||
class ServerBinding: public Transaction
|
||||
{
|
||||
public:
|
||||
ServerBinding();
|
||||
virtual ~ServerBinding();
|
||||
void setLocalTieBreaker(std::string tieBreaker);
|
||||
|
||||
bool processData(StunMessage& msg, NetworkAddress& address);
|
||||
ByteBuffer* generateData(bool force = false);
|
||||
void restart();
|
||||
|
||||
// Checks if incoming request has UseCandidate attribute
|
||||
bool hasUseCandidate();
|
||||
|
||||
// Checks if processed StunMessage was authenticated ok
|
||||
bool gotRequest();
|
||||
|
||||
// Gets the role from processed message. It can be Controlled or Controlling or None.
|
||||
int role();
|
||||
|
||||
// Instructs transaction to response with 487 code
|
||||
void generate487();
|
||||
|
||||
// Returns received tie breaker
|
||||
std::string remoteTieBreaker();
|
||||
|
||||
protected:
|
||||
NetworkAddress mSourceAddress;
|
||||
bool mUseCandidate;
|
||||
bool mGenerate400;
|
||||
bool mGenerate487;
|
||||
int mRole;
|
||||
std::string mLocalTieBreaker;
|
||||
std::string mRemoteTieBreaker;
|
||||
};
|
||||
|
||||
|
||||
class BindingIndication: public Transaction
|
||||
{
|
||||
public:
|
||||
BindingIndication(unsigned int interval);
|
||||
virtual ~BindingIndication();
|
||||
|
||||
bool processData(StunMessage& msg, NetworkAddress& address);
|
||||
ByteBuffer* generateData(bool force = false);
|
||||
|
||||
protected:
|
||||
unsigned mTimestamp;
|
||||
unsigned mInterval;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
60
src/libs/ice/ICEBox.cpp
Normal file
60
src/libs/ice/ICEBox.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEBox.h"
|
||||
#include "ICEBoxImpl.h"
|
||||
#include <time.h>
|
||||
|
||||
using namespace ice;
|
||||
|
||||
Stack::~Stack()
|
||||
{
|
||||
}
|
||||
|
||||
void Stack::initialize()
|
||||
{
|
||||
}
|
||||
|
||||
void Stack::finalize()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
Stack* Stack::makeICEBox(const ServerConfig& config)
|
||||
{
|
||||
return new StackImpl(config);
|
||||
}
|
||||
|
||||
bool Stack::isDataIndication(ByteBuffer& source, ByteBuffer* plain)
|
||||
{
|
||||
return Session::isDataIndication(source, plain);
|
||||
}
|
||||
|
||||
bool Stack::isStun(ByteBuffer& source)
|
||||
{
|
||||
return Session::isStun(source);
|
||||
}
|
||||
|
||||
bool Stack::isRtp(ByteBuffer& data)
|
||||
{
|
||||
return Session::isRtp(data);
|
||||
}
|
||||
|
||||
bool Stack::isChannelData(ByteBuffer& data, TurnPrefix prefix)
|
||||
{
|
||||
return Session::isChannelData(data, prefix);
|
||||
}
|
||||
|
||||
ByteBuffer Stack::makeChannelData(TurnPrefix prefix, const void* data, unsigned datasize)
|
||||
{
|
||||
ByteBuffer result;
|
||||
result.resize(4 + datasize);
|
||||
BufferWriter writer(result);
|
||||
writer.writeUShort(prefix);
|
||||
writer.writeUShort(datasize);
|
||||
writer.writeBuffer(data, datasize);
|
||||
|
||||
return result;
|
||||
}
|
||||
251
src/libs/ice/ICEBox.h
Normal file
251
src/libs/ice/ICEBox.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __NAT_ICE_H
|
||||
#define __NAT_ICE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ICESync.h"
|
||||
#include "ICEEvent.h"
|
||||
#include "ICEAddress.h"
|
||||
#include "ICEByteBuffer.h"
|
||||
#include "ICECandidate.h"
|
||||
|
||||
#define ICE_TIMEOUT 8000
|
||||
#define ICE_CHECK_INTERVAL 20
|
||||
|
||||
#define ICE_RTP_ID 1
|
||||
#define ICE_RTCP_ID 2
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
// Structure to describe used STUN/TURN configuration
|
||||
struct ServerConfig
|
||||
{
|
||||
std::vector<NetworkAddress> mServerList4, mServerList6; // List of STUN/TURN servers for IPv4 and IPv6 protocols
|
||||
bool mUseIPv4; // Marks if IPv4 should be used when gathering candidates
|
||||
bool mUseIPv6; // Marks if IPv6 should be used when gathering candidates
|
||||
std::string mHost; // Target host to get default IP interface; usually it is public IP address
|
||||
bool mRelay; // Marks if TURN is to be used instead STUN
|
||||
unsigned int mTimeout; // Timeout value in milliseconds
|
||||
unsigned int mPeriod; // Transmission interval
|
||||
std::string mUsername; // User name for TURN server [optional]
|
||||
std::string mPassword; // Password for TURN server [optional]
|
||||
|
||||
ServerConfig()
|
||||
:mUseIPv4(true), mUseIPv6(true), mRelay(false), mTimeout(ICE_TIMEOUT), mPeriod(ICE_CHECK_INTERVAL)
|
||||
{
|
||||
}
|
||||
|
||||
~ServerConfig()
|
||||
{}
|
||||
};
|
||||
|
||||
enum IceState
|
||||
{
|
||||
IceNone = 0, // Stack non active now
|
||||
IceGathering = 1, // Stack gathering candidates now
|
||||
IceGathered = 2, // Stack gathered candidates
|
||||
IceChecking = 3, // Stack runs connectivity checks now
|
||||
IceCheckSuccess = 4, // Connectivity checks were successful
|
||||
IceFailed = 5, // Connectivity checks or gathering failed
|
||||
IceTimeout = 6 // Timeout
|
||||
};
|
||||
|
||||
|
||||
enum AgentRole
|
||||
{
|
||||
RoleControlled = 1,
|
||||
RoleControlling = 2
|
||||
};
|
||||
|
||||
// ICE stack
|
||||
class Stack
|
||||
{
|
||||
public:
|
||||
static void initialize();
|
||||
static void finalize();
|
||||
|
||||
static Stack* makeICEBox(const ServerConfig& config);
|
||||
|
||||
// Service methods to check incoming packets
|
||||
static bool isDataIndication(ByteBuffer& source, ByteBuffer* plain);
|
||||
static bool isStun(ByteBuffer& source);
|
||||
static bool isRtp(ByteBuffer& data);
|
||||
static bool isChannelData(ByteBuffer& data, TurnPrefix prefix);
|
||||
|
||||
static ByteBuffer makeChannelData(TurnPrefix prefix, const void* data, unsigned datasize);
|
||||
|
||||
/*! Sets ICE event handler pointer in stack.
|
||||
* @param handler Pointer to ICE event handler instance.
|
||||
* @param tag Custom user tag. */
|
||||
virtual void setEventHandler(StageHandler* handler, void* tag) = 0;
|
||||
|
||||
/*! Adds new stream to ICE stack object.
|
||||
* @return ID of created stream.
|
||||
*/
|
||||
virtual int addStream() = 0;
|
||||
|
||||
/*! Adds new component (socket) to media stream.
|
||||
* @param portNumber specifies used local port number for the socket.
|
||||
* @returns component ID. This ID is unique only for specified stream. Two components in different streams can have the same */
|
||||
virtual int addComponent(int streamID, void* tag, unsigned short port4, unsigned short port6) = 0;
|
||||
|
||||
/*! Removes media stream from ICE stack object. */
|
||||
virtual void removeStream(int streamID) = 0;
|
||||
|
||||
virtual bool hasStream(int streamId) = 0;
|
||||
virtual bool hasComponent(int streamId, int componentId) = 0;
|
||||
virtual void setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6) = 0;
|
||||
|
||||
virtual void setRole(AgentRole role) = 0;
|
||||
virtual AgentRole role() = 0;
|
||||
|
||||
/*! Processes incoming data.
|
||||
* @param streamID ICE stream ID
|
||||
* @param componentID ICE component ID
|
||||
* @param sourceIP IP of remote peer
|
||||
* @param port number of remote peer
|
||||
* @param sourceBuffer pointer to incoming data buffer
|
||||
* @param sourceSize size of incoming data (in bytes)
|
||||
*/
|
||||
virtual bool processIncomingData(int stream, int component, ByteBuffer& incomingData) = 0;
|
||||
|
||||
/*! Generates outgoing data for sending.
|
||||
* @param response marks if the returned packet is response packet to remote peer's request
|
||||
* @param streamID ICE stream ID
|
||||
* @param componentID ICE component ID
|
||||
* @param destIP Character buffer to write destination IP address in textual form
|
||||
* @param destPort Destination port number.
|
||||
* @param dataBuffer Pointer to output buffer. It must be big enough to include at least 1500 bytes - it is biggest UDP datagram in this library.
|
||||
*/
|
||||
virtual PByteBuffer generateOutgoingData(bool& response, int& stream, int& component, void*& tag) = 0;
|
||||
|
||||
/*! Searches stream&component IDs by used local port and socket family. */
|
||||
virtual bool findStreamAndComponent(int family, unsigned short port, int* stream, int* component) = 0;
|
||||
|
||||
/*! Starts to gather local candidates. */
|
||||
virtual void gatherCandidates() = 0;
|
||||
|
||||
/*! Starts ICE connectivity checks. */
|
||||
virtual void checkConnectivity() = 0;
|
||||
|
||||
/*! Checks if gathering is finished. */
|
||||
virtual IceState state() = 0;
|
||||
|
||||
/*! Creates common part of SDP. It includes ice-full attribute and ufrag/pwd pair.
|
||||
* @param common Common part of SDP. */
|
||||
virtual void createSdp(std::vector<std::string>& common) = 0;
|
||||
|
||||
/*! Returns default address for specified stream/component ID.
|
||||
* @param ip Default IP address.
|
||||
* @param port Default port number. */
|
||||
virtual NetworkAddress defaultAddress(int streamID, int componentID) = 0;
|
||||
|
||||
/*! Returns candidate list for specified stream/component ID.
|
||||
* @param streamID Stream ID.
|
||||
* @param componentID Component ID.
|
||||
* @param candidateList Output vector of local candidates. */
|
||||
virtual void fillCandidateList(int streamID, int componentID, std::vector<std::string>& candidateList) = 0;
|
||||
|
||||
/*! Process ICE offer text for specified stream.
|
||||
* @param streamIndex ICE stream index.
|
||||
* @param candidateList Input vector of strings - it holds candidate list.
|
||||
* @param defaultIP Default IP for component ID 0.
|
||||
* @param defaultPort Default port number for component ID 0.
|
||||
* @return Returns true if processing(parsing) was ok, otherwise method returns false. */
|
||||
virtual bool processSdpOffer(int streamIndex, std::vector<std::string>& candidateList,
|
||||
const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed) = 0;
|
||||
|
||||
virtual NetworkAddress getRemoteRelayedCandidate(int stream, int component) = 0;
|
||||
virtual NetworkAddress getRemoteReflexiveCandidate(int stream, int component) = 0;
|
||||
|
||||
/*! Notifies stack about ICE password of remote peer.
|
||||
* @param pwd ICE password. */
|
||||
virtual void setRemotePassword(const std::string& pwd, int streamId = -1) = 0;
|
||||
virtual std::string remotePassword(int streamId = -1) const = 0;
|
||||
|
||||
/*! Notifies stack about ICE ufrag of remote peer.
|
||||
* @param ufrag ICE ufrag credential. */
|
||||
virtual void setRemoteUfrag(const std::string& ufrag, int streamId = -1) = 0;
|
||||
virtual std::string remoteUfrag(int streamId = -1) const = 0;
|
||||
|
||||
/*! Returns local ICE password.
|
||||
* @return ICE password. */
|
||||
virtual std::string localPassword() const = 0;
|
||||
|
||||
/*! Returns local ICE ufrag.
|
||||
* @return ICE ufrag credential. */
|
||||
virtual std::string localUfrag() const = 0;
|
||||
|
||||
/*! Checks if the specified value is ICE session's TURN prefix value.
|
||||
* @return Returns true if parameter is TURN prefix value, false otherwise. */
|
||||
virtual bool hasTurnPrefix(unsigned short prefix) = 0;
|
||||
|
||||
/*! Gets the discovered during connectivity checks remote party's address.
|
||||
* @return Remote party's address */
|
||||
virtual NetworkAddress remoteAddress(int stream, int component) = 0;
|
||||
virtual NetworkAddress localAddress(int stream, int component) = 0;
|
||||
|
||||
/*! Seeks for conclude pair.
|
||||
*/
|
||||
virtual bool findConcludePair(int stream, Candidate& local, Candidate& remote) = 0;
|
||||
|
||||
/*! Checks if remote candidate list contains specified address.
|
||||
* @return True if address is found in remote candidate list, false otherwise. */
|
||||
virtual bool candidateListContains(int stream, const std::string& remoteIP, unsigned short remotePort) = 0;
|
||||
|
||||
// Dumps current state of stack to output
|
||||
virtual void dump(std::ostream& output) = 0;
|
||||
|
||||
// Returns if ICE session must be restarted after new offer.
|
||||
virtual bool mustRestart() = 0;
|
||||
|
||||
// Clears all connectivity checks and reset state of session to None. It does not delete streams and components.
|
||||
virtual void clear() = 0;
|
||||
|
||||
// Prepares stack to restart - clears remote candidate list, cancels existing connectivity checks, resets turn allocation counter
|
||||
virtual void clearForRestart(bool localNetworkChanged) = 0;
|
||||
|
||||
// Stops all connectivity & gathering checks.
|
||||
virtual void stopChecks() = 0;
|
||||
|
||||
// Refreshes local password and ufrag values. Useful when connectivity checks must be restarted.
|
||||
virtual void refreshPwdUfrag() = 0;
|
||||
|
||||
// Binds channel and return channel prefix
|
||||
virtual TurnPrefix bindChannel(int stream, int component, const NetworkAddress& target, ChannelBoundCallback* cb) = 0;
|
||||
virtual bool isChannelBindingFailed(int stream, int component, TurnPrefix prefix) = 0;
|
||||
|
||||
virtual void installPermissions(int stream, int component, const NetworkAddress& address, InstallPermissionsCallback* cb) = 0;
|
||||
|
||||
// Starts freeing of TURN allocations
|
||||
virtual void freeAllocation(int stream, int component, DeleteAllocationCallback* cb) = 0;
|
||||
|
||||
// Checks if there are active allocations on TURN server
|
||||
virtual bool hasAllocations() = 0;
|
||||
|
||||
// Checks if any of stream has set error code and return it
|
||||
virtual int errorCode() = 0;
|
||||
|
||||
// Returns the list of candidates from remote peer
|
||||
virtual std::vector<Candidate>* remoteCandidates(int stream) = 0;
|
||||
|
||||
// Returns chosen stun server address during last candidate gathering
|
||||
virtual NetworkAddress activeStunServer(int stream) const = 0;
|
||||
|
||||
virtual void setup(const ServerConfig& config) = 0;
|
||||
virtual bool isRelayHost(const NetworkAddress& remote) = 0;
|
||||
virtual bool isRelayAddress(const NetworkAddress& remote) = 0;
|
||||
|
||||
virtual ~Stack();
|
||||
};
|
||||
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
445
src/libs/ice/ICEBoxImpl.cpp
Normal file
445
src/libs/ice/ICEBoxImpl.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEBoxImpl.h"
|
||||
#include "ICELog.h"
|
||||
//#include "TargetConditionals.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
StackImpl::StackImpl(const ServerConfig& config)
|
||||
:mConfig(config), mEventHandler(NULL), mEventTag(NULL), mTimeout(false), mActionTimestamp(0)
|
||||
{
|
||||
setup(config);
|
||||
}
|
||||
|
||||
StackImpl::~StackImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void StackImpl::setEventHandler(StageHandler* handler, void* tag)
|
||||
{
|
||||
mEventHandler = handler;
|
||||
mEventTag = tag;
|
||||
}
|
||||
|
||||
int StackImpl::addStream()
|
||||
{
|
||||
return mSession.addStream();
|
||||
}
|
||||
|
||||
int StackImpl::addComponent(int streamID, void* tag, unsigned short port4, unsigned short port6)
|
||||
{
|
||||
return mSession.addComponent(streamID, tag, port4, port6);
|
||||
}
|
||||
|
||||
void StackImpl::removeStream(int streamID)
|
||||
{
|
||||
mSession.removeStream(streamID);
|
||||
}
|
||||
|
||||
bool StackImpl::findStreamAndComponent(int family, unsigned short port, int* stream, int* component)
|
||||
{
|
||||
return mSession.findStreamAndComponent(family, port, stream, component);
|
||||
}
|
||||
|
||||
bool StackImpl::hasStream(int streamId)
|
||||
{
|
||||
return mSession.hasStream(streamId);
|
||||
}
|
||||
|
||||
bool StackImpl::hasComponent(int streamId, int componentId)
|
||||
{
|
||||
return mSession.hasComponent(streamId, componentId);
|
||||
}
|
||||
|
||||
void StackImpl::setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6)
|
||||
{
|
||||
mSession.setComponentPort(streamId, componentId, port4, port6);
|
||||
}
|
||||
|
||||
|
||||
void StackImpl::setRole(AgentRole role)
|
||||
{
|
||||
mSession.setRole(role);
|
||||
}
|
||||
|
||||
AgentRole StackImpl::role()
|
||||
{
|
||||
return mSession.role();
|
||||
}
|
||||
|
||||
bool StackImpl::processIncomingData(int stream, int component, ByteBuffer& incomingData)
|
||||
{
|
||||
#if defined(ICE_EMULATE_SYMMETRIC_NAT) //&& (TARGET_IPHONE_SIMULATOR)
|
||||
if (!isRelayHost(incomingData.remoteAddress()))
|
||||
{
|
||||
ICELogDebug(<<"Discard packet as symmetric NAT is emulating now");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (incomingData.remoteAddress().isEmpty())
|
||||
{
|
||||
ICELogDebug(<< "Incoming packet; remote address is unknown");
|
||||
incomingData.setRemoteAddress(NetworkAddress::LoopbackAddress4);
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogDebug(<< "Incoming packet from " << incomingData.remoteAddress().toStdString());
|
||||
}
|
||||
// Save previous ICE stack state
|
||||
int icestate = state();
|
||||
|
||||
incomingData.setComponent(component);
|
||||
bool result = mSession.processData(incomingData, stream, component);
|
||||
|
||||
// Run handlers
|
||||
if (result && mEventHandler)
|
||||
{
|
||||
int newicestate = state();
|
||||
if (icestate < IceCheckSuccess && newicestate >= IceCheckSuccess)
|
||||
{
|
||||
// Connectivity check finished
|
||||
if (newicestate == IceCheckSuccess)
|
||||
mEventHandler->onSuccess(this, mEventTag);
|
||||
else
|
||||
mEventHandler->onFailed(this, mEventTag);
|
||||
}
|
||||
else
|
||||
if (icestate < IceGathered && newicestate >= IceGathered)
|
||||
{
|
||||
// Candidates are gathered
|
||||
mEventHandler->onGathered(this, mEventTag);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PByteBuffer StackImpl::generateOutgoingData(bool& response, int& stream, int& component, void*& tag)
|
||||
{
|
||||
// Get current timestamp
|
||||
unsigned timestamp = ICETimeHelper::timestamp();
|
||||
|
||||
// Find amount of spent time
|
||||
unsigned spent = ICETimeHelper::findDelta(mActionTimestamp, timestamp);
|
||||
|
||||
// Check for timeout
|
||||
if (mConfig.mTimeout && spent > mConfig.mTimeout)
|
||||
{
|
||||
ice::RunningState sessionState = mSession.state();
|
||||
|
||||
bool timeout = sessionState == ice::ConnCheck || sessionState == ice::CandidateGathering;
|
||||
|
||||
if (!mTimeout && timeout)
|
||||
{
|
||||
// Mark stack as timeouted
|
||||
mTimeout = true;
|
||||
ICELogInfo(<< "Timeout detected.");
|
||||
|
||||
if (sessionState == ice::CandidateGathering)
|
||||
mSession.cancelAllocations();
|
||||
|
||||
// Find default address amongst host candidates
|
||||
mSession.chooseDefaults();
|
||||
|
||||
if (mEventHandler)
|
||||
{
|
||||
if (sessionState == ice::ConnCheck)
|
||||
{
|
||||
// Session should not be cleared here - the CT uses direct path for connectivity checks and relayed if checks are failed. Relayed allocations will not be refreshed in such case
|
||||
mEventHandler->onFailed(this, this->mEventTag);
|
||||
}
|
||||
else
|
||||
mEventHandler->onGathered(this, this->mEventTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
// Check if keepalive transactions are scheduled
|
||||
if (!mSession.hasAllocations())
|
||||
return PByteBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
// No timeout, proceed...
|
||||
PByteBuffer result = mSession.getDataToSend(response, stream, component, tag);
|
||||
if (result)
|
||||
ICELogInfo(<< "Sending: " << result->comment() << " to " << result->remoteAddress().toStdString() << ". Data: \r\n" << result->hexstring());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void StackImpl::gatherCandidates()
|
||||
{
|
||||
mActionTimestamp = ICETimeHelper::timestamp();
|
||||
mSession.gatherCandidates();
|
||||
|
||||
// If there is no STUN server set or IP6 only interfaces - the candidate gathering can finish without network I/O
|
||||
if (mSession.state() == CreatingSDP && mEventHandler)
|
||||
mEventHandler->onGathered(this, mEventTag);
|
||||
}
|
||||
|
||||
void StackImpl::checkConnectivity()
|
||||
{
|
||||
// Connectivity check can work if candidate gathering timeout-ed yet - it relies on host candidates only in this case
|
||||
// So timeout flag is reset
|
||||
mTimeout = false;
|
||||
mActionTimestamp = ICETimeHelper::timestamp();
|
||||
mSession.checkConnectivity();
|
||||
}
|
||||
|
||||
void StackImpl::stopChecks()
|
||||
{
|
||||
mTimeout = false;
|
||||
mActionTimestamp = 0;
|
||||
mSession.stopChecks();
|
||||
}
|
||||
|
||||
IceState StackImpl::state()
|
||||
{
|
||||
if (mTimeout)
|
||||
return IceTimeout;
|
||||
|
||||
switch (mSession.state())
|
||||
{
|
||||
case ice::None: return IceNone;
|
||||
case ice::CandidateGathering: return IceGathering;
|
||||
case ice::CreatingSDP: return IceGathered;
|
||||
case ice::ConnCheck: return IceChecking;
|
||||
case ice::Failed: return IceFailed;
|
||||
case ice::Success: return IceCheckSuccess;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return IceNone; // to avoid compiler warning
|
||||
}
|
||||
|
||||
void StackImpl::createSdp(std::vector<std::string>& commonPart)
|
||||
{
|
||||
mSession.createSdp(commonPart);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::defaultAddress(int stream, int component)
|
||||
{
|
||||
return mSession.defaultAddress(stream, component);
|
||||
}
|
||||
|
||||
void StackImpl::fillCandidateList(int streamID, int componentID, std::vector<std::string>& candidateList)
|
||||
{
|
||||
mSession.fillCandidateList(streamID, componentID, candidateList);
|
||||
}
|
||||
|
||||
bool StackImpl::processSdpOffer(int streamIndex, std::vector<std::string>& candidateList,
|
||||
const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed)
|
||||
{
|
||||
return mSession.processSdpOffer(streamIndex, candidateList, defaultIP, defaultPort, deleteRelayed);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::getRemoteRelayedCandidate(int stream, int component)
|
||||
{
|
||||
return mSession.getRemoteRelayedCandidate(stream, component);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::getRemoteReflexiveCandidate(int stream, int component)
|
||||
{
|
||||
return mSession.getRemoteReflexiveCandidate(stream, component);
|
||||
}
|
||||
|
||||
|
||||
void StackImpl::setRemotePassword(const std::string& pwd, int streamId)
|
||||
{
|
||||
mSession.setRemotePassword(pwd, streamId);
|
||||
}
|
||||
|
||||
std::string StackImpl::remotePassword(int streamId) const
|
||||
{
|
||||
return mSession.remotePassword(streamId);
|
||||
}
|
||||
|
||||
void StackImpl::setRemoteUfrag(const std::string& ufrag, int streamId)
|
||||
{
|
||||
mSession.setRemoteUfrag(ufrag, streamId);
|
||||
}
|
||||
|
||||
std::string StackImpl::remoteUfrag(int streamId) const
|
||||
{
|
||||
return mSession.remoteUfrag(streamId);
|
||||
}
|
||||
|
||||
std::string StackImpl::localPassword() const
|
||||
{
|
||||
return mSession.mLocalPwd;
|
||||
}
|
||||
|
||||
std::string StackImpl::localUfrag() const
|
||||
{
|
||||
return mSession.mLocalUfrag;
|
||||
}
|
||||
|
||||
bool StackImpl::hasTurnPrefix(unsigned short prefix)
|
||||
{
|
||||
return mSession.hasTurnPrefix((TurnPrefix)prefix);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::remoteAddress(int stream, int component)
|
||||
{
|
||||
return mSession.remoteAddress(stream, component);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::localAddress(int stream, int component)
|
||||
{
|
||||
return mSession.localAddress(stream, component);
|
||||
}
|
||||
|
||||
bool StackImpl::findConcludePair(int stream, Candidate& local, Candidate& remote)
|
||||
{
|
||||
// Find nominated pair in stream
|
||||
return mSession.findConcludePair(stream, local, remote);
|
||||
}
|
||||
|
||||
bool StackImpl::candidateListContains(int stream, const std::string& remoteIP, unsigned short remotePort)
|
||||
{
|
||||
return mSession.mStreamMap[stream]->candidateListContains(remoteIP, remotePort);
|
||||
}
|
||||
|
||||
void StackImpl::dump(std::ostream& output)
|
||||
{
|
||||
mSession.dump(output);
|
||||
}
|
||||
|
||||
bool StackImpl::mustRestart()
|
||||
{
|
||||
return mSession.mustRestart();
|
||||
}
|
||||
|
||||
void StackImpl::clear()
|
||||
{
|
||||
mSession.clear();
|
||||
mActionTimestamp = 0;
|
||||
mTimeout = false;
|
||||
}
|
||||
|
||||
void StackImpl::clearForRestart(bool localNetworkChanged)
|
||||
{
|
||||
mSession.clearForRestart(localNetworkChanged);
|
||||
mActionTimestamp = 0;
|
||||
mTimeout = false;
|
||||
}
|
||||
|
||||
void StackImpl::refreshPwdUfrag()
|
||||
{
|
||||
mSession.refreshPwdUfrag();
|
||||
}
|
||||
|
||||
TurnPrefix StackImpl::bindChannel(int stream, int component, const ice::NetworkAddress &target, ChannelBoundCallback* cb)
|
||||
{
|
||||
return mSession.bindChannel(stream, component, target, cb);
|
||||
}
|
||||
|
||||
bool StackImpl::isChannelBindingFailed(int stream, int component, TurnPrefix prefix)
|
||||
{
|
||||
return mSession.isChannelBindingFailed(stream, component, prefix);
|
||||
}
|
||||
|
||||
void StackImpl::installPermissions(int stream, int component, const NetworkAddress &address, InstallPermissionsCallback* cb)
|
||||
{
|
||||
mSession.installPermissions(stream, component, address, cb);
|
||||
}
|
||||
|
||||
void StackImpl::freeAllocation(int stream, int component, DeleteAllocationCallback* cb)
|
||||
{
|
||||
mSession.freeAllocation(stream, component, cb);
|
||||
}
|
||||
|
||||
bool StackImpl::hasAllocations()
|
||||
{
|
||||
return mSession.hasAllocations();
|
||||
}
|
||||
|
||||
int StackImpl::errorCode()
|
||||
{
|
||||
return mSession.errorCode();
|
||||
}
|
||||
|
||||
std::vector<Candidate>* StackImpl::remoteCandidates(int stream)
|
||||
{
|
||||
return mSession.remoteCandidates(stream);
|
||||
}
|
||||
|
||||
NetworkAddress StackImpl::activeStunServer(int stream) const
|
||||
{
|
||||
return mSession.activeStunServer(stream);
|
||||
}
|
||||
|
||||
void StackImpl::setup(const ServerConfig& config)
|
||||
{
|
||||
mConfig = config;
|
||||
mSession.mConfig.mServerList4 = config.mServerList4;
|
||||
mSession.mConfig.mServerList6 = config.mServerList6;
|
||||
|
||||
// Set initial STUN/TURN server IP for case if there will no gathering candidates & selecting fastest server.
|
||||
if (mSession.mConfig.mServerList4.size())
|
||||
mSession.mConfig.mServerAddr4 = mSession.mConfig.mServerList4.front();
|
||||
if (mSession.mConfig.mServerList6.size())
|
||||
mSession.mConfig.mServerAddr6 = mSession.mConfig.mServerList6.front();
|
||||
mSession.mConfig.mUseIPv4 = config.mUseIPv4;
|
||||
mSession.mConfig.mUseIPv6 = config.mUseIPv6;
|
||||
|
||||
if (mConfig.mRelay)
|
||||
{
|
||||
mSession.mConfig.mTurnPassword = config.mPassword;
|
||||
mSession.mConfig.mTurnUsername = config.mUsername;
|
||||
|
||||
mSession.mConfig.mUseTURN = true;
|
||||
mSession.mConfig.mUseSTUN = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSession.mConfig.mUseTURN = false;
|
||||
mSession.mConfig.mUseSTUN = true;
|
||||
}
|
||||
mSession.mConfig.mTimeout = config.mTimeout;
|
||||
|
||||
mSession.setup(mSession.mConfig);
|
||||
}
|
||||
|
||||
bool StackImpl::isRelayHost(const NetworkAddress& remote)
|
||||
{
|
||||
for (unsigned i=0; i<mConfig.mServerList4.size(); i++)
|
||||
{
|
||||
if (NetworkAddress::isSameHost(mConfig.mServerList4[i], remote))
|
||||
return true;
|
||||
}
|
||||
for (unsigned i=0; i<mConfig.mServerList6.size(); i++)
|
||||
{
|
||||
if (NetworkAddress::isSameHost(mConfig.mServerList6[i], remote))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StackImpl::isRelayAddress(const NetworkAddress& remote)
|
||||
{
|
||||
for (unsigned i=0; i<mConfig.mServerList4.size(); i++)
|
||||
{
|
||||
if (mConfig.mServerList4[i] == remote)
|
||||
return true;
|
||||
}
|
||||
for (unsigned i=0; i<mConfig.mServerList6.size(); i++)
|
||||
{
|
||||
if (mConfig.mServerList6[i] == remote)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
105
src/libs/ice/ICEBoxImpl.h
Normal file
105
src/libs/ice/ICEBoxImpl.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_BOX_IMPL__H
|
||||
#define __ICE_BOX_IMPL__H
|
||||
|
||||
#include "ICEBox.h"
|
||||
#include "ICESession.h"
|
||||
#include "ICELog.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class StackImpl: public Stack
|
||||
{
|
||||
protected:
|
||||
ServerConfig mConfig;
|
||||
Mutex mGuard; //mutex to protect ICE stack object
|
||||
Session mSession;
|
||||
StageHandler* mEventHandler;
|
||||
void* mEventTag;
|
||||
bool mTimeout;
|
||||
unsigned int mActionTimestamp;
|
||||
|
||||
void logMsg(LogLevel level, const char* msg);
|
||||
|
||||
public:
|
||||
|
||||
StackImpl(const ServerConfig& config);
|
||||
~StackImpl();
|
||||
|
||||
void setEventHandler(StageHandler* handler, void* tag) override;
|
||||
int addStream() override;
|
||||
int addComponent(int streamID, void* tag, unsigned short port4, unsigned short port6) override;
|
||||
void removeStream(int streamID) override;
|
||||
bool findStreamAndComponent(int family, unsigned short port, int* stream, int* component) override;
|
||||
bool hasStream(int streamId) override;
|
||||
bool hasComponent(int streamId, int componentId) override;
|
||||
void setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6) override;
|
||||
|
||||
void setRole(AgentRole role) override;
|
||||
AgentRole role() override;
|
||||
|
||||
bool processIncomingData(int stream, int component, ByteBuffer& incomingData) override;
|
||||
PByteBuffer generateOutgoingData(bool& response, int& stream, int& component, void*& tag) override;
|
||||
|
||||
// Attempt to gather candidates for specified channel
|
||||
void gatherCandidates() override;
|
||||
void checkConnectivity() override;
|
||||
void stopChecks() override;
|
||||
void restartCheckConnectivity();
|
||||
|
||||
IceState state() override;
|
||||
|
||||
void createSdp(std::vector<std::string>& commonPart) override;
|
||||
NetworkAddress defaultAddress(int streamID, int componentID) override;
|
||||
void fillCandidateList(int streamID, int componentID, std::vector<std::string>& candidateList) override;
|
||||
|
||||
bool processSdpOffer(int streamIndex, std::vector<std::string>& candidateList,
|
||||
const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed) override;
|
||||
NetworkAddress getRemoteRelayedCandidate(int stream, int component) override;
|
||||
NetworkAddress getRemoteReflexiveCandidate(int stream, int component) override;
|
||||
|
||||
void setRemotePassword(const std::string& pwd, int streamId = -1) override;
|
||||
std::string remotePassword(int streamId = -1) const override;
|
||||
void setRemoteUfrag(const std::string& ufrag, int streamId = -1) override;
|
||||
std::string remoteUfrag(int streamId = -1) const override;
|
||||
|
||||
std::string localPassword() const override;
|
||||
std::string localUfrag() const override;
|
||||
bool hasTurnPrefix(unsigned short prefix) override;
|
||||
NetworkAddress remoteAddress(int stream, int component) override;
|
||||
NetworkAddress localAddress(int stream, int component) override;
|
||||
bool findConcludePair(int stream, Candidate& local, Candidate& remote) override;
|
||||
bool candidateListContains(int stream, const std::string& remoteIP, unsigned short remotePort) override;
|
||||
void dump(std::ostream& output) override;
|
||||
bool mustRestart() override;
|
||||
void clear() override;
|
||||
void clearForRestart(bool localNetworkChanged) override;
|
||||
void refreshPwdUfrag() override;
|
||||
|
||||
// Channel binding
|
||||
TurnPrefix bindChannel(int stream, int component, const NetworkAddress& target, ChannelBoundCallback* cb) override;
|
||||
bool isChannelBindingFailed(int stream, int component, TurnPrefix prefix) override;
|
||||
|
||||
// Permissions
|
||||
void installPermissions(int stream, int component, const NetworkAddress& address, InstallPermissionsCallback* cb) override;
|
||||
|
||||
// Allocations
|
||||
void freeAllocation(int stream, int component, DeleteAllocationCallback* cb) override;
|
||||
bool hasAllocations() override;
|
||||
|
||||
|
||||
int errorCode() override;
|
||||
std::vector<Candidate>*
|
||||
remoteCandidates(int stream) override;
|
||||
NetworkAddress activeStunServer(int stream) const override;
|
||||
void setup(const ServerConfig& config) override;
|
||||
bool isRelayHost(const NetworkAddress& remote) override;
|
||||
bool isRelayAddress(const NetworkAddress& remote) override;
|
||||
};
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
560
src/libs/ice/ICEByteBuffer.cpp
Normal file
560
src/libs/ice/ICEByteBuffer.cpp
Normal file
@@ -0,0 +1,560 @@
|
||||
/* Copyright(C) 2007-2018 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEByteBuffer.h"
|
||||
#include "ICEError.h"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace ice;
|
||||
ByteBuffer::ByteBuffer()
|
||||
{
|
||||
initEmpty();
|
||||
mData.reserve(512);
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(size_t initialCapacity)
|
||||
{
|
||||
initEmpty();
|
||||
mData.reserve(initialCapacity);
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(const ByteBuffer& src)
|
||||
:mData(src.mData), mComponent(src.mComponent), mTag(nullptr),
|
||||
mRelayed(src.mRelayed), mCopyBehavior(src.mCopyBehavior),
|
||||
mDataPtr(src.mDataPtr), mDataSize(src.mDataSize)
|
||||
{
|
||||
if (mCopyBehavior == CopyBehavior::CopyMemory && mData.size())
|
||||
mDataPtr = &mData[0];
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(const void* packetPtr, size_t packetSize, CopyBehavior behavior)
|
||||
:mComponent(-1), mTag(nullptr), mRelayed(false), mCopyBehavior(behavior), mDataPtr(nullptr), mDataSize(packetSize)
|
||||
{
|
||||
switch (behavior)
|
||||
{
|
||||
case CopyBehavior::CopyMemory:
|
||||
mData.resize(packetSize);
|
||||
memcpy(&mData[0], packetPtr, packetSize);
|
||||
mDataPtr = &mData[0];
|
||||
break;
|
||||
|
||||
case CopyBehavior::UseExternal:
|
||||
mDataPtr = (uint8_t*)packetPtr;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuffer::~ByteBuffer()
|
||||
{
|
||||
if (mCopyBehavior == CopyBehavior::CopyMemory)
|
||||
memset(mDataPtr, 0, mDataSize);
|
||||
}
|
||||
|
||||
ByteBuffer& ByteBuffer::operator = (const ByteBuffer& src)
|
||||
{
|
||||
mRelayed = src.mRelayed;
|
||||
mComment = src.mComment;
|
||||
mComponent = src.mComponent;
|
||||
mRemoteAddress = src.mRemoteAddress;
|
||||
mTag = src.mTag;
|
||||
|
||||
if (src.mCopyBehavior == CopyBehavior::CopyMemory)
|
||||
{
|
||||
mData = src.mData;
|
||||
mCopyBehavior = CopyBehavior::CopyMemory;
|
||||
syncPointer();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mDataPtr = src.mDataPtr;
|
||||
mDataSize = src.mDataSize;
|
||||
mCopyBehavior = CopyBehavior::UseExternal;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ByteBuffer::clear()
|
||||
{
|
||||
mData.resize(0);
|
||||
mDataSize = 0;
|
||||
mDataPtr = nullptr;
|
||||
}
|
||||
|
||||
size_t ByteBuffer::size() const
|
||||
{
|
||||
return mDataSize;
|
||||
}
|
||||
|
||||
const uint8_t* ByteBuffer::data() const
|
||||
{
|
||||
return (const uint8_t*)mDataPtr;
|
||||
}
|
||||
|
||||
uint8_t* ByteBuffer::mutableData()
|
||||
{
|
||||
return mDataPtr;
|
||||
}
|
||||
|
||||
NetworkAddress& ByteBuffer::remoteAddress()
|
||||
{
|
||||
return mRemoteAddress;
|
||||
}
|
||||
|
||||
void ByteBuffer::setRemoteAddress(const NetworkAddress& addr)
|
||||
{
|
||||
mRemoteAddress = addr;
|
||||
}
|
||||
|
||||
void ByteBuffer::setComment(std::string comment)
|
||||
{
|
||||
mComment = comment;
|
||||
}
|
||||
|
||||
std::string ByteBuffer::comment()
|
||||
{
|
||||
return mComment;
|
||||
}
|
||||
|
||||
std::string ByteBuffer::hexstring()
|
||||
{
|
||||
std::string result;
|
||||
result.resize(mDataSize * 2, (char)0xCC);
|
||||
for (std::vector<uint8_t>::size_type index = 0; index < mDataSize; index++)
|
||||
{
|
||||
char value[3];
|
||||
sprintf(value, "%02X", (unsigned char)mDataPtr[index]);
|
||||
result[index*2] = value[0];
|
||||
result[index*2+1] = value[1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ByteBuffer::reserve(size_t capacity)
|
||||
{
|
||||
mData.reserve(capacity);
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
void ByteBuffer::insertTurnPrefix(unsigned short prefix)
|
||||
{
|
||||
assert(mCopyBehavior == CopyBehavior::CopyMemory);
|
||||
|
||||
mData.insert(mData.begin(), 2, 32);
|
||||
unsigned short nprefix = htons(prefix);
|
||||
mData[0] = nprefix & 0xFF;
|
||||
mData[1] = (nprefix & 0xFF00) >> 8;
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
int ByteBuffer::component()
|
||||
{
|
||||
return mComponent;
|
||||
}
|
||||
|
||||
void ByteBuffer::setComponent(int component)
|
||||
{
|
||||
mComponent = component;
|
||||
}
|
||||
|
||||
void ByteBuffer::truncate(size_t newsize)
|
||||
{
|
||||
assert (mCopyBehavior == CopyBehavior::CopyMemory);
|
||||
|
||||
mData.erase(mData.begin() + newsize, mData.end());
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
void ByteBuffer::erase(size_t p, size_t l)
|
||||
{
|
||||
assert (mCopyBehavior == CopyBehavior::CopyMemory);
|
||||
|
||||
mData.erase(mData.begin()+p, mData.begin()+p+l);
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
void ByteBuffer::resize(size_t newsize)
|
||||
{
|
||||
assert (mCopyBehavior == CopyBehavior::CopyMemory);
|
||||
|
||||
std::vector<uint8_t>::size_type sz = mData.size();
|
||||
mData.resize(newsize);
|
||||
if (newsize > sz)
|
||||
memset(&mData[sz], 0, newsize - sz);
|
||||
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
void ByteBuffer::appendBuffer(const void *data, size_t size)
|
||||
{
|
||||
assert (mCopyBehavior == CopyBehavior::CopyMemory);
|
||||
|
||||
size_t len = mData.size();
|
||||
mData.resize(len + size);
|
||||
memmove(mData.data() + len, data, size);
|
||||
syncPointer();
|
||||
}
|
||||
|
||||
void* ByteBuffer::tag()
|
||||
{
|
||||
return mTag;
|
||||
}
|
||||
|
||||
void ByteBuffer::setTag(void* tag)
|
||||
{
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
bool ByteBuffer::relayed()
|
||||
{
|
||||
return mRelayed;
|
||||
}
|
||||
|
||||
void ByteBuffer::setRelayed(bool value)
|
||||
{
|
||||
mRelayed = value;
|
||||
}
|
||||
|
||||
void ByteBuffer::initEmpty()
|
||||
{
|
||||
mDataPtr = nullptr;
|
||||
mDataSize = 0;
|
||||
mCopyBehavior = CopyBehavior::CopyMemory;
|
||||
mRelayed = false;
|
||||
mComponent = -1;
|
||||
mTag = nullptr;
|
||||
}
|
||||
|
||||
void ByteBuffer::syncPointer()
|
||||
{
|
||||
mDataPtr = mData.empty() ? nullptr : &mData[0];
|
||||
mDataSize = mData.size();
|
||||
}
|
||||
|
||||
uint8_t ByteBuffer::operator[](int index) const
|
||||
{
|
||||
return mDataPtr[index];
|
||||
}
|
||||
|
||||
uint8_t& ByteBuffer::operator[](int index)
|
||||
{
|
||||
return mDataPtr[index];
|
||||
}
|
||||
|
||||
// ----------------- BitReader -------------------
|
||||
BitReader::BitReader(const ByteBuffer &buffer)
|
||||
:mStream(buffer.data()), mStreamLen(buffer.size()), mStreamOffset(0), mCurrentBit(0)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
BitReader::BitReader(const void *input, size_t bytes)
|
||||
:mStream((const uint8_t*)input), mStreamLen(bytes), mStreamOffset(0), mCurrentBit(0)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void BitReader::init()
|
||||
{
|
||||
mStreamOffset = mStreamLen << 3;
|
||||
mCurrentPosition = 0;//mStreamOffset - 1;
|
||||
mCurrentBit = 0;
|
||||
}
|
||||
|
||||
BitReader::~BitReader()
|
||||
{}
|
||||
|
||||
// Check for valid position
|
||||
uint8_t BitReader::readBit()
|
||||
{
|
||||
uint8_t value = 0x00;
|
||||
if (mCurrentPosition < mStreamOffset)
|
||||
{
|
||||
// Read single BIT
|
||||
size_t currentByte = mCurrentPosition >> 3;
|
||||
uint8_t currentBit = (uint8_t)(mCurrentPosition % 8);
|
||||
value = ((uint8_t)(mStream[currentByte] << currentBit) >> 7);
|
||||
mCurrentPosition = std::max((size_t)0, std::min(mStreamOffset-1, mCurrentPosition+1));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t BitReader::readBits(size_t nrOfBits)
|
||||
{
|
||||
assert (nrOfBits <= 32);
|
||||
|
||||
uint32_t result = 0;
|
||||
BitWriter bw(&result);
|
||||
for (int i=0; i<(int)nrOfBits; i++)
|
||||
bw.writeBit(readBit());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t BitReader::readBits(void *output, size_t nrOfBits)
|
||||
{
|
||||
// Check how much bits available
|
||||
nrOfBits = std::min(nrOfBits, mStreamOffset - mCurrentPosition);
|
||||
|
||||
BitWriter bw(output);
|
||||
for (int i=0; i<(int)nrOfBits; i++)
|
||||
bw.writeBit(readBit());
|
||||
|
||||
return nrOfBits;
|
||||
}
|
||||
|
||||
size_t BitReader::count() const
|
||||
{
|
||||
return mStreamOffset;
|
||||
}
|
||||
|
||||
size_t BitReader::position() const
|
||||
{
|
||||
return mCurrentPosition;
|
||||
}
|
||||
|
||||
// ----------------- BitWriter -------------------
|
||||
BitWriter::BitWriter(ByteBuffer &buffer)
|
||||
:mStream(buffer.mutableData()), mStreamLen(0), mStreamOffset(0), mCurrentBit(0)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
BitWriter::BitWriter(void *output)
|
||||
:mStream((uint8_t*)output), mStreamLen(0), mStreamOffset(0), mCurrentBit(0)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void BitWriter::init()
|
||||
{
|
||||
mStreamOffset = mStreamLen << 3;
|
||||
mCurrentPosition = 0;//mStreamOffset - 1;
|
||||
mCurrentBit = 0;
|
||||
}
|
||||
|
||||
BitWriter::~BitWriter()
|
||||
{}
|
||||
|
||||
BitWriter& BitWriter::writeBit(int bit)
|
||||
{
|
||||
bit = bit ? 1 : 0;
|
||||
|
||||
// Check for current bit offset
|
||||
if ((mCurrentBit % 8) == 0)
|
||||
{
|
||||
// Write new zero byte to the end of stream
|
||||
mCurrentBit = 0;
|
||||
mStreamLen++;
|
||||
mStream[mStreamLen-1] = 0;
|
||||
}
|
||||
|
||||
// Write single BIT
|
||||
mStream[mStreamLen-1] <<= 1;
|
||||
mStream[mStreamLen-1] |= bit;
|
||||
mStreamOffset++;
|
||||
mCurrentPosition = mStreamOffset - 1;
|
||||
mCurrentBit++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t BitWriter::count() const
|
||||
{
|
||||
return mStreamOffset;
|
||||
}
|
||||
|
||||
// ----------------- BufferReader ----------------
|
||||
BufferReader::BufferReader(const ByteBuffer &buffer)
|
||||
:mData(buffer.data()), mSize(buffer.size()), mIndex(0)
|
||||
{}
|
||||
|
||||
BufferReader::BufferReader(const void *input, size_t bytes)
|
||||
:mData((const uint8_t*)input), mSize(bytes), mIndex(0)
|
||||
{}
|
||||
|
||||
|
||||
uint32_t BufferReader::readUInt()
|
||||
{
|
||||
uint32_t nresult = 0;
|
||||
readBuffer(&nresult, 4);
|
||||
|
||||
return ntohl(nresult);
|
||||
}
|
||||
|
||||
uint32_t BufferReader::readNativeUInt()
|
||||
{
|
||||
uint32_t nresult = 0;
|
||||
readBuffer(&nresult, 4);
|
||||
|
||||
return nresult;
|
||||
}
|
||||
|
||||
uint16_t BufferReader::readUShort()
|
||||
{
|
||||
uint16_t result = 0;
|
||||
readBuffer(&result, 2);
|
||||
|
||||
return ntohs(result);
|
||||
}
|
||||
|
||||
uint16_t BufferReader::readNativeUShort()
|
||||
{
|
||||
uint16_t result = 0;
|
||||
readBuffer(&result, 2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t BufferReader::readUChar()
|
||||
{
|
||||
uint8_t result = 0;
|
||||
readBuffer(&result, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NetworkAddress BufferReader::readIp(int family)
|
||||
{
|
||||
if (family == AF_INET)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
readBuffer(&addr.sin_addr.s_addr, 4);
|
||||
|
||||
return NetworkAddress((sockaddr&)addr, sizeof(addr));
|
||||
}
|
||||
else
|
||||
if (family == AF_INET6)
|
||||
{
|
||||
sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_port = 0;
|
||||
readBuffer(&addr.sin6_addr, 16);
|
||||
|
||||
return NetworkAddress((sockaddr&)addr, sizeof(addr));
|
||||
}
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
size_t BufferReader::readBuffer(void* dataPtr, size_t dataSize)
|
||||
{
|
||||
if (dataSize > 0)
|
||||
{
|
||||
size_t available = mSize - mIndex;
|
||||
if (available < dataSize)
|
||||
dataSize = available;
|
||||
if (NULL != dataPtr)
|
||||
memcpy(dataPtr, mData + mIndex, dataSize);
|
||||
|
||||
mIndex += dataSize;
|
||||
return dataSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t BufferReader::readBuffer(ByteBuffer& bb, size_t dataSize)
|
||||
{
|
||||
if (dataSize > 0)
|
||||
{
|
||||
// Find how much data are available in fact
|
||||
size_t available = mSize - mIndex;
|
||||
if (available < dataSize)
|
||||
dataSize = available;
|
||||
|
||||
// Extend byte buffer
|
||||
size_t startIndex = bb.size();
|
||||
bb.resize(bb.size() + dataSize);
|
||||
memcpy(bb.mutableData() + startIndex, mData + mIndex, dataSize);
|
||||
mIndex += dataSize;
|
||||
return dataSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t BufferReader::count() const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
// -------------- BufferWriter ----------------------
|
||||
BufferWriter::BufferWriter(ByteBuffer &buffer)
|
||||
:mData(buffer.mutableData()), mIndex(0)
|
||||
{}
|
||||
|
||||
BufferWriter::BufferWriter(void *output)
|
||||
:mData((uint8_t*)output), mIndex(0)
|
||||
{}
|
||||
|
||||
|
||||
void BufferWriter::writeUInt(uint32_t value)
|
||||
{
|
||||
// Convert to network order bytes
|
||||
uint32_t nvalue = htonl(value);
|
||||
|
||||
writeBuffer(&nvalue, 4);
|
||||
}
|
||||
|
||||
|
||||
void BufferWriter::writeUShort(uint16_t value)
|
||||
{
|
||||
uint16_t nvalue = htons(value);
|
||||
writeBuffer(&nvalue, 2);
|
||||
}
|
||||
|
||||
void BufferWriter::writeUChar(uint8_t value)
|
||||
{
|
||||
writeBuffer(&value, 1);
|
||||
}
|
||||
|
||||
void BufferWriter::writeIp(const NetworkAddress& ip)
|
||||
{
|
||||
switch (ip.stunType())
|
||||
{
|
||||
case 1/*IPv4*/:
|
||||
writeBuffer(&ip.sockaddr4()->sin_addr, 4);
|
||||
break;
|
||||
|
||||
case 2/*IPv6*/:
|
||||
writeBuffer(&ip.sockaddr6()->sin6_addr, 16);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferWriter::writeBuffer(const void* dataPtr, size_t dataSize)
|
||||
{
|
||||
memmove(mData + mIndex, dataPtr, dataSize);
|
||||
mIndex += dataSize;
|
||||
}
|
||||
|
||||
void BufferWriter::rewind()
|
||||
{
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
void BufferWriter::skip(int count)
|
||||
{
|
||||
mIndex += count;
|
||||
}
|
||||
|
||||
size_t BufferWriter::offset() const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
187
src/libs/ice/ICEByteBuffer.h
Normal file
187
src/libs/ice/ICEByteBuffer.h
Normal file
@@ -0,0 +1,187 @@
|
||||
/* Copyright(C) 2007-2016 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_BYTE_BUFFER_H
|
||||
#define __ICE_BYTE_BUFFER_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICETypes.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "ICETypes.h"
|
||||
#include "ICEAddress.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class ByteBuffer
|
||||
{
|
||||
public:
|
||||
enum class CopyBehavior
|
||||
{
|
||||
CopyMemory,
|
||||
UseExternal
|
||||
};
|
||||
|
||||
ByteBuffer();
|
||||
ByteBuffer(size_t initialCapacity);
|
||||
ByteBuffer(const ByteBuffer& src);
|
||||
ByteBuffer(const void* packetPtr, size_t packetSize, CopyBehavior behavior = CopyBehavior::CopyMemory);
|
||||
~ByteBuffer();
|
||||
|
||||
ByteBuffer& operator = (const ByteBuffer& src);
|
||||
|
||||
void clear();
|
||||
|
||||
size_t size() const;
|
||||
const uint8_t* data() const;
|
||||
uint8_t* mutableData();
|
||||
|
||||
NetworkAddress& remoteAddress();
|
||||
void setRemoteAddress(const NetworkAddress& addr);
|
||||
int component();
|
||||
void setComponent(int component);
|
||||
void setComment(std::string comment);
|
||||
std::string comment();
|
||||
std::string hexstring();
|
||||
|
||||
void reserve(size_t capacity);
|
||||
void insertTurnPrefix(unsigned short prefix);
|
||||
void truncate(size_t newsize);
|
||||
void erase(size_t p, size_t l);
|
||||
void resize(size_t newsize);
|
||||
void appendBuffer(const void* data, size_t size);
|
||||
void* tag();
|
||||
void setTag(void* tag);
|
||||
void trim();
|
||||
bool relayed();
|
||||
void setRelayed(bool value);
|
||||
void syncPointer();
|
||||
|
||||
uint8_t operator[](int index) const;
|
||||
uint8_t& operator[](int index);
|
||||
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> mData; // Internal storage
|
||||
uint8_t* mDataPtr; // Pointer to internal storage or external data
|
||||
uint32_t mDataSize; // Used only for mCopyBehavior == UseExternal
|
||||
NetworkAddress mRemoteAddress; // Associates buffer with IP address
|
||||
int mComponent; // Associates buffer with component ID
|
||||
std::string mComment; // Comment's for this buffer - useful in debugging
|
||||
void* mTag; // User tag
|
||||
bool mRelayed; // Marks if buffer was received via relay
|
||||
CopyBehavior mCopyBehavior; // Determines if buffer manages internal or external data
|
||||
|
||||
void initEmpty();
|
||||
int bitsInBuffer(uint8_t bufferMask);
|
||||
};
|
||||
|
||||
|
||||
typedef std::shared_ptr<ByteBuffer> PByteBuffer;
|
||||
|
||||
class BitReader
|
||||
{
|
||||
protected:
|
||||
const uint8_t* mStream;
|
||||
size_t mStreamLen;
|
||||
size_t mStreamOffset;
|
||||
size_t mCurrentPosition;
|
||||
size_t mCurrentBit;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
BitReader(const void* input, size_t bytes);
|
||||
BitReader(const ByteBuffer& buffer);
|
||||
~BitReader();
|
||||
|
||||
uint8_t readBit();
|
||||
uint32_t readBits(size_t nrOfBits);
|
||||
size_t readBits(void* output, size_t nrOfBits);
|
||||
|
||||
size_t count() const;
|
||||
size_t position() const;
|
||||
};
|
||||
|
||||
class BitWriter
|
||||
{
|
||||
protected:
|
||||
uint8_t* mStream;
|
||||
size_t mStreamLen;
|
||||
size_t mStreamOffset;
|
||||
size_t mCurrentPosition;
|
||||
size_t mCurrentBit;
|
||||
|
||||
void init();
|
||||
public:
|
||||
BitWriter(void* output);
|
||||
BitWriter(ByteBuffer& buffer);
|
||||
~BitWriter();
|
||||
|
||||
// Bit must be 0 or 1
|
||||
BitWriter& writeBit(int bit);
|
||||
size_t count() const;
|
||||
};
|
||||
|
||||
class BufferReader
|
||||
{
|
||||
protected:
|
||||
const uint8_t* mData;
|
||||
size_t mSize;
|
||||
size_t mIndex;
|
||||
|
||||
public:
|
||||
BufferReader(const void* input, size_t bytes);
|
||||
BufferReader(const ByteBuffer& buffer);
|
||||
|
||||
// This methods reads uint32_t from stream and converts network byte order to host byte order.
|
||||
uint32_t readUInt();
|
||||
|
||||
// This method reads uint32_t from stream. No conversion between byte orders.
|
||||
uint32_t readNativeUInt();
|
||||
|
||||
// This method reads uint16_t from stream and converts network byte order to host byte order.
|
||||
uint16_t readUShort();
|
||||
|
||||
// This method reads uint16_t. No conversion between byte orders.
|
||||
uint16_t readNativeUShort();
|
||||
uint8_t readUChar();
|
||||
|
||||
// Reads in_addr or in6_addr from stream and wraps it to NetworkAddress
|
||||
NetworkAddress readIp(int family);
|
||||
|
||||
// Reads to plain memory buffer
|
||||
size_t readBuffer(void* dataPtr, size_t dataSize);
|
||||
|
||||
// Read directly to byte buffer. New data will be appended to byte buffer
|
||||
size_t readBuffer(ByteBuffer& bb, size_t dataSize);
|
||||
|
||||
size_t count() const;
|
||||
};
|
||||
|
||||
class BufferWriter
|
||||
{
|
||||
protected:
|
||||
uint8_t* mData;
|
||||
size_t mIndex;
|
||||
|
||||
public:
|
||||
BufferWriter(void* output);
|
||||
BufferWriter(ByteBuffer& buffer);
|
||||
|
||||
void writeUInt(uint32_t value);
|
||||
void writeUShort(uint16_t value);
|
||||
void writeUChar(uint8_t value);
|
||||
void writeIp(const NetworkAddress& ip);
|
||||
void writeBuffer(const void* dataPtr, size_t dataSize);
|
||||
void rewind();
|
||||
void skip(int count);
|
||||
|
||||
size_t offset() const;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
177
src/libs/ice/ICECRC32.cpp
Normal file
177
src/libs/ice/ICECRC32.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _CCRC32_CPP
|
||||
#define _CCRC32_CPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "ICECRC32.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
using namespace ice;
|
||||
|
||||
CRC32::CRC32(void)
|
||||
{
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CRC32::~CRC32(void)
|
||||
{
|
||||
//No destructor code.
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
This function initializes "CRC Lookup Table". You only need to call it once to
|
||||
initalize the table before using any of the other CRC32 calculation functions.
|
||||
*/
|
||||
|
||||
void CRC32::initialize(void)
|
||||
{
|
||||
//0x04C11DB7 is the official polynomial used by PKZip, WinZip and Ethernet.
|
||||
unsigned long ulPolynomial = 0x04C11DB7;
|
||||
|
||||
//memset(&this->ulTable, 0, sizeof(this->ulTable));
|
||||
|
||||
// 256 values representing ASCII character codes.
|
||||
for(int iCodes = 0; iCodes <= 0xFF; iCodes++)
|
||||
{
|
||||
this->ulTable[iCodes] = this->reflect(iCodes, 8) << 24;
|
||||
|
||||
for(int iPos = 0; iPos < 8; iPos++)
|
||||
{
|
||||
this->ulTable[iCodes] = (this->ulTable[iCodes] << 1)
|
||||
^ ((this->ulTable[iCodes] & (1 << 31)) ? ulPolynomial : 0);
|
||||
}
|
||||
|
||||
this->ulTable[iCodes] = this->reflect(this->ulTable[iCodes], 32);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Reflection is a requirement for the official CRC-32 standard.
|
||||
You can create CRCs without it, but they won't conform to the standard.
|
||||
*/
|
||||
|
||||
unsigned long CRC32::reflect(unsigned long ulReflect, const char cChar)
|
||||
{
|
||||
unsigned long ulValue = 0;
|
||||
|
||||
// Swap bit 0 for bit 7, bit 1 For bit 6, etc....
|
||||
for(int iPos = 1; iPos < (cChar + 1); iPos++)
|
||||
{
|
||||
if(ulReflect & 1)
|
||||
{
|
||||
ulValue |= (1 << (cChar - iPos));
|
||||
}
|
||||
ulReflect >>= 1;
|
||||
}
|
||||
|
||||
return ulValue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Calculates the CRC32 by looping through each of the bytes in sData.
|
||||
|
||||
Note: For Example usage example, see FileCRC().
|
||||
*/
|
||||
|
||||
void CRC32::partialCrc(unsigned long *ulCRC, const unsigned char *sData, unsigned long ulDataLength)
|
||||
{
|
||||
while(ulDataLength--)
|
||||
{
|
||||
//If your compiler complains about the following line, try changing each
|
||||
// occurrence of *ulCRC with "((unsigned long)*ulCRC)" or "*(unsigned long *)ulCRC".
|
||||
|
||||
*(unsigned long *)ulCRC =
|
||||
((*(unsigned long *)ulCRC) >> 8) ^ this->ulTable[((*(unsigned long *)ulCRC) & 0xFF) ^ *sData++];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Returns the calculated CRC32 (through ulOutCRC) for the given string.
|
||||
*/
|
||||
|
||||
void CRC32::fullCrc(const unsigned char *sData, unsigned long ulDataLength, unsigned long *ulOutCRC)
|
||||
{
|
||||
*(unsigned long *)ulOutCRC = 0xffffffff; //Initilaize the CRC.
|
||||
this->partialCrc(ulOutCRC, sData, ulDataLength);
|
||||
*(unsigned long *)ulOutCRC ^= 0xffffffff; //Finalize the CRC.
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Returns the calculated CRC23 for the given string.
|
||||
*/
|
||||
|
||||
unsigned long CRC32::fullCrc(const unsigned char *sData, unsigned long ulDataLength)
|
||||
{
|
||||
unsigned long ulCRC = 0xffffffff; //Initilaize the CRC.
|
||||
this->partialCrc(&ulCRC, sData, ulDataLength);
|
||||
return(ulCRC ^ 0xffffffff); //Finalize the CRC and return.
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Calculates the CRC32 of a file using the a user defined buffer.
|
||||
|
||||
Note: The buffer size DOES NOT affect the resulting CRC,
|
||||
it has been provided for performance purposes only.
|
||||
*/
|
||||
|
||||
bool CRC32::fileCrc(const char *sFileName, unsigned long *ulOutCRC, unsigned long ulBufferSize)
|
||||
{
|
||||
*(unsigned long *)ulOutCRC = 0xffffffff; //Initilaize the CRC.
|
||||
|
||||
FILE *fSource = NULL;
|
||||
unsigned char *sBuf = NULL;
|
||||
int iBytesRead = 0;
|
||||
|
||||
if((fSource = fopen(sFileName, "rb")) == NULL)
|
||||
{
|
||||
return false; //Failed to open file for read access.
|
||||
}
|
||||
|
||||
if(!(sBuf = (unsigned char *)malloc(ulBufferSize))) //Allocate memory for file buffering.
|
||||
{
|
||||
fclose(fSource);
|
||||
return false; //Out of memory.
|
||||
}
|
||||
|
||||
while((iBytesRead = fread(sBuf, sizeof(char), ulBufferSize, fSource)))
|
||||
{
|
||||
this->partialCrc(ulOutCRC, sBuf, iBytesRead);
|
||||
}
|
||||
|
||||
free(sBuf);
|
||||
fclose(fSource);
|
||||
|
||||
*(unsigned long *)ulOutCRC ^= 0xffffffff; //Finalize the CRC.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Calculates the CRC32 of a file using the a default buffer size of 1MB.
|
||||
|
||||
Note: The buffer size DOES NOT affect the resulting CRC,
|
||||
it has been provided for performance purposes only.
|
||||
*/
|
||||
|
||||
bool CRC32::fileCrc(const char *sFileName, unsigned long *ulOutCRC)
|
||||
{
|
||||
return this->fileCrc(sFileName, ulOutCRC, 1048576);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#endif
|
||||
32
src/libs/ice/ICECRC32.h
Normal file
32
src/libs/ice/ICECRC32.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_CRC32_H
|
||||
#define __ICE_CRC32_H
|
||||
namespace ice
|
||||
{
|
||||
class CRC32
|
||||
{
|
||||
public:
|
||||
CRC32(void);
|
||||
~CRC32(void);
|
||||
|
||||
void initialize(void);
|
||||
|
||||
bool fileCrc(const char *sFileName, unsigned long *ulOutCRC);
|
||||
bool fileCrc(const char *sFileName, unsigned long *ulOutCRC, unsigned long ulBufferSize);
|
||||
|
||||
unsigned long fullCrc(const unsigned char *sData, unsigned long ulDataLength);
|
||||
void fullCrc(const unsigned char *sData, unsigned long ulLength, unsigned long *ulOutCRC);
|
||||
|
||||
void partialCrc(unsigned long *ulCRC, const unsigned char *sData, unsigned long ulDataLength);
|
||||
|
||||
private:
|
||||
unsigned long reflect(unsigned long ulReflect, const char cChar);
|
||||
unsigned long ulTable[256]; // CRC lookup table array.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
188
src/libs/ice/ICECandidate.cpp
Normal file
188
src/libs/ice/ICECandidate.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICECandidate.h"
|
||||
#include "ICEError.h"
|
||||
#include "ICELog.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
void Candidate::setLocalAndExternalAddresses(std::string& ip, unsigned short portNumber)
|
||||
{
|
||||
mLocalAddr.setIp(ip);
|
||||
mLocalAddr.setPort(portNumber);
|
||||
mExternalAddr.setIp(ip);
|
||||
mExternalAddr.setPort(portNumber);
|
||||
}
|
||||
|
||||
void Candidate::setLocalAndExternalAddresses(NetworkAddress& addr)
|
||||
{
|
||||
mLocalAddr = addr;
|
||||
mExternalAddr = addr;
|
||||
}
|
||||
|
||||
void Candidate::setLocalAndExternalAddresses(NetworkAddress &addr, unsigned short altPort)
|
||||
{
|
||||
mLocalAddr = addr;
|
||||
mExternalAddr = addr;
|
||||
mLocalAddr.setPort(altPort);
|
||||
mExternalAddr.setPort(altPort);
|
||||
}
|
||||
|
||||
void Candidate::computePriority(int* typepreflist)
|
||||
{
|
||||
mPriority = (typepreflist[mType] << 24) + (mInterfacePriority << 8) + (256 - mComponentId);
|
||||
}
|
||||
|
||||
void Candidate::computeFoundation()
|
||||
{
|
||||
sprintf(mFoundation, "%u", unsigned((mType << 24) + inet_addr(mLocalAddr.ip().c_str())));
|
||||
}
|
||||
|
||||
const char* Candidate::type()
|
||||
{
|
||||
switch (mType)
|
||||
{
|
||||
case Candidate::Host:
|
||||
return "host";
|
||||
|
||||
case Candidate::ServerReflexive:
|
||||
return "srflx";
|
||||
|
||||
case Candidate::ServerRelayed:
|
||||
return "relay";
|
||||
|
||||
case Candidate::PeerReflexive:
|
||||
return "prflx";
|
||||
|
||||
default:
|
||||
ICELogCritical(<< "Bad candidate type, reverted to Host.");
|
||||
return "host";
|
||||
}
|
||||
}
|
||||
|
||||
std::string Candidate::createSdp()
|
||||
{
|
||||
char buffer[512];
|
||||
|
||||
unsigned port = (unsigned)mExternalAddr.port();
|
||||
|
||||
#define SDP_CANDIDATE_FORMAT "%s %u %s %u %s %u typ %s"
|
||||
|
||||
sprintf(buffer, SDP_CANDIDATE_FORMAT, strlen(mFoundation) ? mFoundation : "16777000" , mComponentId, "UDP", mPriority, mExternalAddr.ip().c_str(), port, type());
|
||||
|
||||
if (mType != Candidate::Host)
|
||||
{
|
||||
char relattr[64];
|
||||
sprintf(relattr, " raddr %s rport %u", mLocalAddr.ip().c_str(), (unsigned int)mLocalAddr.port());
|
||||
strcat(buffer, relattr);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Candidate Candidate::parseSdp(const char* sdp)
|
||||
{
|
||||
Candidate result(Candidate::Host);
|
||||
|
||||
char protocol[32], externalIP[128], candtype[32], raddr[64];
|
||||
unsigned int remoteport = 0;
|
||||
|
||||
unsigned int externalPort = 0;
|
||||
|
||||
// Look for "typ" string
|
||||
const char* formatstring;
|
||||
if (strstr(sdp, "typ"))
|
||||
formatstring = "%s %u %s %u %s %u typ %s raddr %s rport %u";
|
||||
else
|
||||
formatstring = "%s %u %s %u %s %u %s raddr %s rport %u";
|
||||
int wasread = sscanf(sdp, formatstring, &result.mFoundation, &result.mComponentId, protocol,
|
||||
&result.mPriority, externalIP, &externalPort, candtype, raddr, &remoteport);
|
||||
|
||||
if (wasread >= 7)
|
||||
{
|
||||
// Save external address
|
||||
result.mExternalAddr.setIp( externalIP );
|
||||
result.mExternalAddr.setPort( externalPort );
|
||||
|
||||
result.mLocalAddr = result.mExternalAddr;
|
||||
|
||||
// Check the protocol (UDP4/6 is supported only)
|
||||
#ifdef _WIN32
|
||||
_strupr(protocol); _strupr(candtype);
|
||||
#else
|
||||
strupr(protocol); strupr(candtype);
|
||||
#endif
|
||||
if (strcmp(protocol, "UDP") != 0)
|
||||
throw Exception(UDP_SUPPORTED_ONLY);
|
||||
|
||||
// Save candidate type
|
||||
result.mType = typeFromString(candtype);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Candidate::Type Candidate::typeFromString(const char* candtype)
|
||||
{
|
||||
if (!strcmp(candtype, "HOST"))
|
||||
return Candidate::Host;
|
||||
else
|
||||
if (!strcmp(candtype, "SRFLX"))
|
||||
return Candidate::ServerReflexive;
|
||||
else
|
||||
if (!strcmp(candtype, "PRFLX"))
|
||||
return Candidate::PeerReflexive;
|
||||
else
|
||||
if (!strcmp(candtype, "RELAY"))
|
||||
return Candidate::ServerRelayed;
|
||||
else
|
||||
{
|
||||
ICELogCritical(<< "Bad candidate type in parser. Reverted to Host");
|
||||
return Candidate::Host;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Candidate::equal(Candidate& cand1, Candidate& cand2)
|
||||
{
|
||||
if (cand1.mType != cand2.mType)
|
||||
return false;
|
||||
switch (cand1.mType)
|
||||
{
|
||||
case Candidate::Host:
|
||||
return (cand1.mLocalAddr == cand2.mLocalAddr);
|
||||
|
||||
case Candidate::ServerReflexive:
|
||||
case Candidate::PeerReflexive:
|
||||
case Candidate::ServerRelayed:
|
||||
return (cand1.mExternalAddr == cand2.mExternalAddr);
|
||||
|
||||
}
|
||||
|
||||
ICELogCritical(<< "Bad candidate type, comparing as Host");
|
||||
return cand1.mLocalAddr == cand2.mLocalAddr;
|
||||
}
|
||||
|
||||
bool Candidate::operator == (const Candidate& rhs) const
|
||||
{
|
||||
bool relayed1 = mType == ServerRelayed, relayed2 = rhs.mType == ServerRelayed;
|
||||
|
||||
return relayed1 == relayed2 && mLocalAddr == rhs.mLocalAddr && mExternalAddr == rhs.mExternalAddr;
|
||||
}
|
||||
|
||||
void Candidate::dump(std::ostream& output)
|
||||
{
|
||||
output << Logger::TabPrefix<< Logger::TabPrefix << createSdp().c_str() << std::endl;
|
||||
}
|
||||
|
||||
int Candidate::component()
|
||||
{
|
||||
return mComponentId;
|
||||
}
|
||||
113
src/libs/ice/ICECandidate.h
Normal file
113
src/libs/ice/ICECandidate.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_CANDIDATE_H
|
||||
#define __ICE_CANDIDATE_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICESmartPtr.h"
|
||||
#include "ICEAddress.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
struct Candidate
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Host = 0,
|
||||
ServerReflexive = 1,
|
||||
PeerReflexive = 2,
|
||||
ServerRelayed = 3
|
||||
};
|
||||
|
||||
// Type of candidate - host, server reflexive/relayes or peer reflexive
|
||||
Type mType;
|
||||
|
||||
// External address
|
||||
NetworkAddress mExternalAddr;
|
||||
|
||||
// The component ID in ICE session
|
||||
int mComponentId;
|
||||
|
||||
// Mark the gathered candidate
|
||||
bool mReady;
|
||||
|
||||
// Mark the failed candidate - gathering is failed
|
||||
bool mFailed;
|
||||
|
||||
// The candidate priority
|
||||
unsigned int mPriority;
|
||||
|
||||
// Candidate's foundation
|
||||
char mFoundation[33];
|
||||
|
||||
// Interface priority
|
||||
unsigned int mInterfacePriority;
|
||||
|
||||
// Local used address
|
||||
NetworkAddress mLocalAddr;
|
||||
|
||||
Candidate()
|
||||
:mType(Host), mComponentId(0), mReady(false), mFailed(false), mPriority(0),
|
||||
mInterfacePriority(0)
|
||||
{
|
||||
memset(mFoundation, 0, sizeof mFoundation);
|
||||
}
|
||||
|
||||
/* Constructor.
|
||||
* @param _type Type of candidate - host, reflexive or relayed.
|
||||
* @param componentID ID of component where candidate is used.
|
||||
* @param portNumber Local port number.
|
||||
* @param ipAddress Local IP address.
|
||||
* @param interfacePriority Priority of specified interface.
|
||||
* @param startTimeout Start time for STUN/TURN checks. */
|
||||
Candidate(Type _type)
|
||||
: mType(_type), mComponentId(0), mReady(false), mFailed(false), mPriority(0),
|
||||
mInterfacePriority(0)
|
||||
{
|
||||
// Check if type is "host" - the candidate is ready in this case
|
||||
if (_type == Host)
|
||||
mReady = true;
|
||||
memset(mFoundation, 0, sizeof mFoundation);
|
||||
}
|
||||
|
||||
~Candidate()
|
||||
{}
|
||||
|
||||
// Sets local and external address simultaneously
|
||||
void setLocalAndExternalAddresses(std::string& ip, unsigned short portNumber);
|
||||
void setLocalAndExternalAddresses(NetworkAddress& addr);
|
||||
void setLocalAndExternalAddresses(NetworkAddress &addr, unsigned short altPort);
|
||||
|
||||
// Returns type of candidate as string - 'host', 'reflexive' or 'relayed'
|
||||
const char* type();
|
||||
|
||||
// Computes priority value basing on members and type priority list
|
||||
void computePriority(int* typepreflist);
|
||||
|
||||
// Updates foundation value depending on members
|
||||
void computeFoundation();
|
||||
|
||||
// Returns SDP line for this candidate
|
||||
std::string createSdp();
|
||||
|
||||
// Creates ICECandidate instance based on SDP line
|
||||
static Candidate parseSdp(const char* sdp);
|
||||
|
||||
// Returns candidate type basing on string type representation
|
||||
static Type typeFromString(const char* candtype);
|
||||
|
||||
// Compares if two candidates are equal
|
||||
static bool equal(Candidate& cand1, Candidate& cand2);
|
||||
|
||||
bool operator == (const Candidate& rhs) const;
|
||||
void dump(std::ostream& output);
|
||||
int component();
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
223
src/libs/ice/ICECandidatePair.cpp
Normal file
223
src/libs/ice/ICECandidatePair.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICECandidatePair.h"
|
||||
#include "ICEBinding.h"
|
||||
#include "ICERelaying.h"
|
||||
#include "ICELog.h"
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace ice;
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
CandidatePair::CandidatePair()
|
||||
:mPriority(0), mState(CandidatePair::Frozen), mControlledIndex(0), mControllingIndex(1),
|
||||
mNomination(Nomination_None), mRole(Regular), mTransaction(NULL)
|
||||
{
|
||||
memset(mFoundation, 0, sizeof mFoundation);
|
||||
}
|
||||
|
||||
CandidatePair::CandidatePair(const CandidatePair& src)
|
||||
:mPriority(src.mPriority), mState(src.mState), mControlledIndex(src.mControlledIndex),
|
||||
mControllingIndex(src.mControllingIndex), mNomination(src.mNomination),
|
||||
mRole(src.mRole), mTransaction(src.mTransaction)
|
||||
{
|
||||
mCandidate[0] = src.mCandidate[0];
|
||||
mCandidate[1] = src.mCandidate[1];
|
||||
memcpy(mFoundation, src.mFoundation, sizeof mFoundation);
|
||||
}
|
||||
|
||||
CandidatePair& CandidatePair::operator = (const CandidatePair& src)
|
||||
{
|
||||
mPriority = src.mPriority;
|
||||
mState = src.mState;
|
||||
memcpy(mFoundation, src.mFoundation, sizeof mFoundation);
|
||||
mControlledIndex = src.mControlledIndex;
|
||||
mControllingIndex = src.mControllingIndex;
|
||||
mNomination = src.mNomination;
|
||||
mTransaction = src.mTransaction;
|
||||
mRole = src.mRole;
|
||||
mCandidate[0] = src.mCandidate[0];
|
||||
mCandidate[1] = src.mCandidate[1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
CandidatePair::~CandidatePair()
|
||||
{
|
||||
}
|
||||
|
||||
const char* CandidatePair::stateToString(State s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case Waiting: return "Waiting";
|
||||
case InProgress: return "InProgress";
|
||||
case Succeeded: return "Succeeded";
|
||||
case Failed: return "Failed";
|
||||
case Frozen: return "Frozen";
|
||||
}
|
||||
return "UNEXPECTED";
|
||||
}
|
||||
|
||||
const char* CandidatePair::nominationToString(ice::CandidatePair::Nomination n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case Nomination_None: return "nomination:none";
|
||||
case Nomination_Started: return "nomination:started";
|
||||
case Nomination_Finished: return "nomination:finished";
|
||||
}
|
||||
return "nomination:bad";
|
||||
}
|
||||
|
||||
void CandidatePair::updatePriority()
|
||||
{
|
||||
unsigned int G = mCandidate[mControllingIndex].mPriority;
|
||||
unsigned int D = mCandidate[mControlledIndex].mPriority;
|
||||
// As RFC says...
|
||||
mPriority = 0xFFFFFFFF * MINVALUE(G, D) + 2*MAXVALUE(G,D) + ((G>D)?1:0);
|
||||
|
||||
// ICELogDebug(<< "G=" << G << ", D=" << D << ", priority=" << mPriority);
|
||||
}
|
||||
|
||||
void CandidatePair::updateFoundation()
|
||||
{
|
||||
// Get a combination of controlling and controlled foundations
|
||||
strcpy(mFoundation, mCandidate[mControlledIndex].mFoundation);
|
||||
strcat(mFoundation, mCandidate[mControlledIndex].mFoundation);
|
||||
}
|
||||
|
||||
bool CandidatePair::operator == (const CandidatePair& rhs) const
|
||||
{
|
||||
return this->mCandidate[0] == rhs.mCandidate[0] && this->mCandidate[1] == rhs.mCandidate[1];
|
||||
}
|
||||
|
||||
const char* CandidatePair::roleToString(Role r)
|
||||
{
|
||||
switch (r)
|
||||
{
|
||||
case Regular: return "regular";
|
||||
case Triggered: return "triggered";
|
||||
case Valid: return "valid";
|
||||
case None: return "none";
|
||||
}
|
||||
return "UNEXPECTED";
|
||||
}
|
||||
|
||||
std::string CandidatePair::toStdString()
|
||||
{
|
||||
char result[256];
|
||||
|
||||
sprintf(result, "(%s%s) %s %s -> %s %s ", roleToString(mRole), nominationToString(mNomination),
|
||||
mCandidate[0].type(), mCandidate[0].mLocalAddr.toStdString().c_str(), mCandidate[1].type(),
|
||||
mCandidate[1].mExternalAddr.toStdString().c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CandidatePair::isLanOnly() const
|
||||
{
|
||||
return first().mLocalAddr.isLAN() &&
|
||||
second().mExternalAddr.isLAN();
|
||||
}
|
||||
|
||||
CandidatePair::Role CandidatePair::role() const
|
||||
{
|
||||
return mRole;
|
||||
};
|
||||
|
||||
void CandidatePair::setRole(Role role)
|
||||
{
|
||||
mRole = role;
|
||||
}
|
||||
|
||||
CandidatePair::Nomination CandidatePair::nomination() const
|
||||
{
|
||||
return mNomination;
|
||||
}
|
||||
|
||||
void CandidatePair::setNomination(ice::CandidatePair::Nomination n)
|
||||
{
|
||||
mNomination = n;
|
||||
}
|
||||
|
||||
const char* CandidatePair::foundation() const
|
||||
{
|
||||
return mFoundation;
|
||||
};
|
||||
|
||||
void CandidatePair::setFoundation(const char* foundation)
|
||||
{
|
||||
strcpy(mFoundation, foundation);
|
||||
}
|
||||
|
||||
CandidatePair::State CandidatePair::state() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void CandidatePair::setState(CandidatePair::State state)
|
||||
{
|
||||
mState = state;
|
||||
}
|
||||
|
||||
int64_t CandidatePair::priority() const
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
void CandidatePair::setPriority(int64_t priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
Candidate& CandidatePair::first()
|
||||
{
|
||||
return mCandidate[0];
|
||||
}
|
||||
|
||||
const Candidate& CandidatePair::first() const
|
||||
{
|
||||
return mCandidate[0];
|
||||
}
|
||||
|
||||
Candidate& CandidatePair::second()
|
||||
{
|
||||
return mCandidate[1];
|
||||
}
|
||||
|
||||
const Candidate& CandidatePair::second() const
|
||||
{
|
||||
return mCandidate[1];
|
||||
}
|
||||
unsigned CandidatePair::controlledIndex()
|
||||
{
|
||||
return mControlledIndex;
|
||||
}
|
||||
|
||||
void CandidatePair::setControlledIndex(unsigned index)
|
||||
{
|
||||
mControlledIndex = index;
|
||||
}
|
||||
|
||||
unsigned CandidatePair::controllingIndex()
|
||||
{
|
||||
return mControllingIndex;
|
||||
}
|
||||
|
||||
void CandidatePair::setControllingIndex(unsigned index)
|
||||
{
|
||||
mControllingIndex = index;
|
||||
}
|
||||
|
||||
void* CandidatePair::transaction()
|
||||
{
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
void CandidatePair::setTransaction(void* t)
|
||||
{
|
||||
mTransaction = t;
|
||||
}
|
||||
113
src/libs/ice/ICECandidatePair.h
Normal file
113
src/libs/ice/ICECandidatePair.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_CANDIDATE_PAIR_H
|
||||
#define __ICE_CANDIDATE_PAIR_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICECandidate.h"
|
||||
#include "ICEByteBuffer.h"
|
||||
#include "ICESmartPtr.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class CandidatePair
|
||||
{
|
||||
public:
|
||||
/// Possible pair states
|
||||
enum State
|
||||
{
|
||||
Waiting = 0,
|
||||
InProgress,
|
||||
Succeeded,
|
||||
Failed,
|
||||
Frozen
|
||||
};
|
||||
|
||||
enum Role
|
||||
{
|
||||
None = 0,
|
||||
Regular = 1,
|
||||
Triggered = 2,
|
||||
Valid = 3
|
||||
};
|
||||
|
||||
enum Nomination
|
||||
{
|
||||
Nomination_None,
|
||||
Nomination_Started,
|
||||
Nomination_Finished
|
||||
};
|
||||
|
||||
static const char* stateToString(State s);
|
||||
static const char* nominationToString(Nomination n);
|
||||
|
||||
|
||||
protected:
|
||||
/// Local and remote candidates. First (zero index) is local candidate, second (index one) is remote candidate
|
||||
Candidate mCandidate[2];
|
||||
|
||||
/// Pair's priority, computed in UpdatePriority() method
|
||||
int64_t mPriority;
|
||||
|
||||
/// Pair's state
|
||||
State mState;
|
||||
|
||||
/// Index of controlled candidate in mCandidate array
|
||||
int mControlledIndex;
|
||||
|
||||
/// Index of controlling candidate in mCandidate array
|
||||
int mControllingIndex;
|
||||
|
||||
/// Combination of controlled and controlling candidates foundations, computed in UpdateFoundation() method.
|
||||
char mFoundation[65];
|
||||
|
||||
/// Marks nominated pair
|
||||
Nomination mNomination;
|
||||
|
||||
/// Mark pair role - regular, triggered, valid, nominated
|
||||
Role mRole;
|
||||
void* mTransaction;
|
||||
|
||||
static const char* roleToString(Role r);
|
||||
|
||||
public:
|
||||
CandidatePair();
|
||||
CandidatePair(const CandidatePair& src);
|
||||
~CandidatePair();
|
||||
|
||||
Candidate& first();
|
||||
const Candidate& first() const;
|
||||
Candidate& second();
|
||||
const Candidate& second() const;
|
||||
|
||||
int64_t priority() const;
|
||||
void setPriority(int64_t priority);
|
||||
State state() const;
|
||||
void setState(State state);
|
||||
const char* foundation() const;
|
||||
void setFoundation(const char* foundation);
|
||||
Role role() const;
|
||||
void setRole(Role role);
|
||||
Nomination nomination() const;
|
||||
void setNomination(Nomination n);
|
||||
unsigned controlledIndex();
|
||||
void setControlledIndex(unsigned index);
|
||||
unsigned controllingIndex();
|
||||
void setControllingIndex(unsigned index);
|
||||
void updatePriority();
|
||||
void updateFoundation();
|
||||
std::string toStdString();
|
||||
bool isLanOnly() const;
|
||||
|
||||
void* transaction();
|
||||
void setTransaction(void* t);
|
||||
CandidatePair& operator = (const CandidatePair& rhs);
|
||||
bool operator == (const CandidatePair& rhs) const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<CandidatePair> PCandidatePair;
|
||||
}
|
||||
#endif
|
||||
436
src/libs/ice/ICECheckList.cpp
Normal file
436
src/libs/ice/ICECheckList.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICECheckList.h"
|
||||
#include "ICENetworkHelper.h"
|
||||
#include "ICELog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <strstream>
|
||||
|
||||
using namespace ice;
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
const char* CheckList::stateToString(int state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Running: return "Running";
|
||||
case Completed: return "Completed";
|
||||
case Failed: return "Failed";
|
||||
}
|
||||
return "Undefined";
|
||||
}
|
||||
CheckList::CheckList()
|
||||
:mState(CheckList::Running)
|
||||
{
|
||||
}
|
||||
|
||||
CheckList::State CheckList::state()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void CheckList::setState(CheckList::State state)
|
||||
{
|
||||
mState = state;
|
||||
}
|
||||
|
||||
CheckList::PairList& CheckList::pairlist()
|
||||
{
|
||||
return mPairList;
|
||||
}
|
||||
|
||||
static bool ComparePairByPriority(const PCandidatePair& pair1, const PCandidatePair& pair2)
|
||||
{
|
||||
return pair1->priority() > pair2->priority();
|
||||
}
|
||||
|
||||
void CheckList::pruneDuplicates()
|
||||
{
|
||||
// Remove duplicates
|
||||
for (PairList::iterator pairIter = mPairList.begin(); pairIter != mPairList.end(); pairIter++)
|
||||
{
|
||||
PCandidatePair& pair = *pairIter;
|
||||
|
||||
// Get the iterator to next element
|
||||
PairList::iterator nextPairIter = pairIter; std::advance(nextPairIter, 1);
|
||||
|
||||
// Iterate next elements to find duplicates
|
||||
while (nextPairIter != mPairList.end())
|
||||
{
|
||||
PCandidatePair& nextPair = *nextPairIter;
|
||||
|
||||
bool sameType = pair->first().mLocalAddr.family() == nextPair->second().mLocalAddr.family();
|
||||
bool sameAddress = pair->second().mExternalAddr == nextPair->second().mExternalAddr;
|
||||
bool relayed1 = pair->first().mType == Candidate::ServerRelayed;
|
||||
bool relayed2 = nextPair->first().mType == Candidate::ServerRelayed;
|
||||
bool sameRelayed = relayed1 == relayed2;
|
||||
#ifdef ICE_SMART_PRUNE_CHECKLIST
|
||||
if (sameType && sameAddress && sameRelayed)
|
||||
#else
|
||||
if (equalCandidates && sameAddress && sameRelayed)
|
||||
#endif
|
||||
nextPairIter = mPairList.erase(nextPairIter);
|
||||
else
|
||||
nextPairIter++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CheckList::prune(int /*checkLimit*/)
|
||||
{
|
||||
// Sort list by priority
|
||||
std::sort(mPairList.begin(), mPairList.end(), ComparePairByPriority);
|
||||
|
||||
// Replace server reflexive candidates with bases
|
||||
for (unsigned ci=0; ci<mPairList.size(); ci++)
|
||||
{
|
||||
PCandidatePair& pair = mPairList[ci];
|
||||
if (pair->first().mType == Candidate::ServerReflexive)
|
||||
pair->first().mType = Candidate::Host;
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
Win32Preprocess();
|
||||
#endif
|
||||
*/
|
||||
|
||||
pruneDuplicates();
|
||||
|
||||
// Erase all relayed checks to LAN candidates
|
||||
PairList::iterator pairIter = mPairList.begin();
|
||||
while (pairIter != mPairList.end())
|
||||
{
|
||||
PCandidatePair& pair = *pairIter;
|
||||
if (pair->first().mType == Candidate::ServerRelayed && !pair->second().mExternalAddr.isPublic())
|
||||
pairIter = mPairList.erase(pairIter);
|
||||
else
|
||||
pairIter++;
|
||||
}
|
||||
|
||||
#ifndef ICE_LOOPBACK_SUPPORT
|
||||
pairIter = mPairList.begin();
|
||||
while (pairIter != mPairList.end())
|
||||
{
|
||||
PCandidatePair& pair = *pairIter;
|
||||
if (pair->second().mExternalAddr.isLoopback())
|
||||
pairIter = mPairList.erase(pairIter);
|
||||
else
|
||||
pairIter++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ICE_SKIP_LINKLOCAL
|
||||
pairIter = mPairList.begin();
|
||||
while (pairIter != mPairList.end())
|
||||
{
|
||||
PCandidatePair& pair = *pairIter;
|
||||
if (pair->second().mExternalAddr.isLinkLocal())
|
||||
pairIter = mPairList.erase(pairIter);
|
||||
else
|
||||
pairIter++;
|
||||
}
|
||||
#endif
|
||||
pruneDuplicates();
|
||||
|
||||
#ifdef ICE_POSTPONE_RELAYEDCHECKS
|
||||
postponeRelayed();
|
||||
#endif
|
||||
|
||||
// Put all LAN checks before other.
|
||||
// Therefore it should be priorities sorting should be enough.
|
||||
// But in answer's SDP remote peer can put external IP to the top of the list
|
||||
// It can cause its promote to the top of list of connectivity checks
|
||||
std::sort(mPairList.begin(), mPairList.end(), [](const PCandidatePair& p1, const PCandidatePair& p2) -> bool
|
||||
{
|
||||
return p1->isLanOnly() && !p2->isLanOnly();
|
||||
});
|
||||
|
||||
// Cut all checks that are behind the limit
|
||||
if (mPairList.size() > ICE_CONNCHECK_LIMIT)
|
||||
{
|
||||
ICELogDebug(<<"Cut extra connection checks. The total number of checks should not exceed " << ICE_CONNCHECK_LIMIT);
|
||||
PairList::iterator bcIter = mPairList.begin();
|
||||
std::advance(bcIter, ICE_CONNCHECK_LIMIT);
|
||||
mPairList.erase(bcIter, mPairList.end());
|
||||
}
|
||||
}
|
||||
|
||||
void CheckList::win32Preprocess()
|
||||
{
|
||||
for (unsigned i=0; i<mPairList.size(); i++)
|
||||
{
|
||||
PCandidatePair& pair = mPairList[i];
|
||||
ICELogDebug(<<"Win32Preprocess pair " << pair->toStdString());
|
||||
|
||||
if (pair->first().mType == Candidate::Host)
|
||||
{
|
||||
// Get best source interface for remote candidate
|
||||
NetworkAddress bestInterface = NetworkHelper::instance().sourceInterface(pair->second().mExternalAddr);
|
||||
|
||||
// Replace IP address to found
|
||||
if (!bestInterface.isEmpty())
|
||||
pair->first().mLocalAddr.setIp(bestInterface.ip());
|
||||
else
|
||||
ICELogDebug(<<"Failed to find source interface for remote candidate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findEqualPair(CandidatePair& _pair, ComparisionType ct)
|
||||
{
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
if (p->role() != CandidatePair::None)
|
||||
{
|
||||
switch (ct)
|
||||
{
|
||||
case CT_TreatHostAsUniform:
|
||||
if (p->first().mType == Candidate::Host && _pair.first().mType == Candidate::Host && p->second() == _pair.second())
|
||||
return p;
|
||||
if (p->first().mLocalAddr == _pair.first().mLocalAddr && p->first().mType == Candidate::Host && p->second() == _pair.second() && _pair.first().mType != Candidate::ServerRelayed)
|
||||
return p;
|
||||
if (_pair == *p)
|
||||
return p;
|
||||
|
||||
break;
|
||||
|
||||
case CT_Strict:
|
||||
if (_pair == *p)
|
||||
return p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PCandidatePair();
|
||||
}
|
||||
|
||||
unsigned CheckList::add(CandidatePair& p)
|
||||
{
|
||||
mPairList.push_back(PCandidatePair(new CandidatePair(p)));
|
||||
|
||||
// Sort list by priority
|
||||
std::sort(mPairList.begin(), mPairList.end(), ComparePairByPriority);
|
||||
|
||||
return (unsigned)mPairList.size();
|
||||
}
|
||||
|
||||
unsigned CheckList::count()
|
||||
{
|
||||
return (unsigned)mPairList.size();
|
||||
}
|
||||
std::string CheckList::toStdString()
|
||||
{
|
||||
std::string dump = "";
|
||||
for (size_t i=0; i<mPairList.size(); i++)
|
||||
{
|
||||
dump += mPairList[i]->toStdString();
|
||||
dump += "\n";
|
||||
}
|
||||
|
||||
return dump;
|
||||
}
|
||||
|
||||
PCandidatePair& CheckList::operator[] (size_t index)
|
||||
{
|
||||
return mPairList[index];
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findNominatedPair(int component)
|
||||
{
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
if (p->first().component() == component &&
|
||||
p->role() != CandidatePair::None &&
|
||||
p->nomination() == CandidatePair::Nomination_Finished)
|
||||
return p;
|
||||
}
|
||||
return PCandidatePair();
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findValidPair(int component)
|
||||
{
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
if (p->first().component() == component &&
|
||||
p->role() == CandidatePair::Valid)
|
||||
return p;
|
||||
}
|
||||
return PCandidatePair();
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findBestValidPair(int componentId)
|
||||
{
|
||||
PairList found;
|
||||
std::copy_if(mPairList.begin(), mPairList.end(), std::back_inserter(found),
|
||||
[componentId](const PCandidatePair& p) -> bool
|
||||
{
|
||||
return (p->first().component() == componentId &&
|
||||
p->role() == CandidatePair::Valid &&
|
||||
p->first().mExternalAddr.isLAN() &&
|
||||
p->second().mExternalAddr.isLAN());
|
||||
});
|
||||
|
||||
if (found.size())
|
||||
return found.front();
|
||||
|
||||
return findValidPair(componentId);
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findHighestNominatedPair(int component)
|
||||
{
|
||||
#ifdef ICE_POSTPONE_RELAYEDCHECKS
|
||||
PairList found;
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
if (p->first().component() == component &&
|
||||
p->role() != CandidatePair::None &&
|
||||
p->nomination() == CandidatePair::Nomination_Finished &&
|
||||
p->first().mType != Candidate::ServerRelayed &&
|
||||
p->second().mType != Candidate::ServerRelayed)
|
||||
found.push_back( p );
|
||||
}
|
||||
|
||||
if (found.size())
|
||||
{
|
||||
PCandidatePair& f0 = found.front();
|
||||
for (PCandidatePair& p: mPairList)
|
||||
if (p == f0)
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
int64_t priority = -1;
|
||||
PCandidatePair result;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Looking for highest nominated pair in list:";
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
oss << "\n " << p->toStdString().c_str() << ", priority " << p->priority();
|
||||
|
||||
if (p->first().component() == component &&
|
||||
p->role() != CandidatePair::None &&
|
||||
p->nomination() == CandidatePair::Nomination_Finished &&
|
||||
p->priority() > priority )
|
||||
{
|
||||
result = p;
|
||||
priority = p->priority();
|
||||
}
|
||||
}
|
||||
|
||||
// ICELogDebug( << oss.str() );
|
||||
|
||||
if (result)
|
||||
{
|
||||
ICELogDebug(<< "Result is " << result->toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// ICELogDebug(<< "No nominated pair");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CheckList::removePairs(CandidatePair::State state, int component)
|
||||
{
|
||||
for (unsigned i=0; i<mPairList.size(); i++)
|
||||
{
|
||||
CandidatePair& p = *mPairList[i];
|
||||
if (p.first().component() == component &&
|
||||
p.state() == state &&
|
||||
p.role() != CandidatePair::None)
|
||||
p.setRole(CandidatePair::None);
|
||||
}
|
||||
}
|
||||
|
||||
PCandidatePair CheckList::findLowestNominatedPair(int component)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
int64_t priority = 0x7FFFFFFFFFFF;
|
||||
#else
|
||||
int64_t priority = 0x7FFFFFFFFFFFLL;
|
||||
#endif
|
||||
PCandidatePair result;
|
||||
|
||||
for (PCandidatePair& p: mPairList)
|
||||
{
|
||||
if (p->first().component() == component &&
|
||||
p->role() != CandidatePair::None &&
|
||||
p->nomination() == CandidatePair::Nomination_Finished &&
|
||||
p->priority() < priority )
|
||||
{
|
||||
result = p;
|
||||
priority = p->priority();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CheckList::updatePairPriorities()
|
||||
{
|
||||
for (unsigned i=0; i<mPairList.size(); i++)
|
||||
mPairList[i]->updatePriority();
|
||||
}
|
||||
|
||||
void CheckList::clear()
|
||||
{
|
||||
mState = Running;
|
||||
mPairList.clear();
|
||||
}
|
||||
|
||||
void CheckList::dump(std::ostream& output)
|
||||
{
|
||||
output << Logger::TabPrefix << Logger::TabPrefix << "State: " << CheckList::stateToString(mState) << std::endl;
|
||||
for (unsigned i=0; i<mPairList.size(); i++)
|
||||
if (mPairList[i]->role() != CandidatePair::None)
|
||||
output << Logger::TabPrefix << Logger::TabPrefix << mPairList[i]->toStdString().c_str() << std::endl;
|
||||
}
|
||||
|
||||
unsigned CheckList::countOfValidPairs()
|
||||
{
|
||||
unsigned result = 0;
|
||||
for (unsigned i=0; i<mPairList.size(); i++)
|
||||
if (mPairList[i]->role() >= CandidatePair::Valid)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void CheckList::postponeRelayed()
|
||||
{
|
||||
PairList relayed, relayedTarget;
|
||||
|
||||
PairList::iterator pairIter = mPairList.begin();
|
||||
while ( pairIter != mPairList.end() )
|
||||
{
|
||||
PCandidatePair& p = *pairIter;
|
||||
if (p->first().mType == Candidate::ServerRelayed)
|
||||
{
|
||||
relayed.push_back(p);
|
||||
pairIter = mPairList.erase( pairIter );
|
||||
}
|
||||
else
|
||||
if (p->second().mType == Candidate::ServerRelayed)
|
||||
{
|
||||
relayedTarget.push_back(p);
|
||||
pairIter = mPairList.erase( pairIter );
|
||||
}
|
||||
else
|
||||
pairIter++;
|
||||
}
|
||||
|
||||
for (pairIter = relayedTarget.begin(); pairIter != relayedTarget.end(); pairIter++)
|
||||
mPairList.push_back(*pairIter);
|
||||
|
||||
for (pairIter = relayed.begin(); pairIter != relayed.end(); pairIter++)
|
||||
mPairList.push_back(*pairIter);
|
||||
}
|
||||
102
src/libs/ice/ICECheckList.h
Normal file
102
src/libs/ice/ICECheckList.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_CHECK_LIST_H
|
||||
#define __ICE_CHECK_LIST_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICECandidatePair.h"
|
||||
#include "ICESmartPtr.h"
|
||||
#include <vector>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class CheckList
|
||||
{
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
Running = 0,
|
||||
Completed,
|
||||
Failed
|
||||
};
|
||||
static const char* stateToString(int state);
|
||||
typedef std::vector<PCandidatePair> PairList;
|
||||
|
||||
protected:
|
||||
/// Check list state
|
||||
State mState;
|
||||
|
||||
/// Vector of pairs
|
||||
PairList mPairList;
|
||||
|
||||
void pruneDuplicates();
|
||||
void postponeRelayed();
|
||||
|
||||
public:
|
||||
CheckList();
|
||||
|
||||
State state();
|
||||
void setState(State state);
|
||||
PairList& pairlist();
|
||||
|
||||
/// Sorts check list, prunes from duplicate pairs and cuts to checkLimit number of elements.
|
||||
void prune(int checkLimit);
|
||||
|
||||
/// Replaces local host candidates IP addresses with best source interface.
|
||||
void win32Preprocess();
|
||||
|
||||
/// Finds&returns smart pointer to pair
|
||||
enum ComparisionType
|
||||
{
|
||||
CT_TreatHostAsUniform,
|
||||
CT_Strict
|
||||
};
|
||||
PCandidatePair findEqualPair(CandidatePair& p, ComparisionType ct);
|
||||
|
||||
/// Add&sort
|
||||
unsigned add(CandidatePair& p);
|
||||
|
||||
/// Returns number of pairs in list
|
||||
unsigned count();
|
||||
|
||||
/// Returns items of list
|
||||
//ICECandidatePair operator[] (size_t index) const;
|
||||
PCandidatePair& operator[] (size_t index);
|
||||
|
||||
/// Dumps check list to string
|
||||
std::string toStdString();
|
||||
|
||||
/// Updates state of list based on items state
|
||||
void updateState();
|
||||
|
||||
/// Find first nominated pair for specified component ID
|
||||
PCandidatePair findNominatedPair(int componentID);
|
||||
|
||||
/// Finds valid pair for specified component ID. It is used for valid lists only.
|
||||
PCandidatePair findValidPair(int componentID);
|
||||
|
||||
/// Finds best valid pair for specified component ID. 'Best' means to prefer 1) LAN addresses 2) reflexive addresses 3)relayed addresses is low priority
|
||||
PCandidatePair findBestValidPair(int componentId);
|
||||
|
||||
/// Finds nominated pair with highest priority and specified component ID
|
||||
PCandidatePair findHighestNominatedPair(int componentID);
|
||||
|
||||
/// Finds nominated pair with highest priority and specified component ID
|
||||
PCandidatePair findLowestNominatedPair(int componentID);
|
||||
|
||||
/// Removes from list pairs with specified state and component ID.
|
||||
void removePairs(CandidatePair::State state, int componentID);
|
||||
|
||||
/// Recomputes pair priorities
|
||||
void updatePairPriorities();
|
||||
|
||||
void clear();
|
||||
void dump(std::ostream& output);
|
||||
unsigned countOfValidPairs();
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
84
src/libs/ice/ICEError.cpp
Normal file
84
src/libs/ice/ICEError.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEError.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
std::string ErrorInfo::errorMsg(int errorCode)
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case 403: return "(Forbidden): The request was valid, but cannot be performed due \
|
||||
to administrative or similar restrictions.";
|
||||
|
||||
case 437: return "(Allocation Mismatch): A request was received by the server that \
|
||||
requires an allocation to be in place, but there is none, or a \
|
||||
request was received which requires no allocation, but there is \
|
||||
one.";
|
||||
|
||||
case 441: return "(Wrong Credentials): The credentials in the (non-Allocate) \
|
||||
request, though otherwise acceptable to the server, do not match \
|
||||
those used to create the allocation.";
|
||||
|
||||
case 442: return "(Unsupported Transport Protocol): The Allocate request asked the \
|
||||
server to use a transport protocol between the server and the peer \
|
||||
that the server does not support. NOTE: This does NOT refer to \
|
||||
the transport protocol used in the 5-tuple.";
|
||||
|
||||
case 486: return "(Allocation Quota Reached): No more allocations using this \
|
||||
username can be created at the present time.";
|
||||
|
||||
case 508: return "(Insufficient Capacity): The server is unable to carry out the \
|
||||
request due to some capacity limit being reached. In an Allocate \
|
||||
response, this could be due to the server having no more relayed \
|
||||
transport addresses available right now, or having none with the \
|
||||
requested properties, or the one that corresponds to the specified \
|
||||
reservation token is not available.";
|
||||
}
|
||||
|
||||
return "Unknown error.";
|
||||
|
||||
}
|
||||
|
||||
Exception::Exception(int errorCode, std::string errorMsg)
|
||||
:mErrorCode(errorCode), mSubcode(0), mErrorMsg(errorMsg)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::Exception(int code)
|
||||
:mErrorCode(code), mSubcode(0)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::Exception(int code, int subcode)
|
||||
:mErrorCode(code), mSubcode(subcode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Exception::Exception(const Exception& src)
|
||||
:mErrorCode(src.mErrorCode), mErrorMsg(src.mErrorMsg)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::~Exception()
|
||||
{
|
||||
}
|
||||
|
||||
int Exception::code()
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
|
||||
int Exception::subcode()
|
||||
{
|
||||
return mSubcode;
|
||||
}
|
||||
|
||||
std::string Exception::message()
|
||||
{
|
||||
return mErrorMsg;
|
||||
}
|
||||
57
src/libs/ice/ICEError.h
Normal file
57
src/libs/ice/ICEError.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_ERROR_H
|
||||
#define __ICE_ERROR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
class ErrorInfo
|
||||
{
|
||||
public:
|
||||
static std::string errorMsg(int errorCode);
|
||||
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GETIFADDRS_FAILED = 1,
|
||||
WRONG_CANDIDATE_TYPE,
|
||||
UDP_SUPPORTED_ONLY,
|
||||
NOT_ENOUGH_DATA,
|
||||
CANNOT_FIND_INTERFACES,
|
||||
NO_PRIORITY_ATTRIBUTE,
|
||||
CANNOT_FIND_ATTRIBUTE,
|
||||
UNKNOWN_ATTRIBUTE,
|
||||
WRONG_IP_ADDRESS
|
||||
};
|
||||
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
|
||||
Exception(int code, std::string msg);
|
||||
Exception(int code);
|
||||
Exception(int code, int subcode);
|
||||
|
||||
Exception(const Exception& src);
|
||||
~Exception();
|
||||
|
||||
int code();
|
||||
int subcode();
|
||||
std::string message();
|
||||
|
||||
protected:
|
||||
int mErrorCode;
|
||||
int mSubcode;
|
||||
std::string mErrorMsg;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
62
src/libs/ice/ICEEvent.h
Normal file
62
src/libs/ice/ICEEvent.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_EVENT_H
|
||||
#define __ICE_EVENT_H
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class Stack;
|
||||
|
||||
class StageHandler
|
||||
{
|
||||
public:
|
||||
// Fires when candidates are gathered
|
||||
virtual void onGathered(Stack* stack, void* tag) = 0;
|
||||
|
||||
// Fires when connectivity checks finished ok
|
||||
virtual void onSuccess(Stack* stack, void* tag) = 0;
|
||||
|
||||
// Fires when connectivity checks failed (timeout usually)
|
||||
virtual void onFailed(Stack* stack, void* tag) = 0;
|
||||
};
|
||||
|
||||
class ChannelBoundCallback
|
||||
{
|
||||
public:
|
||||
virtual ~ChannelBoundCallback() {}
|
||||
virtual void onChannelBound(int /*stream*/, int /*component*/, int /*error*/) {}
|
||||
};
|
||||
|
||||
class InstallPermissionsCallback
|
||||
{
|
||||
public:
|
||||
virtual ~InstallPermissionsCallback() {}
|
||||
virtual void onPermissionsInstalled(int /*stream*/, int /*component*/, int /*error*/) {}
|
||||
};
|
||||
|
||||
class DeletePermissionsCallback
|
||||
{
|
||||
public:
|
||||
virtual ~DeletePermissionsCallback() {}
|
||||
virtual void onPermissionsDeleted(int /*stream*/, int /*component*/, int /*error*/) {}
|
||||
};
|
||||
|
||||
class DeleteAllocationCallback
|
||||
{
|
||||
public:
|
||||
virtual ~DeleteAllocationCallback() {}
|
||||
virtual void onAllocationDeleted(int /*stream*/, int /*component*/, int /*error*/) {}
|
||||
};
|
||||
|
||||
class RefreshAllocationCallback
|
||||
{
|
||||
public:
|
||||
virtual ~RefreshAllocationCallback();
|
||||
virtual void onAllocationRefreshed(int /*stream*/, int /*component*/, int /*error*/) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
65
src/libs/ice/ICEHMAC.cpp
Normal file
65
src/libs/ice/ICEHMAC.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
//******************************************************************************
|
||||
//* HMAC_SHA1.cpp : Implementation of HMAC SHA1 algorithm
|
||||
//* Comfort to RFC 2104
|
||||
//*
|
||||
//******************************************************************************
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "ICEHMAC.h"
|
||||
#include "ICESHA1.h"
|
||||
|
||||
void ICEHMAC::GetDigest(unsigned char *text, int text_len, unsigned char *key, int key_len, unsigned char *digest)
|
||||
{
|
||||
memset(SHA1_Key, 0, SHA1_BLOCK_SIZE);
|
||||
|
||||
/* repeated 64 times for values in ipad and opad */
|
||||
memset(m_ipad, 0x36, sizeof(m_ipad));
|
||||
memset(m_opad, 0x5c, sizeof(m_opad));
|
||||
|
||||
/* STEP 1 */
|
||||
if (key_len > SHA1_DIGEST_LENGTH)
|
||||
{
|
||||
ICESHA1::Reset();
|
||||
ICESHA1::Update((UINT_8 *)key, key_len);
|
||||
ICESHA1::Final();
|
||||
|
||||
ICESHA1::GetHash((UINT_8 *)SHA1_Key);
|
||||
}
|
||||
else
|
||||
memcpy(SHA1_Key, key, key_len);
|
||||
|
||||
/* STEP 2 */
|
||||
for (int i=0; i<sizeof(m_ipad); i++)
|
||||
{
|
||||
m_ipad[i] ^= SHA1_Key[i];
|
||||
}
|
||||
|
||||
/* STEP 3 */
|
||||
memcpy(AppendBuf1, m_ipad, sizeof(m_ipad));
|
||||
memcpy(AppendBuf1 + sizeof(m_ipad), text, text_len);
|
||||
|
||||
/* STEP 4 */
|
||||
ICESHA1::Reset();
|
||||
ICESHA1::Update((unsigned char *)AppendBuf1, sizeof(m_ipad) + text_len);
|
||||
ICESHA1::Final();
|
||||
|
||||
ICESHA1::GetHash((unsigned char *)szReport);
|
||||
|
||||
/* STEP 5 */
|
||||
for (int j=0; j<sizeof(m_opad); j++)
|
||||
{
|
||||
m_opad[j] ^= SHA1_Key[j];
|
||||
}
|
||||
|
||||
/* STEP 6 */
|
||||
memcpy(AppendBuf2, m_opad, sizeof(m_opad));
|
||||
memcpy(AppendBuf2 + sizeof(m_opad), szReport, SHA1_DIGEST_LENGTH);
|
||||
|
||||
/*STEP 7 */
|
||||
ICESHA1::Reset();
|
||||
ICESHA1::Update((unsigned char*)AppendBuf2, sizeof(m_opad) + SHA1_DIGEST_LENGTH);
|
||||
ICESHA1::Final();
|
||||
|
||||
ICESHA1::GetHash((unsigned char *)digest);
|
||||
}
|
||||
50
src/libs/ice/ICEHMAC.h
Normal file
50
src/libs/ice/ICEHMAC.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Based on 100% free public domain implementation of the HMAC-SHA1 algorithm
|
||||
by Chien-Chung, Chung (Jim Chung) <jimchung1221@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __ICE_HMAC_H
|
||||
#define __ICE_HMAC_H
|
||||
|
||||
#include "ICESHA1.h"
|
||||
|
||||
class ICEHMAC: public ICESHA1
|
||||
{
|
||||
private:
|
||||
unsigned char m_ipad[64];
|
||||
unsigned char m_opad[64];
|
||||
|
||||
unsigned char * szReport ;
|
||||
unsigned char * SHA1_Key ;
|
||||
unsigned char * AppendBuf1 ;
|
||||
unsigned char * AppendBuf2 ;
|
||||
|
||||
|
||||
public:
|
||||
enum {
|
||||
SHA1_DIGEST_LENGTH = 20,
|
||||
SHA1_BLOCK_SIZE = 64,
|
||||
HMAC_BUF_LEN = 4096
|
||||
} ;
|
||||
|
||||
ICEHMAC()
|
||||
:szReport(new unsigned char[HMAC_BUF_LEN]),
|
||||
AppendBuf1(new unsigned char[HMAC_BUF_LEN]),
|
||||
AppendBuf2(new unsigned char[HMAC_BUF_LEN]),
|
||||
SHA1_Key(new unsigned char[HMAC_BUF_LEN])
|
||||
{
|
||||
}
|
||||
|
||||
~ICEHMAC()
|
||||
{
|
||||
delete[] szReport ;
|
||||
delete[] AppendBuf1 ;
|
||||
delete[] AppendBuf2 ;
|
||||
delete[] SHA1_Key ;
|
||||
}
|
||||
|
||||
void GetDigest(unsigned char *text, int text_len, unsigned char* key, int key_len, unsigned char *digest);
|
||||
};
|
||||
|
||||
|
||||
#endif /* __HMAC_SHA1_H__ */
|
||||
18
src/libs/ice/ICEIosSupport.h
Normal file
18
src/libs/ice/ICEIosSupport.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_IOS_SUPPORT_H
|
||||
#define __ICE_IOS_SUPPORT_H
|
||||
|
||||
#include <vector>
|
||||
#include "ICEAddress.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
int getIosIp(int networkType, int family, char* adress);
|
||||
int fillIosInterfaceList(int family, int networkType, std::vector<ice::NetworkAddress>& output);
|
||||
}
|
||||
|
||||
#endif
|
||||
224
src/libs/ice/ICEIosSupport.mm
Normal file
224
src/libs/ice/ICEIosSupport.mm
Normal file
@@ -0,0 +1,224 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEIosSupport.h"
|
||||
#include "ICENetworkHelper.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include "TargetConditionals.h"
|
||||
#include "ICEAddress.h"
|
||||
#include "ICELog.h"
|
||||
#include "ICENetworkHelper.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
typedef enum {
|
||||
ConnectionTypeUnknown,
|
||||
ConnectionTypeNone,
|
||||
ConnectionType3G,
|
||||
ConnectionTypeWiFi
|
||||
} ConnectionType;
|
||||
|
||||
|
||||
ConnectionType FindConnectionType(int family)
|
||||
{
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, family == AF_INET ? "8.8.8.8" : "2001:4860:4860::8888");
|
||||
SCNetworkReachabilityFlags flags;
|
||||
BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);
|
||||
CFRelease(reachability);
|
||||
if (!success) {
|
||||
return ConnectionTypeUnknown;
|
||||
}
|
||||
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
|
||||
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
|
||||
BOOL isNetworkReachable = (isReachable && !needsConnection);
|
||||
|
||||
if (!isNetworkReachable) {
|
||||
return ConnectionTypeNone;
|
||||
} else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
|
||||
return ConnectionType3G;
|
||||
} else {
|
||||
return ConnectionTypeWiFi;
|
||||
}
|
||||
}
|
||||
|
||||
int fillIosInterfaceList(int family, int networkType, std::vector<ice::NetworkAddress>& output)
|
||||
{
|
||||
if (networkType == NetworkHelper::NetworkType_None)
|
||||
{
|
||||
switch (FindConnectionType(family))
|
||||
{
|
||||
case ConnectionTypeNone:
|
||||
return 0;
|
||||
|
||||
case ConnectionType3G:
|
||||
networkType = NetworkHelper::NetworkType_3G;
|
||||
break;
|
||||
|
||||
case ConnectionTypeWiFi:
|
||||
networkType = NetworkHelper::NetworkType_WiFi;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
struct ifaddrs *interfaces = NULL;
|
||||
struct ifaddrs *temp_addr = NULL;
|
||||
|
||||
// retrieve the current interfaces - returns 0 on success
|
||||
if(!getifaddrs(&interfaces))
|
||||
{
|
||||
// Loop through linked list of interfaces
|
||||
for (temp_addr = interfaces; temp_addr != NULL; temp_addr = temp_addr->ifa_next)
|
||||
{
|
||||
sa_family_t sa_type = temp_addr->ifa_addr->sa_family;
|
||||
|
||||
if (sa_type == family)
|
||||
{
|
||||
NSString* name = [NSString stringWithUTF8String: temp_addr->ifa_name];
|
||||
|
||||
if (networkType != NetworkHelper::NetworkType_None)
|
||||
{
|
||||
bool wifi = [name rangeOfString: @"en"].location == 0;
|
||||
bool cell = [name rangeOfString: @"pdp_ip"].location == 0;
|
||||
/*bool vpn = [name rangeOfString: @"tun"].location == 0 || [name rangeOfString: @"tap"].location == 0;*/
|
||||
|
||||
switch (networkType)
|
||||
{
|
||||
case NetworkHelper::NetworkType_3G:
|
||||
if (wifi) // Skip wifi addresses here. Use cell and vpn addresses here.
|
||||
continue;
|
||||
break;
|
||||
|
||||
case NetworkHelper::NetworkType_WiFi:
|
||||
if (cell) // Skip cell addresses here. Use other addresses - wifi and vpn.
|
||||
continue;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ice::NetworkAddress addr;
|
||||
if (sa_type == AF_INET6)
|
||||
addr = ice::NetworkAddress(((sockaddr_in6*)temp_addr->ifa_addr)->sin6_addr, 1000);
|
||||
else
|
||||
addr = ice::NetworkAddress(((sockaddr_in*)temp_addr->ifa_addr)->sin_addr, 1000);
|
||||
|
||||
//ICELogDebug(<< "Found: " << addr.toStdString());
|
||||
|
||||
if (!addr.isLoopback() && !addr.isLinkLocal())
|
||||
output.push_back(addr);
|
||||
}
|
||||
|
||||
}
|
||||
// Free memory
|
||||
freeifaddrs(interfaces);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getIosIp(int networkType, int family, char* address)
|
||||
{
|
||||
assert(address);
|
||||
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
struct ifaddrs *interfaces = NULL;
|
||||
struct ifaddrs *temp_addr = NULL;
|
||||
NSString *wifiAddress = nil;
|
||||
NSString *cellAddress = nil;
|
||||
|
||||
// retrieve the current interfaces - returns 0 on success
|
||||
if(!getifaddrs(&interfaces))
|
||||
{
|
||||
// Loop through linked list of interfaces
|
||||
temp_addr = interfaces;
|
||||
while(temp_addr != NULL)
|
||||
{
|
||||
sa_family_t sa_type = temp_addr->ifa_addr->sa_family;
|
||||
|
||||
if (sa_type == family)
|
||||
{
|
||||
NSString* name = [NSString stringWithUTF8String: temp_addr->ifa_name];
|
||||
char buffer[128];
|
||||
if (sa_type == AF_INET6)
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6*)temp_addr->ifa_addr)->sin6_addr, buffer, sizeof(buffer));
|
||||
else
|
||||
inet_ntop(AF_INET, &((sockaddr_in*)temp_addr->ifa_addr)->sin_addr, buffer, sizeof(buffer));
|
||||
|
||||
NSString* addr = [NSString stringWithUTF8String: buffer];
|
||||
|
||||
if([name rangeOfString: @"en"].location == 0)
|
||||
{
|
||||
// Interface is the wifi connection on the iPhone
|
||||
wifiAddress = addr;
|
||||
}
|
||||
else
|
||||
if([name isEqualToString: @"pdp_ip0"])
|
||||
{
|
||||
// Interface is the cell connection on the iPhone
|
||||
cellAddress = addr;
|
||||
}
|
||||
else
|
||||
if ([name rangeOfString: @"tun"].location == 0 || [name rangeOfString: @"tap"].location == 0)
|
||||
{
|
||||
wifiAddress = addr;
|
||||
}
|
||||
}
|
||||
temp_addr = temp_addr->ifa_next;
|
||||
}
|
||||
// Free memory
|
||||
freeifaddrs(interfaces);
|
||||
}
|
||||
|
||||
NSString* currentAddr = nil;
|
||||
switch (networkType)
|
||||
{
|
||||
case ice::NetworkHelper::NetworkType_None:
|
||||
currentAddr = wifiAddress ? wifiAddress : cellAddress;
|
||||
break;
|
||||
|
||||
case ice::NetworkHelper::NetworkType_WiFi:
|
||||
currentAddr = wifiAddress;
|
||||
break;
|
||||
|
||||
case ice::NetworkHelper::NetworkType_3G:
|
||||
currentAddr = cellAddress;
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentAddr)
|
||||
strcpy(address, [currentAddr UTF8String]);
|
||||
else
|
||||
if (wifiAddress)
|
||||
strcpy(address, [wifiAddress UTF8String]);
|
||||
else
|
||||
if (cellAddress)
|
||||
strcpy(address, [cellAddress UTF8String]);
|
||||
else
|
||||
strcpy(address, "127.0.0.1");
|
||||
|
||||
[pool release];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
251
src/libs/ice/ICELog.cpp
Normal file
251
src/libs/ice/ICELog.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICELog.h"
|
||||
#include "ICETime.h"
|
||||
#include "ICESync.h"
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#if defined(__ANDROID_API__)
|
||||
# include <android/log.h>
|
||||
#endif
|
||||
|
||||
using namespace ice;
|
||||
|
||||
ice::Logger ice::GLogger;
|
||||
const char* ice::Logger::TabPrefix = " ";
|
||||
|
||||
Logger::Logger()
|
||||
:mStream(nullptr)
|
||||
{
|
||||
mFile = nullptr;
|
||||
mUseDebugWindow = false;
|
||||
mDelegate = nullptr;
|
||||
mLevel = LL_DEBUG;
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
//LogGuard l(mGuard);
|
||||
if (mFile)
|
||||
{
|
||||
fclose(mFile);
|
||||
mFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Logger::useDebugWindow(bool enable)
|
||||
{
|
||||
LogGuard l(mGuard);
|
||||
|
||||
mUseDebugWindow = enable;
|
||||
}
|
||||
|
||||
void
|
||||
Logger::useFile(const char* filepath)
|
||||
{
|
||||
LogGuard l(mGuard);
|
||||
|
||||
mLogPath = filepath ? filepath : "";
|
||||
if (mLogPath.empty())
|
||||
return;
|
||||
|
||||
FILE* f = fopen(filepath, "at");
|
||||
if (f)
|
||||
{
|
||||
if (mFile)
|
||||
fclose(mFile);
|
||||
mFile = f;
|
||||
}
|
||||
|
||||
if (f)
|
||||
{
|
||||
fprintf(f, "New log chunk starts here.\n");
|
||||
fflush(f);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Logger::useNull()
|
||||
{
|
||||
LogGuard l(mGuard);
|
||||
|
||||
mUseDebugWindow = false;
|
||||
if (mFile)
|
||||
{
|
||||
fflush(mFile);
|
||||
fclose(mFile);
|
||||
mFile = NULL;
|
||||
}
|
||||
|
||||
mDelegate = NULL;
|
||||
}
|
||||
|
||||
void Logger::closeFile()
|
||||
{
|
||||
LogGuard l(mGuard);
|
||||
if (mFile)
|
||||
{
|
||||
fflush(mFile);
|
||||
fclose(mFile);
|
||||
mFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::openFile()
|
||||
{
|
||||
LogGuard l(mGuard);
|
||||
if (mLogPath.empty())
|
||||
return;
|
||||
|
||||
remove(mLogPath.c_str());
|
||||
useFile(mLogPath.c_str());
|
||||
}
|
||||
|
||||
|
||||
void Logger::useDelegate(LogHandler* delegate_)
|
||||
{
|
||||
mDelegate = delegate_;
|
||||
}
|
||||
|
||||
LogGuard& Logger::mutex()
|
||||
{
|
||||
return mGuard;
|
||||
}
|
||||
|
||||
LogLevel Logger::level()
|
||||
{
|
||||
return mLevel;
|
||||
}
|
||||
|
||||
void Logger::setLevel(LogLevel level)
|
||||
{
|
||||
mLevel = level;
|
||||
}
|
||||
|
||||
void Logger::beginLine(LogLevel level, const char* filename, int linenumber, const char* subsystem)
|
||||
{
|
||||
mMsgLevel = level;
|
||||
mStream = new std::ostringstream();
|
||||
|
||||
#ifdef WIN32
|
||||
const char* filenamestart = strrchr(filename, '\\');
|
||||
#else
|
||||
const char* filenamestart = strrchr(filename, '/');
|
||||
#endif
|
||||
if (!filenamestart)
|
||||
filenamestart = filename;
|
||||
else
|
||||
filenamestart++;
|
||||
|
||||
mFilename = filenamestart;
|
||||
mLine = linenumber;
|
||||
mSubsystem = subsystem;
|
||||
|
||||
//*mStream << std::setw(8) << ICETimeHelper::timestamp() << " | " << std::setw(8) << ThreadInfo::currentThread() << " | " << std::setw(30) << filenamestart << " | " << std::setw(4) << linenumber << " | " << std::setw(12) << subsystem << " | ";
|
||||
}
|
||||
|
||||
void
|
||||
Logger::endLine()
|
||||
{
|
||||
*mStream << std::endl;
|
||||
*mStream << std::flush;
|
||||
mStream->flush();
|
||||
|
||||
std::ostringstream result;
|
||||
result << std::setw(8) << ICETimeHelper::timestamp() << " | " << std::setw(8) << ThreadInfo::currentThread() << " | " << std::setw(30) << mFilename.c_str() << " | " << std::setw(4) << mLine << " | " << std::setw(12) << mSubsystem.c_str() << " | " << mStream->str().c_str();
|
||||
|
||||
std::string t = result.str();
|
||||
if (mUseDebugWindow)
|
||||
#ifdef TARGET_WIN
|
||||
OutputDebugStringA(t.c_str());
|
||||
#elif defined(TARGET_ANDROID)
|
||||
if (t.size() > 512)
|
||||
{
|
||||
std::string cut = t; cut.erase(480); // Erase tail of string
|
||||
cut += "\r\n... [cut]";
|
||||
__android_log_print(ANDROID_LOG_INFO, "VoipAgent", "%s", cut.c_str());
|
||||
}
|
||||
else {
|
||||
__android_log_print(ANDROID_LOG_INFO, "VoipAgent", "%s", t.c_str());
|
||||
}
|
||||
#else
|
||||
std::cerr << result.str() << std::endl << std::flush;
|
||||
#endif
|
||||
if (mFile)
|
||||
{
|
||||
fprintf(mFile, "%s", result.str().c_str());
|
||||
fflush(mFile);
|
||||
}
|
||||
if (mDelegate)
|
||||
mDelegate->onIceLog(mMsgLevel, mFilename, mLine, mSubsystem, mStream->str());
|
||||
|
||||
delete mStream; mStream = NULL;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator << (const char* data)
|
||||
{
|
||||
*mStream << data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator << (const wchar_t* data)
|
||||
{
|
||||
*mStream << data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator << (const int data)
|
||||
{
|
||||
*mStream << data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator << (const float data)
|
||||
{
|
||||
*mStream << data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator<<(const int64_t data)
|
||||
{
|
||||
*mStream << data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger&
|
||||
Logger::operator<<(const unsigned int data)
|
||||
{
|
||||
*mStream << data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Logger&
|
||||
Logger::operator<<(const uint64_t data)
|
||||
{
|
||||
*mStream << data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Logger&
|
||||
Logger::operator << (const std::string& data)
|
||||
{
|
||||
*mStream << data.c_str();
|
||||
return *this;
|
||||
}
|
||||
163
src/libs/ice/ICELog.h
Normal file
163
src/libs/ice/ICELog.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_LOG_H
|
||||
#define __ICE_LOG_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICETypes.h"
|
||||
#include "ICEEvent.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
// Defines log levels
|
||||
enum LogLevel
|
||||
{
|
||||
LL_NONE = -1,
|
||||
LL_CRITICAL = 0,
|
||||
LL_INFO = 1,
|
||||
LL_DEBUG = 2,
|
||||
LL_MEDIA = 3
|
||||
};
|
||||
|
||||
class LogHandler
|
||||
{
|
||||
public:
|
||||
virtual void onIceLog(LogLevel level, const std::string& filename, int line, const std::string& subsystem, const std::string& msg) = 0;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
class LogGuard
|
||||
{
|
||||
public:
|
||||
LogGuard() { ::InitializeCriticalSection(&mCS); }
|
||||
~LogGuard() { ::DeleteCriticalSection(&mCS); }
|
||||
void Lock() { ::EnterCriticalSection(&mCS); }
|
||||
void Unlock() { ::LeaveCriticalSection(&mCS); }
|
||||
|
||||
protected:
|
||||
CRITICAL_SECTION mCS;
|
||||
};
|
||||
|
||||
class LogLock
|
||||
{
|
||||
public:
|
||||
LogLock(LogGuard& g) :mGuard(g) { mGuard.Lock(); }
|
||||
~LogLock() { mGuard.Unlock(); }
|
||||
|
||||
protected:
|
||||
LogGuard& mGuard;
|
||||
};
|
||||
#else
|
||||
class LogGuard
|
||||
{
|
||||
public:
|
||||
LogGuard() { ::pthread_mutex_init(&mMutex, NULL); }
|
||||
~LogGuard() { ::pthread_mutex_destroy(&mMutex); }
|
||||
void Lock() { ::pthread_mutex_lock(&mMutex); }
|
||||
void Unlock() { ::pthread_mutex_unlock(&mMutex); }
|
||||
|
||||
protected:
|
||||
pthread_mutex_t mMutex;
|
||||
};
|
||||
|
||||
class LogLock
|
||||
{
|
||||
public:
|
||||
LogLock(LogGuard& g) :mGuard(g) { mGuard.Lock(); }
|
||||
~LogLock() { mGuard.Unlock(); }
|
||||
|
||||
protected:
|
||||
LogGuard& mGuard;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
static const char* TabPrefix;
|
||||
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
void useDebugWindow(bool enable);
|
||||
void useFile(const char* filepath);
|
||||
void useDelegate(LogHandler* delegate_);
|
||||
void useNull();
|
||||
void closeFile();
|
||||
void openFile();
|
||||
|
||||
LogGuard& mutex();
|
||||
LogLevel level();
|
||||
void setLevel(LogLevel level);
|
||||
void beginLine(LogLevel level, const char* filename, int linenumber, const char* subsystem);
|
||||
void endLine();
|
||||
|
||||
Logger& operator << (const char* data);
|
||||
Logger& operator << (const wchar_t* data);
|
||||
Logger& operator << (const int data);
|
||||
Logger& operator << (const float data);
|
||||
Logger& operator << (const std::string& data);
|
||||
Logger& operator << (const int64_t data);
|
||||
Logger& operator << (const unsigned int data);
|
||||
Logger& operator << (const uint64_t data);
|
||||
|
||||
protected:
|
||||
LogGuard mGuard;
|
||||
FILE* mFile;
|
||||
std::string mLogPath;
|
||||
|
||||
bool mUseDebugWindow;
|
||||
LogHandler* mDelegate;
|
||||
LogLevel mLevel;
|
||||
std::ostringstream* mStream;
|
||||
|
||||
LogLevel mMsgLevel;
|
||||
std::string mFilename;
|
||||
int mLine;
|
||||
std::string mSubsystem;
|
||||
};
|
||||
|
||||
|
||||
extern Logger GLogger;
|
||||
|
||||
|
||||
#define ICELog(level_, subsystem_, args_)\
|
||||
{do\
|
||||
{\
|
||||
if (GLogger.level() >= level_)\
|
||||
{\
|
||||
LogLock l(GLogger.mutex());\
|
||||
GLogger.beginLine(level_, __FILE__, __LINE__, subsystem_);\
|
||||
GLogger args_;\
|
||||
GLogger.endLine();\
|
||||
}\
|
||||
} while (false);}
|
||||
|
||||
#define ICELogCritical(args_) ICELog(LL_CRITICAL, LOG_SUBSYSTEM, args_)
|
||||
#define ICELogInfo(args_) ICELog(LL_INFO, LOG_SUBSYSTEM, args_)
|
||||
#define ICELogDebug(args_) ICELog(LL_DEBUG, LOG_SUBSYSTEM, args_)
|
||||
#define ICELogMedia(args_) ICELog(LL_MEDIA, LOG_SUBSYSTEM, args_)
|
||||
|
||||
|
||||
/*
|
||||
#define ICELogCritical(args_)
|
||||
#define ICELogInfo(args_)
|
||||
#define ICELogDebug(args_)
|
||||
#define ICELogMedia(args_)
|
||||
*/
|
||||
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
305
src/libs/ice/ICEMD5.cpp
Normal file
305
src/libs/ice/ICEMD5.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEMD5.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
|
||||
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
|
||||
#include "../CryptoPP/md5.h"
|
||||
using namespace CryptoPP;
|
||||
|
||||
void ice::md5Bin(const void* inputData, size_t inputSize, void* digest)
|
||||
{
|
||||
Weak::MD5 md5;
|
||||
md5.Update((const byte*)inputData, inputSize);
|
||||
|
||||
md5.Final((byte*)digest);
|
||||
}
|
||||
|
||||
#elif defined(USE_OPENSSL)
|
||||
|
||||
#ifdef USE_FIPS
|
||||
typedef unsigned int MD5_u32plus;
|
||||
|
||||
typedef struct {
|
||||
MD5_u32plus lo, hi;
|
||||
MD5_u32plus a, b, c, d;
|
||||
unsigned char buffer[64];
|
||||
MD5_u32plus block[16];
|
||||
} MD5_CTX;
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F and G are optimized compared to their RFC 1321 definitions for
|
||||
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
|
||||
* implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them
|
||||
* in a properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned
|
||||
* memory accesses is just an optimization. Nothing will break if it
|
||||
* doesn't work.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
|
||||
#define SET(n) \
|
||||
(*(MD5_u32plus *)&ptr[(n) * 4])
|
||||
#define GET(n) \
|
||||
SET(n)
|
||||
#else
|
||||
#define SET(n) \
|
||||
(ctx->block[(n)] = \
|
||||
(MD5_u32plus)ptr[(n) * 4] | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
|
||||
#define GET(n) \
|
||||
(ctx->block[(n)])
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update
|
||||
* the bit counters. There are no alignment requirements.
|
||||
*/
|
||||
static void *body(MD5_CTX *ctx, void *data, unsigned long size)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
MD5_u32plus a, b, c, d;
|
||||
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = (unsigned char*)data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void MD5_Init(MD5_CTX *ctx)
|
||||
{
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
|
||||
{
|
||||
MD5_u32plus saved_lo;
|
||||
unsigned long used, free;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
|
||||
ctx->hi++;
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
free = 64 - used;
|
||||
|
||||
if (size < free) {
|
||||
memcpy(&ctx->buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[used], data, free);
|
||||
data = (unsigned char *)data + free;
|
||||
size -= free;
|
||||
body(ctx, ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(ctx, data, size & ~(unsigned long)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx->buffer, data, size);
|
||||
}
|
||||
|
||||
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
|
||||
{
|
||||
unsigned long used, free;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
free = 64 - used;
|
||||
|
||||
if (free < 8) {
|
||||
memset(&ctx->buffer[used], 0, free);
|
||||
body(ctx, ctx->buffer, 64);
|
||||
used = 0;
|
||||
free = 64;
|
||||
}
|
||||
|
||||
memset(&ctx->buffer[used], 0, free - 8);
|
||||
|
||||
ctx->lo <<= 3;
|
||||
ctx->buffer[56] = ctx->lo;
|
||||
ctx->buffer[57] = ctx->lo >> 8;
|
||||
ctx->buffer[58] = ctx->lo >> 16;
|
||||
ctx->buffer[59] = ctx->lo >> 24;
|
||||
ctx->buffer[60] = ctx->hi;
|
||||
ctx->buffer[61] = ctx->hi >> 8;
|
||||
ctx->buffer[62] = ctx->hi >> 16;
|
||||
ctx->buffer[63] = ctx->hi >> 24;
|
||||
|
||||
body(ctx, ctx->buffer, 64);
|
||||
|
||||
result[0] = ctx->a;
|
||||
result[1] = ctx->a >> 8;
|
||||
result[2] = ctx->a >> 16;
|
||||
result[3] = ctx->a >> 24;
|
||||
result[4] = ctx->b;
|
||||
result[5] = ctx->b >> 8;
|
||||
result[6] = ctx->b >> 16;
|
||||
result[7] = ctx->b >> 24;
|
||||
result[8] = ctx->c;
|
||||
result[9] = ctx->c >> 8;
|
||||
result[10] = ctx->c >> 16;
|
||||
result[11] = ctx->c >> 24;
|
||||
result[12] = ctx->d;
|
||||
result[13] = ctx->d >> 8;
|
||||
result[14] = ctx->d >> 16;
|
||||
result[15] = ctx->d >> 24;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
#else
|
||||
#include <openssl/md5.h>
|
||||
#endif
|
||||
|
||||
void ice::md5Bin(const void* inputData, size_t inputSize, void* digest)
|
||||
{
|
||||
MD5_CTX md5;
|
||||
MD5_Init(&md5);
|
||||
#ifdef USE_FIPS
|
||||
MD5_Update(&md5, (void*)inputData, inputSize);
|
||||
#else
|
||||
MD5_Update(&md5, (const unsigned char*)inputData, inputSize);
|
||||
#endif
|
||||
MD5_Final((unsigned char*)digest, &md5);
|
||||
}
|
||||
|
||||
#endif
|
||||
15
src/libs/ice/ICEMD5.h
Normal file
15
src/libs/ice/ICEMD5.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_MD5_H
|
||||
#define __ICE_MD5_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
extern void md5Bin(const void* inputData, size_t inputSize, void* digest );
|
||||
}
|
||||
#endif
|
||||
484
src/libs/ice/ICENetworkHelper.cpp
Normal file
484
src/libs/ice/ICENetworkHelper.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICENetworkHelper.h"
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICELog.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <tchar.h>
|
||||
# if defined(WINDOWS_RT)
|
||||
# include "ICEWinRtSupport.h"
|
||||
# endif
|
||||
#else
|
||||
#if defined(TARGET_IOS)
|
||||
# include "ICEIosSupport.h"
|
||||
#endif
|
||||
# include <unistd.h>
|
||||
# if defined(TARGET_ANDROID) || defined(TARGET_LINUX)
|
||||
# include <linux/in6.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "ICEError.h"
|
||||
using namespace ice;
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
class IPHlpApi
|
||||
{
|
||||
public:
|
||||
HMODULE mHandle;
|
||||
DWORD (WINAPI *pGetBestInterfaceEx) (struct sockaddr* pDestAddr, PDWORD pdwBestIfIndex);
|
||||
DWORD (WINAPI *pGetBestInterface) (IPAddr dwDestAddr, PDWORD pdwBestIfIndex);
|
||||
ULONG (WINAPI *pGetAdaptersAddresses) (ULONG Family, ULONG Flags, PVOID Reserved, PIP_ADAPTER_ADDRESSES AdapterAddresses, PULONG SizePointer);
|
||||
|
||||
void ResolveProc(const char* name, FARPROC& proc)
|
||||
{
|
||||
proc = GetProcAddress(mHandle, name);
|
||||
}
|
||||
|
||||
public:
|
||||
IPHlpApi()
|
||||
{
|
||||
pGetBestInterfaceEx = NULL;
|
||||
pGetBestInterface = NULL;
|
||||
pGetAdaptersAddresses = NULL;
|
||||
mHandle = ::LoadLibraryW(L"iphlpapi.dll");
|
||||
if (mHandle)
|
||||
{
|
||||
ResolveProc("GetBestInterfaceEx", (FARPROC&)pGetBestInterfaceEx);
|
||||
ResolveProc("GetBestInterface", (FARPROC&)pGetBestInterface);
|
||||
ResolveProc("GetAdaptersAddresses", (FARPROC&)pGetAdaptersAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
~IPHlpApi()
|
||||
{
|
||||
if (mHandle)
|
||||
FreeLibrary(mHandle);
|
||||
}
|
||||
|
||||
};
|
||||
IPHlpApi IPHelper;
|
||||
#endif
|
||||
|
||||
NetworkHelper::NetworkHelper()
|
||||
{
|
||||
reload(NetworkType_None);
|
||||
}
|
||||
|
||||
NetworkHelper::~NetworkHelper()
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkHelper::reload(int networkType)
|
||||
{
|
||||
Lock l(mInstanceGuard);
|
||||
|
||||
// Get list of interfaces
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
mInterfaceList.clear();
|
||||
mIP2IndexList.clear();
|
||||
|
||||
DWORD dwRet, dwSize;
|
||||
DWORD flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
||||
IP_ADAPTER_ADDRESSES* ipAdapters = NULL;
|
||||
dwRet = IPHelper.pGetAdaptersAddresses(AF_INET, flags, NULL, NULL, &dwSize);
|
||||
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
ipAdapters = (IP_ADAPTER_ADDRESSES*)malloc(dwSize);
|
||||
if (!ipAdapters)
|
||||
return;
|
||||
|
||||
dwRet = IPHelper.pGetAdaptersAddresses(AF_INET, flags, NULL, ipAdapters, &dwSize);
|
||||
if (dwRet != ERROR_SUCCESS)
|
||||
{
|
||||
free(ipAdapters);
|
||||
throw Exception(CANNOT_FIND_INTERFACES);
|
||||
}
|
||||
processAdaptersList(ipAdapters);
|
||||
free(ipAdapters);
|
||||
}
|
||||
|
||||
#ifdef ICE_IPV6SUPPORT
|
||||
dwRet = IPHelper.pGetAdaptersAddresses(AF_INET6, flags, NULL, NULL, &dwSize);
|
||||
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
ipAdapters = (IP_ADAPTER_ADDRESSES*)malloc(dwSize);
|
||||
if (!ipAdapters)
|
||||
return;
|
||||
|
||||
dwRet = IPHelper.pGetAdaptersAddresses(AF_INET6, flags, NULL, ipAdapters, &dwSize);
|
||||
if (dwRet != ERROR_SUCCESS)
|
||||
{
|
||||
free(ipAdapters);
|
||||
throw NetworkAddress(CANNOT_FIND_INTERFACES);
|
||||
}
|
||||
processAdaptersList(ipAdapters);
|
||||
free(ipAdapters);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned i=0; i<mIP2IndexList.size(); i++)
|
||||
{
|
||||
bool handleIt = true;
|
||||
NetworkAddress ip = mIP2IndexList[i].mIP;
|
||||
#ifndef ICE_LOOPBACK_SUPPORT
|
||||
handleIt = !ip.isLoopback();
|
||||
#endif
|
||||
#ifdef ICE_SKIP_LINKLOCAL
|
||||
handleIt &= !ip.isLinkLocal();
|
||||
#endif
|
||||
if (handleIt)
|
||||
mInterfaceList.push_back(ip);
|
||||
}
|
||||
#else
|
||||
mIPList.clear();
|
||||
#if defined(TARGET_OS_IPHONE)
|
||||
ICELogDebug(<< "Obtaining IPv4 interfaces.");
|
||||
fillIosInterfaceList(AF_INET, networkType, mIPList);
|
||||
ICELogDebug(<< "Obtaining IPv6 interfaces.");
|
||||
fillIosInterfaceList(AF_INET6, networkType, mIPList);
|
||||
for (auto& addr: mIPList)
|
||||
ICELogDebug(<< " " << addr.toStdString());
|
||||
#elif defined(WINDOWS_RT)
|
||||
fillUwpInterfaceList(AF_INET, networkType, mIPList);
|
||||
fillUwpInterfaceList(AF_INET6, networkType, mIPList);
|
||||
#else
|
||||
struct ifaddrs* il = NULL;
|
||||
if (getifaddrs(&il))
|
||||
throw Exception(GETIFADDRS_FAILED, errno);
|
||||
if (il)
|
||||
{
|
||||
struct ifaddrs* current = il;
|
||||
while (current)
|
||||
{
|
||||
//char ipbuffer[64];
|
||||
NetworkAddress addr;
|
||||
addr.setPort(1000); // Set fake address to keep NetworkAddress initialized
|
||||
bool handleIt = true;
|
||||
switch(current->ifa_addr->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
// just for debug
|
||||
// printf("%s", inet_ntoa(reinterpret_cast<sockaddr_in*>(current->ifa_addr)->sin_addr));
|
||||
addr.setIp(reinterpret_cast<sockaddr_in*>(current->ifa_addr)->sin_addr);
|
||||
#ifndef ICE_LOOPBACK_SUPPORT
|
||||
handleIt = !addr.isLoopback();
|
||||
#endif
|
||||
#ifdef ICE_SKIP_LINKLOCAL
|
||||
handleIt &= !addr.isLinkLocal();
|
||||
#endif
|
||||
if (handleIt)
|
||||
mIPList.push_back(addr);
|
||||
break;
|
||||
|
||||
#ifdef ICE_IPV6SUPPORT
|
||||
case AF_INET6:
|
||||
addr.setIp(reinterpret_cast<sockaddr_in6*>(current->ifa_addr)->sin6_addr);
|
||||
#ifndef ICE_LOOPBACK_SUPPORT
|
||||
if (!addr.isLoopback())
|
||||
#endif
|
||||
mIPList.push_back(addr);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
current = current->ifa_next;
|
||||
}
|
||||
freeifaddrs(il);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
void NetworkHelper::processAdaptersList(IP_ADAPTER_ADDRESSES* addresses)
|
||||
{
|
||||
IP_ADAPTER_ADDRESSES *AI;
|
||||
int i;
|
||||
for (i = 0, AI = addresses; AI != NULL; AI = AI->Next, i++)
|
||||
{
|
||||
for (PIP_ADAPTER_UNICAST_ADDRESS unicast = AI->FirstUnicastAddress; unicast; unicast = unicast->Next)
|
||||
{
|
||||
#ifdef ICE_IPV6SUPPORT
|
||||
if (unicast->Address.lpSockaddr->sa_family != AF_INET && unicast->Address.lpSockaddr->sa_family != AF_INET6)
|
||||
continue;
|
||||
#else
|
||||
if (unicast->Address.lpSockaddr->sa_family != AF_INET)
|
||||
continue;
|
||||
#endif
|
||||
IP2Index rec;
|
||||
rec.mIP = NetworkAddress(*unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength);
|
||||
rec.mIndex = AI->IfIndex;
|
||||
mIP2IndexList.push_back(rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Mutex& NetworkHelper::guard()
|
||||
{
|
||||
return mInstanceGuard;
|
||||
}
|
||||
|
||||
std::vector<NetworkAddress>& NetworkHelper::interfaceList()
|
||||
{
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
return mInterfaceList;
|
||||
#else
|
||||
return mIPList;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
NetworkAddress NetworkHelper::sourceInterface(const NetworkAddress& remoteIP)
|
||||
{
|
||||
//Lock l(mInstanceGuard);
|
||||
|
||||
if (remoteIP.isLoopback())
|
||||
return remoteIP.family() == AF_INET ? NetworkAddress::LoopbackAddress4 : NetworkAddress::LoopbackAddress6;
|
||||
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
if (IPHelper.pGetBestInterfaceEx)
|
||||
{
|
||||
DWORD index = 0;
|
||||
DWORD rescode = IPHelper.pGetBestInterfaceEx(remoteIP.genericsockaddr(), &index);
|
||||
if (rescode == NO_ERROR)
|
||||
return ipInterfaceByIndex(index);
|
||||
}
|
||||
else
|
||||
if (IPHelper.pGetBestInterface)
|
||||
{
|
||||
DWORD index = 0;
|
||||
DWORD rescode = IPHelper.pGetBestInterface(remoteIP.sockaddr4()->sin_addr.S_un.S_addr, &index);
|
||||
if (rescode == NO_ERROR)
|
||||
return ipInterfaceByIndex(index);
|
||||
}
|
||||
#ifdef ICE_LOOPBACK_SUPPORT
|
||||
return NetworkAddress::LoopbackAddress4;
|
||||
#else
|
||||
return remoteIP;
|
||||
#endif
|
||||
|
||||
#elif defined(WINDOWS_RT)
|
||||
std::vector<NetworkAddress>& il = interfaceList();
|
||||
if (il.size())
|
||||
return il.front();
|
||||
else
|
||||
return NetworkAddress();
|
||||
#else
|
||||
if (remoteIP.isLoopback())
|
||||
return remoteIP;
|
||||
|
||||
/*#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
|
||||
// There is only one interface - return it
|
||||
std::vector<NetworkAddress>& il = interfaceList();
|
||||
if (il.size())
|
||||
{
|
||||
if (!il.front().isZero())
|
||||
return il.front();
|
||||
}
|
||||
|
||||
#endif*/
|
||||
|
||||
// Here magic goes.
|
||||
// 1) Check if remoteIP is IP4 or IP6
|
||||
// 2) Check if it is LAN or loopback or internet address
|
||||
// 3) For LAN address - try to find interface from the same network
|
||||
// For Loopback address - return loopback address
|
||||
// For internet address - find default interface
|
||||
|
||||
//printf("remote ip %s\n", remoteIP.GetIP().c_str());
|
||||
if (remoteIP.isLAN())
|
||||
{
|
||||
for (unsigned i=0; i<mIPList.size(); i++)
|
||||
{
|
||||
//printf("local ip %s\n", mIPList[i].GetIP().c_str());
|
||||
if (NetworkAddress::isSameLAN(mIPList[i], remoteIP))
|
||||
return mIPList[i];
|
||||
}
|
||||
}
|
||||
#ifdef ICE_LOOPBACK_SUPPORT
|
||||
NetworkAddress result = remoteIP.type() == AF_INET ? NetworkAddress::LoopbackAddress4 : NetworkAddress::LoopbackAddress6;
|
||||
#else
|
||||
NetworkAddress result = remoteIP;
|
||||
#endif
|
||||
|
||||
// Find default interface - this operation costs, so the result must be cached
|
||||
SOCKET s = INVALID_SOCKET;
|
||||
int rescode = 0;
|
||||
sockaddr_in ipv4;
|
||||
#ifdef ICE_IPV6SUPPORT
|
||||
sockaddr_in6 ipv6;
|
||||
#endif
|
||||
socklen_t addrLen = 0;
|
||||
switch (remoteIP.family())
|
||||
{
|
||||
case AF_INET:
|
||||
s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s != INVALID_SOCKET)
|
||||
{
|
||||
rescode = ::connect(s, remoteIP.genericsockaddr(), remoteIP.sockaddrLen());
|
||||
if (rescode != -1)
|
||||
{
|
||||
addrLen = sizeof(ipv4);
|
||||
if (::getsockname(s, (sockaddr*)&ipv4, &addrLen) != -1)
|
||||
result = NetworkAddress((sockaddr&)ipv4, addrLen);
|
||||
}
|
||||
::close(s);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef ICE_IPV6SUPPORT
|
||||
case AF_INET6:
|
||||
s = ::socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s != INVALID_SOCKET)
|
||||
{
|
||||
rescode = ::connect(s, remoteIP.genericsockaddr(), remoteIP.sockaddrLen());
|
||||
if (rescode != -1)
|
||||
{
|
||||
addrLen = sizeof(ipv6);
|
||||
if (::getsockname(s, (sockaddr*)&ipv6, &addrLen) != -1)
|
||||
result = NetworkAddress((sockaddr&)ipv6, addrLen);
|
||||
}
|
||||
::close(s);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (result.isEmpty())
|
||||
{
|
||||
// Get first available interface
|
||||
for (unsigned i=0; i<mIPList.size() && result.isEmpty(); i++)
|
||||
{
|
||||
if (!mIPList[i].isLoopback() && mIPList[i].family() == remoteIP.family())
|
||||
result = mIPList[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
NetworkAddress NetworkHelper::ipInterfaceByIndex(int index)
|
||||
{
|
||||
for (unsigned i=0; i<mIP2IndexList.size(); i++)
|
||||
{
|
||||
if (mIP2IndexList[i].mIndex == index)
|
||||
return mIP2IndexList[i].mIP;
|
||||
}
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
NetworkHelper& NetworkHelper::instance()
|
||||
{
|
||||
mGuard.lock();
|
||||
try
|
||||
{
|
||||
if (!mInstance)
|
||||
mInstance = new NetworkHelper();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ICELogCritical(<< "Failed to create NetworkHelper instance");
|
||||
}
|
||||
mGuard.unlock();
|
||||
|
||||
return *mInstance;
|
||||
}
|
||||
|
||||
void NetworkHelper::destroyInstance()
|
||||
{
|
||||
delete mInstance;
|
||||
}
|
||||
|
||||
bool NetworkHelper::hasIPv4() const
|
||||
{
|
||||
// Check interface list and see if it has LAN or public IP
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
for (const auto& item: mInterfaceList)
|
||||
#else
|
||||
for (const auto& item: mIPList)
|
||||
#endif
|
||||
if (item.family() == AF_INET && (item.isLAN() || item.isPublic()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkHelper::hasIPv6() const
|
||||
{
|
||||
#if defined(TARGET_WIN) && !defined(WINDOWS_RT)
|
||||
for (const auto& item : mInterfaceList)
|
||||
#else
|
||||
for (const auto& item: mIPList)
|
||||
#endif
|
||||
if (item.family() == AF_INET6 && (item.isLAN() || item.isPublic()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkHelper::NetworkToHost(const in6_addr& addr6, uint32_t* output)
|
||||
{
|
||||
for (int i=0; i<4; i++)
|
||||
#if defined(TARGET_WIN)
|
||||
output[i] = ntohl(((uint32_t*)addr6.u.Byte[0])[i]);
|
||||
#elif defined(TARGET_IOS) || defined(TARGET_OSX)
|
||||
output[i] = ntohl(addr6.__u6_addr.__u6_addr32[i]);
|
||||
#elif defined(TARGET_LINUX)
|
||||
output[i] = ntohl(addr6.__in6_u.__u6_addr32[i]);
|
||||
#elif defined(TARGET_ANDROID)
|
||||
output[i] = ntohl(addr6.in6_u.u6_addr32[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkHelper::HostToNetwork(const uint32_t* input, in6_addr& output)
|
||||
{
|
||||
for (int i=0; i<4; i++)
|
||||
#if defined(TARGET_WIN)
|
||||
((uint32_t*)&output.u.Byte[0])[i] = htonl(input[i]);
|
||||
#elif defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
output.__u6_addr.__u6_addr32[i] = htonl(input[i]);
|
||||
#elif defined(TARGET_LINUX)
|
||||
output.__in6_u.__u6_addr32[i] = htonl(input[i]);
|
||||
#elif defined(TARGET_ANDROID)
|
||||
output.in6_u.u6_addr32[i] = htonl(input[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
NetworkHelper* NetworkHelper::mInstance = NULL;
|
||||
Mutex NetworkHelper::mGuard;
|
||||
|
||||
//-------------------------------------- INHDestroy ---------------------------------
|
||||
class INHDestroy
|
||||
{
|
||||
public:
|
||||
INHDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
~INHDestroy()
|
||||
{
|
||||
NetworkHelper::destroyInstance();
|
||||
}
|
||||
};
|
||||
|
||||
INHDestroy GINHDestroyer;
|
||||
79
src/libs/ice/ICENetworkHelper.h
Normal file
79
src/libs/ice/ICENetworkHelper.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_NETWORK_HELPER_H
|
||||
#define __ICE_NETWORK_HELPER_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICESync.h"
|
||||
#include "ICEAddress.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <Iphlpapi.h>
|
||||
#else
|
||||
# include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
class NetworkHelper
|
||||
{
|
||||
public:
|
||||
NetworkHelper();
|
||||
~NetworkHelper();
|
||||
|
||||
enum
|
||||
{
|
||||
NetworkType_None,
|
||||
NetworkType_WiFi,
|
||||
NetworkType_3G
|
||||
};
|
||||
|
||||
Mutex& guard();
|
||||
|
||||
/* Returns list of IP4 interfaces installed in system. */
|
||||
std::vector<NetworkAddress>& interfaceList();
|
||||
|
||||
bool hasIPv4() const;
|
||||
bool hasIPv6() const;
|
||||
|
||||
/* Finds source interface for specified remote address. */
|
||||
NetworkAddress sourceInterface(const NetworkAddress& remoteIP);
|
||||
|
||||
void reload(int networkType);
|
||||
|
||||
static NetworkHelper& instance();
|
||||
static void destroyInstance();
|
||||
|
||||
static void NetworkToHost(const in6_addr& addr6, uint32_t* output);
|
||||
static void HostToNetwork(const uint32_t* input, in6_addr& output);
|
||||
|
||||
protected:
|
||||
#ifdef _WIN32
|
||||
struct IP2Index
|
||||
{
|
||||
NetworkAddress mIP;
|
||||
DWORD mIndex;
|
||||
};
|
||||
std::vector<IP2Index> mIP2IndexList;
|
||||
std::vector<NetworkAddress> mInterfaceList;
|
||||
|
||||
void processAdaptersList(IP_ADAPTER_ADDRESSES* addresses);
|
||||
NetworkAddress ipInterfaceByIndex(int index);
|
||||
#else
|
||||
std::vector<NetworkAddress> mIPList;
|
||||
#endif
|
||||
static NetworkHelper* mInstance;
|
||||
static Mutex mGuard;
|
||||
Mutex mInstanceGuard;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
94
src/libs/ice/ICEPacketTimer.cpp
Normal file
94
src/libs/ice/ICEPacketTimer.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEPacketTimer.h"
|
||||
#include "ICETime.h"
|
||||
|
||||
using namespace ice;
|
||||
PacketScheduler::PacketScheduler()
|
||||
{
|
||||
mTimestamp = 0;
|
||||
mAttemptCounter = 0;
|
||||
mInitialRTO = 100;
|
||||
mLastRTO = 100;
|
||||
}
|
||||
|
||||
PacketScheduler::~PacketScheduler()
|
||||
{
|
||||
}
|
||||
|
||||
void PacketScheduler::setInitialRTO(int value)
|
||||
{
|
||||
mInitialRTO = value;
|
||||
}
|
||||
|
||||
int PacketScheduler::initialRTO()
|
||||
{
|
||||
return mInitialRTO;
|
||||
}
|
||||
|
||||
void PacketScheduler::start()
|
||||
{
|
||||
mLastRTO = mInitialRTO;
|
||||
mAttemptCounter = 0;
|
||||
mTimestamp = 0;//ICETimeHelper::timestamp();
|
||||
}
|
||||
|
||||
void PacketScheduler::stop()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool PacketScheduler::isTimeToRetransmit()
|
||||
{
|
||||
if (!mTimestamp)
|
||||
{
|
||||
mTimestamp = ICETimeHelper::timestamp();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned currentTime = ICETimeHelper::timestamp();
|
||||
unsigned delta = ICETimeHelper::findDelta(mTimestamp, currentTime);
|
||||
|
||||
return delta >= mLastRTO;
|
||||
}
|
||||
|
||||
/// Instructs timer that attempt made
|
||||
void PacketScheduler::attemptMade()
|
||||
{
|
||||
// Increase attempt counter
|
||||
mAttemptCounter++;
|
||||
|
||||
// Save new timestamp
|
||||
mTimestamp = ICETimeHelper::timestamp();
|
||||
|
||||
// Increase mLastRTO
|
||||
#ifndef ICE_SIMPLE_SCHEDULE
|
||||
mLastRTO *= 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Checks if attempt limit reached
|
||||
bool PacketScheduler::isAttemptLimitReached()
|
||||
{
|
||||
return mAttemptCounter >= ICE_TRANSACTION_RTO_LIMIT;
|
||||
}
|
||||
|
||||
/// Checks if timeout happens
|
||||
bool PacketScheduler::isTimeout()
|
||||
{
|
||||
// Are attempts to send finished?
|
||||
if (!isAttemptLimitReached())
|
||||
return false;
|
||||
|
||||
// Get current time
|
||||
unsigned int currentTime = ICETimeHelper::timestamp();
|
||||
|
||||
// Get difference between current time and last send attempt
|
||||
unsigned int delta = ICETimeHelper::findDelta(mTimestamp, currentTime);
|
||||
|
||||
return delta > mLastRTO * 16;
|
||||
}
|
||||
43
src/libs/ice/ICEPacketTimer.h
Normal file
43
src/libs/ice/ICEPacketTimer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_PACKET_TIMER_H
|
||||
#define __ICE_PACKET_TIMER_H
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
class PacketScheduler
|
||||
{
|
||||
public:
|
||||
PacketScheduler();
|
||||
~PacketScheduler();
|
||||
|
||||
void setInitialRTO(int value);
|
||||
int initialRTO();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
bool isTimeToRetransmit();
|
||||
|
||||
/// Instructs timer that attempt made
|
||||
void attemptMade();
|
||||
|
||||
/// Checks if attempt limit reached
|
||||
bool isAttemptLimitReached();
|
||||
|
||||
/// Checks if timeout happens
|
||||
bool isTimeout();
|
||||
|
||||
protected:
|
||||
unsigned int mTimestamp;
|
||||
unsigned int mAttemptCounter;
|
||||
unsigned int mInitialRTO;
|
||||
unsigned int mLastRTO;
|
||||
};
|
||||
|
||||
} //end of namespace
|
||||
|
||||
#endif
|
||||
22
src/libs/ice/ICEPlatform.cpp
Normal file
22
src/libs/ice/ICEPlatform.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <ctype.h>
|
||||
char* strupr(char* buffer)
|
||||
{
|
||||
char* result = buffer;
|
||||
|
||||
while (*buffer)
|
||||
{
|
||||
*buffer = toupper(*buffer);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
119
src/libs/ice/ICEPlatform.h
Normal file
119
src/libs/ice/ICEPlatform.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_PLATFORM_H
|
||||
#define __ICE_PLATFORM_H
|
||||
|
||||
#define NoIndex 0xFFFFFFFF
|
||||
|
||||
enum AddressFamily
|
||||
{
|
||||
IPv4 = 1,
|
||||
IPv6 = 2
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include <ws2tcpip.h>
|
||||
# include <time.h>
|
||||
# define MINVALUE(X,Y) (((X)<(Y)) ? (X) : (Y))
|
||||
# define MAXVALUE(X,Y) (((X)>(Y)) ? (X) : (Y))
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <stdlib.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netinet/in.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
# include <algorithm>
|
||||
# define MINVALUE(X,Y) (((X)<(Y)) ? (X) : (Y))
|
||||
# define MAXVALUE(X,Y) (((X)>(Y)) ? (X) : (Y))
|
||||
|
||||
#ifndef SOCKET
|
||||
typedef int SOCKET;
|
||||
#endif
|
||||
|
||||
extern char* strupr(char*);
|
||||
|
||||
#ifndef INVALID_SOCKET
|
||||
# define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// TURN channel prefix
|
||||
typedef unsigned short TurnPrefix;
|
||||
|
||||
|
||||
// Limit of candidates per SIP offer/answer
|
||||
#define ICE_CANDIDATE_LIMIT 64
|
||||
|
||||
// Limit of connection checks
|
||||
#define ICE_CONNCHECK_LIMIT 100
|
||||
|
||||
// Connection checks timer interval (in milliseconds)
|
||||
#define ICE_SCHEDULE_TIMER_INTERVAL 5
|
||||
|
||||
#define ICE_PERMISSIONS_REFRESH_INTERVAL 240
|
||||
|
||||
// Turns on OutputDebugString() logging
|
||||
#define ICE_REALTIME_LOG
|
||||
|
||||
// Enables keep-alive packets. It MUST be defined! There is only 1 reason to undefine it - debugging.
|
||||
#define ICE_ENABLE_KEEPALIVE
|
||||
|
||||
#define ICE_SKIP_LINKLOCAL
|
||||
#define ICE_SKIP_RELAYED_CHECKS
|
||||
|
||||
//#define ICE_IPV6_SUPPORT
|
||||
//#define ICE_LOOPBACK_SUPPORT
|
||||
|
||||
//#define ICE_DISABLE_KEEP
|
||||
|
||||
// Makes check list shorter. Most checks are triggered in this case.
|
||||
#define ICE_SMART_PRUNE_CHECKLIST
|
||||
|
||||
// Simulates symmetric NAT behavior - stack rejects all data coming not from specified STUN/TURN servers.
|
||||
//#define ICE_EMULATE_SYMMETRIC_NAT
|
||||
|
||||
// Publishes more reflexive candidates than was gathered.
|
||||
// The virtual candidates will have port number +1 +2 +3 ... +ICE_VIRTUAL_CANDIDATES
|
||||
//
|
||||
#define ICE_VIRTUAL_CANDIDATES (0)
|
||||
|
||||
// Use simple model to schedule connectivity checks
|
||||
//#define ICE_SIMPLE_SCHEDULE
|
||||
|
||||
// Limit of packet retransmission during ice checks
|
||||
#define ICE_TRANSACTION_RTO_LIMIT (10)
|
||||
|
||||
#define ICE_POSTPONE_RELAYEDCHECKS
|
||||
|
||||
// #define ICE_AGGRESSIVE
|
||||
// Use aggressive nomination + treat requests as confirmations
|
||||
// #define ICE_VERYAGGRESSIVE
|
||||
|
||||
// Define to emulate network problem
|
||||
//#define ICE_TEST_VERYAGGRESSIVE
|
||||
|
||||
// Use this define to avoid gathering reflexive/relayed candidates for public IP address
|
||||
//#define ICE_REST_ON_PUBLICIP
|
||||
|
||||
// #define ICE_DONT_CANCEL_CHECKS_ON_SUCCESS
|
||||
|
||||
// Defines the waiting time to accumulate valid pairs to choose best of them later. Can be zero.
|
||||
#define ICE_NOMINATION_WAIT_INTERVAL (50)
|
||||
|
||||
#define MAX_CLIENTCHANNELBIND_ATTEMPTS (1)
|
||||
|
||||
#define ICE_CACHE_REALM_NONCE
|
||||
|
||||
// Should be 1100 in normal conditions. 1000000 is for debugging
|
||||
#define ICE_TIMEOUT_VALUE 1100
|
||||
|
||||
|
||||
#endif
|
||||
417
src/libs/ice/ICERelaying.cpp
Normal file
417
src/libs/ice/ICERelaying.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICERelaying.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICETime.h"
|
||||
#include "ICEMD5.h"
|
||||
#include "ICELog.h"
|
||||
#include "ICEStream.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
// ------------------ ClientAllocate -----------------
|
||||
|
||||
ClientAllocate::ClientAllocate(unsigned int lifetime)
|
||||
:AuthTransaction(), mLifetime(lifetime), mWireFamily(AF_INET), mAllocFamily(AF_INET)
|
||||
{
|
||||
assert(lifetime > 60 || !lifetime);
|
||||
setComment("ClientAllocate");
|
||||
addLongTermCredentials(true);
|
||||
}
|
||||
|
||||
ClientAllocate::~ClientAllocate()
|
||||
{
|
||||
}
|
||||
|
||||
NetworkAddress& ClientAllocate::relayedAddress()
|
||||
{
|
||||
return mRelayedAddr;
|
||||
}
|
||||
|
||||
NetworkAddress& ClientAllocate::reflexiveAddress()
|
||||
{
|
||||
return mReflexiveAddr;
|
||||
}
|
||||
|
||||
NetworkAddress& ClientAllocate::responseAddress()
|
||||
{
|
||||
return mResponseAddr;
|
||||
}
|
||||
|
||||
void ClientAllocate::setInitialRequest(StunMessage& msg)
|
||||
{
|
||||
if (keepalive())
|
||||
{
|
||||
// Build refresh request
|
||||
msg.setMessageType(ice::StunMessage::Refresh);
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build allocate request
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.setMessageType(StunMessage::Allocate);
|
||||
|
||||
// Add UDP transport attribute (RequestedTransport is UDP by default)
|
||||
msg.setAttribute(new RequestedTransport());
|
||||
|
||||
// Add request family attribute
|
||||
if (mAllocFamily != mWireFamily)
|
||||
msg.setAttribute(new RequestedAddressFamily(mAllocFamily == AF_INET ? IPv4 : IPv6));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientAllocate::setAuthenticatedRequest(StunMessage& msg)
|
||||
{
|
||||
if (keepalive())
|
||||
{
|
||||
// Build refresh request
|
||||
msg.setMessageType(ice::StunMessage::Refresh);
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.lifetimeAttr().setLifetime(mLifetime);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.setMessageType(StunMessage::Allocate);
|
||||
|
||||
// Add UDP transport attribute
|
||||
msg.setAttribute(new RequestedTransport());
|
||||
|
||||
// Add LIFETIME
|
||||
msg.lifetimeAttr().setLifetime(mLifetime);
|
||||
|
||||
// Add request family attribute
|
||||
if (mAllocFamily != mWireFamily)
|
||||
msg.setAttribute(new RequestedAddressFamily(mAllocFamily == AF_INET ? IPv4 : IPv6));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientAllocate::processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress)
|
||||
{
|
||||
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
||||
mReflexiveAddr = msg.xorMappedAddressAttr().address();
|
||||
|
||||
if (msg.hasAttribute(StunAttribute::XorRelayedAddress))
|
||||
mRelayedAddr = msg.xorRelayedAddressAttr().address();
|
||||
|
||||
if (msg.hasAttribute(StunAttribute::Lifetime))
|
||||
mLifetime = msg.lifetimeAttr().lifetime();
|
||||
|
||||
mResponseAddr = sourceAddress;
|
||||
ICELogDebug(<< "Allocated for " << (int)mLifetime << " seconds.");
|
||||
}
|
||||
|
||||
int ClientAllocate::lifetime()
|
||||
{
|
||||
return mLifetime;
|
||||
}
|
||||
|
||||
void ClientAllocate::setWireFamily(int family)
|
||||
{
|
||||
mWireFamily = family;
|
||||
}
|
||||
|
||||
int ClientAllocate::getWireFamily() const
|
||||
{
|
||||
return mWireFamily;
|
||||
}
|
||||
|
||||
void ClientAllocate::setAllocFamily(int family)
|
||||
{
|
||||
mAllocFamily = family;
|
||||
}
|
||||
|
||||
int ClientAllocate::getAllocFamily() const
|
||||
{
|
||||
return mAllocFamily;
|
||||
}
|
||||
|
||||
|
||||
//------------------- ClientRefresh -------------------------------
|
||||
ClientRefresh::ClientRefresh(unsigned int lifetime, Stream* stream, ClientAllocate* allocate)
|
||||
:AuthTransaction(), mLifetime(lifetime), mStream(stream)
|
||||
{
|
||||
assert(stream);
|
||||
setComment("ClientRefresh");
|
||||
addLongTermCredentials(true);
|
||||
|
||||
// Copy data from Allocate transaction
|
||||
if (allocate)
|
||||
{
|
||||
setDestination( allocate->destination() );
|
||||
setPassword( stream->mConfig.mTurnPassword );
|
||||
setUsername( stream->mConfig.mTurnUsername );
|
||||
setComponent( allocate->component() );
|
||||
#ifdef ICE_CACHE_REALM_NONCE
|
||||
setRealm( allocate->realm() );
|
||||
setNonce( allocate->nonce() );
|
||||
#endif
|
||||
mRelayed = allocate->relayedAddress();
|
||||
mReflexive = allocate->reflexiveAddress();
|
||||
}
|
||||
}
|
||||
|
||||
ClientRefresh::~ClientRefresh()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientRefresh::setInitialRequest(StunMessage& msg)
|
||||
{
|
||||
ICELogDebug(<< "Prepare to run ClientRefresh on TURN server with lifetime " << mLifetime);
|
||||
msg.setMessageType(ice::StunMessage::Refresh);
|
||||
msg.setMessageClass(ice::StunMessage::RequestClass);
|
||||
}
|
||||
|
||||
void ClientRefresh::setAuthenticatedRequest(StunMessage& msg)
|
||||
{
|
||||
msg.setMessageType(StunMessage::Refresh);
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.lifetimeAttr().setLifetime(mLifetime);
|
||||
}
|
||||
|
||||
void ClientRefresh::processSuccessMessage(StunMessage& /*msg*/, NetworkAddress& /*sourceAddress*/)
|
||||
{
|
||||
if (mLifetime)
|
||||
{
|
||||
ICELogDebug(<< "TURN allocation refreshed for " << (int)mLifetime << " seconds.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mStream)
|
||||
{
|
||||
if (mStream->mTurnAllocated > 0)
|
||||
mStream->mTurnAllocated--;
|
||||
}
|
||||
ICELogDebug(<< "TURN allocation is deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
void ClientRefresh::processError()
|
||||
{
|
||||
if (mStream)
|
||||
{
|
||||
if (!mLifetime)
|
||||
{
|
||||
if (mStream->mTurnAllocated > 0)
|
||||
mStream->mTurnAllocated--;
|
||||
ICELogCritical(<< "TURN allocation is not deleted due to error " << errorCode() << " " << errorResponse());
|
||||
}
|
||||
else
|
||||
ICELogDebug(<< "ClientRefresh failed due to error " << errorCode() << " " << errorResponse());
|
||||
}
|
||||
}
|
||||
|
||||
NetworkAddress ClientRefresh::relayedAddress()
|
||||
{
|
||||
return mRelayed;
|
||||
}
|
||||
|
||||
NetworkAddress ClientRefresh::reflexiveAddress()
|
||||
{
|
||||
return mReflexive;
|
||||
}
|
||||
|
||||
//------------------- ClientChannelBind ----------------------------
|
||||
|
||||
static TurnPrefix GPrefix = 0;
|
||||
static Mutex GPrefixGuard;
|
||||
static TurnPrefix obtainNewPrefix()
|
||||
{
|
||||
Lock l(GPrefixGuard);
|
||||
|
||||
// Generate initial value if needed
|
||||
if (!GPrefix)
|
||||
GPrefix = (rand() % (0x7FFE - 0x4000)) + 0x4000;
|
||||
|
||||
// Avoid logical overflow
|
||||
if (GPrefix == 0x7FFE)
|
||||
GPrefix = 0x4000;
|
||||
|
||||
return ++GPrefix;
|
||||
}
|
||||
|
||||
ClientChannelBind::ClientChannelBind(const NetworkAddress& address)
|
||||
:AuthTransaction(), mPeerAddress(address)
|
||||
{
|
||||
setComment( "ClientChannelBind" );
|
||||
// Compute prefix
|
||||
mChannelPrefix = obtainNewPrefix();
|
||||
ICELogInfo(<< "Channel prefix for TURN channel bind is " << mChannelPrefix);
|
||||
addLongTermCredentials(true);
|
||||
}
|
||||
|
||||
ClientChannelBind::~ClientChannelBind()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientChannelBind::setInitialRequest(StunMessage& msg)
|
||||
{
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.setMessageType(StunMessage::ChannelBind);
|
||||
|
||||
msg.channelNumberAttr().setChannelNumber(mChannelPrefix);
|
||||
msg.xorPeerAddressAttr().address() = mPeerAddress;
|
||||
}
|
||||
|
||||
void ClientChannelBind::setAuthenticatedRequest(StunMessage& msg)
|
||||
{
|
||||
setInitialRequest(msg);
|
||||
}
|
||||
|
||||
unsigned short ClientChannelBind::channelPrefix()
|
||||
{
|
||||
return mChannelPrefix;
|
||||
}
|
||||
|
||||
bool ClientChannelBind::processData(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
return AuthTransaction::processData(msg, address);
|
||||
}
|
||||
|
||||
void ClientChannelBind::processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress)
|
||||
{
|
||||
ICELogDebug(<< mPeerAddress.toStdString() << " is bound to " << mChannelPrefix);
|
||||
// Make this transaction keepalive
|
||||
setKeepalive( true );
|
||||
setType( KeepAlive );
|
||||
setInterval( ICE_PERMISSIONS_REFRESH_INTERVAL );
|
||||
setTimestamp( ICETimeHelper::timestamp() );
|
||||
}
|
||||
|
||||
void ClientChannelBind::processError()
|
||||
{
|
||||
ICELogCritical(<< "Failed to bind channel with error " << errorCode());
|
||||
}
|
||||
|
||||
NetworkAddress ClientChannelBind::peerAddress()
|
||||
{
|
||||
return mPeerAddress;
|
||||
}
|
||||
//----------------- ClientCreatePermission ------------------------
|
||||
|
||||
ClientCreatePermission::ClientCreatePermission()
|
||||
:AuthTransaction()
|
||||
{
|
||||
setComment("ClientCreatePermission");
|
||||
addLongTermCredentials(true);
|
||||
}
|
||||
|
||||
ClientCreatePermission::~ClientCreatePermission()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void ClientCreatePermission::addIpAddress(const NetworkAddress& ip)
|
||||
{
|
||||
// Skip loopback / empty / LAN / IPv6 addresses addresses
|
||||
if (!ip.isLoopback() && !ip.isEmpty() && !ip.isLAN() && ip.family() == AF_INET)
|
||||
{
|
||||
for (unsigned i=0; i<mIPAddressList.size(); i++)
|
||||
if (mIPAddressList[i].ip() == ip.ip())
|
||||
return;
|
||||
|
||||
ICELogInfo( << "Permission is to be installed for " << ip.toStdString());
|
||||
mIPAddressList.push_back(ip);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCreatePermission::processSuccessMessage(StunMessage& /*msg*/, NetworkAddress& /*sourceAddress*/)
|
||||
{
|
||||
ICELogDebug(<< "Set permissions ok.");
|
||||
setKeepalive( true );
|
||||
setType( KeepAlive );
|
||||
setInterval( ICE_PERMISSIONS_REFRESH_INTERVAL );
|
||||
setTimestamp( ICETimeHelper::timestamp() );
|
||||
}
|
||||
|
||||
void ClientCreatePermission::setInitialRequest(StunMessage& msg)
|
||||
{
|
||||
msg.setMessageClass(StunMessage::RequestClass);
|
||||
msg.setMessageType(StunMessage::CreatePermission);
|
||||
|
||||
for (unsigned i=0; i<mIPAddressList.size(); i++)
|
||||
{
|
||||
XorPeerAddress* xpa = new XorPeerAddress();
|
||||
xpa->address() = mIPAddressList[i];
|
||||
msg.addAttribute(xpa);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClientCreatePermission::setAuthenticatedRequest(StunMessage& msg)
|
||||
{
|
||||
setInitialRequest(msg);
|
||||
}
|
||||
|
||||
ClientCreatePermission::IpList& ClientCreatePermission::ipList()
|
||||
{
|
||||
return mIPAddressList;
|
||||
}
|
||||
|
||||
// ------------------ SendIndication ----------------------------------
|
||||
SendIndication::SendIndication()
|
||||
{
|
||||
}
|
||||
|
||||
SendIndication::~SendIndication()
|
||||
{
|
||||
}
|
||||
|
||||
void SendIndication::setTarget(NetworkAddress& addr)
|
||||
{
|
||||
mTarget = addr;
|
||||
}
|
||||
|
||||
NetworkAddress& SendIndication::target()
|
||||
{
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
void SendIndication::setPlainData(ByteBuffer& plain)
|
||||
{
|
||||
mPlainData = plain;
|
||||
}
|
||||
|
||||
ByteBuffer& SendIndication::plainData()
|
||||
{
|
||||
return mPlainData;
|
||||
}
|
||||
|
||||
ByteBuffer* SendIndication::buildPacket()
|
||||
{
|
||||
StunMessage m;
|
||||
m.setMessageClass(StunMessage::IndicationClass);
|
||||
m.setMessageType(StunMessage::Send);
|
||||
|
||||
XorPeerAddress* xpa = new XorPeerAddress();
|
||||
xpa->address() = mTarget;
|
||||
|
||||
DataAttribute* da = new DataAttribute();
|
||||
da->setData(mPlainData);
|
||||
|
||||
m.setAttribute(xpa);
|
||||
m.setAttribute(da);
|
||||
|
||||
ByteBuffer* result = new ByteBuffer();
|
||||
m.buildPacket(*result, "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ByteBuffer* SendIndication::buildPacket(NetworkAddress& target, ByteBuffer& data, NetworkAddress& relay, int component)
|
||||
{
|
||||
SendIndication si;
|
||||
si.setTarget( target );
|
||||
si.setPlainData( data );
|
||||
|
||||
ByteBuffer* result = si.buildPacket();
|
||||
result->setComponent( component );
|
||||
result->setRemoteAddress( relay );
|
||||
|
||||
return result;
|
||||
}
|
||||
136
src/libs/ice/ICERelaying.h
Normal file
136
src/libs/ice/ICERelaying.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_RELAYING_H
|
||||
#define __ICE_RELAYING_H
|
||||
|
||||
#include "ICEStunTransaction.h"
|
||||
#include "ICEAddress.h"
|
||||
#include "ICEAuthTransaction.h"
|
||||
#include "ICEBinding.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
const int ChannelBindTimeout = 10 * 60 * 1000;
|
||||
const int PermissionTimeout = 5 * 60 * 1000;
|
||||
|
||||
// This class encapsulates TURN Allocate transaction
|
||||
// Usage: create instance, set password and login, generate data.
|
||||
// pass incoming data while GetState() != StunTransaction::Failed or Success
|
||||
|
||||
class ClientAllocate: public AuthTransaction
|
||||
{
|
||||
public:
|
||||
ClientAllocate(unsigned int lifetime);
|
||||
virtual ~ClientAllocate();
|
||||
|
||||
NetworkAddress& relayedAddress();
|
||||
NetworkAddress& reflexiveAddress();
|
||||
NetworkAddress& responseAddress();
|
||||
int lifetime();
|
||||
void setWireFamily(int family);
|
||||
int getWireFamily() const;
|
||||
void setAllocFamily(int family);
|
||||
int getAllocFamily() const;
|
||||
|
||||
virtual void setInitialRequest(StunMessage& msg);
|
||||
virtual void setAuthenticatedRequest(StunMessage& msg);
|
||||
virtual void processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress);
|
||||
|
||||
protected:
|
||||
NetworkAddress mRelayedAddr;
|
||||
NetworkAddress mReflexiveAddr;
|
||||
NetworkAddress mResponseAddr;
|
||||
unsigned int mLifetime;
|
||||
int mWireFamily;
|
||||
int mAllocFamily;
|
||||
};
|
||||
|
||||
class ClientChannelBind: public AuthTransaction
|
||||
{
|
||||
public:
|
||||
ClientChannelBind(const NetworkAddress& peerAddress);
|
||||
virtual ~ClientChannelBind();
|
||||
|
||||
/*Channel prefix must be 0x4000 through 0x7FFF: These values are the allowed channel
|
||||
numbers (16,383 possible values)
|
||||
*/
|
||||
unsigned short channelPrefix();
|
||||
|
||||
void setInitialRequest(StunMessage& msg);
|
||||
void setAuthenticatedRequest(StunMessage& msg);
|
||||
void processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress);
|
||||
bool processData(StunMessage& msg, NetworkAddress& address);
|
||||
void processError();
|
||||
NetworkAddress peerAddress();
|
||||
|
||||
protected:
|
||||
unsigned short mChannelPrefix;
|
||||
NetworkAddress mPeerAddress;
|
||||
};
|
||||
|
||||
class ClientCreatePermission: public AuthTransaction
|
||||
{
|
||||
public:
|
||||
typedef std::vector<NetworkAddress> IpList;
|
||||
|
||||
ClientCreatePermission();
|
||||
virtual ~ClientCreatePermission();
|
||||
|
||||
void addIpAddress(const NetworkAddress& ip);
|
||||
|
||||
virtual void setInitialRequest(StunMessage& msg);
|
||||
virtual void setAuthenticatedRequest(StunMessage& msg);
|
||||
virtual void processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress);
|
||||
|
||||
IpList& ipList();
|
||||
|
||||
protected:
|
||||
IpList mIPAddressList;
|
||||
};
|
||||
|
||||
struct Stream;
|
||||
class ClientRefresh: public AuthTransaction
|
||||
{
|
||||
public:
|
||||
ClientRefresh(unsigned int lifetime, Stream* stream, ClientAllocate* allocate = NULL);
|
||||
virtual ~ClientRefresh();
|
||||
|
||||
virtual void setInitialRequest(StunMessage& msg);
|
||||
virtual void setAuthenticatedRequest(StunMessage& msg);
|
||||
virtual void processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress);
|
||||
virtual void processError();
|
||||
unsigned int lifetime() { return mLifetime; }
|
||||
NetworkAddress relayedAddress();
|
||||
NetworkAddress reflexiveAddress();
|
||||
|
||||
protected:
|
||||
unsigned int mLifetime;
|
||||
Stream* mStream;
|
||||
NetworkAddress mRelayed, mReflexive;
|
||||
};
|
||||
|
||||
class SendIndication
|
||||
{
|
||||
public:
|
||||
SendIndication();
|
||||
~SendIndication();
|
||||
|
||||
void setTarget(NetworkAddress& addr);
|
||||
NetworkAddress& target();
|
||||
void setPlainData(ByteBuffer& plain);
|
||||
ByteBuffer& plainData();
|
||||
|
||||
ByteBuffer* buildPacket();
|
||||
|
||||
static ByteBuffer* buildPacket(NetworkAddress& target, ByteBuffer& data, NetworkAddress& relay, int component);
|
||||
|
||||
protected:
|
||||
NetworkAddress mTarget;
|
||||
ByteBuffer mPlainData;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
446
src/libs/ice/ICEReliableTransport.cpp
Normal file
446
src/libs/ice/ICEReliableTransport.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEReliableTransport.h"
|
||||
#include "ICETypes.h"
|
||||
#include <algorithm>
|
||||
using namespace ICEImpl;
|
||||
|
||||
//------------------- ReliableTransport::Packet --------------------------
|
||||
ReliableTransport::Packet::Packet()
|
||||
{
|
||||
mType = AppData;
|
||||
mSeqno = 0;
|
||||
mAppSeqno = 0;
|
||||
}
|
||||
|
||||
ReliableTransport::Packet::~Packet()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ReliableTransport::Packet::parse(const void* dataptr, int datasize, Encryption* encryption)
|
||||
{
|
||||
assert(encryption != NULL);
|
||||
assert(dataptr != NULL);
|
||||
assert(datasize != 0);
|
||||
|
||||
// 1) Check if packet has mEncryption->GetBlockSize() bytes boundary
|
||||
if (datasize % encryption->blockSize())
|
||||
return false;
|
||||
|
||||
// 2) Save data
|
||||
mData.enqueueBuffer(dataptr, datasize);
|
||||
|
||||
// 2) Decrypt it using mEncryption provider
|
||||
encryption->decrypt(mData.mutableData(), mData.size());
|
||||
|
||||
// 3) Check CRC from first 4 bytes (it is for little endian only)
|
||||
unsigned crc = mData.dequeueUInt();
|
||||
if (crc != encryption->crc(mData.data(), mData.size()))
|
||||
return false;
|
||||
|
||||
// 4) Check next byte for signature
|
||||
unsigned char signature = mData.dequeueUChar();
|
||||
if (signature != RTSignature)
|
||||
return false;
|
||||
|
||||
// 5) Next 2 bits are type of packet - AppData, Confirmation, Request or Ack
|
||||
unsigned short ts = mData.dequeueUShort();
|
||||
|
||||
mType = (Packet::Type)((ts & 0xC000) >> 14);
|
||||
|
||||
// 6) Next 14 bits are real size of packet
|
||||
unsigned short realsize = ts & 0x3FFF;
|
||||
|
||||
// 7) Seqno
|
||||
mSeqno = mData.dequeueUShort();
|
||||
|
||||
// 7) Check if we deal with AppData
|
||||
if (mType == AppData)
|
||||
mAppSeqno = mData.dequeueUShort();
|
||||
|
||||
// 8) Truncate app data to real size
|
||||
mData.truncate(realsize);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::Packet::build(const void* dataptr, int datasize, Encryption* encryption)
|
||||
{
|
||||
assert(encryption && dataptr && datasize);
|
||||
|
||||
mData.clear();
|
||||
|
||||
// Reserve place for CRC
|
||||
mData.enqueueUInt(0);
|
||||
|
||||
// Signature
|
||||
mData.enqueueUChar(RTSignature);
|
||||
|
||||
// Real size and type of packet
|
||||
unsigned short ts = (unsigned short)datasize;
|
||||
ts |= mType << 14;
|
||||
|
||||
// Enqueue real size and type of packet
|
||||
mData.enqueueUShort(ts);
|
||||
|
||||
// Enqueue sequence number
|
||||
mData.enqueueUShort(mSeqno);
|
||||
|
||||
// Enqueue application sequence number if needed
|
||||
if (mType == Packet::AppData)
|
||||
mData.enqueueUShort(mAppSeqno);
|
||||
|
||||
// Enqueue payload data
|
||||
mData.enqueueBuffer(dataptr, datasize);
|
||||
|
||||
// Padding with zero bytes
|
||||
int tail = mData.size() % encryption->blockSize();
|
||||
if (tail)
|
||||
{
|
||||
for (int i=0; i < encryption->blockSize() - tail; i++)
|
||||
mData.enqueueUChar(0);
|
||||
}
|
||||
|
||||
// Get CRC on packet
|
||||
unsigned crc = encryption->crc((const char*)mData.data() + 4, mData.size() - 4);
|
||||
|
||||
crc = htonl(crc); //It is here as corresponding DequeueUInt does ntohl
|
||||
memcpy(mData.mutableData(), &crc, 4);
|
||||
|
||||
// Encrypt
|
||||
encryption->encrypt(mData.mutableData(), mData.size());
|
||||
}
|
||||
|
||||
void ReliableTransport::Packet::setType(Type packetType)
|
||||
{
|
||||
mType = packetType;
|
||||
}
|
||||
|
||||
ReliableTransport::Packet::Type ReliableTransport::Packet::type()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void ReliableTransport::Packet::setSeqno(unsigned short seqno)
|
||||
{
|
||||
mSeqno = seqno;
|
||||
}
|
||||
|
||||
unsigned short ReliableTransport::Packet::seqno()
|
||||
{
|
||||
return mSeqno;
|
||||
}
|
||||
|
||||
void ReliableTransport::Packet::setAppSeqno(unsigned short seqno)
|
||||
{
|
||||
mAppSeqno = seqno;
|
||||
}
|
||||
|
||||
unsigned short ReliableTransport::Packet::appSeqno()
|
||||
{
|
||||
return mAppSeqno;
|
||||
}
|
||||
|
||||
ICEByteBuffer& ReliableTransport::Packet::data()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
//-------------------- ReliableTransport --------------------
|
||||
|
||||
ReliableTransport::ReliableTransport()
|
||||
{
|
||||
mEncryption = NULL;
|
||||
mSeqno = 0;
|
||||
}
|
||||
|
||||
ReliableTransport::~ReliableTransport()
|
||||
{
|
||||
}
|
||||
|
||||
void ReliableTransport::setMaxDatagram(int size)
|
||||
{
|
||||
mMaxDatagramSize = size;
|
||||
}
|
||||
|
||||
|
||||
int ReliableTransport::maxDatagram()
|
||||
{
|
||||
return mMaxDatagramSize;
|
||||
}
|
||||
|
||||
bool ReliableTransport::processIncoming(const void* dataptr, int datasize)
|
||||
{
|
||||
Packet* p = new Packet();
|
||||
if (p->parse(dataptr, datasize, mEncryption))
|
||||
{
|
||||
switch (p->type())
|
||||
{
|
||||
case Packet::AppData:
|
||||
processAppData(p);
|
||||
break;
|
||||
|
||||
case Packet::Confirmation:
|
||||
processConfirmation(p);
|
||||
break;
|
||||
|
||||
case Packet::Ack:
|
||||
processAck(p);
|
||||
break;
|
||||
|
||||
case Packet::Request:
|
||||
processRequest(p);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete p;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::queueOutgoing(const void *dataPtr, int dataSize)
|
||||
{
|
||||
// Split enqueued data to datagrams using mMaxDatagramSize value.
|
||||
// Do not forget about service bytes - seqno, appseqno, CRC, signature...
|
||||
int payloadsize = this->mMaxDatagramSize - (2 /*CRC*/ + 1 /* signature */ + 2 /* type and size */);
|
||||
|
||||
// Find packet count
|
||||
int packetcount = dataSize / payloadsize;
|
||||
if (dataSize % payloadsize)
|
||||
packetcount++;
|
||||
|
||||
// Case input pointer
|
||||
const char* dataIn = (const char*)dataPtr;
|
||||
|
||||
// Split data to packets
|
||||
for (int i=0; i<packetcount; i++)
|
||||
{
|
||||
Packet *p = new Packet();
|
||||
p->setSeqno(mSeqno++);
|
||||
p->setAppSeqno(i);
|
||||
if (i == packetcount-1 && dataSize % payloadsize)
|
||||
p->build(dataIn + i * payloadsize, dataSize % payloadsize, mEncryption);
|
||||
else
|
||||
p->build(dataIn + i * payloadsize, payloadsize, mEncryption);
|
||||
|
||||
mOutgoingData.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::processAck(Packet* p)
|
||||
{
|
||||
// Ack received for confirmation
|
||||
int count = p->data().dequeueUShort();
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
// Extract Confirmation packet seqno
|
||||
int seqno = p->data().dequeueUShort();
|
||||
|
||||
// Find corresponding confirmation packet and remove it
|
||||
removePacket(seqno);
|
||||
}
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::processConfirmation(Packet* p)
|
||||
{
|
||||
int count = p->data().dequeueUShort();
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
// Extract AppData packet seqno
|
||||
int seqno = p->data().dequeueUShort();
|
||||
|
||||
// Find corresponding AppData packet and remove it
|
||||
removePacket(seqno);
|
||||
}
|
||||
|
||||
// Create Ack packet
|
||||
mOutgoingData.push_back(makeAckPacket(p->seqno()));
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::processRequest(Packet* p)
|
||||
{
|
||||
int count = p->data().dequeueUShort();
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
// Extract AppData packet seqno
|
||||
int seqno = p->data().dequeueUShort();
|
||||
|
||||
// Find specified by seqno packet and move to top of list
|
||||
prioritizePacket(seqno);
|
||||
}
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::processAppData(Packet* p)
|
||||
{
|
||||
// 1) Add seqno to confirmation list
|
||||
mConfirmationData.push_back(p->seqno());
|
||||
|
||||
// 2) Check if confirmation list if big enough to transmit confirmation packet
|
||||
if (mConfirmationData.size() >= (unsigned)MaxConfirmationQueue)
|
||||
{
|
||||
mOutgoingData.push_back(makeConfirmationPacket(mConfirmationData));
|
||||
mConfirmationData.clear();
|
||||
}
|
||||
|
||||
// 3) Move packet to mIncomingAppData with optional packet assembling
|
||||
mIncomingAppData.push_back(p);
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::removePacket(Packet::Seqno seqno)
|
||||
{
|
||||
PacketList::iterator pit = mOutgoingData.begin();
|
||||
while (pit != mOutgoingData.end())
|
||||
{
|
||||
Packet* p = *pit;
|
||||
|
||||
if (p->seqno() == seqno)
|
||||
{
|
||||
pit = mOutgoingData.erase(pit);
|
||||
delete p;
|
||||
}
|
||||
else
|
||||
pit++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::prioritizePacket(Packet::Seqno seqno)
|
||||
{
|
||||
PacketList::iterator pit = mOutgoingData.begin();
|
||||
while (pit != mOutgoingData.end())
|
||||
{
|
||||
Packet* p = *pit;
|
||||
|
||||
if (p->seqno() == seqno)
|
||||
{
|
||||
// Remove pointer from old index
|
||||
pit = mOutgoingData.erase(pit);
|
||||
|
||||
// Insert at beginning of queue
|
||||
mOutgoingData.insert(mOutgoingData.begin(), p);
|
||||
|
||||
// Exit from loop
|
||||
break;
|
||||
}
|
||||
else
|
||||
pit++;
|
||||
}
|
||||
}
|
||||
|
||||
ReliableTransport::Packet*
|
||||
ReliableTransport::makeAckPacket(Packet::Seqno seqno)
|
||||
{
|
||||
// Create packet
|
||||
Packet* p = new Packet();
|
||||
|
||||
// Set type
|
||||
p->setType(Packet::Ack);
|
||||
|
||||
// Convert seqno number to network byte order
|
||||
unsigned short value = htons(seqno);
|
||||
|
||||
// Build packet
|
||||
p->build(&value, sizeof(value), mEncryption);
|
||||
|
||||
// Return packet
|
||||
return p;
|
||||
}
|
||||
|
||||
ReliableTransport::Packet*
|
||||
ReliableTransport::makeConfirmationPacket(std::vector<Packet::Seqno> seqnovector)
|
||||
{
|
||||
// Convert seqno values to network byte order
|
||||
std::vector<Packet::Seqno> value;
|
||||
for (unsigned i=0; i<seqnovector.size(); i++)
|
||||
value.push_back(htons(seqnovector[i]));
|
||||
|
||||
// Create packet
|
||||
Packet* p = new Packet();
|
||||
|
||||
// Set type
|
||||
p->setType(Packet::Ack);
|
||||
|
||||
// Build packet
|
||||
p->build(&value[0], value.size() * sizeof(Packet::Seqno), mEncryption);
|
||||
|
||||
// Return packet
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ReliableTransport::hasAppData()
|
||||
{
|
||||
return !mIncomingAppData.empty();
|
||||
}
|
||||
|
||||
unsigned ReliableTransport::appData(void* ptr)
|
||||
{
|
||||
if (mIncomingAppData.empty())
|
||||
return 0;
|
||||
|
||||
Packet& p = *mIncomingAppData.front();
|
||||
unsigned length = p.data().size();
|
||||
if (!ptr)
|
||||
return length;
|
||||
|
||||
memcpy(ptr, p.data().data(), length);
|
||||
mIncomingAppData.erase(mIncomingAppData.begin());
|
||||
return length;
|
||||
}
|
||||
|
||||
bool
|
||||
ReliableTransport::hasPacketToSend()
|
||||
{
|
||||
return !mOutgoingData.empty();
|
||||
}
|
||||
|
||||
void
|
||||
ReliableTransport::getPacketToSend(void* outputptr, int& outputsize)
|
||||
{
|
||||
if (mOutgoingData.empty())
|
||||
{
|
||||
outputsize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!outputptr)
|
||||
{
|
||||
outputsize = mOutgoingData.front()->data().size();
|
||||
}
|
||||
else
|
||||
{
|
||||
Packet& p = *mOutgoingData.front();
|
||||
|
||||
unsigned tocopy = MINVALUE((int)outputsize, (int)p.data().size());
|
||||
memcpy(outputptr, p.data().data(), p.data().size());
|
||||
outputsize = tocopy;
|
||||
|
||||
mOutgoingData.erase(mOutgoingData.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTransport::setEncryption(ICEImpl::ReliableTransport::Encryption *encryption)
|
||||
{
|
||||
mEncryption = encryption;
|
||||
}
|
||||
168
src/libs/ice/ICEReliableTransport.h
Normal file
168
src/libs/ice/ICEReliableTransport.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __RELIABLE_TRANSPORT_H
|
||||
#define __RELIABLE_TRANSPORT_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ICEByteBuffer.h"
|
||||
|
||||
|
||||
namespace ICEImpl {
|
||||
|
||||
// ReliableTransport is protocol stack to provide reliable transport over UDP protocol. It remains packet based protocol, not stream.
|
||||
// But it guarantees integrity and order of packets.
|
||||
class ReliableTransport
|
||||
{
|
||||
public:
|
||||
// ReliableTransport encryption interface
|
||||
class Encryption
|
||||
{
|
||||
public:
|
||||
// Returns block size for encryption algorythm
|
||||
virtual int blockSize() = 0;
|
||||
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
virtual void encrypt(void* dataPtr, int dataSize) = 0;
|
||||
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
virtual void decrypt(void* dataPtr, int dataSize) = 0;
|
||||
|
||||
// Calculates CRC
|
||||
virtual unsigned crc(const void* dataptr, int datasize) = 0;
|
||||
};
|
||||
|
||||
// Signature byte value
|
||||
static const int RTSignature = 123;
|
||||
|
||||
// Maximal count of confirmation items
|
||||
static const int MaxConfirmationQueue = 50;
|
||||
|
||||
ReliableTransport();
|
||||
~ReliableTransport();
|
||||
|
||||
// Sets maximal datagram size. This value must be odd to Encryption::GetBlockSize() returned value.
|
||||
void setMaxDatagram(int size);
|
||||
|
||||
// Returns maximal datagram size
|
||||
int maxDatagram();
|
||||
|
||||
// Sets encryption interface
|
||||
void setEncryption(Encryption* encryption);
|
||||
|
||||
// Returns current encryption interface. Default is NULL.
|
||||
Encryption* encryption();
|
||||
|
||||
// Process incoming UDP packet. Returns true if packet is recognized. Otherwise it returns false.
|
||||
bool processIncoming(const void* dataPtr, int dataSize);
|
||||
|
||||
// Enqueue outgoing packet
|
||||
void queueOutgoing(const void* dataPtr, int dataSize);
|
||||
|
||||
// Checks if there are incoming application data
|
||||
bool hasAppData();
|
||||
|
||||
// Returns incoming application data. ptr may be NULL - in this case method will return packet size.
|
||||
unsigned appData(void* ptr);
|
||||
|
||||
// Checks if there are raw packet(s) to send
|
||||
bool hasPacketToSend();
|
||||
|
||||
// Returns raw packet data to send.
|
||||
void getPacketToSend(void* outputPtr, int& outputSize);
|
||||
|
||||
protected:
|
||||
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Request = 0,
|
||||
Confirmation = 1,
|
||||
Ack = 2,
|
||||
AppData = 3
|
||||
};
|
||||
|
||||
typedef unsigned short Seqno;
|
||||
typedef unsigned short AppSeqno;
|
||||
|
||||
Packet();
|
||||
~Packet();
|
||||
|
||||
bool parse(const void* dataptr, int datasize, Encryption* encryption);
|
||||
void build(const void* dataptr, int datasize, Encryption* encryption);
|
||||
void setType(Type packetType);
|
||||
Type type();
|
||||
void setSeqno(Seqno seqno);
|
||||
Seqno seqno();
|
||||
void setAppSeqno(AppSeqno seqno);
|
||||
AppSeqno appSeqno();
|
||||
ByteBuffer& data();
|
||||
|
||||
protected:
|
||||
Type mType;
|
||||
Seqno mSeqno;
|
||||
AppSeqno mAppSeqno;
|
||||
ByteBuffer mData;
|
||||
};
|
||||
|
||||
// List of UDP packets
|
||||
typedef std::vector<Packet*> PacketList;
|
||||
|
||||
// Incoming UDP packets
|
||||
PacketList mIncomingData;
|
||||
|
||||
// Outgoing UDP packets
|
||||
PacketList mOutgoingData;
|
||||
|
||||
// Used encryption provider
|
||||
Encryption* mEncryption;
|
||||
|
||||
// Maximal datagram size
|
||||
int mMaxDatagramSize;
|
||||
|
||||
// Vector of packet numbers to confirm
|
||||
std::vector<Packet::Seqno> mConfirmationData;
|
||||
|
||||
// Vector of packet numbers to ack
|
||||
std::vector<Packet::Seqno> mAckData;
|
||||
|
||||
// Incoming decrypted application data packets
|
||||
PacketList mIncomingAppData;
|
||||
|
||||
// Seqno generator
|
||||
Packet::Seqno mSeqno;
|
||||
|
||||
// Process incoming Ack packets
|
||||
void processAck(Packet* p);
|
||||
|
||||
// Process confirmation packets
|
||||
void processConfirmation(Packet* p);
|
||||
|
||||
// Process requests packets
|
||||
void processRequest(Packet* p);
|
||||
|
||||
// Process app. data
|
||||
void processAppData(Packet* p);
|
||||
|
||||
// Remove packet from outgoing queue
|
||||
void removePacket(Packet::Seqno seqno);
|
||||
|
||||
// Prioritizes packet with specified seqno
|
||||
void prioritizePacket(Packet::Seqno seqno);
|
||||
|
||||
// Creates Ack packet for specified seqno index
|
||||
Packet* makeAckPacket(Packet::Seqno seqno);
|
||||
|
||||
// Creates Confirm packet for specified seqno indexes
|
||||
Packet* makeConfirmationPacket(std::vector<unsigned short> seqnovector);
|
||||
};
|
||||
} // end of namespace ICEImpl
|
||||
|
||||
#endif
|
||||
31
src/libs/ice/ICESHA1.cpp
Normal file
31
src/libs/ice/ICESHA1.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICESHA1.h"
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
|
||||
#include "../CryptoPP/hmac.h"
|
||||
#include "../CryptoPP/sha.h"
|
||||
|
||||
void hmacSha1Digest(const void* inputData, size_t inputSize, void* outputData, const void* key, size_t keySize)
|
||||
{
|
||||
CryptoPP::HMAC<CryptoPP::SHA1> mac((const byte*)key, keySize);
|
||||
|
||||
mac.Update((const byte*)inputData, inputSize);
|
||||
mac.Final((byte*)outputData);
|
||||
}
|
||||
|
||||
#elif defined(USE_OPENSSL)
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
void hmacSha1Digest(const void* inputData, size_t inputSize, void* outputData, const void* key, size_t keySize)
|
||||
{
|
||||
unsigned outputSize = 0;
|
||||
HMAC(EVP_sha1(), key, keySize, (const unsigned char*)inputData, inputSize, (unsigned char*)outputData, &outputSize);
|
||||
}
|
||||
|
||||
#endif
|
||||
13
src/libs/ice/ICESHA1.h
Normal file
13
src/libs/ice/ICESHA1.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_SHA1_H
|
||||
#define __ICE_SHA1_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void hmacSha1Digest(const void* inputData, size_t inputSize, void* outputData, const void* key, size_t keySize);
|
||||
|
||||
#endif
|
||||
893
src/libs/ice/ICESession.cpp
Normal file
893
src/libs/ice/ICESession.cpp
Normal file
@@ -0,0 +1,893 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICESession.h"
|
||||
#include "ICENetworkHelper.h"
|
||||
#include "ICEAction.h"
|
||||
#include "ICESmartPtr.h"
|
||||
#include "ICEBinding.h"
|
||||
#include "ICERelaying.h"
|
||||
#include "ICEAddress.h"
|
||||
#include "ICEStream.h"
|
||||
#include "ICELog.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#ifdef TARGET_WIN
|
||||
#include <iphlpapi.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace ice;
|
||||
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
|
||||
Session::Session()
|
||||
{
|
||||
//No activity yet
|
||||
mState = None;
|
||||
|
||||
//start interval for STUN messages
|
||||
mStartInterval = 20;
|
||||
|
||||
mLocalPortNumber = 0;
|
||||
refreshPwdUfrag();
|
||||
|
||||
//default role is Controlled
|
||||
mRole = RoleControlled;
|
||||
|
||||
mTieBreaker = " ";
|
||||
for (size_t i=0; i<8; i++)
|
||||
mTieBreaker[i] = rand() & 0xFF;
|
||||
|
||||
mFoundationGenerator = 0xFFFFFFFF;
|
||||
mMustRestart = false;
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
}
|
||||
|
||||
void Session::setup(StackConfig& config)
|
||||
{
|
||||
if (!config.mUseSTUN && !config.mUseTURN)
|
||||
throw std::logic_error("ICE is configured to not use STUN and not use TURN.");
|
||||
if (config.mUseSTUN && config.mUseTURN)
|
||||
throw std::logic_error("ICE is configured to use both STUN and TURN.");
|
||||
|
||||
// Save the configuration
|
||||
mConfig = config;
|
||||
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->setConfig(config);
|
||||
}
|
||||
|
||||
int Session::addStream()
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
ICEStreamPtr s(new Stream());
|
||||
s->setAgentRole(mRole);
|
||||
s->setConfig(mConfig);
|
||||
s->mTieBreaker = mTieBreaker;
|
||||
s->setLocalPwd(mLocalPwd);
|
||||
s->setLocalUfrag(mLocalUfrag);
|
||||
s->mId = mStreamMap.size();
|
||||
mStreamMap[s->mId] = s;
|
||||
|
||||
ICELogInfo(<< "New stream " << s->mId << " is add.");
|
||||
|
||||
return s->mId;
|
||||
}
|
||||
|
||||
int Session::addComponent(int streamID, void* tag, unsigned short port4, unsigned short port6)
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
if (mStreamMap.find(streamID) == mStreamMap.end())
|
||||
{
|
||||
ICELogCritical(<< "Cannot find stream " << streamID << " to add new component.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get reference to stream
|
||||
Stream& stream = *mStreamMap[streamID];
|
||||
|
||||
// Assign new ID to component
|
||||
int componentID = stream.addComponent(tag, port4, port6);
|
||||
|
||||
ICELogInfo( << "New component " << componentID << " is add to stream " << streamID << ".");
|
||||
|
||||
return componentID;
|
||||
}
|
||||
|
||||
void Session::removeStream(int streamID)
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
mStreamMap.erase(streamIter);
|
||||
}
|
||||
|
||||
bool Session::findStreamAndComponent(int family, unsigned short port, int* stream, int* component)
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
// Iterate streams
|
||||
ICEStreamMap::iterator sit;
|
||||
for (sit = mStreamMap.begin(); sit != mStreamMap.end(); ++sit)
|
||||
{
|
||||
if (sit->second->hasPortNumber(family, port, component))
|
||||
{
|
||||
*stream = sit->first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Session::hasStream(int streamId)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
return mStreamMap.find(streamId) != mStreamMap.end();
|
||||
}
|
||||
|
||||
bool Session::hasComponent(int streamId, int componentId)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICEStreamMap::const_iterator streamIter = mStreamMap.find(streamId);
|
||||
if (streamIter == mStreamMap.end())
|
||||
return false;
|
||||
if (!streamIter->second)
|
||||
return false;
|
||||
|
||||
return streamIter->second->mComponentMap.find(componentId) != streamIter->second->mComponentMap.end();
|
||||
}
|
||||
|
||||
void Session::setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
if (hasComponent(streamId, componentId))
|
||||
{
|
||||
mStreamMap[streamId]->mComponentMap[componentId].mPort4 = port4;
|
||||
mStreamMap[streamId]->mComponentMap[componentId].mPort6 = port6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Session::gatherCandidates()
|
||||
{
|
||||
//protect instance
|
||||
Lock lock(mGuard);
|
||||
|
||||
//parse the STUN/TURN server IP address
|
||||
//mConfig.mSTUNServerAddr.GetSockaddr((sockaddr&)mSTUNServerAddr);
|
||||
//mConfig.mTURNServerAddr.GetSockaddr((sockaddr&)mTURNServerAddr);
|
||||
|
||||
//as RFC says...
|
||||
//if (mConfig.mUseTURN)
|
||||
// mSTUNServerAddr = mTURNServerAddr;
|
||||
|
||||
// Define the list of used IP interfaces
|
||||
std::vector<NetworkAddress> hostip;
|
||||
|
||||
// Get list of IP interfaces
|
||||
// std::vector<NetworkAddress>& ipList = NetworkHelper::Instance().GetInterfaceList();
|
||||
|
||||
// Iterate all streams and instructs them to gather candidates
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter;
|
||||
int finished = 0;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
{
|
||||
streamIter->second->gatherCandidates();
|
||||
if (streamIter->second->mState > CandidateGathering)
|
||||
finished++;
|
||||
}
|
||||
|
||||
// Set session state as "candidate gathering" or "creating sdp" - depending on stream state
|
||||
if ((size_t)finished == mStreamMap.size())
|
||||
mState = CreatingSDP;
|
||||
else
|
||||
mState = CandidateGathering;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Session::processData(ByteBuffer& buffer, int streamID, int component)
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
// Attempt to parse
|
||||
StunMessage msg;
|
||||
if (!msg.parsePacket(buffer))
|
||||
return false;
|
||||
|
||||
ICELogDebug( << "Received STUN packet from " << buffer.remoteAddress().toStdString().c_str() << ". Data: " << buffer.hexstring());
|
||||
|
||||
// Old&new states
|
||||
int oldstate = None, newstate = None;
|
||||
|
||||
// Find stream responsible for this buffer
|
||||
if (mStreamMap.find(streamID) == mStreamMap.end())
|
||||
return false;
|
||||
|
||||
// Get pointer to stream
|
||||
ICEStreamPtr streamPtr = mStreamMap[streamID];
|
||||
if (!streamPtr)
|
||||
return false;
|
||||
|
||||
Stream& stream = *streamPtr;
|
||||
oldstate = stream.mState;
|
||||
/*bool result = */stream.processData(msg, buffer, component);
|
||||
newstate = stream.mState;
|
||||
|
||||
if (newstate != oldstate)
|
||||
{
|
||||
// State is changed. Maybe full session state is changed too?
|
||||
int failedcount = 0, successcount = 0;
|
||||
|
||||
switch (newstate)
|
||||
{
|
||||
case CreatingSDP:
|
||||
|
||||
// Check if all streams gathered candidates or failed
|
||||
for (ICEStreamMap::iterator si=mStreamMap.begin(); si != mStreamMap.end(); ++si)
|
||||
{
|
||||
if (!si->second)
|
||||
continue;
|
||||
|
||||
Stream& stream = *si->second;
|
||||
if (stream.mState == CreatingSDP)
|
||||
successcount++;
|
||||
else
|
||||
if (stream.mState == Failed)
|
||||
failedcount++;
|
||||
}
|
||||
|
||||
if (failedcount == (int)mStreamMap.size())
|
||||
mState = Failed;
|
||||
else
|
||||
mState = CreatingSDP;
|
||||
|
||||
break;
|
||||
|
||||
case Success:
|
||||
case Failed:
|
||||
// Check if all streams a success or failed
|
||||
for (ICEStreamMap::iterator si=mStreamMap.begin(); si != mStreamMap.end(); ++si)
|
||||
{
|
||||
if (!si->second)
|
||||
continue;
|
||||
|
||||
Stream& stream = *si->second;
|
||||
if (stream.mState == Success)
|
||||
successcount++;
|
||||
else
|
||||
if (stream.mState == Failed)
|
||||
failedcount++;
|
||||
}
|
||||
|
||||
if (failedcount == (int)mStreamMap.size())
|
||||
mState = Failed;
|
||||
else
|
||||
mState = Success;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PByteBuffer Session::getDataToSend(bool& response, int& stream, int& component, void*&tag)
|
||||
{
|
||||
// Protect instance
|
||||
Lock lock(mGuard);
|
||||
|
||||
PByteBuffer result;
|
||||
|
||||
// Iterate streams to update their states (maybe timeout occured since last call of getDataToSend())
|
||||
ICEStreamMap::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
if (streamIter->second)
|
||||
streamIter->second->isTimeout();
|
||||
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
{
|
||||
if (streamIter->second)
|
||||
result = streamIter->second->getDataToSend(response, component, tag);
|
||||
if (result)
|
||||
{
|
||||
stream = streamIter->first;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Session::active()
|
||||
{
|
||||
//protect instance
|
||||
Lock lock(mGuard);
|
||||
|
||||
return (mState != None);
|
||||
}
|
||||
|
||||
void Session::createSdp(std::vector<std::string>& common)
|
||||
{
|
||||
common.push_back("a=ice-full");
|
||||
common.push_back("a=ice-pwd:" + localPwd());
|
||||
common.push_back("a=ice-ufrag:" + localUfrag());
|
||||
}
|
||||
|
||||
void Session::createSdp(int streamID, std::vector<std::string>& defaultIP,
|
||||
std::vector<unsigned short>& defaultPort, std::vector<std::string>& candidateList)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
streamIter->second->createOfferSdp(defaultIP, defaultPort, candidateList);
|
||||
}
|
||||
|
||||
|
||||
bool Session::findCandidate(std::vector<Candidate>&cv, Candidate::Type _type,
|
||||
const std::string& baseIP, int componentID, Candidate& result)
|
||||
{
|
||||
for (unsigned int i=0; i<cv.size(); i++)
|
||||
{
|
||||
Candidate& c = cv[i];
|
||||
if (c.mType == _type && c.mComponentId == componentID && c.mLocalAddr.ip()/*mBase*/ == baseIP)
|
||||
{
|
||||
result = c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RunningState Session::state()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
std::string Session::createUfrag()
|
||||
{
|
||||
std::string result;
|
||||
result.resize(4);
|
||||
|
||||
for (size_t i=0; i<4; i++)
|
||||
{
|
||||
int r = rand();
|
||||
char c = 'a' + int((float)r / ((float)RAND_MAX / 26.0));
|
||||
|
||||
result[i] = c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Session::createPassword()
|
||||
{
|
||||
std::string result;
|
||||
result.resize(22);
|
||||
|
||||
for (size_t i=0; i<22; i++)
|
||||
{
|
||||
int r = rand();
|
||||
char c = 'a' + int((float)r / ((float)RAND_MAX / 26.0));
|
||||
|
||||
result[i] = c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Session::localUfrag()
|
||||
{
|
||||
return mLocalUfrag;
|
||||
}
|
||||
|
||||
std::string Session::localPwd()
|
||||
{
|
||||
return mLocalPwd;
|
||||
}
|
||||
|
||||
void Session::setRemotePassword(const std::string& pwd, int streamId)
|
||||
{
|
||||
mMustRestart |= pwd != mRemotePwd;
|
||||
mRemotePwd = pwd;
|
||||
for (ICEStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); streamIter++)
|
||||
{
|
||||
ICEStreamPtr s = streamIter->second;
|
||||
if (s)
|
||||
{
|
||||
if (streamId == -1 || streamIter->first == streamId)
|
||||
s->setRemotePwd(pwd);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string Session::remotePassword(int streamId) const
|
||||
{
|
||||
if (streamId == -1)
|
||||
return mRemotePwd;
|
||||
ICEStreamMap::const_iterator streamIter = mStreamMap.find(streamId);
|
||||
if (streamIter == mStreamMap.end())
|
||||
return std::string();
|
||||
if (!streamIter->second)
|
||||
return std::string();
|
||||
|
||||
return streamIter->second->mRemotePwd;
|
||||
}
|
||||
|
||||
void Session::setRemoteUfrag(const std::string& ufrag, int streamId)
|
||||
{
|
||||
mMustRestart |= ufrag != mRemoteUfrag;
|
||||
mRemoteUfrag = ufrag;
|
||||
|
||||
// Set it to streams
|
||||
for (ICEStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); streamIter++)
|
||||
{
|
||||
ICEStreamPtr s = streamIter->second;
|
||||
if (s)
|
||||
{
|
||||
if (streamId == -1 || streamIter->first == streamId)
|
||||
s->setRemoteUfrag(ufrag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Session::remoteUfrag(int streamId) const
|
||||
{
|
||||
if (streamId == -1)
|
||||
return mRemoteUfrag;
|
||||
ICEStreamMap::const_iterator streamIter = mStreamMap.find(streamId);
|
||||
if (streamIter == mStreamMap.end())
|
||||
return std::string();
|
||||
if (!streamIter->second)
|
||||
return std::string();
|
||||
|
||||
return streamIter->second->mRemoteUfrag;
|
||||
}
|
||||
|
||||
bool Session::processSdpOffer(int streamIndex, std::vector<std::string>& candidateList,
|
||||
const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamIndex);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->processSdpOffer(candidateList, defaultIP, defaultPort, deleteRelayed);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkAddress Session::getRemoteRelayedCandidate(int stream, int component)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(stream);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->remoteRelayedAddress(component);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
NetworkAddress Session::getRemoteReflexiveCandidate(int stream, int component)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(stream);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->remoteReflexiveAddress(component);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
NetworkAddress Session::defaultAddress(int streamID, int componentID)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->defaultAddress(componentID);
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
void Session::fillCandidateList(int streamID, int componentID, std::vector<std::string>& candidateList)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
streamIter->second->candidateList(componentID, candidateList);
|
||||
}
|
||||
|
||||
void Session::checkConnectivity()
|
||||
{
|
||||
ICELogInfo( << "Starting connectivity checks.");
|
||||
|
||||
// Check current session state to ensure is did not run connectivity checks already
|
||||
if (mState == ConnCheck || mState == Success)
|
||||
return;
|
||||
|
||||
// Make current state "connection checking now"
|
||||
mState = ConnCheck;
|
||||
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); streamIter++)
|
||||
{
|
||||
streamIter->second->setRemotePwd(mRemotePwd);
|
||||
streamIter->second->setRemoteUfrag(mRemoteUfrag);
|
||||
streamIter->second->startChecks();
|
||||
}
|
||||
|
||||
// Set session state to "connection checks"
|
||||
mState = ConnCheck;
|
||||
|
||||
/*
|
||||
Checks are generated only by full implementations. Lite
|
||||
implementations MUST skip the steps described in this section.
|
||||
|
||||
An agent performs ordinary checks and triggered checks. The
|
||||
generation of both checks is governed by a timer which fires
|
||||
periodically for each media stream. The agent maintains a FIFO
|
||||
queue, called the triggered check queue, which contains candidate
|
||||
pairs for which checks are to be sent at the next available
|
||||
|
||||
opportunity. When the timer fires, the agent removes the top pair
|
||||
from triggered check queue, performs a connectivity check on that
|
||||
pair, and sets the state of the candidate pair to In-Progress. If
|
||||
there are no pairs in the triggered check queue, an ordinary check is
|
||||
sent.
|
||||
|
||||
Once the agent has computed the check lists as described in
|
||||
Section 5.7, it sets a timer for each active check list. The timer
|
||||
fires every Ta*N seconds, where N is the number of active check lists
|
||||
(initially, there is only one active check list). Implementations
|
||||
MAY set the timer to fire less frequently than this. Implementations
|
||||
SHOULD take care to spread out these timers so that they do not fire
|
||||
at the same time for each media stream. Ta and the retransmit timer
|
||||
RTO are computed as described in Section 16. Multiplying by N allows
|
||||
this aggregate check throughput to be split between all active check
|
||||
lists. The first timer fires immediately, so that the agent performs
|
||||
a connectivity check the moment the offer/answer exchange has been
|
||||
done, followed by the next check Ta seconds later (since there is
|
||||
only one active check list).
|
||||
|
||||
When the timer fires, and there is no triggered check to be sent, the
|
||||
agent MUST choose an ordinary check as follows:
|
||||
|
||||
o Find the highest priority pair in that check list that is in the
|
||||
Waiting state.
|
||||
|
||||
o If there is such a pair:
|
||||
|
||||
* Send a STUN check from the local candidate of that pair to the
|
||||
remote candidate of that pair. The procedures for forming the
|
||||
STUN request for this purpose are described in Section 7.1.1.
|
||||
|
||||
* Set the state of the candidate pair to In-Progress.
|
||||
|
||||
o If there is no such pair:
|
||||
|
||||
* Find the highest priority pair in that check list that is in
|
||||
the Frozen state.
|
||||
|
||||
* If there is such a pair:
|
||||
|
||||
+ Unfreeze the pair.
|
||||
|
||||
+ Perform a check for that pair, causing its state to
|
||||
transition to In-Progress.
|
||||
|
||||
* If there is no such pair:
|
||||
|
||||
+ Terminate the timer for that check list.
|
||||
|
||||
To compute the message integrity for the check, the agent uses the
|
||||
remote username fragment and password learned from the SDP from its
|
||||
peer. The local username fragment is known directly by the agent for
|
||||
its own candidate.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void Session::setRole(AgentRole role)
|
||||
{
|
||||
mRole = role;
|
||||
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->setAgentRole(role);
|
||||
}
|
||||
|
||||
AgentRole Session::role()
|
||||
{
|
||||
return mRole;
|
||||
}
|
||||
|
||||
|
||||
void Session::clear()
|
||||
{
|
||||
refreshPwdUfrag();
|
||||
mState = None;
|
||||
std::map<int, ICEStreamPtr>::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->clear();
|
||||
mTurnPrefixMap.clear();
|
||||
}
|
||||
|
||||
void Session::clearForRestart(bool localNetworkChanged)
|
||||
{
|
||||
refreshPwdUfrag();
|
||||
mState = ice::None;
|
||||
ICEStreamMap::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->clearForRestart(localNetworkChanged);
|
||||
}
|
||||
|
||||
void Session::stopChecks()
|
||||
{
|
||||
ICELogInfo(<< "Stop connectivity checks");
|
||||
|
||||
ICEStreamMap::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->stopChecks();
|
||||
|
||||
if (mState < Failed)
|
||||
mState = Failed;
|
||||
}
|
||||
|
||||
void Session::cancelAllocations()
|
||||
{
|
||||
for (auto stream: mStreamMap)
|
||||
stream.second->cancelAllocations();
|
||||
}
|
||||
|
||||
bool Session::finished()
|
||||
{
|
||||
return (mState == Failed || mState == Success);
|
||||
}
|
||||
|
||||
NetworkAddress Session::remoteAddress(int streamID, int componentID)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->remoteAddress(componentID);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
NetworkAddress Session::localAddress(int streamID, int componentID)
|
||||
{
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->localAddress(componentID);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
NetworkAddress Session::reflexiveAddress(int streamID, int componentID)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->reflexiveAddress(componentID);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
NetworkAddress Session::relayedAddress(int streamID, int componentID)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(streamID);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->relayedAddress(componentID);
|
||||
|
||||
return NetworkAddress();
|
||||
}
|
||||
|
||||
bool Session::hasTurnPrefix(TurnPrefix prefix)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
TurnPrefixMap::iterator mit = mTurnPrefixMap.find(prefix);
|
||||
return mit != mTurnPrefixMap.end();
|
||||
}
|
||||
|
||||
void Session::chooseDefaults()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
|
||||
ICEStreamMap::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->Handle_CD();
|
||||
}
|
||||
|
||||
void Session::dump(std::ostream& output)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
|
||||
ICEStreamMap::iterator streamIter;
|
||||
for (streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
streamIter->second->dump(output);
|
||||
}
|
||||
|
||||
bool Session::findConcludePair(int stream, Candidate& local, Candidate& remote)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(stream);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->findConcludePair(local, remote);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Session::mustRestart() const
|
||||
{
|
||||
return mMustRestart;
|
||||
}
|
||||
|
||||
void Session::refreshPwdUfrag()
|
||||
{
|
||||
mLocalPwd = Session::createPassword();
|
||||
mLocalUfrag = Session::createUfrag();
|
||||
|
||||
// Update all streams
|
||||
for (ICEStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
{
|
||||
if (streamIter->second)
|
||||
{
|
||||
streamIter->second->setLocalPwd(mLocalPwd);
|
||||
streamIter->second->setLocalUfrag(mLocalUfrag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::freeAllocation(int stream, int component, DeleteAllocationCallback* cb)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(stream);
|
||||
if (streamIter != mStreamMap.end())
|
||||
streamIter->second->freeAllocation(component, cb);
|
||||
}
|
||||
|
||||
bool Session::hasAllocations()
|
||||
{
|
||||
int allocated = 0;
|
||||
Lock l(mGuard);
|
||||
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.begin();
|
||||
for (;streamIter != mStreamMap.end(); ++streamIter)
|
||||
if (streamIter->second)
|
||||
allocated += streamIter->second->mTurnAllocated;
|
||||
|
||||
return allocated > 0;
|
||||
}
|
||||
|
||||
bool Session::isDataIndication(ByteBuffer& source, ByteBuffer* plain)
|
||||
{
|
||||
StunMessage msg;
|
||||
if (!msg.parsePacket(source))
|
||||
return false;
|
||||
|
||||
if (msg.hasAttribute(StunAttribute::Data) && msg.hasAttribute(StunAttribute::XorPeerAddress))
|
||||
{
|
||||
// It is Data indication packet
|
||||
DataAttribute& d = dynamic_cast<DataAttribute&>(msg.attribute(StunAttribute::Data));
|
||||
XorPeerAddress& xpa = dynamic_cast<XorPeerAddress&>(msg.attribute(StunAttribute::XorPeerAddress));
|
||||
|
||||
if (plain)
|
||||
{
|
||||
*plain = d.data();
|
||||
NetworkAddress remoteAddress = xpa.address();
|
||||
remoteAddress.setRelayed(true);
|
||||
plain->setRemoteAddress(remoteAddress);
|
||||
plain->setRelayed(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Session::isStun(ByteBuffer& source)
|
||||
{
|
||||
if (source.size() < 8)
|
||||
return false;
|
||||
|
||||
const unsigned* magic = (const unsigned*)source.data();
|
||||
return (ntohl(magic[1]) == 0x2112A442);
|
||||
}
|
||||
|
||||
|
||||
int Session::errorCode()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICEStreamMap::const_iterator streamIter = mStreamMap.begin();
|
||||
for (;streamIter != mStreamMap.end(); ++streamIter)
|
||||
if (streamIter->second->mState == /*RunningState::*/Failed)
|
||||
return streamIter->second->mErrorCode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
TurnPrefix Session::bindChannel(int stream, int component, const NetworkAddress& target, ChannelBoundCallback* cb)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICELogInfo(<< "Bind channel " << stream << "/" << component << " to " << target.toStdString());
|
||||
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.find(stream);
|
||||
if (streamIter != mStreamMap.end())
|
||||
return streamIter->second->bindChannel(target, component, cb);
|
||||
|
||||
return 0; // Channel numbers are in range [0x4000...0x7FFF]
|
||||
}
|
||||
|
||||
bool Session::isChannelBindingFailed(int stream, int component, TurnPrefix prefix)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.begin();
|
||||
if (streamIter != mStreamMap.end())
|
||||
if (streamIter->second)
|
||||
return streamIter->second->isChannelBindingFailed(component, prefix);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::installPermissions(int stream, int component, const NetworkAddress &address, InstallPermissionsCallback* cb)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
ICELogInfo(<< "Install permissions " << stream << "/" << component << " for " << address.toStdString());
|
||||
|
||||
ICEStreamMap::iterator streamIter = mStreamMap.begin();
|
||||
if (streamIter != mStreamMap.end())
|
||||
streamIter->second->installPermissions(component, address, cb);
|
||||
}
|
||||
|
||||
bool Session::isRtp(ByteBuffer& source)
|
||||
{
|
||||
if (!source.size())
|
||||
return false;
|
||||
unsigned char b = *(const unsigned char*)source.data();
|
||||
return (b & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
bool Session::isChannelData(ByteBuffer& source, TurnPrefix prefix)
|
||||
{
|
||||
if (source.size() < 4)
|
||||
return false;
|
||||
|
||||
if (!prefix)
|
||||
{
|
||||
unsigned char b = *(const unsigned char*)source.data();
|
||||
return (b & 0xC0) == 0x40;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short pp = ntohs(*(const unsigned short*)source.data());
|
||||
return pp == prefix;
|
||||
}
|
||||
}
|
||||
|
||||
Stream::CandidateVector* Session::remoteCandidates(int stream)
|
||||
{
|
||||
ICEStreamMap::iterator iter = mStreamMap.find(stream);
|
||||
if (iter != mStreamMap.end())
|
||||
return &iter->second->mRemoteCandidate;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NetworkAddress Session::activeStunServer(int stream) const
|
||||
{
|
||||
ICEStreamMap::const_iterator iter = mStreamMap.find(stream);
|
||||
if (iter != mStreamMap.end())
|
||||
return iter->second->mConfig.mServerAddr4;
|
||||
else
|
||||
return NetworkAddress();
|
||||
}
|
||||
221
src/libs/ice/ICESession.h
Normal file
221
src/libs/ice/ICESession.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_SESSION_H
|
||||
#define __ICE_SESSION_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "ICEStunConfig.h"
|
||||
#include "ICECandidate.h"
|
||||
#include "ICEStunMessage.h"
|
||||
#include "ICEBinding.h"
|
||||
#include "ICERelaying.h"
|
||||
#include "ICESync.h"
|
||||
#include "ICECheckList.h"
|
||||
#include "ICETime.h"
|
||||
#include "ICESmartPtr.h"
|
||||
#include "ICEStream.h"
|
||||
|
||||
namespace ice {
|
||||
|
||||
// ICE session context
|
||||
struct Session
|
||||
{
|
||||
public:
|
||||
Mutex mGuard; // Mutex to protect this instance
|
||||
typedef std::map<int, ICEStreamPtr> ICEStreamMap;
|
||||
ICEStreamMap mStreamMap; // Streams
|
||||
RunningState mState; // State of session
|
||||
StackConfig mConfig; // Configuration
|
||||
|
||||
size_t mStartInterval; /// Start interval
|
||||
unsigned short mLocalPortNumber; /// Local port number
|
||||
std::string mLocalPwd,
|
||||
mLocalUfrag,
|
||||
mRemotePwd,
|
||||
mRemoteUfrag;
|
||||
|
||||
NetworkAddress mRemoteDefaultAddr; /// Remote default address
|
||||
AgentRole mRole; /// Marks if agent is Controlled or Controlling
|
||||
std::string mTieBreaker; /// Tie breaker
|
||||
unsigned int mFoundationGenerator; /// Foundation value generator. Used when foundation of remote candidate is not available.
|
||||
bool mCanTransmit; /// Marks if transmission can start
|
||||
bool mMustReInvite; /// Must reINVITE
|
||||
TurnPrefixMap mTurnPrefixMap; /// Map of established relays
|
||||
bool mMustRestart; /// Marks if session has to restart
|
||||
|
||||
/*! Default constructor. */
|
||||
Session();
|
||||
|
||||
/*! Destructor. */
|
||||
~Session();
|
||||
|
||||
/*! Adjusts session to work with specified component number and specified local port number. */
|
||||
void setup(StackConfig& config);
|
||||
|
||||
/*! Checks if ICE session is started. */
|
||||
bool active();
|
||||
|
||||
/*! Initiates candidate gathering. Setup() must be called before. */
|
||||
void gatherCandidates();
|
||||
|
||||
/*! Processing incoming data.
|
||||
* @return True if data were processed, false otherwise (too old response or not STUN data at all). */
|
||||
bool processData(ByteBuffer& buffer, int stream, int component);
|
||||
|
||||
/*! Checks if there is any data to send.
|
||||
* @return True if more data to send are available, false if all available data are returned in current call. */
|
||||
PByteBuffer getDataToSend(bool& response, int& stream, int& component, void*&tag);
|
||||
|
||||
|
||||
/*! Handles incoming data in gathering candidate stage.
|
||||
* @return True if data was handled, false otherwise (too old response or not STUN data at all. */
|
||||
bool Handle_CG_In(StunMessage& msg, NetworkAddress& address);
|
||||
|
||||
/*! Handle eliminate redudand stage. */
|
||||
void Handle_ER();
|
||||
|
||||
// Compute foundations
|
||||
void Handle_CF();
|
||||
|
||||
// Starting keep alive timers
|
||||
void Handle_SKA();
|
||||
|
||||
// Prioritize candidates
|
||||
void Handle_PC();
|
||||
|
||||
// Choosing default candidate
|
||||
void chooseDefaults();
|
||||
|
||||
// Creation of common SDP strings
|
||||
void createSdp(std::vector<std::string>& common);
|
||||
|
||||
// Creation of component SDP strings
|
||||
void createSdp(int streamID, std::vector<std::string>& defaultIP,
|
||||
std::vector<unsigned short>& defaultPort, std::vector<std::string>& candidateList);
|
||||
|
||||
bool findCandidate(std::vector<Candidate>&cv, Candidate::Type _type,
|
||||
const std::string& baseIP, int componentID, Candidate& result);
|
||||
|
||||
RunningState state();
|
||||
|
||||
std::string localUfrag();
|
||||
std::string localPwd();
|
||||
|
||||
/*! Process ICE offer text. It does not mean SIP offer. No, this method applies to initial ICE candidate list and other information in both SIP offer and answer packets. */
|
||||
bool processSdpOffer(int streamIndex, std::vector<std::string>& candidateList,
|
||||
const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed);
|
||||
NetworkAddress getRemoteRelayedCandidate(int stream, int component);
|
||||
NetworkAddress getRemoteReflexiveCandidate(int stream, int component);
|
||||
|
||||
NetworkAddress defaultAddress(int streamID, int componentID);
|
||||
void fillCandidateList(int streamID, int componentID, std::vector<std::string>& candidateList);
|
||||
|
||||
/*! Seeks in candidate list for candidate with specified IP and port. */
|
||||
bool candidateListContains(std::string remoteIP, unsigned short remotePort);
|
||||
|
||||
/*! Marks agent as Controlled or Controlling role. */
|
||||
void setRole(AgentRole role);
|
||||
|
||||
/*! Gets current role. */
|
||||
AgentRole role();
|
||||
|
||||
/*! Binds channel to prefix */
|
||||
TurnPrefix bindChannel(int stream, int component, const NetworkAddress& target, ChannelBoundCallback* cb);
|
||||
bool isChannelBindingFailed(int stream, int component, TurnPrefix prefix);
|
||||
|
||||
void installPermissions(int stream, int component, const NetworkAddress &address, InstallPermissionsCallback* cb);
|
||||
|
||||
/*! Enqueues CreatePermisssion transaction with zero lifetime - it removes permission. */
|
||||
void deletePermission(int stream, int component, DeletePermissionsCallback* cb);
|
||||
|
||||
// Enqueues ClientAllocate with zero lifetime
|
||||
void freeAllocation(int stream, int component, DeleteAllocationCallback* cb);
|
||||
|
||||
// Returns if there were any TURN allocations from this stack
|
||||
bool hasAllocations();
|
||||
|
||||
static bool isDataIndication(ByteBuffer& source, ByteBuffer* plain);
|
||||
static bool isStun(ByteBuffer& source);
|
||||
static bool isRtp(ByteBuffer& source);
|
||||
static bool isChannelData(ByteBuffer& source, TurnPrefix prefix);
|
||||
|
||||
/*! Starts connectivity checks. */
|
||||
void checkConnectivity();
|
||||
|
||||
// Clears state of session and streams; does not delete streams or components. Stops all connectivity checks. Candidates must be gathered again.
|
||||
void clear();
|
||||
|
||||
void clearForRestart(bool localNetworkChanged);
|
||||
|
||||
// Returns true if state of session is Success or Failed
|
||||
bool finished();
|
||||
|
||||
// Stop connectivity checks and gathering requests.
|
||||
void stopChecks();
|
||||
|
||||
// Cancel allocation requests. Called when timeout is detected and allocation should be cancelled.
|
||||
// Allocation transaction and session can have different timeout values - so this method is neccessary
|
||||
void cancelAllocations();
|
||||
|
||||
/*! Returns concluding addresses . */
|
||||
NetworkAddress remoteAddress(int streamID, int componentID);
|
||||
NetworkAddress localAddress(int streamID, int componentID);
|
||||
|
||||
/*! Searches for local server reflexive candidate with specified component ID and returns its external address. */
|
||||
NetworkAddress reflexiveAddress(int streamID, int componentID);
|
||||
|
||||
/*! Searches for local server relayed candidate with specified component ID and returns its external address. */
|
||||
NetworkAddress relayedAddress(int streamID, int componentID);
|
||||
|
||||
/*! Checks if argument is used TURN prefix in one of the TURN bound channels. */
|
||||
bool hasTurnPrefix(TurnPrefix prefix);
|
||||
|
||||
/*! Adds stream to session. Returns stream index. */
|
||||
int addStream();
|
||||
|
||||
/*! Adds component to stream.
|
||||
* @param streamIndex Stream index.
|
||||
* @param tag Tag associated with component.
|
||||
* @return Created component record index. */
|
||||
int addComponent(int streamID, void *tag, unsigned short port4, unsigned short port6);
|
||||
|
||||
/* Removes stream with specified ID. All stream's components are removed too. */
|
||||
void removeStream(int streamID);
|
||||
|
||||
/* Searches the stream&component ID basing on socket family (AF_INET or AF_INET6) and local port number. */
|
||||
bool findStreamAndComponent(int family, unsigned short port, int* stream, int* component);
|
||||
|
||||
bool hasStream(int streamId);
|
||||
bool hasComponent(int streamId, int componentId);
|
||||
void setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6);
|
||||
|
||||
/*! Generates new ICE ufrag string. */
|
||||
static std::string createUfrag();
|
||||
|
||||
/*! Generates new ICE pwd string. */
|
||||
static std::string createPassword();
|
||||
|
||||
void setRemotePassword(const std::string& pwd, int streamId = -1);
|
||||
std::string remotePassword(int streamId = -1) const;
|
||||
void setRemoteUfrag(const std::string& ufrag, int streamId = -1);
|
||||
std::string remoteUfrag(int streamId = -1) const;
|
||||
|
||||
void refreshPwdUfrag();
|
||||
|
||||
void dump(std::ostream& output);
|
||||
bool mustRestart() const;
|
||||
bool findConcludePair(int stream, Candidate& local, Candidate& remote);
|
||||
int errorCode();
|
||||
Stream::CandidateVector* remoteCandidates(int stream);
|
||||
|
||||
NetworkAddress activeStunServer(int stream) const;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
396
src/libs/ice/ICESmartCount.h
Normal file
396
src/libs/ice/ICESmartCount.h
Normal file
@@ -0,0 +1,396 @@
|
||||
// Based on resiprocate's implementation of smart pointer, which is based on boost implementation
|
||||
// Its license is smth close to BSD
|
||||
|
||||
#ifndef __ICE_SMART_COUNT_H
|
||||
#define __ICE_SMART_COUNT_H
|
||||
|
||||
// Note: This implementation is a modified version of shared_count from
|
||||
// Boost.org
|
||||
//
|
||||
|
||||
#include <memory> // std::auto_ptr, std::allocator
|
||||
#include <functional> // std::less
|
||||
#include <exception> // std::exception
|
||||
#include <new> // std::bad_alloc
|
||||
#include <typeinfo> // std::type_info in get_deleter
|
||||
#include <cstddef> // std::size_t
|
||||
#include "ICESync.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
# pragma warn -8026 // Functions with excep. spec. are not expanded inline
|
||||
# pragma warn -8027 // Functions containing try are not expanded inline
|
||||
#endif
|
||||
|
||||
// verify that types are complete for increased safety
|
||||
template<class T> inline void checked_delete(T * x)
|
||||
{
|
||||
// intentionally complex - simplification causes regressions
|
||||
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
|
||||
(void) sizeof(type_must_be_complete);
|
||||
delete x;
|
||||
};
|
||||
|
||||
template<class T> struct checked_deleter
|
||||
{
|
||||
typedef void result_type;
|
||||
typedef T * argument_type;
|
||||
|
||||
void operator()(T * x) const
|
||||
{
|
||||
// resip:: disables ADL
|
||||
checked_delete(x);
|
||||
}
|
||||
};
|
||||
|
||||
// The standard library that comes with Borland C++ 5.5.1
|
||||
// defines std::exception and its members as having C calling
|
||||
// convention (-pc). When the definition of bad_weak_ptr
|
||||
// is compiled with -ps, the compiler issues an error.
|
||||
// Hence, the temporary #pragma option -pc below. The version
|
||||
// check is deliberately conservative.
|
||||
#if defined(__BORLANDC__) && __BORLANDC__ == 0x551
|
||||
# pragma option push -pc
|
||||
#endif
|
||||
|
||||
class bad_weak_ptr: public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
virtual char const * what() const throw()
|
||||
{
|
||||
return "resip::bad_weak_ptr";
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__BORLANDC__) && __BORLANDC__ == 0x551
|
||||
# pragma option pop
|
||||
#endif
|
||||
|
||||
class sp_counted_base
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
|
||||
sp_counted_base(): use_count_(1), weak_count_(1)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~sp_counted_base() // nothrow
|
||||
{
|
||||
}
|
||||
|
||||
// dispose() is called when use_count_ drops to zero, to release
|
||||
// the resources managed by *this.
|
||||
virtual void dispose() = 0; // nothrow
|
||||
|
||||
// destruct() is called when weak_count_ drops to zero.
|
||||
virtual void destruct() // nothrow
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void * get_deleter(std::type_info const & ti) = 0;
|
||||
|
||||
void add_ref_copy()
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
++use_count_;
|
||||
//GenericLog(Subsystem::SIP, resip::Log::Info, << "********* SharedCount::add_ref_copy: " << use_count_);
|
||||
}
|
||||
|
||||
void add_ref_lock()
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
// if(use_count_ == 0) throw(resip::bad_weak_ptr());
|
||||
if (use_count_ == 0) throw bad_weak_ptr();
|
||||
++use_count_;
|
||||
//GenericLog(Subsystem::SIP, resip::Log::Info, << "********* SharedCount::add_ref_lock: " << use_count_);
|
||||
}
|
||||
|
||||
void release() // nothrow
|
||||
{
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
long new_use_count = --use_count_;
|
||||
//GenericLog(Subsystem::SIP, resip::Log::Info, << "********* SharedCount::release: " << use_count_);
|
||||
|
||||
if(new_use_count != 0) return;
|
||||
}
|
||||
|
||||
dispose();
|
||||
weak_release();
|
||||
}
|
||||
|
||||
void weak_add_ref() // nothrow
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
++weak_count_;
|
||||
}
|
||||
|
||||
void weak_release() // nothrow
|
||||
{
|
||||
long new_weak_count;
|
||||
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
new_weak_count = --weak_count_;
|
||||
}
|
||||
|
||||
if(new_weak_count == 0)
|
||||
{
|
||||
destruct();
|
||||
}
|
||||
}
|
||||
|
||||
long use_count() const // nothrow
|
||||
{
|
||||
Lock lock(mMutex); (void)lock;
|
||||
return use_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
sp_counted_base(sp_counted_base const &);
|
||||
sp_counted_base & operator= (sp_counted_base const &);
|
||||
|
||||
long use_count_; // #shared
|
||||
long weak_count_; // #weak + (#shared != 0)
|
||||
|
||||
mutable Mutex mMutex;
|
||||
};
|
||||
|
||||
//
|
||||
// Borland's Codeguard trips up over the -Vx- option here:
|
||||
//
|
||||
#ifdef __CODEGUARD__
|
||||
# pragma option push -Vx-
|
||||
#endif
|
||||
|
||||
template<class P, class D> class sp_counted_base_impl: public sp_counted_base
|
||||
{
|
||||
private:
|
||||
|
||||
P ptr; // copy constructor must not throw
|
||||
D del; // copy constructor must not throw
|
||||
|
||||
sp_counted_base_impl(sp_counted_base_impl const &);
|
||||
sp_counted_base_impl & operator= (sp_counted_base_impl const &);
|
||||
|
||||
typedef sp_counted_base_impl<P, D> this_type;
|
||||
|
||||
public:
|
||||
|
||||
// pre: initial_use_count <= initial_weak_count, d(p) must not throw
|
||||
sp_counted_base_impl(P p, D d): ptr(p), del(d)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void dispose() // nothrow
|
||||
{
|
||||
del(ptr);
|
||||
}
|
||||
|
||||
virtual void * get_deleter(std::type_info const & ti)
|
||||
{
|
||||
return ti == typeid(D)? &del: 0;
|
||||
}
|
||||
|
||||
void * operator new(size_t)
|
||||
{
|
||||
return std::allocator<this_type>().allocate(1, static_cast<this_type *>(0));
|
||||
}
|
||||
|
||||
void operator delete(void * p)
|
||||
{
|
||||
std::allocator<this_type>().deallocate(static_cast<this_type *>(p), 1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class shared_count
|
||||
{
|
||||
private:
|
||||
|
||||
sp_counted_base * pi_;
|
||||
|
||||
public:
|
||||
|
||||
shared_count(): pi_(0) // nothrow
|
||||
{
|
||||
}
|
||||
|
||||
template<class P, class D> shared_count(P p, D d): pi_(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
pi_ = new sp_counted_base_impl<P, D>(p, d);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
d(p); // delete p
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// auto_ptr<Y> is special cased to provide the strong guarantee
|
||||
template<class Y>
|
||||
explicit shared_count(std::auto_ptr<Y> & r): pi_(new sp_counted_base_impl< Y *, checked_deleter<Y> >(r.get(), checked_deleter<Y>()))
|
||||
{
|
||||
r.release();
|
||||
}
|
||||
|
||||
~shared_count() // nothrow
|
||||
{
|
||||
if(pi_ != 0) pi_->release();
|
||||
}
|
||||
|
||||
shared_count(shared_count const & r): pi_(r.pi_) // nothrow
|
||||
{
|
||||
if(pi_ != 0) pi_->add_ref_copy();
|
||||
}
|
||||
|
||||
shared_count & operator= (shared_count const & r) // nothrow
|
||||
{
|
||||
sp_counted_base * tmp = r.pi_;
|
||||
if(tmp != 0) tmp->add_ref_copy();
|
||||
if(pi_ != 0) pi_->release();
|
||||
pi_ = tmp;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(shared_count & r) // nothrow
|
||||
{
|
||||
sp_counted_base * tmp = r.pi_;
|
||||
r.pi_ = pi_;
|
||||
pi_ = tmp;
|
||||
}
|
||||
|
||||
long use_count() const // nothrow
|
||||
{
|
||||
return pi_ != 0? pi_->use_count(): 0;
|
||||
}
|
||||
|
||||
bool unique() const // nothrow
|
||||
{
|
||||
return use_count() == 1;
|
||||
}
|
||||
|
||||
friend inline bool operator==(shared_count const & a, shared_count const & b)
|
||||
{
|
||||
return a.pi_ == b.pi_;
|
||||
}
|
||||
|
||||
friend inline bool operator<(shared_count const & a, shared_count const & b)
|
||||
{
|
||||
return std::less<sp_counted_base *>()(a.pi_, b.pi_);
|
||||
}
|
||||
|
||||
void * get_deleter(std::type_info const & ti) const
|
||||
{
|
||||
return pi_? pi_->get_deleter(ti): 0;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __CODEGUARD__
|
||||
# pragma option pop
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
# pragma warn .8027 // Functions containing try are not expanded inline
|
||||
# pragma warn .8026 // Functions with excep. spec. are not expanded inline
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Note: This implementation is a modified version of shared_count from
|
||||
// Boost.org
|
||||
//
|
||||
|
||||
/* ====================================================================
|
||||
*
|
||||
* Boost Software License - Version 1.0 - August 17th, 2003
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person or organization
|
||||
* obtaining a copy of the software and accompanying documentation covered by
|
||||
* this license (the "Software") to use, reproduce, display, distribute,
|
||||
* execute, and transmit the Software, and to prepare derivative works of the
|
||||
* Software, and to permit third-parties to whom the Software is furnished to
|
||||
* do so, all subject to the following:
|
||||
*
|
||||
* The copyright notices in the Software and this entire statement, including
|
||||
* the above license grant, this restriction and the following disclaimer,
|
||||
* must be included in all copies of the Software, in whole or in part, and
|
||||
* all derivative works of the Software, unless such copies or derivative
|
||||
* works are solely in the form of machine-executable object code generated by
|
||||
* a source language processor.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
|
||||
/* ====================================================================
|
||||
* The Vovida Software License, Version 1.0
|
||||
*
|
||||
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
|
||||
* and "Vovida Open Communication Application Library (VOCAL)" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact vocal@vovida.org.
|
||||
*
|
||||
* 4. Products derived from this software may not be called "VOCAL", nor
|
||||
* may "VOCAL" appear in their name, without prior written
|
||||
* permission of Vovida Networks, Inc.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
|
||||
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
|
||||
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
|
||||
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by Vovida
|
||||
* Networks, Inc. and many individuals on behalf of Vovida Networks,
|
||||
* Inc. For more information on Vovida Networks, Inc., please see
|
||||
* <http://www.vovida.org/>.
|
||||
*
|
||||
*/
|
||||
382
src/libs/ice/ICESmartPtr.h
Normal file
382
src/libs/ice/ICESmartPtr.h
Normal file
@@ -0,0 +1,382 @@
|
||||
// Based on resiprocate's implementation of smart pointer, which is based on boost implementation
|
||||
// Its license is smth close to BSD
|
||||
|
||||
#ifndef __ICE_SMART_PTR_H
|
||||
#define __ICE_SMART_PTR_H
|
||||
|
||||
#ifndef USE_NATIVE_SMARTPTR
|
||||
|
||||
#include "ICESmartCount.h"
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <algorithm> // for std::swap
|
||||
#include <functional> // for std::less
|
||||
#include <typeinfo> // for std::bad_cast
|
||||
#include <iosfwd> // for std::basic_ostream
|
||||
#include <cassert>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
|
||||
template<class T> class enable_shared_from_this;
|
||||
|
||||
struct static_cast_tag {};
|
||||
struct const_cast_tag {};
|
||||
struct dynamic_cast_tag {};
|
||||
struct polymorphic_cast_tag {};
|
||||
|
||||
template<class T> struct SmartPtr_traits
|
||||
{
|
||||
typedef T & reference;
|
||||
};
|
||||
|
||||
template<> struct SmartPtr_traits<void>
|
||||
{
|
||||
typedef void reference;
|
||||
};
|
||||
|
||||
template<> struct SmartPtr_traits<void const>
|
||||
{
|
||||
typedef void reference;
|
||||
};
|
||||
|
||||
template<> struct SmartPtr_traits<void volatile>
|
||||
{
|
||||
typedef void reference;
|
||||
};
|
||||
|
||||
template<> struct SmartPtr_traits<void const volatile>
|
||||
{
|
||||
typedef void reference;
|
||||
};
|
||||
|
||||
// enable_shared_from_this support
|
||||
|
||||
template<class T, class Y> void sp_enable_shared_from_this( shared_count const & pn, enable_shared_from_this<T> const * pe, Y const * px )
|
||||
{
|
||||
if(pe != 0) pe->_internal_weak_this._internal_assign(const_cast<Y*>(px), pn);
|
||||
}
|
||||
|
||||
inline void sp_enable_shared_from_this( shared_count const & /*pn*/, ... )
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// SmartPtr
|
||||
//
|
||||
// Reference counted copy semantics.
|
||||
// The object pointed to is deleted when the last SmartPtr pointing to it
|
||||
// is destroyed or reset.
|
||||
//
|
||||
|
||||
template<class T> class SmartPtr
|
||||
{
|
||||
private:
|
||||
|
||||
// Borland 5.5.1 specific workaround
|
||||
typedef SmartPtr<T> this_type;
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
typedef T value_type;
|
||||
typedef T * pointer;
|
||||
typedef typename SmartPtr_traits<T>::reference reference;
|
||||
|
||||
SmartPtr(): px(0), pn() // never throws in 1.30+
|
||||
{
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
explicit SmartPtr(Y * p): px(p), pn(p, checked_deleter<Y>()) // Y must be complete
|
||||
{
|
||||
sp_enable_shared_from_this( pn, p, p );
|
||||
}
|
||||
|
||||
//
|
||||
// Requirements: D's copy constructor must not throw
|
||||
//
|
||||
// SmartPtr will release p by calling d(p)
|
||||
//
|
||||
|
||||
template<class Y, class D> SmartPtr(Y * p, D d): px(p), pn(p, d)
|
||||
{
|
||||
sp_enable_shared_from_this( pn, p, p );
|
||||
}
|
||||
|
||||
// generated copy constructor, assignment, destructor are fine...
|
||||
|
||||
// except that Borland C++ has a bug, and g++ with -Wsynth warns
|
||||
#if defined(__BORLANDC__) || defined(__GNUC__)
|
||||
SmartPtr & operator=(SmartPtr const & r) // never throws
|
||||
{
|
||||
px = r.px;
|
||||
pn = r.pn; // shared_count::op= doesn't throw
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Y>
|
||||
SmartPtr(SmartPtr<Y> const & r): px(r.px), pn(r.pn) // never throws
|
||||
{
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr(SmartPtr<Y> const & r, static_cast_tag): px(static_cast<element_type *>(r.px)), pn(r.pn)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr(SmartPtr<Y> const & r, const_cast_tag): px(const_cast<element_type *>(r.px)), pn(r.pn)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr(SmartPtr<Y> const & r, dynamic_cast_tag): px(dynamic_cast<element_type *>(r.px)), pn(r.pn)
|
||||
{
|
||||
if(px == 0) // need to allocate new counter -- the cast failed
|
||||
{
|
||||
pn = /*resip::*/shared_count();
|
||||
}
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr(SmartPtr<Y> const & r, polymorphic_cast_tag): px(dynamic_cast<element_type *>(r.px)), pn(r.pn)
|
||||
{
|
||||
if(px == 0)
|
||||
{
|
||||
throw std::bad_cast();
|
||||
}
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
explicit SmartPtr(std::auto_ptr<Y> & r): px(r.get()), pn()
|
||||
{
|
||||
Y * tmp = r.get();
|
||||
pn = shared_count(r);
|
||||
sp_enable_shared_from_this( pn, tmp, tmp );
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr & operator=(SmartPtr<Y> const & r) // never throws
|
||||
{
|
||||
px = r.px;
|
||||
pn = r.pn; // shared_count::op= doesn't throw
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Y>
|
||||
SmartPtr & operator=(std::auto_ptr<Y> & r)
|
||||
{
|
||||
this_type(r).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() // never throws in 1.30+
|
||||
{
|
||||
this_type().swap(*this);
|
||||
}
|
||||
|
||||
template<class Y> void reset(Y * p) // Y must be complete
|
||||
{
|
||||
assert(p == 0 || p != px); // catch self-reset errors
|
||||
this_type(p).swap(*this);
|
||||
}
|
||||
|
||||
template<class Y, class D> void reset(Y * p, D d)
|
||||
{
|
||||
this_type(p, d).swap(*this);
|
||||
}
|
||||
|
||||
reference operator* () const // never throws
|
||||
{
|
||||
assert(px != 0);
|
||||
return *px;
|
||||
}
|
||||
|
||||
T * operator-> () const // never throws
|
||||
{
|
||||
//assert(px != 0);
|
||||
return px;
|
||||
}
|
||||
|
||||
T * get() const // never throws
|
||||
{
|
||||
return px;
|
||||
}
|
||||
|
||||
// implicit conversion to "bool"
|
||||
#if defined(__SUNPRO_CC) // BOOST_WORKAROUND(__SUNPRO_CC, <= 0x530)
|
||||
operator bool () const
|
||||
{
|
||||
return px != 0;
|
||||
}
|
||||
#elif defined(__MWERKS__) // BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003))
|
||||
typedef T * (this_type::*unspecified_bool_type)() const;
|
||||
operator unspecified_bool_type() const // never throws
|
||||
{
|
||||
return px == 0? 0: &this_type::get;
|
||||
}
|
||||
#else
|
||||
typedef T * this_type::*unspecified_bool_type;
|
||||
operator unspecified_bool_type() const // never throws
|
||||
{
|
||||
return px == 0? 0: &this_type::px;
|
||||
}
|
||||
#endif
|
||||
|
||||
// operator! is redundant, but some compilers need it
|
||||
bool operator! () const // never throws
|
||||
{
|
||||
return px == 0;
|
||||
}
|
||||
|
||||
bool unique() const // never throws
|
||||
{
|
||||
return pn.unique();
|
||||
}
|
||||
|
||||
long use_count() const // never throws
|
||||
{
|
||||
return pn.use_count();
|
||||
}
|
||||
|
||||
void swap(SmartPtr<T> & other) // never throws
|
||||
{
|
||||
std::swap(px, other.px);
|
||||
pn.swap(other.pn);
|
||||
}
|
||||
|
||||
template<class Y> bool _internal_less(SmartPtr<Y> const & rhs) const
|
||||
{
|
||||
return pn < rhs.pn;
|
||||
}
|
||||
|
||||
void * _internal_get_deleter(std::type_info const & ti) const
|
||||
{
|
||||
return pn.get_deleter(ti);
|
||||
}
|
||||
|
||||
// Tasteless as this may seem, making all members public allows member templates
|
||||
// to work in the absence of member template friends. (Matthew Langston)
|
||||
|
||||
private:
|
||||
|
||||
template<class Y> friend class SmartPtr;
|
||||
|
||||
T * px; // contained pointer
|
||||
shared_count pn; // reference counter
|
||||
|
||||
}; // SmartPtr
|
||||
|
||||
template<class T, class U> inline bool operator==(SmartPtr<T> const & a, SmartPtr<U> const & b)
|
||||
{
|
||||
return a.get() == b.get();
|
||||
}
|
||||
|
||||
template<class T, class U> inline bool operator!=(SmartPtr<T> const & a, SmartPtr<U> const & b)
|
||||
{
|
||||
return a.get() != b.get();
|
||||
}
|
||||
|
||||
#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96
|
||||
|
||||
// Resolve the ambiguity between our op!= and the one in rel_ops
|
||||
|
||||
template<class T> inline bool operator!=(SmartPtr<T> const & a, SmartPtr<T> const & b)
|
||||
{
|
||||
return a.get() != b.get();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<class T, class U> inline bool operator<(SmartPtr<T> const & a, SmartPtr<U> const & b)
|
||||
{
|
||||
return a._internal_less(b);
|
||||
}
|
||||
|
||||
template<class T> inline void swap(SmartPtr<T> & a, SmartPtr<T> & b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> static_pointer_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, static_cast_tag());
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> const_pointer_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, const_cast_tag());
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> dynamic_pointer_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, dynamic_cast_tag());
|
||||
}
|
||||
|
||||
// shared_*_cast names are deprecated. Use *_pointer_cast instead.
|
||||
|
||||
template<class T, class U> SmartPtr<T> shared_static_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, static_cast_tag());
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> shared_dynamic_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, dynamic_cast_tag());
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> shared_polymorphic_cast(SmartPtr<U> const & r)
|
||||
{
|
||||
return SmartPtr<T>(r, polymorphic_cast_tag());
|
||||
}
|
||||
|
||||
template<class T, class U> SmartPtr<T> shared_polymorphic_downcast(SmartPtr<U> const & r)
|
||||
{
|
||||
assert(dynamic_cast<T *>(r.get()) == r.get());
|
||||
return shared_static_cast<T>(r);
|
||||
}
|
||||
|
||||
template<class T> inline T * get_pointer(SmartPtr<T> const & p)
|
||||
{
|
||||
return p.get();
|
||||
}
|
||||
|
||||
// operator<<
|
||||
#if defined(__GNUC__) && (__GNUC__ < 3)
|
||||
template<class Y> std::ostream & operator<< (std::ostream & os, SmartPtr<Y> const & p)
|
||||
{
|
||||
os << p.get();
|
||||
return os;
|
||||
}
|
||||
#else
|
||||
template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, SmartPtr<Y> const & p)
|
||||
{
|
||||
os << p.get();
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
// get_deleter (experimental)
|
||||
#if (defined(__GNUC__) && (__GNUC__ < 3)) || (defined(__EDG_VERSION__) && (__EDG_VERSION__ <= 238))
|
||||
// g++ 2.9x doesn't allow static_cast<X const *>(void *)
|
||||
// apparently EDG 2.38 also doesn't accept it
|
||||
template<class D, class T> D * get_deleter(SmartPtr<T> const & p)
|
||||
{
|
||||
void const * q = p._internal_get_deleter(typeid(D));
|
||||
return const_cast<D *>(static_cast<D const *>(q));
|
||||
}
|
||||
#else
|
||||
template<class D, class T> D * get_deleter(SmartPtr<T> const & p)
|
||||
{
|
||||
return static_cast<D *>(p._internal_get_deleter(typeid(D)));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
32
src/libs/ice/ICESocket.h
Normal file
32
src/libs/ice/ICESocket.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_SOCKET_H
|
||||
#define __ICE_SOCKET_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICETypes.h"
|
||||
#include <string>
|
||||
|
||||
namespace ice {
|
||||
|
||||
struct ICESocket
|
||||
{
|
||||
std::string mHostIP;
|
||||
unsigned short mPort;
|
||||
SOCKET mHandle;
|
||||
unsigned int mPriority;
|
||||
|
||||
ICESocket()
|
||||
:mPort(0), mHandle(INVALID_SOCKET), mPriority(0)
|
||||
{}
|
||||
|
||||
~ICESocket()
|
||||
{}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
2895
src/libs/ice/ICEStream.cpp
Normal file
2895
src/libs/ice/ICEStream.cpp
Normal file
File diff suppressed because it is too large
Load Diff
367
src/libs/ice/ICEStream.h
Normal file
367
src/libs/ice/ICEStream.h
Normal file
@@ -0,0 +1,367 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_STREAM_H
|
||||
#define __ICE_STREAM_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ICEBox.h"
|
||||
#include "ICECandidate.h"
|
||||
#include "ICEStunConfig.h"
|
||||
#include "ICEAction.h"
|
||||
#include "ICEStunTransaction.h"
|
||||
#include "ICECheckList.h"
|
||||
#include "ICEBinding.h"
|
||||
#include "ICETime.h"
|
||||
#include "ICETransactionList.h"
|
||||
#include "ICERelaying.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
enum Failover
|
||||
{
|
||||
FailoverOn = 1,
|
||||
FailoverOff = 2
|
||||
};
|
||||
|
||||
// Session state
|
||||
enum RunningState
|
||||
{
|
||||
None = 0,
|
||||
CandidateGathering,
|
||||
EliminateRedudand,
|
||||
ComputingFoundations,
|
||||
StartingKeepAlives,
|
||||
PrioritizingCandidates,
|
||||
ChoosingDefault,
|
||||
CreatingSDP,
|
||||
ConnCheck,
|
||||
Failed,
|
||||
Success
|
||||
};
|
||||
|
||||
extern const char* RunningStateToString(RunningState state);
|
||||
|
||||
// Smart pointer to ICE stream type
|
||||
typedef SmartPtr<Stream> ICEStreamPtr;
|
||||
|
||||
|
||||
// Map of used channel TURN prefixes as key and corresponding address as value
|
||||
typedef std::map<TurnPrefix, NetworkAddress> TurnPrefixMap;
|
||||
|
||||
struct Component
|
||||
{
|
||||
void* mTag;
|
||||
unsigned short mPort4;
|
||||
unsigned short mPort6;
|
||||
unsigned mNominationWaitIntervalStartTime;
|
||||
Component()
|
||||
:mTag(NULL), mPort4(0), mPort6(0), mNominationWaitIntervalStartTime(0)
|
||||
{}
|
||||
|
||||
~Component()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<int, Component> ComponentMap;
|
||||
|
||||
// Represents single ICE stream with single or multiple components (sockets)
|
||||
struct Stream
|
||||
{
|
||||
// Stream id
|
||||
int mId;
|
||||
|
||||
// Stack ID. Used for debugging purposes.
|
||||
int mStackId;
|
||||
|
||||
// Map of component ID -> user tag
|
||||
ComponentMap mComponentMap;
|
||||
|
||||
// List of local candidates and list of remote candidates
|
||||
typedef std::vector<Candidate> CandidateVector;
|
||||
CandidateVector mLocalCandidate, mRemoteCandidate, mRemoteRelayedCandidate;
|
||||
|
||||
// Check list
|
||||
CheckList mCheckList;
|
||||
|
||||
// Logger
|
||||
Logger* mLogger;
|
||||
|
||||
// Foundation generator value to provide foundation value for peer reflexive candidates
|
||||
unsigned int mFoundationGenerator;
|
||||
|
||||
// Active STUN transactions. This vector includes connectivity check transactions and gather candidates transactions.
|
||||
// Keepalive transactions are stored in mKeepAliveList
|
||||
TransactionList mActiveChecks;
|
||||
|
||||
// ICE agent role
|
||||
AgentRole mAgentRole;
|
||||
|
||||
// Map of established relay channels
|
||||
TurnPrefixMap mTurnPrefixMap;
|
||||
|
||||
// Current state of media stream
|
||||
RunningState mState;
|
||||
|
||||
// Used configuration
|
||||
StackConfig mConfig;
|
||||
|
||||
// Helper vector used to prioritize candidates
|
||||
std::vector<unsigned> mComponentLimit;
|
||||
|
||||
// Default candidate list - one per each component
|
||||
std::map<int, Candidate> mDefaultCandidate;
|
||||
|
||||
// Local password/ufrag
|
||||
std::string mLocalPwd;
|
||||
std::string mLocalUfrag;
|
||||
|
||||
// Remote password/ufrag
|
||||
std::string mRemotePwd;
|
||||
std::string mRemoteUfrag;
|
||||
|
||||
// Marks if selected during connectivity checks default IP list differs from used to generate offer
|
||||
bool mDefaultIPChanged;
|
||||
|
||||
// Marks if checks was ok for this stream and each component has valid nominated pair
|
||||
bool mCanTransmit;
|
||||
|
||||
std::vector<ByteBuffer*> mResponseQueue;
|
||||
// Tie breaker
|
||||
std::string mTieBreaker;
|
||||
|
||||
// Timer to schedule connection checks (CC)
|
||||
ICEScheduleTimer mScheduleTimer;
|
||||
|
||||
// Counter of TURN allocations
|
||||
int mTurnAllocated;
|
||||
|
||||
// Last error code during gathering/checks
|
||||
int mErrorCode;
|
||||
|
||||
// Cached realm and nonce for used TURN server
|
||||
std::string mCachedRealm, mCachedNonce;
|
||||
|
||||
// Timestamp of nomination waiting timer. Used to accumulate valid pairs and chose the "best" of them.
|
||||
unsigned mNominationWaitStartTime;
|
||||
|
||||
// Number of finished failover gathering requests
|
||||
int mFailoverRequestsFinished;
|
||||
|
||||
// Failover ID generator. Failover transactions which share the same goal share the same ID.
|
||||
int mFailoverIdGenerator;
|
||||
|
||||
struct BoundChannel
|
||||
{
|
||||
int mComponentId;
|
||||
TurnPrefix mPrefix;
|
||||
NetworkAddress mPeerAddress;
|
||||
int mResultCode;
|
||||
};
|
||||
typedef std::vector<BoundChannel> BoundChannelList;
|
||||
BoundChannelList mBoundChannelList;
|
||||
|
||||
// Create Allocate transaction and associate with passed action
|
||||
struct AllocateOptions
|
||||
{
|
||||
NetworkAddress mServerAddress;
|
||||
PAction mActionOnFinish;
|
||||
int mComponent = 0;
|
||||
Failover mFailoverOption = FailoverOn;
|
||||
int mWireFamily = AF_INET;
|
||||
int mAllocFamily = AF_INET;
|
||||
int mFailoverId = 0;
|
||||
};
|
||||
void allocate(const AllocateOptions& options);
|
||||
|
||||
// Create Bind transaction and associate with passed action
|
||||
void bind(const NetworkAddress& addr, PAction action, bool auth, int component, Failover failover);
|
||||
|
||||
// Searches for local candidate with specified address
|
||||
unsigned findLocalCandidate(const NetworkAddress& addr);
|
||||
|
||||
// Cancels found transaction.
|
||||
void cancelCheck(CandidatePair& p);
|
||||
|
||||
// Cancels allocations
|
||||
void cancelAllocations();
|
||||
|
||||
// Searches for remote candidate with specified remote address and corresponding to local port number
|
||||
unsigned findRemoteCandidate(const NetworkAddress& addr, int componentID);
|
||||
|
||||
// Process incoming binding request
|
||||
void handleBindingRequest(ServerBinding& binding, ByteBuffer& buffer, int component);
|
||||
|
||||
// Handles incoming data in gathering candidate stage.
|
||||
bool handleCgIn(StunMessage& msg, NetworkAddress& address);
|
||||
|
||||
// Handle eliminate redudand stage
|
||||
void Handle_ER();
|
||||
|
||||
// Compute foundations
|
||||
void Handle_CF();
|
||||
|
||||
// Starting keep alive timers
|
||||
void Handle_SKA();
|
||||
|
||||
// Prioritize candidates
|
||||
void Handle_PC();
|
||||
|
||||
// Choosing default candidate
|
||||
void Handle_CD();
|
||||
|
||||
// Handles keepalive messages
|
||||
//bool handleKeepAlivesIn(StunMessage& msg, NetworkAddress& address);
|
||||
|
||||
// Handles incoming messages in connectivity checks stage
|
||||
// This method uses Handle_KA_In to perform keepalive handling
|
||||
bool handleConnChecksIn(StunMessage& msg, NetworkAddress& address);
|
||||
|
||||
// Handle incoming Bind request
|
||||
void handleIncomingRequest(StunMessage& msg, ByteBuffer& buffer, int component);
|
||||
|
||||
// Chooses default candidate for specified component ID
|
||||
Candidate findDefaultCandidate(int componentID);
|
||||
|
||||
//Performs 8.1.2. Updating States
|
||||
void checkNominated(int componentID);
|
||||
void checkNominated();
|
||||
bool handleRoleConflict(ServerBinding& binding);
|
||||
|
||||
//Checks active transactions (keepalive and ordinary) for timeouts
|
||||
void isTimeout();
|
||||
|
||||
// Checks for next byte buffer for sending
|
||||
ByteBuffer* handleConnChecksOut();
|
||||
|
||||
// Creates connectivity check request for specified pair.
|
||||
Transaction* createCheckRequest(PCandidatePair& p);
|
||||
|
||||
// Checks for TURN channel prefix by specified peer's address.
|
||||
// Returns zero prefix if it is not found.
|
||||
TurnPrefix findTurnPrefixByAddress(NetworkAddress& peerAddress);
|
||||
|
||||
// Create candidate pair in check list.
|
||||
void createCheckList();
|
||||
|
||||
// Starts connectivity checks - calls Create_CheckList to create check list + starts retransmission timer
|
||||
void startChecks();
|
||||
|
||||
// Stops connectivity checks and gathering requests
|
||||
void stopChecks();
|
||||
|
||||
// Initiates ChannelBind transaction to TURN server.
|
||||
TurnPrefix bindChannel(const NetworkAddress& peerAddress, int component, ChannelBoundCallback* cb = NULL);
|
||||
bool isChannelBindingFailed(int component, TurnPrefix prefix);
|
||||
void removeBindingResult(int component);
|
||||
|
||||
// Attempts to free allocation.
|
||||
void freeAllocation(int component, DeleteAllocationCallback* cb = NULL);
|
||||
|
||||
// Searches for local server reflexive candidate with specified component ID and returns its external address.
|
||||
NetworkAddress reflexiveAddress(int componentID);
|
||||
|
||||
/*! Searches for local server relayed candidate with specified component ID and returns its external address. */
|
||||
NetworkAddress relayedAddress(int componentID);
|
||||
|
||||
// Searches for remote server relayed candidate with specified component
|
||||
NetworkAddress remoteRelayedAddress(int component);
|
||||
|
||||
// Searches for remote server reflexive candidate with specified component
|
||||
NetworkAddress remoteReflexiveAddress(int component);
|
||||
|
||||
|
||||
Stream();
|
||||
~Stream();
|
||||
|
||||
// Sets config
|
||||
void setConfig(StackConfig& config);
|
||||
|
||||
// Sets ICE agent role - Controlled or Controlling
|
||||
void setAgentRole(AgentRole role);
|
||||
|
||||
// Sets local password
|
||||
void setLocalPwd(const std::string& pwd);
|
||||
|
||||
// Sets local ufrag
|
||||
void setLocalUfrag(const std::string& ufrag);
|
||||
|
||||
// Sets remote password
|
||||
void setRemotePwd(const std::string& pwd);
|
||||
|
||||
// Sets remote ufrag
|
||||
void setRemoteUfrag(const std::string& ufrag);
|
||||
|
||||
// Sets tie breaker
|
||||
void setTieBreaker(const std::string& tieBreaker);
|
||||
|
||||
// Adds new component to stream
|
||||
int addComponent(void* tag, unsigned short port4, unsigned short port6);
|
||||
|
||||
// Gathers candidates for stream
|
||||
void gatherCandidates();
|
||||
|
||||
// Returns reference to check list
|
||||
CheckList& checkList();
|
||||
|
||||
// Check is stream owns specified port number
|
||||
bool hasPortNumber(int family, unsigned short portNumber, int* component = NULL);
|
||||
|
||||
// Processes incoming data
|
||||
bool processData(StunMessage& msg, ByteBuffer& buffer, int component);
|
||||
|
||||
void createOfferSdp(std::vector<std::string>& defaultIP, std::vector<unsigned short>& defaultPort,
|
||||
std::vector<std::string>& candidateList);
|
||||
|
||||
NetworkAddress defaultAddress(int componentID);
|
||||
void candidateList(int componentID, std::vector<std::string>& candidateList);
|
||||
|
||||
PByteBuffer getDataToSend(bool& response, int& component, void*&tag);
|
||||
|
||||
// Constructs new keepalive transaction and adds to mKeepAlives
|
||||
void addKeepAliveCheck(CandidatePair& p);
|
||||
|
||||
// Processes SDP offer, adding remote candidates from it
|
||||
bool processSdpOffer(std::vector<std::string>& candidateList, std::string defaultIP, unsigned short defaultPort, bool deleteRelayed);
|
||||
|
||||
// Checks if remote candidate list contains specified address
|
||||
bool candidateListContains(const std::string& remoteIP, unsigned short remotePort);
|
||||
|
||||
// Restarts stream's connectivity checks
|
||||
void restart();
|
||||
|
||||
// Clears the stack; resets state to None. The free allocation requests are left in the queue
|
||||
void clear();
|
||||
|
||||
// Deletes existing connectivity checks and resets turn allocation counters
|
||||
void clearForRestart(bool localNetworkChanged);
|
||||
|
||||
// Returns resolved address of remote party identified by its componmentID
|
||||
NetworkAddress remoteAddress(int component);
|
||||
NetworkAddress localAddress(int component);
|
||||
|
||||
void installPermissions(int component = -1, const NetworkAddress& addr = NetworkAddress(), InstallPermissionsCallback* cb = NULL);
|
||||
void dump(std::ostream& output);
|
||||
bool findConcludePair(Candidate& local, Candidate& remote);
|
||||
int findComponentIdByPort(unsigned short port, int family);
|
||||
Transaction* runCheckList(CandidatePair::Role role, CandidatePair::State state);
|
||||
void deleteTransactionAt(unsigned index);
|
||||
void clearChecks();
|
||||
|
||||
void dumpLocalCandidateList();
|
||||
void processStateChain();
|
||||
|
||||
// Disables (removes) active transactions responsible for gathering candidates - binding & allocating
|
||||
void removeGatherRequests(int component, int failoverId, Transaction* validOne);
|
||||
void unfreeze(const char* foundation);
|
||||
void nominatePair(PCandidatePair& p);
|
||||
bool ressurectAllocation(int component);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
956
src/libs/ice/ICEStunAttributes.cpp
Normal file
956
src/libs/ice/ICEStunAttributes.cpp
Normal file
@@ -0,0 +1,956 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICENetworkHelper.h"
|
||||
#include <stdexcept>
|
||||
using namespace ice;
|
||||
|
||||
|
||||
|
||||
// --- MappedAddress ---
|
||||
MappedAddress::MappedAddress()
|
||||
{
|
||||
}
|
||||
|
||||
MappedAddress::~MappedAddress()
|
||||
{
|
||||
}
|
||||
|
||||
int MappedAddress::type() const
|
||||
{
|
||||
return StunAttribute::MappedAddress;
|
||||
}
|
||||
|
||||
NetworkAddress& MappedAddress::address()
|
||||
{
|
||||
return mAddress;
|
||||
}
|
||||
|
||||
void MappedAddress::buildPacket(BufferWriter& writer)
|
||||
{
|
||||
writer.writeUChar(0);
|
||||
writer.writeUChar(mAddress.stunType());
|
||||
writer.writeUShort(mAddress.port());
|
||||
|
||||
switch (mAddress.stunType())
|
||||
{
|
||||
case IPv4:
|
||||
writer.writeBuffer(&mAddress.sockaddr4()->sin_addr, 4);
|
||||
break;
|
||||
|
||||
case IPv6:
|
||||
writer.writeBuffer(&mAddress.sockaddr6()->sin6_addr, 16);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool MappedAddress::parsePacket(BufferReader& reader)
|
||||
{
|
||||
// Dequeue zero byte and ignore it
|
||||
/*uint8_t zeroByte = */reader.readUChar();
|
||||
|
||||
// Dequeue family
|
||||
mAddress.setStunType(reader.readUChar());
|
||||
|
||||
// Dequeue port
|
||||
mAddress.setPort(reader.readUShort());
|
||||
|
||||
// Deqeueue IP
|
||||
mAddress.setIp(reader.readIp(mAddress.stunType() == IPv4 ? AF_INET : AF_INET6));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MappedAddress::dump(std::ostream& output)
|
||||
{
|
||||
output << "MappedAddress " << mAddress.toStdString();
|
||||
}
|
||||
|
||||
|
||||
//----------- XorMappedAddress ---------------
|
||||
XorMappedAddress::XorMappedAddress()
|
||||
{}
|
||||
|
||||
XorMappedAddress::~XorMappedAddress()
|
||||
{}
|
||||
|
||||
int XorMappedAddress::type() const
|
||||
{
|
||||
return StunAttribute::XorMappedAddress;
|
||||
}
|
||||
|
||||
|
||||
void XorMappedAddress::buildPacket(BufferWriter& writer)
|
||||
{
|
||||
// Queue zero byte
|
||||
writer.writeUChar(0);
|
||||
|
||||
// Queue family
|
||||
writer.writeUChar(mAddress.stunType());
|
||||
|
||||
// Xor&queue port
|
||||
uint16_t port = mAddress.port() ^ 0x2112;
|
||||
writer.writeUShort(port);
|
||||
|
||||
// Xor&queue ip
|
||||
unsigned int ip4 = 0;
|
||||
uint32_t ip6[4];
|
||||
switch (mAddress.stunType())
|
||||
{
|
||||
case IPv4:
|
||||
ip4 = mAddress.sockaddr4()->sin_addr.s_addr; //this gets ip in network byte order
|
||||
ip4 = ntohl(ip4); // get host byte order
|
||||
ip4 ^= 0x2112A442;
|
||||
writer.writeUInt(ip4); // It will convert again to network byte order
|
||||
break;
|
||||
|
||||
case IPv6:
|
||||
// Get copy of address in network byte order
|
||||
memcpy(&ip6, &mAddress.sockaddr6()->sin6_addr, 16);
|
||||
|
||||
for (int i=0; i<3; i++)
|
||||
ip6[i] ^= htonl(0x2112A442);
|
||||
|
||||
writer.writeBuffer(ip6, 16);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool XorMappedAddress::parsePacket(BufferReader& reader)
|
||||
{
|
||||
// Dequeue zero byte and ignore it
|
||||
reader.readUChar();
|
||||
|
||||
// Dequeue family
|
||||
mAddress.setStunType(reader.readUChar());
|
||||
|
||||
// Dequeue port
|
||||
mAddress.setPort(reader.readUShort() ^ 0x2112);
|
||||
|
||||
// Deqeueue IP
|
||||
unsigned int ip4;
|
||||
union
|
||||
{
|
||||
uint32_t ip6[4];
|
||||
in6_addr addr6;
|
||||
} v6;
|
||||
|
||||
switch (mAddress.stunType())
|
||||
{
|
||||
case IPv4:
|
||||
ip4 = htonl(reader.readUInt() ^ 0x2112A442);
|
||||
mAddress.setIp(ip4);
|
||||
break;
|
||||
|
||||
case IPv6:
|
||||
reader.readBuffer(v6.ip6, 16);
|
||||
// XOR buffer
|
||||
for (int i=0; i<3; i++)
|
||||
v6.ip6[i] ^= htonl(0x2112A442);
|
||||
mAddress.setIp(v6.addr6);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XorMappedAddress::dump(std::ostream &output)
|
||||
{
|
||||
output << "XorMappedAddress " << mAddress.toStdString();
|
||||
}
|
||||
|
||||
//------------ StringAttr -----------------------
|
||||
StringAttr::StringAttr()
|
||||
{
|
||||
}
|
||||
StringAttr::~StringAttr()
|
||||
{
|
||||
}
|
||||
|
||||
int StringAttr::type() const
|
||||
{
|
||||
return StunAttribute::UnknownAttributes;
|
||||
}
|
||||
|
||||
std::string StringAttr::value() const
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
void StringAttr::setValue(const std::string& value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
void StringAttr::buildPacket(BufferWriter& writer)
|
||||
{
|
||||
if (!mValue.empty())
|
||||
writer.writeBuffer(mValue.c_str(), mValue.length());
|
||||
}
|
||||
|
||||
bool StringAttr::parsePacket(BufferReader& reader)
|
||||
{
|
||||
char temp[1024]; memset(temp, 0, sizeof temp);
|
||||
reader.readBuffer(temp, sizeof temp);
|
||||
mValue = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StringAttr::dump(std::ostream& output)
|
||||
{
|
||||
output << "StringAttr " << mValue;
|
||||
}
|
||||
|
||||
//----------- Username ---------------------------------
|
||||
Username::Username()
|
||||
{}
|
||||
|
||||
Username::~Username()
|
||||
{}
|
||||
|
||||
int Username::type() const
|
||||
{
|
||||
return StunAttribute::Username;
|
||||
}
|
||||
|
||||
void Username::dump(std::ostream &output)
|
||||
{
|
||||
output << "Username " << value();
|
||||
}
|
||||
|
||||
//----------- MessageIntegrity -------------------------
|
||||
MessageIntegrity::MessageIntegrity()
|
||||
{
|
||||
memset(mValue, 0, sizeof(mValue));
|
||||
}
|
||||
MessageIntegrity::~MessageIntegrity()
|
||||
{
|
||||
}
|
||||
|
||||
int MessageIntegrity::type() const
|
||||
{
|
||||
return StunAttribute::MessageIntegrity;
|
||||
}
|
||||
|
||||
void MessageIntegrity::setValue(const void* data)
|
||||
{
|
||||
if (data)
|
||||
memcpy(mValue, data, 20);
|
||||
}
|
||||
|
||||
const void* MessageIntegrity::value() const
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
void MessageIntegrity::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeBuffer(mValue, 20);
|
||||
}
|
||||
|
||||
bool MessageIntegrity::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.readBuffer(mValue, 20);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageIntegrity::dump(std::ostream &output)
|
||||
{
|
||||
ByteBuffer buffer(mValue, 20);
|
||||
output << "MessageIntegrity " << buffer.hexstring();
|
||||
}
|
||||
|
||||
//--------------- Fingerprint ----------------
|
||||
Fingerprint::Fingerprint()
|
||||
{
|
||||
mCRC32 = 0;
|
||||
}
|
||||
|
||||
Fingerprint::~Fingerprint()
|
||||
{
|
||||
}
|
||||
|
||||
int Fingerprint::type() const
|
||||
{
|
||||
return StunAttribute::Fingerprint;
|
||||
}
|
||||
|
||||
void Fingerprint::setCrc32(unsigned int crc)
|
||||
{
|
||||
mCRC32 = crc;
|
||||
}
|
||||
|
||||
unsigned int Fingerprint::crc32() const
|
||||
{
|
||||
return mCRC32;
|
||||
}
|
||||
|
||||
void Fingerprint::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUInt(mCRC32);
|
||||
}
|
||||
|
||||
bool Fingerprint::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mCRC32 = stream.readUInt();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Fingerprint::dump(std::ostream &output)
|
||||
{
|
||||
output << "Fingerprint " << mCRC32;
|
||||
}
|
||||
|
||||
//---------------- ErrorCode ---------------
|
||||
ErrorCode::ErrorCode()
|
||||
{
|
||||
mErrorCode = 0;
|
||||
}
|
||||
|
||||
ErrorCode::~ErrorCode()
|
||||
{
|
||||
}
|
||||
|
||||
int ErrorCode::type() const
|
||||
{
|
||||
return StunAttribute::ErrorCode;
|
||||
}
|
||||
|
||||
void ErrorCode::setErrorCode(int errorCode)
|
||||
{
|
||||
mErrorCode = errorCode;
|
||||
}
|
||||
|
||||
int ErrorCode::errorCode() const
|
||||
{
|
||||
return mErrorCode;
|
||||
}
|
||||
|
||||
void ErrorCode::setErrorPhrase(const std::string& phrase)
|
||||
{
|
||||
mErrorPhrase = phrase;
|
||||
}
|
||||
|
||||
std::string ErrorCode::errorPhrase() const
|
||||
{
|
||||
return mErrorPhrase;
|
||||
}
|
||||
|
||||
void ErrorCode::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUShort(0);
|
||||
|
||||
uint8_t b = 0;
|
||||
// Get hundreds digit
|
||||
int digit = mErrorCode / 100;
|
||||
b = (digit & 4 ? 1 : 0) << 2;
|
||||
b |= (digit & 2 ? 1 : 0) << 1;
|
||||
b |= (digit & 1 ? 1 : 0);
|
||||
stream.writeUChar(b);
|
||||
|
||||
stream.writeUChar(mErrorCode % 100);
|
||||
|
||||
if (!mErrorPhrase.empty())
|
||||
stream.writeBuffer(mErrorPhrase.c_str(), mErrorPhrase.length());
|
||||
}
|
||||
|
||||
bool ErrorCode::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.readUShort();
|
||||
unsigned char _class = stream.readUChar();
|
||||
unsigned char _number = stream.readUChar();
|
||||
|
||||
mErrorCode = _class * 100 + _number;
|
||||
char temp[1024]; memset(temp, 0, sizeof temp);
|
||||
stream.readBuffer(temp, sizeof temp);
|
||||
mErrorPhrase = std::string(temp);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ErrorCode::dump(std::ostream &output)
|
||||
{
|
||||
output << "ErrorCode " << mErrorCode << " " << mErrorPhrase;
|
||||
}
|
||||
|
||||
//----------------- Realm ------------------------
|
||||
Realm::Realm()
|
||||
{}
|
||||
|
||||
Realm::~Realm()
|
||||
{}
|
||||
|
||||
int Realm::type() const
|
||||
{
|
||||
return StunAttribute::Realm;
|
||||
}
|
||||
|
||||
void Realm::dump(std::ostream &output)
|
||||
{
|
||||
output << "Realm " << value();
|
||||
}
|
||||
|
||||
//----------------- Nonce ------------------------
|
||||
Nonce::Nonce()
|
||||
{}
|
||||
|
||||
Nonce::~Nonce()
|
||||
{}
|
||||
|
||||
int Nonce::type() const
|
||||
{
|
||||
return StunAttribute::Nonce;
|
||||
}
|
||||
|
||||
void Nonce::dump(std::ostream &output)
|
||||
{
|
||||
output << "Nonce " << value();
|
||||
}
|
||||
|
||||
//----------------- Server -----------------------
|
||||
Server::Server()
|
||||
{}
|
||||
|
||||
Server::~Server()
|
||||
{}
|
||||
|
||||
int Server::type() const
|
||||
{
|
||||
return StunAttribute::Server;
|
||||
}
|
||||
|
||||
void Server::dump(std::ostream &output)
|
||||
{
|
||||
output << "Server " << value();
|
||||
}
|
||||
|
||||
//----------------- AlternateServer --------------
|
||||
AlternateServer::AlternateServer()
|
||||
{}
|
||||
|
||||
AlternateServer::~AlternateServer()
|
||||
{}
|
||||
|
||||
int AlternateServer::type() const
|
||||
{
|
||||
return StunAttribute::AlternateServer;
|
||||
}
|
||||
|
||||
void AlternateServer::dump(std::ostream &output)
|
||||
{
|
||||
output << "AlternateServer " << address().toStdString();
|
||||
}
|
||||
|
||||
//----------------- UnknownAttributes ------------
|
||||
UnknownAttributes::UnknownAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
UnknownAttributes::~UnknownAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
int UnknownAttributes::type() const
|
||||
{
|
||||
return StunAttribute::UnknownAttributes;
|
||||
}
|
||||
|
||||
void UnknownAttributes::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
char zeroBytes[8]; memset(zeroBytes, 0, sizeof(zeroBytes));
|
||||
stream.writeBuffer(zeroBytes, sizeof(zeroBytes));
|
||||
}
|
||||
|
||||
bool UnknownAttributes::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
char zeroBytes[8]; memset(zeroBytes, 0, sizeof(zeroBytes));
|
||||
stream.readBuffer(zeroBytes, sizeof(zeroBytes));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnknownAttributes::dump(std::ostream &output)
|
||||
{
|
||||
output << "UnknownAttributes";
|
||||
}
|
||||
|
||||
//---------------- ChannelNumber ----------------
|
||||
ChannelNumber::ChannelNumber()
|
||||
:mChannelNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
ChannelNumber::~ChannelNumber()
|
||||
{
|
||||
}
|
||||
|
||||
int ChannelNumber::type() const
|
||||
{
|
||||
return StunAttribute::ChannelNumber;
|
||||
}
|
||||
|
||||
void ChannelNumber::setChannelNumber(unsigned short value)
|
||||
{
|
||||
mChannelNumber = value;
|
||||
}
|
||||
|
||||
unsigned short ChannelNumber::channelNumber() const
|
||||
{
|
||||
return mChannelNumber;
|
||||
}
|
||||
|
||||
void ChannelNumber::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUShort(mChannelNumber);
|
||||
stream.writeUChar(0);
|
||||
stream.writeUChar(0);
|
||||
}
|
||||
|
||||
bool ChannelNumber::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mChannelNumber = stream.readUShort();
|
||||
stream.readUChar();
|
||||
stream.readUChar();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChannelNumber::dump(std::ostream &output)
|
||||
{
|
||||
output << "ChannelNumber " << mChannelNumber;
|
||||
}
|
||||
|
||||
//--------------------------------- Lifetime -----------------------
|
||||
Lifetime::Lifetime()
|
||||
:mLifetime(0)
|
||||
{
|
||||
}
|
||||
|
||||
Lifetime::~Lifetime()
|
||||
{
|
||||
}
|
||||
|
||||
int Lifetime::type() const
|
||||
{
|
||||
return StunAttribute::Lifetime;
|
||||
}
|
||||
|
||||
void Lifetime::setLifetime(unsigned int value)
|
||||
{
|
||||
mLifetime = value;
|
||||
}
|
||||
|
||||
unsigned int Lifetime::lifetime() const
|
||||
{
|
||||
return mLifetime;
|
||||
}
|
||||
|
||||
void Lifetime::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUInt(mLifetime);
|
||||
}
|
||||
|
||||
bool Lifetime::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mLifetime = stream.readUInt();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lifetime::dump(std::ostream &output)
|
||||
{
|
||||
output << "Lifetime " << mLifetime;
|
||||
}
|
||||
|
||||
//----------------------- DataAttribute ----------------------
|
||||
|
||||
DataAttribute::DataAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
DataAttribute::~DataAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
int DataAttribute::type() const
|
||||
{
|
||||
return StunAttribute::Data;
|
||||
}
|
||||
|
||||
void DataAttribute::setData(ByteBuffer& buffer)
|
||||
{
|
||||
mData = buffer;
|
||||
}
|
||||
|
||||
ByteBuffer DataAttribute::data() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
void DataAttribute::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeBuffer(mData.data(), mData.size());
|
||||
}
|
||||
|
||||
bool DataAttribute::parsePacket(BufferReader& stream)
|
||||
{
|
||||
mData.resize(1024);
|
||||
mData.resize(stream.readBuffer(mData.mutableData(), mData.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataAttribute::dump(std::ostream &output)
|
||||
{
|
||||
output << "DataAttribute " << mData.hexstring();
|
||||
}
|
||||
|
||||
//---------------------- XorRelayedTransport -------------------------
|
||||
XorRelayedAddress::XorRelayedAddress()
|
||||
{}
|
||||
|
||||
XorRelayedAddress::~XorRelayedAddress()
|
||||
{}
|
||||
|
||||
int XorRelayedAddress::type() const
|
||||
{
|
||||
return StunAttribute::XorRelayedAddress;
|
||||
}
|
||||
|
||||
void XorRelayedAddress::dump(std::ostream &output)
|
||||
{
|
||||
output << "XorRelayedAddress " << address().toStdString();
|
||||
}
|
||||
|
||||
//---------------------- RequestedTransport ---------------------------
|
||||
RequestedTransport::RequestedTransport()
|
||||
:mRequestedTransport(UDP)
|
||||
{
|
||||
}
|
||||
|
||||
RequestedTransport::~RequestedTransport()
|
||||
{
|
||||
}
|
||||
|
||||
int RequestedTransport::type() const
|
||||
{
|
||||
return StunAttribute::RequestedTransport;
|
||||
}
|
||||
|
||||
void RequestedTransport::setRequestedTransport(RequestedTransport::TransportType value)
|
||||
{
|
||||
mRequestedTransport = value;
|
||||
}
|
||||
|
||||
unsigned char RequestedTransport::requestedTransport() const
|
||||
{
|
||||
return mRequestedTransport;
|
||||
}
|
||||
|
||||
void RequestedTransport::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUChar(mRequestedTransport);
|
||||
stream.writeUChar(0);
|
||||
stream.writeUChar(0);
|
||||
stream.writeUChar(0);
|
||||
}
|
||||
|
||||
bool RequestedTransport::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mRequestedTransport = stream.readUChar();
|
||||
stream.readUChar();
|
||||
stream.readUChar();
|
||||
stream.readUChar();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RequestedTransport::dump(std::ostream &output)
|
||||
{
|
||||
output << "RequestedTransport " << mRequestedTransport;
|
||||
}
|
||||
|
||||
//--------------------------------- Controlled -----------------------
|
||||
ControlledAttr::ControlledAttr()
|
||||
{
|
||||
}
|
||||
|
||||
ControlledAttr::~ControlledAttr()
|
||||
{
|
||||
}
|
||||
|
||||
int ControlledAttr::type() const
|
||||
{
|
||||
return StunAttribute::ControlledAttr;
|
||||
}
|
||||
|
||||
std::string ControlledAttr::tieBreaker() const
|
||||
{
|
||||
return std::string((const char*)mTieBreaker, 8);
|
||||
}
|
||||
|
||||
void ControlledAttr::setTieBreaker(const std::string& tieBreaker)
|
||||
{
|
||||
assert(tieBreaker.length() == 8);
|
||||
|
||||
memcpy(mTieBreaker, tieBreaker.c_str(), 8);
|
||||
}
|
||||
|
||||
void ControlledAttr::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeBuffer(mTieBreaker, 8);
|
||||
}
|
||||
|
||||
bool ControlledAttr::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.readBuffer(mTieBreaker, 8);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControlledAttr::dump(std::ostream &output)
|
||||
{
|
||||
ByteBuffer b(mTieBreaker, 8);
|
||||
output << "ControlledAttr " << b.hexstring();
|
||||
}
|
||||
|
||||
//------------------- ControllingAttr --------------
|
||||
ControllingAttr::ControllingAttr()
|
||||
{
|
||||
memset(mTieBreaker, 0, sizeof(mTieBreaker));
|
||||
}
|
||||
|
||||
ControllingAttr::~ControllingAttr()
|
||||
{
|
||||
}
|
||||
|
||||
int ControllingAttr::type() const
|
||||
{
|
||||
return StunAttribute::ControllingAttr;
|
||||
}
|
||||
|
||||
std::string ControllingAttr::tieBreaker() const
|
||||
{
|
||||
return std::string((const char*)mTieBreaker, 8);
|
||||
}
|
||||
|
||||
void ControllingAttr::setTieBreaker(const std::string& tieBreaker)
|
||||
{
|
||||
memcpy(mTieBreaker, tieBreaker.c_str(), 8);
|
||||
}
|
||||
|
||||
void ControllingAttr::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeBuffer(mTieBreaker, 8);
|
||||
}
|
||||
|
||||
bool ControllingAttr::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.readBuffer(mTieBreaker, 8);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControllingAttr::dump(std::ostream &output)
|
||||
{
|
||||
ByteBuffer b(mTieBreaker, 8);
|
||||
output << "ControllingAttr " << b.hexstring();
|
||||
}
|
||||
|
||||
//----------- ICEPriority ---------------
|
||||
ICEPriority::ICEPriority()
|
||||
{
|
||||
mPriority = 0;
|
||||
}
|
||||
|
||||
ICEPriority::~ICEPriority()
|
||||
{
|
||||
}
|
||||
|
||||
int ICEPriority::type() const
|
||||
{
|
||||
return StunAttribute::ICEPriority;
|
||||
}
|
||||
|
||||
unsigned int ICEPriority::priority() const
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
void ICEPriority::setPriority(unsigned int priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
void ICEPriority::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUInt(mPriority);
|
||||
}
|
||||
|
||||
bool ICEPriority::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mPriority = stream.readUInt();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICEPriority::dump(std::ostream &output)
|
||||
{
|
||||
output << "ICEPriority " << mPriority;
|
||||
}
|
||||
|
||||
//------------------ USE-CANDIDATE -----------------------
|
||||
ICEUseCandidate::ICEUseCandidate()
|
||||
{
|
||||
}
|
||||
|
||||
ICEUseCandidate::~ICEUseCandidate()
|
||||
{
|
||||
}
|
||||
|
||||
int ICEUseCandidate::type() const
|
||||
{
|
||||
return StunAttribute::ICEUseCandidate;
|
||||
}
|
||||
|
||||
void ICEUseCandidate::buildPacket(BufferWriter& /*buffer*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool ICEUseCandidate::parsePacket(BufferReader& /*buffer*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICEUseCandidate::dump(std::ostream &output)
|
||||
{
|
||||
output << "ICEUseCandidate";
|
||||
}
|
||||
// ------------- REQUESTED-ADDRESS-FAMILY -------------
|
||||
RequestedAddressFamily::RequestedAddressFamily()
|
||||
:mAddressFamily(IPv4)
|
||||
{}
|
||||
|
||||
RequestedAddressFamily::RequestedAddressFamily(AddressFamily family)
|
||||
:mAddressFamily(family)
|
||||
{}
|
||||
|
||||
RequestedAddressFamily::~RequestedAddressFamily()
|
||||
{}
|
||||
|
||||
int RequestedAddressFamily::type() const
|
||||
{
|
||||
return StunAttribute::RequestedAddressFamily;
|
||||
}
|
||||
|
||||
AddressFamily RequestedAddressFamily::family() const
|
||||
{
|
||||
return mAddressFamily;
|
||||
}
|
||||
|
||||
void RequestedAddressFamily::buildPacket(BufferWriter& stream)
|
||||
{
|
||||
stream.writeUChar(mAddressFamily);
|
||||
stream.writeUChar(0); stream.writeUChar(0); stream.writeUChar(0);
|
||||
}
|
||||
|
||||
bool RequestedAddressFamily::parsePacket(BufferReader& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
mAddressFamily = (AddressFamily)stream.readUChar();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RequestedAddressFamily::dump(std::ostream &output)
|
||||
{
|
||||
output << "RequestedAddressFamily " << (mAddressFamily == IPv4 ? "IPv4" : "IPv6");
|
||||
}
|
||||
363
src/libs/ice/ICEStunAttributes.h
Normal file
363
src/libs/ice/ICEStunAttributes.h
Normal file
@@ -0,0 +1,363 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_STUN_ATTRIBUTES_H
|
||||
#define __ICE_STUN_ATTRIBUTES_H
|
||||
|
||||
#include "ICEStunMessage.h"
|
||||
#include <string>
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class MappedAddress: public StunAttribute
|
||||
{
|
||||
public:
|
||||
MappedAddress();
|
||||
virtual ~MappedAddress();
|
||||
|
||||
virtual int type() const override;
|
||||
NetworkAddress& address();
|
||||
virtual void buildPacket(BufferWriter& buffer) override;
|
||||
virtual bool parsePacket(BufferReader& buffer) override;
|
||||
virtual void dump(std::ostream& output) override;
|
||||
|
||||
protected:
|
||||
NetworkAddress mAddress;
|
||||
};
|
||||
|
||||
class XorMappedAddress: public MappedAddress
|
||||
{
|
||||
public:
|
||||
XorMappedAddress();
|
||||
virtual ~XorMappedAddress();
|
||||
virtual int type() const override;
|
||||
|
||||
virtual void buildPacket(BufferWriter& writer) override;
|
||||
virtual bool parsePacket(BufferReader& reader) override;
|
||||
virtual void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class StringAttr: public StunAttribute
|
||||
{
|
||||
public:
|
||||
StringAttr();
|
||||
virtual ~StringAttr();
|
||||
|
||||
virtual int type() const override;
|
||||
std::string value() const;
|
||||
void setValue(const std::string& value);
|
||||
|
||||
virtual void buildPacket(BufferWriter& writer) override;
|
||||
virtual bool parsePacket(BufferReader& reader) override;
|
||||
virtual void dump(std::ostream& output) override;
|
||||
|
||||
protected:
|
||||
std::string mValue;
|
||||
};
|
||||
|
||||
class Username: public StringAttr
|
||||
{
|
||||
public:
|
||||
Username();
|
||||
~Username();
|
||||
int type() const;
|
||||
void dump(std::ostream& output);
|
||||
};
|
||||
|
||||
class MessageIntegrity: public StunAttribute
|
||||
{
|
||||
public:
|
||||
MessageIntegrity();
|
||||
~MessageIntegrity();
|
||||
|
||||
int type() const override;
|
||||
void setValue(const void* data);
|
||||
const void* value() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
|
||||
protected:
|
||||
unsigned char mValue[20];
|
||||
};
|
||||
|
||||
class Fingerprint: public StunAttribute
|
||||
{
|
||||
public:
|
||||
Fingerprint();
|
||||
~Fingerprint();
|
||||
|
||||
int type() const override;
|
||||
void setCrc32(unsigned int crc);
|
||||
unsigned int crc32() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
|
||||
protected:
|
||||
unsigned int mCRC32;
|
||||
};
|
||||
|
||||
class ErrorCode: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ErrorCode();
|
||||
~ErrorCode();
|
||||
|
||||
virtual int type() const override;
|
||||
void setErrorCode(int errorCode);
|
||||
int errorCode() const;
|
||||
|
||||
void setErrorPhrase(const std::string& phrase);
|
||||
std::string errorPhrase() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
|
||||
protected:
|
||||
int mErrorCode;
|
||||
std::string mErrorPhrase;
|
||||
};
|
||||
|
||||
class Realm: public StringAttr
|
||||
{
|
||||
public:
|
||||
Realm();
|
||||
~Realm();
|
||||
int type() const override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class Nonce: public StringAttr
|
||||
{
|
||||
public:
|
||||
Nonce();
|
||||
~Nonce();
|
||||
|
||||
int type() const override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class UnknownAttributes: public StunAttribute
|
||||
{
|
||||
public:
|
||||
UnknownAttributes();
|
||||
~UnknownAttributes();
|
||||
|
||||
int type() const override;
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class Server: public StringAttr
|
||||
{
|
||||
public:
|
||||
Server();
|
||||
~Server();
|
||||
int type() const override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class AlternateServer: public MappedAddress
|
||||
{
|
||||
public:
|
||||
AlternateServer();
|
||||
~AlternateServer();
|
||||
int type() const override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
|
||||
class ChannelNumber: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ChannelNumber();
|
||||
~ChannelNumber();
|
||||
|
||||
int type() const override;
|
||||
void setChannelNumber(unsigned short value);
|
||||
unsigned short channelNumber() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned short mChannelNumber;
|
||||
};
|
||||
|
||||
class Lifetime: public StunAttribute
|
||||
{
|
||||
public:
|
||||
Lifetime();
|
||||
~Lifetime();
|
||||
|
||||
int type() const override;
|
||||
void setLifetime(unsigned int value);
|
||||
unsigned int lifetime() const;
|
||||
|
||||
void buildPacket(BufferWriter& writer) override;
|
||||
bool parsePacket(BufferReader& reader) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned int mLifetime;
|
||||
};
|
||||
|
||||
class XorPeerAddress: public XorMappedAddress
|
||||
{
|
||||
public:
|
||||
int type() const override
|
||||
{
|
||||
return StunAttribute::XorPeerAddress;
|
||||
}
|
||||
};
|
||||
|
||||
class DataAttribute: public StunAttribute
|
||||
{
|
||||
public:
|
||||
DataAttribute();
|
||||
virtual ~DataAttribute();
|
||||
|
||||
int type() const override;
|
||||
void setData(ByteBuffer& buffer);
|
||||
ByteBuffer data() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream &output) override;
|
||||
|
||||
protected:
|
||||
ByteBuffer mData;
|
||||
};
|
||||
|
||||
class XorRelayedAddress: public XorMappedAddress
|
||||
{
|
||||
public:
|
||||
XorRelayedAddress();
|
||||
~XorRelayedAddress();
|
||||
int type() const override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class RequestedTransport: public StunAttribute
|
||||
{
|
||||
public:
|
||||
RequestedTransport();
|
||||
virtual ~RequestedTransport();
|
||||
|
||||
enum TransportType
|
||||
{
|
||||
UDP = 17
|
||||
};
|
||||
|
||||
int type() const override;
|
||||
void setRequestedTransport(TransportType value);
|
||||
unsigned char requestedTransport() const;
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned char mRequestedTransport; //always 17!
|
||||
};
|
||||
|
||||
class ReservedToken: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ReservedToken();
|
||||
~ReservedToken();
|
||||
|
||||
int type() const override;
|
||||
void setToken(const std::string& token);
|
||||
std::string token() const;
|
||||
|
||||
void buildPacket(BufferWriter& buffer) override;
|
||||
bool parsePacket(BufferReader& buffer) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
std::string mToken;
|
||||
};
|
||||
|
||||
class ControlledAttr: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ControlledAttr();
|
||||
~ControlledAttr();
|
||||
|
||||
int type() const override;
|
||||
|
||||
std::string tieBreaker() const;
|
||||
void setTieBreaker(const std::string& tieBreaker);
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned char mTieBreaker[8];
|
||||
};
|
||||
|
||||
class ControllingAttr: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ControllingAttr();
|
||||
~ControllingAttr();
|
||||
|
||||
int type() const override;
|
||||
std::string tieBreaker() const;
|
||||
void setTieBreaker(const std::string& tieBreaker);
|
||||
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned char mTieBreaker[8];
|
||||
};
|
||||
|
||||
class ICEPriority: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ICEPriority();
|
||||
~ICEPriority();
|
||||
|
||||
int type() const override;
|
||||
unsigned int priority() const;
|
||||
void setPriority(unsigned int priority);
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
unsigned int mPriority;
|
||||
};
|
||||
|
||||
class ICEUseCandidate: public StunAttribute
|
||||
{
|
||||
public:
|
||||
ICEUseCandidate();
|
||||
virtual ~ICEUseCandidate();
|
||||
int type() const override;
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
};
|
||||
|
||||
class RequestedAddressFamily: public StunAttribute
|
||||
{
|
||||
public:
|
||||
RequestedAddressFamily();
|
||||
RequestedAddressFamily(AddressFamily family);
|
||||
~RequestedAddressFamily();
|
||||
|
||||
int type() const override;
|
||||
AddressFamily family() const;
|
||||
void buildPacket(BufferWriter& stream) override;
|
||||
bool parsePacket(BufferReader& stream) override;
|
||||
void dump(std::ostream& output) override;
|
||||
protected:
|
||||
AddressFamily mAddressFamily;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
50
src/libs/ice/ICEStunConfig.cpp
Normal file
50
src/libs/ice/ICEStunConfig.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEStunConfig.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
std::string StackConfig::mDefaultIpTarget = ICE_FALLBACK_IP_ADDR;
|
||||
|
||||
StackConfig::StackConfig()
|
||||
:mUseTURN(false), mTimeout(DEFAULT_STUN_FINISH_TIMEOUT), mPeriod(DEFAULT_STUN_RETRANSMIT_TIMEOUT),
|
||||
mUseSTUN(true), mUseIPv4(true), mUseIPv6(true)
|
||||
{
|
||||
#ifdef ICE_AGGRESSIVE
|
||||
mAggressiveNomination = true;
|
||||
mTreatRequestAsConfirmation = false;
|
||||
#endif
|
||||
|
||||
#ifdef ICE_VERYAGGRESSIVE
|
||||
mAggressiveNomination = true;
|
||||
mTreatRequestAsConfirmation = true;
|
||||
#else
|
||||
mAggressiveNomination = false;
|
||||
mTreatRequestAsConfirmation = false;
|
||||
#endif
|
||||
mInitialRTO = 100;
|
||||
mKeepAliveInterval = 5000;
|
||||
mServerAddr4.setPort( 3478 );
|
||||
mServerAddr6.setPort( 3478 );
|
||||
mTurnLifetime = 300;
|
||||
mUseProtocolRelay = true;// false;
|
||||
|
||||
#ifdef TEST_RELAYING
|
||||
mTypePreferenceList[Candidate::Host] = 0;
|
||||
mTypePreferenceList[Candidate::ServerReflexive] = 110;
|
||||
mTypePreferenceList[Candidate::PeerReflexive] = 100;
|
||||
mTypePreferenceList[Candidate::ServerRelayed] = 126;
|
||||
#else
|
||||
mTypePreferenceList[Candidate::Host] = 126;
|
||||
mTypePreferenceList[Candidate::ServerReflexive] = 100;
|
||||
mTypePreferenceList[Candidate::PeerReflexive] = 110;
|
||||
mTypePreferenceList[Candidate::ServerRelayed] = 0;
|
||||
#endif
|
||||
mTargetIP = mDefaultIpTarget;
|
||||
}
|
||||
|
||||
StackConfig::~StackConfig()
|
||||
{}
|
||||
86
src/libs/ice/ICEStunConfig.h
Normal file
86
src/libs/ice/ICEStunConfig.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_STUN_CONFIG_H
|
||||
#define __ICE_STUN_CONFIG_H
|
||||
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICECandidate.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ice {
|
||||
|
||||
#define DEFAULT_STUN_RETRANSMIT_TIMEOUT 790000
|
||||
#define DEFAULT_STUN_FINISH_TIMEOUT 790000
|
||||
#define ICE_FALLBACK_IP_ADDR "8.8.8.8"
|
||||
// #define TEST_RELAYING
|
||||
|
||||
struct StackConfig
|
||||
{
|
||||
// The IP of STUN server
|
||||
std::vector<NetworkAddress> mServerList4,
|
||||
mServerList6;
|
||||
NetworkAddress mServerAddr4,
|
||||
mServerAddr6;
|
||||
|
||||
// Use IPv4 when gathering candidates
|
||||
bool mUseIPv4;
|
||||
|
||||
// Use IPv6 when gathering candidates
|
||||
bool mUseIPv6;
|
||||
|
||||
// Should we use relying (TURN)?
|
||||
bool mUseTURN;
|
||||
|
||||
// Should we use IPv4 <--> IPv6 bypassing via relaying ?
|
||||
bool mUseProtocolRelay;
|
||||
|
||||
// The timeout for STUN transaction
|
||||
unsigned int mTimeout;
|
||||
|
||||
// The RTO
|
||||
unsigned int mPeriod;
|
||||
|
||||
// Marks if STUN use is disalbed
|
||||
bool mUseSTUN;
|
||||
|
||||
// Sets the possible peer IP for ICE session
|
||||
std::string mTargetIP;
|
||||
|
||||
// The type preference list for ICE session
|
||||
int mTypePreferenceList[4];
|
||||
|
||||
// The initial RTO value
|
||||
int mInitialRTO;
|
||||
|
||||
// Interval for keepalive checks
|
||||
int mKeepAliveInterval;
|
||||
|
||||
std::string mTurnUsername,
|
||||
mTurnPassword;
|
||||
|
||||
// TURN lifetime
|
||||
int mTurnLifetime;
|
||||
|
||||
// Enable/disable aggressive nomination
|
||||
bool mAggressiveNomination;
|
||||
|
||||
// Treats requests as confirmation for connectivity checks sent in reverse direction if the corresponding pair already exists
|
||||
// It violates RFC. It can be needed in poor networks.
|
||||
bool mTreatRequestAsConfirmation;
|
||||
|
||||
// Default IP target if mTargetIP is not set
|
||||
static std::string mDefaultIpTarget;
|
||||
|
||||
StackConfig();
|
||||
~StackConfig();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
852
src/libs/ice/ICEStunMessage.cpp
Normal file
852
src/libs/ice/ICEStunMessage.cpp
Normal file
@@ -0,0 +1,852 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEStunMessage.h"
|
||||
#include "ICECRC32.h"
|
||||
#include "ICESHA1.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICEError.h"
|
||||
#include "ICELog.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <arpa/inet.h>
|
||||
# include <alloca.h>
|
||||
# include <stdexcept>
|
||||
#endif
|
||||
|
||||
#define STUN_HEADER_SIZE 20
|
||||
#define HMAC_DIGEST_SIZE 20
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
bool StunMessage::TransactionID::operator == (const StunMessage::TransactionID& rhs)
|
||||
{
|
||||
return memcmp(mValue, rhs.mValue, 12) == 0;
|
||||
}
|
||||
|
||||
bool StunMessage::TransactionID::operator != (const TransactionID& rhs)
|
||||
{
|
||||
return memcmp(mValue, rhs.mValue, 12) != 0;
|
||||
}
|
||||
|
||||
StunMessage::TransactionID::TransactionID()
|
||||
{
|
||||
for (int i=0; i<12; i++)
|
||||
mValue[i] = rand() & 0xFF;
|
||||
|
||||
//memset(mValue, 0, 12);
|
||||
}
|
||||
|
||||
std::string StunMessage::TransactionID::toStdString()
|
||||
{
|
||||
char hex[3];
|
||||
std::string result;
|
||||
for (size_t i=0; i<12; i++)
|
||||
{
|
||||
sprintf(hex, "%2x", mValue[i]);
|
||||
result += hex;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StunMessage::TransactionID StunMessage::TransactionID::generateNew()
|
||||
{
|
||||
TransactionID result;
|
||||
for (size_t i=0; i<12; i++)
|
||||
result.mValue[i] = (unsigned char)rand();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------- StunAttribute ----------------------
|
||||
StunAttribute::StunAttribute()
|
||||
{
|
||||
mLength = 0;
|
||||
mType = 0;
|
||||
mDataOffset = 0;
|
||||
}
|
||||
|
||||
StunAttribute::~StunAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
void StunAttribute::setDataOffset(size_t offset)
|
||||
{
|
||||
mDataOffset = offset;
|
||||
}
|
||||
|
||||
size_t StunAttribute::dataOffset() const
|
||||
{
|
||||
return mDataOffset;
|
||||
}
|
||||
|
||||
//---------------------------- StunMessage -----------------------
|
||||
StunMessage::StunMessage()
|
||||
:mMagicCookie(0x2112A442)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
StunMessage::~StunMessage()
|
||||
{
|
||||
// Iterate map to delete all attributes
|
||||
for (AttributeMap::iterator ait=mAttrMap.begin(); ait != mAttrMap.end(); ++ait)
|
||||
delete ait->second;
|
||||
}
|
||||
|
||||
std::string StunMessage::comment()
|
||||
{
|
||||
return mComment;
|
||||
}
|
||||
|
||||
void StunMessage::setComment(std::string comment)
|
||||
{
|
||||
mComment = comment;
|
||||
}
|
||||
|
||||
|
||||
void StunMessage::setMessageType(Type type)
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
|
||||
|
||||
StunMessage::Type StunMessage::messageType()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void StunMessage::setMessageClass(Class value)
|
||||
{
|
||||
mClass = value;
|
||||
}
|
||||
|
||||
StunMessage::Class StunMessage::messageClass()
|
||||
{
|
||||
return mClass;
|
||||
}
|
||||
|
||||
unsigned int StunMessage::magicCookie()
|
||||
{
|
||||
return mMagicCookie;
|
||||
}
|
||||
|
||||
bool StunMessage::isMagicCookieValid()
|
||||
{
|
||||
return mMagicCookie == 0x2112A442;
|
||||
}
|
||||
|
||||
void StunMessage::setTransactionId(TransactionID id)
|
||||
{
|
||||
mTransactionID = id;
|
||||
}
|
||||
|
||||
StunMessage::TransactionID StunMessage::transactionId()
|
||||
{
|
||||
return mTransactionID;
|
||||
}
|
||||
|
||||
static void EmbedAttrBuffer(StunAttribute& attr, BufferWriter& writer)
|
||||
{
|
||||
ByteBuffer attrBuffer; attrBuffer.resize(512);
|
||||
BufferWriter attrWriter(attrBuffer);
|
||||
attr.buildPacket(attrWriter);
|
||||
attrBuffer.resize(attrWriter.offset());
|
||||
|
||||
// Enqueue attribute type
|
||||
writer.writeUShort(attr.type());
|
||||
|
||||
// Enqueue length
|
||||
writer.writeUShort(attrBuffer.size());
|
||||
|
||||
// Save data offset in generated buffer
|
||||
attr.setDataOffset(writer.offset() + 2); // 2 is for first 16 bits written by bitstream
|
||||
|
||||
// Enqueue attribute's value
|
||||
if (attrBuffer.size() > 0)
|
||||
writer.writeBuffer(attrBuffer.data(), attrBuffer.size());
|
||||
|
||||
if (attrBuffer.size() & 0x3)
|
||||
{
|
||||
size_t paddingLength = 4 - (attrBuffer.size() & 0x3);
|
||||
char paddingBytes[4] = { 0, 0, 0, 0 };
|
||||
writer.writeBuffer(paddingBytes, paddingLength);
|
||||
}
|
||||
}
|
||||
|
||||
void StunMessage::buildPacket(ByteBuffer& buffer, const std::string& password)
|
||||
{
|
||||
// Make enough space for packet
|
||||
buffer.resize(1024);
|
||||
|
||||
// Write bits
|
||||
BitWriter bitstream(buffer);
|
||||
|
||||
bitstream.writeBit(0)
|
||||
.writeBit(0);
|
||||
|
||||
unsigned int msgtype = mType & 0xFFF;
|
||||
unsigned int msgclass = mClass & 0x3;
|
||||
|
||||
// Enqueue last 5 bits of mtype
|
||||
for (size_t i=0; i<5; i++)
|
||||
bitstream.writeBit(bit(msgtype, 11 - i));
|
||||
|
||||
// Enqueue last bit of msgclass
|
||||
bitstream.writeBit(bit(msgclass, 1));
|
||||
|
||||
// Enqueue 3 bits of msgtype
|
||||
for (size_t i=0; i<3; i++)
|
||||
bitstream.writeBit(bit(msgtype, 6 - i));
|
||||
|
||||
// Enqueue first bit of msgclass
|
||||
bitstream.writeBit(bit(msgclass, 0));
|
||||
|
||||
// Enqueue 4 bits of msgtype
|
||||
for (size_t i=0; i<4; i++)
|
||||
bitstream.writeBit(bit(msgtype, 3-i));
|
||||
|
||||
// Enqueue 2 bytes of length - now it is zero
|
||||
BufferWriter stream(buffer.mutableData() + bitstream.count() / 8);
|
||||
stream.writeUShort(0);
|
||||
|
||||
// Enqueue magic cookie value
|
||||
unsigned int cookie = htonl(mMagicCookie);
|
||||
stream.writeBuffer(&cookie, 4);
|
||||
|
||||
// Enqueue transaction ID
|
||||
//memset(mTransactionID.mValue, 0, sizeof(mTransactionID.mValue)); // For debugging only
|
||||
stream.writeBuffer(mTransactionID.mValue, 12);
|
||||
|
||||
// Iterate attributes
|
||||
AttributeMap::iterator attrIter;
|
||||
for (attrIter = mAttrMap.begin(); attrIter != mAttrMap.end(); ++attrIter)
|
||||
{
|
||||
StunAttribute& attr = *attrIter->second;
|
||||
|
||||
// Check if it is MessageIntegrity or Fingerprint - they comes last and skip them for now
|
||||
if (attr.type() == StunAttribute::MessageIntegrity || attr.type() == StunAttribute::Fingerprint)
|
||||
continue;
|
||||
|
||||
EmbedAttrBuffer(attr, stream);
|
||||
}
|
||||
|
||||
// Append MessageIntegrity attribute if exists
|
||||
AttributeMap::iterator miIter = mAttrMap.find(StunAttribute::MessageIntegrity);
|
||||
if ( miIter != mAttrMap.end())
|
||||
{
|
||||
EmbedAttrBuffer(*miIter->second, stream);
|
||||
}
|
||||
|
||||
int lengthWithoutFingerprint = stream.offset() + 2;
|
||||
|
||||
// Append Fingerprint attribute if exists
|
||||
AttributeMap::iterator fpIter = mAttrMap.find(StunAttribute::Fingerprint);
|
||||
if (fpIter != mAttrMap.end())
|
||||
{
|
||||
EmbedAttrBuffer(*fpIter->second, stream);
|
||||
}
|
||||
|
||||
// Check for message integrity attribute
|
||||
miIter = mAttrMap.find(StunAttribute::MessageIntegrity);
|
||||
if (miIter != mAttrMap.end())
|
||||
{
|
||||
// Update length in header
|
||||
*((unsigned short*)buffer.mutableData() + 1) = htons(lengthWithoutFingerprint - STUN_HEADER_SIZE);
|
||||
|
||||
// Prepare HMAC digest buffer
|
||||
unsigned char digest[HMAC_DIGEST_SIZE];
|
||||
|
||||
// Get data offset for output digest
|
||||
size_t dataOffset = miIter->second->dataOffset();
|
||||
|
||||
// Get digest
|
||||
hmacSha1Digest(buffer.data(), dataOffset - 4, digest, password.c_str(), password.length());
|
||||
|
||||
// Copy digest to proper place
|
||||
memcpy((unsigned char*)buffer.data() + dataOffset, digest, HMAC_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
// Resize resulting buffer
|
||||
buffer.resize(stream.offset() + 2);
|
||||
|
||||
// Put length in header
|
||||
*((unsigned short*)buffer.mutableData() + 1) = htons(buffer.size() - STUN_HEADER_SIZE);
|
||||
|
||||
// Check for fingerprint attribute
|
||||
fpIter = mAttrMap.find(StunAttribute::Fingerprint);
|
||||
if (fpIter != mAttrMap.end())
|
||||
{
|
||||
// Get data offset for fingeprint attribute
|
||||
size_t dataOffset = fpIter->second->dataOffset();
|
||||
|
||||
// Find CRC32
|
||||
CRC32 crc32;
|
||||
unsigned int crc = crc32.fullCrc((unsigned char*)buffer.data(), dataOffset - 4);
|
||||
|
||||
// Update attribute value with CRC32 value
|
||||
memcpy((unsigned char*)buffer.data() + dataOffset, &crc, 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "ICEStunAttributes.h"
|
||||
|
||||
bool StunMessage::parsePacket(ByteBuffer& buffer)
|
||||
{
|
||||
// Save incoming packet
|
||||
mPacket2Parse = buffer;
|
||||
|
||||
// Clear attribute list
|
||||
mAttrMap.clear();
|
||||
|
||||
BufferReader stream(buffer);
|
||||
|
||||
uint8_t firstByte = stream.readUChar();
|
||||
uint8_t secondByte = stream.readUChar();
|
||||
|
||||
if (firstByte & ~0x3F)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int c1 = firstByte & 0x1;
|
||||
int m11 = firstByte & 0x20 ? 1 : 0;
|
||||
int m10 = firstByte & 0x10 ? 1 : 0;
|
||||
int m9 = firstByte & 0x8 ? 1 : 0;
|
||||
int m8 = firstByte & 0x4 ? 1 : 0;
|
||||
int m7 = firstByte & 0x2 ? 1 : 0;
|
||||
|
||||
int m6 = secondByte & 0x80 ? 1 : 0;
|
||||
int m5 = secondByte & 0x40 ? 1 : 0;
|
||||
int m4 = secondByte & 0x20 ? 1 : 0;
|
||||
int c0 = secondByte & 0x10 ? 1 : 0;
|
||||
int m3 = secondByte & 0x8 ? 1 : 0;
|
||||
int m2 = secondByte & 0x4 ? 1 : 0;
|
||||
int m1 = secondByte & 0x2 ? 1 : 0;
|
||||
int m0 = secondByte & 0x1 ? 1 : 0;
|
||||
|
||||
mType = (StunMessage::Type)((m11 << 11) + (m10 << 10) + (m9 << 9) + (m8 << 8) + (m7 << 7) + (m6 << 6) + (m5 << 5) + (m4 << 4) + (m3 << 3) + (m2 << 2) + (m1 << 1) + m0);
|
||||
mClass = (StunMessage::Class) ((c1 << 1) + c0);
|
||||
|
||||
unsigned short length = stream.readUShort();
|
||||
if (length & 0x3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dequeue magic cookie
|
||||
mMagicCookie = stream.readUInt();
|
||||
if (!isMagicCookieValid())
|
||||
return false;
|
||||
|
||||
// Dequeue transaction id
|
||||
char id[12];
|
||||
stream.readBuffer(id, 12);
|
||||
|
||||
memcpy(mTransactionID.mValue, id, 12);
|
||||
|
||||
// Dequeue attributes
|
||||
while (stream.count() < buffer.size())
|
||||
{
|
||||
int attrType = stream.readUShort();
|
||||
StunAttribute* attr = NULL;
|
||||
switch (attrType)
|
||||
{
|
||||
case StunAttribute::Data:
|
||||
attr = new DataAttribute();
|
||||
break;
|
||||
|
||||
case StunAttribute::MappedAddress:
|
||||
attr = new MappedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::XorMappedAddress:
|
||||
attr = new XorMappedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::ErrorCode:
|
||||
attr = new ErrorCode();
|
||||
break;
|
||||
|
||||
case StunAttribute::AlternateServer:
|
||||
attr = new AlternateServer();
|
||||
break;
|
||||
|
||||
case StunAttribute::UnknownAttributes:
|
||||
attr = new UnknownAttributes();
|
||||
break;
|
||||
|
||||
case StunAttribute::Fingerprint:
|
||||
attr = new Fingerprint();
|
||||
break;
|
||||
|
||||
case StunAttribute::MessageIntegrity:
|
||||
attr = new MessageIntegrity();
|
||||
break;
|
||||
|
||||
case StunAttribute::Nonce:
|
||||
attr = new Nonce();
|
||||
break;
|
||||
|
||||
case StunAttribute::Realm:
|
||||
attr = new Realm();
|
||||
break;
|
||||
|
||||
case StunAttribute::Server:
|
||||
attr = new Server();
|
||||
break;
|
||||
|
||||
case StunAttribute::Username:
|
||||
attr = new Username();
|
||||
break;
|
||||
|
||||
case StunAttribute::ICEPriority:
|
||||
attr = new ICEPriority();
|
||||
break;
|
||||
|
||||
case StunAttribute::ControlledAttr:
|
||||
attr = new ControlledAttr();
|
||||
break;
|
||||
|
||||
case StunAttribute::ControllingAttr:
|
||||
attr = new ControllingAttr();
|
||||
break;
|
||||
|
||||
case StunAttribute::ICEUseCandidate:
|
||||
attr = new ICEUseCandidate();
|
||||
break;
|
||||
|
||||
case StunAttribute::XorRelayedAddress:
|
||||
attr = new XorRelayedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::XorPeerAddress:
|
||||
attr = new XorPeerAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::Lifetime:
|
||||
attr = new Lifetime();
|
||||
break;
|
||||
|
||||
case StunAttribute::RequestedTransport:
|
||||
attr = new RequestedTransport();
|
||||
break;
|
||||
|
||||
case StunAttribute::RequestedAddressFamily:
|
||||
attr = new RequestedAddressFamily();
|
||||
break;
|
||||
|
||||
default:
|
||||
attr = NULL;
|
||||
}
|
||||
|
||||
// Get attribute value length
|
||||
int attrLen = stream.readUShort();
|
||||
|
||||
unsigned int dataOffset = stream.count();
|
||||
|
||||
// Get attribute buffer
|
||||
ByteBuffer attrBuffer;
|
||||
if (attrLen > 0)
|
||||
{
|
||||
attrBuffer.resize(attrLen);
|
||||
stream.readBuffer(attrBuffer.mutableData(), attrLen);
|
||||
}
|
||||
|
||||
// Skip padding bytes
|
||||
if (attrLen & 0x3)
|
||||
{
|
||||
size_t paddingLength = 4 - (attrLen & 0x3);
|
||||
char paddingBytes[4];
|
||||
stream.readBuffer(paddingBytes, paddingLength);
|
||||
}
|
||||
|
||||
// Parse attribute value
|
||||
if (attr)
|
||||
{
|
||||
// Parse attribute
|
||||
BufferReader attrReader(attrBuffer);
|
||||
if (!attr->parsePacket(attrReader))
|
||||
return false;
|
||||
|
||||
// Set data offset
|
||||
attr->setDataOffset(dataOffset);
|
||||
|
||||
// Add attribute
|
||||
addAttribute(attr);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StunMessage::validatePacket(std::string key)
|
||||
{
|
||||
// Iterate attributes
|
||||
if (hasAttribute(StunAttribute::MessageIntegrity))
|
||||
{
|
||||
// Get HMAC digest
|
||||
MessageIntegrity& mi = dynamic_cast<MessageIntegrity&>(attribute(StunAttribute::MessageIntegrity));
|
||||
|
||||
unsigned char digest[HMAC_DIGEST_SIZE];
|
||||
|
||||
// Reset length to length of packet without Fingerprint
|
||||
unsigned short* lengthPtr = ((unsigned short*)mPacket2Parse.mutableData() + 1);
|
||||
|
||||
// Save old value - it is for backup purposes only. So there is no call to ntohs().
|
||||
unsigned short len = *lengthPtr;
|
||||
|
||||
// Set fake length including MessageIntegrity attribute but excluding any attributes behind of it and excluding STUN header size
|
||||
*lengthPtr = htons(mi.dataOffset() + HMAC_DIGEST_SIZE - STUN_HEADER_SIZE);
|
||||
|
||||
// Find HMAC-SHA1 again
|
||||
hmacSha1Digest(mPacket2Parse.data(), mi.dataOffset() - 4, digest, key.c_str(), key.size());
|
||||
|
||||
// And restore old value
|
||||
*lengthPtr = len;
|
||||
|
||||
if (memcmp(mi.value(), digest, HMAC_DIGEST_SIZE) != 0)
|
||||
{
|
||||
ICELogCritical(<< "Bad MessageIntegrity in STUN message");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (hasAttribute(StunAttribute::Fingerprint))
|
||||
{
|
||||
// Get fingerpring attribute
|
||||
Fingerprint& fp = dynamic_cast<Fingerprint&>(attribute(StunAttribute::Fingerprint));
|
||||
|
||||
// Find CRC32
|
||||
CRC32 crcObj;
|
||||
unsigned long crcValue = crcObj.fullCrc((const unsigned char*)mPacket2Parse.data(), fp.dataOffset() - 4);
|
||||
|
||||
// Compare with saved one
|
||||
if (crcValue != fp.crc32())
|
||||
{
|
||||
ICELogCritical(<< "Bad CRC32 value in STUN message");
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline char StunMessage::bit(unsigned int value, size_t index)
|
||||
{
|
||||
unsigned int mask = (1 << index);
|
||||
|
||||
return value & mask ? 1 : 0;
|
||||
}
|
||||
|
||||
inline void StunMessage::setBit(unsigned int& result, size_t index, char value)
|
||||
{
|
||||
unsigned int mask = (1 << index);
|
||||
if (value != 0)
|
||||
result |= mask;
|
||||
else
|
||||
result &= ~mask;
|
||||
}
|
||||
|
||||
void StunMessage::addAttribute(StunAttribute* attr)
|
||||
{
|
||||
assert(NULL != attr);
|
||||
mAttrMap.insert(std::pair<int, StunAttribute*>(attr->type(), attr));
|
||||
}
|
||||
|
||||
void StunMessage::setAttribute(StunAttribute *attr)
|
||||
{
|
||||
assert(NULL != attr);
|
||||
|
||||
AttributeMap::iterator attrIter = mAttrMap.find(attr->type());
|
||||
if (attrIter != mAttrMap.end())
|
||||
{
|
||||
delete attrIter->second;
|
||||
attrIter->second = attr;
|
||||
}
|
||||
else
|
||||
mAttrMap.insert(std::pair<int, StunAttribute*>(attr->type(), attr));
|
||||
}
|
||||
|
||||
bool StunMessage::hasAttribute(int attrType) const
|
||||
{
|
||||
return (mAttrMap.find(attrType) != mAttrMap.end());
|
||||
}
|
||||
|
||||
|
||||
StunAttribute& StunMessage::attribute(int attrType)
|
||||
{
|
||||
AttributeMap::iterator attrIter = mAttrMap.find(attrType);
|
||||
if (attrIter != mAttrMap.end())
|
||||
return *attrIter->second;
|
||||
|
||||
throw Exception(CANNOT_FIND_ATTRIBUTE, attrType);
|
||||
}
|
||||
|
||||
const StunAttribute&
|
||||
StunMessage::operator [] (int attribute) const
|
||||
{
|
||||
AttributeMap::iterator attrIter = mAttrMap.find(attribute);
|
||||
if (attrIter != mAttrMap.end())
|
||||
return *attrIter->second;
|
||||
|
||||
throw Exception(CANNOT_FIND_ATTRIBUTE, attribute);
|
||||
}
|
||||
|
||||
StunAttribute&
|
||||
StunMessage::operator[] (int attribute)
|
||||
{
|
||||
AttributeMap::iterator attrIter = mAttrMap.find(attribute);
|
||||
if (attrIter != mAttrMap.end())
|
||||
return *attrIter->second;
|
||||
|
||||
// Create attribute
|
||||
StunAttribute* attr = NULL;
|
||||
|
||||
switch (attribute)
|
||||
{
|
||||
case StunAttribute::MappedAddress:
|
||||
attr = new MappedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::XorMappedAddress:
|
||||
attr = new XorMappedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::ErrorCode:
|
||||
attr = new ErrorCode();
|
||||
break;
|
||||
|
||||
case StunAttribute::AlternateServer:
|
||||
attr = new AlternateServer();
|
||||
break;
|
||||
|
||||
case StunAttribute::UnknownAttributes:
|
||||
attr = new UnknownAttributes();
|
||||
break;
|
||||
|
||||
case StunAttribute::Fingerprint:
|
||||
attr = new Fingerprint();
|
||||
break;
|
||||
|
||||
case StunAttribute::MessageIntegrity:
|
||||
attr = new MessageIntegrity();
|
||||
break;
|
||||
|
||||
case StunAttribute::Nonce:
|
||||
attr = new Nonce();
|
||||
break;
|
||||
|
||||
case StunAttribute::Realm:
|
||||
attr = new Realm();
|
||||
break;
|
||||
|
||||
case StunAttribute::Server:
|
||||
attr = new Server();
|
||||
break;
|
||||
|
||||
case StunAttribute::Username:
|
||||
attr = new Username();
|
||||
break;
|
||||
|
||||
case StunAttribute::ICEPriority:
|
||||
attr = new ICEPriority();
|
||||
break;
|
||||
|
||||
case StunAttribute::ControlledAttr:
|
||||
attr = new ControlledAttr();
|
||||
break;
|
||||
|
||||
case StunAttribute::ControllingAttr:
|
||||
attr = new ControllingAttr();
|
||||
break;
|
||||
|
||||
case StunAttribute::ICEUseCandidate:
|
||||
attr = new ICEUseCandidate();
|
||||
break;
|
||||
|
||||
case StunAttribute::XorRelayedAddress:
|
||||
attr = new XorRelayedAddress();
|
||||
break;
|
||||
|
||||
case StunAttribute::Lifetime:
|
||||
attr = new Lifetime();
|
||||
break;
|
||||
|
||||
default:
|
||||
attr = NULL;
|
||||
}
|
||||
|
||||
if (!attr)
|
||||
throw Exception(UNKNOWN_ATTRIBUTE, attribute);
|
||||
|
||||
mAttrMap.insert(std::pair<int, StunAttribute*>(attribute, attr));
|
||||
return *attr;
|
||||
}
|
||||
|
||||
Username&
|
||||
StunMessage::usernameAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<Username&>(self[StunAttribute::Username]);
|
||||
}
|
||||
|
||||
Realm&
|
||||
StunMessage::realmAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<Realm&>(self[StunAttribute::Realm]);
|
||||
}
|
||||
|
||||
ErrorCode&
|
||||
StunMessage::errorCodeAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<ErrorCode&>(self[StunAttribute::ErrorCode]);
|
||||
}
|
||||
|
||||
Nonce&
|
||||
StunMessage::nonceAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<Nonce&>(self[StunAttribute::Nonce]);
|
||||
}
|
||||
|
||||
MessageIntegrity&
|
||||
StunMessage::messageIntegrityAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<MessageIntegrity&>(self[StunAttribute::MessageIntegrity]);
|
||||
}
|
||||
|
||||
MappedAddress&
|
||||
StunMessage::mappedAddressAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<MappedAddress&>(self[StunAttribute::MappedAddress]);
|
||||
}
|
||||
|
||||
XorMappedAddress&
|
||||
StunMessage::xorMappedAddressAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<XorMappedAddress&>(self[StunAttribute::XorMappedAddress]);
|
||||
}
|
||||
|
||||
ControlledAttr&
|
||||
StunMessage::iceControlledAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<ControlledAttr&>(self[StunAttribute::ControlledAttr]);
|
||||
}
|
||||
|
||||
ControllingAttr&
|
||||
StunMessage::iceControllingAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<ControllingAttr&>(self[StunAttribute::ControllingAttr]);
|
||||
}
|
||||
|
||||
ICEPriority&
|
||||
StunMessage::icePriorityAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<ICEPriority&>(self[StunAttribute::ICEPriority]);
|
||||
}
|
||||
|
||||
Lifetime&
|
||||
StunMessage::lifetimeAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<Lifetime&>(self[StunAttribute::Lifetime]);
|
||||
}
|
||||
|
||||
XorRelayedAddress&
|
||||
StunMessage::xorRelayedAddressAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
return dynamic_cast<XorRelayedAddress&>(self[StunAttribute::XorRelayedAddress]);
|
||||
}
|
||||
|
||||
ChannelNumber&
|
||||
StunMessage::channelNumberAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
if (!self.hasAttribute(StunAttribute::ChannelNumber))
|
||||
self.addAttribute(new ChannelNumber());
|
||||
|
||||
return dynamic_cast<ChannelNumber&>(self[StunAttribute::ChannelNumber]);
|
||||
}
|
||||
|
||||
XorPeerAddress&
|
||||
StunMessage::xorPeerAddressAttr()
|
||||
{
|
||||
StunMessage& self = *this;
|
||||
if (!self.hasAttribute(StunAttribute::XorPeerAddress))
|
||||
self.addAttribute(new XorPeerAddress());
|
||||
return dynamic_cast<XorPeerAddress&>(self[StunAttribute::XorPeerAddress]);
|
||||
}
|
||||
|
||||
class CompareAttributesByOffsetFunctor
|
||||
{
|
||||
public:
|
||||
bool operator () (const StunAttribute* attr1, const StunAttribute* attr2)
|
||||
{
|
||||
return attr1->dataOffset() < attr2->dataOffset();
|
||||
}
|
||||
};
|
||||
|
||||
void StunMessage::dump(std::ostream& output)
|
||||
{
|
||||
switch (messageClass())
|
||||
{
|
||||
case RequestClass: output << "Request"; break;
|
||||
case IndicationClass: output << "Indication"; break;
|
||||
case SuccessClass: output << "Success"; break;
|
||||
case ErrorClass: output << "Error"; break;
|
||||
default:
|
||||
output << "Invalid";
|
||||
break;
|
||||
}
|
||||
|
||||
output << " / ";
|
||||
switch (messageType())
|
||||
{
|
||||
case Binding: output << "Binding"; break;
|
||||
case Allocate: output << "Allocate"; break;
|
||||
case Refresh: output << "Refresh"; break;
|
||||
case Send: output << "Send"; break;
|
||||
case Data: output << "Data"; break;
|
||||
case CreatePermission: output << "CreatePermission"; break;
|
||||
case ChannelBind: output << "ChannelBind"; break;
|
||||
default:
|
||||
output << "Invalid";
|
||||
break;
|
||||
}
|
||||
|
||||
output << std::endl;
|
||||
|
||||
// Sort attribytes by data offset
|
||||
std::vector<StunAttribute*> attrList;
|
||||
for (AttributeMap::iterator attrIter = mAttrMap.begin(); attrIter != mAttrMap.end(); attrIter++)
|
||||
attrList.push_back(attrIter->second);
|
||||
|
||||
std::sort(attrList.begin(), attrList.end(), CompareAttributesByOffsetFunctor());
|
||||
|
||||
for (unsigned i=0; i<attrList.size(); i++)
|
||||
{
|
||||
output << "Offset " << attrList[i]->dataOffset() <<": ";
|
||||
attrList[i]->dump(output);
|
||||
output << std::endl;
|
||||
}
|
||||
}
|
||||
200
src/libs/ice/ICEStunMessage.h
Normal file
200
src/libs/ice/ICEStunMessage.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_STUN_MESSAGE_H
|
||||
#define __ICE_STUN_MESSAGE_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "ICEByteBuffer.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class StunAttribute
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
NotExist = 0,
|
||||
MappedAddress = 1,
|
||||
Username = 6,
|
||||
MessageIntegrity = 8,
|
||||
ErrorCode = 9,
|
||||
UnknownAttributes = 10,
|
||||
Realm = 20,
|
||||
Nonce = 21,
|
||||
XorMappedAddress = 32,
|
||||
Server = 0x8022,
|
||||
AlternateServer = 0x8023,
|
||||
Fingerprint = 0x8028,
|
||||
|
||||
// TURN
|
||||
ChannelNumber = 0x0c,
|
||||
Lifetime = 0x0d,
|
||||
XorPeerAddress = 0x12,
|
||||
Data = 0x13,
|
||||
XorRelayedAddress = 0x16,
|
||||
RequestedAddressFamily = 0x17,
|
||||
RequestedTransport = 0x19,
|
||||
ReservedToken = 0x22,
|
||||
|
||||
// ICE
|
||||
ControlledAttr = 0x8029,
|
||||
ControllingAttr = 0x802a,
|
||||
ICEPriority = 0x0024,
|
||||
ICEUseCandidate = 0x0025
|
||||
};
|
||||
|
||||
StunAttribute();
|
||||
virtual ~StunAttribute();
|
||||
|
||||
virtual int type() const = 0;
|
||||
virtual void buildPacket(BufferWriter& writer) = 0;
|
||||
virtual bool parsePacket(BufferReader& reader) = 0;
|
||||
|
||||
void setDataOffset(size_t offset);
|
||||
size_t dataOffset() const;
|
||||
virtual void dump(std::ostream& output) = 0;
|
||||
|
||||
protected:
|
||||
int mType;
|
||||
int mLength;
|
||||
size_t mDataOffset;
|
||||
};
|
||||
|
||||
|
||||
class Username;
|
||||
class Realm;
|
||||
class Nonce;
|
||||
class ErrorCode;
|
||||
class MessageIntegrity;
|
||||
class MappedAddress;
|
||||
class XorMappedAddress;
|
||||
class ControlledAttr;
|
||||
class ControllingAttr;
|
||||
class ICEPriority;
|
||||
class Lifetime;
|
||||
class XorRelayedAddress;
|
||||
class ChannelNumber;
|
||||
class XorPeerAddress;
|
||||
|
||||
class StunMessage
|
||||
{
|
||||
public:
|
||||
struct TransactionID
|
||||
{
|
||||
unsigned char mValue[12];
|
||||
|
||||
bool operator == (const TransactionID& rhs);
|
||||
bool operator != (const TransactionID& rhs);
|
||||
|
||||
TransactionID();
|
||||
std::string toStdString();
|
||||
|
||||
static TransactionID generateNew();
|
||||
};
|
||||
|
||||
enum Class
|
||||
{
|
||||
RequestClass = 0, //0b00,
|
||||
IndicationClass = 1, //0b01,
|
||||
SuccessClass = 2, //0b10,
|
||||
ErrorClass = 3 //0b11
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
Binding = 1,
|
||||
Allocate = 3,
|
||||
Refresh = 4,
|
||||
Send = 6,
|
||||
Data = 7,
|
||||
CreatePermission = 8,
|
||||
ChannelBind = 9
|
||||
};
|
||||
|
||||
StunMessage();
|
||||
virtual ~StunMessage();
|
||||
|
||||
std::string comment();
|
||||
void setComment(std::string comment);
|
||||
|
||||
void setMessageType(Type type);
|
||||
Type messageType();
|
||||
|
||||
void setMessageClass(Class value);
|
||||
Class messageClass();
|
||||
|
||||
unsigned int magicCookie();
|
||||
bool isMagicCookieValid();
|
||||
|
||||
void setTransactionId(TransactionID id);
|
||||
TransactionID transactionId();
|
||||
|
||||
// Builds STUN packet using specified password (if MessageIntegrity attribute is add before)
|
||||
void buildPacket(ByteBuffer& buffer, const std::string& password);
|
||||
|
||||
// Parses STUN packet but do not validates it
|
||||
bool parsePacket(ByteBuffer& buffer);
|
||||
|
||||
// Validate parsed packet. Must be called right after ParsePacket() method.
|
||||
// Returns true if validated ok, false otherwise.
|
||||
bool validatePacket(std::string key);
|
||||
|
||||
// Adds attribute to STUN message. If attribute of the same type exists - new one will be add.
|
||||
void addAttribute(StunAttribute* attr);
|
||||
|
||||
// Sets attribute to STUN message. If attribute of the same type exists - it will be overwritten.
|
||||
void setAttribute(StunAttribute* attr);
|
||||
|
||||
// Checks if there is specified attribute
|
||||
bool hasAttribute(int attrType) const;
|
||||
|
||||
// Gets reference to attribute object
|
||||
StunAttribute& attribute(int attrType);
|
||||
|
||||
const StunAttribute& operator [] (int attribute) const;
|
||||
StunAttribute& operator[] (int attribute);
|
||||
|
||||
Username& usernameAttr();
|
||||
Realm& realmAttr();
|
||||
ErrorCode& errorCodeAttr();
|
||||
Nonce& nonceAttr();
|
||||
MessageIntegrity& messageIntegrityAttr();
|
||||
MappedAddress& mappedAddressAttr();
|
||||
XorMappedAddress& xorMappedAddressAttr();
|
||||
ControlledAttr& iceControlledAttr();
|
||||
ControllingAttr& iceControllingAttr();
|
||||
ICEPriority& icePriorityAttr();
|
||||
Lifetime& lifetimeAttr();
|
||||
XorRelayedAddress& xorRelayedAddressAttr();
|
||||
ChannelNumber& channelNumberAttr();
|
||||
XorPeerAddress& xorPeerAddressAttr();
|
||||
|
||||
void dump(std::ostream& output);
|
||||
|
||||
protected:
|
||||
unsigned int mMagicCookie; // STUN magic cookie predefined value
|
||||
Type mType; // Type of STUN message
|
||||
Class mClass; // Class of STUN message
|
||||
TransactionID mTransactionID; // Transaction ID of STUN message
|
||||
|
||||
typedef std::multimap<int, StunAttribute*> AttributeMap;
|
||||
|
||||
mutable AttributeMap mAttrMap; // Attribute list
|
||||
ByteBuffer mPacket2Parse; // Incoming packet
|
||||
ByteBuffer mPacket2Send; // Outgoing packet
|
||||
|
||||
std::string mComment; // Comment for this message
|
||||
|
||||
//Helper method to get bit with specified index from 'value' parameter.
|
||||
inline char bit(unsigned int value, size_t index);
|
||||
|
||||
//Helper method to set bit in specified 'result' parameter
|
||||
inline void setBit(unsigned int& result, size_t index, char value);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
493
src/libs/ice/ICEStunTransaction.cpp
Normal file
493
src/libs/ice/ICEStunTransaction.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICEStunTransaction.h"
|
||||
#include "ICEStunAttributes.h"
|
||||
#include "ICELog.h"
|
||||
#include "ICEError.h"
|
||||
#include "ICEAction.h"
|
||||
#include "ICETime.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <stdexcept>
|
||||
#endif
|
||||
|
||||
using namespace ice;
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
const char* Transaction::stateToString(State state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Running: return "Running";
|
||||
case Failed: return "Failed";
|
||||
case Success: return "Success";
|
||||
}
|
||||
return "Undefined";
|
||||
}
|
||||
|
||||
Transaction::Transaction()
|
||||
{
|
||||
mTransportType = IPv4;
|
||||
|
||||
// There is no default ICE stack
|
||||
mStackID = 0;
|
||||
|
||||
// There is no default logger
|
||||
mLog = NULL;
|
||||
|
||||
// Packet is not composed in constructor
|
||||
mComposed = false;
|
||||
|
||||
// Initial state of transaction - "Running"
|
||||
mState = Transaction::Running;
|
||||
|
||||
// Reset user data
|
||||
mTag = 0;
|
||||
|
||||
// Do not use message integrity attribute by default
|
||||
mMessageIntegrity = false;
|
||||
|
||||
// Do not use fingerprint attribute by default
|
||||
mFingerprint = false;
|
||||
|
||||
// Do not use long term credentials by default
|
||||
mLongTerm = false;
|
||||
|
||||
// Do not use short term credentials by default
|
||||
mShortTerm = false;
|
||||
|
||||
// Create new transaction ID
|
||||
generateId();
|
||||
|
||||
// CONTROLLING attribute is not used by default
|
||||
mEnableControlling = false;
|
||||
|
||||
// CONTROLLED attribute is not used by default
|
||||
mEnableControlled = false;
|
||||
|
||||
// PRIORITY attribute is not used by default
|
||||
mEnablePriority = false;
|
||||
mPriority = 0;
|
||||
|
||||
// Transaction is not cancelled
|
||||
mCancelled = false;
|
||||
|
||||
// Transaction is not relayed by default
|
||||
mRelayed = false;
|
||||
|
||||
// Component port is not set by default
|
||||
mComponent = 0;
|
||||
|
||||
mKeepalive = false;
|
||||
mInterval = 0;
|
||||
mUserObject = NULL;
|
||||
mType = None;
|
||||
mRemoved = false;
|
||||
mFailoverId = 0;
|
||||
}
|
||||
|
||||
Transaction::~Transaction()
|
||||
{
|
||||
}
|
||||
|
||||
void Transaction::generateId()
|
||||
{
|
||||
for (size_t i=0; i<12; i++)
|
||||
mTransactionID.mValue[i] = rand();
|
||||
}
|
||||
void Transaction::setStackId(int id)
|
||||
{
|
||||
mStackID = id;
|
||||
}
|
||||
|
||||
int Transaction::stackId()
|
||||
{
|
||||
return mStackID;
|
||||
}
|
||||
|
||||
void Transaction::setTransportType(int _type)
|
||||
{
|
||||
assert(_type == IPv4 || _type == IPv6);
|
||||
mTransportType = _type;
|
||||
}
|
||||
|
||||
int Transaction::transportType()
|
||||
{
|
||||
return mTransportType;
|
||||
}
|
||||
|
||||
std::string Transaction::toStdString()
|
||||
{
|
||||
std::ostringstream output;
|
||||
output << "State: " << Transaction::stateToString(mState) << ", ";
|
||||
output << "Transport: " << (mTransportType == IPv4 ? "IPv4" : "IPv6") << ", ";
|
||||
output << (mCancelled ? "Cancelled, " : "");
|
||||
output << "Destination: " << mDestination.toStdString().c_str() << ", ";
|
||||
output << "Comment: " << mComment.c_str();
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
void Transaction::setTag(unsigned int tag)
|
||||
{
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
unsigned Transaction::tag()
|
||||
{
|
||||
return mTag;
|
||||
}
|
||||
|
||||
void Transaction::enqueueMessage(ice::StunMessage& msg)
|
||||
{
|
||||
// Clear outgoing buffer
|
||||
mOutgoingData.clear();
|
||||
|
||||
// Check if message should be secured by message integrity
|
||||
std::string password = mPassword;
|
||||
if (true == mMessageIntegrity)
|
||||
{
|
||||
// Ensure credentials are enabled
|
||||
assert(mLongTerm || mShortTerm);
|
||||
|
||||
// Create username attribute
|
||||
Username* attr = new Username();
|
||||
|
||||
// Set username value
|
||||
if (mLongTerm || mShortTerm)
|
||||
attr->setValue(mUsername);
|
||||
|
||||
// Add username attribute itself
|
||||
msg.setAttribute(attr);
|
||||
|
||||
// Add message integrity attribute
|
||||
msg.setAttribute(new MessageIntegrity());
|
||||
}
|
||||
|
||||
if (mFingerprint)
|
||||
msg.setAttribute(new Fingerprint());
|
||||
|
||||
// Add ICE-CONTROLLED attribute if needed
|
||||
if (mEnableControlled)
|
||||
{
|
||||
assert(mTieBreaker.length() == 8);
|
||||
|
||||
ControlledAttr* attr = new ControlledAttr();
|
||||
attr->setTieBreaker(mTieBreaker);
|
||||
msg.setAttribute(attr);
|
||||
}
|
||||
|
||||
// Add ICE-CONTROLLING attribute if needed
|
||||
if (mEnableControlling)
|
||||
{
|
||||
assert(mTieBreaker.length() == 8);
|
||||
|
||||
ControllingAttr* attr = new ControllingAttr();
|
||||
attr->setTieBreaker(mTieBreaker);
|
||||
msg.setAttribute(attr);
|
||||
}
|
||||
|
||||
// Add ICE-PRIORITY attribute if needed
|
||||
if (mEnablePriority)
|
||||
{
|
||||
ICEPriority* attr = new ICEPriority();
|
||||
attr->setPriority(mPriority);
|
||||
msg.setAttribute(attr);
|
||||
}
|
||||
|
||||
msg.buildPacket(mOutgoingData, password);
|
||||
}
|
||||
|
||||
ByteBuffer* Transaction::generateData(bool force)
|
||||
{
|
||||
if (mCancelled)
|
||||
return NULL;
|
||||
|
||||
// Check if there is any outgoing data
|
||||
if (mOutgoingData.size() == 0)
|
||||
return NULL;
|
||||
|
||||
// Check if there is timeout for next transmission attempt
|
||||
if (mRTOTimer.isTimeout() && !force)
|
||||
{
|
||||
ICELogCritical(<< "Transaction " << mComment << " timeouted.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if transmission was made too much times - client should not send billions of packets.
|
||||
if (mRTOTimer.isAttemptLimitReached() && !force)
|
||||
return NULL;
|
||||
|
||||
// Check if time to retransmit now
|
||||
if (mRTOTimer.isTimeToRetransmit() || force)
|
||||
{
|
||||
mRTOTimer.attemptMade();
|
||||
|
||||
// Copy outgoing data
|
||||
ByteBuffer* buffer = new ByteBuffer(mOutgoingData);
|
||||
|
||||
buffer->setComment(mComment);
|
||||
buffer->setRemoteAddress(destination());
|
||||
buffer->setComponent(component());
|
||||
buffer->setComment(comment());
|
||||
buffer->setTag(this);
|
||||
return buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Transaction::restart()
|
||||
{
|
||||
mComposed = false;
|
||||
mState = Transaction::Running;
|
||||
mOutgoingData.clear();
|
||||
mRemoved = false;
|
||||
mRTOTimer.stop();
|
||||
mRTOTimer.start();
|
||||
}
|
||||
|
||||
bool Transaction::isTimeout()
|
||||
{
|
||||
if (mRTOTimer.isTimeout())
|
||||
{
|
||||
mState = Failed;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transaction::addMessageIntegrity(bool enable)
|
||||
{
|
||||
mMessageIntegrity = enable;
|
||||
}
|
||||
|
||||
void Transaction::addFingerprint(bool enable)
|
||||
{
|
||||
mFingerprint = enable;
|
||||
}
|
||||
|
||||
void Transaction::addShortTermCredentials(bool enable)
|
||||
{
|
||||
this->mShortTerm = enable;
|
||||
}
|
||||
|
||||
void Transaction::addLongTermCredentials(bool enable)
|
||||
{
|
||||
this->mLongTerm = enable;
|
||||
}
|
||||
|
||||
void Transaction::addControllingRole(const std::string& tieBreaker)
|
||||
{
|
||||
mEnableControlling = true;
|
||||
mTieBreaker = tieBreaker;
|
||||
}
|
||||
|
||||
void Transaction::addControlledRole(const std::string& tieBreaker)
|
||||
{
|
||||
mEnableControlled = true;
|
||||
mTieBreaker = tieBreaker;
|
||||
}
|
||||
|
||||
void Transaction::addPriority(unsigned int priority)
|
||||
{
|
||||
mEnablePriority = true;
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
unsigned int Transaction::priorityValue()
|
||||
{
|
||||
if (!mEnablePriority)
|
||||
throw Exception(NO_PRIORITY_ATTRIBUTE);
|
||||
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
|
||||
void Transaction::setUsername(const std::string& username)
|
||||
{
|
||||
mUsername = username;
|
||||
}
|
||||
|
||||
void Transaction::setPassword(const std::string& password)
|
||||
{
|
||||
mPassword = password;
|
||||
}
|
||||
|
||||
Transaction::State Transaction::state() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void Transaction::setAction(PAction action)
|
||||
{
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
PAction Transaction::action() const
|
||||
{
|
||||
return mAction;
|
||||
}
|
||||
|
||||
void Transaction::cancel()
|
||||
{
|
||||
mCancelled = true;
|
||||
}
|
||||
|
||||
bool Transaction::isCancelled() const
|
||||
{
|
||||
return mCancelled;
|
||||
}
|
||||
|
||||
void Transaction::setComment(const std::string& comment)
|
||||
{
|
||||
mComment = comment;
|
||||
}
|
||||
|
||||
std::string Transaction::comment() const
|
||||
{
|
||||
return mComment;
|
||||
}
|
||||
|
||||
void Transaction::setDestination(const NetworkAddress& addr)
|
||||
{
|
||||
mDestination = addr;
|
||||
}
|
||||
|
||||
void Transaction::setComponent(int component)
|
||||
{
|
||||
mComponent = component;
|
||||
}
|
||||
|
||||
int Transaction::component()
|
||||
{
|
||||
return mComponent;
|
||||
}
|
||||
|
||||
NetworkAddress& Transaction::destination()
|
||||
{
|
||||
return mDestination;
|
||||
}
|
||||
|
||||
bool Transaction::relayed()
|
||||
{
|
||||
return mRelayed;
|
||||
}
|
||||
|
||||
void Transaction::setRelayed(bool relayed)
|
||||
{
|
||||
mRelayed = relayed;
|
||||
}
|
||||
|
||||
bool Transaction::keepalive()
|
||||
{
|
||||
return mKeepalive;
|
||||
}
|
||||
|
||||
void Transaction::setKeepalive(bool keepalive)
|
||||
{
|
||||
mKeepalive = keepalive;
|
||||
}
|
||||
|
||||
unsigned Transaction::interval()
|
||||
{
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
void Transaction::setInterval(unsigned interval)
|
||||
{
|
||||
mInterval = interval;
|
||||
}
|
||||
|
||||
void* Transaction::userObject()
|
||||
{
|
||||
return mUserObject;
|
||||
}
|
||||
|
||||
void Transaction::setUserObject(void* obj)
|
||||
{
|
||||
mUserObject = obj;
|
||||
}
|
||||
|
||||
Transaction::Type Transaction::type()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void Transaction::setType(Transaction::Type t)
|
||||
{
|
||||
mType = t;
|
||||
}
|
||||
|
||||
bool Transaction::removed()
|
||||
{
|
||||
return mRemoved;
|
||||
}
|
||||
|
||||
void Transaction::setRemoved(bool removed)
|
||||
{
|
||||
if (!mRemoved && removed)
|
||||
ICELogDebug(<< "Transaction " << mComment << " removed.");
|
||||
|
||||
mRemoved = removed;
|
||||
}
|
||||
|
||||
unsigned Transaction::timestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
void Transaction::setTimestamp(unsigned timestamp)
|
||||
{
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
StunMessage::TransactionID Transaction::transactionId()
|
||||
{
|
||||
return mTransactionID;
|
||||
}
|
||||
|
||||
bool Transaction::hasToRunNow()
|
||||
{
|
||||
if (!mKeepalive)
|
||||
return true;
|
||||
|
||||
//bool cf = (mComment == "ClientRefresh");
|
||||
|
||||
unsigned current = ICETimeHelper::timestamp();
|
||||
/*if (cf)
|
||||
{
|
||||
ICELogDebug(<< "ClientRefresh interval: " << ICETimeHelper::findDelta(timestamp(), current) << ", interval " << interval()*1000);
|
||||
}*/
|
||||
|
||||
if (timestamp())
|
||||
{
|
||||
if (ICETimeHelper::findDelta(timestamp(), current) < interval() * 1000)
|
||||
return false;
|
||||
|
||||
setTimestamp(current);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
setTimestamp(current);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Transaction::setFailoverId(int failoverId)
|
||||
{
|
||||
mFailoverId = failoverId;
|
||||
}
|
||||
|
||||
int Transaction::failoverId() const
|
||||
{
|
||||
return mFailoverId;
|
||||
}
|
||||
245
src/libs/ice/ICEStunTransaction.h
Normal file
245
src/libs/ice/ICEStunTransaction.h
Normal file
@@ -0,0 +1,245 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_STUN_TRANSACTION_H
|
||||
#define __ICE_STUN_TRANSACTION_H
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
#include "ICEStunMessage.h"
|
||||
#include "ICEPacketTimer.h"
|
||||
#include "ICESmartPtr.h"
|
||||
#include "ICECandidatePair.h"
|
||||
#include "ICEAction.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class Logger;
|
||||
|
||||
class Transaction
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Binding = 1,
|
||||
Relaying = 2,
|
||||
KeepAlive = 4,
|
||||
All = 7,
|
||||
None = 8,
|
||||
};
|
||||
|
||||
enum State
|
||||
{
|
||||
Running = 0, //Transaction is running now
|
||||
Failed = 1, //Transaction is failed
|
||||
Success = 2 //Transaction is succeeded
|
||||
};
|
||||
static const char* stateToString(State state);
|
||||
|
||||
Transaction();
|
||||
virtual ~Transaction();
|
||||
|
||||
void generateId();
|
||||
|
||||
// Returns if transaction is relayed and must be sent to TURN server instead its regular destination
|
||||
bool relayed();
|
||||
|
||||
// Marks if transaction works through relay
|
||||
void setRelayed(bool relayed);
|
||||
|
||||
// Attempts to generate outgoing data.
|
||||
// It is time-sensitive method - it can return empty pointer if there is not time to send data to network.
|
||||
virtual ByteBuffer*
|
||||
generateData(bool force = false);
|
||||
|
||||
// Process incoming STUN message with source address
|
||||
virtual bool processData(StunMessage& msg, NetworkAddress& address) = 0;
|
||||
|
||||
// Attempts to restart transaction
|
||||
virtual void restart();
|
||||
|
||||
// Checks if transaction is timeouted.
|
||||
bool isTimeout();
|
||||
|
||||
// Adds MESSAGE-INTEGRITY attribute to outgoing messages
|
||||
void addMessageIntegrity(bool enable);
|
||||
|
||||
// Adds FINGERPRINT attribute to outgoing messages
|
||||
void addFingerprint(bool enable);
|
||||
|
||||
// Enables shortterm credentials on this transaction
|
||||
void addShortTermCredentials(bool enable);
|
||||
|
||||
// Enables longterm credentials on this transaction
|
||||
void addLongTermCredentials(bool enable);
|
||||
|
||||
// Adds CONTROLLING attribute with specified tie breaker value
|
||||
void addControllingRole(const std::string& tieBreaker);
|
||||
|
||||
// Adds CONTROLLED attribute with specified tie breaker value
|
||||
void addControlledRole(const std::string& tieBreaker);
|
||||
|
||||
// Adds PRIORITY attribute with specified value
|
||||
void addPriority(unsigned int priority);
|
||||
|
||||
// Returns received PRIORITY attribute
|
||||
unsigned int priorityValue();
|
||||
|
||||
void setUsername(const std::string& username);
|
||||
void setPassword(const std::string& password);
|
||||
|
||||
// Sets associated tag value
|
||||
void setTag(unsigned int tag);
|
||||
unsigned int tag();
|
||||
|
||||
// Returns transaction state - Succeeded, Failed, Running
|
||||
State state() const;
|
||||
|
||||
// Associates action to be performed on finish
|
||||
void setAction(PAction action);
|
||||
PAction action() const;
|
||||
|
||||
// Ceases all outgoing transmission for this transaction
|
||||
void cancel();
|
||||
bool isCancelled() const;
|
||||
|
||||
void setComment(const std::string& comment);
|
||||
std::string comment() const;
|
||||
|
||||
void setDestination(const NetworkAddress& addr);
|
||||
NetworkAddress& destination();
|
||||
|
||||
void setComponent(int component);
|
||||
int component();
|
||||
|
||||
void setStackId(int id);
|
||||
int stackId();
|
||||
|
||||
void setTransportType(int _type);
|
||||
int transportType();
|
||||
|
||||
bool keepalive();
|
||||
void setKeepalive(bool keepalive);
|
||||
|
||||
// Interval for keepalive transactions in seconds
|
||||
unsigned interval();
|
||||
void setInterval(unsigned interval);
|
||||
|
||||
unsigned timestamp();
|
||||
void setTimestamp(unsigned timestamp);
|
||||
|
||||
void* userObject();
|
||||
void setUserObject(void* obj);
|
||||
|
||||
Type type();
|
||||
void setType(Type t);
|
||||
|
||||
bool removed();
|
||||
void setRemoved(bool removed);
|
||||
|
||||
virtual bool hasToRunNow();
|
||||
|
||||
StunMessage::TransactionID transactionId();
|
||||
|
||||
std::string toStdString();
|
||||
|
||||
void setFailoverId(int failoverId);
|
||||
int failoverId() const;
|
||||
|
||||
protected:
|
||||
|
||||
// Logger
|
||||
Logger* mLog;
|
||||
|
||||
// Retransmission timer
|
||||
PacketScheduler mRTOTimer;
|
||||
|
||||
// Marks if transaction is cancelled
|
||||
bool mCancelled;
|
||||
|
||||
// Marks if packet was created
|
||||
bool mComposed;
|
||||
|
||||
// Long-term credentials
|
||||
std::string mUsername;
|
||||
std::string mPassword;
|
||||
|
||||
// Marks if long term credentials must be used
|
||||
bool mLongTerm;
|
||||
|
||||
// Marks if short term credentials must be used
|
||||
bool mShortTerm;
|
||||
|
||||
// Marks if CONTROLLING attribute must be used
|
||||
bool mEnableControlling;
|
||||
|
||||
// Marks if CONTROLLED attribute must be used
|
||||
bool mEnableControlled;
|
||||
|
||||
// Marks if PRIORITY attribute must be used
|
||||
bool mEnablePriority;
|
||||
|
||||
// Priority value
|
||||
unsigned int mPriority;
|
||||
|
||||
// Tie breaker value for CONTROLLING&CONTROLLED attributes
|
||||
std::string mTieBreaker;
|
||||
|
||||
// Transaction ID
|
||||
StunMessage::TransactionID mTransactionID;
|
||||
|
||||
// Buffer to hold raw outgoing data
|
||||
ByteBuffer mOutgoingData;
|
||||
|
||||
// Marks if fingerprint attribute must be used for outgoing messages
|
||||
bool mFingerprint;
|
||||
|
||||
// Marks if message integrity attribyte must be used for outgoing messages
|
||||
bool mMessageIntegrity;
|
||||
|
||||
// Tag associated with transaction
|
||||
unsigned int mTag;
|
||||
|
||||
// Transaction state - Running, Failed or Success
|
||||
State mState;
|
||||
|
||||
// Action that must be run on transaction finish
|
||||
PAction mAction;
|
||||
|
||||
// Comment that describes this transaction
|
||||
std::string mComment;
|
||||
|
||||
// Destination address
|
||||
NetworkAddress mDestination;
|
||||
|
||||
// Relayed flag
|
||||
bool mRelayed;
|
||||
|
||||
// Source component's ID
|
||||
int mComponent;
|
||||
|
||||
// Used ICE stack ID. This member is used for debugging purpose.
|
||||
int mStackID;
|
||||
int mTransportType;
|
||||
|
||||
bool mKeepalive;
|
||||
unsigned mInterval;
|
||||
void* mUserObject;
|
||||
Type mType;
|
||||
bool mRemoved;
|
||||
int mFailoverId;
|
||||
|
||||
// Timestamp when transaction runs in last time. Default is zero.
|
||||
unsigned mTimestamp;
|
||||
|
||||
// Updates msg with auth. data (MessageDigest + Fingerprint) + fills mOutgoingData
|
||||
void enqueueMessage(ice::StunMessage& msg);
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
99
src/libs/ice/ICESync.cpp
Normal file
99
src/libs/ice/ICESync.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICESync.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeCriticalSection(&mSection);
|
||||
#else
|
||||
pthread_mutexattr_init(&mAttr);
|
||||
pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mMutex, &mAttr);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection(&mSection);
|
||||
#else
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
pthread_mutexattr_destroy(&mAttr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&mSection);
|
||||
#else
|
||||
pthread_mutex_lock(&mMutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&mSection);
|
||||
#else
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Lock::Lock(Mutex &mutex)
|
||||
:mMutex(mutex)
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
|
||||
Lock::~Lock()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static pthread_mutex_t DoGuard = PTHREAD_MUTEX_INITIALIZER ;
|
||||
#endif
|
||||
|
||||
long Atomic::increment(long *value)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::InterlockedIncrement((LONG*)value);
|
||||
#else
|
||||
pthread_mutex_lock(&DoGuard);
|
||||
(void)*value++;
|
||||
long result = *value;
|
||||
pthread_mutex_unlock(&DoGuard);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
long Atomic::decrement(long *value)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::InterlockedDecrement((LONG*)value);
|
||||
#else
|
||||
pthread_mutex_lock(&DoGuard);
|
||||
(void)*value--;
|
||||
long result = *value;
|
||||
pthread_mutex_unlock(&DoGuard);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* ThreadInfo::currentThread()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (void*)::GetCurrentThreadId();
|
||||
#else
|
||||
return (void*)(::pthread_self());
|
||||
#endif
|
||||
}
|
||||
62
src/libs/ice/ICESync.h
Normal file
62
src/libs/ice/ICESync.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_SYNC_H
|
||||
#define __ICE_SYNC_H
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace ice
|
||||
{
|
||||
|
||||
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
protected:
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION mSection;
|
||||
#else
|
||||
pthread_mutexattr_t mAttr;
|
||||
pthread_mutex_t mMutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock(Mutex& mutex);
|
||||
~Lock();
|
||||
|
||||
protected:
|
||||
Mutex& mMutex;
|
||||
};
|
||||
|
||||
class Atomic
|
||||
{
|
||||
public:
|
||||
static long increment(long* value);
|
||||
static long decrement(long* value);
|
||||
};
|
||||
|
||||
class ThreadInfo
|
||||
{
|
||||
public:
|
||||
static void* currentThread();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
232
src/libs/ice/ICETime.cpp
Normal file
232
src/libs/ice/ICETime.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/* Copyright(C) 2007-2017 Voipobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <time.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/times.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
|
||||
#include "ICETime.h"
|
||||
using namespace ice;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static unsigned GetMilliseconds()
|
||||
{
|
||||
return ::GetTickCount();
|
||||
}
|
||||
#else
|
||||
static int ClocksPerSec = (int)sysconf(_SC_CLK_TCK);
|
||||
static tms GlobalStartTimeStruct;
|
||||
static clock_t GlobalStartTime = times(&GlobalStartTimeStruct);
|
||||
static unsigned GetMilliseconds()
|
||||
{
|
||||
tms t;
|
||||
clock_t result = times(&t) - GlobalStartTime;
|
||||
float ticksPerMilliseconds = ClocksPerSec / 1000.0;
|
||||
return unsigned( result / ticksPerMilliseconds );
|
||||
}
|
||||
#endif
|
||||
|
||||
TimeoutDetector::TimeoutDetector()
|
||||
:mTimestamp(0), mAttempt(0)
|
||||
{
|
||||
}
|
||||
|
||||
TimeoutDetector::~TimeoutDetector()
|
||||
{
|
||||
}
|
||||
|
||||
void TimeoutDetector::start()
|
||||
{
|
||||
mTimestamp = ::GetMilliseconds();
|
||||
}
|
||||
|
||||
void TimeoutDetector::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void TimeoutDetector::reset()
|
||||
{
|
||||
mAttempt = 0;
|
||||
}
|
||||
|
||||
bool TimeoutDetector::isTimeToRetransmit()
|
||||
{
|
||||
static time_t RetransmissionIntervalArray[] = { 100, 200, 200, 200, 200, 200 };
|
||||
|
||||
//get current time
|
||||
time_t current = ::GetMilliseconds();
|
||||
|
||||
//find the delta
|
||||
time_t delta = current - mTimestamp;
|
||||
|
||||
//find a wait interval for the mAttempt value
|
||||
time_t referenceInterval = 0;
|
||||
|
||||
size_t arrayLength = sizeof(RetransmissionIntervalArray) / sizeof(RetransmissionIntervalArray[0]);
|
||||
|
||||
if (mAttempt < arrayLength )
|
||||
referenceInterval = RetransmissionIntervalArray[mAttempt];
|
||||
else
|
||||
referenceInterval = RetransmissionIntervalArray[arrayLength-1];
|
||||
|
||||
if (delta >= referenceInterval)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TimeoutDetector::nextAttempt()
|
||||
{
|
||||
mAttempt++;
|
||||
mTimestamp = ::GetMilliseconds();
|
||||
}
|
||||
|
||||
bool TimeoutDetector::isTimeout()
|
||||
{
|
||||
// Get current time
|
||||
time_t current = ::GetMilliseconds();
|
||||
|
||||
// Find the delta
|
||||
time_t delta = current - mTimestamp;
|
||||
|
||||
return delta > ICE_TIMEOUT_VALUE;
|
||||
}
|
||||
|
||||
|
||||
ICEStartTimer::ICEStartTimer()
|
||||
:mTimestamp(0), mInterval(0), mEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
ICEStartTimer::~ICEStartTimer()
|
||||
{
|
||||
}
|
||||
|
||||
void ICEStartTimer::start(unsigned interval)
|
||||
{
|
||||
mInterval = interval;
|
||||
mTimestamp = ::GetMilliseconds();
|
||||
mEnabled = true;
|
||||
}
|
||||
|
||||
void ICEStartTimer::stop()
|
||||
{
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
bool ICEStartTimer::isTimeToBegin()
|
||||
{
|
||||
if (!mEnabled)
|
||||
return false;
|
||||
|
||||
unsigned t = ::GetMilliseconds();
|
||||
unsigned delta = 0;
|
||||
|
||||
if (t < 0x7FFFFFFF && mTimestamp > 0x7FFFFFFF)
|
||||
delta = 0xFFFFFFFF - mTimestamp + t;
|
||||
else
|
||||
delta = t - mTimestamp;
|
||||
|
||||
return delta >= mInterval;
|
||||
}
|
||||
|
||||
ICEScheduleTimer::ICEScheduleTimer()
|
||||
:mEnabled(false), mTimestamp(0), mTail(0), mInterval(0)
|
||||
{
|
||||
}
|
||||
|
||||
ICEScheduleTimer::~ICEScheduleTimer()
|
||||
{
|
||||
}
|
||||
|
||||
void ICEScheduleTimer::start(int interval)
|
||||
{
|
||||
mEnabled = true;
|
||||
mInterval = interval;
|
||||
mTail = 1; // First check is immediate!
|
||||
mTimestamp = ICETimeHelper::timestamp();
|
||||
}
|
||||
|
||||
bool ICEScheduleTimer::isTimeToSend()
|
||||
{
|
||||
if (!mEnabled)
|
||||
return false;
|
||||
|
||||
#ifdef ICE_SIMPLE_SCHEDULE
|
||||
unsigned current = ICETimeHelper::timestamp();
|
||||
unsigned delta = ICETimeHelper::findDelta(mTimestamp, current);
|
||||
|
||||
return delta > mInterval;
|
||||
#else
|
||||
// Check if there are already scheduled checks
|
||||
if (mTail > 0)
|
||||
{
|
||||
mTail--;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned current = ICETimeHelper::timestamp();
|
||||
unsigned delta = ICETimeHelper::findDelta(mTimestamp, current);
|
||||
|
||||
if (delta > (unsigned)mInterval)
|
||||
{
|
||||
mTail += delta / mInterval;
|
||||
mTimestamp = current - delta % mInterval;
|
||||
}
|
||||
|
||||
if (mTail > 0)
|
||||
{
|
||||
mTail--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ICEScheduleTimer::stop()
|
||||
{
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
int ICEScheduleTimer::remaining()
|
||||
{
|
||||
unsigned delta = ICETimeHelper::findDelta(mTimestamp, ICETimeHelper::timestamp());
|
||||
#ifdef ICE_SIMPLE_SCHEDULE
|
||||
return (delta >= mInterval) ? 0 : mInterval - delta;
|
||||
#else
|
||||
return ((int)delta + mTail >= mInterval) ? 0 : mInterval - delta - mTail;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ICEScheduleTimer::interval() const
|
||||
{
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
void ICEScheduleTimer::setInterval(int interval)
|
||||
{
|
||||
mTail = 0;
|
||||
mInterval = interval;
|
||||
}
|
||||
|
||||
unsigned int ICETimeHelper::timestamp()
|
||||
{
|
||||
return ::GetMilliseconds();
|
||||
}
|
||||
|
||||
unsigned int ICETimeHelper::findDelta(unsigned int oldTS, unsigned int newTS)
|
||||
{
|
||||
return (oldTS > 0x7FFFFFFF && newTS < 0x7FFFFFFF) ? 0xFFFFFFFF - oldTS + newTS : newTS - oldTS;
|
||||
}
|
||||
113
src/libs/ice/ICETime.h
Normal file
113
src/libs/ice/ICETime.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_TIME_H
|
||||
#define __ICE_TIME_H
|
||||
|
||||
#include "ICEPlatform.h"
|
||||
#include "ICETypes.h"
|
||||
#ifndef _WIN32
|
||||
# include <time.h>
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
namespace ice {
|
||||
|
||||
class TimeoutDetector
|
||||
{
|
||||
public:
|
||||
TimeoutDetector();
|
||||
~TimeoutDetector();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
/*! Resets the countdown. */
|
||||
void reset();
|
||||
|
||||
/*! Checks if it is a time to retransmit non-answered packet
|
||||
* @return true if time to retransmit packet, false if not. */
|
||||
bool isTimeToRetransmit();
|
||||
|
||||
/*! Notifies timer about retransmission. */
|
||||
void nextAttempt();
|
||||
|
||||
/*! Checks if it is a time to mark the transaction timeouted.
|
||||
* @return true if transaction is timeouted, false if not. */
|
||||
bool isTimeout();
|
||||
protected:
|
||||
|
||||
/// Time tracking member
|
||||
time_t mTimestamp;
|
||||
|
||||
/// Retransmission attempt counter
|
||||
size_t mAttempt;
|
||||
};
|
||||
|
||||
class ICEStartTimer
|
||||
{
|
||||
public:
|
||||
ICEStartTimer();
|
||||
~ICEStartTimer();
|
||||
|
||||
void start(unsigned interval);
|
||||
bool isTimeToBegin();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
unsigned mTimestamp;
|
||||
unsigned mInterval;
|
||||
bool mEnabled;
|
||||
};
|
||||
|
||||
/*! Class intendend to schedule connectivity checks. It is simple timer which returns true from IsTimeToSend() every interval milliseconds. */
|
||||
class ICEScheduleTimer
|
||||
{
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
ICEScheduleTimer();
|
||||
|
||||
/*! Destructor. */
|
||||
~ICEScheduleTimer();
|
||||
|
||||
/*! Start timer with interval in milliseconds. */
|
||||
void start(int interval);
|
||||
|
||||
/*! Check if it is a time to send next check. This method must be called in while (IsTimeToSend()) {} loop -
|
||||
* as multiple checks can be scheduled for elapsed time interval. */
|
||||
bool isTimeToSend();
|
||||
|
||||
/*! Stops timer. */
|
||||
void stop();
|
||||
|
||||
/*! Returns remaining time in milliseconds. */
|
||||
int remaining();
|
||||
|
||||
/*! Returns interval time in milliseconds. */
|
||||
int interval() const;
|
||||
|
||||
/*! Sets new interval time in milliseconds. Can be called on active timer, will reschedule timeout event. */
|
||||
void setInterval(int interval);
|
||||
|
||||
protected:
|
||||
bool mEnabled; /// Marks if timer is started
|
||||
unsigned mTimestamp; /// Last timestamp
|
||||
int mTail; /// How much checks was scheduled for elapsed time interval.
|
||||
int mInterval; /// Timer interval
|
||||
};
|
||||
|
||||
class ICETimeHelper
|
||||
{
|
||||
public:
|
||||
static unsigned timestamp();
|
||||
static unsigned findDelta(unsigned oldTimestamp, unsigned int newTimestamp);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
288
src/libs/ice/ICETransactionList.cpp
Normal file
288
src/libs/ice/ICETransactionList.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICETransactionList.h"
|
||||
#include "ICEAction.h"
|
||||
#include "ICELog.h"
|
||||
|
||||
using namespace ice;
|
||||
|
||||
#define LOG_SUBSYSTEM "ICE"
|
||||
|
||||
TransactionList::TransactionList()
|
||||
:mRegularIndex(0), mPrioritizedIndex(0)
|
||||
{
|
||||
mRegularList.reserve(1024);
|
||||
mPrioritizedList.reserve(1024);
|
||||
}
|
||||
|
||||
TransactionList::~TransactionList()
|
||||
{
|
||||
for (unsigned i=0; i<mPrioritizedList.size(); i++)
|
||||
delete mPrioritizedList[i];
|
||||
for (unsigned i=0; i<mRegularList.size(); i++)
|
||||
delete mRegularList[i];
|
||||
}
|
||||
|
||||
bool TransactionList::processIncoming(List& l, StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
unsigned index;
|
||||
for (index = 0; index < l.size(); index++)
|
||||
{
|
||||
bool finished = false;
|
||||
Transaction* t = l[index];
|
||||
if (t->removed())
|
||||
continue;
|
||||
|
||||
finished = t->state() == Transaction::Success || t->state() == Transaction::Failed;
|
||||
if (!finished)
|
||||
{
|
||||
if (t->processData(msg, address))
|
||||
{
|
||||
finished = t->state() == Transaction::Success || t->state() == Transaction::Failed;
|
||||
if (finished && t->action())
|
||||
t->action()->finished(*t);
|
||||
|
||||
// Previous action could restart transaction - so check transaction state again
|
||||
finished = t->state() == Transaction::Success || t->state() == Transaction::Failed;
|
||||
|
||||
if (finished)
|
||||
{
|
||||
if (t->keepalive())
|
||||
t->restart();
|
||||
else
|
||||
t->setRemoved(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransactionList::processIncoming(StunMessage& msg, NetworkAddress& address)
|
||||
{
|
||||
if (processIncoming(mRegularList, msg, address))
|
||||
return true;
|
||||
if (processIncoming(mPrioritizedList, msg, address))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransactionList::checkTimeout(List& l)
|
||||
{
|
||||
// Iterate checks
|
||||
unsigned index;
|
||||
for (index=0; index<l.size(); index++)
|
||||
{
|
||||
// Check only running transactions
|
||||
Transaction* t = l[index];
|
||||
if (t->state() == Transaction::Running)
|
||||
{
|
||||
// Is transaction timeouted?
|
||||
if (t->isTimeout())
|
||||
{
|
||||
ICELogDebug(<< "Transaction " << t->comment() << " timeouted");
|
||||
if (t->action())
|
||||
t->action()->finished(*t);
|
||||
|
||||
if (t->keepalive())
|
||||
t->restart();
|
||||
else
|
||||
t->setRemoved(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransactionList::checkTimeout()
|
||||
{
|
||||
if (checkTimeout(mPrioritizedList))
|
||||
return true;
|
||||
if (checkTimeout(mRegularList))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransactionList::addRegular(Transaction* p)
|
||||
{
|
||||
List::iterator regularIter = std::find(mRegularList.begin(), mRegularList.end(), p);
|
||||
List::iterator prioritizedIter = std::find(mPrioritizedList.begin(), mPrioritizedList.end(), p);
|
||||
|
||||
if (regularIter == mRegularList.end() && prioritizedIter == mPrioritizedList.end())
|
||||
mRegularList.push_back(p);
|
||||
}
|
||||
|
||||
void TransactionList::erase(Transaction* p)
|
||||
{
|
||||
List::iterator it = std::find(mRegularList.begin(), mRegularList.end(), p);
|
||||
if (it != mRegularList.end())
|
||||
p->setRemoved(true);
|
||||
else
|
||||
{
|
||||
it = std::find(mPrioritizedList.begin(), mPrioritizedList.end(), p);
|
||||
if (it != mPrioritizedList.end())
|
||||
p->setRemoved(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionList::erase(const TransactionCondition& cond)
|
||||
{
|
||||
List::iterator iter;
|
||||
for (iter = mRegularList.begin();iter != mRegularList.end(); iter++)
|
||||
{
|
||||
if (cond.check(*iter))
|
||||
(*iter)->setRemoved(true);
|
||||
}
|
||||
|
||||
for (iter = mPrioritizedList.begin(); iter != mPrioritizedList.end(); iter++)
|
||||
{
|
||||
if (cond.check(*iter))
|
||||
(*iter)->setRemoved(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionList::eraseMatching(unsigned types)
|
||||
{
|
||||
List::iterator iter;
|
||||
for (iter = mRegularList.begin();iter != mRegularList.end(); iter++)
|
||||
{
|
||||
if (types && (*iter)->type())
|
||||
(*iter)->setRemoved(true);
|
||||
}
|
||||
|
||||
for (iter = mPrioritizedList.begin(); iter != mPrioritizedList.end(); iter++)
|
||||
{
|
||||
if (types && (*iter)->type())
|
||||
(*iter)->setRemoved(true);
|
||||
}
|
||||
}
|
||||
|
||||
int TransactionList::copyMatching(const TransactionCondition& condition, std::vector<Transaction*>& result)
|
||||
{
|
||||
List::iterator iter;
|
||||
int counter = (int)result.size();
|
||||
for (iter = mRegularList.begin();iter != mRegularList.end(); iter++)
|
||||
{
|
||||
if (condition.check(*iter))
|
||||
result.push_back(*iter);
|
||||
}
|
||||
|
||||
for (iter = mPrioritizedList.begin(); iter != mPrioritizedList.end(); iter++)
|
||||
{
|
||||
if (condition.check(*iter))
|
||||
result.push_back(*iter);
|
||||
}
|
||||
|
||||
return (int)result.size() - counter;
|
||||
}
|
||||
|
||||
void TransactionList::prioritize(Transaction* p)
|
||||
{
|
||||
// Check if this transaction is prioritized already
|
||||
List::iterator iter = std::find(mPrioritizedList.begin(), mPrioritizedList.end(), p);
|
||||
if (iter != mPrioritizedList.end())
|
||||
return;
|
||||
|
||||
// Check if the transaction is in regular list
|
||||
iter = std::find(mRegularList.begin(), mRegularList.end(), p);
|
||||
if (iter == mRegularList.end())
|
||||
return;
|
||||
|
||||
// Remove the transaction from regular list
|
||||
mRegularList.erase(iter);
|
||||
|
||||
// And move to priority list
|
||||
mPrioritizedList.push_back(p);
|
||||
}
|
||||
|
||||
Transaction* TransactionList::getNextTransaction()
|
||||
{
|
||||
while (mPrioritizedIndex < mPrioritizedList.size() && mPrioritizedList[mPrioritizedIndex]->removed())
|
||||
mPrioritizedIndex++;
|
||||
if (mPrioritizedIndex < mPrioritizedList.size())
|
||||
return mPrioritizedList[mPrioritizedIndex++];
|
||||
|
||||
while (mRegularIndex < mRegularList.size() && mRegularList[mRegularIndex]->removed())
|
||||
mRegularIndex++;
|
||||
if (mRegularIndex < mRegularList.size())
|
||||
return mRegularList[mRegularIndex++];
|
||||
|
||||
mPrioritizedIndex = 0;
|
||||
mRegularIndex = 0;
|
||||
|
||||
while (mPrioritizedIndex < mPrioritizedList.size() && mPrioritizedList[mPrioritizedIndex]->removed())
|
||||
mPrioritizedIndex++;
|
||||
if (mPrioritizedIndex < mPrioritizedList.size())
|
||||
return mPrioritizedList[mPrioritizedIndex++];
|
||||
|
||||
while (mRegularIndex < mRegularList.size() && mRegularList[mRegularIndex]->removed())
|
||||
mRegularIndex++;
|
||||
if (mRegularIndex < mRegularList.size())
|
||||
return mRegularList[mRegularIndex++];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TransactionList::addPrioritized(Transaction* p)
|
||||
{
|
||||
List::iterator regularIter = std::find(mRegularList.begin(), mRegularList.end(), p);
|
||||
List::iterator prioritizedIter = std::find(mPrioritizedList.begin(), mPrioritizedList.end(), p);
|
||||
|
||||
if (regularIter != mRegularList.end())
|
||||
mRegularList.erase(regularIter);
|
||||
|
||||
if (prioritizedIter == mPrioritizedList.end())
|
||||
mPrioritizedList.push_back(p);
|
||||
}
|
||||
|
||||
int TransactionList::count()
|
||||
{
|
||||
return (int)mRegularList.size() + (int)mPrioritizedList.size();
|
||||
}
|
||||
|
||||
void TransactionList::clear()
|
||||
{
|
||||
mRegularList.clear();
|
||||
mPrioritizedList.clear();
|
||||
}
|
||||
|
||||
bool TransactionList::exists(Transaction* t)
|
||||
{
|
||||
List::iterator regularIter = std::find(mRegularList.begin(), mRegularList.end(), t);
|
||||
List::iterator prioritizedIter = std::find(mPrioritizedList.begin(), mPrioritizedList.end(), t);
|
||||
|
||||
bool removed = true;
|
||||
|
||||
if (regularIter != mRegularList.end())
|
||||
removed &= (*regularIter)->removed();
|
||||
|
||||
if (prioritizedIter != mPrioritizedList.end())
|
||||
removed &= (*prioritizedIter)->removed();
|
||||
|
||||
return !removed;
|
||||
}
|
||||
|
||||
bool TransactionList::exists(const TransactionCondition& condition)
|
||||
{
|
||||
int counter = 0;
|
||||
List::iterator iter;
|
||||
for (iter = mRegularList.begin();iter != mRegularList.end(); iter++)
|
||||
{
|
||||
if (condition.check(*iter))
|
||||
counter++;
|
||||
}
|
||||
|
||||
for (iter = mPrioritizedList.begin(); iter != mPrioritizedList.end(); iter++)
|
||||
{
|
||||
if (condition.check(*iter))
|
||||
counter++;
|
||||
}
|
||||
return counter > 0;
|
||||
}
|
||||
50
src/libs/ice/ICETransactionList.h
Normal file
50
src/libs/ice/ICETransactionList.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_TRANSACTION_LIST_H
|
||||
#define __ICE_TRANSACTION_LIST_H
|
||||
|
||||
#include <vector>
|
||||
#include "ICEStunTransaction.h"
|
||||
|
||||
namespace ice
|
||||
{
|
||||
class TransactionCondition
|
||||
{
|
||||
public:
|
||||
virtual bool check(Transaction* t) const = 0;
|
||||
};
|
||||
|
||||
class TransactionList
|
||||
{
|
||||
protected:
|
||||
typedef std::vector<Transaction*> List;
|
||||
List mRegularList, mPrioritizedList;
|
||||
unsigned mRegularIndex, mPrioritizedIndex;
|
||||
|
||||
bool processIncoming(List& l, StunMessage& msg, NetworkAddress& address);
|
||||
bool checkTimeout(List& l);
|
||||
public:
|
||||
TransactionList();
|
||||
~TransactionList();
|
||||
|
||||
bool processIncoming(StunMessage& msg, NetworkAddress& address);
|
||||
bool checkTimeout();
|
||||
void addRegular(Transaction* p);
|
||||
void addPrioritized(Transaction* p);
|
||||
void prioritize(Transaction* p);
|
||||
void erase(Transaction* p);
|
||||
void erase(const TransactionCondition& condition);
|
||||
void eraseMatching(unsigned types);
|
||||
int copyMatching(const TransactionCondition& condition, std::vector<Transaction*>& result);
|
||||
Transaction* getNextTransaction();
|
||||
int count();
|
||||
void clear();
|
||||
bool exists(Transaction* t);
|
||||
bool exists(const TransactionCondition& condition);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
16
src/libs/ice/ICETypes.h
Normal file
16
src/libs/ice/ICETypes.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ICE_TYPES_H
|
||||
#define __ICE_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//#define INVALID_SOCKET (-1)
|
||||
# ifndef SOCKET
|
||||
# define SOCKET int
|
||||
# endif
|
||||
|
||||
#endif
|
||||
373
src/libs/ice/License.txt
Normal file
373
src/libs/ice/License.txt
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
154
src/libs/ice/ice_vs2013.vcxproj
Normal file
154
src/libs/ice/ice_vs2013.vcxproj
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>ice</ProjectName>
|
||||
<ProjectGuid>{5E483901-D5B2-40EE-B9ED-35F8D64AC04E}</ProjectGuid>
|
||||
<RootNamespace>ICEImpl</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>12.0.21005.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../openssl/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;USE_OPENSSL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>../openssl/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;USE_OPENSSL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ICEAction.h" />
|
||||
<ClInclude Include="ICEAddress.h" />
|
||||
<ClInclude Include="ICEAuthTransaction.h" />
|
||||
<ClInclude Include="ICEBinding.h" />
|
||||
<ClInclude Include="ICEBox.h" />
|
||||
<ClInclude Include="ICEBoxImpl.h" />
|
||||
<ClInclude Include="ICEByteBuffer.h" />
|
||||
<ClInclude Include="ICECandidate.h" />
|
||||
<ClInclude Include="ICECandidatePair.h" />
|
||||
<ClInclude Include="ICECheckList.h" />
|
||||
<ClInclude Include="ICECRC32.h" />
|
||||
<ClInclude Include="ICEError.h" />
|
||||
<ClInclude Include="ICEEvent.h" />
|
||||
<ClInclude Include="ICELog.h" />
|
||||
<ClInclude Include="ICEMD5.h" />
|
||||
<ClInclude Include="ICENetworkHelper.h" />
|
||||
<ClInclude Include="ICEPacketTimer.h" />
|
||||
<ClInclude Include="ICEPlatform.h" />
|
||||
<ClInclude Include="ICERelaying.h" />
|
||||
<ClInclude Include="ICEReliableTransport.h" />
|
||||
<ClInclude Include="ICESession.h" />
|
||||
<ClInclude Include="ICESHA1.h" />
|
||||
<ClInclude Include="ICESmartCount.h" />
|
||||
<ClInclude Include="ICESmartPtr.h" />
|
||||
<ClInclude Include="ICESocket.h" />
|
||||
<ClInclude Include="ICEStream.h" />
|
||||
<ClInclude Include="ICEStunAttributes.h" />
|
||||
<ClInclude Include="ICEStunConfig.h" />
|
||||
<ClInclude Include="ICEStunMessage.h" />
|
||||
<ClInclude Include="ICEStunTransaction.h" />
|
||||
<ClInclude Include="ICESync.h" />
|
||||
<ClInclude Include="ICETime.h" />
|
||||
<ClInclude Include="ICETransactionList.h" />
|
||||
<ClInclude Include="ICETypes.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ICEAddress.cpp" />
|
||||
<ClCompile Include="ICEAuthTransaction.cpp" />
|
||||
<ClCompile Include="ICEBinding.cpp" />
|
||||
<ClCompile Include="ICEBox.cpp" />
|
||||
<ClCompile Include="ICEBoxImpl.cpp" />
|
||||
<ClCompile Include="ICEByteBuffer.cpp" />
|
||||
<ClCompile Include="ICECandidate.cpp" />
|
||||
<ClCompile Include="ICECandidatePair.cpp" />
|
||||
<ClCompile Include="ICECheckList.cpp" />
|
||||
<ClCompile Include="ICECRC32.cpp" />
|
||||
<ClCompile Include="ICEError.cpp" />
|
||||
<ClCompile Include="ICELog.cpp" />
|
||||
<ClCompile Include="ICEMD5.cpp" />
|
||||
<ClCompile Include="ICENetworkHelper.cpp" />
|
||||
<ClCompile Include="ICEPacketTimer.cpp" />
|
||||
<ClCompile Include="ICERelaying.cpp" />
|
||||
<ClCompile Include="ICEReliableTransport.cpp" />
|
||||
<ClCompile Include="ICESession.cpp" />
|
||||
<ClCompile Include="ICESHA1.cpp" />
|
||||
<ClCompile Include="ICEStream.cpp" />
|
||||
<ClCompile Include="ICEStunAttributes.cpp" />
|
||||
<ClCompile Include="ICEStunConfig.cpp" />
|
||||
<ClCompile Include="ICEStunMessage.cpp" />
|
||||
<ClCompile Include="ICEStunTransaction.cpp" />
|
||||
<ClCompile Include="ICESync.cpp" />
|
||||
<ClCompile Include="ICETime.cpp" />
|
||||
<ClCompile Include="ICETransactionList.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
11
src/libs/ice/ice_vs2013.vcxproj.filters
Normal file
11
src/libs/ice/ice_vs2013.vcxproj.filters
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ICESync.cpp" />
|
||||
<ClCompile Include="ICETime.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ICESync.h" />
|
||||
<ClInclude Include="ICETime.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
4
src/libs/ice/ice_vs2013.vcxproj.user
Normal file
4
src/libs/ice/ice_vs2013.vcxproj.user
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
Reference in New Issue
Block a user