[DHT] Integrate the protocol into the game's network infrastructure

This commit is contained in:
momo5502 2017-09-03 18:14:08 +02:00
parent 76b8e299a0
commit 5d4cd82ed1
6 changed files with 84 additions and 74 deletions

View File

@ -43,7 +43,6 @@ extern "C" int dht_gettimeofday(struct timeval *tp, struct timezone* /*tzp*/)
namespace Components namespace Components
{ {
SOCKET DHT::Socket;
std::mutex DHT::Mutex; std::mutex DHT::Mutex;
std::vector<Network::Address> DHT::Nodes; std::vector<Network::Address> DHT::Nodes;
std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> DHT::Handlers; std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> DHT::Handlers;
@ -66,6 +65,12 @@ namespace Components
DHT::Handlers[hashStr] = callback; DHT::Handlers[hashStr] = callback;
} }
void DHT::BootstrapDone()
{
// Tell the game we got our external ip
Utils::Hook::Set<BYTE>(0x649D6F0, 1);
}
void DHT::Hash(std::string data, void* out, size_t size) void DHT::Hash(std::string data, void* out, size_t size)
{ {
ZeroMemory(out, size); ZeroMemory(out, size);
@ -115,11 +120,10 @@ namespace Components
} }
} }
void DHT::OnData(std::string data, Network::Address address) void DHT::OnData(char* buf, int len, sockaddr* from, int fromlen)
{ {
time_t tosleep = 0; time_t tosleep = 0;
sockaddr addr = address.getSockAddr(); dht_periodic(buf, len, from, fromlen, &tosleep, DHT::Callback, NULL);
dht_periodic(data.data(), data.size(), &addr, sizeof(addr), &tosleep, DHT::Callback, NULL);
} }
void DHT::StoreNodes(bool force) void DHT::StoreNodes(bool force)
@ -213,29 +217,8 @@ namespace Components
} }
} }
void DHT::SocketFrame()
{
static char buffer[0x2001];
sockaddr_in addr;
int addrLen = sizeof(addr);
while (true)
{
int len = recvfrom(DHT::Socket, buffer, sizeof buffer, 0, reinterpret_cast<sockaddr*>(&addr), &addrLen);
if (len <= 0) break;
Network::Address address(&addr);
std::string data(buffer, len);
DHT::OnData(data, address);
}
}
void DHT::RunFrame() void DHT::RunFrame()
{ {
DHT::SocketFrame();
time_t tosleep = 0; time_t tosleep = 0;
dht_periodic(NULL, 0, NULL, 0, &tosleep, DHT::Callback, NULL); dht_periodic(NULL, 0, NULL, 0, &tosleep, DHT::Callback, NULL);
DHT::StoreNodes(false); DHT::StoreNodes(false);
@ -256,36 +239,6 @@ namespace Components
dht_ping_node(&addr, sizeof(addr)); dht_ping_node(&addr, sizeof(addr));
} }
void DHT::InitSocket()
{
WSAData data;
WSAStartup(MAKEWORD(2, 2), &data);
DHT::Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int broadcastOn = 1;
setsockopt(DHT::Socket, SOL_SOCKET, SO_BROADCAST, LPSTR(&broadcastOn), sizeof(broadcastOn));
unsigned long nonBlocking = 1;
ioctlsocket(DHT::Socket, FIONBIO, &nonBlocking);
sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
unsigned short port = DHT_PORT;
while (port < DHT_PORT + DHT_RANGE)
{
addr.sin_port = htons(port++);
if (bind(DHT::Socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != SOCKET_ERROR)
{
break;
}
}
}
DHT::DHT() DHT::DHT()
{ {
// #ifdef DEBUG // #ifdef DEBUG
@ -314,23 +267,22 @@ namespace Components
Utils::IO::WriteFile(file, idData); Utils::IO::WriteFile(file, idData);
} }
DHT::InitSocket(); dht_init(INT(*Game::ip_socket), -1, reinterpret_cast<unsigned char*>(idData.data()), reinterpret_cast<unsigned char*>("JC\0\0"));
dht_init(INT(DHT::Socket), -1, reinterpret_cast<unsigned char*>(idData.data()), reinterpret_cast<unsigned char*>("JC\0\0"));
DHT::LoadNodes(); DHT::LoadNodes();
Scheduler::OnFrame(DHT::RunFrame); Scheduler::OnFrame(DHT::RunFrame);
DHT::Bootstrap("router.bittorrent.com:6881"); DHT::Bootstrap("router.bittorrent.com:6881");
DHT::Bootstrap("router.utorrent.com:6881");
DHT::Bootstrap("dht.transmissionbt.com:6881"); DHT::Bootstrap("dht.transmissionbt.com:6881");
//DHT::Bootstrap("router.bitcomet.com:6881"); // Blacklisted
DHT::Bootstrap("dht.aelitis.com:6881"); DHT::Bootstrap("dht.aelitis.com:6881");
}); });
DHT::Insert("IW4x", [](std::vector<Network::Address> addresses) auto callback = [](std::vector<Network::Address> addresses)
{ {
std::lock_guard<std::mutex> _(DHT::Mutex); std::lock_guard<std::mutex> _(DHT::Mutex);
DHT::BootstrapDone();
for (auto& address : addresses) for (auto& address : addresses)
{ {
if (std::find(DHT::Nodes.begin(), DHT::Nodes.end(), address) == DHT::Nodes.end()) if (std::find(DHT::Nodes.begin(), DHT::Nodes.end(), address) == DHT::Nodes.end())
@ -348,7 +300,9 @@ namespace Components
Logger::Print("Received %s\n", address.getCString()); Logger::Print("Received %s\n", address.getCString());
} }
}); };
DHT::Insert("xPROTO_IW4x", callback);
Command::Add("addnode", [](Command::Params* params) Command::Add("addnode", [](Command::Params* params)
{ {
@ -359,14 +313,11 @@ namespace Components
}); });
} }
DHT::~DHT() void DHT::preDestroy()
{ {
DHT::Handlers.clear(); DHT::Handlers.clear();
DHT::StoreNodes(true); DHT::StoreNodes(true);
dht_uninit(); dht_uninit();
closesocket(DHT::Socket);
WSACleanup();
} }
} }

