#include "STDInclude.hpp" namespace Components { std::string Network::SelectedPacket; Utils::Signal Network::StartupSignal; std::map> Network::PacketHandlers; Network::Address::Address(const std::string& addrString) { Game::NET_StringToAdr(addrString.data(), &this->address); } Network::Address::Address(sockaddr* addr) { Game::SockadrToNetadr(addr, &this->address); } bool Network::Address::operator==(const Network::Address &obj) const { return Game::NET_CompareAdr(this->address, obj.address); } void Network::Address::setPort(unsigned short port) { this->address.port = htons(port); } unsigned short Network::Address::getPort() { return ntohs(this->address.port); } void Network::Address::setIP(DWORD ip) { this->address.ip.full = ip; } void Network::Address::setIP(Game::netIP_t ip) { this->address.ip = ip; } Game::netIP_t Network::Address::getIP() { return this->address.ip; } void Network::Address::setType(Game::netadrtype_t type) { this->address.type = type; } Game::netadrtype_t Network::Address::getType() { return this->address.type; } sockaddr Network::Address::getSockAddr() { sockaddr addr; this->toSockAddr(&addr); return addr; } void Network::Address::toSockAddr(sockaddr* addr) { if (addr) { Game::NetadrToSockadr(&this->address, addr); } } void Network::Address::toSockAddr(sockaddr_in* addr) { this->toSockAddr(reinterpret_cast(addr)); } Game::netadr_t* Network::Address::get() { return &this->address; } const char* Network::Address::getCString() const { return Game::NET_AdrToString(this->address); } std::string Network::Address::getString() const { return this->getCString(); } bool Network::Address::isLocal() { // According to: https://en.wikipedia.org/wiki/Private_network // 10.X.X.X if (this->getIP().bytes[0] == 10) return true; // 192.168.X.X if (this->getIP().bytes[0] == 192 && this->getIP().bytes[1] == 168) return true; // 172.16.X.X - 172.31.X.X if (this->getIP().bytes[0] == 172 && (this->getIP().bytes[1] >= 16) && (this->getIP().bytes[1] < 32)) return true; // 127.0.0.1 if (this->getIP().full == 0x0100007F) return true; // TODO: Maybe check for matching localIPs and subnet mask return false; } bool Network::Address::isSelf() { if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback if (this->getPort() != Network::GetPort()) return false; // Port not equal for (int i = 0; i < *Game::numIP; ++i) { if (this->getIP().full == Game::localIP[i].full) { return true; } } return false; } bool Network::Address::isLoopback() { if (this->getIP().full == 0x100007f) // 127.0.0.1 { return true; } return Game::NET_IsLocalAddress(this->address); } bool Network::Address::isValid() { return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP); } void Network::Handle(const std::string& packet, Utils::Slot callback) { Network::PacketHandlers[Utils::String::ToLower(packet)] = callback; } void Network::OnStart(Utils::Slot callback) { Network::StartupSignal.connect(callback); } void Network::Send(Game::netsrc_t type, Network::Address target, const std::string& data) { // NET_OutOfBandPrint only supports non-binary data! //Game::NET_OutOfBandPrint(type, *target.Get(), data.data()); std::string rawData; rawData.append("\xFF\xFF\xFF\xFF", 4); rawData.append(data); //rawData.append("\0", 1); Network::SendRaw(type, target, rawData); } void Network::Send(Network::Address target, const std::string& data) { Network::Send(Game::netsrc_t::NS_CLIENT1, target, data); } void Network::SendRaw(Game::netsrc_t type, Network::Address target, const std::string& data) { if (!target.isValid()) return; // NET_OutOfBandData doesn't seem to work properly //Game::NET_OutOfBandData(type, *target.Get(), data.data(), data.size()); Game::Sys_SendPacket(type, data.size(), data.data(), *target.get()); } void Network::SendRaw(Network::Address target, const std::string& data) { Network::SendRaw(Game::netsrc_t::NS_CLIENT1, target, data); } void Network::SendCommand(Game::netsrc_t type, Network::Address target, const std::string& command, const std::string& data) { // Use space as separator (possible separators are '\n', ' '). // Though, our handler only needs exactly 1 char as separator and doesn't care which char it is. // EDIT: Most 3rd party tools expect a line break, so let's use that instead! std::string packet; packet.append(command); packet.append("\n", 1); packet.append(data); Network::Send(type, target, packet); } void Network::SendCommand(Network::Address target, const std::string& command, const std::string& data) { Network::SendCommand(Game::netsrc_t::NS_CLIENT1, target, command, data); } void Network::Broadcast(unsigned short port, const std::string& data) { Address target; target.setPort(port); target.setIP(INADDR_BROADCAST); target.setType(Game::netadrtype_t::NA_BROADCAST); Network::Send(Game::netsrc_t::NS_CLIENT1, target, data); } void Network::BroadcastRange(unsigned int min, unsigned int max, const std::string& data) { for (unsigned int i = min; i < max; ++i) { Network::Broadcast(static_cast(i & 0xFFFF), data); } } void Network::BroadcastAll(const std::string& data) { Network::BroadcastRange(100, 65536, data); } int Network::PacketInterceptionHandler(const char* packet) { // Packet rate limit. static uint32_t packets = 0; static int lastClean = 0; if ((Game::Sys_Milliseconds() - lastClean) > 1'000) { packets = 0; lastClean = Game::Sys_Milliseconds(); } if ((++packets) > NETWORK_MAX_PACKETS_PER_SECOND) { return 1; } std::string packetCommand = packet; auto pos = packetCommand.find_first_of("\\\n "); if (pos != std::string::npos) { packetCommand = packetCommand.substr(0, pos); } packetCommand = Utils::String::ToLower(packetCommand); // Check if custom handler exists for (auto i = Network::PacketHandlers.begin(); i != Network::PacketHandlers.end(); ++i) { if (Utils::String::ToLower(i->first) == packetCommand) { Network::SelectedPacket = i->first; return 0; } } // No interception return 1; } void Network::DeployPacket(Game::netadr_t* from, Game::msg_t* msg) { if (Network::PacketHandlers.find(Network::SelectedPacket) != Network::PacketHandlers.end()) { std::string data; size_t offset = Network::SelectedPacket.size() + 4 + 1; if (static_cast(msg->cursize) > offset) { data.append(msg->data + offset, msg->cursize - offset); } // Remove trailing 0x00 byte // Actually, don't remove it, it might be part of the packet. Send correctly formatted packets instead! //if (data.size() && !data[data.size() - 1]) data.pop_back(); Network::PacketHandlers[Network::SelectedPacket](from, data); } else { Logger::Print("Error: Network packet intercepted, but handler is missing!\n"); } } void Network::NetworkStart() { Network::StartupSignal(); } unsigned short Network::GetPort() { return static_cast(Dvar::Var(0x64A3004).get()); } __declspec(naked) void Network::NetworkStartStub() { __asm { mov eax, 64D900h call eax jmp Network::NetworkStart } } __declspec(naked) void Network::DeployPacketStub() { __asm { lea eax, [esp + 0C54h] pushad push ebp // Command push eax // Address pointer call Network::DeployPacket add esp, 8h popad mov al, 1 pop edi pop esi pop ebp pop ebx add esp, 0C40h retn } } __declspec(naked) void Network::PacketErrorCheck() { __asm { cmp eax, 2746h jz returnIgnore cmp eax, WSAENETRESET jz returnIgnore push 465325h retn returnIgnore: push 4654C6h retn } } void Network::NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg) { if (msg->cursize > 0 && msg->cursize <= 1404) Game::NET_DeferPacketToClient(from, msg); } Network::Network() { AssertSize(Game::netadr_t, 20); // maximum size in NET_OutOfBandPrint Utils::Hook::Set(0x4AEF08, 0x1FFFC); Utils::Hook::Set(0x4AEFA3, 0x1FFFC); // increase max port binding attempts from 10 to 100 Utils::Hook::Set(0x4FD48A, 100); // increase cl_maxpackets limit Utils::Hook::Set(0x4050A1, 125); // increase snaps //Utils::Hook::Set(0x405357, 40); // default maxpackets and snaps Utils::Hook::Set(0x40535B, 30); Utils::Hook::Set(0x4050A5, 125); // Parse port as short in Net_AddrToString Utils::Hook::Set(0x4698E3, "%u.%u.%u.%u:%hu"); // Install startup handler Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick(); // Install interception handler Utils::Hook(0x5AA709, Network::PacketInterceptionHandler, HOOK_CALL).install()->quick(); // Prevent recvfrom error spam Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick(); // Install packet deploy hook Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub); // Fix packets causing buffer overflow Utils::Hook(0x6267E3, Network::NET_DeferPacketToClientStub, HOOK_CALL).install()->quick(); Network::Handle("resolveAddress", [](Address address, const std::string& /*data*/) { Network::SendRaw(address, address.getString()); }); } Network::~Network() { Network::SelectedPacket.clear(); Network::PacketHandlers.clear(); Network::StartupSignal.clear(); } }