From 45ed8cddb7bd7805a446464da1215740677ebdcc Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 25 Feb 2020 16:29:06 +0700 Subject: [PATCH] Fix Local IP detection on non-Windows system --- Core/HLE/proAdhoc.cpp | 112 ++++++++++++++++++++++++++++-------- Core/HLE/proAdhoc.h | 5 +- Core/HLE/proAdhocServer.cpp | 2 +- Core/HLE/sceNet.cpp | 18 +++--- 4 files changed, 101 insertions(+), 36 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 93d1d2d82ba0..add10119f12a 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -24,6 +24,10 @@ #if !defined(_WIN32) #include #include +#include +#include +#include +#include #endif #include @@ -53,7 +57,7 @@ int actionAfterMatchingMipsCall; // Broadcast MAC uint8_t broadcastMAC[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -int metasocket; +int metasocket = (int)INVALID_SOCKET; SceNetAdhocctlParameter parameter; SceNetAdhocctlAdhocId product_code; std::thread friendFinderThread; @@ -69,7 +73,8 @@ bool updateChatScreen = false; int newChat = 0; bool isLocalServer = false; uint8_t PPSSPP_ID = 0; -sockaddr localIP; +sockaddr LocalhostIP; +sockaddr LocalIP; bool isLocalMAC(const SceNetEtherAddr * addr) { SceNetEtherAddr saddr; @@ -1484,38 +1489,92 @@ int getActivePeerCount(const bool excludeTimedout) { return count; } -int getLocalIp(sockaddr_in * SocketAddress){ +int getLocalIp(sockaddr_in* SocketAddress) { + if (metasocket != (int)INVALID_SOCKET) { + struct sockaddr_in localAddr; + localAddr.sin_addr.s_addr = INADDR_ANY; + socklen_t addrLen = sizeof(localAddr); + if (SOCKET_ERROR != getsockname(metasocket, (struct sockaddr*) & localAddr, &addrLen)) { + if (isLocalServer) { + localAddr.sin_addr = ((sockaddr_in*)&LocalhostIP)->sin_addr; + } + SocketAddress->sin_addr = localAddr.sin_addr; + return 0; + } + } + +// Fallback if not connected to AdhocServer #if defined(_WIN32) // Get local host name char szHostName[256] = ""; - if(::gethostname(szHostName, sizeof(szHostName))) { + if (::gethostname(szHostName, sizeof(szHostName))) { // Error handling } // Get local IP addresses - struct hostent *pHost = 0; - pHost = ::gethostbyname(szHostName); - if(pHost) { + struct hostent* pHost = 0; + pHost = ::gethostbyname(szHostName); // On Non-Windows (UNIX/POSIX) gethostbyname("localhost") will always returns a useless 127.0.0.1, while on Windows it returns LAN IP when available + if (pHost) { memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length); if (isLocalServer) { - SocketAddress->sin_addr = ((sockaddr_in*)&localIP)->sin_addr; + SocketAddress->sin_addr = ((sockaddr_in*)&LocalhostIP)->sin_addr; } return 0; } return -1; -#else - char szHostName[256] = ""; - gethostname(szHostName, sizeof(szHostName)); - struct hostent* pHost = 0; - pHost = gethostbyname(szHostName); - if (pHost) { - memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length); + +#elif defined(getifaddrs) // On Android: Requires __ANDROID_API__ >= 24 + struct ifaddrs* ifAddrStruct = NULL; + struct ifaddrs* ifa = NULL; + + getifaddrs(&ifAddrStruct); + if (ifAddrStruct != NULL) { + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + continue; + } + if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 + // is a valid IP4 Address + SocketAddress->sin_addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; + break; + } + } + freeifaddrs(ifAddrStruct); if (isLocalServer) { - SocketAddress->sin_addr = ((sockaddr_in*)&localIP)->sin_addr; + SocketAddress->sin_addr = ((sockaddr_in*)&LocalhostIP)->sin_addr; } return 0; } return -1; + +#else // Alternative way + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock != SOCKET_ERROR) { + const char* kGoogleDnsIp = "8.8.8.8"; // Needs to be an IP string so it can be resolved as fast as possible to IP, doesn't need to be reachable + uint16_t kDnsPort = 53; + struct sockaddr_in serv; + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp); + serv.sin_port = htons(kDnsPort); + + int err = connect(sock, (const sockaddr*)&serv, sizeof(serv)); + if (err != SOCKET_ERROR) { + sockaddr_in name; + socklen_t namelen = sizeof(name); + err = getsockname(sock, (sockaddr*)&name, &namelen); + if (err != SOCKET_ERROR) { + SocketAddress->sin_addr = name.sin_addr; // May be we should cache this so it doesn't need to use connect all the time, or even better cache it when connecting to adhoc server to get an accurate IP + closesocket(sock); + if (isLocalServer) { + SocketAddress->sin_addr = ((sockaddr_in*)&LocalhostIP)->sin_addr; + } + return 0; + } + } + closesocket(sock); + } + return -1; #endif } @@ -1525,7 +1584,7 @@ uint32_t getLocalIp(int sock) { socklen_t addrLen = sizeof(localAddr); getsockname(sock, (struct sockaddr*)&localAddr, &addrLen); if (isLocalServer) { - localAddr.sin_addr = ((sockaddr_in*)&localIP)->sin_addr; + localAddr.sin_addr = ((sockaddr_in*)&LocalhostIP)->sin_addr; } return localAddr.sin_addr.s_addr; } @@ -1679,18 +1738,19 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ setsockopt(metasocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); setsockopt(metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on)); - ((struct sockaddr_in*) & localIP)->sin_port = 0; + ((struct sockaddr_in*) & LocalhostIP)->sin_port = 0; // Bind Local Address to Socket - iResult = bind(metasocket, (struct sockaddr*) & localIP, sizeof(sockaddr)); + iResult = bind(metasocket, (struct sockaddr*) & LocalhostIP, sizeof(sockaddr)); if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", inet_ntoa(((struct sockaddr_in*) & localIP)->sin_addr), iResult); - host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + inet_ntoa(((struct sockaddr_in*) & localIP)->sin_addr), 3.0, 0x0000ff); + ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", inet_ntoa(((struct sockaddr_in*) & LocalhostIP)->sin_addr), iResult); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + inet_ntoa(((struct sockaddr_in*) & LocalhostIP)->sin_addr), 3.0, 0x0000ff); } } // Default/Initial Network memset(¶meter, 0, sizeof(parameter)); - strcpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str()); + strncpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); + parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; parameter.channel = g_Config.iWlanAdhocChannel; // Fake Channel, 0 = Auto where JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11) if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = 1; getLocalMac(¶meter.bssid.mac_addr); @@ -1725,11 +1785,15 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ SceNetEtherAddr addres; getLocalMac(&addres); packet.mac = addres; - strcpy((char *)packet.name.data, g_Config.sNickName.c_str()); + strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); + packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); int sent = send(metasocket, (char*)&packet, sizeof(packet), 0); changeBlockingMode(metasocket, 1); // Change to non-blocking if (sent > 0) { + socklen_t addrLen = sizeof(LocalIP); + memset(&LocalIP, 0, addrLen); + getsockname(metasocket, &LocalIP, &addrLen); host->NotifyUserMessage(n->T("Network Initialized"), 1.0); return 0; } @@ -1750,7 +1814,7 @@ bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) { getLocalIp(&addr); uint32_t localIp = addr.sin_addr.s_addr; - if (ip == localIp || ip == ((sockaddr_in*)&localIP)->sin_addr.s_addr){ + if (ip == localIp || ip == ((sockaddr_in*)&LocalhostIP)->sin_addr.s_addr){ getLocalMac(mac); return true; } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 25865354440a..5e77f56ed009 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -846,7 +846,8 @@ extern SceNetAdhocPtpStat * ptp[255]; extern uint16_t portOffset; extern bool isLocalServer; extern uint8_t PPSSPP_ID; -extern sockaddr localIP; +extern sockaddr LocalhostIP; // Used to differentiate localhost IP on multiple-instance +extern sockaddr LocalIP; // IP of Network Adapter used to connect to Adhoc Server (LAN/WAN) extern uint32_t fakePoolSize; extern SceNetAdhocMatchingContext * contexts; extern int one; @@ -1151,7 +1152,7 @@ void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEthe int getActivePeerCount(const bool excludeTimedout = true); /** - * Returns the locall Ip of this machine, TODO: Implement the linux version + * Returns the locall Ip of this machine * @param SocketAddres OUT: local ip */ int getLocalIp(sockaddr_in * SocketAddress); diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 58cd00db329a..a385e7d8e593 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -1809,7 +1809,7 @@ int create_listen_socket(uint16_t port) //Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. Doesn't work well when PPSSPP_ID reseted everytime emulation restarted. /* if (PPSSPP_ID > 1) { - local.sin_addr = ((sockaddr_in *)&localIP)->sin_addr; + local.sin_addr = ((sockaddr_in *)&LocalhostIP)->sin_addr; } */ diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 4e20b59c2e02..18c300f43bf8 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -178,7 +178,7 @@ static void PPSSPPIDCleanup() { #endif } -static int InitLocalIP() { +static int InitLocalhostIP() { // find local IP addrinfo* localAddr; addrinfo* ptr; @@ -188,19 +188,19 @@ static int InitLocalIP() { if (iResult != 0) { ERROR_LOG(SCENET, "DNS Error (%s) result: %d\n", ipstr, iResult); //osm.Show("DNS Error, can't resolve client bind " + ipstr, 8.0f); - ((sockaddr_in*)&localIP)->sin_family = AF_INET; - ((sockaddr_in*)&localIP)->sin_addr.s_addr = inet_addr(ipstr); //"127.0.0.1" - ((sockaddr_in*)&localIP)->sin_port = 0; + ((sockaddr_in*)&LocalhostIP)->sin_family = AF_INET; + ((sockaddr_in*)&LocalhostIP)->sin_addr.s_addr = inet_addr(ipstr); //"127.0.0.1" + ((sockaddr_in*)&LocalhostIP)->sin_port = 0; return iResult; } for (ptr = localAddr; ptr != NULL; ptr = ptr->ai_next) { switch (ptr->ai_family) { case AF_INET: - memcpy(&localIP, ptr->ai_addr, sizeof(sockaddr)); + memcpy(&LocalhostIP, ptr->ai_addr, sizeof(sockaddr)); break; } } - ((sockaddr_in*)&localIP)->sin_port = 0; + ((sockaddr_in*)&LocalhostIP)->sin_port = 0; freeaddrinfo(localAddr); // Resolve server dns @@ -221,7 +221,7 @@ static int InitLocalIP() { } } freeaddrinfo(resultAddr); - isLocalServer = (((uint8_t*)&serverIp.s_addr)[0] == 0x7f); // (serverIp.S_un.S_un_b.s_b1 = 0x7f); + isLocalServer = (((uint8_t*)&serverIp.s_addr)[0] == 0x7f); return 0; } @@ -242,12 +242,12 @@ void __NetInit() { } net::Init(); - InitLocalIP(); + InitLocalhostIP(); char tmpmac[18]; SceNetEtherAddr mac; getLocalMac(&mac); - INFO_LOG(SCENET, "LocalHost IP will be %s [MAC: %s]", inet_ntoa(((sockaddr_in*)&localIP)->sin_addr), mac2str(&mac, tmpmac)); + INFO_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(((sockaddr_in*)&LocalhostIP)->sin_addr), mac2str(&mac, tmpmac)); // Only initialize when UPnP is enabled since it takes a few seconds to detect UPnP device (may affect people who don't have UPnP device) if (g_Config.bEnableUPnP) { g_PortManager.Init();