View File

@ -11,9 +11,9 @@ namespace Components
typedef unsigned char Id[20]; typedef unsigned char Id[20];
DHT(); DHT();
~DHT(); void preDestroy() override;
static void OnData(std::string data, Network::Address address); static void OnData(char* buf, int len, sockaddr* from, int fromlen);
static void Insert(std::string data, Utils::Slot<void(std::vector<Network::Address>)> callback); static void Insert(std::string data, Utils::Slot<void(std::vector<Network::Address>)> callback);
@ -28,8 +28,9 @@ namespace Components
static void Hash(std::string data, void* out, size_t size); static void Hash(std::string data, void* out, size_t size);
static void BootstrapDone();
private: private:
static SOCKET Socket;
static std::mutex Mutex; static std::mutex Mutex;
static std::vector<Network::Address> Nodes; static std::vector<Network::Address> Nodes;
static std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> Handlers; static std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> Handlers;
@ -39,9 +40,6 @@ namespace Components
static void StoreNodes(bool force); static void StoreNodes(bool force);
static void LoadNodes(); static void LoadNodes();
static void InitSocket();
static void SocketFrame();
static void Bootstrap(Network::Address node); static void Bootstrap(Network::Address node);
}; };
} }

View File

@ -287,6 +287,8 @@ namespace Components
} }
}); });
Localization::Set("PLATFORM_POPUP_CONNECTION", "Connecting to the DHT network");
// #ifndef DISABLE_ANTICHEAT // #ifndef DISABLE_ANTICHEAT
// if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && !Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests()) // if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && !Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests())
// { // {

