Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions game/addons/sourcemod/configs/sourcebans/sourcebans.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@

// This is the ID of this server (Check in the admin panel -> servers to find the ID of this server)
"ServerID" "-1"

// Public/NAT IPv4 address of this server as registered in the web panel (e.g. "1.2.3.4").
// Set this when the server runs behind NAT (e.g. in a container) and the
// auto-detected "hostip" is a private LAN address that will never match
// the IP stored in the web panel. Leave empty to use the auto-detected IP.
// An invalid value is ignored (logged as an error) and the auto-detected IP is used.
// Note: the port is always taken from the "hostport" cvar, so if your NAT also
// remaps the port, the web panel entry must use the server's internal port.
"ServerIP" ""
}

/*
Expand Down
79 changes: 79 additions & 0 deletions game/addons/sourcemod/scripting/sbpp_comms.sp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Database SQLiteDB;

char
ServerIp[24]
, ServerIpOverride[24] /* Optional public/NAT IP from sourcebans.cfg */
, ServerPort[7]
, DatabasePrefix[10] = "sb"
#if defined LOG_QUERIES
Expand Down Expand Up @@ -2029,6 +2030,26 @@ public SMCResult ReadConfig_KeyValue(SMCParser smc, const char[] key, const char
RetryTime = 60.0;
}
}
else if (strcmp("ServerIP", key, false) == 0)
{
char ipValue[64];
strcopy(ipValue, sizeof(ipValue), value);
TrimString(ipValue);

ServerIpOverride[0] = '\0';

if (ipValue[0] != '\0')
{
if (IsValidServerIp(ipValue))
{
strcopy(ServerIpOverride, sizeof(ServerIpOverride), ipValue);
}
else
{
LogError("Invalid \"ServerIP\" value \"%s\" in sourcebans.cfg, falling back to the auto-detected server IP", value);
}
}
}
else if (strcmp("ServerID", key, false) == 0)
{
serverID = StringToInt(value);
Expand Down Expand Up @@ -2794,8 +2815,54 @@ stock void InsertTempBlock(int length, int type, const char[] name, const char[]
SQLiteDB.Query(Query_ErrorCheck, sQuery);
}

/**
* Validates that the given string is a dotted-quad IPv4 address.
*/
stock bool IsValidServerIp(const char[] ip)
{
int octets = 0, value = 0, digits = 0;

for (int i = 0; ; i++)
{
if (ip[i] == '.' || ip[i] == '\0')
{
if (digits == 0 || value > 255)
return false;

octets++;

if (ip[i] == '\0')
break;

if (octets == 4)
return false;

value = 0;
digits = 0;
}
else if (ip[i] >= '0' && ip[i] <= '9')
{
if (++digits > 3)
return false;

value = value * 10 + (ip[i] - '0');
}
else
{
return false;
}
}

return (octets == 4);
}

stock void ServerInfo()
{
if (CvarHostIp == null || CvarPort == null)
{
return;
}

int pieces[4];
int longip = CvarHostIp.IntValue;
pieces[0] = (longip >> 24) & 0x000000FF;
Expand All @@ -2804,6 +2871,12 @@ stock void ServerInfo()
pieces[3] = longip & 0x000000FF;
FormatEx(ServerIp, sizeof(ServerIp), "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
CvarPort.GetString(ServerPort, sizeof(ServerPort));

// Prefer the public/NAT IP configured in sourcebans.cfg over the auto-detected hostip.
if (ServerIpOverride[0] != '\0')
{
strcopy(ServerIp, sizeof(ServerIp), ServerIpOverride);
}
}

stock void ReadConfig()
Expand All @@ -2816,13 +2889,19 @@ stock void ReadConfig()
}

char ConfigFile1[PLATFORM_MAX_PATH], ConfigFile2[PLATFORM_MAX_PATH];

// Reset so a removed/emptied "ServerIP" key does not keep a stale override on reload.
ServerIpOverride[0] = '\0';

BuildPath(Path_SM, ConfigFile1, sizeof(ConfigFile1), "configs/sourcebans/sourcebans.cfg");
BuildPath(Path_SM, ConfigFile2, sizeof(ConfigFile2), "configs/sourcebans/sourcecomms.cfg");

if (FileExists(ConfigFile1))
{
PrintToServer("%sLoading configs/sourcebans/sourcebans.cfg config file", PREFIX);
InternalReadConfig(ConfigFile1);
// Re-apply the (possibly changed) "ServerIP" override on top of the auto-detected hostip.
ServerInfo();
}
else
{
Expand Down
101 changes: 98 additions & 3 deletions game/addons/sourcemod/scripting/sbpp_main.sp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Database SQLiteDB;

char
ServerIp[24]
, ServerIpOverride[24] /* Optional public/NAT IP from sourcebans.cfg */
, ServerPort[7]
, DatabasePrefix[10] = "sb"
, WebsiteAddress[128]
Expand All @@ -97,6 +98,7 @@ bool
, requireSiteLogin = false /* Require a lastvisited from SB site */
, backupConfig = true
, enableAdmins = true
, ConfigLoaded = false /* sourcebans.cfg has been parsed at least once */
, PlayerStatus[MAXPLAYERS + 1]; /* Player ban check status */

int
Expand Down Expand Up @@ -1083,6 +1085,13 @@ public void GotDatabase(Database db, const char[] error, any data)
DB.Query(ErrorCheckCallback, query);
}

// The database can be ready before the first ReadConfig() (OnMapStart), and every
// query below depends on config values (DatabasePrefix, AutoAdd, ServerIP override).
if (!ConfigLoaded)
{
ResetSettings();
}

InsertServerInfo();

//CreateTimer(900.0, PruneBans);
Expand Down Expand Up @@ -2257,6 +2266,26 @@ public SMCResult ReadConfig_KeyValue(SMCParser smc, const char[] key, const char
CommandDisable |= DISABLE_ADDBAN;
}
}
else if (strcmp("ServerIP", key, false) == 0)
{
char ipValue[64];
strcopy(ipValue, sizeof(ipValue), value);
TrimString(ipValue);

ServerIpOverride[0] = '\0';

if (ipValue[0] != '\0')
{
if (IsValidServerIp(ipValue))
{
strcopy(ServerIpOverride, sizeof(ServerIpOverride), ipValue);
}
else
{
LogError("Invalid \"ServerIP\" value \"%s\" in sourcebans.cfg, falling back to the auto-detected server IP", value);
}
}
}
else if (strcmp("AutoAddServer", key, false) == 0)
{
int sAutoAdd = StringToInt(value);
Expand Down Expand Up @@ -2647,13 +2676,57 @@ stock void CheckLoadAdmins(AdminCachePart part)
}
}

