summaryrefslogtreecommitdiff
path: root/Runtime/Network/NetworkUtility.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Network/NetworkUtility.cpp')
-rw-r--r--Runtime/Network/NetworkUtility.cpp1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/Runtime/Network/NetworkUtility.cpp b/Runtime/Network/NetworkUtility.cpp
new file mode 100644
index 0000000..976366b
--- /dev/null
+++ b/Runtime/Network/NetworkUtility.cpp
@@ -0,0 +1,1076 @@
+#include "UnityPrefix.h"
+#include "NetworkUtility.h"
+
+#if UNITY_OSX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include "External/RakNet/builds/include/GetTime.h"
+#elif UNITY_WIN
+#include "External/RakNet/builds/include/GetTime.h"
+#include <winsock2.h>
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+#if !UNITY_WP8
+#include <iphlpapi.h>
+#include <windns.h>
+#endif
+#elif UNITY_WII || UNITY_PEPPER
+#pragma message("Dummy network")
+#elif UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#elif UNITY_PS3
+#include <netex/libnetctl.h>
+typedef unsigned long u_long;
+#elif UNITY_XENON
+#include <Xtl.h>
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#include "Runtime/Threads/Thread.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <android/log.h>
+#if DEBUGMODE
+ #define TRACE_PING(...) __android_log_print(ANDROID_LOG_VERBOSE, "PING", __VA_ARGS__)
+#else
+ #define TRACE_PING(...)
+#endif
+#elif UNITY_FLASH
+#elif ENABLE_NETWORK
+#error "Unsupported platform"
+#endif
+
+
+#if ENABLE_NETWORK
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "NetworkEnums.h"
+#include "External/RakNet/builds/include/GetTime.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#include "Configuration/UnityConfigureVersion.h"
+#endif
+
+
+void NetworkInitialize ()
+{
+#if USE_WINSOCK_APIS
+ WSADATA WsaData;
+ WSAStartup( MAKEWORD(2,2), &WsaData );
+#endif
+}
+
+void NetworkCleanup ()
+{
+#if USE_WINSOCK_APIS
+ WSACleanup();
+#endif
+}
+
+
+std::string GetLocalIP()
+{
+ std::string returnValue = "0.0.0.0";
+
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+
+ struct in_addr remote;
+ struct sockaddr_in raddress;
+ struct sockaddr *raddr = (struct sockaddr *)&raddress;
+
+ struct sockaddr_in laddress;
+ struct sockaddr *laddr = (struct sockaddr *)&laddress;
+
+ int sock;
+ int err;
+
+ std::string result;
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if ( sock < 1 ) {
+ perror("GetLocalIP: Error setting socket");
+ return returnValue;
+ }
+
+ // www.unity3d.com hardcoded
+ inet_aton("83.221.146.11", &remote);
+
+ raddress.sin_port = htons(80);
+ raddress.sin_family = AF_INET;
+ raddress.sin_addr = remote;
+
+ err = connect(sock, raddr, sizeof(raddress ));
+ if ( err < 0 ) {
+ perror("GetLocalIP: Error during connect");
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ close(sock);
+ if (ipCount > 0) return availableIPs[0];
+ return returnValue;
+ }
+
+ socklen_t len = sizeof(laddress);
+ err = getsockname(sock, laddr, &len );
+ if ( err < 0 ) {
+ perror("GetLocalIP: Error using getsockname");
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ close(sock);
+ if (ipCount > 0) return availableIPs[0];
+ return returnValue;
+ }
+
+ close(sock);
+
+ returnValue = std::string(inet_ntoa(laddress.sin_addr));
+
+ #elif UNITY_WIN
+
+ #if !UNITY_METRO
+
+ WSADATA WSAData;
+ SOCKET s;
+
+ // Initialize winsock
+ if( ::WSAStartup(MAKEWORD(2, 2), &WSAData) != 0 ) {
+ printf_console( "GetLocalIP: Failed to initialize winsock\n" );
+ return returnValue;
+ }
+
+ s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if ( s == INVALID_SOCKET ) {
+ printf_console("GetLocalIP: Error setting socket, %d", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ struct sockaddr_in raddress;
+ struct sockaddr *raddr = (struct sockaddr *)&raddress;
+
+ struct sockaddr_in laddress;
+ struct sockaddr * laddr = (struct sockaddr *)&laddress;
+
+ int err;
+
+ // www.unity3d.com hardcoded
+ char target[16];
+ strcpy(target, "83.221.146.11");
+
+ raddress.sin_family = AF_INET;
+ raddress.sin_port = htons(80);
+ raddress.sin_addr.s_addr = inet_addr(target);
+
+ err = connect(s, raddr, sizeof(raddress ));
+ if ( err != 0 ) {
+ printf_console("GetLocalIP: Error during connect, %d ", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ int len = sizeof(laddress);
+ err = getsockname(s, laddr, &len );
+ if ( err == SOCKET_ERROR ) {
+ printf_console("GetLocalIP: Error using getsockname, %d ", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ closesocket(s);
+
+ ::WSACleanup();
+
+ returnValue = std::string(inet_ntoa(laddress.sin_addr));
+
+ #else
+
+ #pragma message("todo: implement") // ?!-
+
+ #endif
+
+ #elif UNITY_WII || UNITY_PS3 || UNITY_PEPPER || UNITY_FLASH
+
+ returnValue = "127.0.0.1";
+
+ #elif UNITY_XENON
+
+ XNADDR xboxAddr;
+ DWORD ret = XNetGetTitleXnAddr(&xboxAddr);
+ if ((ret != XNET_GET_XNADDR_PENDING) && // IP address can not be determined yet
+ (ret & XNET_GET_XNADDR_NONE) == 0) // IP address is not available
+ {
+ char buffer[64];
+ if (XNetInAddrToString(xboxAddr.ina, buffer, sizeof(buffer)) == 0)
+ {
+ returnValue = buffer;
+ }
+ }
+
+ #elif ENABLE_NETWORK
+ #error "Unsupported platform"
+ #endif
+
+ return returnValue;
+}
+
+int GetIPs( char ips[10][16] )
+{
+ memset( ips, 0, 10*16 );
+ int ipnumber = 0;
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ struct ifaddrs *myaddrs, *ifa;
+ struct sockaddr_in *addr;
+ int status;
+ status = getifaddrs(&myaddrs);
+ if (status != 0)
+ {
+ perror("Error getting interface addresses");
+ }
+
+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
+ {
+ if (ifa->ifa_addr == NULL) continue;
+ // Discard inactive interfaces
+ if ((ifa->ifa_flags & IFF_UP) == 0) continue;
+
+ // Get IPV4 address
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ {
+ addr = (struct sockaddr_in *)(ifa->ifa_addr);
+ if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(addr->sin_addr), ips[ipnumber], sizeof(ips[ipnumber])) == NULL)
+ {
+ printf_console("%s: inet_ntop failed!\n", ifa->ifa_name);
+ }
+ else
+ {
+ if (strcmp(ips[ipnumber], "127.0.0.1")==0)
+ continue;
+ else
+ {
+ ipnumber++;
+ if (ipnumber == 10) break;
+ }
+ }
+ }
+ }
+
+ freeifaddrs(myaddrs);
+
+#elif UNITY_WIN
+
+ #if !UNITY_WINRT
+
+ PMIB_IPADDRTABLE ipAddrTable;
+ DWORD tableSize = 0;
+ IN_ADDR in_addr;
+
+ ipAddrTable = (MIB_IPADDRTABLE*)UNITY_MALLOC(kMemNetwork, sizeof(MIB_IPADDRTABLE));
+
+ if (ipAddrTable)
+ {
+ // If sizeof(MIB_IPADDRTABLE) is not enough, allocate appropriate size
+ if (GetIpAddrTable(ipAddrTable, &tableSize, 0) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = (MIB_IPADDRTABLE*)UNITY_MALLOC(kMemNetwork, tableSize);
+ }
+ }
+ if (ipAddrTable == NULL)
+ {
+ printf_console("Memory allocation failed for GetIpAddrTable\n");
+ return 0;
+ }
+ else
+ {
+ // Get actual data
+ DWORD status = 0;
+ if ((status = GetIpAddrTable(ipAddrTable, &tableSize, 0)) != NO_ERROR)
+ {
+ printf_console("GetIpAddrTable failed with error %d\n", status);
+ LPVOID messageBuffer;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL))
+ {
+ printf_console("Error: %s", messageBuffer);
+ LocalFree(messageBuffer);
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = NULL;
+ }
+ return 0;
+ }
+ }
+
+ for (int i = 0; i < (int)ipAddrTable->dwNumEntries; i++)
+ {
+ in_addr.S_un.S_addr = (u_long)ipAddrTable->table[i].dwAddr;
+ strcpy( ips[ipnumber], inet_ntoa(in_addr) );
+ if (strcmp(ips[ipnumber], "127.0.0.1") == 0)
+ continue;
+ else
+ {
+ ++ipnumber;
+ if (ipnumber == 10) break;
+ }
+ }
+
+ if (ipAddrTable)
+ {
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = NULL;
+ }
+
+ #else
+ using namespace Windows::Networking::Connectivity;
+
+ auto hostnames = NetworkInformation::GetHostNames();
+ for (int i = hostnames->Size-1; i >= 0; --i)
+ {
+ auto ipInfo = hostnames->GetAt(i)->IPInformation;
+
+ if (ipInfo == nullptr)
+ continue;
+
+ auto name = hostnames->GetAt(i)->CanonicalName;
+
+ // IPv4 only - TODO IPv6
+ if (name->Length() > 16)
+ continue;
+
+ strcpy( ips[ipnumber], ConvertStringToUtf8(name).c_str() );
+ ++ipnumber;
+ if (ipnumber == 10) break;
+ }
+ if (ipnumber < 10)
+ {
+ strcpy( ips[ipnumber], "127.0.0.1" );
+ ++ipnumber;
+ }
+ #endif
+
+#elif UNITY_WII || UNITY_PEPPER || UNITY_FLASH
+
+#elif UNITY_PS3
+ cellNetCtlInit();
+ CellNetCtlInfo info;
+ cellNetCtlGetInfo(CELL_NET_CTL_INFO_IP_ADDRESS, &info);
+ ipnumber = 1;
+ strcpy(&ips[0][0], info.ip_address);
+
+
+#elif UNITY_XENON
+
+ std::string localIp = GetLocalIP();
+ if (localIp.length() > 0 && localIp.length() < 16)
+ {
+ ipnumber = 1;
+ strcpy(&ips[0][0], localIp.c_str());
+ }
+
+#elif UNITY_ANDROID
+ struct ifconf ifc;
+ struct ifreq ifreqs[8];
+ memset(&ifc, 0, sizeof(ifc));
+ ifc.ifc_buf = (char*) (ifreqs);
+ ifc.ifc_len = sizeof(ifreqs);
+
+ struct ifreq* IFR;
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ {
+ printf_console("android.permission.INTERNET not available?");
+ return 0;
+ }
+
+ if ((ioctl(sock, SIOCGIFCONF, (char*) &ifc )) < 0 )
+ ifc.ifc_len = 0;
+
+ char* ifrp;
+ struct ifreq* ifr, ifr2;
+ IFR = ifc.ifc_req;
+ for (ifrp = ifc.ifc_buf;
+ (ifrp - ifc.ifc_buf) < ifc.ifc_len;
+ ifrp += sizeof(ifr->ifr_name) + sizeof(struct sockaddr))
+ {
+ ifr = (struct ifreq*)ifrp;
+
+ // Get network interface flags
+ ifr2 = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr2) < 0)
+ continue;
+
+ // Discard inactive interfaces
+ if ((ifr2.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ // Skip the loopback/localhost interface
+ if ((ifr2.ifr_flags & IFF_LOOPBACK))
+ continue;
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ strcpy(&ips[ipnumber++][0], inet_ntoa(((struct sockaddr_in*)(&ifr->ifr_addr))->sin_addr));
+ if (ipnumber == 10) break;
+ }
+ close(sock);
+
+#elif ENABLE_NETWORK
+#error "Unsupported platform"
+#endif
+
+ return ipnumber;
+}
+
+std::string GetHostName ()
+{
+#if UNITY_OSX || (UNITY_WIN && !UNITY_WINRT) || UNITY_IPHONE
+ // Get local host name
+ char szHostName[128] = "";
+
+ if( ::gethostname(szHostName, sizeof(szHostName)) )
+ printf_console( "Failed to get host name - returning IP\n" );
+ else
+ return std::string(szHostName);
+#elif UNITY_WINRT
+ using namespace Windows::Networking::Connectivity;
+
+ auto hostnames = NetworkInformation::GetHostNames();
+ if (hostnames->Size > 0)
+ {
+ auto hostname = hostnames->GetAt(0)->DisplayName;
+ return ConvertStringToUtf8(hostname);
+ }
+#elif UNITY_XENON && !MASTER_BUILD
+
+ // Only available in development builds
+ char hostName[256] = "";
+ DWORD length = sizeof(hostName);
+ if (XBDM_NOERR == DmGetXboxName(hostName, &length))
+ return std::string(hostName);
+ else
+ printf_console( "Failed to get host name - returning IP\n" );
+#elif UNITY_ANDROID
+ return std::string(android::systeminfo::HardwareModel()) + ":" + GetLocalIP();
+#endif
+
+ return GetLocalIP();
+}
+
+
+
+
+#if ENABLE_SOCKETS && !UNITY_WINRT
+
+std::string InAddrToIP(sockaddr_in* in)
+{
+ #if UNITY_XENON
+ char ip[256] = "";
+ XNetInAddrToString(in->sin_addr, ip, sizeof(ip));
+ return std::string(ip);
+ #else
+ return std::string(inet_ntoa(in->sin_addr));
+ #endif
+}
+
+#endif
+
+#if ENABLE_NETWORK
+
+bool CompareToPrivateRange(u_long host_number)
+{
+ u_long network_number = htonl(host_number);
+
+ if ( (!(network_number > 167772160u && network_number < 184549375u) &&
+ !(network_number > 2851995648u && network_number < 2852061183u) &&
+ !(network_number > 2886729728u && network_number < 2887778303u) &&
+ !(network_number > 3232235520u && network_number < 3232301055u) ) &&
+ (network_number != 2130706433) )
+ return true;
+ return false;
+}
+
+bool CheckForPublicAddress()
+{
+ bool isPublic = false;
+
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_ANDROID || UNITY_WIN || UNITY_BB10 || UNITY_TIZEN
+
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ for (int i=0; i<ipCount; ++i)
+ {
+ #if UNITY_WIN
+ unsigned int address;
+ #else
+ in_addr_t address;
+ #endif
+ address = inet_addr(availableIPs[i]);
+ isPublic = CompareToPrivateRange((u_long)address);
+ }
+
+ #elif UNITY_WII || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON
+
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ return isPublic;
+}
+
+#if UNITY_WIN
+char* DNSQueryRecursive(const char* domainName)
+{
+ IN_ADDR ipaddr;
+ char* ipAddress;
+ PDNS_RECORD pDnsRecord = NULL;
+
+ DNS_STATUS status = DnsQuery(domainName, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL);
+
+ if (status)
+ printf_console("DNSLookup: Error looking up %s (%d)\n", domainName, status);
+ // If it's a CNAME then the A record is bogus, so lookup again
+ else if (pDnsRecord->wType == DNS_TYPE_CNAME)
+ {
+ char* aliasName;
+ size_t size = strlen(pDnsRecord->Data.CNAME.pNameHost);
+ ALLOC_TEMP(aliasName, char, size+1);
+ strncpy(aliasName, pDnsRecord->Data.CNAME.pNameHost, size+1);
+ DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
+ return DNSQueryRecursive(aliasName);
+ }
+ else
+ {
+ ipaddr.S_un.S_addr = (pDnsRecord->Data.A.IpAddress);
+ ipAddress = inet_ntoa(ipaddr);
+ DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
+ return ipAddress;
+ }
+
+ return NULL;
+}
+#endif
+
+
+char* DNSLookup(const char* domainName)
+{
+ char* ipAddress;
+
+#if UNITY_WIN
+ ipAddress = DNSQueryRecursive(domainName);
+ // Fall back to gethostbyname()
+ if (!ipAddress)
+#endif
+ ipAddress = const_cast<char*>(SocketLayer::Instance()->DomainNameToIP(domainName));
+
+ return ipAddress;
+}
+
+void ResolveAddress(SystemAddress& address, const char* domainName, const char* betaDomainName, const char* errorMessage)
+{
+ if (address.binaryAddress == 0)
+ {
+ char* resolvedAddress;
+ if (UNITY_IS_BETA)
+ resolvedAddress = DNSLookup(betaDomainName);
+ else
+ resolvedAddress = DNSLookup(domainName);
+ if (resolvedAddress)
+ address.SetBinaryAddress(resolvedAddress);
+ else
+ ErrorString(errorMessage);
+ }
+}
+
+unsigned short makeChecksum(unsigned short *buffer, int size)
+{
+ unsigned long cksum=0;
+ while (size > 1)
+ {
+ cksum += *buffer++;
+ size -= sizeof(unsigned short);
+ }
+ if (size)
+ {
+ cksum += *(unsigned char*)buffer;
+ }
+ cksum = (cksum >> 16) + (cksum & 0xffff);
+ cksum += (cksum >>16);
+ return (unsigned short)(~cksum);
+}
+
+
+#if UNITY_WIN
+/*
+// ICMP structures for Windows
+typedef struct {
+ unsigned char Ttl; // Time To Live
+ unsigned char Tos; // Type Of Service
+ unsigned char Flags; // IP header flags
+ unsigned char OptionsSize; // Size in bytes of options data
+ unsigned char *OptionsData; // Pointer to options data
+} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
+
+typedef struct {
+ DWORD Address; // Replying address
+ unsigned long Status; // Reply status
+ unsigned long RoundTripTime; // RTT in milliseconds
+ unsigned short DataSize; // Echo data size
+ unsigned short Reserved; // Reserved for system use
+ void *Data; // Pointer to the echo data
+ IP_OPTION_INFORMATION Options; // Reply options
+} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
+*/
+
+// Wrapper for the ICMP library instance handle to make sure it unloads on quit.
+class PingLib
+{
+public:
+ PingLib() : hIcmp(NULL) {}
+
+ ~PingLib()
+ {
+ if(hIcmp)
+ FreeLibrary(hIcmp);
+ }
+
+ HINSTANCE getInstance() {
+ if (hIcmp == NULL)
+ {
+ hIcmp = LoadLibrary("icmp.dll");
+ if (hIcmp == 0)
+ printf_console("Unable to locate icmp.dll");
+ }
+ return hIcmp;
+ }
+private:
+ HINSTANCE hIcmp;
+};
+
+PingLib pingLib;
+
+#endif
+
+
+void* PingImpl(void* data)
+{
+ Ping& time = *(Ping*)data;
+
+ time.SetTime(-1);
+ time.SetIsDone(false);
+
+ #if UNITY_WIN
+
+ // Get an instance of the libary, it is only loaded once.
+ HINSTANCE hIcmp = pingLib.getInstance();
+
+ typedef HANDLE (WINAPI* pfnHV)(VOID);
+ typedef BOOL (WINAPI* pfnBH)(HANDLE);
+ typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
+ pfnHV pIcmpCreateFile;
+ pfnBH pIcmpCloseHandle;
+ pfnDHDPWPipPDD pIcmpSendEcho;
+ pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp, "IcmpCreateFile");
+ pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp, "IcmpCloseHandle");
+ pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp, "IcmpSendEcho");
+ if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) || (pIcmpSendEcho == 0))
+ {
+ printf_console("Failed to get proc addr info for ICMP functions.");
+ time.Release();
+ return NULL;
+ }
+
+ unsigned int targetAddress = inet_addr(time.GetIP().c_str());
+
+ HANDLE h = pIcmpCreateFile();
+ if (h == INVALID_HANDLE_VALUE) {
+ printf_console("Ping: Error creating icmp handle\n");
+ time.Release();
+ return NULL;
+ }
+
+ char pingBody[56];
+ memset(pingBody, 'X', 56);
+
+ LPVOID replyBuffer = malloc(sizeof(ICMP_ECHO_REPLY) + sizeof(pingBody));
+ if (!replyBuffer) {
+ printf_console("Ping: Error allocating reply buffer\n");
+ pIcmpCloseHandle(h);
+ time.Release();
+ return NULL;
+ }
+
+ unsigned int retval = pIcmpSendEcho(h, targetAddress, &pingBody, sizeof(pingBody), 0, replyBuffer, sizeof(ICMP_ECHO_REPLY) + sizeof(pingBody), 1000);
+ if (retval == 0)
+ {
+ printf_console("Ping: Error performing ICMP transmission. Possibly because of a timeout\n");
+ pIcmpCloseHandle(h);
+ free(replyBuffer);
+ time.Release();
+ return NULL;
+ }
+
+ PICMP_ECHO_REPLY rep = (PICMP_ECHO_REPLY)replyBuffer;
+
+ time.SetIsDone(true);
+ time.SetTime((int)rep->RoundTripTime);
+
+ pIcmpCloseHandle(h);
+ free(replyBuffer);
+
+ #elif UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ int err = 0;
+ char packet[64];
+ int psize = 64;
+ unsigned short checksum;
+ struct sockaddr_in address_in;
+ struct sockaddr *address = (struct sockaddr *)&address_in;
+
+ if (time.GetIP().empty())
+ {
+ ErrorString("No IP present in PingTime structure.");
+ time.Release();
+ return NULL;
+ }
+
+ address_in.sin_family = AF_INET;
+ address_in.sin_addr.s_addr = inet_addr(time.GetIP().c_str());
+
+ // Use a ICMP datagram type IPv4 socket
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+
+ if (s < 0)
+ {
+ perror ("Ping: Error creating socket");
+ time.Release();
+ return NULL;
+ }
+
+ // Set timeout for recv
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ err = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
+ if (err < 0)
+ {
+ perror("Ping: Error setting socket options");
+ time.Release();
+ return NULL;
+ }
+
+ err = connect(s, address, sizeof(address_in));
+ if (err < 0)
+ {
+ perror("Ping: Error during connect");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+
+ // Prepare ping packet
+ memset(packet, 0, psize-1);
+ memset(packet+8, 'X', psize-9);
+ ((struct icmpheader*)packet)->type = 0x8;
+ checksum = makeChecksum( (unsigned short*)&packet, psize);
+ ((struct icmpheader*)packet)->checksum = checksum;
+
+ /*printf_console("Checksum is: %x\n", checksum);
+ printf_console("Packet contents: \n");
+ for (int i=0; i<psize-1; i++)
+ printf_console("%x", packet[i]);
+ printf_console("\n");*/
+
+ // NOTE: This is capable of doing multiple pings in a row with updated sequence numbers, but at the moment only a single ping is ever performed
+ RakNetTime currTime;
+ int count = 1;
+ int pingTime = -1;
+ for (int i=0; i < count; i++)
+ {
+ currTime = RakNet::GetTime();
+
+ // Send ping
+ err = send(s, (packet), psize, 0);
+ if ( err != psize )
+ {
+ perror("Ping: Error sending ICMP packet");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+
+ // Receive reponse
+ char buffer[64];
+ err = recv(s, buffer, 64, 0);
+ if ( err < 0 )
+ {
+ // This is a timeout
+ perror("Ping: Error receiving ICMP packet response. Possibly a timeout.");
+ //lets just return -1 to keep it consistent with windows version
+ }
+ else if ( err == 0)
+ {
+ printf_console("Ping: Nothing to receive");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+ else
+ {
+ pingTime = (int)(RakNet::GetTime() - currTime);
+ //printf_console("Ping: Ping nr. %d - Packet size is %d - Ping time %d ms\n", i, psize, pingTime);
+ }
+
+ // Update ping packet
+ ((struct icmpheader*)packet)->seq_num = htons(i+1);
+ ((struct icmpheader*)packet)->checksum = 0;
+ checksum = makeChecksum( (unsigned short*)&packet, psize);
+ ((struct icmpheader*)packet)->checksum = checksum;
+ }
+
+ // Stop receiving and transmitting
+ err = close(s);
+ if (err < 0)
+ perror ("Ping: Error closing socket");
+
+ time.SetTime(pingTime);
+ time.SetIsDone(true);
+
+ #elif UNITY_WII || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON
+
+ data = 0;
+
+ #elif UNITY_ANDROID
+ // ICMP sockets are not available without root. The only protocols supported for applications are TCP and UDP.
+ // Because /system/bin/ping is a setuid program, we can use that to send ICMP packets.
+ // (using "/system/bin/ping -qnc 1 <addr>")
+
+ char ip[256];
+ strncpy(ip, time.GetIP().c_str(), sizeof(ip)); ip[sizeof(ip)-1] = 0;
+ time.SetTime(-1); // in case of error, return -1 to keep it consistent with windows version
+
+ // create stdout pipe from child process
+ int rwpipes[2];
+ if( pipe(rwpipes) )
+ {
+ ErrorStringMsg("Error creating pipe! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+ if( fcntl(rwpipes[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(rwpipes[1], F_SETFL, O_NONBLOCK) != 0 )
+ {
+ ErrorStringMsg("Unable to set non-blocking pipe! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("forking process @ %s:%i", __FUNCTION__, __LINE__);
+ // fork process
+ pid_t pid;
+ if( (pid=fork()) == -1)
+ {
+ ErrorStringMsg("Error forking process! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("process forked @ %s:%i", __FUNCTION__, __LINE__);
+
+ if (!pid) // <child> process fork
+ {
+ TRACE_PING("secondary process @ %s:%i", __FUNCTION__, __LINE__);
+
+ TRACE_PING("dup'ing pipes @ %s:%i", __FUNCTION__, __LINE__);
+ dup2(rwpipes[1],1); // replace stdout with write pipe ..
+ dup2(rwpipes[1],2); // .. and stderr too ..
+
+ TRACE_PING("closing pipes @ %s:%i", __FUNCTION__, __LINE__);
+ close(rwpipes[0]); // .. close read pipe ..
+
+ TRACE_PING("exec'ing @ %s:%i", __FUNCTION__, __LINE__);
+ const char* ping = "/system/bin/ping";
+ if(execl(ping, ping, "-qnc", "1", ip, NULL) == -1)
+ {
+ printf("Error spawning child process '%s'! (%s, %i)", ping, strerror(errno), errno);
+ exit(1);
+ }
+
+ TRACE_PING("error? @ %s:%i", __FUNCTION__, __LINE__);
+ }
+
+ // <parent> process fork ...
+ TRACE_PING("parent fork @ %s:%i", __FUNCTION__, __LINE__);
+
+ // close write pipe
+ close(rwpipes[1]);
+
+ std::string output;
+ char buffer[256];
+
+ TRACE_PING("starting looping @ %s:%i", __FUNCTION__, __LINE__);
+
+ // loop until child process has died...
+ const double sleep_value = 0.5; // seconds
+ const double max_wait = 5.0; // seconds
+ int num_loops = (int)ceil(max_wait/sleep_value);
+ int ret_value;
+ while(waitpid(pid, &ret_value, WNOHANG) == 0)
+ {
+ TRACE_PING("reading @ %s:%i", __FUNCTION__, __LINE__);
+ int num_read = read(rwpipes[0], buffer, sizeof(buffer));
+ if (num_read > 0)
+ output.append(buffer, num_read);
+
+ TRACE_PING("sleeping # %i @ %s:%i", num_loops, __FUNCTION__, __LINE__);
+ Thread::Sleep(sleep_value);
+ if (num_loops-- < 0)
+ break;
+ }
+
+ // .. read any leftovers in the pipe ..
+ int num_read = read(rwpipes[0], buffer, sizeof(buffer));
+ if (num_read > 0)
+ output.append(buffer, num_read);
+
+ // ... and close read pipe
+ close(rwpipes[0]);
+
+ if (num_loops < 0)
+ {
+ TRACE_PING("pid killed @ %s:%i", __FUNCTION__, __LINE__);
+ kill(pid, SIGKILL);
+ ret_value = num_loops;
+ }
+
+ if (ret_value != 0)
+ {
+ ErrorString(output.c_str());
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ float ping_rtt;
+ size_t rtt_pos = output.rfind(" = ");
+ if (rtt_pos == std::string::npos || sscanf(&output[rtt_pos], "%*s%f", &ping_rtt) != 1)
+ {
+ ErrorStringMsg("Error parsing ping output!\n%s", output.c_str());
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("ping done %i @ %s:%i", (int)ping_rtt, __FUNCTION__, __LINE__);
+
+ time.SetTime((int)ping_rtt);
+ time.SetIsDone(true);
+
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ time.Release();
+ return NULL;
+}
+
+Ping::Ping (const std::string& ip)
+{
+ m_Time = -1;
+ m_IsDone = false;
+ m_IP = ip;
+ m_Refcount = 1;
+}
+
+int Ping::GetTime()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_Time;
+}
+
+void Ping::SetTime(int value)
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Time = value;
+}
+
+int Ping::GetIsDone()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_IsDone;
+}
+
+void Ping::SetIsDone(bool value)
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_IsDone = value;
+}
+
+std::string Ping::GetIP()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_IP;
+}
+
+void Ping::Retain()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Refcount++;
+}
+
+void Ping::Release()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Refcount--;
+ if (m_Refcount == 0)
+ delete this;
+}
+
+void SendToAllNetworkViews (const MessageIdentifier& msg, int inData)
+{
+ MessageData data;
+ data.SetData(inData, ClassID (int));
+ SendMessageToEveryone(msg, data);
+}
+
+void SendToAllNetworkViews (const MessageIdentifier& msg)
+{
+ MessageData data;
+ SendMessageToEveryone(msg, data);
+}
+
+// These are machine specific + depend on network connection, so we can't run them by default
+#if ENABLE_UNIT_TESTS && 0
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+TEST(ResolveAddressWorks)
+{
+ char* ip = DNSLookup("masterserver.unity3d.com");
+ const char* expected = "67.225.180.24";
+ bool isValid = strncmp(expected, ip, strlen(expected)) == 0;
+ CHECK(isValid);
+}
+
+TEST(CollectAllIPsWorks)
+{
+ char ips[10][16];
+ int count = GetIPs(ips);
+ printf ("Got %d ips\n", count);
+ for (int i=0; i<count; i++)
+ printf("\t%s\n", ips[i]);
+ CHECK(count == 3);
+}
+
+#endif // ENABLE_UNIT_TESTS
+
+#endif // ENABLE_NETWORK