Skip to content

Commit

Permalink
Merge pull request #14335 from ANR2ME/adhoc
Browse files Browse the repository at this point in the history
[Adhoc] Prevent flooding Adhoc Server with connection attempts
  • Loading branch information
hrydgard authored Apr 2, 2021
2 parents 91b7a4a + 36ba5e0 commit af277bb
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 76 deletions.
14 changes: 11 additions & 3 deletions Core/HLE/proAdhoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ SceNetAdhocctlPeerInfo * friends = NULL;
SceNetAdhocctlScanInfo * networks = NULL;
SceNetAdhocctlScanInfo * newnetworks = NULL;
u64 adhocctlStartTime = 0;
bool isAdhocctlNeedLogin = false;
bool isAdhocctlBusy = false;
int adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
int adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
Expand Down Expand Up @@ -1371,7 +1372,7 @@ int friendFinder(){
//_acquireNetworkLock();

// Reconnect when disconnected while Adhocctl is still inited
if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited) {
if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited && isAdhocctlNeedLogin) {
if (g_Config.bEnableWlan) {
if (initNetwork(&product_code) == 0) {
networkInited = true;
Expand All @@ -1389,6 +1390,8 @@ int friendFinder(){
}
}
}
// Prevent retrying to Login again unless it was on demand
isAdhocctlNeedLogin = false;

if (networkInited) {
// Ping Server
Expand Down Expand Up @@ -1897,6 +1900,10 @@ bool isPrivateIP(uint32_t ip) {
return false;
}

bool isLoopbackIP(uint32_t ip) {
return ((uint8_t*)&ip)[0] == 0x7f;
}

void getLocalMac(SceNetEtherAddr * addr){
// Read MAC Address from config
uint8_t mac[ETHER_ADDR_LEN] = {0};
Expand Down Expand Up @@ -2143,8 +2150,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){

// If Server is at localhost Try to Bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by AdhocServer
// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
if (((uint8_t*)&g_adhocServerIP.in.sin_addr.s_addr)[0] == 0x7f) { // (serverIp.S_un.S_un_b.s_b1 == 0x7f)
if (isLoopbackIP(g_adhocServerIP.in.sin_addr.s_addr)) {
int on = 1;
// Not sure what is this SO_DONTROUTE supposed to fix, but i do remembered there were issue related to multiple-instances without SO_DONTROUTE, but forgot how to reproduce it :(
setsockopt(metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on));
setSockReuseAddrPort(metasocket);

Expand Down Expand Up @@ -2194,7 +2202,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
}
if (IsSocketReady(metasocket, false, true) <= 0) {
ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port));
host->NotifyUserMessage(n->T("Failed to connect to Adhoc Server"), 1.0f, 0x0000ff);
host->NotifyUserMessage(std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")", 1.0f, 0x0000ff);
return iResult;
}
}
Expand Down
6 changes: 6 additions & 0 deletions Core/HLE/proAdhoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,7 @@ extern bool friendFinderRunning;
extern SceNetAdhocctlPeerInfo * friends;
extern SceNetAdhocctlScanInfo * networks;
extern u64 adhocctlStartTime;
extern bool isAdhocctlNeedLogin;
extern bool isAdhocctlBusy;
extern int adhocctlState;
extern int adhocctlCurrentMode;
Expand Down Expand Up @@ -1279,6 +1280,11 @@ uint32_t getLocalIp(int sock);
*/
bool isPrivateIP(uint32_t ip);

/*
* Check if an IP (big-endian/network order) is Loopback IP
*/
bool isLoopbackIP(uint32_t ip);

