Skip to content

Implement connection limits. Fix #1631 #2518 #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: community
Choose a base branch
from
Open
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
29 changes: 21 additions & 8 deletions src/engine/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD

//
m_aCmdConnect[0] = 0;
m_pErrorOverride = 0;

// map download
m_aMapdownloadFilename[0] = 0;
Expand Down Expand Up @@ -511,6 +512,24 @@ void CClient::Connect(const char *pAddress)
{
char aBuf[512];
int Port = 8303;
NETADDR Addr;

if(net_addr_from_str(&Addr, pAddress) != 0 && net_host_lookup(pAddress, &Addr, m_NetClient.NetType()) != 0)
{
char aBufMsg[256];
str_format(aBufMsg, sizeof(aBufMsg), "could not find the address of %s, connecting to localhost", aBuf);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBufMsg);
net_host_lookup("localhost", &Addr, m_NetClient.NetType());
}

if(m_NetClient.Connlimit(Addr, 1, 1))
{
m_pErrorOverride = "Too fast";
SetState(IClient::STATE_CONNECTING);
DisconnectWithReason(m_pErrorOverride);
m_pErrorOverride = 0;
return;
}

Disconnect();

Expand All @@ -521,13 +540,7 @@ void CClient::Connect(const char *pAddress)

mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo));

if(net_addr_from_str(&m_ServerAddress, m_aServerAddressStr) != 0 && net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient.NetType()) != 0)
{
char aBufMsg[256];
str_format(aBufMsg, sizeof(aBufMsg), "could not find the address of %s, connecting to localhost", aBuf);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBufMsg);
net_host_lookup("localhost", &m_ServerAddress, m_NetClient.NetType());
}
m_ServerAddress = Addr;

m_RconAuthed = 0;
m_UseTempRconCommands = 0;
Expand Down Expand Up @@ -763,7 +776,7 @@ void CClient::Quit()

const char *CClient::ErrorString() const
{
return m_NetClient.ErrorString();
return m_pErrorOverride ? m_pErrorOverride : m_NetClient.ErrorString();
}

void CClient::Render()
Expand Down
1 change: 1 addition & 0 deletions src/engine/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class CClient : public IClient, public CDemoPlayer::IListener

//
char m_aCmdConnect[256];
const char *m_pErrorOverride;

// map download
char m_aMapdownloadFilename[256];
Expand Down
2 changes: 2 additions & 0 deletions src/engine/shared/config_variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SAVE|CFGF
MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SAVE|CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick")
MACRO_CONFIG_INT(SvAutoDemoRecord, sv_auto_demo_record, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_SERVER, "Automatically record demos")
MACRO_CONFIG_INT(SvAutoDemoMax, sv_auto_demo_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_SERVER, "Maximum number of automatically recorded demos (0 = no limit)")
MACRO_CONFIG_INT(SvConnlimit, sv_connlimit, 4, 0, 100, CFGFLAG_SERVER, "Connlimit: Number of connections an IP is allowed to do in a timespan")
MACRO_CONFIG_INT(SvConnlimitWindow, sv_connlimit_window, 20, 0, 1000, CFGFLAG_SERVER, "Connlimit: Time in which IP's connections are counted")

MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_SAVE|CFGFLAG_ECON, "Address to bind the external console to. Anything but 'localhost' is dangerous")
MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_ECON, "Port to use for the external console")
Expand Down
34 changes: 34 additions & 0 deletions src/engine/shared/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,40 @@ void CNetBase::SendControlMsgWithToken(const NETADDR *pAddr, TOKEN Token, int Ac
SendControlMsg(pAddr, Token, 0, ControlMsg, m_aRequestTokenBuf, Extended ? sizeof(m_aRequestTokenBuf) : 4);
}

