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

631 lines
15 KiB
C++
Raw Normal View History

2022-02-03 14:05:24 -05:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/thread.hpp>
#include "game/game.hpp"
#include "game/demonware/servers/lobby_server.hpp"
#include "game/demonware/servers/auth3_server.hpp"
#include "game/demonware/servers/stun_server.hpp"
#include "game/demonware/servers/umbrella_server.hpp"
#include "game/demonware/server_registry.hpp"
#include <game/dvars.hpp>
#define TCP_BLOCKING true
#define UDP_BLOCKING false
namespace demonware
{
namespace
{
volatile bool exit_server;
std::thread server_thread;
utils::concurrency::container<std::unordered_map<SOCKET, bool>> blocking_sockets;
utils::concurrency::container<std::unordered_map<SOCKET, tcp_server*>> socket_map;
server_registry<tcp_server> tcp_servers;
server_registry<udp_server> udp_servers;
tcp_server* find_server(const SOCKET socket)
{
return socket_map.access<tcp_server*>([&](const std::unordered_map<SOCKET, tcp_server*>& map) -> tcp_server*
2022-02-23 15:23:00 -05:00
{
const auto entry = map.find(socket);
if (entry == map.end())
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
return nullptr;
}
2022-02-03 14:05:24 -05:00
2022-02-23 15:23:00 -05:00
return entry->second;
});
2022-02-03 14:05:24 -05:00
}
bool socket_link(const SOCKET socket, const uint32_t address)
{
auto* server = tcp_servers.find(address);
if (!server)
{
return false;
}
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& map)
2022-02-23 15:23:00 -05:00
{
map[socket] = server;
});
2022-02-03 14:05:24 -05:00
return true;
}
void socket_unlink(const SOCKET socket)
{
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& map)
2022-02-23 15:23:00 -05:00
{
const auto entry = map.find(socket);
if (entry != map.end())
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
map.erase(entry);
}
});
2022-02-03 14:05:24 -05:00
}
bool is_socket_blocking(const SOCKET socket, const bool def)
{
return blocking_sockets.access<bool>([&](std::unordered_map<SOCKET, bool>& map)
2022-02-23 15:23:00 -05:00
{
const auto entry = map.find(socket);
if (entry == map.end())
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
return def;
}
2022-02-03 14:05:24 -05:00
2022-02-23 15:23:00 -05:00
return entry->second;
});
2022-02-03 14:05:24 -05:00
}
void remove_blocking_socket(const SOCKET socket)
{
blocking_sockets.access([&](std::unordered_map<SOCKET, bool>& map)
2022-02-23 15:23:00 -05:00
{
const auto entry = map.find(socket);
if (entry != map.end())
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
map.erase(entry);
}
});
2022-02-03 14:05:24 -05:00
}
void add_blocking_socket(const SOCKET socket, const bool block)
{
blocking_sockets.access([&](std::unordered_map<SOCKET, bool>& map)
2022-02-23 15:23:00 -05:00
{
map[socket] = block;
});
2022-02-03 14:05:24 -05:00
}
void server_main()
{
exit_server = false;
while (!exit_server)
{
tcp_servers.frame();
udp_servers.frame();
std::this_thread::sleep_for(50ms);
}
}
namespace io
{
2022-02-24 09:11:19 -05:00
int getaddrinfo_stub(const char* name, const char* service,
const addrinfo* hints, addrinfo** res)
{
#ifdef DEBUG
printf("[ network ]: [getaddrinfo]: \"%s\" \"%s\"\n", name, service);
#endif
base_server* server = tcp_servers.find(name);
if (!server)
{
server = udp_servers.find(name);
}
if (!server)
{
2022-02-26 10:07:45 -05:00
return getaddrinfo(name, service, hints, res);
2022-02-24 09:11:19 -05:00
}
2022-02-26 10:07:45 -05:00
const auto address = utils::memory::get_allocator()->allocate<sockaddr>();
const auto ai = utils::memory::get_allocator()->allocate<addrinfo>();
auto in_addr = reinterpret_cast<sockaddr_in*>(address);
in_addr->sin_addr.s_addr = server->get_address();
in_addr->sin_family = AF_INET;
ai->ai_family = AF_INET;
ai->ai_socktype = SOCK_STREAM;
ai->ai_addr = address;
ai->ai_addrlen = sizeof(sockaddr);
ai->ai_next = nullptr;
ai->ai_flags = 0;
ai->ai_protocol = 0;
ai->ai_canonname = const_cast<char*>(name);
*res = ai;
2022-02-24 09:11:19 -05:00
return 0;
}
2022-02-26 10:07:45 -05:00
void freeaddrinfo_stub(addrinfo* ai)
{
if (!utils::memory::get_allocator()->find(ai))
{
return freeaddrinfo(ai);
}
utils::memory::get_allocator()->free(ai->ai_addr);
utils::memory::get_allocator()->free(ai);
}
2022-02-25 09:30:46 -05:00
int getpeername_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen)
{
auto* server = find_server(s);
if (server)
{
auto in_addr = reinterpret_cast<sockaddr_in*>(addr);
in_addr->sin_addr.s_addr = server->get_address();
in_addr->sin_family = AF_INET;
*addrlen = sizeof(sockaddr);
return 0;
}
return getpeername(s, addr, addrlen);
}
int getsockname_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen)
{
auto* server = find_server(s);
if (server)
{
auto in_addr = reinterpret_cast<sockaddr_in*>(addr);
in_addr->sin_addr.s_addr = server->get_address();
in_addr->sin_family = AF_INET;
*addrlen = sizeof(sockaddr);
return 0;
}
return getsockname(s, addr, addrlen);
}
2022-02-03 14:05:24 -05:00
hostent* gethostbyname_stub(const char* name)
{
#ifdef DEBUG
printf("[ network ]: [gethostbyname]: \"%s\"\n", name);
#endif
base_server* server = tcp_servers.find(name);
if (!server)
{
server = udp_servers.find(name);
}
if (!server)
{
#pragma warning(push)
#pragma warning(disable: 4996)
return gethostbyname(name);
#pragma warning(pop)
}
static thread_local in_addr address{};
address.s_addr = server->get_address();
static thread_local in_addr* addr_list[2]{};
addr_list[0] = &address;
addr_list[1] = nullptr;
static thread_local hostent host{};
host.h_name = const_cast<char*>(name);
host.h_aliases = nullptr;
host.h_addrtype = AF_INET;
host.h_length = sizeof(in_addr);
host.h_addr_list = reinterpret_cast<char**>(addr_list);
return &host;
}
int connect_stub(const SOCKET s, const struct sockaddr* addr, const int len)
{
if (len == sizeof(sockaddr_in))
{
const auto* in_addr = reinterpret_cast<const sockaddr_in*>(addr);
if (socket_link(s, in_addr->sin_addr.s_addr)) return 0;
}
return connect(s, addr, len);
}
int closesocket_stub(const SOCKET s)
{
remove_blocking_socket(s);
socket_unlink(s);
return closesocket(s);
}
int send_stub(const SOCKET s, const char* buf, const int len, const int flags)
{
auto* server = find_server(s);
if (server)
{
server->handle_input(buf, len);
return len;
}
return send(s, buf, len, flags);
}
int recv_stub(const SOCKET s, char* buf, const int len, const int flags)
{
auto* server = find_server(s);
if (server)
{
if (server->pending_data())
{
return static_cast<int>(server->handle_output(buf, len));
}
else
{
WSASetLastError(WSAEWOULDBLOCK);
return -1;
}
}
return recv(s, buf, len, flags);
}
int sendto_stub(const SOCKET s, const char* buf, const int len, const int flags, const sockaddr* to,
const int tolen)
{
const auto* in_addr = reinterpret_cast<const sockaddr_in*>(to);
auto* server = udp_servers.find(in_addr->sin_addr.s_addr);
if (server)
{
server->handle_input(buf, len, { s, to, tolen });
2022-02-03 14:05:24 -05:00
return len;
}
return sendto(s, buf, len, flags, to, tolen);
}
int recvfrom_stub(const SOCKET s, char* buf, const int len, const int flags, struct sockaddr* from,
int* fromlen)
{
// Not supported yet
if (is_socket_blocking(s, UDP_BLOCKING))
{
return recvfrom(s, buf, len, flags, from, fromlen);
}
size_t result = 0;
udp_servers.for_each([&](udp_server& server)
2022-02-23 15:23:00 -05:00
{
if (server.pending_data(s))
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
result = server.handle_output(
s, buf, static_cast<size_t>(len), from, fromlen);
}
});
2022-02-03 14:05:24 -05:00
if (result)
{
return static_cast<int>(result);
}
return recvfrom(s, buf, len, flags, from, fromlen);
}
int select_stub(const int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
struct timeval* timeout)
{
if (exit_server)
{
return select(nfds, readfds, writefds, exceptfds, timeout);
}
auto result = 0;
std::vector<SOCKET> read_sockets;
std::vector<SOCKET> write_sockets;
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& sockets)
2022-02-23 15:23:00 -05:00
{
for (auto& s : sockets)
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
if (readfds)
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
if (FD_ISSET(s.first, readfds))
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
if (s.second->pending_data())
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
read_sockets.push_back(s.first);
FD_CLR(s.first, readfds);
2022-02-03 14:05:24 -05:00
}
}
2022-02-23 15:23:00 -05:00
}
2022-02-03 14:05:24 -05:00
2022-02-23 15:23:00 -05:00
if (writefds)
{
if (FD_ISSET(s.first, writefds))
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
write_sockets.push_back(s.first);
FD_CLR(s.first, writefds);
2022-02-03 14:05:24 -05:00
}
2022-02-23 15:23:00 -05:00
}
2022-02-03 14:05:24 -05:00
2022-02-23 15:23:00 -05:00
if (exceptfds)
{
if (FD_ISSET(s.first, exceptfds))
2022-02-03 14:05:24 -05:00
{
2022-02-23 15:23:00 -05:00
FD_CLR(s.first, exceptfds);
2022-02-03 14:05:24 -05:00
}
}
2022-02-23 15:23:00 -05:00
}
});
2022-02-03 14:05:24 -05:00
if ((!readfds || readfds->fd_count == 0) && (!writefds || writefds->fd_count == 0))
{
timeout->tv_sec = 0;
timeout->tv_usec = 0;
}
result = select(nfds, readfds, writefds, exceptfds, timeout);
if (result < 0) result = 0;
for (const auto& socket : read_sockets)
{
if (readfds)
{
FD_SET(socket, readfds);
result++;
}
}
for (const auto& socket : write_sockets)
{
if (writefds)
{
FD_SET(socket, writefds);
result++;
}
}
return result;
}
int ioctlsocket_stub(const SOCKET s, const long cmd, u_long* argp)
{
if (static_cast<unsigned long>(cmd) == (FIONBIO))
{
add_blocking_socket(s, *argp == 0);
}
return ioctlsocket(s, cmd, argp);
}
BOOL internet_get_connected_state_stub(LPDWORD, DWORD)
{
// Allow offline play
return TRUE;
}
}
void bd_logger_stub()
2022-02-03 14:05:24 -05:00
{
//printf("logged\n");
2022-02-25 09:30:46 -05:00
}
#ifdef DEBUG
void a(unsigned int n)
{
printf("bdAuth: Auth task failed with HTTP code [%u]\n", n);
}
void b(unsigned int n)
{
printf("bdAuth: Decoded client ticket of unexpected size [%u]\n", n);
}
void c(unsigned int n)
{
printf("bdAuth: Decoded server ticket of unexpected size [%u]\n", n);
}
2022-02-03 14:05:24 -05:00
2022-02-25 09:30:46 -05:00
void d()
{
printf("bdAuth: Auth ticket magic number mismatch\n");
}
2022-02-03 14:05:24 -05:00
2022-02-25 09:30:46 -05:00
void e()
{
printf("bdAuth: Cross Authentication completed\n");
}
2022-02-03 14:05:24 -05:00
2022-02-25 09:30:46 -05:00
void f()
{
printf("bdAuth: Auth task reply contains invalid data / format\n");
}
2022-02-03 14:05:24 -05:00
2022-02-25 09:30:46 -05:00
void g(unsigned int n)
{
printf("bdAuth: Auth task returned with error code [%u]\n", n);
2022-02-03 14:05:24 -05:00
}
2022-02-25 09:30:46 -05:00
void h(unsigned int n)
{
printf("bdAuth: Invalid or No Task ID [%u] in Auth reply\n", n);
}
void i()
{
printf("bdAuth: Received reply from DemonWare Auth server\n");
}
void l()
{
printf("bdAuth: Unknown error\n");
}
#endif
2022-05-17 19:56:15 -04:00
utils::hook::detour handle_auth_reply_hook;
bool handle_auth_reply_stub(void* a1, void* a2, void* a3)
{
2022-05-17 19:56:15 -04:00
// Skip bdAuth::validateResponseSignature
utils::hook::set(0x7D4AB0_b, 0xC301B0);
2022-05-17 19:56:15 -04:00
// Skip bdAuth::processPlatformData
utils::hook::set(0x7D55C0_b, 0xC301B0);
2022-05-17 19:56:15 -04:00
return handle_auth_reply_hook.invoke<bool>(a1, a2, a3);
}
void* allocate_somewhere_near(uint8_t* base_address)
{
const size_t PAGE_SIZE = 0x1000;
size_t offset = 0;
while (true)
{
offset += PAGE_SIZE;
auto res = VirtualAlloc(base_address - offset, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (res)
{
std::memset(res, 0, PAGE_SIZE);
return res;
}
}
}
2022-05-18 08:16:58 -04:00
void request_start_match_stub()
{
const auto* args = "StartServer";
2022-05-19 11:01:34 -04:00
game::UI_RunMenuScript(0, &args);
2022-05-18 08:16:58 -04:00
}
2022-02-03 14:05:24 -05:00
}
class component final : public component_interface
{
public:
component()
{
udp_servers.create<stun_server>("phoenix.stun.us.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.eu.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.jp.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.au.demonware.net");
udp_servers.create<stun_server>("stun.us.demonware.net");
udp_servers.create<stun_server>("stun.eu.demonware.net");
udp_servers.create<stun_server>("stun.jp.demonware.net");
udp_servers.create<stun_server>("stun.au.demonware.net");
tcp_servers.create<auth3_server>("mwr-pc-steam-auth3.prod.demonware.net");
tcp_servers.create<lobby_server>("mwr-pc-steam-lobby.prod.demonware.net");
tcp_servers.create<umbrella_server>("prod.umbrella.demonware.net");
}
void post_load() override
{
2022-05-25 17:56:10 -04:00
if (game::environment::is_sp())
{
return;
}
2022-02-03 14:05:24 -05:00
server_thread = utils::thread::create_named_thread("Demonware", server_main);
}
void* load_import(const std::string& library, const std::string& function) override
{
if (library == "WS2_32.dll")
{
if (function == "#3") return io::closesocket_stub;
if (function == "#4") return io::connect_stub;
2022-02-25 09:30:46 -05:00
if (function == "#5") return io::getpeername_stub;
if (function == "#6") return io::getsockname_stub;
2022-02-03 14:05:24 -05:00
if (function == "#10") return io::ioctlsocket_stub;
if (function == "#16") return io::recv_stub;
if (function == "#17") return io::recvfrom_stub;
if (function == "#18") return io::select_stub;
if (function == "#19") return io::send_stub;
if (function == "#20") return io::sendto_stub;
if (function == "#52") return io::gethostbyname_stub;
2022-02-24 09:11:19 -05:00
if (function == "getaddrinfo") return io::getaddrinfo_stub;
2022-02-26 10:07:45 -05:00
if (function == "freeaddrinfo") return io::freeaddrinfo_stub;
2022-02-03 14:05:24 -05:00
}
if (function == "InternetGetConnectedState")
{
return io::internet_get_connected_state_stub;
}
return nullptr;
}
void post_unpack() override
{
if (game::environment::is_sp())
{
2022-05-30 12:06:32 -04:00
utils::hook::set<uint8_t>(0x68DDA0_b, 0xC3); // bdAuthSteam
utils::hook::set<uint8_t>(0x366600_b, 0xC3); // dwNet
2022-02-03 14:05:24 -05:00
return;
}
2022-05-17 19:56:15 -04:00
utils::hook::set<uint8_t>(0x7C0AD9_b, 0x0); // CURLOPT_SSL_VERIFYPEER
utils::hook::set<uint8_t>(0x7C0AC5_b, 0xAF); // CURLOPT_SSL_VERIFYHOST
utils::hook::set<uint8_t>(0xA1327C_b, 0x0); // HTTPS -> HTTP
2022-02-03 14:05:24 -05:00
2022-05-18 15:47:26 -04:00
std::memcpy(reinterpret_cast<void*>(0x8D0298_b),
"http://prod.umbrella.demonware.net/v1.0/", sizeof("http://prod.umbrella.demonware.net/v1.0/"));
std::memcpy(reinterpret_cast<void*>(0x8D05A8_b),
"http://prod.uno.demonware.net/v1.0/", sizeof("http://prod.uno.demonware.net/v1.0/"));
std::memcpy(reinterpret_cast<void*>(0x9EDB08_b), "http://%s:%d/auth/", sizeof("http://%s:%d/auth/"));
// utils::hook::set<uint8_t>(0x19F8C0_b, 0xC3); // SV_SendMatchData, not sure
2022-06-04 18:20:11 -04:00
utils::hook::nop(0x19BB67_b, 5); // LiveStorage_SendMatchDataComplete (crashes at the end of match)
utils::hook::nop(0x19BC3F_b, 5); // LiveStorage_GettingStoreConfigComplete probably (crashes randomly)
utils::hook::nop(0x19BC48_b, 5); // similar to above (crashes in killcam)
2022-05-17 19:56:15 -04:00
utils::hook::set<uint8_t>(0x1A3340_b, 0xC3); // Live_CheckForFullDisconnect
2022-05-17 19:56:15 -04:00
// Remove some while loop that freezes the rendering for a few secs while connecting
utils::hook::nop(0x625555_b, 5);
handle_auth_reply_hook.create(0x7AC600_b, handle_auth_reply_stub);
// Skip update check in Live_SyncOnlineDataFlags
utils::hook::set(0x47A6D0_b, 0xC301B0);
// Remove update failed popup
utils::hook::set(0x47B2B0_b, 0xC301B0);
2022-05-18 08:16:58 -04:00
// xpartygo -> just start the match
2022-06-04 18:20:11 -04:00
utils::hook::jump(0x355B80_b, request_start_match_stub);
2022-05-18 08:16:58 -04:00
utils::hook::set(0x396AD0_b, 0xC301B0); // DB_IsZoneLoaded("ffotd")
utils::hook::set(0x4DD600_b, 0xC300B0); // dont use ffotd
utils::hook::set(0x4DD5B0_b, 0xC300B0); // dont dl ffotd
2022-02-03 14:05:24 -05:00
}
void pre_destroy() override
{
exit_server = true;
if (server_thread.joinable())
{
server_thread.join();
}
}
};
}
REGISTER_COMPONENT(demonware::component)