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:
parent
f1467d10fd
commit
895e4e6aa6
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user