h1-mod/src/client/component/network.cpp

321 lines
8.5 KiB
C++
Raw Normal View History

2022-02-03 14:05:24 -05:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "command.hpp"
#include "network.hpp"
2022-02-11 22:21:53 -05:00
#include "console.hpp"
2022-02-28 14:31:25 -05:00
#include "dvars.hpp"
2022-02-11 22:21:53 -05:00
#include "game/dvars.hpp"
2022-02-03 14:05:24 -05:00
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace network
{
namespace
{
2022-02-03 14:05:24 -05:00
std::unordered_map<std::string, callback>& get_callbacks()
{
static std::unordered_map<std::string, callback> callbacks{};
return callbacks;
}
bool handle_command(game::netadr_s* address, const char* command, game::msg_t* message)
{
const auto cmd_string = utils::string::to_lower(command);
auto& callbacks = get_callbacks();
const auto handler = callbacks.find(cmd_string);
const auto offset = cmd_string.size() + 5;
2022-03-10 17:21:31 -05:00
if (message->cursize < offset || handler == callbacks.end())
2022-03-01 18:33:17 -05:00
{
return false;
}
2022-02-03 14:05:24 -05:00
const std::string_view data(message->data + offset, message->cursize - offset);
handler->second(*address, data);
#ifdef DEBUG
console::info("[Network] Handling command %s\n", cmd_string.data());
#endif
2022-02-03 14:05:24 -05:00
return true;
}
void handle_command_stub(utils::hook::assembler& a)
{
const auto return_unhandled = a.newLabel();
a.pushad64();
2022-02-26 22:10:11 -05:00
a.mov(r8, rdi); // message
a.mov(rdx, rbx); // command
a.mov(rcx, rsi); // netaddr
2022-02-03 14:05:24 -05:00
a.call_aligned(handle_command);
a.test(al, al);
a.jz(return_unhandled);
// Command handled
a.popad64();
a.mov(al, 1);
2022-05-17 15:56:11 -04:00
a.jmp(0x12FCAA_b);
2022-02-03 14:05:24 -05:00
a.bind(return_unhandled);
a.popad64();
2022-05-17 15:56:11 -04:00
a.jmp(0x12F3AC_b);
2022-02-03 14:05:24 -05:00
}
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2)
{
if (a1->type == a2->type)
{
switch (a1->type)
{
case game::netadrtype_t::NA_BOT:
case game::netadrtype_t::NA_LOOPBACK:
return a1->port == a2->port;
case game::netadrtype_t::NA_IP:
return !memcmp(a1->ip, a2->ip, 4);
case game::netadrtype_t::NA_BROADCAST:
return true;
default:
break;
}
}
return false;
}
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2)
{
return net_compare_base_address(a1, a2) && a1->port == a2->port;
}
void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*,
const char*, bool)
{
// This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid
// We don't want this to do anything. It decides to crash seemingly randomly
// Rather than try and let the player in, just tell them they are a duplicate player and reject connection
game::NET_OutOfBandPrint(game::NS_SERVER, from, "error\nYou are already connected to the server.");
}
2022-02-28 09:01:30 -05:00
2022-02-28 09:07:22 -05:00
SOCKET create_socket(const char* net_interface, int port, int protocol)
2022-02-28 09:01:30 -05:00
{
2022-02-28 09:07:22 -05:00
sockaddr_in address{};
2022-02-28 09:01:30 -05:00
2022-02-28 09:07:22 -05:00
if (net_interface && net_interface != "localhost"s)
{
// Sys_StringToSockaddr
2022-05-17 15:56:11 -04:00
utils::hook::invoke<void>(0x59E810_b, net_interface, &address);
2022-02-28 09:07:22 -05:00
}
2022-02-28 09:01:30 -05:00
2022-02-28 09:07:22 -05:00
address.sin_family = AF_INET;
2022-02-28 09:13:03 -05:00
address.sin_port = ntohs(static_cast<short>(port));
2022-02-28 09:01:30 -05:00
2022-05-18 18:36:32 -04:00
const auto sock = socket(AF_INET, SOCK_DGRAM, protocol);
2022-02-28 09:01:30 -05:00
2022-02-28 09:07:22 -05:00
u_long arg = 1;
ioctlsocket(sock, FIONBIO, &arg);
2022-05-18 18:36:32 -04:00
char optval[4] = {1};
2022-02-28 09:07:22 -05:00
setsockopt(sock, 0xFFFF, 32, optval, 4);
2022-02-28 09:01:30 -05:00
2022-02-28 09:07:22 -05:00
if (bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) != -1)
{
return sock;
}
closesocket(sock);
return 0;
}
2022-02-03 14:05:24 -05:00
}
void on(const std::string& command, const callback& callback)
{
get_callbacks()[utils::string::to_lower(command)] = callback;
}
2022-02-11 22:21:53 -05:00
int dw_send_to_stub(const int size, const char* src, game::netadr_s* a3)
2022-02-03 14:05:24 -05:00
{
sockaddr s = {};
2022-02-11 22:21:53 -05:00
game::NetadrToSockadr(a3, &s);
2022-02-28 09:13:03 -05:00
return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0;
2022-02-03 14:05:24 -05:00
}
void send(const game::netadr_s& address, const std::string& command, const std::string& data, const char separator)
{
std::string packet = "\xFF\xFF\xFF\xFF";
packet.append(command);
packet.push_back(separator);
packet.append(data);
send_data(address, packet);
}
void send_data(const game::netadr_s& address, const std::string& data)
{
2022-02-11 22:21:53 -05:00
auto size = static_cast<int>(data.size());
2022-02-03 14:05:24 -05:00
if (address.type == game::NA_LOOPBACK)
{
2022-02-11 22:21:53 -05:00
// TODO: Fix this for loopback
if (size > 1280)
{
console::error("Packet was too long. Truncated!\n");
size = 1280;
}
game::NET_SendLoopPacket(game::NS_CLIENT1, size, data.data(), &address);
2022-02-03 14:05:24 -05:00
}
else
{
2022-02-11 22:21:53 -05:00
game::Sys_SendPacket(size, data.data(), &address);
2022-02-03 14:05:24 -05:00
}
}
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b)
{
return net_compare_address(&a, &b);
}
const char* net_adr_to_string(const game::netadr_s& a)
{
if (a.type == game::netadrtype_t::NA_LOOPBACK)
{
return "loopback";
}
if (a.type == game::netadrtype_t::NA_BOT)
{
return "bot";
}
if (a.type == game::netadrtype_t::NA_IP || a.type == game::netadrtype_t::NA_BROADCAST)
{
if (a.port)
{
return utils::string::va("%u.%u.%u.%u:%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3], htons(a.port));
}
return utils::string::va("%u.%u.%u.%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
}
return "bad";
}
game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags,
const char* description)
{
2022-02-11 22:21:53 -05:00
game::dvar_t* dvar;
2022-03-12 18:56:15 -05:00
dvar = dvars::register_int("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port");
2022-02-03 14:05:24 -05:00
// read net_port from command line
command::read_startup_variable("net_port");
return dvar;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
{
if (game::environment::is_sp())
{
return;
}
// redirect dw_sendto to raw socket
2022-05-28 13:02:02 -04:00
utils::hook::jump(0x5EEC90_b, dw_send_to_stub);
utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub);
2022-02-03 14:05:24 -05:00
// intercept command handling
2022-05-17 15:56:11 -04:00
utils::hook::jump(0x12F387_b, utils::hook::assemble(handle_command_stub), true);
2022-02-03 14:05:24 -05:00
// handle xuid without secure connection
2022-05-17 15:56:11 -04:00
utils::hook::nop(0x554222_b, 6);
2022-02-03 14:05:24 -05:00
2022-05-28 13:02:02 -04:00
utils::hook::jump(0x4F1800_b, net_compare_address);
utils::hook::jump(0x4F1850_b, net_compare_base_address);
2022-02-03 14:05:24 -05:00
// don't establish secure conenction
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x358C8D_b, 0xEB);
utils::hook::set<uint8_t>(0x358D5E_b, 0xEB);
utils::hook::set<uint8_t>(0x3591CD_b, 0xEB);
utils::hook::set<uint8_t>(0x12CD0F_b, 0xEB);
2022-02-03 14:05:24 -05:00
// ignore unregistered connection
2022-05-18 18:36:32 -04:00
utils::hook::jump(0x54E2D1_b, 0x54E270_b, true);
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x54E2C6_b, 0xEB);
2022-02-03 14:05:24 -05:00
// disable xuid verification
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x728BF_b, 0xEB);
2022-02-03 14:05:24 -05:00
// disable xuid verification
2022-05-17 15:56:11 -04:00
utils::hook::nop(0x5509D9_b, 2);
utils::hook::set<uint8_t>(0x550A36_b, 0xEB);
2022-02-03 14:05:24 -05:00
// ignore configstring mismatch
2022-05-18 18:36:32 -04:00
utils::hook::set<uint8_t>(0x341261_b, 0xEB);
2022-02-03 14:05:24 -05:00
// ignore dw handle in SV_PacketEvent
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x1CBC22_b, 0xEB);
2022-02-03 14:05:24 -05:00
// ignore dw handle in SV_FindClientByAddress
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x1CB24D_b, 0xEB);
2022-02-03 14:05:24 -05:00
// ignore dw handle in SV_DirectConnect
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x54DFE8_b, 0xEB);
utils::hook::set<uint8_t>(0x54E1FD_b, 0xEB);
2022-02-03 14:05:24 -05:00
// increase cl_maxpackets
2022-02-28 14:31:25 -05:00
dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED);
2022-02-11 22:21:53 -05:00
// increase snaps
2022-02-28 14:31:25 -05:00
dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE);
2022-02-03 14:05:24 -05:00
// ignore impure client
2022-05-28 13:02:02 -04:00
utils::hook::jump(0x54EDD3_b, 0x54EE69_b);
2022-02-03 14:05:24 -05:00
// don't send checksum
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x59E628_b, 0);
2022-02-03 14:05:24 -05:00
// don't read checksum
2022-05-17 15:56:11 -04:00
utils::hook::set(0x59E8B0_b, 0xC301B0);
2022-02-03 14:05:24 -05:00
// don't try to reconnect client
2022-05-28 13:02:02 -04:00
utils::hook::jump(0x54D220_b, reconnect_migratated_client);
2022-05-17 15:56:11 -04:00
utils::hook::nop(0x54E168_b, 4); // this crashes when reconnecting for some reason
2022-02-03 14:05:24 -05:00
// allow server owner to modify net_port before the socket bind
2022-05-28 13:02:02 -04:00
utils::hook::call(0x5BD032_b, register_netport_stub);
2022-05-28 11:08:55 -04:00
utils::hook::call(0x5BD3F0_b, register_netport_stub);
2022-02-03 14:05:24 -05:00
2022-02-11 22:21:53 -05:00
// increase allowed packet size
const auto max_packet_size = 0x20000;
2022-05-17 15:56:11 -04:00
utils::hook::set<int>(0x4F1ED1_b, max_packet_size);
utils::hook::set<int>(0x4F1F10_b, max_packet_size);
utils::hook::set<int>(0x4F1E02_b, max_packet_size);
utils::hook::set<int>(0x4F1E25_b, max_packet_size);
2022-02-11 22:21:53 -05:00
2022-02-03 14:05:24 -05:00
// ignore built in "print" oob command and add in our own
2022-05-17 15:56:11 -04:00
utils::hook::set<uint8_t>(0x12F817_b, 0xEB);
2022-02-11 22:21:53 -05:00
on("print", [](const game::netadr_s&, const std::string_view& data)
2022-02-23 15:23:00 -05:00
{
const std::string message{data};
console::info(message.data());
});
2022-02-28 09:01:30 -05:00
// Use our own socket since the game's socket doesn't work with non localhost addresses
// why? no idea
2022-05-28 13:02:02 -04:00
utils::hook::jump(0x5BD210_b, create_socket);
2022-02-03 14:05:24 -05:00
}
}
};
}
2022-05-18 18:36:32 -04:00
REGISTER_COMPONENT(network::component)