View File

@ -336,6 +336,52 @@ namespace Components
} }
} }
int __stdcall Network::SendTo(SOCKET s, const char * buf, int len, int flags, const sockaddr* to, int tolen)
{
static char buffer[0x20000];
if (len <= 4 || *reinterpret_cast<const int*>(buf) != -1)
{
if (len + 4 > sizeof(buffer))
{
Logger::Error("Network layer 1: Packet exceeds buffer length!\n");
}
std::memmove(buffer + 4, buf, len);
*reinterpret_cast<int*>(buffer) = -2;
len += 4;
buf = buffer;
}
return sendto(s, buf, len, flags, to, tolen);
}
int __stdcall Network::RecvFrom(SOCKET s, char* buf, int len, int flags, sockaddr* from, int * fromlen)
{
int size = recvfrom(s, buf, len, flags, from, fromlen);
if (size >= 4)
{
int magic = *reinterpret_cast<int*>(buf);
if (magic == -2)
{
size -= 4;
std::memmove(buf, buf + 4, size);
}
else if (magic != -1)
{
if (len > size) buf[size] = 0;
DHT::OnData(buf, size, from, *fromlen);
size = -1;
WSASetLastError(WSAEWOULDBLOCK);
}
}
return size;
}
Network::Network() Network::Network()
{ {
AssertSize(Game::netadr_t, 20); AssertSize(Game::netadr_t, 20);
@ -372,6 +418,10 @@ namespace Components
// Install packet deploy hook // Install packet deploy hook
Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub); Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub);
// Intercept the games native network IO interaction
Utils::Hook::Set(0x6D740C, Network::SendTo);
Utils::Hook::Set(0x6D7464, Network::RecvFrom);
Network::Handle("resolveAddress", [](Address address, std::string data) Network::Handle("resolveAddress", [](Address address, std::string data)
{ {
Network::SendRaw(address, address.getString()); Network::SendRaw(address, address.getString());

View File

@ -89,6 +89,9 @@ namespace Components
static void NetworkStartStub(); static void NetworkStartStub();
static void PacketErrorCheck(); static void PacketErrorCheck();
static int __stdcall SendTo(SOCKET s, const char * buf, int len, int flags, const sockaddr* to, int tolen);
static int __stdcall RecvFrom(SOCKET s, char* buf, int len, int flags, sockaddr* from, int * fromlen);
}; };
} }

View File

@ -259,7 +259,7 @@ namespace Components
Utils::Hook::Nop(0x684080, 5); // Don't spam the console Utils::Hook::Nop(0x684080, 5); // Don't spam the console
// spawn upnp thread when UPNP_init returns // spawn upnp thread when UPNP_init returns
Utils::Hook::Hook(0x47982B, []() /*Utils::Hook::Hook(0x47982B, []()
{ {
std::thread([]() std::thread([]()
{ {
@ -271,10 +271,16 @@ namespace Components
std::this_thread::sleep_for(500ms); std::this_thread::sleep_for(500ms);
} }
}).detach(); }).detach();
}, HOOK_JUMP).install()->quick(); }, HOOK_JUMP).install()->quick();*/
// Completely disable UPNP, as it conflicts with our new network protocol
Utils::Hook::Set<BYTE>(0x4797F2, 0xC3);
// disable the IWNet IP detection (default 'got ipdetect' flag to 1) // disable the IWNet IP detection (default 'got ipdetect' flag to 1)
Utils::Hook::Set<BYTE>(0x649D6F0, 1); //Utils::Hook::Set<BYTE>(0x649D6F0, 1);
// Tell the game we're already trying to receive our IP
Utils::Hook(0x48C5C0, 0x4513B4, HOOK_JUMP).install()->quick();
// Fix stats sleeping // Fix stats sleeping
Utils::Hook::Set<BYTE>(0x6832BA, 0xEB); Utils::Hook::Set<BYTE>(0x6832BA, 0xEB);