From d7a4fb7c20b36733c965603b7f70cb0e95db7d46 Mon Sep 17 00:00:00 2001 From: wraitii Date: Sat, 22 May 2021 08:34:00 +0000 Subject: [PATCH] Try to punch a hole through local firewalls, and fallback to localhost. Fixes (probably rare) regression in 2034136560. Differential Revision: https://code.wildfiregames.com/D3999 This was SVN commit r25515. --- source/lobby/XmppClient.cpp | 4 ++- source/network/NetClient.cpp | 59 ++++++++++++++++++++++++++---------- source/network/NetClient.h | 3 +- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/source/lobby/XmppClient.cpp b/source/lobby/XmppClient.cpp index 4b7ee097ac..e1279c2dec 100644 --- a/source/lobby/XmppClient.cpp +++ b/source/lobby/XmppClient.cpp @@ -862,7 +862,7 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq) } g_NetClient->SetupServerData(cd->m_Ip.to_string(), stoi(cd->m_Port.to_string()), !cd->m_UseSTUN.empty()); - g_NetClient->TryToConnect(iq.from().full()); + g_NetClient->TryToConnect(iq.from().full(), !cd->m_IsLocalIP.empty()); } if (gq) { @@ -995,6 +995,7 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq) connectionData->m_Ip = g_NetServer->GetPublicIp(); connectionData->m_Port = std::to_string(g_NetServer->GetPublicPort()); connectionData->m_UseSTUN = g_NetServer->GetUseSTUN() ? "true" : ""; + connectionData->m_IsLocalIP = ""; } else { @@ -1004,6 +1005,7 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq) connectionData->m_Ip = ip; connectionData->m_Port = std::to_string(g_NetServer->GetLocalPort()); connectionData->m_UseSTUN = ""; + connectionData->m_IsLocalIP = "true"; } else connectionData->m_Error = "local_ip_failed"; diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index 61dda12afd..8f75a32f24 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -231,7 +231,7 @@ void CNetClient::HandleGetServerDataFailed(const CStr& error) ); } -bool CNetClient::TryToConnect(const CStr& hostJID) +bool CNetClient::TryToConnect(const CStr& hostJID, bool localNetwork) { if (m_Session) return false; @@ -245,23 +245,22 @@ bool CNetClient::TryToConnect(const CStr& hostJID) return false; } - ENetHost* enetClient = nullptr; + ENetAddress hostAddr{ ENET_HOST_ANY, ENET_PORT_ANY }; + ENetHost* enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0); + + if (!enetClient) + { + PushGuiMessage( + "type", "netstatus", + "status", "disconnected", + "reason", static_cast(NDR_STUN_PORT_FAILED)); + return false; + } + + CStr ip; + u16 port; if (g_XmppClient && m_UseSTUN) { - ENetAddress hostAddr{ ENET_HOST_ANY, ENET_PORT_ANY }; - enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0); - - if (!enetClient) - { - PushGuiMessage( - "type", "netstatus", - "status", "disconnected", - "reason", static_cast(NDR_STUN_PORT_FAILED)); - return false; - } - - CStr ip; - u16 port; if (!StunClient::FindPublicIP(*enetClient, ip, port)) { PushGuiMessage( @@ -280,11 +279,39 @@ bool CNetClient::TryToConnect(const CStr& hostJID) // Return true anyways - we're on a success path here. return true; } + } + else if (g_XmppClient && localNetwork) + { + // We may need to punch a hole through the local firewall, so fetch our local IP. + // NB: we'll ignore failures here, and hope that the firewall will be open to connection + // if we fail to fetch the local IP (which is unlikely anyways). + if (!StunClient::FindLocalIP(ip)) + ip = ""; + // Check if we're hosting on localhost, and if so, explicitly use that + // (this circumvents, at least, the 'block all incoming connections' setting + // on the MacOS firewall). + if (ip == m_ServerAddress) + { + m_ServerAddress = "127.0.0.1"; + ip = ""; + } + port = enetClient->address.port; + } + LOGMESSAGE("NetClient: connecting to server at %s:%i", m_ServerAddress, m_ServerPort); + + if (!ip.empty()) + { + // UDP hole-punching + // Step 0: send a message, via XMPP, to the server with our external IP & port. g_XmppClient->SendStunEndpointToHost(ip, port, hostJID); + // Step 1b: Wait some time - we need the host to receive the stun endpoint and start punching a hole themselves before + // we try to establish the connection below. SDL_Delay(1000); + // Step 2: Send a message ourselves to the server so that the NAT, if any, routes incoming trafic correctly. + // TODO: verify if this step is necessary, since we'll try and connect anyways below. StunClient::SendHolePunchingMessages(*enetClient, m_ServerAddress, m_ServerPort); } diff --git a/source/network/NetClient.h b/source/network/NetClient.h index ff1200bd82..4af25b8614 100644 --- a/source/network/NetClient.h +++ b/source/network/NetClient.h @@ -134,9 +134,10 @@ public: /** * Connect to the remote networked server using lobby. * Push netstatus messages on failure. + * @param localNetwork - if true, assume we are trying to connect on the local network. * @return true on success, false on connection failure */ - bool TryToConnect(const CStr& hostJID); + bool TryToConnect(const CStr& hostJID, bool localNetwork); /** * Destroy the connection to the server.