Merge pull request #80 from rtw1x1/main

code cleanup
This commit is contained in:
rtw1x1
2026-02-12 23:58:05 +00:00
committed by GitHub
14 changed files with 6 additions and 502 deletions

View File

@@ -119,10 +119,6 @@ bool SecureCipher::ComputeServerKeys(const uint8_t* client_pk)
sodium_memzero(m_rx_stream_nonce, NONCE_SIZE);
m_rx_stream_nonce[0] = 0x02;
sys_log(0, "[CIPHER] Server keys computed (tx_key: %02x%02x%02x%02x, rx_key: %02x%02x%02x%02x)",
m_tx_key[0], m_tx_key[1], m_tx_key[2], m_tx_key[3],
m_rx_key[0], m_rx_key[1], m_rx_key[2], m_rx_key[3]);
return true;
}

View File

@@ -101,14 +101,6 @@ bool g_bCheckMultiHack = true;
int g_iSpamBlockMaxLevel = 10;
int g_iFloodMaxPacketsPerSec = 300;
int g_iFloodMaxConnectionsPerIP = 10;
int g_iFloodMaxGlobalConnections = 8192;
bool g_bFirewallEnable = false;
int g_iFirewallTcpSynLimit = 500;
int g_iFirewallTcpSynBurst = 1000;
void LoadStateUserCount();
void LoadValidCRCList();
bool LoadClientVersion();
@@ -703,41 +695,6 @@ void config_init(const string& st_localeServiceName)
str_to_number(g_iSpamBlockMaxLevel, value_string);
}
TOKEN("flood_max_packets_per_sec")
{
str_to_number(g_iFloodMaxPacketsPerSec, value_string);
g_iFloodMaxPacketsPerSec = MAX(50, g_iFloodMaxPacketsPerSec);
}
TOKEN("flood_max_connections_per_ip")
{
str_to_number(g_iFloodMaxConnectionsPerIP, value_string);
g_iFloodMaxConnectionsPerIP = MAX(1, g_iFloodMaxConnectionsPerIP);
}
TOKEN("flood_max_global_connections")
{
str_to_number(g_iFloodMaxGlobalConnections, value_string);
g_iFloodMaxGlobalConnections = MAX(64, g_iFloodMaxGlobalConnections);
}
TOKEN("firewall_enable")
{
str_to_number(g_bFirewallEnable, value_string);
}
TOKEN("firewall_tcp_syn_limit")
{
str_to_number(g_iFirewallTcpSynLimit, value_string);
g_iFirewallTcpSynLimit = MAX(10, g_iFirewallTcpSynLimit);
}
TOKEN("firewall_tcp_syn_burst")
{
str_to_number(g_iFirewallTcpSynBurst, value_string);
g_iFirewallTcpSynBurst = MAX(10, g_iFirewallTcpSynBurst);
}
TOKEN("protect_normal_player")
{
str_to_number(g_protectNormalPlayer, value_string);

View File

@@ -105,9 +105,5 @@ extern int gPlayerMaxLevel;
extern bool g_BlockCharCreation;
extern bool g_bFirewallEnable;
extern int g_iFirewallTcpSynLimit;
extern int g_iFirewallTcpSynBurst;
#endif /* __INC_METIN_II_GAME_CONFIG_H__ */

View File

@@ -81,10 +81,6 @@ void DESC::Initialize()
m_offtime = 0;
m_pkDisconnectEvent = NULL;
m_iFloodCheckPulse = 0;
m_dwFloodPacketCount = 0;
m_bIPCountTracked = false;
}
void DESC::Destroy()
@@ -216,7 +212,6 @@ bool DESC::Setup(LPFDWATCH _fdw, socket_t _fd, const struct sockaddr_in & c_rSoc
m_pkPingEvent = event_create(ping_event, info, ping_event_second_cycle);
// Set Phase to handshake and begin secure key exchange
SetPhase(PHASE_HANDSHAKE);
m_handshake_time = get_dword_time();
SendKeyChallenge();
@@ -242,7 +237,6 @@ int DESC::ProcessInput()
else if (bytes_read == 0)
return 0;
// Decrypt only the newly received bytes before committing to the buffer
if (m_secureCipher.IsActivated()) {
m_secureCipher.DecryptInPlace(m_inputBuffer.WritePtr(), bytes_read);
}
@@ -328,7 +322,6 @@ void DESC::Packet(const void * c_pvData, int iSize)
if (m_iPhase == PHASE_CLOSE)
return;
// Log the packet for sequence tracking (only for real packet sends, not buffered flushes)
if (!m_hasBufferedOutput && iSize >= (int)sizeof(uint16_t) * 2)
{
const uint16_t wHeader = *static_cast<const uint16_t*>(c_pvData);
@@ -465,43 +458,13 @@ bool DESC::IsExpiredHandshake() const
return (m_handshake_time + (5 * 1000)) < get_dword_time();
}
bool DESC::CheckPacketFlood()
{
extern int g_iFloodMaxPacketsPerSec;
// Use thecore_pulse() (cached per game-loop iteration) instead of
// get_dword_time() (gettimeofday syscall) to avoid per-packet syscall overhead.
int pulse = thecore_pulse();
int pps = static_cast<int>(thecore_pulse_per_second());
if (pulse - m_iFloodCheckPulse >= pps)
{
m_iFloodCheckPulse = pulse;
m_dwFloodPacketCount = 1;
return false;
}
++m_dwFloodPacketCount;
if (m_dwFloodPacketCount > (uint32_t)g_iFloodMaxPacketsPerSec)
{
sys_log(0, "FLOOD: %s exceeded %d packets/sec (count: %u), disconnecting",
GetHostName(), g_iFloodMaxPacketsPerSec, m_dwFloodPacketCount);
return true;
}
return false;
}
DWORD DESC::GetClientTime()
{
return m_dwClientTime;
}
// Secure key exchange methods (libsodium/XChaCha20-Poly1305)
void DESC::SendKeyChallenge()
{
// Initialize cipher and generate keypair
if (!m_secureCipher.Initialize())
{
sys_err("Failed to initialize SecureCipher");
@@ -509,10 +472,8 @@ void DESC::SendKeyChallenge()
return;
}
// Generate challenge
m_secureCipher.GenerateChallenge(m_challenge);
// Build and send challenge packet
TPacketGCKeyChallenge packet;
packet.header = GC::KEY_CHALLENGE;
packet.length = sizeof(packet);
@@ -529,14 +490,12 @@ void DESC::SendKeyChallenge()
bool DESC::HandleKeyResponse(const uint8_t* client_pk, const uint8_t* challenge_response)
{
// Compute session keys from client's public key
if (!m_secureCipher.ComputeServerKeys(client_pk))
{
sys_err("Failed to compute server session keys for %s", GetHostName());
return false;
}
// Verify challenge response
if (!m_secureCipher.VerifyChallengeResponse(m_challenge, challenge_response))
{
sys_err("Challenge response verification failed for %s", GetHostName());
@@ -550,17 +509,14 @@ bool DESC::HandleKeyResponse(const uint8_t* client_pk, const uint8_t* challenge_
void DESC::SendKeyComplete()
{
// Generate session token
uint8_t session_token[SecureCipher::SESSION_TOKEN_SIZE];
randombytes_buf(session_token, sizeof(session_token));
m_secureCipher.SetSessionToken(session_token);
// Build and send complete packet
TPacketGCKeyComplete packet;
packet.header = GC::KEY_COMPLETE;
packet.length = sizeof(packet);
// Encrypt the session token
if (!m_secureCipher.EncryptToken(session_token, sizeof(session_token),
packet.encrypted_token, packet.nonce))
{
@@ -571,10 +527,7 @@ void DESC::SendKeyComplete()
Packet(&packet, sizeof(packet));
// Flush before activating encryption
ProcessOutput();
// Activate encryption
m_secureCipher.SetActivated(true);
sys_log(0, "[HANDSHAKE] Cipher ACTIVATED for %s (tx_nonce: %llu, rx_nonce: %llu)",
@@ -852,8 +805,6 @@ void DESC::ChatPacket(BYTE type, const char * format, ...)
Packet(buf.read_peek(), buf.size());
}
// --- Packet sequence tracking ---
void DESC::LogRecvPacket(uint16_t header, uint16_t length)
{
auto& e = m_aRecentRecvPackets[m_dwRecvPacketSeq % PACKET_LOG_SIZE];

View File

@@ -104,7 +104,6 @@ class DESC
DWORD GetClientTime();
// Secure key exchange (libsodium/XChaCha20-Poly1305)
void SendKeyChallenge();
bool HandleKeyResponse(const uint8_t* client_pk, const uint8_t* challenge_response);
void SendKeyComplete();
@@ -142,15 +141,9 @@ class DESC
bool isChannelStatusRequested() const { return m_bChannelStatusRequested; }
void SetChannelStatusRequested(bool bChannelStatusRequested) { m_bChannelStatusRequested = bChannelStatusRequested; }
// Handshake timeout check
bool IsExpiredHandshake() const;
void SetHandshakeTime(uint32_t handshake_time) { m_handshake_time = handshake_time; }
// Flood protection
bool CheckPacketFlood();
void SetIPCountTracked(bool b) { m_bIPCountTracked = b; }
bool IsIPCountTracked() const { return m_bIPCountTracked; }
protected:
void Initialize();
@@ -215,15 +208,7 @@ class DESC
bool m_bDestroyed;
bool m_bChannelStatusRequested;
// Handshake timeout protection
uint32_t m_handshake_time;
// Flood protection
int m_iFloodCheckPulse;
uint32_t m_dwFloodPacketCount;
bool m_bIPCountTracked;
// Secure cipher (libsodium/XChaCha20-Poly1305)
SecureCipher m_secureCipher;
uint8_t m_challenge[SecureCipher::CHALLENGE_SIZE];
@@ -242,7 +227,6 @@ class DESC
void RawPacket(const void * c_pvData, int iSize);
void ChatPacket(BYTE type, const char * format, ...);
// --- Packet sequence tracking (debug aid) ---
public:
struct PacketLogEntry
{

View File

@@ -88,27 +88,6 @@ LPDESC DESC_MANAGER::AcceptDesc(LPFDWATCH fdw, socket_t s)
strlcpy(host, inet_ntoa(peer.sin_addr), sizeof(host));
// Global connection limit
extern int g_iFloodMaxGlobalConnections;
if (m_iSocketsConnected >= g_iFloodMaxGlobalConnections)
{
sys_log(0, "FLOOD: rejecting connection from %s (global limit %d/%d reached)",
host, m_iSocketsConnected, g_iFloodMaxGlobalConnections);
socket_close(desc);
return NULL;
}
// Per-IP connection limit
extern int g_iFloodMaxConnectionsPerIP;
auto itIP = m_map_ipConnCount.find(host);
if (itIP != m_map_ipConnCount.end() && itIP->second >= g_iFloodMaxConnectionsPerIP)
{
sys_log(0, "FLOOD: rejecting connection from %s (%d/%d connections)",
host, itIP->second, g_iFloodMaxConnectionsPerIP);
socket_close(desc);
return NULL;
}
newd = M2_NEW DESC;
if (!newd->Setup(fdw, desc, peer, ++m_iHandleCount))
@@ -123,10 +102,6 @@ LPDESC DESC_MANAGER::AcceptDesc(LPFDWATCH fdw, socket_t s)
m_set_pkDesc.insert(newd);
++m_iSocketsConnected;
// Track per-IP count
++m_map_ipConnCount[host];
newd->SetIPCountTracked(true);
return (newd);
}
@@ -181,17 +156,6 @@ void DESC_MANAGER::DestroyDesc(LPDESC d, bool bEraseFromSet)
else
m_set_pkClientDesc.erase((LPCLIENT_DESC) d);
// Decrement per-IP connection count (before Destroy invalidates state)
if (d->IsIPCountTracked())
{
auto it = m_map_ipConnCount.find(d->GetHostName());
if (it != m_map_ipConnCount.end())
{
if (--it->second <= 0)
m_map_ipConnCount.erase(it);
}
}
// Explicit call to the virtual function Destroy()
d->Destroy();
@@ -419,7 +383,7 @@ DWORD DESC_MANAGER::CreateLoginKey(LPDESC d)
do
{
dwKey = randombytes_uniform(INT_MAX) + 1; // CSPRNG: [1, INT_MAX]
dwKey = randombytes_uniform(INT_MAX) + 1;
if (m_map_pkLoginKey.find(dwKey) != m_map_pkLoginKey.end())
continue;
@@ -445,7 +409,6 @@ void DESC_MANAGER::ProcessExpiredLoginKey()
{
it2 = it++;
// Clean up orphaned keys (descriptor gone but never expired)
if (it2->second->m_dwExpireTime == 0 && it2->second->m_pkDesc == NULL)
{
M2_DELETE(it2->second);

View File

@@ -67,8 +67,6 @@ class DESC_MANAGER : public singleton<DESC_MANAGER>
CLIENT_DESC_SET m_set_pkClientDesc;
DESC_SET m_set_pkDesc;
std::unordered_map<std::string, int> m_map_ipConnCount;
DESC_HANDLE_MAP m_map_handle;
//DESC_ACCOUNTID_MAP m_AccountIDMap;
DESC_LOGINNAME_MAP m_map_loginName;

View File

@@ -1,227 +0,0 @@
#include "stdafx.h"
#include "config.h"
#include "firewall_manager.h"
#ifndef OS_WINDOWS
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <signal.h>
#include <sys/wait.h>
#endif
extern bool g_bFirewallEnable;
extern int g_iFirewallTcpSynLimit;
extern int g_iFirewallTcpSynBurst;
FirewallManager::FirewallManager() = default;
FirewallManager::~FirewallManager() = default;
#ifdef OS_WINDOWS
// Windows: no-op stubs
bool FirewallManager::Initialize(WORD, WORD) { return false; }
void FirewallManager::Destroy() {}
#else
bool FirewallManager::RunCommand(const char* fmt, ...)
{
char cmd[512];
va_list args;
va_start(args, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, args);
va_end(args);
// Temporarily set SIGCHLD to SIG_DFL before calling system().
// The game server sets SIGCHLD to SIG_IGN (auto-reap children), which
// causes waitpid() inside system() to return -1/ECHILD on FreeBSD
// because the child gets reaped before status can be collected.
struct sigaction saved, dflt;
memset(&dflt, 0, sizeof(dflt));
dflt.sa_handler = SIG_DFL;
sigemptyset(&dflt.sa_mask);
sigaction(SIGCHLD, &dflt, &saved);
int status = system(cmd);
sigaction(SIGCHLD, &saved, NULL);
if (status == -1)
{
sys_err("FirewallManager: system() failed for: %s", cmd);
return false;
}
return (WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
#ifdef OS_FREEBSD
// ---------------------------------------------------------------
// FreeBSD: ipfw
// Uses deterministic rule numbers based on port to avoid conflicts
// between multiple game processes on the same machine.
// Rule base = 50000 + (gamePort % 1000) * 10
// ---------------------------------------------------------------
void FirewallManager::CleanupRules()
{
if (m_ruleBase == 0)
return;
for (int i = 0; i < 10; ++i)
RunCommand("/sbin/ipfw -q delete %d 2>/dev/null", m_ruleBase + i);
m_ruleCount = 0;
}
bool FirewallManager::Initialize(WORD gamePort, WORD p2pPort)
{
if (!g_bFirewallEnable)
return false;
m_ruleBase = 50000 + (gamePort % 1000) * 10;
// Clean up stale rules from previous crash
CleanupRules();
// Test if ipfw is available (use absolute path — popen() may have minimal PATH)
if (!RunCommand("/sbin/ipfw -q list >/dev/null 2>&1"))
{
sys_err("FirewallManager: ipfw not available (module not loaded? try: kldload ipfw)");
m_ruleBase = 0;
return false;
}
int r = m_ruleBase;
// Drop inbound UDP to game and P2P ports
RunCommand("/sbin/ipfw -q add %d deny udp from any to me dst-port %d in", r++, gamePort);
RunCommand("/sbin/ipfw -q add %d deny udp from any to me dst-port %d in", r++, p2pPort);
// Drop ICMP port-unreachable (type 3)
RunCommand("/sbin/ipfw -q add %d deny icmp from any to me icmptypes 3 in", r++);
// TCP SYN rate limiting on game port (per-source concurrent connection limit)
RunCommand("/sbin/ipfw -q add %d allow tcp from any to me dst-port %d setup limit src-addr %d",
r++, gamePort, g_iFirewallTcpSynLimit);
RunCommand("/sbin/ipfw -q add %d deny tcp from any to me dst-port %d setup",
r++, gamePort);
// TCP SYN rate limiting on P2P port
RunCommand("/sbin/ipfw -q add %d allow tcp from any to me dst-port %d setup limit src-addr %d",
r++, p2pPort, g_iFirewallTcpSynLimit);
RunCommand("/sbin/ipfw -q add %d deny tcp from any to me dst-port %d setup",
r++, p2pPort);
m_ruleCount = r - m_ruleBase;
m_initialized = true;
sys_log(0, "FirewallManager: ipfw rules %d-%d installed (UDP DROP ports %d/%d, TCP SYN limit %d/src)",
m_ruleBase, r - 1, gamePort, p2pPort, g_iFirewallTcpSynLimit);
return true;
}
#else
// ---------------------------------------------------------------
// Linux: iptables
// Uses a dedicated chain per process (e.g., M2_GUARD_11011)
// ---------------------------------------------------------------
void FirewallManager::CleanupRules()
{
if (m_chainName.empty())
return;
// Unhook from INPUT (ignore failure — may not exist)
RunCommand("iptables -D INPUT -j %s 2>/dev/null", m_chainName.c_str());
// Flush and delete the chain (ignore failure)
RunCommand("iptables -F %s 2>/dev/null", m_chainName.c_str());
RunCommand("iptables -X %s 2>/dev/null", m_chainName.c_str());
}
bool FirewallManager::Initialize(WORD gamePort, WORD p2pPort)
{
if (!g_bFirewallEnable)
return false;
// Chain name includes port to avoid conflicts with multi-channel servers
char buf[64];
snprintf(buf, sizeof(buf), "M2_GUARD_%d", gamePort);
m_chainName = buf;
// Always clean up stale rules first (handles crash recovery)
CleanupRules();
// Create fresh chain
if (!RunCommand("iptables -N %s", m_chainName.c_str()))
{
sys_err("FirewallManager: failed to create chain %s (not root? iptables not installed?)", m_chainName.c_str());
m_chainName.clear();
return false;
}
// Allow established/related UDP (e.g. DNS replies from outbound queries)
RunCommand("iptables -A %s -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT",
m_chainName.c_str());
// DROP all unsolicited inbound UDP
RunCommand("iptables -A %s -p udp -j DROP", m_chainName.c_str());
// Rate-limit ICMP to prevent reflection attacks
RunCommand("iptables -A %s -p icmp --icmp-type port-unreachable -j DROP",
m_chainName.c_str());
RunCommand("iptables -A %s -p icmp -m limit --limit 10/s --limit-burst 20 -j ACCEPT",
m_chainName.c_str());
RunCommand("iptables -A %s -p icmp -j DROP", m_chainName.c_str());
// TCP SYN flood protection on game port
RunCommand("iptables -A %s -p tcp --dport %d --syn -m limit --limit %d/s --limit-burst %d -j ACCEPT",
m_chainName.c_str(), gamePort, g_iFirewallTcpSynLimit, g_iFirewallTcpSynBurst);
RunCommand("iptables -A %s -p tcp --dport %d --syn -j DROP",
m_chainName.c_str(), gamePort);
// TCP SYN flood protection on P2P port
RunCommand("iptables -A %s -p tcp --dport %d --syn -m limit --limit %d/s --limit-burst %d -j ACCEPT",
m_chainName.c_str(), p2pPort, g_iFirewallTcpSynLimit, g_iFirewallTcpSynBurst);
RunCommand("iptables -A %s -p tcp --dport %d --syn -j DROP",
m_chainName.c_str(), p2pPort);
// Hook chain into INPUT
if (!RunCommand("iptables -A INPUT -j %s", m_chainName.c_str()))
{
sys_err("FirewallManager: failed to hook chain into INPUT");
CleanupRules();
m_chainName.clear();
return false;
}
m_initialized = true;
sys_log(0, "FirewallManager: chain %s installed (UDP DROP, TCP SYN limit %d/s burst %d)",
m_chainName.c_str(), g_iFirewallTcpSynLimit, g_iFirewallTcpSynBurst);
return true;
}
#endif // OS_FREEBSD
void FirewallManager::Destroy()
{
if (!m_initialized)
return;
CleanupRules();
m_initialized = false;
#ifdef OS_FREEBSD
sys_log(0, "FirewallManager: ipfw rules %d-%d removed", m_ruleBase, m_ruleBase + m_ruleCount - 1);
m_ruleBase = 0;
m_ruleCount = 0;
#else
sys_log(0, "FirewallManager: chain %s removed", m_chainName.c_str());
m_chainName.clear();
#endif
}
#endif // !OS_WINDOWS

View File

@@ -1,33 +0,0 @@
#ifndef __INC_FIREWALL_MANAGER_H__
#define __INC_FIREWALL_MANAGER_H__
// Kernel-level firewall management.
// Linux: iptables chains. FreeBSD: ipfw rules.
// Installs DROP rules for unsolicited UDP and rate-limits TCP SYN floods.
// Windows: no-op stubs.
class FirewallManager : public singleton<FirewallManager>
{
public:
FirewallManager();
~FirewallManager();
// Install firewall rules — call after config_init()
bool Initialize(WORD gamePort, WORD p2pPort);
// Remove firewall rules — call during shutdown
void Destroy();
private:
#ifndef OS_WINDOWS
bool RunCommand(const char* fmt, ...);
void CleanupRules();
#endif
std::string m_chainName; // Linux: iptables chain name
int m_ruleBase = 0; // FreeBSD: ipfw rule number base
int m_ruleCount = 0; // FreeBSD: number of rules installed
bool m_initialized = false;
};
#endif

View File

@@ -131,12 +131,6 @@ bool CInputProcessor::Process(LPDESC lpDesc, const void * c_pvOrig, int iBytes,
if (wHeader)
{
if (lpDesc->CheckPacketFlood())
{
lpDesc->SetPhase(PHASE_CLOSE);
return true;
}
m_pPacketInfo->Start();
int iExtraPacketSize = Analyze(lpDesc, wHeader, c_pData);

View File

@@ -128,7 +128,6 @@ class CInputMain : public CInputProcessor
protected:
int Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;
// Handler dispatch
using MainHandler = int (CInputMain::*)(LPDESC, const char*);
struct HandlerEntry {
MainHandler handler;
@@ -137,14 +136,12 @@ class CInputMain : public CInputProcessor
std::unordered_map<uint16_t, HandlerEntry> m_handlers;
virtual void RegisterHandlers();
// Template adapters for common handler patterns (defined in input_main.cpp)
template<void (CInputMain::*fn)(LPCHARACTER, const char*)>
int SimpleHandler(LPDESC d, const char* p);
template<void (CInputMain::*fn)(LPCHARACTER, const void*)>
int SimpleHandlerV(LPDESC d, const char* p);
// Custom adapters for non-standard handler signatures
int HandlePong(LPDESC d, const char* p);
int HandleChat(LPDESC d, const char* p);
int HandleWhisper(LPDESC d, const char* p);
@@ -262,19 +259,14 @@ protected:
void RegisterHandlers();
LPDESC FindByHandle() const;
// Template: void handler(const char*) — data-only
template<void (CInputDB::*fn)(const char*)>
int DataHandler(LPDESC, const char* p) { (this->*fn)(p); return 0; }
// Template: void handler(LPDESC, const char*) — desc from FindByHandle
template<void (CInputDB::*fn)(LPDESC, const char*)>
int DescHandler(LPDESC, const char* p) { (this->*fn)(FindByHandle(), p); return 0; }
// Template: void handler(T*) — cast c_pData to typed pointer
template<class T, void (CInputDB::*fn)(T*)>
int TypedHandler(LPDESC, const char* p) { (this->*fn)((T*)p); return 0; }
// Custom adapters for special signatures
int HandleLoginSuccess(LPDESC, const char*);
int HandleLoginNotExist(LPDESC, const char*);
int HandleLoginWrongPasswd(LPDESC, const char*);

View File

@@ -1729,7 +1729,6 @@ void CInputMain::Attack(LPCHARACTER ch, const uint16_t header, const char* data)
if (NULL == ch)
return;
// Updated for new packet format: [header:2][length:2][bType:1]
struct type_identifier
{
uint16_t header;
@@ -2673,7 +2672,7 @@ int CInputMain::Guild(LPCHARACTER ch, const char * data, size_t uiBytes)
}
// ---------------------------------------------------------------------------
// Guild sub-handlers — extracted from Guild() switch cases
// Guild sub-handlers
// ---------------------------------------------------------------------------
int CInputMain::GuildSub_DepositMoney(LPCHARACTER ch, const char* data, size_t uiBytes)
@@ -3214,7 +3213,7 @@ CInputMain::CInputMain()
RegisterHandlers();
}
// Custom adapter methods — bridge varied handler signatures to unified MainHandler
// Custom adapter methods
int CInputMain::HandlePong(LPDESC d, const char*)
{
@@ -3391,7 +3390,7 @@ void CInputMain::RegisterHandlers()
reg(CG::CLIENT_VERSION, &CInputMain::HandleClientVersion);
reg(CG::DRAGON_SOUL_REFINE,&CInputMain::HandleDragonSoulRefine);
// Simple void(LPCHARACTER, const char*) — via template adapter
// SimpleHandler<fn>(LPCHARACTER, const char*)
reg(CG::CHARACTER_POSITION,&CInputMain::SimpleHandler<&CInputMain::Position>);
reg(CG::ITEM_USE, &CInputMain::SimpleHandler<&CInputMain::ItemUse>, true);
reg(CG::ITEM_DROP, &CInputMain::SimpleHandler<&CInputMain::ItemDrop>, true);
@@ -3421,7 +3420,7 @@ void CInputMain::RegisterHandlers()
reg(CG::HACK, &CInputMain::SimpleHandler<&CInputMain::Hack>);
reg(CG::REFINE, &CInputMain::SimpleHandler<&CInputMain::Refine>);
// void(LPCHARACTER, const void*) — via template adapter
// SimpleHandlerV<fn>(LPCHARACTER, const void*)
reg(CG::SCRIPT_ANSWER, &CInputMain::SimpleHandlerV<&CInputMain::ScriptAnswer>);
reg(CG::SCRIPT_BUTTON, &CInputMain::SimpleHandlerV<&CInputMain::ScriptButton>);
reg(CG::SCRIPT_SELECT_ITEM, &CInputMain::SimpleHandlerV<&CInputMain::ScriptSelectItem>);
@@ -3429,8 +3428,6 @@ void CInputMain::RegisterHandlers()
reg(CG::QUEST_CONFIRM, &CInputMain::SimpleHandlerV<&CInputMain::QuestConfirm>);
}
// ---------------------------------------------------------------------------
// Table-driven Analyze — replaces switch statement
// ---------------------------------------------------------------------------
int CInputMain::Analyze(LPDESC d, uint16_t wHeader, const char * c_pData)
@@ -3458,7 +3455,7 @@ int CInputMain::Analyze(LPDESC d, uint16_t wHeader, const char * c_pData)
}
// ---------------------------------------------------------------------------
// CInputDead — only allows PONG, CHAT, WHISPER, HACK
// CInputDead
// ---------------------------------------------------------------------------
CInputDead::CInputDead()

View File

@@ -54,7 +54,6 @@
#include "DragonLair.h"
#include "skill_power.h"
#include "DragonSoul.h"
#include "firewall_manager.h"
// #ifndef OS_WINDOWS
// #include <gtest/gtest.h>
@@ -89,11 +88,6 @@ BYTE g_bLogLevel = 0;
socket_t tcp_socket = 0;
socket_t p2p_socket = 0;
// UDP sink sockets — bound but never read, prevents kernel ICMP port-unreachable generation
// during UDP floods (kernel silently drops once the tiny receive buffer fills)
static socket_t udp_sink_game = INVALID_SOCKET;
static socket_t udp_sink_p2p = INVALID_SOCKET;
LPFDWATCH main_fdw = NULL;
int io_loop(LPFDWATCH fdw);
@@ -347,7 +341,6 @@ int main(int argc, char **argv)
CThreeWayWar threeway_war;
CDragonLairManager dl_manager;
DSManager dsManager;
FirewallManager firewall_manager;
if (!start(argc, argv)) {
CleanUpForEarlyExit();
@@ -421,8 +414,6 @@ int main(int argc, char **argv)
char_manager.Destroy();
sys_log(0, "<shutdown> Destroying ITEM_MANAGER...");
item_manager.Destroy();
sys_log(0, "<shutdown> Destroying FirewallManager...");
firewall_manager.Destroy();
sys_log(0, "<shutdown> Destroying DESC_MANAGER...");
desc_manager.Destroy();
sys_log(0, "<shutdown> Destroying quest::CQuestManager...");
@@ -440,49 +431,6 @@ int main(int argc, char **argv)
return 1;
}
// Create a UDP sink socket: bind to ip:port with minimal receive buffer, never read.
// Prevents ICMP port-unreachable replies during UDP floods — the kernel silently drops
// packets once the tiny buffer fills. Returns INVALID_SOCKET on failure (non-fatal).
static socket_t create_udp_sink(const char* ip, WORD port)
{
#ifdef OS_WINDOWS
(void)ip; (void)port;
return INVALID_SOCKET;
#else
socket_t s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
sys_err("create_udp_sink: socket() failed for port %d: %s", port, strerror(errno));
return INVALID_SOCKET;
}
// Allow rebind if previous instance didn't clean up
int reuse = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
// Minimal receive buffer — kernel rounds up to its minimum (typically 256 bytes on Linux).
// Once full, incoming UDP is silently dropped with zero CPU overhead.
int rcvbuf = 1;
setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
sys_err("create_udp_sink: bind() failed for port %d: %s", port, strerror(errno));
close(s);
return INVALID_SOCKET;
}
sys_log(0, "UDP sink bound on %s:%d (fd %d) — ICMP suppression active", ip, port, s);
return s;
#endif
}
void usage()
{
printf("Option list\n"
@@ -613,13 +561,6 @@ int start(int argc, char **argv)
fdwatch_add_fd(main_fdw, tcp_socket, NULL, FDW_READ, false);
fdwatch_add_fd(main_fdw, p2p_socket, NULL, FDW_READ, false);
// UDP sink sockets — suppress ICMP port-unreachable during UDP floods
udp_sink_game = create_udp_sink(g_szPublicIP, mother_port);
udp_sink_p2p = create_udp_sink(g_szPublicIP, p2p_port);
// Kernel-level firewall — DROP unsolicited UDP + rate-limit TCP SYN at netfilter layer
FirewallManager::instance().Initialize(mother_port, p2p_port);
db_clientdesc = DESC_MANAGER::instance().CreateConnectionDesc(main_fdw, db_addr, db_port, PHASE_DBCLIENT, true);
if (!g_bAuthServer) {
db_clientdesc->UpdateChannelStatus(0, true);
@@ -669,9 +610,6 @@ void destroy()
socket_close(tcp_socket);
socket_close(p2p_socket);
if (udp_sink_game != INVALID_SOCKET) { socket_close(udp_sink_game); udp_sink_game = INVALID_SOCKET; }
if (udp_sink_p2p != INVALID_SOCKET) { socket_close(udp_sink_p2p); udp_sink_p2p = INVALID_SOCKET; }
sys_log(0, "<shutdown> fdwatch_delete()...");
fdwatch_delete(main_fdw);

View File

@@ -128,8 +128,6 @@ public:
return v;
}
// --- Read struct (for backward compatibility during migration) ---
// Reads a struct directly via memcpy. Use sparingly — prefer typed reads.
template<typename T>
bool ReadStruct(T& out)
{