- initial import

This commit is contained in:
2018-06-05 11:05:37 +03:00
commit e1a4931375
4673 changed files with 1383093 additions and 0 deletions

View 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
View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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

View 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
View 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

View 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;
}

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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__ */

View 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

View 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
View 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
View 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
View 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
View 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

View 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;

View 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

View 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;
}

View 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

View 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
View 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

View 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
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

367
src/libs/ice/ICEStream.h Normal file
View 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

View 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");
}

View 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

View 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()
{}

View 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

View 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;
}
}

View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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.

View 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>

View 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>

View 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>