mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
a629555e6b
Fixed bug with UPnP so that it will grab the proper address and protocal Fixed bug that caused dolphin to freeze when host codes were to large
360 lines
8.0 KiB
C++
360 lines
8.0 KiB
C++
// This file is public domain, in case it's useful to anyone. -comex
|
|
|
|
#include "Timer.h"
|
|
#include "Common/TraversalClient.h"
|
|
|
|
static void GetRandomishBytes(u8* buf, size_t size)
|
|
{
|
|
// We don't need high quality random numbers (which might not be available),
|
|
// just non-repeating numbers!
|
|
srand(enet_time_get());
|
|
for (size_t i = 0; i < size; i++)
|
|
buf[i] = rand() & 0xff;
|
|
}
|
|
|
|
TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server, const u16 port)
|
|
: m_NetHost(netHost)
|
|
, m_Client(nullptr)
|
|
, m_FailureReason(0)
|
|
, m_ConnectRequestId(0)
|
|
, m_PendingConnect(false)
|
|
, m_Server(server)
|
|
, m_port(port)
|
|
, m_PingTime(0)
|
|
{
|
|
netHost->intercept = TraversalClient::InterceptCallback;
|
|
|
|
Reset();
|
|
|
|
ReconnectToServer();
|
|
}
|
|
|
|
TraversalClient::~TraversalClient()
|
|
{
|
|
}
|
|
|
|
void TraversalClient::ReconnectToServer()
|
|
{
|
|
if (enet_address_set_host(&m_ServerAddress, m_Server.c_str()))
|
|
{
|
|
OnFailure(BadHost);
|
|
return;
|
|
}
|
|
m_ServerAddress.port = m_port;
|
|
|
|
m_State = Connecting;
|
|
|
|
TraversalPacket hello = {};
|
|
hello.type = TraversalPacketHelloFromClient;
|
|
hello.helloFromClient.protoVersion = TraversalProtoVersion;
|
|
SendTraversalPacket(hello);
|
|
if (m_Client)
|
|
m_Client->OnTraversalStateChanged();
|
|
}
|
|
|
|
static ENetAddress MakeENetAddress(TraversalInetAddress* address)
|
|
{
|
|
ENetAddress eaddr;
|
|
if (address->isIPV6)
|
|
{
|
|
eaddr.port = 0; // no support yet :(
|
|
}
|
|
else
|
|
{
|
|
eaddr.host = address->address[0];
|
|
eaddr.port = ntohs(address->port);
|
|
}
|
|
return eaddr;
|
|
}
|
|
|
|
void TraversalClient::ConnectToClient(const std::string& host)
|
|
{
|
|
if (host.size() > sizeof(TraversalHostId))
|
|
{
|
|
PanicAlert("host too long");
|
|
return;
|
|
}
|
|
TraversalPacket packet = {};
|
|
packet.type = TraversalPacketConnectPlease;
|
|
memcpy(packet.connectPlease.hostId.data(), host.c_str(), host.size());
|
|
m_ConnectRequestId = SendTraversalPacket(packet);
|
|
m_PendingConnect = true;
|
|
}
|
|
|
|
bool TraversalClient::TestPacket(u8* data, size_t size, ENetAddress* from)
|
|
{
|
|
if (from->host == m_ServerAddress.host &&
|
|
from->port == m_ServerAddress.port)
|
|
{
|
|
if (size < sizeof(TraversalPacket))
|
|
{
|
|
ERROR_LOG(NETPLAY, "Received too-short traversal packet.");
|
|
}
|
|
else
|
|
{
|
|
HandleServerPacket((TraversalPacket*) data);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--Temporary until more of the old netplay branch is moved over
|
|
void TraversalClient::Update()
|
|
{
|
|
ENetEvent netEvent;
|
|
if (enet_host_service(m_NetHost, &netEvent, 4) > 0)
|
|
{
|
|
switch (netEvent.type)
|
|
{
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
TestPacket(netEvent.packet->data, netEvent.packet->dataLength, &netEvent.peer->address);
|
|
|
|
enet_packet_destroy(netEvent.packet);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
HandleResends();
|
|
}
|
|
|
|
void TraversalClient::HandleServerPacket(TraversalPacket* packet)
|
|
{
|
|
u8 ok = 1;
|
|
switch (packet->type)
|
|
{
|
|
case TraversalPacketAck:
|
|
if (!packet->ack.ok)
|
|
{
|
|
OnFailure(ServerForgotAboutUs);
|
|
break;
|
|
}
|
|
for (auto it = m_OutgoingTraversalPackets.begin(); it != m_OutgoingTraversalPackets.end(); ++it)
|
|
{
|
|
if (it->packet.requestId == packet->requestId)
|
|
{
|
|
m_OutgoingTraversalPackets.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case TraversalPacketHelloFromServer:
|
|
if (m_State != Connecting)
|
|
break;
|
|
if (!packet->helloFromServer.ok)
|
|
{
|
|
OnFailure(VersionTooOld);
|
|
break;
|
|
}
|
|
m_HostId = packet->helloFromServer.yourHostId;
|
|
m_State = Connected;
|
|
if (m_Client)
|
|
m_Client->OnTraversalStateChanged();
|
|
break;
|
|
case TraversalPacketPleaseSendPacket:
|
|
{
|
|
// security is overrated.
|
|
ENetAddress addr = MakeENetAddress(&packet->pleaseSendPacket.address);
|
|
if (addr.port != 0)
|
|
{
|
|
char message[] = "Hello from Dolphin Netplay...";
|
|
ENetBuffer buf;
|
|
buf.data = message;
|
|
buf.dataLength = sizeof(message) - 1;
|
|
enet_socket_send(m_NetHost->socket, &addr, &buf, 1);
|
|
}
|
|
else
|
|
{
|
|
// invalid IPV6
|
|
ok = 0;
|
|
}
|
|
break;
|
|
}
|
|
case TraversalPacketConnectReady:
|
|
case TraversalPacketConnectFailed:
|
|
{
|
|
if (!m_PendingConnect || packet->connectReady.requestId != m_ConnectRequestId)
|
|
break;
|
|
|
|
m_PendingConnect = false;
|
|
|
|
if (!m_Client)
|
|
break;
|
|
|
|
if (packet->type == TraversalPacketConnectReady)
|
|
m_Client->OnConnectReady(MakeENetAddress(&packet->connectReady.address));
|
|
else
|
|
m_Client->OnConnectFailed(packet->connectFailed.reason);
|
|
break;
|
|
}
|
|
default:
|
|
WARN_LOG(NETPLAY, "Received unknown packet with type %d", packet->type);
|
|
break;
|
|
}
|
|
if (packet->type != TraversalPacketAck)
|
|
{
|
|
TraversalPacket ack = {};
|
|
ack.type = TraversalPacketAck;
|
|
ack.requestId = packet->requestId;
|
|
ack.ack.ok = ok;
|
|
|
|
ENetBuffer buf;
|
|
buf.data = &ack;
|
|
buf.dataLength = sizeof(ack);
|
|
if (enet_socket_send(m_NetHost->socket, &m_ServerAddress, &buf, 1) == -1)
|
|
OnFailure(SocketSendError);
|
|
}
|
|
}
|
|
|
|
void TraversalClient::OnFailure(int reason)
|
|
{
|
|
m_State = Failure;
|
|
m_FailureReason = reason;
|
|
|
|
switch (reason)
|
|
{
|
|
case TraversalClient::BadHost:
|
|
{
|
|
auto server = "dolphin-emu.org";
|
|
PanicAlertT("Couldn't look up central server %s", server);
|
|
break;
|
|
}
|
|
case TraversalClient::VersionTooOld:
|
|
PanicAlertT("Dolphin too old for traversal server");
|
|
break;
|
|
case TraversalClient::ServerForgotAboutUs:
|
|
PanicAlertT("Disconnected from traversal server");
|
|
break;
|
|
case TraversalClient::SocketSendError:
|
|
PanicAlertT("Socket error sending to traversal server");
|
|
break;
|
|
case TraversalClient::ResendTimeout:
|
|
PanicAlertT("Timeout connecting to traversal server");
|
|
break;
|
|
default:
|
|
PanicAlertT("Unknown error %x", reason);
|
|
break;
|
|
}
|
|
|
|
if (m_Client)
|
|
m_Client->OnTraversalStateChanged();
|
|
}
|
|
|
|
void TraversalClient::ResendPacket(OutgoingTraversalPacketInfo* info)
|
|
{
|
|
info->sendTime = enet_time_get();
|
|
info->tries++;
|
|
ENetBuffer buf;
|
|
buf.data = &info->packet;
|
|
buf.dataLength = sizeof(info->packet);
|
|
if (enet_socket_send(m_NetHost->socket, &m_ServerAddress, &buf, 1) == -1)
|
|
OnFailure(SocketSendError);
|
|
}
|
|
|
|
void TraversalClient::HandleResends()
|
|
{
|
|
enet_uint32 now = enet_time_get();
|
|
for (auto& tpi : m_OutgoingTraversalPackets)
|
|
{
|
|
if (now - tpi.sendTime >= (u32) (300 * tpi.tries))
|
|
{
|
|
if (tpi.tries >= 5)
|
|
{
|
|
OnFailure(ResendTimeout);
|
|
m_OutgoingTraversalPackets.clear();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ResendPacket(&tpi);
|
|
}
|
|
}
|
|
}
|
|
HandlePing();
|
|
}
|
|
|
|
void TraversalClient::HandlePing()
|
|
{
|
|
enet_uint32 now = enet_time_get();
|
|
if (m_State == Connected && now - m_PingTime >= 500)
|
|
{
|
|
TraversalPacket ping = {0};
|
|
ping.type = TraversalPacketPing;
|
|
ping.ping.hostId = m_HostId;
|
|
SendTraversalPacket(ping);
|
|
m_PingTime = now;
|
|
}
|
|
}
|
|
|
|
TraversalRequestId TraversalClient::SendTraversalPacket(const TraversalPacket& packet)
|
|
{
|
|
OutgoingTraversalPacketInfo info;
|
|
info.packet = packet;
|
|
GetRandomishBytes((u8*) &info.packet.requestId, sizeof(info.packet.requestId));
|
|
info.tries = 0;
|
|
m_OutgoingTraversalPackets.push_back(info);
|
|
ResendPacket(&m_OutgoingTraversalPackets.back());
|
|
return info.packet.requestId;
|
|
}
|
|
|
|
void TraversalClient::Reset()
|
|
{
|
|
m_PendingConnect = false;
|
|
m_Client = nullptr;
|
|
}
|
|
|
|
int ENET_CALLBACK TraversalClient::InterceptCallback(ENetHost* host, ENetEvent* event)
|
|
{
|
|
auto traversalClient = g_TraversalClient.get();
|
|
if (traversalClient->TestPacket(host->receivedData, host->receivedDataLength, &host->receivedAddress))
|
|
{
|
|
event->type = (ENetEventType)42;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::unique_ptr<TraversalClient> g_TraversalClient;
|
|
std::unique_ptr<ENetHost> g_MainNetHost;
|
|
|
|
// The settings at the previous TraversalClient reset - notably, we
|
|
// need to know not just what port it's on, but whether it was
|
|
// explicitly requested.
|
|
static std::string g_OldServer;
|
|
static u16 g_OldPort;
|
|
|
|
bool EnsureTraversalClient(const std::string& server, u16 port)
|
|
{
|
|
|
|
if (!g_MainNetHost || !g_TraversalClient || server != g_OldServer || port != g_OldPort)
|
|
{
|
|
g_OldServer = server;
|
|
g_OldPort = port ;
|
|
|
|
ENetAddress addr = { ENET_HOST_ANY, 0 };
|
|
ENetHost* host = enet_host_create(
|
|
&addr, // address
|
|
50, // peerCount
|
|
1, // channelLimit
|
|
0, // incomingBandwidth
|
|
0); // outgoingBandwidth
|
|
if (!host)
|
|
{
|
|
g_MainNetHost.reset();
|
|
return false;
|
|
}
|
|
g_MainNetHost.reset(host);
|
|
g_TraversalClient.reset(new TraversalClient(g_MainNetHost.get(), server, port));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ReleaseTraversalClient()
|
|
{
|
|
if (!g_TraversalClient)
|
|
return;
|
|
|
|
g_TraversalClient.release();
|
|
g_MainNetHost.release();
|
|
} |