/*
* Get Number of bytes available in buffer to be Received
* @param sock fd
Expand Down
4 changes: 2 additions & 2 deletions Core/HLE/sceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static void __ApctlState(u64 userdata, int cyclesLate) {

SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
if (waitID == 0 || error != 0) {
WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error);
WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
return;
}

Expand All @@ -151,7 +151,7 @@ static void __ApctlState(u64 userdata, int cyclesLate) {
}

__KernelResumeThreadFromWait(threadID, result);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState);
}

// Used to change Apctl State after a delay and before executing callback mipscall (since we don't have beforeAction)
Expand Down
100 changes: 37 additions & 63 deletions Core/HLE/sceNetAdhoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {

SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
if (waitID == 0 || error != 0) {
WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error);
WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
return;
}

Expand All @@ -269,6 +269,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
packet.base.opcode = req.opcode;
packet.group = req.group;

// Don't send any packets not in these cases (by setting the len to 0)
switch (req.opcode)
{
case OPCODE_CONNECT:
Expand All @@ -287,7 +288,8 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
if (len > 0) {
ret = SOCKET_ERROR;
sockerr = EAGAIN;
if (IsSocketReady(metasocket, false, true) > 0) {
// Don't send anything yet if connection to Adhoc Server is still in progress
if (!isAdhocctlNeedLogin && IsSocketReady(metasocket, false, true) > 0) {
ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL);
sockerr = errno;
// Successfully Sent or Connection has been closed or Connection failure occurred
Expand All @@ -300,6 +302,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
}
}

// Retry until successfully sent. Login packet sent after successfully connected to Adhoc Server (indicated by networkInited), so we're not sending Login again here
if ((req.opcode == OPCODE_LOGIN && !networkInited) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) {
u64 now = (u64)(time_now_d() * 1000000.0);
if (now - adhocctlStartTime <= static_cast<u64>(adhocDefaultTimeout) + 500) {
Expand All @@ -316,7 +319,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {

u32 waitVal = __KernelGetWaitValue(threadID, error);
__KernelResumeThreadFromWait(threadID, result);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState);

// We are done with this request
adhocctlRequests.erase(uid);
Expand All @@ -332,7 +335,7 @@ static void __AdhocctlState(u64 userdata, int cyclesLate) {

SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
if (waitID == 0 || error != 0) {
WARN_LOG(SCENET, "sceNetAdhocctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error);
WARN_LOG(SCENET, "sceNetAdhocctl State WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
return;
}

Expand All @@ -348,7 +351,7 @@ static void __AdhocctlState(u64 userdata, int cyclesLate) {
}

__KernelResumeThreadFromWait(threadID, result);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState);
DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState);
}

// Used to simulate blocking on metasocket when send OP code to AdhocServer
Expand Down Expand Up @@ -815,7 +818,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {

SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
if (waitID == 0 || error != 0) {
WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error);
WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
return;
}

Expand Down Expand Up @@ -901,7 +904,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
}

__KernelResumeThreadFromWait(threadID, result);
DEBUG_LOG(SCENET, "Returning (ThreadId: %d, WaitID: %d, error: %d) Result (%08x) of sceNetAdhoc[%d] - SocketID: %d", threadID, waitID, error, (int)result, req.type, req.id);
DEBUG_LOG(SCENET, "Returning (ThreadId: %d, WaitID: %d, error: %08x) Result (%08x) of sceNetAdhoc[%d] - SocketID: %d", threadID, waitID, error, (int)result, req.type, req.id);

// We are done with this socket
adhocSocketRequests.erase(userdata);
Expand Down Expand Up @@ -1056,6 +1059,8 @@ void __NetAdhocDoState(PointerWrap &p) {
netAdhocMatchingInited = cur_netAdhocMatchingInited;
netAdhocctlInited = cur_netAdhocctlInited;
netAdhocInited = cur_netAdhocInited;

isAdhocctlNeedLogin = false;
}
}

Expand Down Expand Up @@ -1142,6 +1147,7 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {

adhocctlEvents.clear();
netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success)
isAdhocctlNeedLogin = true;

// Create fake PSP Thread for callback
// TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here
Expand All @@ -1157,7 +1163,7 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
friendFinderThread = std::thread(friendFinder);
}

// Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room
// Need to make sure to be connected to Adhoc Server (indicated by networkInited) before returning to prevent GTA VCS failed to create/join a group and unable to see any game room
int us = adhocDefaultDelay;
if (g_Config.bEnableWlan && !networkInited) {
AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} };
Expand Down Expand Up @@ -2092,6 +2098,7 @@ int sceNetAdhocctlScan() {
// Only scan when in Disconnected state, otherwise AdhocServer will kick you out
if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) {
isAdhocctlBusy = true;
isAdhocctlNeedLogin = true;
adhocctlState = ADHOCCTL_STATE_SCANNING;
adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL;

Expand All @@ -2101,24 +2108,12 @@ int sceNetAdhocctlScan() {
networks = NULL;
peerlock.unlock();

// Prepare Scan Request Packet
uint8_t opcode = OPCODE_SCAN;

// Send Scan Request Packet, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP)
int iResult = send(metasocket, (char *)&opcode, 1, MSG_NOSIGNAL);
int error = errno;

if (iResult == SOCKET_ERROR) {
if (error != EAGAIN && error != EWOULDBLOCK) {
ERROR_LOG(SCENET, "Socket error (%i) when sending", error);
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
//if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer
return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy");
}
else if (friendFinderRunning) {
AdhocctlRequest req = { OPCODE_SCAN, {0} };
return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan");
}
if (friendFinderRunning) {
AdhocctlRequest req = { OPCODE_SCAN, {0} };
return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan");
}
else {
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
}

// Return Success and let friendFinder thread to notify the handler when scan completed
Expand Down Expand Up @@ -2603,36 +2598,28 @@ int NetAdhocctl_Create(const char* groupName) {
// Disconnected State
if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) {
isAdhocctlBusy = true;
isAdhocctlNeedLogin = true;

// Set Network Name
if (groupNameStruct != NULL) parameter.group_name = *groupNameStruct;
if (groupNameStruct != NULL)
parameter.group_name = *groupNameStruct;

// Reset Network Name
else memset(&parameter.group_name, 0, sizeof(parameter.group_name));

// Prepare Connect Packet
SceNetAdhocctlConnectPacketC2S packet;

// Clear Packet Memory
memset(&packet, 0, sizeof(packet));

// Set Packet Opcode
packet.base.opcode = OPCODE_CONNECT;

// Set Target Group
if (groupNameStruct != NULL) packet.group = *groupNameStruct;

// Acquire Network Lock

// Send Packet
int iResult = send(metasocket, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);
int error = errno;
adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
else
memset(&parameter.group_name, 0, sizeof(parameter.group_name));

if (iResult == SOCKET_ERROR && error != EAGAIN && error != EWOULDBLOCK) {
ERROR_LOG(SCENET, "Socket error (%i) when sending", error);
// Set HUD Connection Status
//setConnectionStatus(1);

//Faking success, to prevent Full Auto 2 from freezing while Initializing Network
// Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
int us = adhocDefaultDelay;
if (friendFinderRunning) {
AdhocctlRequest req = { OPCODE_CONNECT, parameter.group_name };
return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect");
}
//Faking success, to prevent Full Auto 2 from freezing while Initializing Network
else {
adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
adhocctlState = ADHOCCTL_STATE_GAMEMODE;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
Expand All @@ -2645,19 +2632,6 @@ int NetAdhocctl_Create(const char* groupName) {
}
}

// Free Network Lock

// Set HUD Connection Status
//setConnectionStatus(1);

// Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
int us = adhocDefaultDelay;
if (adhocctlState != ADHOCCTL_STATE_CONNECTED && adhocctlState != ADHOCCTL_STATE_GAMEMODE && iResult == SOCKET_ERROR && friendFinderRunning) {
AdhocctlRequest req = { OPCODE_CONNECT, {0} };
if (groupNameStruct != NULL) req.group = *groupNameStruct;
return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect");
}

hleEatMicro(us);
// Return Success
// FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line.
Expand Down
30 changes: 22 additions & 8 deletions Core/Util/PortManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ bool PortManager::Initialize(const unsigned int timeout) {
}

ERROR_LOG(SCENET, "PortManager - upnpDiscover failed (error: %i) or No UPnP device detected", error);
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff);
if (g_Config.bEnableUPnP) {
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff);
}
m_InitState = UPNP_INITSTATE_NONE;
#endif // WITH_UPNP
return false;
Expand All @@ -197,13 +199,18 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short
char port_str[16];
char intport_str[16];
int r;
auto n = GetI18NCategory("Networking");

if (intport == 0)
intport = port;
INFO_LOG(SCENET, "PortManager::Add(%s, %d, %d)", protocol, port, intport);
if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0')
{
if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Add - the init was not done !");
if (g_Config.bEnableUPnP) {
WARN_LOG(SCENET, "PortManager::Add - the init was not done !");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
}
Terminate();
return false;
}
sprintf(port_str, "%d", port);
Expand All @@ -229,8 +236,9 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short
{
ERROR_LOG(SCENET, "PortManager - AddPortMapping failed (error: %i)", r);
if (r == UPNPCOMMAND_HTTP_ERROR) {
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
if (g_Config.bEnableUPnP) {
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
}
Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts
return false;
}
Expand All @@ -248,11 +256,16 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short
bool PortManager::Remove(const char* protocol, unsigned short port) {
#ifdef WITH_UPNP
char port_str[16];
auto n = GetI18NCategory("Networking");

INFO_LOG(SCENET, "PortManager::Remove(%s, %d)", protocol, port);
if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0')
{
if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Remove - the init was not done !");
if (g_Config.bEnableUPnP) {
WARN_LOG(SCENET, "PortManager::Remove - the init was not done !");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
}
Terminate();
return false;
}
sprintf(port_str, "%d", port);
Expand All @@ -261,8 +274,9 @@ bool PortManager::Remove(const char* protocol, unsigned short port) {
{
ERROR_LOG(SCENET, "PortManager - DeletePortMapping failed (error: %i)", r);
if (r == UPNPCOMMAND_HTTP_ERROR) {
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
if (g_Config.bEnableUPnP) {
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
}
Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts
return false;
}
Expand Down

0 comments on commit af277bb

Please sign in to comment.