rtphone/src/libs/ice/ICENetworkHelper.cpp

489 lines
13 KiB
C++

/* 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(...)
{
ICELogError(<< "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_OPENWRT) || defined(TARGET_MUSL)
output[i] = ntohl(addr6.__in6_union.__s6_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_OPENWRT) || defined(TARGET_MUSL)
output.__in6_union.__s6_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;