Skip to content

Commit

Permalink
Fix Local IP detection on non-Windows system
Browse files Browse the repository at this point in the history
  • Loading branch information
anr2me committed Jul 15, 2020
1 parent 6bb15b8 commit 45ed8cd
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 36 deletions.
112 changes: 88 additions & 24 deletions Core/HLE/proAdhoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#if !defined(_WIN32)
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
#endif

#include <cstring>
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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(&parameter, 0, sizeof(parameter));
strcpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str());
strncpy((char *)&parameter.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(&parameter.bssid.mac_addr);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
5 changes: 3 additions & 2 deletions Core/HLE/proAdhoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion Core/HLE/proAdhocServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
*/

Expand Down
18 changes: 9 additions & 9 deletions Core/HLE/sceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ static void PPSSPPIDCleanup() {
#endif
}

static int InitLocalIP() {
static int InitLocalhostIP() {
// find local IP
addrinfo* localAddr;
addrinfo* ptr;
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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();
Expand Down

0 comments on commit 45ed8cd

Please sign in to comment.