stock void InsertServerInfo()
/**
* Validates that the given string is a dotted-quad IPv4 address.
*/
stock bool IsValidServerIp(const char[] ip)
{
if (DB == INVALID_HANDLE) {
int octets = 0, value = 0, digits = 0;

for (int i = 0; ; i++)
{
if (ip[i] == '.' || ip[i] == '\0')
{
if (digits == 0 || value > 255)
return false;

octets++;

if (ip[i] == '\0')
break;

if (octets == 4)
return false;

value = 0;
digits = 0;
}
else if (ip[i] >= '0' && ip[i] <= '9')
{
if (++digits > 3)
return false;

value = value * 10 + (ip[i] - '0');
}
else
{
return false;
}
}

return (octets == 4);
}

/**
* Refreshes ServerIp/ServerPort from the game cvars, applying the optional
* "ServerIP" override from sourcebans.cfg (public/NAT address).
*/
stock void UpdateServerIp()
{
if (CvarHostIp == null || CvarPort == null) {
return;
}

char query[100];
int pieces[4];
int longip = CvarHostIp.IntValue;

Expand All @@ -2665,6 +2738,23 @@ stock void InsertServerInfo()
FormatEx(ServerIp, sizeof(ServerIp), "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
CvarPort.GetString(ServerPort, sizeof(ServerPort));

// Prefer the public/NAT IP configured in sourcebans.cfg over the auto-detected hostip.
if (ServerIpOverride[0] != '\0')
{
strcopy(ServerIp, sizeof(ServerIp), ServerIpOverride);
}
}

stock void InsertServerInfo()
{
if (DB == INVALID_HANDLE) {
return;
}

char query[100];

UpdateServerIp();

if (AutoAdd != AUTO_ADD_SERVER_DISABLED) {
FormatEx(query, sizeof(query), "SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s'", DatabasePrefix, ServerIp, ServerPort);
DB.Query(ServerInfoCallback, query);
Expand Down Expand Up @@ -2733,9 +2823,14 @@ stock void ReadConfig()
char ConfigFile[PLATFORM_MAX_PATH];
BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg");

// Reset so a removed/emptied "ServerIP" key does not keep a stale override on reload.
ServerIpOverride[0] = '\0';

if (FileExists(ConfigFile))
{
ConfigLoaded = true;
InternalReadConfig(ConfigFile);
UpdateServerIp();
PrintToServer("%sLoading configs/sourcebans.cfg config file", Prefix);
} else {
char Error[PLATFORM_MAX_PATH + 64];
Expand Down
Loading