bool CNetBase::Connlimit(const NETADDR &Addr, int Window, int Limit)
{
int64 Now = time_get();
int Oldest = 0;

// This could probably be optimized, maybe addr hashes?
for(int i = 0; i < NET_CONNLIMIT_IPS; ++i)
{
if(!net_addr_comp(&m_aConnLog[i].m_Addr, &Addr))
{
if(m_aConnLog[i].m_Time > Now - time_freq() * Window)
{
if(m_aConnLog[i].m_Conns >= Limit)
return true;
}
else
{
m_aConnLog[i].m_Time = Now;
m_aConnLog[i].m_Conns = 0;
}
m_aConnLog[i].m_Conns++;
return false;
}

if(m_aConnLog[i].m_Time < m_aConnLog[Oldest].m_Time)
Oldest = i;
}

m_aConnLog[Oldest].m_Addr = Addr;
m_aConnLog[Oldest].m_Time = Now;
m_aConnLog[Oldest].m_Conns = 1;
return false;
}

unsigned char *CNetChunkHeader::Pack(unsigned char *pData)
{
pData[0] = ((m_Flags&0x03)<<6) | ((m_Size>>6)&0x3F);
Expand Down
17 changes: 14 additions & 3 deletions src/engine/shared/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ enum
enum
{
NET_MAX_CHUNKHEADERSIZE = 3,

// packets
NET_PACKETHEADERSIZE = 7,
NET_PACKETHEADERSIZE_CONNLESS = NET_PACKETHEADERSIZE + 2,
Expand Down Expand Up @@ -108,7 +108,7 @@ enum
//
NET_MAX_CLIENTS = 64,
NET_MAX_CONSOLE_CLIENTS = 4,

NET_MAX_SEQUENCE = 1<<10,
NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,

Expand All @@ -130,6 +130,7 @@ enum
NET_CTRLMSG_TOKEN=5,

NET_CONN_BUFFERSIZE=1024*32,
NET_CONNLIMIT_IPS=16,

NET_ENUM_TERMINATOR
};
Expand Down Expand Up @@ -208,13 +209,21 @@ class CNetBase
CHuffman m_Huffman;
unsigned char m_aRequestTokenBuf[NET_TOKENREQUEST_DATASIZE];

struct SConnection
{
NETADDR m_Addr;
int64 m_Time;
int m_Conns;
};
SConnection m_aConnLog[NET_CONNLIMIT_IPS];

public:
CNetBase();
~CNetBase();
CConfig *Config() { return m_pConfig; }
class IEngine *Engine() { return m_pEngine; }
int NetType() { return m_Socket.type; }

void Init(NETSOCKET Socket, class CConfig *pConfig, class IConsole *pConsole, class IEngine *pEngine);
void Shutdown();
void UpdateLogHandles();
Expand All @@ -225,6 +234,8 @@ class CNetBase
void SendPacketConnless(const NETADDR *pAddr, TOKEN Token, TOKEN ResponseToken, const void *pData, int DataSize);
void SendPacket(const NETADDR *pAddr, CNetPacketConstruct *pPacket);
int UnpackPacket(NETADDR *pAddr, unsigned char *pBuffer, CNetPacketConstruct *pPacket);

bool Connlimit(const NETADDR &Addr, int Window, int Limit);
};

class CNetTokenManager
Expand Down
21 changes: 11 additions & 10 deletions src/engine/shared/network_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <engine/console.h>

#include "config.h"
#include "netban.h"
#include "network.h"

Expand All @@ -19,7 +20,7 @@ bool CNetServer::Open(NETADDR BindAddr, CConfig *pConfig, IConsole *pConsole, IE
NETSOCKET Socket = net_udp_create(BindAddr, 0);
if(!Socket.type)
return false;

// init
m_pNetBan = pNetBan;
Init(Socket, pConfig, pConsole, pEngine);
Expand Down Expand Up @@ -63,7 +64,6 @@ void CNetServer::Drop(int ClientID, const char *pReason)

int CNetServer::Update()
{
int64 Now = time_get();
for(int i = 0; i < NET_MAX_CLIENTS; i++)
{
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
Expand All @@ -72,13 +72,7 @@ int CNetServer::Update()
m_aSlots[i].m_Connection.Update();
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
{
if(Now - m_aSlots[i].m_Connection.ConnectTime() < time_freq() && NetBan())
{
if(NetBan()->BanAddr(ClientAddr(i), 60, "Stressing network") == -1)
Drop(i, m_aSlots[i].m_Connection.ErrorString());
}
else
Drop(i, m_aSlots[i].m_Connection.ErrorString());
Drop(i, m_aSlots[i].m_Connection.ErrorString());
}
}

Expand Down Expand Up @@ -173,6 +167,13 @@ int CNetServer::Recv(CNetChunk *pChunk, TOKEN *pResponseToken)
continue;
}

if(Connlimit(Addr, Config()->m_SvConnlimitWindow, Config()->m_SvConnlimit))
{
const char Msg[] = "Too many connections in a short time";
SendControlMsg(&Addr, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg));
continue;
}

