Revert "[DHT] Implement DHT"
This commit is contained in:
parent
537cbe12f0
commit
b2c94a7be3
2
deps/dht
vendored
2
deps/dht
vendored
@ -1 +1 @@
|
||||
Subproject commit 1a489272ec72e5d571b63e3448063690d6d8656c
|
||||
Subproject commit 54ee76af630f56cb97646bc770b88bfb592fd328
|
@ -33,7 +33,6 @@ namespace Components
|
||||
Loader::Register(new Singleton());
|
||||
Loader::Register(new Exception()); // install our exception handler as early as posssible to get better debug dumps from startup crashes
|
||||
|
||||
Loader::Register(new DHT());
|
||||
Loader::Register(new Auth());
|
||||
Loader::Register(new Bans());
|
||||
Loader::Register(new Bots());
|
||||
@ -41,6 +40,7 @@ namespace Components
|
||||
Loader::Register(new Lean());
|
||||
Loader::Register(new Maps());
|
||||
Loader::Register(new News());
|
||||
Loader::Register(new Node());
|
||||
Loader::Register(new RCon());
|
||||
Loader::Register(new Stats());
|
||||
Loader::Register(new Menus());
|
||||
@ -63,6 +63,7 @@ namespace Components
|
||||
Loader::Register(new ModList());
|
||||
Loader::Register(new Monitor());
|
||||
Loader::Register(new Network());
|
||||
Loader::Register(new Session());
|
||||
Loader::Register(new Theatre());
|
||||
//Loader::Register(new ClanTags());
|
||||
Loader::Register(new Download());
|
||||
|
@ -86,13 +86,14 @@ namespace Components
|
||||
#include "Modules/Network.hpp"
|
||||
#include "Modules/Theatre.hpp"
|
||||
#include "Modules/QuickPatch.hpp"
|
||||
#include "Modules/DHT.hpp"
|
||||
#include "Modules/Node.hpp"
|
||||
#include "Modules/RCon.hpp"
|
||||
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
|
||||
#include "Modules/IW4MVM.hpp"
|
||||
#include "Modules/Logger.hpp"
|
||||
#include "Modules/Friends.hpp"
|
||||
#include "Modules/IPCPipe.hpp"
|
||||
#include "Modules/Session.hpp"
|
||||
#include "Modules/ClanTags.hpp"
|
||||
#include "Modules/Download.hpp"
|
||||
#include "Modules/Playlist.hpp"
|
||||
|
@ -1,372 +0,0 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
int dht_random_bytes(void *buf, size_t size)
|
||||
{
|
||||
Utils::Cryptography::Rand::GetRandomBytes(buf, size);
|
||||
return INT(size);
|
||||
}
|
||||
|
||||
void dht_hash(void *hash_return, int hash_size, const void *v1, int len1, const void *v2, int len2, const void *v3, int len3)
|
||||
{
|
||||
std::string data;
|
||||
data.append(LPSTR(v1), len1);
|
||||
data.append(LPSTR(v2), len2);
|
||||
data.append(LPSTR(v3), len3);
|
||||
|
||||
Components::DHT::Hash(data, hash_return, size_t(hash_size));
|
||||
}
|
||||
|
||||
int dht_blacklisted(const struct sockaddr* /*sa*/, int /*salen*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int dht_gettimeofday(struct timeval *tp, struct timezone* /*tzp*/)
|
||||
{
|
||||
static const unsigned __int64 epoch = 116444736000000000ULL;
|
||||
|
||||
SYSTEMTIME systemTime;
|
||||
GetSystemTime(&systemTime);
|
||||
|
||||
FILETIME fileTime;
|
||||
SystemTimeToFileTime(&systemTime, &fileTime);
|
||||
|
||||
ULARGE_INTEGER ularge;
|
||||
ularge.LowPart = fileTime.dwLowDateTime;
|
||||
ularge.HighPart = fileTime.dwHighDateTime;
|
||||
|
||||
tp->tv_sec = LONG((ularge.QuadPart - epoch) / 10000000L);
|
||||
tp->tv_usec = LONG(systemTime.wMilliseconds * 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace Components
|
||||
{
|
||||
SOCKET DHT::Socket;
|
||||
std::mutex DHT::Mutex;
|
||||
std::vector<Network::Address> DHT::Nodes;
|
||||
std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> DHT::Handlers;
|
||||
|
||||
void DHT::Insert(std::string data, Utils::Slot<void(std::vector<Network::Address>)> callback)
|
||||
{
|
||||
unsigned char hash[20];
|
||||
DHT::Hash(data, hash, sizeof(hash));
|
||||
DHT::InsertHash(hash, callback);
|
||||
}
|
||||
|
||||
void DHT::InsertHash(char* hash, Utils::Slot<void(std::vector<Network::Address>)> callback)
|
||||
{
|
||||
DHT::InsertHash(reinterpret_cast<unsigned char*>(hash), callback);
|
||||
}
|
||||
|
||||
void DHT::InsertHash(unsigned char* hash, Utils::Slot<void(std::vector<Network::Address>)> callback)
|
||||
{
|
||||
std::basic_string<uint8_t> hashStr(hash, 20);
|
||||
DHT::Handlers[hashStr] = callback;
|
||||
}
|
||||
|
||||
void DHT::Hash(std::string data, void* out, size_t size)
|
||||
{
|
||||
ZeroMemory(out, size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
std::string hashData = data;
|
||||
hashData.append(LPSTR(out), size);
|
||||
|
||||
std::string hash = Utils::Cryptography::SHA512::Compute(hashData);
|
||||
std::memmove(LPSTR(out) + i, hash.data(), std::min(size - i, hash.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void DHT::Callback(void* /*closure*/, int event, const unsigned char* info_hash, const void* data, size_t data_len)
|
||||
{
|
||||
if (event == DHT_EVENT_VALUES)
|
||||
{
|
||||
std::basic_string<uint8_t> hashStr(info_hash, 20);
|
||||
auto handler = DHT::Handlers.find(hashStr);
|
||||
if (handler != DHT::Handlers.end())
|
||||
{
|
||||
std::vector<Network::Address> addresses;
|
||||
|
||||
const unsigned char* bytes = reinterpret_cast<const unsigned char*>(data);
|
||||
while ((data_len - (LPSTR(bytes) - LPSTR(data))) >= 6)
|
||||
{
|
||||
unsigned char ip[4];
|
||||
ip[0] = *bytes++;
|
||||
ip[1] = *bytes++;
|
||||
ip[2] = *bytes++;
|
||||
ip[3] = *bytes++;
|
||||
|
||||
unsigned short port = ntohs(*reinterpret_cast<const unsigned short*>(bytes));
|
||||
bytes += 2;
|
||||
|
||||
Network::Address address(Utils::String::VA("%u.%u.%u.%u:%hu", ip[0], ip[1], ip[2], ip[3], port));
|
||||
addresses.push_back(address);
|
||||
}
|
||||
|
||||
handler->second(addresses);
|
||||
}
|
||||
}
|
||||
else if (event == DHT_EVENT_SEARCH_DONE)
|
||||
{
|
||||
Logger::Print("Search done!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void DHT::OnData(std::string data, Network::Address address)
|
||||
{
|
||||
time_t tosleep = 0;
|
||||
sockaddr addr = address.getSockAddr();
|
||||
dht_periodic(data.data(), data.size(), &addr, sizeof(addr), &tosleep, DHT::Callback, NULL);
|
||||
}
|
||||
|
||||
void DHT::StoreNodes(bool force)
|
||||
{
|
||||
static Utils::Time::Interval interval;
|
||||
if (!force && !interval.elapsed(1min)) return;
|
||||
interval.update();
|
||||
|
||||
Utils::Memory::Allocator allocator;
|
||||
int nodes = dht_nodes(AF_INET, nullptr, nullptr, nullptr, nullptr) + 10;
|
||||
|
||||
sockaddr_in* addresses = allocator.allocateArray<sockaddr_in>(nodes);
|
||||
DHT::Id* ids = allocator.allocateArray<DHT::Id>(nodes);
|
||||
|
||||
int num6 = 0;
|
||||
dht_get_nodes(addresses, reinterpret_cast<unsigned char*>(ids), &nodes, nullptr, nullptr, &num6);
|
||||
|
||||
// We could be offline, don't overwrite anything
|
||||
if (nodes <= 0) return;
|
||||
|
||||
Proto::Network::Nodes protoNodes;
|
||||
|
||||
for (int i = 0; i < nodes; ++i)
|
||||
{
|
||||
Proto::Network::Node* node = protoNodes.add_nodes();
|
||||
|
||||
node->mutable_id()->clear();
|
||||
node->mutable_id()->append(LPSTR(ids + i), sizeof(*ids));
|
||||
|
||||
node->mutable_address()->clear();
|
||||
node->mutable_address()->append(LPSTR(addresses + i), sizeof(*addresses));
|
||||
}
|
||||
|
||||
Utils::IO::WriteFile(Utils::String::VA("players/dht/%hu.nodes", Network::GetPort()), protoNodes.SerializeAsString());
|
||||
}
|
||||
|
||||
void DHT::LoadNodes()
|
||||
{
|
||||
std::string file = Utils::String::VA("players/dht/%hu.nodes", Network::GetPort());
|
||||
if (!Utils::IO::FileExists(file)) return;
|
||||
|
||||
auto data = Utils::IO::ReadFile(file);
|
||||
|
||||
Proto::Network::Nodes nodes;
|
||||
if (!nodes.ParseFromString(data)) return;
|
||||
|
||||
for (auto& node : nodes.nodes())
|
||||
{
|
||||
const std::string& id = node.id();
|
||||
const std::string& address = node.address();
|
||||
|
||||
if (id.size() == 20)
|
||||
{
|
||||
dht_insert_node(reinterpret_cast<const unsigned char*>(id.data()), reinterpret_cast<sockaddr*>(LPSTR(address.data())), INT(address.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DHT::Add(Network::Address addr)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(DHT::Mutex);
|
||||
|
||||
if (std::find(DHT::Nodes.begin(), DHT::Nodes.end(), addr) == DHT::Nodes.end())
|
||||
{
|
||||
DHT::Nodes.push_back(addr);
|
||||
}
|
||||
|
||||
if (ServerList::IsOnlineList())
|
||||
{
|
||||
ServerList::InsertRequest(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void DHT::Search()
|
||||
{
|
||||
// Serverlist specific
|
||||
{
|
||||
std::lock_guard<std::mutex> _(DHT::Mutex);
|
||||
for (auto& address : DHT::Nodes)
|
||||
{
|
||||
if (ServerList::IsOnlineList())
|
||||
{
|
||||
ServerList::InsertRequest(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& hash : DHT::Handlers)
|
||||
{
|
||||
dht_search(hash.first.data(), (Dedicated::IsEnabled() ? (Dvar::Var("sv_lanOnly").get<bool>() ? 0 : Network::GetPort()) : 0), AF_INET, DHT::Callback, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
DHT::SocketFrame();
|
||||
|
||||
time_t tosleep = 0;
|
||||
dht_periodic(NULL, 0, NULL, 0, &tosleep, DHT::Callback, NULL);
|
||||
DHT::StoreNodes(false);
|
||||
|
||||
static std::optional<Utils::Time::Interval> interval;
|
||||
if (!interval.has_value() || interval->elapsed(2min))
|
||||
{
|
||||
if (!interval.has_value()) interval.emplace();
|
||||
interval->update();
|
||||
|
||||
DHT::Search();
|
||||
}
|
||||
}
|
||||
|
||||
void DHT::Bootstrap(Network::Address node)
|
||||
{
|
||||
sockaddr addr = node.getSockAddr();
|
||||
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()
|
||||
{
|
||||
// #ifdef DEBUG
|
||||
// AllocConsole();
|
||||
// AttachConsole(GetCurrentProcessId());
|
||||
//
|
||||
// freopen("conin$", "r", stdin);
|
||||
// freopen("conout$", "w", stdout);
|
||||
// freopen("conout$", "w", stderr);
|
||||
// dht_debug = stdout;
|
||||
// #endif
|
||||
|
||||
Network::OnStart([]()
|
||||
{
|
||||
std::string idData;
|
||||
|
||||
std::string file = Utils::String::VA("players/dht/%hu.id", Network::GetPort());
|
||||
if (Utils::IO::FileExists(file))
|
||||
{
|
||||
idData = Utils::IO::ReadFile(file);
|
||||
}
|
||||
|
||||
if (idData.size() != 20)
|
||||
{
|
||||
idData = Utils::Cryptography::Rand::GetRandomBytes(20);
|
||||
Utils::IO::WriteFile(file, idData);
|
||||
}
|
||||
|
||||
DHT::InitSocket();
|
||||
dht_init(INT(DHT::Socket), -1, reinterpret_cast<unsigned char*>(idData.data()), reinterpret_cast<unsigned char*>("JC\0\0"));
|
||||
|
||||
DHT::LoadNodes();
|
||||
Scheduler::OnFrame(DHT::RunFrame);
|
||||
|
||||
DHT::Bootstrap("router.bittorrent.com:6881");
|
||||
DHT::Bootstrap("router.utorrent.com:6881");
|
||||
DHT::Bootstrap("dht.transmissionbt.com:6881");
|
||||
//DHT::Bootstrap("router.bitcomet.com:6881"); // Blacklisted
|
||||
DHT::Bootstrap("dht.aelitis.com:6881");
|
||||
});
|
||||
|
||||
DHT::Insert("IW4x", [](std::vector<Network::Address> addresses)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(DHT::Mutex);
|
||||
|
||||
for (auto& address : addresses)
|
||||
{
|
||||
if (std::find(DHT::Nodes.begin(), DHT::Nodes.end(), address) == DHT::Nodes.end())
|
||||
{
|
||||
DHT::Nodes.push_back(address);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& address : addresses)
|
||||
{
|
||||
if (ServerList::IsOnlineList())
|
||||
{
|
||||
ServerList::InsertRequest(address);
|
||||
}
|
||||
|
||||
Logger::Print("Received %s\n", address.getCString());
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("addnode", [](Command::Params* params)
|
||||
{
|
||||
if (params->length() >= 2)
|
||||
{
|
||||
DHT::Add(params->get(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DHT::~DHT()
|
||||
{
|
||||
DHT::Handlers.clear();
|
||||
|
||||
DHT::StoreNodes(true);
|
||||
dht_uninit();
|
||||
|
||||
closesocket(DHT::Socket);
|
||||
WSACleanup();
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define DHT_PORT 9000
|
||||
#define DHT_RANGE 100
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class DHT : public Component
|
||||
{
|
||||
public:
|
||||
typedef unsigned char Id[20];
|
||||
|
||||
DHT();
|
||||
~DHT();
|
||||
|
||||
static void OnData(std::string data, Network::Address address);
|
||||
|
||||
static void Insert(std::string data, Utils::Slot<void(std::vector<Network::Address>)> callback);
|
||||
|
||||
static void InsertHash(char* hash, Utils::Slot<void(std::vector<Network::Address>)> callback);
|
||||
static void InsertHash(unsigned char* hash, Utils::Slot<void(std::vector<Network::Address>)> callback);
|
||||
|
||||
static void RunFrame();
|
||||
|
||||
static void Search();
|
||||
|
||||
static void Add(Network::Address addr);
|
||||
|
||||
static void Hash(std::string data, void* out, size_t size);
|
||||
|
||||
private:
|
||||
static SOCKET Socket;
|
||||
static std::mutex Mutex;
|
||||
static std::vector<Network::Address> Nodes;
|
||||
static std::map<std::basic_string<uint8_t>, Utils::Slot<void(std::vector<Network::Address>)>> Handlers;
|
||||
|
||||
static void Callback(void *closure, int event, const unsigned char *info_hash, const void *data, size_t data_len);
|
||||
|
||||
static void StoreNodes(bool force);
|
||||
static void LoadNodes();
|
||||
|
||||
static void InitSocket();
|
||||
static void SocketFrame();
|
||||
|
||||
static void Bootstrap(Network::Address node);
|
||||
};
|
||||
}
|
@ -7,26 +7,6 @@ namespace Components
|
||||
std::thread Discovery::Thread;
|
||||
std::string Discovery::Challenge;
|
||||
|
||||
std::mutex Discovery::Mutex;
|
||||
std::vector<Network::Address> Discovery::LocalServers;
|
||||
|
||||
std::vector<Network::Address> Discovery::GetLocalServers()
|
||||
{
|
||||
std::lock_guard<std::mutex> _(Discovery::Mutex);
|
||||
return Discovery::LocalServers;
|
||||
}
|
||||
|
||||
void Discovery::InsertServer(Network::Address server)
|
||||
{
|
||||
if (!server.isLocal()) return;
|
||||
std::lock_guard<std::mutex> _(Discovery::Mutex);
|
||||
|
||||
if (std::find(Discovery::LocalServers.begin(), Discovery::LocalServers.end(), server) == Discovery::LocalServers.end())
|
||||
{
|
||||
Discovery::LocalServers.push_back(server);
|
||||
}
|
||||
}
|
||||
|
||||
void Discovery::Perform()
|
||||
{
|
||||
Discovery::IsPerforming = true;
|
||||
@ -47,9 +27,6 @@ namespace Components
|
||||
{
|
||||
if (Discovery::IsPerforming)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(Discovery::Mutex);
|
||||
Discovery::LocalServers.clear();
|
||||
|
||||
int start = Game::Sys_Milliseconds();
|
||||
|
||||
Logger::Print("Starting local server discovery...\n");
|
||||
@ -69,23 +46,6 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
if (Dedicated::IsEnabled())
|
||||
{
|
||||
Network::OnStart([]()
|
||||
{
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
static std::optional<Utils::Time::Interval> interval;
|
||||
if (!interval.has_value() || interval->elapsed(10min))
|
||||
{
|
||||
if (!interval.has_value()) interval.emplace(Utils::Time::Interval());
|
||||
interval->update();
|
||||
Discovery::Perform();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Network::Handle("discovery", [](Network::Address address, std::string data)
|
||||
{
|
||||
if (address.isSelf()) return;
|
||||
@ -98,8 +58,6 @@ namespace Components
|
||||
|
||||
Logger::Print("Received discovery request from %s\n", address.getCString());
|
||||
Network::SendCommand(address, "discoveryResponse", data);
|
||||
|
||||
Discovery::InsertServer(address);
|
||||
});
|
||||
|
||||
Network::Handle("discoveryResponse", [](Network::Address address, std::string data)
|
||||
@ -119,7 +77,6 @@ namespace Components
|
||||
}
|
||||
|
||||
Logger::Print("Received discovery response from: %s\n", address.getCString());
|
||||
Discovery::InsertServer(address);
|
||||
|
||||
if (ServerList::IsOfflineList())
|
||||
{
|
||||
|
@ -12,17 +12,10 @@ namespace Components
|
||||
|
||||
static void Perform();
|
||||
|
||||
static std::vector<Network::Address> GetLocalServers();
|
||||
|
||||
private:
|
||||
static bool IsTerminating;
|
||||
static bool IsPerforming;
|
||||
static std::thread Thread;
|
||||
static std::string Challenge;
|
||||
|
||||
static std::mutex Mutex;
|
||||
static std::vector<Network::Address> LocalServers;
|
||||
|
||||
static void InsertServer(Network::Address server);
|
||||
};
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ namespace Components
|
||||
if (entry->server.getType() == Game::NA_LOOPBACK || (entry->server.getType() == Game::NA_IP && entry->server.getIP().full == 0x0100007F)) entry->server.setType(Game::NA_BAD);
|
||||
else if (entry->server.getType() != Game::NA_BAD && entry->server != oldAddress)
|
||||
{
|
||||
DHT::Add(entry->server);
|
||||
Node::Add(entry->server);
|
||||
Network::SendCommand(entry->server, "getinfo", Utils::Cryptography::Rand::GenerateChallenge());
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace Components
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x49F0B0)(); // Com_ClientPacketEvent
|
||||
//Session::RunFrame();
|
||||
DHT::RunFrame();
|
||||
Node::RunFrame();
|
||||
ServerList::Frame();
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
@ -18,7 +18,6 @@ namespace Components
|
||||
Address(sockaddr_in* addr) : Address(reinterpret_cast<sockaddr*>(addr)) {}
|
||||
Address(Game::netadr_t addr) : address(addr) {}
|
||||
Address(Game::netadr_t* addr) : Address(*addr) {}
|
||||
Address(const char* addr) : Address(std::string(addr)) {}
|
||||
Address(const Address& obj) : address(obj.address) {};
|
||||
bool operator!=(const Address &obj) const { return !(*this == obj); };
|
||||
bool operator==(const Address &obj) const;
|
||||
|
355
src/Components/Modules/Node.cpp
Normal file
355
src/Components/Modules/Node.cpp
Normal file
@ -0,0 +1,355 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::recursive_mutex Node::Mutex;
|
||||
std::vector<Node::Entry> Node::Nodes;
|
||||
|
||||
bool Node::wasIngame;
|
||||
|
||||
bool Node::Entry::isValid()
|
||||
{
|
||||
return (this->lastResponse.has_value() && !this->lastResponse->elapsed(NODE_HALFLIFE * 2));
|
||||
}
|
||||
|
||||
bool Node::Entry::isDead()
|
||||
{
|
||||
if (!this->lastResponse.has_value())
|
||||
{
|
||||
if (this->lastRequest.has_value() && this->lastRequest->elapsed(NODE_HALFLIFE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(this->lastResponse->elapsed(NODE_HALFLIFE * 2) && this->lastRequest.has_value() && this->lastRequest->after(*this->lastResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::Entry::requiresRequest()
|
||||
{
|
||||
return (!this->isDead() && (!this->lastRequest.has_value() || this->lastRequest->elapsed(NODE_HALFLIFE)));
|
||||
}
|
||||
|
||||
void Node::Entry::sendRequest()
|
||||
{
|
||||
if (!this->lastRequest.has_value()) this->lastRequest.emplace(Utils::Time::Point());
|
||||
this->lastRequest->update();
|
||||
|
||||
Session::Send(this->address, "nodeListRequest");
|
||||
Node::SendList(this->address);
|
||||
NODE_LOG("Sent request to %s\n", this->address.getCString());
|
||||
}
|
||||
|
||||
void Node::Entry::reset()
|
||||
{
|
||||
// this->lastResponse.reset(); // This would invalidate the node, but maybe we don't want that?
|
||||
this->lastRequest.reset();
|
||||
}
|
||||
|
||||
void Node::LoadNodeRemotePreset()
|
||||
{
|
||||
std::string nodes = Utils::Cache::GetFile("/iw4/nodes.txt");
|
||||
if (nodes.empty()) return;
|
||||
|
||||
auto nodeList = Utils::String::Explode(nodes, '\n');
|
||||
for (auto& node : nodeList)
|
||||
{
|
||||
Utils::String::Replace(node, "\r", "");
|
||||
node = Utils::String::Trim(node);
|
||||
Node::Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::LoadNodePreset()
|
||||
{
|
||||
Proto::Node::List list;
|
||||
|
||||
if (Monitor::IsEnabled())
|
||||
{
|
||||
std::string nodes = Utils::IO::ReadFile("players/nodes_default.dat");
|
||||
if (nodes.empty() || !list.ParseFromString(Utils::Compression::ZLib::Decompress(nodes))) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSystem::File defaultNodes("nodes_default.dat");
|
||||
if (!defaultNodes.exists() || !list.ParseFromString(Utils::Compression::ZLib::Decompress(defaultNodes.getBuffer()))) return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.nodes_size(); ++i)
|
||||
{
|
||||
const std::string& addr = list.nodes(i);
|
||||
|
||||
if (addr.size() == sizeof(sockaddr))
|
||||
{
|
||||
Node::Add(reinterpret_cast<sockaddr*>(const_cast<char*>(addr.data())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::LoadNodes()
|
||||
{
|
||||
Proto::Node::List list;
|
||||
std::string nodes = Utils::IO::ReadFile("players/nodes.dat");
|
||||
if (nodes.empty() || !list.ParseFromString(Utils::Compression::ZLib::Decompress(nodes))) return;
|
||||
|
||||
for (int i = 0; i < list.nodes_size(); ++i)
|
||||
{
|
||||
const std::string& addr = list.nodes(i);
|
||||
|
||||
if (addr.size() == sizeof(sockaddr))
|
||||
{
|
||||
Node::Add(reinterpret_cast<sockaddr*>(const_cast<char*>(addr.data())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::StoreNodes(bool force)
|
||||
{
|
||||
if (Dedicated::IsEnabled() && Dvar::Var("sv_lanOnly").get<bool>()) return;
|
||||
|
||||
static Utils::Time::Interval interval;
|
||||
if (!force && !interval.elapsed(1min)) return;
|
||||
interval.update();
|
||||
|
||||
Proto::Node::List list;
|
||||
|
||||
Node::Mutex.lock();
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
if (node.isValid())
|
||||
{
|
||||
std::string* str = list.add_nodes();
|
||||
|
||||
sockaddr addr = node.address.getSockAddr();
|
||||
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
||||
}
|
||||
}
|
||||
Node::Mutex.unlock();
|
||||
|
||||
Utils::IO::WriteFile("players/nodes.dat", Utils::Compression::ZLib::Compress(list.SerializeAsString()));
|
||||
}
|
||||
|
||||
void Node::Add(Network::Address address)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
if (address.isLocal() || address.isSelf()) return;
|
||||
#endif
|
||||
|
||||
if (!address.isValid()) return;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
for (auto& session : Node::Nodes)
|
||||
{
|
||||
if (session.address == address) return;
|
||||
}
|
||||
|
||||
Node::Entry node;
|
||||
node.address = address;
|
||||
|
||||
Node::Nodes.push_back(node);
|
||||
}
|
||||
|
||||
void Node::RunFrame()
|
||||
{
|
||||
if (Dedicated::IsEnabled() && Dvar::Var("sv_lanOnly").get<bool>()) return;
|
||||
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
wasIngame = true;
|
||||
return; // don't run while ingame because it can still cause lag spikes on lower end PCs
|
||||
}
|
||||
|
||||
if (wasIngame) // our last frame we were ingame and now we aren't so touch all nodes
|
||||
{
|
||||
for (auto i = Node::Nodes.begin(); i != Node::Nodes.end();++i)
|
||||
{
|
||||
// clearing the last request and response times makes the
|
||||
// dispatcher think its a new node and will force a refresh
|
||||
i->lastRequest.reset();
|
||||
i->lastResponse.reset();
|
||||
}
|
||||
wasIngame = false;
|
||||
}
|
||||
|
||||
static Utils::Time::Interval frameLimit;
|
||||
int interval = static_cast<int>(1000.0f / Dvar::Var("net_serverFrames").get<int>());
|
||||
if (!frameLimit.elapsed(std::chrono::milliseconds(interval))) return;
|
||||
frameLimit.update();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
Dvar::Var queryLimit("net_serverQueryLimit");
|
||||
|
||||
int sentRequests = 0;
|
||||
for (auto i = Node::Nodes.begin(); i != Node::Nodes.end();)
|
||||
{
|
||||
if (i->isDead())
|
||||
{
|
||||
i = Node::Nodes.erase(i);
|
||||
continue;
|
||||
}
|
||||
else if (sentRequests < queryLimit.get<int>() && i->requiresRequest())
|
||||
{
|
||||
++sentRequests;
|
||||
i->sendRequest();
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::Synchronize()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
//if (node.isValid()) // Comment out to simulate 'syncnodes' behaviour
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::HandleResponse(Network::Address address, std::string data)
|
||||
{
|
||||
Proto::Node::List list;
|
||||
if (!list.ParseFromString(data)) return;
|
||||
|
||||
NODE_LOG("Received response from %s\n", address.getCString());
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
|
||||
for (int i = 0; i < list.nodes_size(); ++i)
|
||||
{
|
||||
const std::string& addr = list.nodes(i);
|
||||
|
||||
if (addr.size() == sizeof(sockaddr))
|
||||
{
|
||||
Node::Add(reinterpret_cast<sockaddr*>(const_cast<char*>(addr.data())));
|
||||
}
|
||||
}
|
||||
|
||||
if (list.isnode() && (!list.port() || list.port() == address.getPort()))
|
||||
{
|
||||
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && list.protocol() == PROTOCOL)
|
||||
{
|
||||
NODE_LOG("Inserting %s into the serverlist\n", address.getCString());
|
||||
ServerList::InsertRequest(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
NODE_LOG("Dropping serverlist insertion for %s\n", address.getCString());
|
||||
}
|
||||
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
if (address == node.address)
|
||||
{
|
||||
if (!node.lastResponse.has_value()) node.lastResponse.emplace(Utils::Time::Point());
|
||||
node.lastResponse->update();
|
||||
|
||||
node.data.protocol = list.protocol();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Node::Entry entry;
|
||||
entry.address = address;
|
||||
entry.data.protocol = list.protocol();
|
||||
entry.lastResponse.emplace(Utils::Time::Point());
|
||||
|
||||
Node::Nodes.push_back(entry);
|
||||
}
|
||||
}
|
||||
void Node::SendList(Network::Address address)
|
||||
{
|
||||
Proto::Node::List list;
|
||||
list.set_isnode(Dedicated::IsEnabled());
|
||||
list.set_protocol(PROTOCOL);
|
||||
list.set_port(Node::GetPort());
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
if (node.isValid())
|
||||
{
|
||||
std::string* str = list.add_nodes();
|
||||
|
||||
sockaddr addr = node.address.getSockAddr();
|
||||
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
||||
}
|
||||
}
|
||||
|
||||
Session::Send(address, "nodeListResponse", list.SerializeAsString());
|
||||
}
|
||||
|
||||
unsigned short Node::GetPort()
|
||||
{
|
||||
if (Dvar::Var("net_natFix").get<bool>()) return 0;
|
||||
return Network::GetPort();
|
||||
}
|
||||
|
||||
Node::Node()
|
||||
{
|
||||
if (ZoneBuilder::IsEnabled()) return;
|
||||
Dvar::Register<bool>("net_natFix", false, 0, "Fix node registration for certain firewalls/routers");
|
||||
|
||||
Scheduler::OnFrameAsync([]()
|
||||
{
|
||||
Node::StoreNodes(false);
|
||||
});
|
||||
|
||||
Scheduler::OnFrame(Node::RunFrame);
|
||||
Session::Handle("nodeListResponse", Node::HandleResponse);
|
||||
Session::Handle("nodeListRequest", [](Network::Address address, std::string)
|
||||
{
|
||||
Node::SendList(address);
|
||||
});
|
||||
|
||||
// Load stored nodes
|
||||
auto loadNodes = []()
|
||||
{
|
||||
Node::LoadNodePreset();
|
||||
Node::LoadNodes();
|
||||
};
|
||||
|
||||
if (Monitor::IsEnabled()) Network::OnStart(loadNodes);
|
||||
else Dvar::OnInit(loadNodes);
|
||||
|
||||
Network::OnStart([]()
|
||||
{
|
||||
std::thread([]()
|
||||
{
|
||||
Node::LoadNodeRemotePreset();
|
||||
}).detach();
|
||||
});
|
||||
|
||||
Command::Add("listnodes", [](Command::Params*)
|
||||
{
|
||||
Logger::Print("Nodes: %d\n", Node::Nodes.size());
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
Logger::Print("%s\t(%s)\n", node.address.getCString(), node.isValid() ? "Valid" : "Invalid");
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("addnode", [](Command::Params* params)
|
||||
{
|
||||
if (params->length() < 2) return;
|
||||
Node::Add({ params->get(1) });
|
||||
});
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
Node::StoreNodes(true);
|
||||
Node::Nodes.clear();
|
||||
}
|
||||
}
|
64
src/Components/Modules/Node.hpp
Normal file
64
src/Components/Modules/Node.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#define NODE_HALFLIFE (3 * 60 * 1000) //3min
|
||||
|
||||
#ifdef NODE_LOG_MESSAGES
|
||||
#define NODE_LOG(x, ...) Logger::Print(x, __VA_ARGS__)
|
||||
#else
|
||||
#define NODE_LOG(x, ...)
|
||||
#endif
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Node : public Component
|
||||
{
|
||||
public:
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
uint64_t protocol;
|
||||
};
|
||||
|
||||
class Entry
|
||||
{
|
||||
public:
|
||||
Network::Address address;
|
||||
Data data;
|
||||
|
||||
std::optional<Utils::Time::Point> lastRequest;
|
||||
std::optional<Utils::Time::Point> lastResponse;
|
||||
|
||||
bool isValid();
|
||||
bool isDead();
|
||||
|
||||
bool requiresRequest();
|
||||
void sendRequest();
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
Node();
|
||||
~Node();
|
||||
|
||||
static void Add(Network::Address address);
|
||||
static void RunFrame();
|
||||
static void Synchronize();
|
||||
|
||||
static void LoadNodeRemotePreset();
|
||||
|
||||
private:
|
||||
static std::recursive_mutex Mutex;
|
||||
static std::vector<Entry> Nodes;
|
||||
static bool wasIngame;
|
||||
|
||||
static void HandleResponse(Network::Address address, std::string data);
|
||||
|
||||
static void SendList(Network::Address address);
|
||||
|
||||
static void LoadNodePreset();
|
||||
static void LoadNodes();
|
||||
static void StoreNodes(bool force);
|
||||
|
||||
static unsigned short GetPort();
|
||||
};
|
||||
}
|
@ -24,7 +24,7 @@ namespace Components
|
||||
|
||||
void Party::Connect(Network::Address target)
|
||||
{
|
||||
DHT::Add(target);
|
||||
Node::Add(target);
|
||||
|
||||
Party::Container.valid = true;
|
||||
Party::Container.awaitingPlaylist = false;
|
||||
@ -346,27 +346,6 @@ namespace Components
|
||||
info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get<int>()));
|
||||
info.set("sv_running", (Dvar::Var("sv_running").get<bool>() ? "1" : "0"));
|
||||
|
||||
if (Dedicated::IsEnabled())
|
||||
{
|
||||
std::vector<Network::Address> servers = Discovery::GetLocalServers();
|
||||
std::vector<unsigned short> ports;
|
||||
|
||||
std::string portString;
|
||||
for (auto& server : servers)
|
||||
{
|
||||
unsigned short port = server.getPort();
|
||||
if (std::find(ports.begin(), ports.end(), port) == ports.end())
|
||||
{
|
||||
ports.push_back(port);
|
||||
|
||||
if (!portString.empty()) portString.push_back(',');
|
||||
portString.append(Utils::String::VA("%hu", port & 0xFFFF));
|
||||
}
|
||||
}
|
||||
|
||||
info.set("siblings", portString);
|
||||
}
|
||||
|
||||
// Ensure mapname is set
|
||||
if (info.get("mapname").empty() || Party::IsInLobby())
|
||||
{
|
||||
@ -530,23 +509,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
std::string siblings = info.get("siblings");
|
||||
if (!siblings.empty() && !address.isLocal())
|
||||
{
|
||||
Network::Address sibling(address);
|
||||
std::vector<std::string> ports = Utils::String::Explode(siblings, ',');
|
||||
|
||||
for (auto& port : ports)
|
||||
{
|
||||
sibling.setPort(SHORT(atoi(port.data())));
|
||||
|
||||
if (ServerList::IsOnlineList())
|
||||
{
|
||||
ServerList::InsertRequestIfNotInList(sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerList::Insert(address, info);
|
||||
Friends::UpdateServer(address, info.get("hostname"), info.get("mapname"));
|
||||
});
|
||||
|
@ -283,7 +283,7 @@ namespace Components
|
||||
Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL));
|
||||
//Network::SendCommand(ServerList::RefreshContainer.Host, "getservers", "0 full empty");
|
||||
#else
|
||||
DHT::Search();
|
||||
Node::Synchronize();
|
||||
#endif
|
||||
}
|
||||
else if (ServerList::IsFavouriteList())
|
||||
@ -394,22 +394,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void ServerList::InsertRequestIfNotInList(Network::Address address)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
|
||||
|
||||
auto list = ServerList::GetList();
|
||||
if (list)
|
||||
{
|
||||
for (auto& server : *list)
|
||||
{
|
||||
if (server.addr == address) return;
|
||||
}
|
||||
}
|
||||
|
||||
ServerList::InsertRequest(address);
|
||||
}
|
||||
|
||||
void ServerList::InsertRequest(Network::Address address)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
|
||||
|
@ -37,7 +37,6 @@ namespace Components
|
||||
static void RefreshVisibleList(UIScript::Token);
|
||||
static void UpdateVisibleList(UIScript::Token);
|
||||
static void InsertRequest(Network::Address address);
|
||||
static void InsertRequestIfNotInList(Network::Address address);
|
||||
static void Insert(Network::Address address, Utils::InfoString info);
|
||||
|
||||
static ServerInfo* GetCurrentServer();
|
||||
|
282
src/Components/Modules/Session.cpp
Normal file
282
src/Components/Modules/Session.cpp
Normal file
@ -0,0 +1,282 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
bool Session::Terminate;
|
||||
std::thread Session::Thread;
|
||||
|
||||
std::recursive_mutex Session::Mutex;
|
||||
std::unordered_map<Network::Address, Session::Frame> Session::Sessions;
|
||||
std::unordered_map<Network::Address, std::queue<std::shared_ptr<Session::Packet>>> Session::PacketQueue;
|
||||
|
||||
Utils::Cryptography::ECC::Key Session::SignatureKey;
|
||||
|
||||
std::map<std::string, Utils::Slot<Network::Callback>> Session::PacketHandlers;
|
||||
|
||||
std::queue<std::pair<Network::Address, std::string>> Session::SignatureQueue;
|
||||
|
||||
void Session::Send(Network::Address target, std::string command, std::string data)
|
||||
{
|
||||
#ifdef DISABLE_SESSION
|
||||
class DelayedResend
|
||||
{
|
||||
public:
|
||||
Network::Address target;
|
||||
std::string command;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
DelayedResend* delayData = new DelayedResend;
|
||||
delayData->target = target;
|
||||
delayData->command = command;
|
||||
delayData->data = data;
|
||||
|
||||
Network::SendCommand(target, command, data);
|
||||
|
||||
Scheduler::OnDelay([delayData]()
|
||||
{
|
||||
Network::SendCommand(delayData->target, delayData->command, delayData->data);
|
||||
delete delayData;
|
||||
}, 500ms + std::chrono::milliseconds(rand() % 200));
|
||||
#else
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
|
||||
auto queue = Session::PacketQueue.find(target);
|
||||
if (queue == Session::PacketQueue.end())
|
||||
{
|
||||
Session::PacketQueue[target] = std::queue<std::shared_ptr<Session::Packet>>();
|
||||
queue = Session::PacketQueue.find(target);
|
||||
if (queue == Session::PacketQueue.end()) Logger::Error("Failed to enqueue session packet!\n");
|
||||
}
|
||||
|
||||
std::shared_ptr<Session::Packet> packet = std::make_shared<Session::Packet>();
|
||||
packet->command = command;
|
||||
packet->data = data;
|
||||
packet->tries = 0;
|
||||
|
||||
queue->second.push(packet);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::Handle(std::string packet, Utils::Slot<Network::Callback> callback)
|
||||
{
|
||||
#ifdef DISABLE_SESSION
|
||||
Network::Handle(packet, callback);
|
||||
#else
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
Session::PacketHandlers[packet] = callback;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::RunFrame()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
|
||||
for (auto queue = Session::PacketQueue.begin(); queue != Session::PacketQueue.end();)
|
||||
{
|
||||
if (queue->second.empty())
|
||||
{
|
||||
queue = Session::PacketQueue.erase(queue);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<Session::Packet> packet = queue->second.front();
|
||||
if (!packet->lastTry.has_value() || !packet->tries || (packet->lastTry.has_value() && packet->lastTry->elapsed(SESSION_TIMEOUT)))
|
||||
{
|
||||
if (packet->tries <= SESSION_MAX_RETRIES)
|
||||
{
|
||||
packet->tries++;
|
||||
if(!packet->lastTry.has_value()) packet->lastTry.emplace(Utils::Time::Point());
|
||||
packet->lastTry->update();
|
||||
|
||||
Network::SendCommand(queue->first, "sessionSyn");
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->second.pop();
|
||||
}
|
||||
}
|
||||
|
||||
++queue;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::HandleSignatures()
|
||||
{
|
||||
while (!Session::SignatureQueue.empty())
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(Session::Mutex);
|
||||
auto signature = Session::SignatureQueue.front();
|
||||
Session::SignatureQueue.pop();
|
||||
|
||||
auto queue = Session::PacketQueue.find(signature.first);
|
||||
if (queue == Session::PacketQueue.end()) continue;
|
||||
|
||||
if (!queue->second.empty())
|
||||
{
|
||||
std::shared_ptr<Session::Packet> packet = queue->second.front();
|
||||
queue->second.pop();
|
||||
lock.unlock();
|
||||
|
||||
Proto::Session::Packet dataPacket;
|
||||
dataPacket.set_publickey(Session::SignatureKey.getPublicKey());
|
||||
dataPacket.set_signature(Utils::Cryptography::ECC::SignMessage(Session::SignatureKey, signature.second));
|
||||
dataPacket.set_command(packet->command);
|
||||
dataPacket.set_data(packet->data);
|
||||
|
||||
Network::SendCommand(signature.first, "sessionFin", dataPacket.SerializeAsString());
|
||||
std::this_thread::sleep_for(3ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Session::Session()
|
||||
{
|
||||
#ifndef DISABLE_SESSION
|
||||
Session::SignatureKey = Utils::Cryptography::ECC::GenerateKey(512);
|
||||
//Scheduler::OnFrame(Session::RunFrame);
|
||||
|
||||
if (!Loader::IsPerformingUnitTests())
|
||||
{
|
||||
Session::Terminate = false;
|
||||
Session::Thread = std::thread([]()
|
||||
{
|
||||
while (!Session::Terminate)
|
||||
{
|
||||
Session::RunFrame();
|
||||
Session::HandleSignatures();
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Network::Handle("sessionSyn", [](Network::Address address, std::string data)
|
||||
{
|
||||
Session::Frame frame;
|
||||
frame.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
Session::Sessions[address] = frame;
|
||||
|
||||
Network::SendCommand(address, "sessionAck", frame.challenge);
|
||||
});
|
||||
|
||||
Network::Handle("sessionAck", [](Network::Address address, std::string data)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
Session::SignatureQueue.push({ address, data });
|
||||
});
|
||||
|
||||
Network::Handle("sessionFin", [](Network::Address address, std::string data)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
|
||||
auto frame = Session::Sessions.find(address);
|
||||
if (frame == Session::Sessions.end()) return;
|
||||
|
||||
std::string challenge = frame->second.challenge;
|
||||
Session::Sessions.erase(frame);
|
||||
|
||||
Proto::Session::Packet dataPacket;
|
||||
if (!dataPacket.ParseFromString(data)) return;
|
||||
|
||||
Utils::Cryptography::ECC::Key publicKey;
|
||||
publicKey.set(dataPacket.publickey());
|
||||
|
||||
if (!Utils::Cryptography::ECC::VerifyMessage(publicKey, challenge, dataPacket.signature())) return;
|
||||
|
||||
auto handler = Session::PacketHandlers.find(dataPacket.command());
|
||||
if (handler == Session::PacketHandlers.end()) return;
|
||||
|
||||
handler->second(address, dataPacket.data());
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
Session::PacketHandlers.clear();
|
||||
Session::PacketQueue.clear();
|
||||
Session::SignatureQueue = std::queue<std::pair<Network::Address, std::string>>();
|
||||
|
||||
Session::SignatureKey.free();
|
||||
}
|
||||
|
||||
void Session::preDestroy()
|
||||
{
|
||||
Session::Terminate = true;
|
||||
if (Session::Thread.joinable())
|
||||
{
|
||||
Session::Thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::unitTest()
|
||||
{
|
||||
printf("Testing ECDSA key...");
|
||||
Utils::Cryptography::ECC::Key key = Utils::Cryptography::ECC::GenerateKey(512);
|
||||
|
||||
if (!key.isValid())
|
||||
{
|
||||
printf("Error\n");
|
||||
printf("ECDSA key seems invalid!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Success\n");
|
||||
printf("Testing 10 valid signatures...");
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
std::string message = Utils::Cryptography::Rand::GenerateChallenge();
|
||||
std::string signature = Utils::Cryptography::ECC::SignMessage(key, message);
|
||||
|
||||
if (!Utils::Cryptography::ECC::VerifyMessage(key, message, signature))
|
||||
{
|
||||
printf("Error\n");
|
||||
printf("Signature for '%s' (%d) was invalid!\n", message.data(), i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Success\n");
|
||||
printf("Testing 10 invalid signatures...");
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
std::string message = Utils::Cryptography::Rand::GenerateChallenge();
|
||||
std::string signature = Utils::Cryptography::ECC::SignMessage(key, message);
|
||||
|
||||
// Invalidate the message...
|
||||
++message[Utils::Cryptography::Rand::GenerateInt() % message.size()];
|
||||
|
||||
if (Utils::Cryptography::ECC::VerifyMessage(key, message, signature))
|
||||
{
|
||||
printf("Error\n");
|
||||
printf("Signature for '%s' (%d) was valid? What the fuck? That is absolutely impossible...\n", message.data(), i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Success\n");
|
||||
printf("Testing ECDSA key import...");
|
||||
|
||||
std::string pubKey = key.getPublicKey();
|
||||
std::string message = Utils::Cryptography::Rand::GenerateChallenge();
|
||||
std::string signature = Utils::Cryptography::ECC::SignMessage(key, message);
|
||||
|
||||
Utils::Cryptography::ECC::Key testKey;
|
||||
testKey.set(pubKey);
|
||||
|
||||
if (!Utils::Cryptography::ECC::VerifyMessage(key, message, signature))
|
||||
{
|
||||
printf("Error\n");
|
||||
printf("Verifying signature for message '%s' using imported keys failed!\n", message.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Success\n");
|
||||
return true;
|
||||
}
|
||||
}
|
56
src/Components/Modules/Session.hpp
Normal file
56
src/Components/Modules/Session.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#define SESSION_TIMEOUT (10 * 1000) //10s
|
||||
#define SESSION_MAX_RETRIES 3
|
||||
#define SESSION_REQUEST_LIMIT 10
|
||||
|
||||
#define DISABLE_SESSION
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Session : public Component
|
||||
{
|
||||
public:
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
std::string command;
|
||||
std::string data;
|
||||
|
||||
unsigned int tries;
|
||||
std::optional<Utils::Time::Point> lastTry;
|
||||
};
|
||||
|
||||
class Frame
|
||||
{
|
||||
public:
|
||||
std::string challenge;
|
||||
Utils::Time::Point creationPoint;
|
||||
};
|
||||
|
||||
Session();
|
||||
~Session();
|
||||
|
||||
bool unitTest() override;
|
||||
void preDestroy() override;
|
||||
|
||||
static void Send(Network::Address target, std::string command, std::string data = "");
|
||||
static void Handle(std::string packet, Utils::Slot<Network::Callback> callback);
|
||||
|
||||
private:
|
||||
static bool Terminate;
|
||||
static std::thread Thread;
|
||||
static std::recursive_mutex Mutex;
|
||||
static std::unordered_map<Network::Address, Frame> Sessions;
|
||||
static std::unordered_map<Network::Address, std::queue<std::shared_ptr<Packet>>> PacketQueue;
|
||||
|
||||
static Utils::Cryptography::ECC::Key SignatureKey;
|
||||
|
||||
static std::map<std::string, Utils::Slot<Network::Callback>> PacketHandlers;
|
||||
|
||||
static std::queue<std::pair<Network::Address, std::string>> SignatureQueue;
|
||||
|
||||
static void RunFrame();
|
||||
static void HandleSignatures();
|
||||
};
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package Proto.Network;
|
||||
|
||||
message Node
|
||||
{
|
||||
bytes id = 1;
|
||||
bytes address = 2;
|
||||
}
|
||||
|
||||
message Nodes
|
||||
{
|
||||
repeated Node nodes = 1;
|
||||
}
|
16
src/Proto/node.proto
Normal file
16
src/Proto/node.proto
Normal file
@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package Proto.Node;
|
||||
|
||||
message List
|
||||
{
|
||||
repeated bytes nodes = 1;
|
||||
|
||||
// The port is used to check if a dedi sends data through a redirected port.
|
||||
// This usually means the port is not forwarded
|
||||
uint32 port = 2;
|
||||
|
||||
// Additional data
|
||||
bool isNode = 3;
|
||||
uint64 protocol = 4;
|
||||
}
|
11
src/Proto/session.proto
Normal file
11
src/Proto/session.proto
Normal file
@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package Proto.Session;
|
||||
|
||||
message Packet
|
||||
{
|
||||
bytes signature = 1;
|
||||
bytes publicKey = 2;
|
||||
bytes command = 3;
|
||||
bytes data = 4;
|
||||
}
|
@ -81,7 +81,6 @@ template <size_t S> class Sizer { };
|
||||
#include <json11.hpp>
|
||||
#include <tomcrypt.h>
|
||||
#include <udis86.h>
|
||||
#include <dht.h>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
@ -103,9 +102,10 @@ template <size_t S> class Sizer { };
|
||||
#endif
|
||||
|
||||
// Protobuf
|
||||
#include "proto/network.pb.h"
|
||||
#include "proto/session.pb.h"
|
||||
#include "proto/party.pb.h"
|
||||
#include "proto/auth.pb.h"
|
||||
#include "proto/node.pb.h"
|
||||
#include "proto/rcon.pb.h"
|
||||
#include "proto/ipc.pb.h"
|
||||
#include "proto/friends.pb.h"
|
||||
|
@ -40,31 +40,6 @@ namespace Utils
|
||||
rng_make_prng(128, find_prng("fortuna"), &Rand::State, nullptr);
|
||||
}
|
||||
|
||||
std::string Rand::GetRandomBytes(size_t size)
|
||||
{
|
||||
Utils::Memory::Allocator allocator;
|
||||
char* data = allocator.allocateArray<char>(size);
|
||||
|
||||
Rand::GetRandomBytes(data, size);
|
||||
|
||||
return std::string(data, size);
|
||||
}
|
||||
|
||||
void Rand::GetRandomBytes(void* data, size_t size)
|
||||
{
|
||||
char* dataPtr = reinterpret_cast<char*>(data);
|
||||
while (size > 0)
|
||||
{
|
||||
uint32_t num = Rand::GenerateInt();
|
||||
size_t len = std::min(size, sizeof(num));
|
||||
|
||||
std::memmove(dataPtr, &num, len);
|
||||
|
||||
dataPtr += len;
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ECC
|
||||
|
@ -140,9 +140,6 @@ namespace Utils
|
||||
static uint32_t GenerateInt();
|
||||
static void Initialize();
|
||||
|
||||
static std::string GetRandomBytes(size_t size);
|
||||
static void GetRandomBytes(void* data, size_t size);
|
||||
|
||||
private:
|
||||
static prng_state State;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user