1
0
forked from 0ad/0ad

StunClient code cleanup: use enet functions, endianness

Instead of using platform-specific sockets, use enet_socket* functions
(which ends up doing the same).
Clean up some confusing APIs, removing the distinction between finding
the public IP for the host/join.

Fix endianness support & use simpler code.

Refs D364 / 61261d14fc (and some subsequent fixing diffs).

Differential Revision: https://code.wildfiregames.com/D3970
This was SVN commit r25453.
This commit is contained in:
wraitii 2021-05-17 15:14:10 +00:00
parent f1467d10fd
commit 895e4e6aa6
12 changed files with 187 additions and 244 deletions

View File

@ -22,10 +22,6 @@
class ScriptRequest;
namespace StunClient {
struct StunEndpoint;
}
class IXmppClient
{
public:
@ -64,7 +60,7 @@ public:
virtual bool GuiPollHasPlayerListUpdate() = 0;
virtual void SendMUCMessage(const std::string& message) = 0;
virtual void SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJID) = 0;
virtual void SendStunEndpointToHost(const std::string& ip, u16 port, const std::string& hostJID) = 0;
};
extern IXmppClient *g_XmppClient;

View File

@ -20,12 +20,7 @@
#include "XmppClient.h"
#include "StanzaExtensions.h"
#ifdef WIN32
# include <winsock2.h>
#endif
#include "i18n/L10n.h"
#include "lib/external_libraries/enet.h"
#include "lib/utf8.h"
#include "network/NetServer.h"
#include "network/NetClient.h"
@ -883,7 +878,7 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq)
LOGWARNING("XmppClient: Received game with no IP in response to Game Register");
return true;
}
g_NetServer->SetConnectionData(publicIP, g_NetServer->GetPublicPort(), false);
g_NetServer->SetConnectionData(publicIP, g_NetServer->GetPublicPort());
return true;
}
@ -1484,18 +1479,13 @@ std::string XmppClient::RegistrationResultToString(gloox::RegistrationResult res
#undef CASE
}
void XmppClient::SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJIDStr)
void XmppClient::SendStunEndpointToHost(const std::string& ip, u16 port, const std::string& hostJIDStr)
{
DbgXMPP("SendStunEndpointToHost " << hostJIDStr);
char ipStr[256] = "(error)";
ENetAddress addr;
addr.host = ntohl(stunEndpoint.ip);
enet_address_get_host_ip(&addr, ipStr, ARRAY_SIZE(ipStr));
glooxwrapper::JID hostJID(hostJIDStr);
glooxwrapper::Jingle::Session session = m_sessionManager->createSession(hostJID);
session.sessionInitiate(ipStr, stunEndpoint.port);
session.sessionInitiate(ip.c_str(), port);
}
void XmppClient::handleSessionAction(gloox::Jingle::Action action, glooxwrapper::Jingle::Session& session, const glooxwrapper::Jingle::Session::Jingle& jingle)

View File

