Merge branch 'develop' into password-mongoose
This commit is contained in:
commit
a62479816d
@ -144,6 +144,7 @@ namespace Components
|
|||||||
Proto::Auth::Connect connectData;
|
Proto::Auth::Connect connectData;
|
||||||
if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(reinterpret_cast<char*>(&msg->data[12]), msg->cursize - 12)))
|
if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(reinterpret_cast<char*>(&msg->data[12]), msg->cursize - 12)))
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nInvalid connect packet!");
|
Network::Send(address, "error\nInvalid connect packet!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -161,6 +162,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nInvalid infostring data!");
|
Network::Send(address, "error\nInvalid infostring data!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,6 +172,7 @@ namespace Components
|
|||||||
// Validate proto data
|
// Validate proto data
|
||||||
if (connectData.signature().empty() || connectData.publickey().empty() || connectData.token().empty() || connectData.infostring().empty())
|
if (connectData.signature().empty() || connectData.publickey().empty() || connectData.token().empty() || connectData.infostring().empty())
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nInvalid connect data!");
|
Network::Send(address, "error\nInvalid connect data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -184,6 +187,7 @@ namespace Components
|
|||||||
// Ensure there are enough params
|
// Ensure there are enough params
|
||||||
if (params.size() < 3)
|
if (params.size() < 3)
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nInvalid connect string!");
|
Network::Send(address, "error\nInvalid connect string!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -197,6 +201,7 @@ namespace Components
|
|||||||
|
|
||||||
if (steamId.empty() || challenge.empty())
|
if (steamId.empty() || challenge.empty())
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nInvalid connect data!");
|
Network::Send(address, "error\nInvalid connect data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ namespace Components
|
|||||||
std::recursive_mutex Logger::LoggingMutex;
|
std::recursive_mutex Logger::LoggingMutex;
|
||||||
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
||||||
|
|
||||||
Dvar::Var Logger::IW4x_oneLog;
|
Dvar::Var Logger::IW4x_one_log;
|
||||||
|
Dvar::Var Logger::IW4x_fail2ban_location;
|
||||||
|
|
||||||
void(*Logger::PipeCallback)(const std::string&) = nullptr;;
|
void(*Logger::PipeCallback)(const std::string&) = nullptr;;
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ namespace Components
|
|||||||
|
|
||||||
if (shouldPrint)
|
if (shouldPrint)
|
||||||
{
|
{
|
||||||
std::printf("%s", msg.data());
|
(void)std::fputs(msg.data(), stdout);
|
||||||
std::fflush(stdout);
|
(void)std::fflush(stdout);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +117,38 @@ namespace Components
|
|||||||
MessagePrint(channel, msg);
|
MessagePrint(channel, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Logger::PrintFail2BanInternal(const std::string_view& fmt, std::format_args&& args)
|
||||||
|
{
|
||||||
|
static const auto shouldPrint = []() -> bool
|
||||||
|
{
|
||||||
|
return Flags::HasFlag("fail2ban");
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!shouldPrint)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto msg = std::vformat(fmt, args);
|
||||||
|
|
||||||
|
static auto log_next_time_stamp = true;
|
||||||
|
if (log_next_time_stamp)
|
||||||
|
{
|
||||||
|
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||||
|
// Convert to local time
|
||||||
|
std::tm timeInfo = *std::localtime(&now);
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << std::put_time(&timeInfo, "%Y-%m-%d %H:%M:%S ");
|
||||||
|
|
||||||
|
msg.insert(0, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
log_next_time_stamp = (msg.find('\n') != std::string::npos);
|
||||||
|
|
||||||
|
Utils::IO::WriteFile(IW4x_fail2ban_location.get<std::string>(), msg, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Logger::Frame()
|
void Logger::Frame()
|
||||||
{
|
{
|
||||||
std::unique_lock _(MessageMutex);
|
std::unique_lock _(MessageMutex);
|
||||||
@ -233,7 +266,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (std::strcmp(folder, "userraw") != 0)
|
if (std::strcmp(folder, "userraw") != 0)
|
||||||
{
|
{
|
||||||
if (IW4x_oneLog.get<bool>())
|
if (IW4x_one_log.get<bool>())
|
||||||
{
|
{
|
||||||
strncpy_s(folder, 256, "userraw", _TRUNCATE);
|
strncpy_s(folder, 256, "userraw", _TRUNCATE);
|
||||||
}
|
}
|
||||||
@ -388,7 +421,6 @@ namespace Components
|
|||||||
|
|
||||||
Logger::Logger()
|
Logger::Logger()
|
||||||
{
|
{
|
||||||
IW4x_oneLog = Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH, "Only write the game log to the 'userraw' OS folder");
|
|
||||||
Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER);
|
Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER);
|
||||||
@ -405,6 +437,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Events::OnSVInit(AddServerCommands);
|
Events::OnSVInit(AddServerCommands);
|
||||||
|
Events::OnDvarInit([]
|
||||||
|
{
|
||||||
|
IW4x_one_log = Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH, "Only write the game log to the 'userraw' OS folder");
|
||||||
|
IW4x_fail2ban_location = Dvar::Register<const char*>("iw4x_fail2ban_location", "/var/log/iw4x.log", Game::DVAR_NONE, "Fail2Ban logfile location");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::~Logger()
|
Logger::~Logger()
|
||||||
|
@ -18,6 +18,7 @@ namespace Components
|
|||||||
static void ErrorInternal(Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args);
|
static void ErrorInternal(Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
static void PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
static void WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
||||||
|
static void PrintFail2BanInternal(const std::string_view& fmt, std::format_args&& args);
|
||||||
static void DebugInternal(const std::string_view& fmt, std::format_args&& args, const std::source_location& loc);
|
static void DebugInternal(const std::string_view& fmt, std::format_args&& args, const std::source_location& loc);
|
||||||
|
|
||||||
static void Print(const std::string_view& fmt)
|
static void Print(const std::string_view& fmt)
|
||||||
@ -80,6 +81,18 @@ namespace Components
|
|||||||
PrintErrorInternal(channel, fmt, std::make_format_args(args...));
|
PrintErrorInternal(channel, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PrintFail2Ban(const std::string_view& fmt)
|
||||||
|
{
|
||||||
|
PrintFail2BanInternal(fmt, std::make_format_args(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
static void PrintFail2Ban(const std::string_view& fmt, Args&&... args)
|
||||||
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
|
PrintFail2BanInternal(fmt, std::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
struct FormatWithLocation
|
struct FormatWithLocation
|
||||||
{
|
{
|
||||||
std::string_view format;
|
std::string_view format;
|
||||||
@ -114,7 +127,8 @@ namespace Components
|
|||||||
static std::recursive_mutex LoggingMutex;
|
static std::recursive_mutex LoggingMutex;
|
||||||
static std::vector<Network::Address> LoggingAddresses[2];
|
static std::vector<Network::Address> LoggingAddresses[2];
|
||||||
|
|
||||||
static Dvar::Var IW4x_oneLog;
|
static Dvar::Var IW4x_one_log;
|
||||||
|
static Dvar::Var IW4x_fail2ban_location;
|
||||||
|
|
||||||
static void(*PipeCallback)(const std::string&);
|
static void(*PipeCallback)(const std::string&);
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ namespace Components
|
|||||||
// TODO : Remove hook entirely?
|
// TODO : Remove hook entirely?
|
||||||
void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname)
|
void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname)
|
||||||
{
|
{
|
||||||
if (!Utils::String::StartsWith(mapname, "mp_"))
|
if (!Utils::String::StartsWith(mapname, "mp_") && !Utils::String::StartsWith(mapname, "zm_"))
|
||||||
{
|
{
|
||||||
format = "maps/%s.d3dbsp";
|
format = "maps/%s.d3dbsp";
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,11 @@ namespace Components
|
|||||||
return ntohs(this->address.port);
|
return ntohs(this->address.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned short Network::Address::getPortRaw() const
|
||||||
|
{
|
||||||
|
return this->address.port;
|
||||||
|
}
|
||||||
|
|
||||||
void Network::Address::setIP(DWORD ip)
|
void Network::Address::setIP(DWORD ip)
|
||||||
{
|
{
|
||||||
this->address.ip.full = ip;
|
this->address.ip.full = ip;
|
||||||
@ -151,6 +156,31 @@ namespace Components
|
|||||||
return (this->getType() != Game::NA_BAD && this->getType() >= Game::NA_BOT && this->getType() <= Game::NA_IP);
|
return (this->getType() != Game::NA_BAD && this->getType() >= Game::NA_BOT && this->getType() <= Game::NA_IP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Network::AdrToString(const Address& a, const bool port)
|
||||||
|
{
|
||||||
|
if (a.getType() == Game::netadrtype_t::NA_LOOPBACK)
|
||||||
|
{
|
||||||
|
return "loopback";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.getType() == Game::netadrtype_t::NA_BOT)
|
||||||
|
{
|
||||||
|
return "bot";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.getType() == Game::netadrtype_t::NA_IP || a.getType() == Game::netadrtype_t::NA_BROADCAST)
|
||||||
|
{
|
||||||
|
if (a.getPort() && port)
|
||||||
|
{
|
||||||
|
return Utils::String::VA("%u.%u.%u.%u:%u", a.getIP().bytes[0], a.getIP().bytes[1], a.getIP().bytes[2], a.getIP().bytes[3], htons(a.getPortRaw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils::String::VA("%u.%u.%u.%u", a.getIP().bytes[0], a.getIP().bytes[1], a.getIP().bytes[2], a.getIP().bytes[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "bad";
|
||||||
|
}
|
||||||
|
|
||||||
void Network::Send(Game::netsrc_t type, const Address& target, const std::string& data)
|
void Network::Send(Game::netsrc_t type, const Address& target, const std::string& data)
|
||||||
{
|
{
|
||||||
// Do not use NET_OutOfBandPrint. It only supports non-binary data!
|
// Do not use NET_OutOfBandPrint. It only supports non-binary data!
|
||||||
|
@ -22,6 +22,7 @@ namespace Components
|
|||||||
|
|
||||||
void setPort(unsigned short port);
|
void setPort(unsigned short port);
|
||||||
[[nodiscard]] unsigned short getPort() const;
|
[[nodiscard]] unsigned short getPort() const;
|
||||||
|
[[nodiscard]] unsigned short getPortRaw() const;
|
||||||
|
|
||||||
void setIP(DWORD ip);
|
void setIP(DWORD ip);
|
||||||
void setIP(Game::netIP_t ip);
|
void setIP(Game::netIP_t ip);
|
||||||
@ -51,6 +52,8 @@ namespace Components
|
|||||||
|
|
||||||
Network();
|
Network();
|
||||||
|
|
||||||
|
static const char* AdrToString(const Address& a, bool port = false);
|
||||||
|
|
||||||
static std::uint16_t GetPort();
|
static std::uint16_t GetPort();
|
||||||
|
|
||||||
// Send quake-styled binary data
|
// Send quake-styled binary data
|
||||||
|
@ -180,7 +180,8 @@ namespace Components
|
|||||||
const auto pos = data.find_first_of(' ');
|
const auto pos = data.find_first_of(' ');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon request from {}\n", address.getString());
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon request from {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +204,8 @@ namespace Components
|
|||||||
|
|
||||||
if (svPassword != password)
|
if (svPassword != password)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon password sent from {}\n", address.getString());
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon password sent from {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +215,7 @@ namespace Components
|
|||||||
if (RConLogRequests.get<bool>())
|
if (RConLogRequests.get<bool>())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Executing RCon request from {}: {}\n", address.getString(), command);
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Executing RCon request from {}: {}\n", Network::AdrToString(address), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::PipeOutput([](const std::string& output)
|
Logger::PipeOutput([](const std::string& output)
|
||||||
@ -318,6 +320,7 @@ namespace Components
|
|||||||
const auto time = Game::Sys_Milliseconds();
|
const auto time = Game::Sys_Milliseconds();
|
||||||
if (!IsRateLimitCheckDisabled() && !RateLimitCheck(address, time))
|
if (!IsRateLimitCheckDisabled() && !RateLimitCheck(address, time))
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,6 +344,7 @@ namespace Components
|
|||||||
const auto time = Game::Sys_Milliseconds();
|
const auto time = Game::Sys_Milliseconds();
|
||||||
if (!IsRateLimitCheckDisabled() && !RateLimitCheck(address, time))
|
if (!IsRateLimitCheckDisabled() && !RateLimitCheck(address, time))
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,13 +364,15 @@ namespace Components
|
|||||||
Proto::RCon::Command directive;
|
Proto::RCon::Command directive;
|
||||||
if (!directive.ParseFromString(data))
|
if (!directive.ParseFromString(data))
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_NETWORK, "Unable to parse secure command from {}\n", address.getString());
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_NETWORK, "Unable to parse secure command from {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Utils::Cryptography::RSA::VerifyMessage(key, directive.command(), directive.signature()))
|
if (!Utils::Cryptography::RSA::VerifyMessage(key, directive.command(), directive.signature()))
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_NETWORK, "RSA signature verification failed for message from {}\n", address.getString());
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(address));
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_NETWORK, "RSA signature verification failed for message from {}\n", Network::AdrToString(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if ((client->reliableSequence - client->reliableAcknowledge) < 0)
|
if ((client->reliableSequence - client->reliableAcknowledge) < 0)
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(client->header.netchan.remoteAddress));
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n",
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n",
|
||||||
client->name, client->reliableSequence, client->reliableAcknowledge);
|
client->name, client->reliableSequence, client->reliableAcknowledge);
|
||||||
client->reliableAcknowledge = client->reliableSequence;
|
client->reliableAcknowledge = client->reliableSequence;
|
||||||
|
@ -168,6 +168,7 @@ namespace Components
|
|||||||
voicePacket.dataSize = Game::MSG_ReadByte(msg);
|
voicePacket.dataSize = Game::MSG_ReadByte(msg);
|
||||||
if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA)
|
if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA)
|
||||||
{
|
{
|
||||||
|
Logger::PrintFail2Ban("Invalid packet from IP address: {}\n", Network::AdrToString(cl->header.netchan.remoteAddress));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Received invalid voice packet of size {} from {}\n", voicePacket.dataSize, cl->name);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Received invalid voice packet of size {} from {}\n", voicePacket.dataSize, cl->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user