// only allow a specific number of players with the same ip
NETADDR ThisAddr = Addr, OtherAddr;
int FoundAddr = 1;
Expand Down Expand Up @@ -207,7 +208,7 @@ int CNetServer::Recv(CNetChunk *pChunk, TOKEN *pResponseToken)
m_pfnNewClient(i, m_UserPtr);
break;
}
}
}
}
else if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_TOKEN)
m_TokenCache.AddToken(&Addr, m_RecvUnpacker.m_Data.m_ResponseToken, NET_TOKENFLAG_RESPONSEONLY);
Expand Down
11 changes: 7 additions & 4 deletions src/game/client/components/menus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ CMenus::CMenus()
m_ActivePage = PAGE_INTERNET;
m_GamePage = PAGE_GAME;

m_aError[0] = 0;

m_SidebarTab = 0;
m_SidebarActive = true;
m_ShowServerDetails = true;
Expand Down Expand Up @@ -653,13 +655,13 @@ void CMenus::DoScrollbarOptionLabeled(void *pID, int *pOption, const CUIRect *pR
str_format(aBuf, sizeof(aBuf), "%s: %s", pStr, aLabels[Value]);

float FontSize = pRect->h*ms_FontmodHeight*0.8f;

RenderTools()->DrawUIRect(pRect, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);

CUIRect Label, ScrollBar;
pRect->VSplitLeft(5.0f, 0, &Label);
Label.VSplitRight(60.0f, &Label, &ScrollBar);

Label.y += 2.0f;
UI()->DoLabel(&Label, aBuf, FontSize, CUI::ALIGN_LEFT);

Expand Down Expand Up @@ -1722,7 +1724,7 @@ int CMenus::Render()
else if(m_Popup == POPUP_DISCONNECTED)
{
pTitle = Localize("Disconnected");
pExtraText = Client()->ErrorString();
pExtraText = m_aError;
pButtonText = Localize("Ok");
ExtraAlign = CUI::ALIGN_LEFT;
}
Expand Down Expand Up @@ -2397,6 +2399,7 @@ void CMenus::OnStateChange(int NewState, int OldState)
m_Popup = POPUP_NONE;
if(Client()->ErrorString() && Client()->ErrorString()[0] != 0)
{
str_copy(m_aError, Client()->ErrorString(), sizeof(m_aError));
if(str_find(Client()->ErrorString(), "password"))
{
m_Popup = POPUP_PASSWORD;
Expand Down Expand Up @@ -2520,7 +2523,7 @@ void CMenus::OnRender()

bool CMenus::CheckHotKey(int Key) const
{
return !m_KeyReaderIsActive && !m_KeyReaderWasActive && !m_PrevCursorActive && !m_PopupActive &&
return !m_KeyReaderIsActive && !m_KeyReaderWasActive && !m_PrevCursorActive && !m_PopupActive &&
!Input()->KeyIsPressed(KEY_LSHIFT) && !Input()->KeyIsPressed(KEY_RSHIFT) && !Input()->KeyIsPressed(KEY_LCTRL) && !Input()->KeyIsPressed(KEY_RCTRL) && !Input()->KeyIsPressed(KEY_LALT) && // no modifier
Input()->KeyIsPressed(Key) && !m_pClient->m_pGameConsole->IsConsoleActive();
}
Expand Down
1 change: 1 addition & 0 deletions src/game/client/components/menus.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ class CMenus : public CComponent

int m_GamePage;
int m_Popup;
char m_aError[256];
int m_ActivePage;
int m_MenuPage;
int m_MenuPageOld;
Expand Down