@ -106,7 +106,7 @@ public:
JS::Value GUIGetBoardList(const ScriptRequest& rq);
JS::Value GUIGetProfile(const ScriptRequest& rq);
void SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJID);
void SendStunEndpointToHost(const std::string& ip, u16 port, const std::string& hostJID);
/**
* Convert gloox values to string or time.

View File

@ -841,7 +841,7 @@ glooxwrapper::Jingle::Session::~Session()
delete m_Wrapped;
}
bool glooxwrapper::Jingle::Session::sessionInitiate(char* ipStr, u16 port)
bool glooxwrapper::Jingle::Session::sessionInitiate(const char* ipStr, u16 port)
{
gloox::Jingle::ICEUDP::CandidateList candidateList;

View File

@ -673,7 +673,7 @@ namespace glooxwrapper
Session(gloox::Jingle::Session* wrapped, bool owned);
~Session();
bool sessionInitiate(char* ipStr, uint16_t port);
bool sessionInitiate(const char* ipStr, uint16_t port);
};
class GLOOXWRAPPER_API SessionHandler

View File

@ -240,15 +240,8 @@ bool CNetClient::TryToConnect(const CStr& hostJID)
ENetHost* enetClient = nullptr;
if (g_XmppClient && m_UseSTUN)
{
// Find an unused port
for (int i = 0; i < 5 && !enetClient; ++i)
{
// Ports below 1024 are privileged on unix
u16 port = 1024 + rand() % (UINT16_MAX - 1024);
ENetAddress hostAddr{ ENET_HOST_ANY, port };
enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0);
++hostAddr.port;
}
ENetAddress hostAddr{ ENET_HOST_ANY, ENET_PORT_ANY };
enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0);
if (!enetClient)
{
@ -259,9 +252,9 @@ bool CNetClient::TryToConnect(const CStr& hostJID)
return false;
}
StunClient::StunEndpoint stunEndpoint;
CStr ip;
if (!StunClient::FindStunEndpointJoin(*enetClient, stunEndpoint, ip))
u16 port;
if (!StunClient::FindPublicIP(*enetClient, ip, port))
{
PushGuiMessage(
"type", "netstatus",
@ -280,7 +273,7 @@ bool CNetClient::TryToConnect(const CStr& hostJID)
return true;
}
g_XmppClient->SendStunEndpointToHost(stunEndpoint, hostJID);
g_XmppClient->SendStunEndpointToHost(ip, port, hostJID);
SDL_Delay(1000);

View File

@ -1658,11 +1658,20 @@ u16 CNetServer::GetLocalPort() const
return m_Worker->m_Host->address.port;
}
void CNetServer::SetConnectionData(const CStr& ip, const u16 port, bool useSTUN)
void CNetServer::SetConnectionData(const CStr& ip, const u16 port)
{
m_PublicIp = ip;
m_PublicPort = port;
m_UseSTUN = useSTUN;
m_UseSTUN = false;
}
bool CNetServer::SetConnectionDataViaSTUN()
{
m_UseSTUN = true;
std::lock_guard<std::mutex> lock(m_Worker->m_WorkerMutex);
if (!m_Worker->m_Host)
return false;
return StunClient::FindPublicIP(*m_Worker->m_Host, m_PublicIp, m_PublicPort);
}
bool CNetServer::CheckPasswordAndIncrement(const CStr& password, const std::string& username)

View File

@ -149,7 +149,8 @@ public:
void SendHolePunchingMessage(const CStr& ip, u16 port);
void SetConnectionData(const CStr& ip, u16 port, bool useSTUN);
void SetConnectionData(const CStr& ip, u16 port);
bool SetConnectionDataViaSTUN();
bool GetUseSTUN() const;

View File

@ -20,43 +20,25 @@
#include "StunClient.h"
#include "lib/sysdep/os.h"
#include <chrono>
#include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#if OS_WIN
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <sys/socket.h>
# include <netdb.h>
#endif
#include <vector>
#include "lib/external_libraries/enet.h"
#if OS_WIN
#include "lib/sysdep/os/win/wposix/wtime.h"
#endif
#include "lib/byte_order.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/CStr.h"
unsigned int m_StunServerIP;
int m_StunServerPort;
#include "lib/external_libraries/enet.h"
#include <chrono>
#include <vector>
#include <thread>
namespace StunClient
{
/**
* These constants are defined in Section 6 of RFC 5389.
*/
const u32 m_MagicCookie = 0x2112A442;
const u32 m_MethodTypeBinding = 0x0001;
const u16 m_MethodTypeBinding = 0x01;
const u32 m_BindingSuccessResponse = 0x0101;
/**
@ -87,45 +69,76 @@ const u16 m_AttrTypeXORMappedAddress = 0x0020;
*/
u8 m_TransactionID[12];
ENetAddress m_StunServer;
/**
* Discovered STUN endpoint
* Public IP + port discovered via the STUN transaction.
*/
u32 m_IP;
u16 m_Port;
ENetAddress m_PublicAddress;
void AddUInt16(std::vector<u8>& buffer, const u16 value)
/**
* Push POD data to a network-byte-order buffer.
* TODO: this should be optimised & moved to byte_order.h
*/
template<typename T, size_t n = sizeof(T)>
void AddToBuffer(std::vector<u8>& buffer, const T value)
{
buffer.push_back((value >> 8) & 0xff);
buffer.push_back(value & 0xff);
static_assert(std::is_pod_v<T>, "T must be POD");
buffer.reserve(buffer.size() + n);
// std::byte* can alias anything so this is legal.
const std::byte* ptr = reinterpret_cast<const std::byte*>(&value);
for (size_t a = 0; a < n; ++a)
#if BYTE_ORDER == LITTLE_ENDIAN
buffer.push_back(static_cast<u8>(*(ptr + n - 1 - a)));
#else
buffer.push_back(static_cast<u8>(*(ptr + a)));
#endif
}
void AddUInt32(std::vector<u8>& buffer, const u32 value)
{
buffer.push_back((value >> 24) & 0xff);
buffer.push_back((value >> 16) & 0xff);
buffer.push_back((value >> 8) & 0xff);
buffer.push_back( value & 0xff);
}
template<typename T, size_t n>
/**
* Read POD data from a network-byte-order buffer.
* TODO: this should be optimised & moved to byte_order.h
*/
template<typename T, size_t n = sizeof(T)>
bool GetFromBuffer(const std::vector<u8>& buffer, u32& offset, T& result)
{
static_assert(std::is_pod_v<T>, "T must be POD");
if (offset + n > buffer.size())
return false;
int a = n;
offset += n;
while (a--)
{
// Prevent shift count overflow if the type is u8
if (n > 1)
result <<= 8;
// std::byte* can alias anything so this is legal.
std::byte* ptr = reinterpret_cast<std::byte*>(&result);
for (size_t a = 0; a < n; ++a)
#if BYTE_ORDER == LITTLE_ENDIAN
*ptr++ = static_cast<std::byte>(buffer[offset + n - 1 - a]);
#else
*ptr++ = static_cast<std::byte>(buffer[offset + a]);
#endif
result += buffer[offset - 1 - a];
}
offset += n;
return true;
}
void SendStunRequest(ENetHost& transactionHost, ENetAddress addr)
{
std::vector<u8> buffer;
AddToBuffer<u16>(buffer, m_MethodTypeBinding);
AddToBuffer<u16>(buffer, 0); // length
AddToBuffer<u32>(buffer, m_MagicCookie);
for (std::size_t i = 0; i < sizeof(m_TransactionID); ++i)
{
u8 random_byte = rand() % 256;
buffer.push_back(random_byte);
m_TransactionID[i] = random_byte;
}
ENetBuffer enetBuffer;
enetBuffer.data = buffer.data();
enetBuffer.dataLength = buffer.size();
enet_socket_send(transactionHost.socket, &addr, &enetBuffer, 1);
}
/**
* Creates a STUN request and sends it to a STUN server.
* The request is sent through transactionHost, from which the answer
@ -134,73 +147,24 @@ bool GetFromBuffer(const std::vector<u8>& buffer, u32& offset, T& result)
bool CreateStunRequest(ENetHost& transactionHost)
{
CStr server_name;
int port;
CFG_GET_VAL("lobby.stun.server", server_name);
CFG_GET_VAL("lobby.stun.port", m_StunServerPort);
CFG_GET_VAL("lobby.stun.port", port);
debug_printf("GetPublicAddress: Using STUN server %s:%d\n", server_name.c_str(), m_StunServerPort);
LOGMESSAGE("StunClient: Using STUN server %s:%d\n", server_name.c_str(), port);
addrinfo hints;
addrinfo* res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
// Resolve the stun server name so we can send it a STUN request
int status = getaddrinfo(server_name.c_str(), nullptr, &hints, &res);
if (status != 0)
{
#ifdef UNICODE
LOGERROR("GetPublicAddress: Error in getaddrinfo: %s", utf8_from_wstring(gai_strerror(status)));
#else
LOGERROR("GetPublicAddress: Error in getaddrinfo: %s", gai_strerror(status));
#endif
ENetAddress addr;
addr.port = port;
if (enet_address_set_host(&addr, server_name.c_str()) == -1)
return false;
}
ENSURE(res);
m_StunServer = addr;
// Documentation says it points to "one or more addrinfo structures"
sockaddr_in* current_interface = reinterpret_cast<sockaddr_in*>(res->ai_addr);
m_StunServerIP = ntohl(current_interface->sin_addr.s_addr);
StunClient::SendStunRequest(transactionHost, addr);
StunClient::SendStunRequest(transactionHost, m_StunServerIP, m_StunServerPort);
freeaddrinfo(res);
return true;
}
void StunClient::SendStunRequest(ENetHost& transactionHost, u32 targetIp, u16 targetPort)
{
std::vector<u8> buffer;
AddUInt16(buffer, m_MethodTypeBinding);
AddUInt16(buffer, 0); // length
AddUInt32(buffer, m_MagicCookie);
for (std::size_t i = 0; i < sizeof(m_TransactionID); ++i)
{
u8 random_byte = rand() % 256;
buffer.push_back(random_byte);
m_TransactionID[i] = random_byte;
}
sockaddr_in to;
int to_len = sizeof(to);
memset(&to, 0, to_len);
to.sin_family = AF_INET;
to.sin_port = htons(targetPort);
to.sin_addr.s_addr = htonl(targetIp);
sendto(
transactionHost.socket,
reinterpret_cast<char*>(buffer.data()),
static_cast<int>(buffer.size()),
0,
reinterpret_cast<sockaddr*>(&to),
to_len);
}
/**
* Gets the response from the STUN server and checks it for its validity.
*/
@ -212,40 +176,37 @@ bool ReceiveStunResponse(ENetHost& transactionHost, std::vector<u8>& buffer)
memset(input_buffer, 0, LEN);
sockaddr_in addr;
socklen_t from_len = sizeof(addr);
ENetBuffer enetBuffer;
enetBuffer.data = input_buffer;
enetBuffer.dataLength = LEN;
int len = recvfrom(transactionHost.socket, input_buffer, LEN, 0, reinterpret_cast<sockaddr*>(&addr), &from_len);
ENetAddress sender = m_StunServer;
int len = enet_socket_receive(transactionHost.socket, &sender, &enetBuffer, 1);
int delay = 200;
CFG_GET_VAL("lobby.stun.delay", delay);
// Wait to receive the message because enet sockets are non-blocking
const int max_tries = 5;
for (int count = 0; len < 0 && (count < max_tries || max_tries == -1); ++count)
for (int count = 0; len <= 0 && (count < max_tries || max_tries == -1); ++count)
{
usleep(delay * 1000);
len = recvfrom(transactionHost.socket, input_buffer, LEN, 0, reinterpret_cast<sockaddr*>(&addr), &from_len);
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
len = enet_socket_receive(transactionHost.socket, &sender, &enetBuffer, 1);
}
if (len < 0)
if (len <= 0)
{
LOGERROR("GetPublicAddress: recvfrom error (%d): %s", errno, strerror(errno));
LOGERROR("ReceiveStunResponse: recvfrom error (%d): %s", errno, strerror(errno));
return false;
}
u32 sender_ip = ntohl(static_cast<u32>(addr.sin_addr.s_addr));
u16 sender_port = ntohs(addr.sin_port);
if (sender_ip != m_StunServerIP)
LOGERROR("GetPublicAddress: Received stun response from different address: %d:%d (%d.%d.%d.%d:%d) %s",
addr.sin_addr.s_addr,
addr.sin_port,
(sender_ip >> 24) & 0xff,
(sender_ip >> 16) & 0xff,
(sender_ip >> 8) & 0xff,
(sender_ip >> 0) & 0xff,
sender_port,
if (memcmp(&sender, &m_StunServer, sizeof(m_StunServer)) != 0)
LOGERROR("ReceiveStunResponse: Received stun response from different address: %d.%d.%d.%d:%d %s",
(sender.host >> 24) & 0xff,
(sender.host >> 16) & 0xff,
(sender.host >> 8) & 0xff,
(sender.host >> 0) & 0xff,
sender.port,
input_buffer);
// Convert to network string.
@ -260,7 +221,7 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
u32 offset = 0;
u16 responseType = 0;
if (!GetFromBuffer<u16, 2>(buffer, offset, responseType) || responseType != m_BindingSuccessResponse)
if (!GetFromBuffer(buffer, offset, responseType) || responseType != m_BindingSuccessResponse)
{
LOGERROR("STUN response isn't a binding success response");
return false;
@ -270,7 +231,7 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
offset += 2;
u32 cookie = 0;
if (!GetFromBuffer<u32, 4>(buffer, offset, cookie) || cookie != m_MagicCookie)
if (!GetFromBuffer(buffer, offset, cookie) || cookie != m_MagicCookie)
{
LOGERROR("STUN response doesn't contain the magic cookie");
return false;
@ -279,7 +240,7 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
for (std::size_t i = 0; i < sizeof(m_TransactionID); ++i)
{
u8 transactionChar = 0;
if (!GetFromBuffer<u8, 1>(buffer, offset, transactionChar) || transactionChar != m_TransactionID[i])
if (!GetFromBuffer(buffer, offset, transactionChar) || transactionChar != m_TransactionID[i])
{
LOGERROR("STUN response doesn't contain the transaction ID");
return false;
@ -290,8 +251,8 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
{
u16 type = 0;
u16 size = 0;
if (!GetFromBuffer<u16, 2>(buffer, offset, type) ||
!GetFromBuffer<u16, 2>(buffer, offset, size))
if (!GetFromBuffer(buffer, offset, type) ||
!GetFromBuffer(buffer, offset, size))
{
LOGERROR("STUN response contains invalid attribute");
return false;
@ -315,7 +276,7 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
++offset;
u8 ipFamily = 0;
if (!GetFromBuffer<u8, 1>(buffer, offset, ipFamily) || ipFamily != m_IPAddressFamilyIPv4)
if (!GetFromBuffer(buffer, offset, ipFamily) || ipFamily != m_IPAddressFamilyIPv4)
{
LOGERROR("Unsupported address family, IPv4 is expected");
return false;
@ -323,8 +284,8 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
u16 port = 0;
u32 ip = 0;
if (!GetFromBuffer<u16, 2>(buffer, offset, port) ||
!GetFromBuffer<u32, 4>(buffer, offset, ip))
if (!GetFromBuffer(buffer, offset, port) ||
!GetFromBuffer(buffer, offset, ip))
{
LOGERROR("Mapped address doesn't contain IP and port");
return false;
@ -337,8 +298,10 @@ bool ParseStunResponse(const std::vector<u8>& buffer)
ip ^= m_MagicCookie;
}
m_Port = port;
m_IP = ip;
// ENetAddress takes a host byte-order port and network byte-order IP.
// Network byte order is big endian, so convert appropriately.
m_PublicAddress.host = to_be32(ip);
m_PublicAddress.port = port;
break;
}
@ -371,48 +334,24 @@ bool STUNRequestAndResponse(ENetHost& transactionHost)
ParseStunResponse(buffer);
}
bool StunClient::FindStunEndpointHost(CStr& ip, u16& port)
{
ENetAddress hostAddr{ENET_HOST_ANY, static_cast<u16>(port)};
ENetHost* transactionHost = enet_host_create(&hostAddr, 1, 1, 0, 0);
if (!transactionHost)
return false;
bool success = STUNRequestAndResponse(*transactionHost);
enet_host_destroy(transactionHost);
if (!success)
return false;
// Convert m_IP to string
char ipStr[256] = "(error)";
ENetAddress addr;
addr.host = ntohl(m_IP);
int result = enet_address_get_host_ip(&addr, ipStr, ARRAY_SIZE(ipStr));
ip = ipStr;
port = m_Port;
return result == 0;
}
bool StunClient::FindStunEndpointJoin(ENetHost& transactionHost, StunClient::StunEndpoint& stunEndpoint, CStr& ip)
bool FindPublicIP(ENetHost& transactionHost, CStr& ip, u16& port)
{
if (!STUNRequestAndResponse(transactionHost))
return false;
// Convert m_IP to string
char ipStr[256] = "(error)";
ENetAddress addr;
addr.host = ntohl(m_IP);
enet_address_get_host_ip(&addr, ipStr, ARRAY_SIZE(ipStr));
enet_address_get_host_ip(&m_PublicAddress, ipStr, ARRAY_SIZE(ipStr));
ip = ipStr;
stunEndpoint.ip = m_IP;
stunEndpoint.port = m_Port;
port = m_PublicAddress.port;
LOGMESSAGE("StunClient: external IP address is %s:%i", ip.c_str(), port);
return true;
}
void StunClient::SendHolePunchingMessages(ENetHost& enetClient, const std::string& serverAddress, u16 serverPort)
void SendHolePunchingMessages(ENetHost& enetClient, const std::string& serverAddress, u16 serverPort)
{
// Convert ip string to int64
ENetAddress addr;
@ -425,12 +364,12 @@ void StunClient::SendHolePunchingMessages(ENetHost& enetClient, const std::strin
// Send an UDP message from enet host to ip:port
for (int i = 0; i < 3; ++i)
{
StunClient::SendStunRequest(enetClient, htonl(addr.host), serverPort);
usleep(delay * 1000);
SendStunRequest(enetClient, addr);
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
}
}
bool StunClient::FindLocalIP(CStr& ip)
bool FindLocalIP(CStr& ip)
{
// Open an UDP socket.
ENetSocket socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
@ -450,12 +389,15 @@ bool StunClient::FindLocalIP(CStr& ip)
if (enet_socket_get_address(socket, &addr) == -1)
return false;
enet_socket_destroy(socket);
// Convert to a human readable string.
char buf[INET_ADDRSTRLEN];
if (enet_address_get_host_ip(&addr, buf, INET_ADDRSTRLEN) == -1)
char buf[50];
if (enet_address_get_host_ip(&addr, buf, ARRAY_SIZE(buf)) == -1)
return false;
ip = buf;
return true;
}
}

View File

@ -27,18 +27,20 @@ class CStr8;
namespace StunClient
{
/**
* Return the publicly accessible IP of the given ENet host/socket.
* This is done by contacting STUN server.
* The return IP & port should only be considered valid for the give host/socket.
*/
bool FindPublicIP(ENetHost& enetClient, CStr8& ip, u16& port);
struct StunEndpoint {
u32 ip;
u16 port;
};
void SendStunRequest(ENetHost& transactionHost, u32 targetIp, u16 targetPort);
bool FindStunEndpointHost(CStr8& ip, u16& port);
bool FindStunEndpointJoin(ENetHost& transactionHost, StunClient::StunEndpoint& stunEndpoint, CStr8& ip);
/**
* Send a message to the target server with the given ENet host/socket.
* This will open a port on the local gateway (if any) to receive trafic,
* allowing the recipient to answer (thus 'punching a hole' in the NAT).
* NB: this assumes consistent NAT, i.e. the outgoing port is always the same for the given client,
* thus allowing the IP discovered via STUN to be sent to the target server.
*/
void SendHolePunchingMessages(ENetHost& enetClient, const std::string& serverAddress, u16 serverPort);
/**

View File

@ -98,29 +98,6 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
// Always use lobby authentication for lobby matches to prevent impersonation and smurfing, in particular through mods that implemented an UI for arbitrary or other players nicknames.
bool hasLobby = !!g_XmppClient;
g_NetServer = new CNetServer(hasLobby);
// In lobby, we send our public ip and port on request to the players, who want to connect.
// In either case we need to know our public IP. If using STUN, we'll use that,
// otherwise, the lobby's reponse to the game registration stanza will tell us our public IP.
if (hasLobby)
{
CStr ip;
if (!useSTUN)
// Don't store IP - the lobby bot will send it later.
// (if a client tries to connect before it's setup, they'll be disconnected)
g_NetServer->SetConnectionData("", serverPort, false);
else
{
u16 port = serverPort;
// This is using port variable to store return value, do not pass serverPort itself.
if (!StunClient::FindStunEndpointHost(ip, port))
{
ScriptException::Raise(rq, "Failed to host via STUN.");
SAFE_DELETE(g_NetServer);
return;
}
g_NetServer->SetConnectionData(ip, port, true);
}
}
if (!g_NetServer->SetupConnection(serverPort))
{
@ -129,6 +106,23 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
return;
}
// In lobby, we send our public ip and port on request to the players who want to connect.
// Thus we need to know our public IP. Use STUN if that's available,
// otherwise, the lobby's reponse to the game registration stanza will tell us our public IP.
if (hasLobby)
{
if (!useSTUN)
// Don't store IP - the lobby bot will send it later.
// (if a client tries to connect before it's setup, they'll be disconnected)
g_NetServer->SetConnectionData("", serverPort);
else if (!g_NetServer->SetConnectionDataViaSTUN())
{
ScriptException::Raise(rq, "Failed to host via STUN.");
SAFE_DELETE(g_NetServer);
return;
}
}
// Generate a secret to identify the host client.
std::string secret = ps_generate_guid();

View File

@ -20,6 +20,7 @@
#include "network/StunClient.h"
#include "lib/external_libraries/enet.h"
#include "ps/ConfigDB.h"
#include "ps/CStr.h"
class TestStunClient : public CxxTest::TestSuite
@ -60,4 +61,19 @@ public:
if (dots != 3)
TS_FAIL("StunClient::FindLocalIP did not return a valid IPV4 address: wrong separators");
}
void test_stun_DISABLED()
{
// Disabled test -> should return your external IP by connecting to our STUN server.
CConfigDB::Initialise();
CStr ip;
u16 port;
g_ConfigDB.SetValueString(CFG_COMMAND, "lobby.stun.server", "lobby.wildfiregames.com");
g_ConfigDB.SetValueString(CFG_COMMAND, "lobby.stun.port", "3478");
ENetAddress addr { ENET_HOST_ANY, ENET_PORT_ANY };
ENetHost* host = enet_host_create(&addr, 1, 1, 0, 0);
StunClient::FindPublicIP(*host, ip, port);
LOGWARNING("%s %i", ip.c_str(), port);
CConfigDB::Shutdown();
}
};