diff --git a/bam.lua b/bam.lua
index f16f1d9de..32acb7aea 100644
--- a/bam.lua
+++ b/bam.lua
@@ -236,6 +236,7 @@ function build(settings)
versionserver = Compile(settings, Collect("src/versionsrv/*.cpp"))
masterserver = Compile(settings, Collect("src/mastersrv/*.cpp"))
+ banmaster = Compile(settings, Collect("src/banmaster/*.cpp"))
game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash, network_source)
game_client = Compile(settings, CollectRecursive("src/game/client/*.cpp"), client_content_source)
game_server = Compile(settings, CollectRecursive("src/game/server/*.cpp"), server_content_source)
@@ -276,6 +277,9 @@ function build(settings)
masterserver_exe = Link(server_settings, "mastersrv", masterserver,
engine, zlib)
+ banmaster_exe = Link(server_settings, "banmaster", banmaster,
+ engine, zlib)
+
-- make targets
c = PseudoTarget("client".."_"..settings.config_name, client_exe, client_depends)
if string.find(settings.config_name, "nosql") then
@@ -295,9 +299,10 @@ function build(settings)
v = PseudoTarget("versionserver".."_"..settings.config_name, versionserver_exe)
m = PseudoTarget("masterserver".."_"..settings.config_name, masterserver_exe)
+ b = PseudoTarget("banmaster".."_"..settings.config_name, banmaster_exe)
t = PseudoTarget("tools".."_"..settings.config_name, tools)
- all = PseudoTarget(settings.config_name, c, s, v, m, t)
+ all = PseudoTarget(settings.config_name, c, s, v, m, b, t)
return all
end
diff --git a/banmasters.cfg b/banmasters.cfg
new file mode 100644
index 000000000..2d9434824
--- /dev/null
+++ b/banmasters.cfg
@@ -0,0 +1,2 @@
+clear_banmasters
+add_banmaster banmaster.kottnet.net
diff --git a/bans.cfg b/bans.cfg
new file mode 100644
index 000000000..a32469c65
--- /dev/null
+++ b/bans.cfg
@@ -0,0 +1 @@
+unban_all
diff --git a/scripts/make_release.py b/scripts/make_release.py
index b72b2d8e6..c0ea0d52c 100644
--- a/scripts/make_release.py
+++ b/scripts/make_release.py
@@ -64,6 +64,7 @@ shutil.copy("license.txt", package_dir)
shutil.copy("storage.cfg", package_dir)
shutil.copy("announcement.txt", package_dir)
shutil.copy("license_DDRace.txt", package_dir)
+shutil.copy("banmasters.cfg", package_dir)
if include_data and not use_bundle:
os.mkdir(os.path.join(package_dir, "data"))
diff --git a/src/banmaster/banmaster.cpp b/src/banmaster/banmaster.cpp
new file mode 100644
index 000000000..9aee3e2a1
--- /dev/null
+++ b/src/banmaster/banmaster.cpp
@@ -0,0 +1,250 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com. */
+#include
+#include
+#include
+#include
+
+#include "banmaster.h"
+
+enum
+{
+ MAX_BANS=1024,
+ BAN_REREAD_TIME=300,
+ CFGFLAG_BANMASTER=1024,
+};
+
+static const char BANMASTER_BANFILE[] = "bans.cfg";
+
+struct CBan
+{
+ NETADDR m_Address;
+ char m_aReason[256];
+ int64 m_Expire;
+};
+
+static CBan m_aBans[MAX_BANS];
+static int m_NumBans = 0;
+static CNetClient m_Net;
+static IConsole *m_pConsole;
+static char m_aBindAddr[64] = "";
+
+CBan* CheckBan(NETADDR *pCheck)
+{
+ for(int i = 0; i < m_NumBans; i++)
+ {
+ if(pCheck->ip[0] == m_aBans[i].m_Address.ip[0] && pCheck->ip[1] == m_aBans[i].m_Address.ip[1] &&
+ pCheck->ip[2] == m_aBans[i].m_Address.ip[2] && pCheck->ip[3] == m_aBans[i].m_Address.ip[3])
+ return &m_aBans[i];
+ }
+ return 0;
+}
+
+int SendResponse(NETADDR *pAddr, NETADDR *pCheck)
+{
+ static char aIpBan[sizeof(BANMASTER_IPBAN) + 32 + 256] = { 0 };
+ static char *pIpBanContent = aIpBan + sizeof(BANMASTER_IPBAN);
+ if (!aIpBan[0])
+ mem_copy(aIpBan, BANMASTER_IPBAN, sizeof(BANMASTER_IPBAN));
+
+ static CNetChunk p;
+
+ p.m_ClientID = -1;
+ p.m_Address = *pAddr;
+ p.m_Flags = NETSENDFLAG_CONNLESS;
+
+ CBan* pBan = CheckBan(pCheck);
+ if(pBan)
+ {
+ str_format(pIpBanContent, 32, "%d.%d.%d.%d", pCheck->ip[0], pCheck->ip[1], pCheck->ip[2], pCheck->ip[3]);
+ char *pIpBanReason = pIpBanContent + (str_length(pIpBanContent) + 1);
+ str_copy(pIpBanReason, pBan->m_aReason, 256);
+
+ p.m_pData = aIpBan;
+ p.m_DataSize = sizeof(BANMASTER_IPBAN) + str_length(pIpBanContent) + 1 + str_length(pIpBanReason) + 1;
+ m_Net.Send(&p);
+ return 1;
+ }
+ else
+ {
+ p.m_DataSize = sizeof(BANMASTER_IPOK);
+ p.m_pData = BANMASTER_IPOK;
+ m_Net.Send(&p);
+ return 0;
+ }
+}
+
+void AddBan(NETADDR *pAddr, const char *pReason)
+{
+ CBan *pBan = CheckBan(pAddr);
+ if(pBan)
+ {
+ dbg_msg("banmaster", "updated ban: %d.%d.%d.%d \'%s\' -> \'%s\'",
+ pAddr->ip[0], pAddr->ip[1], pAddr->ip[2], pAddr->ip[3], pBan->m_aReason, pReason);
+
+ str_copy(pBan->m_aReason, pReason, sizeof(m_aBans[m_NumBans].m_aReason));
+ pBan->m_Expire = -1;
+ }
+ else
+ {
+ if(m_NumBans == MAX_BANS)
+ {
+ dbg_msg("banmaster", "error: banmaster is full");
+ return;
+ }
+
+ m_aBans[m_NumBans].m_Address = *pAddr;
+ str_copy(m_aBans[m_NumBans].m_aReason, pReason, sizeof(m_aBans[m_NumBans].m_aReason));
+ m_aBans[m_NumBans].m_Expire = -1;
+
+ dbg_msg("banmaster", "added ban: %d.%d.%d.%d \'%s\'",
+ pAddr->ip[0], pAddr->ip[1], pAddr->ip[2], pAddr->ip[3], m_aBans[m_NumBans].m_aReason);
+
+ m_NumBans++;
+ }
+}
+
+void ClearBans()
+{
+ dbg_msg("banmaster", "cleared bans");
+ m_NumBans = 0;
+}
+
+void PurgeBans()
+{
+ int64 Now = time_get();
+ int i = 0;
+ while(i < m_NumBans)
+ {
+ if(m_aBans[i].m_Expire != -1 && m_aBans[i].m_Expire < Now)
+ {
+ // remove ban
+ dbg_msg("banmaster", "expired: %d.%d.%d.%d \'%s\'",
+ m_aBans[i].m_Address.ip[0], m_aBans[i].m_Address.ip[1],
+ m_aBans[i].m_Address.ip[2], m_aBans[i].m_Address.ip[3], m_aBans[i].m_aReason);
+ m_aBans[i] = m_aBans[m_NumBans-1];
+ m_NumBans--;
+ }
+ else
+ i++;
+ }
+}
+
+void ConBan(IConsole::IResult *pResult, void *pUser)
+{
+ NETADDR Addr;
+ const char *pStr = pResult->GetString(0);
+ const char *pReason = "";
+
+ if(pResult->NumArguments() > 1)
+ pReason = pResult->GetString(1);
+
+ if(!net_addr_from_str(&Addr, pStr))
+ AddBan(&Addr, pReason);
+ else
+ dbg_msg("banmaster", "invalid network address to ban");
+}
+
+void ConUnbanAll(IConsole::IResult *pResult, void *pUser)
+{
+ ClearBans();
+}
+
+void ConSetBindAddr(IConsole::IResult *pResult, void *pUser)
+{
+ if(m_aBindAddr[0])
+ return;
+ str_copy(m_aBindAddr, pResult->GetString(0), sizeof(m_aBindAddr));
+ dbg_msg("banmaster/network", "bound to %s", m_aBindAddr);
+}
+
+void StandardOutput(const char *pLine, void *pUser)
+{
+}
+
+int main(int argc, const char **argv) // ignore_convention
+{
+ int64 LastUpdate = time_get();
+
+ dbg_logger_stdout();
+ net_init();
+
+ IKernel *pKernel = IKernel::Create();
+ IStorage *pStorage = CreateStorage("Teeworlds", argc, argv); // ignore_convention
+
+ m_pConsole = CreateConsole(CFGFLAG_BANMASTER);
+ m_pConsole->RegisterPrintCallback(StandardOutput, 0);
+ m_pConsole->Register("ban", "s?r", CFGFLAG_BANMASTER, ConBan, 0, "Bans the specified ip");
+ m_pConsole->Register("unban_all", "", CFGFLAG_BANMASTER, ConUnbanAll, 0, "Unbans all ips");
+ m_pConsole->Register("bind", "s", CFGFLAG_BANMASTER, ConSetBindAddr, 0, "Binds to the specified address");
+
+ {
+ bool RegisterFail = false;
+
+ RegisterFail = RegisterFail || !pKernel->RegisterInterface(m_pConsole);
+ RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);
+
+ if(RegisterFail)
+ return -1;
+ }
+
+ m_pConsole->ExecuteFile(BANMASTER_BANFILE);
+
+ NETADDR BindAddr;
+ if(m_aBindAddr[0] && net_host_lookup(m_aBindAddr, &BindAddr, NETTYPE_IPV4) == 0)
+ {
+ if(BindAddr.port == 0)
+ BindAddr.port = BANMASTER_PORT;
+ }
+ else
+ {
+ mem_zero(&BindAddr, sizeof(BindAddr));
+ BindAddr.port = BANMASTER_PORT;
+ }
+
+ m_Net.Open(BindAddr, 0);
+ // TODO: check socket for errors
+
+ dbg_msg("banmaster", "started");
+
+ while(1)
+ {
+ m_Net.Update();
+
+ // process m_aPackets
+ CNetChunk p;
+ while(m_Net.Recv(&p))
+ {
+ if(p.m_DataSize >= sizeof(BANMASTER_IPCHECK) &&
+ !mem_comp(p.m_pData, BANMASTER_IPCHECK, sizeof(BANMASTER_IPCHECK)))
+ {
+ char *pAddr = (char*)p.m_pData + sizeof(BANMASTER_IPCHECK);
+ NETADDR CheckAddr;
+ if(net_addr_from_str(&CheckAddr, pAddr))
+ {
+ dbg_msg("banmaster", "dropped weird message ip=%d.%d.%d.%d checkaddr='%s'",
+ p.m_Address.ip[0], p.m_Address.ip[1], p.m_Address.ip[2], p.m_Address.ip[3], pAddr);
+ }
+ else
+ {
+ int Banned = SendResponse(&p.m_Address, &CheckAddr);
+ dbg_msg("banmaster", "responded to checkmsg ip=%d.%d.%d.%d checkaddr=%d.%d.%d.%d result=%s",
+ p.m_Address.ip[0], p.m_Address.ip[1], p.m_Address.ip[2], p.m_Address.ip[3],
+ CheckAddr.ip[0], CheckAddr.ip[1], CheckAddr.ip[2], CheckAddr.ip[3], (Banned) ? "ban" : "ok");
+ }
+ }
+ }
+
+ if(time_get() - LastUpdate > time_freq() * BAN_REREAD_TIME)
+ {
+ ClearBans();
+ LastUpdate = time_get();
+ m_pConsole->ExecuteFile(BANMASTER_BANFILE);
+ }
+
+ // be nice to the CPU
+ thread_sleep(1);
+ }
+
+ return 0;
+}
diff --git a/src/banmaster/banmaster.h b/src/banmaster/banmaster.h
new file mode 100644
index 000000000..9a722d5e1
--- /dev/null
+++ b/src/banmaster/banmaster.h
@@ -0,0 +1,12 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com. */
+#ifndef BANMASTER_BANMASTER_H
+#define BANMASTER_BANMASTER_H
+
+static const int BANMASTER_PORT = 8302;
+
+static const char BANMASTER_IPOK[] = {255, 255, 255, 255, 'i', 'p', 'o', 'k'};
+static const char BANMASTER_IPBAN[] = {255, 255, 255, 255, 'i', 'p', 'b', 'a'};
+static const char BANMASTER_IPCHECK[] = {255, 255, 255, 255, 'i', 'p', 'c', 'h'};
+
+#endif
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index d972909a6..a58779534 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -28,6 +28,8 @@
#include
+#include
+
#include "register.h"
#include "server.h"
#include "../shared/linereader.h"
@@ -39,6 +41,8 @@
#include
#endif
+static const char SERVER_BANMASTERFILE[] = "banmasters.cfg";
+
static const char *StrLtrim(const char *pStr)
{
while(*pStr && *pStr >= 0 && *pStr <= 32)
@@ -1095,6 +1099,33 @@ void CServer::PumpNetwork()
{
SendServerInfo(&Packet.m_Address, -1);
}
+
+ if(Packet.m_DataSize >= sizeof(BANMASTER_IPOK) &&
+ mem_comp(Packet.m_pData, BANMASTER_IPOK, sizeof(BANMASTER_IPOK)) == 0 &&
+ m_NetServer.BanmasterCheck(&Packet.m_Address) != -1)
+ {
+ }
+
+ if(Packet.m_DataSize >= sizeof(BANMASTER_IPBAN) &&
+ mem_comp(Packet.m_pData, BANMASTER_IPBAN, sizeof(BANMASTER_IPBAN)) == 0 &&
+ g_Config.m_SvGlobalBantime &&
+ m_NetServer.BanmasterCheck(&Packet.m_Address) != -1)
+ {
+ CUnpacker Up;
+ char aIp[32];
+ char aReason[256];
+ NETADDR Addr;
+ Up.Reset((unsigned char*)Packet.m_pData + sizeof(BANMASTER_IPBAN), Packet.m_DataSize - sizeof(BANMASTER_IPBAN));
+ str_copy(aIp, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(aIp));
+ str_copy(aReason, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(aReason));
+ if (net_addr_from_str(&Addr, aIp))
+ {
+ dbg_msg("globalbans", "dropped weird message from banmaster");
+ return;
+ }
+ m_NetServer.BanAdd(Addr, g_Config.m_SvGlobalBantime * 60, aReason);
+ dbg_msg("globalbans", "added ban, ip=%d.%d.%d.%d, reason=\"%s\"", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], aReason);
+ }
}
}
else
@@ -1214,7 +1245,9 @@ int CServer::Run()
}
m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
-
+
+ Console()->ExecuteFile(SERVER_BANMASTERFILE);
+
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
@@ -1633,6 +1666,10 @@ void CServer::RegisterCommands()
Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "", 3);
Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "", 3);
+ Console()->Register("add_banmaster", "s", CFGFLAG_SERVER, ConAddBanmaster, this, "", 4);
+ Console()->Register("banmasters", "", CFGFLAG_SERVER, ConBanmasters, this, "", 3);
+ Console()->Register("clear_banmasters", "", CFGFLAG_SERVER, ConClearBanmasters, this, "", 4);
+
Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "", 3);
Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
@@ -1913,3 +1950,42 @@ NETADDR CServer::GetClientIP(int ClientID)//this may exist already but i couldn'
return m_NetServer.ClientAddr(ClientID);
}
+void CServer::ConAddBanmaster(IConsole::IResult *pResult, void *pUser, int ClientId)
+{
+ CServer *pServer = (CServer *)pUser;
+
+ int Result = pServer->m_NetServer.BanmasterAdd(pResult->GetString(0));
+
+ if(Result == 0)
+ pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "succesfully added banmaster");
+ else if (Result == 1)
+ pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "invalid address for banmaster / net lookup failed");
+ else
+ pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", "too many banmasters");
+}
+
+void CServer::ConBanmasters(IConsole::IResult *pResult, void *pUser, int ClientId)
+{
+ CServer *pServer = (CServer *)pUser;
+ int NumBanmasters = pServer->m_NetServer.BanmasterNum();
+
+ char aBuf[128];
+ char aIpString[64];
+
+ for(int i = 0; i < NumBanmasters; i++)
+ {
+ NETADDR *pBanmaster = pServer->m_NetServer.BanmasterGet(i);
+ net_addr_str(pBanmaster, aIpString, sizeof(aIpString));
+ str_format(aBuf, sizeof(aBuf), "%d: %s", i, aIpString);
+ pServer->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "server/banmaster", aBuf);
+ }
+}
+
+void CServer::ConClearBanmasters(IConsole::IResult *pResult, void *pUser, int ClientId)
+{
+ CServer *pServer = (CServer *)pUser;
+
+ pServer->m_NetServer.BanmastersClear();
+}
+
+
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
index 5eec01eb9..feb5830a6 100644
--- a/src/engine/server/server.h
+++ b/src/engine/server/server.h
@@ -232,6 +232,9 @@ public:
virtual void SnapFreeID(int ID);
virtual void *SnapNewItem(int Type, int Id, int Size);
void SnapSetStaticsize(int ItemType, int Size);
+ static void ConAddBanmaster(IConsole::IResult *pResult, void *pUser, int ClientId);
+ static void ConBanmasters(IConsole::IResult *pResult, void *pUser, int ClientId);
+ static void ConClearBanmasters(IConsole::IResult *pResult, void *pUser, int ClientId);
};
#endif
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
index 92a686e80..e2b5c47df 100644
--- a/src/engine/shared/config_variables.h
+++ b/src/engine/shared/config_variables.h
@@ -199,5 +199,6 @@ MACRO_CONFIG_INT(ClDemoName, cl_demo_name, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE,
MACRO_CONFIG_INT(ClRaceGhost, cl_race_ghost, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Enable ghost",-1)
MACRO_CONFIG_INT(ClRaceShowGhost, cl_race_show_ghost, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ghost",-1)
MACRO_CONFIG_INT(ClRaceSaveGhost, cl_race_save_ghost, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Save ghost",-1)
+MACRO_CONFIG_INT(SvGlobalBantime, sv_global_ban_time, 60, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if the ban server reports it. 0 to disable", 4)
#endif
diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h
index 972d0da8e..b63d18e19 100644
--- a/src/engine/shared/network.h
+++ b/src/engine/shared/network.h
@@ -289,6 +289,20 @@ public:
//
void SetMaxClientsPerIP(int Max);
+ int BanmasterAdd(const char *pAddrStr);
+ int BanmasterNum() const;
+ NETADDR* BanmasterGet(int Index);
+ int BanmasterCheck(NETADDR *pAddr);
+ void BanmastersClear();
+ enum
+ {
+ MAX_BANMASTERS=16,
+ NETSERVER_DISABLEBANMASTER=1,
+ };
+ int m_Flags;
+ NETADDR m_aBanmasters[MAX_BANMASTERS];
+ int m_NumBanmasters;
+
};
diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp
index fabc865b1..df8192a21 100644
--- a/src/engine/shared/network_server.cpp
+++ b/src/engine/shared/network_server.cpp
@@ -1,6 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include
+#include
#include "network.h"
#define MACRO_LIST_LINK_FIRST(Object, First, Prev, Next) \
@@ -59,6 +60,8 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int
m_BanPool[NET_SERVER_MAXBANS-1].m_pPrev = &m_BanPool[NET_SERVER_MAXBANS-2];
m_BanPool_FirstFree = &m_BanPool[0];
+ m_Flags = Flags;
+
return true;
}
@@ -338,6 +341,25 @@ int CNetServer::Recv(CNetChunk *pChunk)
// client that wants to connect
if(!Found)
{
+ if(!(m_Flags & NETSERVER_DISABLEBANMASTER))
+ {
+ CNetChunk Packet;
+ char aBuffer[64];
+ mem_copy(aBuffer, BANMASTER_IPCHECK, sizeof(BANMASTER_IPCHECK));
+ net_addr_str(&Addr, aBuffer + sizeof(BANMASTER_IPCHECK), sizeof(aBuffer) - sizeof(BANMASTER_IPCHECK));
+
+ Packet.m_ClientID = -1;
+ Packet.m_Flags = NETSENDFLAG_CONNLESS;
+ Packet.m_DataSize = str_length(aBuffer) + 1;
+ Packet.m_pData = aBuffer;
+
+ for(int i = 0; i < m_NumBanmasters; i++)
+ {
+ Packet.m_Address = m_aBanmasters[i];
+ Send(&Packet);
+ }
+ }
+
// only allow a specific number of players with the same ip
NETADDR ThisAddr = Addr, OtherAddr;
int FoundAddr = 1;
@@ -369,6 +391,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr);
if(m_pfnNewClient)
m_pfnNewClient(i, m_UserPtr);
+
break;
}
}
@@ -447,3 +470,48 @@ void CNetServer::SetMaxClientsPerIP(int Max)
m_MaxClientsPerIP = Max;
}
+
+int CNetServer::BanmasterAdd(const char *pAddrStr)
+{
+ if(m_NumBanmasters >= MAX_BANMASTERS)
+ return 2;
+
+ if(net_host_lookup(pAddrStr, &m_aBanmasters[m_NumBanmasters], NETTYPE_IPV4))
+ return 1;
+
+ if(m_aBanmasters[m_NumBanmasters].port == 0)
+ m_aBanmasters[m_NumBanmasters].port = BANMASTER_PORT;
+
+ m_NumBanmasters++;
+ return 0;
+}
+
+int CNetServer::BanmasterNum() const
+{
+ return m_NumBanmasters;
+}
+
+NETADDR* CNetServer::BanmasterGet(int Index)
+{
+ if(Index < 0 || Index >= m_NumBanmasters)
+ return 0;
+
+ return &m_aBanmasters[Index];
+}
+
+int CNetServer::BanmasterCheck(NETADDR *pAddr)
+{
+ for(int i = 0; i < m_NumBanmasters; i++)
+ {
+ if(!net_addr_comp(&m_aBanmasters[i], pAddr))
+ return i;
+ }
+
+ return -1;
+}
+
+void CNetServer::BanmastersClear()
+{
+ m_NumBanmasters = 0;
+}
+