diff options
Diffstat (limited to 'src/engine/shared')
| -rw-r--r-- | src/engine/shared/config.cpp | 7 | ||||
| -rw-r--r-- | src/engine/shared/config.h | 3 | ||||
| -rw-r--r-- | src/engine/shared/config_variables.h | 20 | ||||
| -rw-r--r-- | src/engine/shared/console.cpp | 105 | ||||
| -rw-r--r-- | src/engine/shared/console.h | 5 | ||||
| -rw-r--r-- | src/engine/shared/demo.cpp | 55 | ||||
| -rw-r--r-- | src/engine/shared/demo.h | 5 | ||||
| -rw-r--r-- | src/engine/shared/econ.cpp | 38 | ||||
| -rw-r--r-- | src/engine/shared/econ.h | 5 | ||||
| -rw-r--r-- | src/engine/shared/masterserver.cpp | 8 | ||||
| -rw-r--r-- | src/engine/shared/netban.cpp | 603 | ||||
| -rw-r--r-- | src/engine/shared/netban.h | 193 | ||||
| -rw-r--r-- | src/engine/shared/network.h | 68 | ||||
| -rw-r--r-- | src/engine/shared/network_console.cpp | 99 | ||||
| -rw-r--r-- | src/engine/shared/network_console_conn.cpp | 2 | ||||
| -rw-r--r-- | src/engine/shared/network_server.cpp | 247 | ||||
| -rw-r--r-- | src/engine/shared/storage.cpp | 33 |
17 files changed, 1072 insertions, 424 deletions
diff --git a/src/engine/shared/config.cpp b/src/engine/shared/config.cpp index b9aa1320..d0cb7a6b 100644 --- a/src/engine/shared/config.cpp +++ b/src/engine/shared/config.cpp @@ -111,13 +111,8 @@ public: { if(!m_ConfigFile) return; -#if defined(CONF_FAMILY_WINDOWS) - static const char Newline[] = "\r\n"; -#else - static const char Newline[] = "\n"; -#endif io_write(m_ConfigFile, pLine, str_length(pLine)); - io_write(m_ConfigFile, Newline, sizeof(Newline)-1); + io_write_newline(m_ConfigFile); } }; diff --git a/src/engine/shared/config.h b/src/engine/shared/config.h index ed89daa2..c6d8437f 100644 --- a/src/engine/shared/config.h +++ b/src/engine/shared/config.h @@ -20,7 +20,8 @@ enum CFGFLAG_CLIENT=2, CFGFLAG_SERVER=4, CFGFLAG_STORE=8, - CFGFLAG_MASTER=16 + CFGFLAG_MASTER=16, + CFGFLAG_ECON=32, }; #endif diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index de9a558f..76e4ccd3 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -17,6 +17,7 @@ MACRO_CONFIG_INT(ConsoleOutputLevel, console_output_level, 0, 0, 2, CFGFLAG_CLIE MACRO_CONFIG_INT(ClCpuThrottle, cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(ClLoadCountryFlags, cl_load_country_flags, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Load and show country flags") MACRO_CONFIG_INT(ClAutoDemoRecord, cl_auto_demo_record, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Automatically record demos") MACRO_CONFIG_INT(ClAutoDemoMax, cl_auto_demo_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum number of automatically recorded demos (0 = no limit)") @@ -47,7 +48,7 @@ MACRO_CONFIG_INT(BrSort, br_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrSortOrder, br_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrMaxRequests, br_max_requests, 25, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser") -MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") +MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 128, 32768, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable") MACRO_CONFIG_INT(SndMusic, snd_enable_music, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Play background music") @@ -70,11 +71,14 @@ MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|C MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples") MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") MACRO_CONFIG_INT(GfxFinish, gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(GfxAsyncRender, gfx_asyncrender, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update") + +MACRO_CONFIG_INT(GfxThreaded, gfx_threaded, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use the threaded graphics backend") MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") MACRO_CONFIG_STR(SvName, sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name") -MACRO_CONFIG_STR(SvBindaddr, sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to") +MACRO_CONFIG_STR(Bindaddr, bindaddr, 128, "", CFGFLAG_CLIENT|CFGFLAG_SERVER|CFGFLAG_MASTER, "Address to bind the client/server to") MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server") MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers") MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server") @@ -89,12 +93,12 @@ MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "Th MACRO_CONFIG_INT(SvAutoDemoRecord, sv_auto_demo_record, 0, 0, 1, CFGFLAG_SERVER, "Automatically record demos") MACRO_CONFIG_INT(SvAutoDemoMax, sv_auto_demo_max, 10, 0, 1000, CFGFLAG_SERVER, "Maximum number of automatically recorded demos (0 = no limit)") -MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_SERVER, "Address to bind the external console to. Anything but 'localhost' is dangerous") -MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the external console") -MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_SERVER, "External console password") -MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if econ authentication fails. 0 just closes the connection") -MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_SERVER, "Time in seconds before the the econ authentification times out") -MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_SERVER, "Adjusts the amount of information in the external console") +MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_ECON, "Address to bind the external console to. Anything but 'localhost' is dangerous") +MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_ECON, "Port to use for the external console") +MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_ECON, "External console password") +MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_ECON, "The time a client gets banned if econ authentication fails. 0 just closes the connection") +MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_ECON, "Time in seconds before the the econ authentification times out") +MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_ECON, "Adjusts the amount of information in the external console") MACRO_CONFIG_INT(SvGlobalBantime, sv_global_bantime, 60, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if the ban server reports it. 0 to disable") diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index e4cb1991..443c5904 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -12,6 +12,8 @@ #include "console.h" #include "linereader.h" +// todo: rework this + const char *CConsole::CResult::GetString(unsigned Index) { if (Index < 0 || Index >= m_NumArgs) @@ -374,6 +376,14 @@ void CConsole::ExecuteLine(const char *pStr) CConsole::ExecuteLineStroked(0, pStr); // then release it } +void CConsole::ExecuteLineFlag(const char *pStr, int FlagMask) +{ + int Temp = m_FlagMask; + m_FlagMask = FlagMask; + ExecuteLine(pStr); + m_FlagMask = Temp; +} + void CConsole::ExecuteFile(const char *pFilename) { @@ -564,6 +574,75 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData) } } +void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser) +{ + CConsole* pConsole = static_cast<CConsole *>(pUser); + char aBuf[128] = {0}; + CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), pConsole->m_FlagMask); + if(pCommand) + { + FCommandCallback pfnCallback = pCommand->m_pfnCallback; + void *pUserData = pCommand->m_pUserData; + + // check for chain + if(pCommand->m_pfnCallback == Con_Chain) + { + CChain *pChainInfo = static_cast<CChain *>(pCommand->m_pUserData); + pfnCallback = pChainInfo->m_pfnCallback; + pUserData = pChainInfo->m_pCallbackUserData; + } + + if(pfnCallback == IntVariableCommand) + { + CIntVariableData *pData = static_cast<CIntVariableData *>(pUserData); + int Val = *(pData->m_pVariable)==pResult->GetInteger(1) ? pResult->GetInteger(2) : pResult->GetInteger(1); + str_format(aBuf, sizeof(aBuf), "%s %i", pResult->GetString(0), Val); + pConsole->ExecuteLine(aBuf); + aBuf[0] = 0; + } + else + str_format(aBuf, sizeof(aBuf), "Invalid command: '%s'.", pResult->GetString(0)); + } + else + str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0)); + + if(aBuf[0] != 0) + pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +} + +void CConsole::ConToggleStroke(IConsole::IResult *pResult, void *pUser) +{ + CConsole* pConsole = static_cast<CConsole *>(pUser); + char aBuf[128] = {0}; + CCommand *pCommand = pConsole->FindCommand(pResult->GetString(1), pConsole->m_FlagMask); + if(pCommand) + { + FCommandCallback pfnCallback = pCommand->m_pfnCallback; + + // check for chain + if(pCommand->m_pfnCallback == Con_Chain) + { + CChain *pChainInfo = static_cast<CChain *>(pCommand->m_pUserData); + pfnCallback = pChainInfo->m_pfnCallback; + } + + if(pfnCallback == IntVariableCommand) + { + int Val = pResult->GetInteger(0)==0 ? pResult->GetInteger(3) : pResult->GetInteger(2); + str_format(aBuf, sizeof(aBuf), "%s %i", pResult->GetString(1), Val); + pConsole->ExecuteLine(aBuf); + aBuf[0] = 0; + } + else + str_format(aBuf, sizeof(aBuf), "Invalid command: '%s'.", pResult->GetString(1)); + } + else + str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(1)); + + if(aBuf[0] != 0) + pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +} + CConsole::CConsole(int FlagMask) { m_FlagMask = FlagMask; @@ -585,6 +664,9 @@ CConsole::CConsole(int FlagMask) Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text"); Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file"); + Register("toggle", "sii", CFGFLAG_SERVER|CFGFLAG_CLIENT, ConToggle, this, "Toggle config value"); + Register("+toggle", "sii", CFGFLAG_CLIENT, ConToggleStroke, this, "Toggle config value via keypress"); + Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators"); Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators"); @@ -633,7 +715,7 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments) void CConsole::AddCommandSorted(CCommand *pCommand) { - if(!m_pFirstCommand || str_comp(pCommand->m_pName, m_pFirstCommand->m_pName) < 0) + if(!m_pFirstCommand || str_comp(pCommand->m_pName, m_pFirstCommand->m_pName) <= 0) { if(m_pFirstCommand && m_pFirstCommand->m_pNext) pCommand->m_pNext = m_pFirstCommand; @@ -645,7 +727,7 @@ void CConsole::AddCommandSorted(CCommand *pCommand) { for(CCommand *p = m_pFirstCommand; p; p = p->m_pNext) { - if(!p->m_pNext || str_comp(pCommand->m_pName, p->m_pNext->m_pName) < 0) + if(!p->m_pNext || str_comp(pCommand->m_pName, p->m_pNext->m_pName) <= 0) { pCommand->m_pNext = p->m_pNext; p->m_pNext = pCommand; @@ -658,18 +740,25 @@ void CConsole::AddCommandSorted(CCommand *pCommand) void CConsole::Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) { - CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand; + CCommand *pCommand = FindCommand(pName, Flags); + bool DoAdd = false; + if(pCommand == 0) + { + pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand; + DoAdd = true; + } pCommand->m_pfnCallback = pfnFunc; pCommand->m_pUserData = pUser; pCommand->m_pName = pName; pCommand->m_pHelp = pHelp; pCommand->m_pParams = pParams; - + pCommand->m_Flags = Flags; pCommand->m_Temp = false; - AddCommandSorted(pCommand); + if(DoAdd) + AddCommandSorted(pCommand); } void CConsole::RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp) @@ -699,7 +788,7 @@ void CConsole::RegisterTemp(const char *pName, const char *pParams, int Flags, c } pCommand->m_pfnCallback = 0; - pCommand->m_pUserData = 0; + pCommand->m_pUserData = 0; pCommand->m_Flags = Flags; pCommand->m_Temp = true; @@ -729,7 +818,7 @@ void CConsole::DeregisterTemp(const char *pName) break; } } - + // add to recycle list if(pRemoved) { @@ -742,7 +831,7 @@ void CConsole::DeregisterTempAll() { // set non temp as first one for(; m_pFirstCommand && m_pFirstCommand->m_Temp; m_pFirstCommand = m_pFirstCommand->m_pNext); - + // remove temp entries from command list for(CCommand *pCommand = m_pFirstCommand; pCommand && pCommand->m_pNext; pCommand = pCommand->m_pNext) { diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index 6989c696..bbe267d4 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -54,6 +54,8 @@ class CConsole : public IConsole static void Con_Chain(IResult *pResult, void *pUserData); static void Con_Echo(IResult *pResult, void *pUserData); static void Con_Exec(IResult *pResult, void *pUserData); + static void ConToggle(IResult *pResult, void *pUser); + static void ConToggleStroke(IResult *pResult, void *pUser); static void ConModCommandAccess(IResult *pResult, void *pUser); static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser); @@ -156,7 +158,7 @@ class CConsole : public IConsole public: CConsole(int FlagMask); - virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const; + virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int FlagMask) const; virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp); virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser); @@ -170,6 +172,7 @@ public: virtual bool LineIsValid(const char *pStr); virtual void ExecuteLine(const char *pStr); + virtual void ExecuteLineFlag(const char *pStr, int FlagMask); virtual void ExecuteFile(const char *pFilename); virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData); diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index 51dd51d9..37c82cce 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -13,8 +13,9 @@ #include "snapshot.h" static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0}; -static const unsigned char gs_ActVersion = 3; +static const unsigned char gs_ActVersion = 4; static const int gs_LengthOffset = 152; +static const int gs_NumMarkersOffset = 176; CDemoRecorder::CDemoRecorder(class CSnapshotDelta *pSnapshotDelta) @@ -89,6 +90,8 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con str_copy(Header.m_aType, pType, sizeof(Header.m_aType)); // Header.m_Length - add this on stop str_timestamp(Header.m_aTimestamp, sizeof(Header.m_aTimestamp)); + // Header.m_aNumTimelineMarkers - add this on stop + // Header.m_aTimelineMarkers - add this on stop io_write(DemoFile, &Header, sizeof(Header)); // write map data @@ -105,6 +108,7 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con m_LastKeyFrame = -1; m_LastTickMarker = -1; m_FirstTick = -1; + m_NumTimelineMarkers = 0; char aBuf[256]; str_format(aBuf, sizeof(aBuf), "Recording to '%s'", pFilename); @@ -266,6 +270,25 @@ int CDemoRecorder::Stop() aLength[3] = (DemoLength)&0xff; io_write(m_File, aLength, sizeof(aLength)); + // add the timeline markers to the header + io_seek(m_File, gs_NumMarkersOffset, IOSEEK_START); + char aNumMarkers[4]; + aNumMarkers[0] = (m_NumTimelineMarkers>>24)&0xff; + aNumMarkers[1] = (m_NumTimelineMarkers>>16)&0xff; + aNumMarkers[2] = (m_NumTimelineMarkers>>8)&0xff; + aNumMarkers[3] = (m_NumTimelineMarkers)&0xff; + io_write(m_File, aNumMarkers, sizeof(aNumMarkers)); + for(int i = 0; i < m_NumTimelineMarkers; i++) + { + int Marker = m_aTimelineMarkers[i]; + char aMarker[4]; + aMarker[0] = (Marker>>24)&0xff; + aMarker[1] = (Marker>>16)&0xff; + aMarker[2] = (Marker>>8)&0xff; + aMarker[3] = (Marker)&0xff; + io_write(m_File, aMarker, sizeof(aMarker)); + } + io_close(m_File); m_File = 0; m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", "Stopped recording"); @@ -273,6 +296,24 @@ int CDemoRecorder::Stop() return 0; } +void CDemoRecorder::AddDemoMarker() +{ + if(m_LastTickMarker < 0 || m_NumTimelineMarkers >= MAX_TIMELINE_MARKERS) + return; + + // not more than 1 marker in a second + if(m_NumTimelineMarkers > 0) + { + int Diff = m_LastTickMarker - m_aTimelineMarkers[m_NumTimelineMarkers-1]; + if(Diff < SERVER_TICK_SPEED*1.0f) + return; + } + + m_aTimelineMarkers[m_NumTimelineMarkers++] = m_LastTickMarker; + + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", "Added timeline marker"); +} + CDemoPlayer::CDemoPlayer(class CSnapshotDelta *pSnapshotDelta) @@ -622,6 +663,16 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const mem_free(pMapData); } + // get timeline markers + int Num = ((m_Info.m_Header.m_aNumTimelineMarkers[0]<<24)&0xFF000000) | ((m_Info.m_Header.m_aNumTimelineMarkers[1]<<16)&0xFF0000) | + ((m_Info.m_Header.m_aNumTimelineMarkers[2]<<8)&0xFF00) | (m_Info.m_Header.m_aNumTimelineMarkers[3]&0xFF); + m_Info.m_Info.m_NumTimelineMarkers = Num; + for(int i = 0; i < Num && i < MAX_TIMELINE_MARKERS; i++) + { + char *pTimelineMarker = m_Info.m_Header.m_aTimelineMarkers[i]; + m_Info.m_Info.m_aTimelineMarkers[i] = ((pTimelineMarker[0]<<24)&0xFF000000) | ((pTimelineMarker[1]<<16)&0xFF0000) | + ((pTimelineMarker[2]<<8)&0xFF00) | (pTimelineMarker[3]&0xFF); + } // scan the file for interessting points ScanFile(); @@ -775,7 +826,7 @@ void CDemoPlayer::GetDemoName(char *pBuffer, int BufferSize) const else if(*pFileName == '.') pEnd = pFileName; } - + int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize; str_copy(pBuffer, pExtractedName, Length); } diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index f4ac5685..760e7256 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -17,6 +17,8 @@ class CDemoRecorder : public IDemoRecorder int m_FirstTick; unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE]; class CSnapshotDelta *m_pSnapshotDelta; + int m_NumTimelineMarkers; + int m_aTimelineMarkers[MAX_TIMELINE_MARKERS]; void WriteTickMarker(int Tick, int Keyframe); void Write(int Type, const void *pData, int Size); @@ -25,6 +27,7 @@ public: int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, unsigned MapCrc, const char *pType); int Stop(); + void AddDemoMarker(); void RecordSnapshot(int Tick, const void *pData, int Size); void RecordMessage(const void *pData, int Size); @@ -108,7 +111,7 @@ public: void Unpause(); int Stop(); void SetSpeed(float Speed); - int SetPos(float Precent); + int SetPos(float Percent); const CInfo *BaseInfo() const { return &m_Info.m_Info; } void GetDemoName(char *pBuffer, int BufferSize) const; bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const; diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp index 617cdbd6..eb7df872 100644 --- a/src/engine/shared/econ.cpp +++ b/src/engine/shared/econ.cpp @@ -2,14 +2,15 @@ #include <engine/shared/config.h> #include "econ.h" +#include "netban.h" + int CEcon::NewClientCallback(int ClientID, void *pUser) { CEcon *pThis = (CEcon *)pUser; - NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID); char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + net_addr_str(pThis->m_NetConsole.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[128]; str_format(aBuf, sizeof(aBuf), "client accepted. cid=%d addr=%s'", ClientID, aAddrStr); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); @@ -26,9 +27,8 @@ int CEcon::DelClientCallback(int ClientID, const char *pReason, void *pUser) { CEcon *pThis = (CEcon *)pUser; - NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID); char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + net_addr_str(pThis->m_NetConsole.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); @@ -52,7 +52,15 @@ void CEcon::ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUse } } -void CEcon::Init(IConsole *pConsole) +void CEcon::ConLogout(IConsole::IResult *pResult, void *pUserData) +{ + CEcon *pThis = static_cast<CEcon *>(pUserData); + + if(pThis->m_UserClientID >= 0 && pThis->m_UserClientID < NET_MAX_CONSOLE_CLIENTS && pThis->m_aClients[pThis->m_UserClientID].m_State != CClient::STATE_EMPTY) + pThis->m_NetConsole.Drop(pThis->m_UserClientID, "Logout"); +} + +void CEcon::Init(IConsole *pConsole, CNetBan *pNetBan) { m_pConsole = pConsole; @@ -60,6 +68,7 @@ void CEcon::Init(IConsole *pConsole) m_aClients[i].m_State = CClient::STATE_EMPTY; m_Ready = false; + m_UserClientID = -1; if(g_Config.m_EcPort == 0 || g_Config.m_EcPassword[0] == 0) return; @@ -74,7 +83,7 @@ void CEcon::Init(IConsole *pConsole) BindAddr.port = g_Config.m_EcPort; } - if(m_NetConsole.Open(BindAddr, 0)) + if(m_NetConsole.Open(BindAddr, pNetBan, 0)) { m_NetConsole.SetCallbacks(NewClientCallback, DelClientCallback, this); m_Ready = true; @@ -84,6 +93,8 @@ void CEcon::Init(IConsole *pConsole) Console()->Chain("ec_output_level", ConchainEconOutputLevelUpdate, this); m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_EcOutputLevel, SendLineCB, this); + + Console()->Register("logout", "", CFGFLAG_ECON, ConLogout, this, "Logout of econ"); } else Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"econ", "couldn't open socket. port might already be in use"); @@ -115,27 +126,26 @@ void CEcon::Update() else { m_aClients[ClientID].m_AuthTries++; - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, MAX_AUTH_TRIES); - m_NetConsole.Send(ClientID, aBuf); + char aMsg[128]; + str_format(aMsg, sizeof(aMsg), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, MAX_AUTH_TRIES); + m_NetConsole.Send(ClientID, aMsg); if(m_aClients[ClientID].m_AuthTries >= MAX_AUTH_TRIES) { if(!g_Config.m_EcBantime) m_NetConsole.Drop(ClientID, "Too many authentication tries"); else - { - NETADDR Addr = m_NetConsole.ClientAddr(ClientID); - m_NetConsole.AddBan(Addr, g_Config.m_EcBantime*60); - } + m_NetConsole.NetBan()->BanAddr(m_NetConsole.ClientAddr(ClientID), g_Config.m_EcBantime*60, "Too many authentication tries"); } } } else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED) { char aFormatted[256]; - str_format(aFormatted, sizeof(aBuf), "cid=%d cmd='%s'", ClientID, aBuf); + str_format(aFormatted, sizeof(aFormatted), "cid=%d cmd='%s'", ClientID, aBuf); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aFormatted); + m_UserClientID = ClientID; Console()->ExecuteLine(aBuf); + m_UserClientID = -1; } } diff --git a/src/engine/shared/econ.h b/src/engine/shared/econ.h index daec34c4..197c7a00 100644 --- a/src/engine/shared/econ.h +++ b/src/engine/shared/econ.h @@ -3,6 +3,7 @@ #include "network.h" + class CEcon { enum @@ -31,9 +32,11 @@ class CEcon bool m_Ready; int m_PrintCBIndex; + int m_UserClientID; static void SendLineCB(const char *pLine, void *pUserData); static void ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConLogout(IConsole::IResult *pResult, void *pUserData); static int NewClientCallback(int ClientID, void *pUser); static int DelClientCallback(int ClientID, const char *pReason, void *pUser); @@ -41,7 +44,7 @@ class CEcon public: IConsole *Console() { return m_pConsole; } - void Init(IConsole *pConsole); + void Init(IConsole *pConsole, class CNetBan *pNetBan); void Update(); void Send(int ClientID, const char *pLine); void Shutdown(); diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp index 1271eeaf..95482639 100644 --- a/src/engine/shared/masterserver.cpp +++ b/src/engine/shared/masterserver.cpp @@ -157,7 +157,7 @@ public: Added = true; break; } - + if(!Added) { for(int i = 0; i < MAX_MASTERSERVERS; ++i) @@ -192,13 +192,13 @@ public: { char aAddrStr[NETADDR_MAXSTRSIZE]; if(m_aMasterServers[i].m_Addr.type != NETTYPE_INVALID) - net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr)); + net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr), true); else aAddrStr[0] = 0; char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%s %s\n", m_aMasterServers[i].m_aHostname, aAddrStr); - + str_format(aBuf, sizeof(aBuf), "%s %s", m_aMasterServers[i].m_aHostname, aAddrStr); io_write(File, aBuf, str_length(aBuf)); + io_write_newline(File); } io_close(File); diff --git a/src/engine/shared/netban.cpp b/src/engine/shared/netban.cpp new file mode 100644 index 00000000..d26d64d4 --- /dev/null +++ b/src/engine/shared/netban.cpp @@ -0,0 +1,603 @@ +#include <base/math.h> + +#include <engine/console.h> +#include <engine/storage.h> +#include <engine/shared/config.h> + +#include "netban.h" + + +bool CNetBan::StrAllnum(const char *pStr) +{ + while(*pStr) + { + if(!(*pStr >= '0' && *pStr <= '9')) + return false; + pStr++; + } + return true; +} + + +CNetBan::CNetHash::CNetHash(const NETADDR *pAddr) +{ + if(pAddr->type==NETTYPE_IPV4) + m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3])&0xFF; + else + m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3]+pAddr->ip[4]+pAddr->ip[5]+pAddr->ip[6]+pAddr->ip[7]+ + pAddr->ip[8]+pAddr->ip[9]+pAddr->ip[10]+pAddr->ip[11]+pAddr->ip[12]+pAddr->ip[13]+pAddr->ip[14]+pAddr->ip[15])&0xFF; + m_HashIndex = 0; +} + +CNetBan::CNetHash::CNetHash(const CNetRange *pRange) +{ + m_Hash = 0; + m_HashIndex = 0; + for(int i = 0; pRange->m_LB.ip[i] == pRange->m_UB.ip[i]; ++i) + { + m_Hash += pRange->m_LB.ip[i]; + ++m_HashIndex; + } + m_Hash &= 0xFF; +} + +int CNetBan::CNetHash::MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]) +{ + int Length = pAddr->type==NETTYPE_IPV4 ? 4 : 16; + aHash[0].m_Hash = 0; + aHash[0].m_HashIndex = 0; + for(int i = 1, Sum = 0; i <= Length; ++i) + { + Sum += pAddr->ip[i-1]; + aHash[i].m_Hash = Sum&0xFF; + aHash[i].m_HashIndex = i%Length; + } + return Length; +} + + +template<class T, int HashCount> +typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash) +{ + if(!m_pFirstFree) + return 0; + + // create new ban + CBan<T> *pBan = m_pFirstFree; + pBan->m_Data = *pData; + pBan->m_Info = *pInfo; + pBan->m_NetHash = *pNetHash; + if(pBan->m_pNext) + pBan->m_pNext->m_pPrev = pBan->m_pPrev; + if(pBan->m_pPrev) + pBan->m_pPrev->m_pNext = pBan->m_pNext; + else + m_pFirstFree = pBan->m_pNext; + + // add it to the hash list + if(m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]) + m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]->m_pHashPrev = pBan; + pBan->m_pHashPrev = 0; + pBan->m_pHashNext = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; + m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash] = pBan; + + // insert it into the used list + if(m_pFirstUsed) + { + for(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext) + { + if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) + { + // insert before + pBan->m_pNext = p; + pBan->m_pPrev = p->m_pPrev; + if(p->m_pPrev) + p->m_pPrev->m_pNext = pBan; + else + m_pFirstUsed = pBan; + p->m_pPrev = pBan; + break; + } + + if(!p->m_pNext) + { + // last entry + p->m_pNext = pBan; + pBan->m_pPrev = p; + pBan->m_pNext = 0; + break; + } + } + } + else + { + m_pFirstUsed = pBan; + pBan->m_pNext = pBan->m_pPrev = 0; + } + + // update ban count + ++m_CountUsed; + + return pBan; +} + +template<class T, int HashCount> +int CNetBan::CBanPool<T, HashCount>::Remove(CBan<T> *pBan) +{ + if(pBan == 0) + return -1; + + // remove from hash list + if(pBan->m_pHashNext) + pBan->m_pHashNext->m_pHashPrev = pBan->m_pHashPrev; + if(pBan->m_pHashPrev) + pBan->m_pHashPrev->m_pHashNext = pBan->m_pHashNext; + else + m_paaHashList[pBan->m_NetHash.m_HashIndex][pBan->m_NetHash.m_Hash] = pBan->m_pHashNext; + pBan->m_pHashNext = pBan->m_pHashPrev = 0; + + // remove from used list + if(pBan->m_pNext) + pBan->m_pNext->m_pPrev = pBan->m_pPrev; + if(pBan->m_pPrev) + pBan->m_pPrev->m_pNext = pBan->m_pNext; + else + m_pFirstUsed = pBan->m_pNext; + + // add to recycle list + if(m_pFirstFree) + m_pFirstFree->m_pPrev = pBan; + pBan->m_pPrev = 0; + pBan->m_pNext = m_pFirstFree; + m_pFirstFree = pBan; + + // update ban count + --m_CountUsed; + + return 0; +} + +template<class T, int HashCount> +void CNetBan::CBanPool<T, HashCount>::Update(CBan<CDataType> *pBan, const CBanInfo *pInfo) +{ + pBan->m_Info = *pInfo; + + // remove from used list + if(pBan->m_pNext) + pBan->m_pNext->m_pPrev = pBan->m_pPrev; + if(pBan->m_pPrev) + pBan->m_pPrev->m_pNext = pBan->m_pNext; + else + m_pFirstUsed = pBan->m_pNext; + + // insert it into the used list + if(m_pFirstUsed) + { + for(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext) + { + if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) + { + // insert before + pBan->m_pNext = p; + pBan->m_pPrev = p->m_pPrev; + if(p->m_pPrev) + p->m_pPrev->m_pNext = pBan; + else + m_pFirstUsed = pBan; + p->m_pPrev = pBan; + break; + } + + if(!p->m_pNext) + { + // last entry + p->m_pNext = pBan; + pBan->m_pPrev = p; + pBan->m_pNext = 0; + break; + } + } + } + else + { + m_pFirstUsed = pBan; + pBan->m_pNext = pBan->m_pPrev = 0; + } +} + +template<class T, int HashCount> +void CNetBan::CBanPool<T, HashCount>::Reset() +{ + mem_zero(m_paaHashList, sizeof(m_paaHashList)); + mem_zero(m_aBans, sizeof(m_aBans)); + m_pFirstUsed = 0; + m_CountUsed = 0; + + for(int i = 1; i < MAX_BANS-1; ++i) + { + m_aBans[i].m_pNext = &m_aBans[i+1]; + m_aBans[i].m_pPrev = &m_aBans[i-1]; + } + + m_aBans[0].m_pNext = &m_aBans[1]; + m_aBans[MAX_BANS-1].m_pPrev = &m_aBans[MAX_BANS-2]; + m_pFirstFree = &m_aBans[0]; +} + +template<class T, int HashCount> +typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Get(int Index) const +{ + if(Index < 0 || Index >= Num()) + return 0; + + for(CNetBan::CBan<T> *pBan = m_pFirstUsed; pBan; pBan = pBan->m_pNext, --Index) + { + if(Index == 0) + return pBan; + } + + return 0; +} + + +template<class T> +void CNetBan::MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const +{ + if(pBan == 0) + { + if(BuffSize > 0) + pBuf[0] = 0; + return; + } + + // build type based part + char aBuf[256]; + if(Type == MSGTYPE_PLAYER) + str_copy(aBuf, "You have been banned", sizeof(aBuf)); + else + { + char aTemp[256]; + switch(Type) + { + case MSGTYPE_LIST: + str_format(aBuf, sizeof(aBuf), "%s banned", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; + case MSGTYPE_BANADD: + str_format(aBuf, sizeof(aBuf), "banned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; + case MSGTYPE_BANREM: + str_format(aBuf, sizeof(aBuf), "unbanned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; + default: + aBuf[0] = 0; + } + } + + // add info part + if(pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER) + { + int Mins = ((pBan->m_Info.m_Expires-time_timestamp()) + 59) / 60; + if(Mins <= 1) + str_format(pBuf, BuffSize, "%s for 1 minute (%s)", aBuf, pBan->m_Info.m_aReason); + else + str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason); + } + else + str_format(pBuf, BuffSize, "%s for life (%s)", aBuf, pBan->m_Info.m_aReason); +} + +template<class T> +int CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason) +{ + // do not ban localhost + if(NetMatch(pData, &m_LocalhostIPV4) || NetMatch(pData, &m_LocalhostIPV6)) + { + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban failed (localhost)"); + return -1; + } + + int Stamp = Seconds > 0 ? time_timestamp()+Seconds : CBanInfo::EXPIRES_NEVER; + + // set up info + CBanInfo Info = {0}; + Info.m_Expires = Stamp; + str_copy(Info.m_aReason, pReason, sizeof(Info.m_aReason)); + + // check if it already exists + CNetHash NetHash(pData); + CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); + if(pBan) + { + // adjust the ban + pBanPool->Update(pBan, &Info); + char aBuf[128]; + MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + return 1; + } + + // add ban and print result + pBan = pBanPool->Add(pData, &Info, &NetHash); + if(pBan) + { + char aBuf[128]; + MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + return 0; + } + else + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban failed (full banlist)"); + return -1; +} + +template<class T> +int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData) +{ + CNetHash NetHash(pData); + CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); + if(pBan) + { + char aBuf[256]; + MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM); + pBanPool->Remove(pBan); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + return 0; + } + else + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "unban failed (invalid entry)"); + return -1; +} + +void CNetBan::Init(IConsole *pConsole, IStorage *pStorage) +{ + m_pConsole = pConsole; + m_pStorage = pStorage; + m_BanAddrPool.Reset(); + m_BanRangePool.Reset(); + + net_host_lookup("localhost", &m_LocalhostIPV4, NETTYPE_IPV4); + net_host_lookup("localhost", &m_LocalhostIPV6, NETTYPE_IPV6); + + Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConBan, this, "Ban ip for x minutes for any reason"); + Console()->Register("ban_range", "ss?ir", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConBanRange, this, "Ban ip range for x minutes for any reason"); + Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConUnban, this, "Unban ip/banlist entry"); + Console()->Register("unban_range", "ss", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConUnbanRange, this, "Unban ip range"); + Console()->Register("unban_all", "", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConUnbanAll, this, "Unban all entries"); + Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConBans, this, "Show banlist"); + Console()->Register("bans_save", "s", CFGFLAG_SERVER|CFGFLAG_MASTER|CFGFLAG_STORE, ConBansSave, this, "Save banlist in a file"); +} + +void CNetBan::Update() +{ + int Now = time_timestamp(); + + // remove expired bans + char aBuf[256], aNetStr[256]; + while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now) + { + str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanAddrPool.First()->m_Data, aNetStr, sizeof(aNetStr))); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + m_BanAddrPool.Remove(m_BanAddrPool.First()); + } + while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now) + { + str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanRangePool.First()->m_Data, aNetStr, sizeof(aNetStr))); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + m_BanRangePool.Remove(m_BanRangePool.First()); + } +} + +int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason) +{ + return Ban(&m_BanAddrPool, pAddr, Seconds, pReason); +} + +int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason) +{ + if(pRange->IsValid()) + return Ban(&m_BanRangePool, pRange, Seconds, pReason); + + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban failed (invalid range)"); + return -1; +} + +int CNetBan::UnbanByAddr(const NETADDR *pAddr) +{ + return Unban(&m_BanAddrPool, pAddr); +} + +int CNetBan::UnbanByRange(const CNetRange *pRange) +{ + if(pRange->IsValid()) + return Unban(&m_BanRangePool, pRange); + + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban failed (invalid range)"); + return -1; +} + +int CNetBan::UnbanByIndex(int Index) +{ + int Result; + char aBuf[256]; + CBanAddr *pBan = m_BanAddrPool.Get(Index); + if(pBan) + { + NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); + Result = m_BanAddrPool.Remove(pBan); + } + else + { + CBanRange *pBan = m_BanRangePool.Get(Index-m_BanAddrPool.Num()); + if(pBan) + { + NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); + Result = m_BanRangePool.Remove(pBan); + } + else + { + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "unban failed (invalid index)"); + return -1; + } + } + + char aMsg[256]; + str_format(aMsg, sizeof(aMsg), "unbanned index %i (%s)", Index, aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg); + return Result; +} + +bool CNetBan::IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const +{ + CNetHash aHash[17]; + int Length = CNetHash::MakeHashArray(pAddr, aHash); + + // check ban adresses + CBanAddr *pBan = m_BanAddrPool.Find(pAddr, &aHash[Length]); + if(pBan) + { + MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); + return true; + } + + // check ban ranges + for(int i = Length-1; i >= 0; --i) + { + for(CBanRange *pBan = m_BanRangePool.First(&aHash[i]); pBan; pBan = pBan->m_pHashNext) + { + if(NetMatch(&pBan->m_Data, pAddr, i, Length)) + { + MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); + return true; + } + } + } + + return false; +} + +void CNetBan::ConBan(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + const char *pStr = pResult->GetString(0); + int Minutes = pResult->NumArguments()>1 ? clamp(pResult->GetInteger(1), 0, 44640) : 30; + const char *pReason = pResult->NumArguments()>2 ? pResult->GetString(2) : "No reason given"; + + NETADDR Addr; + if(net_addr_from_str(&Addr, pStr) == 0) + pThis->BanAddr(&Addr, Minutes*60, pReason); + else + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (invalid network address)"); +} + +void CNetBan::ConBanRange(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + const char *pStr1 = pResult->GetString(0); + const char *pStr2 = pResult->GetString(1); + int Minutes = pResult->NumArguments()>2 ? clamp(pResult->GetInteger(2), 0, 44640) : 30; + const char *pReason = pResult->NumArguments()>3 ? pResult->GetString(3) : "No reason given"; + + CNetRange Range; + if(net_addr_from_str(&Range.m_LB, pStr1) == 0 && net_addr_from_str(&Range.m_UB, pStr2) == 0) + pThis->BanRange(&Range, Minutes*60, pReason); + else + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "ban error (invalid range)"); +} + +void CNetBan::ConUnban(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + const char *pStr = pResult->GetString(0); + if(StrAllnum(pStr)) + pThis->UnbanByIndex(str_toint(pStr)); + else + { + NETADDR Addr; + if(net_addr_from_str(&Addr, pStr) == 0) + pThis->UnbanByAddr(&Addr); + else + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "unban error (invalid network address)"); + } +} + +void CNetBan::ConUnbanRange(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + const char *pStr1 = pResult->GetString(0); + const char *pStr2 = pResult->GetString(1); + + CNetRange Range; + if(net_addr_from_str(&Range.m_LB, pStr1) == 0 && net_addr_from_str(&Range.m_UB, pStr2) == 0) + pThis->UnbanByRange(&Range); + else + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "unban error (invalid range)"); +} + +void CNetBan::ConUnbanAll(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + pThis->UnbanAll(); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "unbanned all entries"); +} + +void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + int Count = 0; + char aBuf[256], aMsg[256]; + for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext) + { + pThis->MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); + str_format(aMsg, sizeof(aMsg), "#%i %s", Count++, aBuf); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg); + } + for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext) + { + pThis->MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); + str_format(aMsg, sizeof(aMsg), "#%i %s", Count++, aBuf); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg); + } + str_format(aMsg, sizeof(aMsg), "%d %s", Count, Count==1?"ban":"bans"); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg); +} + +void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser) +{ + CNetBan *pThis = static_cast<CNetBan *>(pUser); + + char aBuf[256]; + IOHANDLE File = pThis->Storage()->OpenFile(pResult->GetString(0), IOFLAG_WRITE, IStorage::TYPE_SAVE); + if(!File) + { + str_format(aBuf, sizeof(aBuf), "failed to save banlist to '%s'", pResult->GetString(0)); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); + return; + } + + int Now = time_timestamp(); + char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; + for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext) + { + int Min = pBan->m_Info.m_Expires>-1 ? (pBan->m_Info.m_Expires-Now+59)/60 : -1; + net_addr_str(&pBan->m_Data, aAddrStr1, sizeof(aAddrStr1), false); + str_format(aBuf, sizeof(aBuf), "ban %s %i %s", aAddrStr1, Min, pBan->m_Info.m_aReason); + io_write(File, aBuf, str_length(aBuf)); + io_write_newline(File); + } + for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext) + { + int Min = pBan->m_Info.m_Expires>-1 ? (pBan->m_Info.m_Expires-Now+59)/60 : -1; + net_addr_str(&pBan->m_Data.m_LB, aAddrStr1, sizeof(aAddrStr1), false); + net_addr_str(&pBan->m_Data.m_UB, aAddrStr2, sizeof(aAddrStr2), false); + str_format(aBuf, sizeof(aBuf), "ban_range %s %s %i %s", aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason); + io_write(File, aBuf, str_length(aBuf)); + io_write_newline(File); + } + + io_close(File); + str_format(aBuf, sizeof(aBuf), "saved banlist to '%s'", pResult->GetString(0)); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aBuf); +} diff --git a/src/engine/shared/netban.h b/src/engine/shared/netban.h new file mode 100644 index 00000000..447a838d --- /dev/null +++ b/src/engine/shared/netban.h @@ -0,0 +1,193 @@ +#ifndef ENGINE_SHARED_NETBAN_H +#define ENGINE_SHARED_NETBAN_H + +#include <base/system.h> + + +inline int NetComp(const NETADDR *pAddr1, const NETADDR *pAddr2) +{ + return mem_comp(pAddr1, pAddr2, pAddr1->type==NETTYPE_IPV4 ? 8 : 20); +} + +class CNetRange +{ +public: + NETADDR m_LB; + NETADDR m_UB; + + bool IsValid() const { return m_LB.type == m_UB.type && NetComp(&m_LB, &m_UB) < 0; } +}; + +inline int NetComp(const CNetRange *pRange1, const CNetRange *pRange2) +{ + return NetComp(&pRange1->m_LB, &pRange2->m_LB) || NetComp(&pRange1->m_UB, &pRange2->m_UB); +} + + +class CNetBan +{ +protected: + bool NetMatch(const NETADDR *pAddr1, const NETADDR *pAddr2) const + { + return NetComp(pAddr1, pAddr2) == 0; + } + + bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr, int Start, int Length) const + { + return pRange->m_LB.type == pAddr->type && + mem_comp(&pRange->m_LB.ip[Start], &pAddr->ip[Start], Length-Start) <= 0 && mem_comp(&pRange->m_UB.ip[Start], &pAddr->ip[Start], Length-Start) >= 0; + } + + bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr) const + { + return NetMatch(pRange, pAddr, 0, pRange->m_LB.type==NETTYPE_IPV4 ? 4 : 16); + } + + const char *NetToString(const NETADDR *pData, char *pBuffer, unsigned BufferSize) const + { + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(pData, aAddrStr, sizeof(aAddrStr), false); + str_format(pBuffer, BufferSize, "'%s'", aAddrStr); + return pBuffer; + } + + const char *NetToString(const CNetRange *pData, char *pBuffer, unsigned BufferSize) const + { + char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; + net_addr_str(&pData->m_LB, aAddrStr1, sizeof(aAddrStr1), false); + net_addr_str(&pData->m_UB, aAddrStr2, sizeof(aAddrStr2), false); + str_format(pBuffer, BufferSize, "'%s' - '%s'", aAddrStr1, aAddrStr2); + return pBuffer; + } + + // todo: move? + static bool StrAllnum(const char *pStr); + + class CNetHash + { + public: + int m_Hash; + int m_HashIndex; // matching parts for ranges, 0 for addr + + CNetHash() {} + CNetHash(const NETADDR *pAddr); + CNetHash(const CNetRange *pRange); + + static int MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]); + }; + + struct CBanInfo + { + enum + { + EXPIRES_NEVER=-1, + REASON_LENGTH=64, + }; + int m_Expires; + char m_aReason[REASON_LENGTH]; + }; + + template<class T> struct CBan + { + T m_Data; + CBanInfo m_Info; + CNetHash m_NetHash; + + // hash list + CBan *m_pHashNext; + CBan *m_pHashPrev; + + // used or free list + CBan *m_pNext; + CBan *m_pPrev; + }; + + template<class T, int HashCount> class CBanPool + { + public: + typedef T CDataType; + + CBan<CDataType> *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash); + int Remove(CBan<CDataType> *pBan); + void Update(CBan<CDataType> *pBan, const CBanInfo *pInfo); + void Reset(); + + int Num() const { return m_CountUsed; } + bool IsFull() const { return m_CountUsed == MAX_BANS; } + + CBan<CDataType> *First() const { return m_pFirstUsed; } + CBan<CDataType> *First(const CNetHash *pNetHash) const { return m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; } + CBan<CDataType> *Find(const CDataType *pData, const CNetHash *pNetHash) const + { + for(CBan<CDataType> *pBan = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; pBan; pBan = pBan->m_pHashNext) + { + if(NetComp(&pBan->m_Data, pData) == 0) + return pBan; + } + + return 0; + } + CBan<CDataType> *Get(int Index) const; + + private: + enum + { + MAX_BANS=1024, + }; + + CBan<CDataType> *m_paaHashList[HashCount][256]; + CBan<CDataType> m_aBans[MAX_BANS]; + CBan<CDataType> *m_pFirstFree; + CBan<CDataType> *m_pFirstUsed; + int m_CountUsed; + }; + + typedef CBanPool<NETADDR, 1> CBanAddrPool; + typedef CBanPool<CNetRange, 16> CBanRangePool; + typedef CBan<NETADDR> CBanAddr; + typedef CBan<CNetRange> CBanRange; + + template<class T> void MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const; + template<class T> int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason); + template<class T> int Unban(T *pBanPool, const typename T::CDataType *pData); + + class IConsole *m_pConsole; + class IStorage *m_pStorage; + CBanAddrPool m_BanAddrPool; + CBanRangePool m_BanRangePool; + NETADDR m_LocalhostIPV4, m_LocalhostIPV6; + +public: + enum + { + MSGTYPE_PLAYER=0, + MSGTYPE_LIST, + MSGTYPE_BANADD, + MSGTYPE_BANREM, + }; + + class IConsole *Console() const { return m_pConsole; } + class IStorage *Storage() const { return m_pStorage; } + + virtual ~CNetBan() {} + virtual void Init(class IConsole *pConsole, class IStorage *pStorage); + void Update(); + + virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); + virtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason); + int UnbanByAddr(const NETADDR *pAddr); + int UnbanByRange(const CNetRange *pRange); + int UnbanByIndex(int Index); + void UnbanAll() { m_BanAddrPool.Reset(); m_BanRangePool.Reset(); } + bool IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const; + + static void ConBan(class IConsole::IResult *pResult, void *pUser); + static void ConBanRange(class IConsole::IResult *pResult, void *pUser); + static void ConUnban(class IConsole::IResult *pResult, void *pUser); + static void ConUnbanRange(class IConsole::IResult *pResult, void *pUser); + static void ConUnbanAll(class IConsole::IResult *pResult, void *pUser); + static void ConBans(class IConsole::IResult *pResult, void *pUser); + static void ConBansSave(class IConsole::IResult *pResult, void *pUser); +}; + +#endif diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index ca6ca02a..cf2310bb 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -73,8 +73,6 @@ enum NET_CTRLMSG_ACCEPT=3, NET_CTRLMSG_CLOSE=4, - NET_SERVER_MAXBANS=1024, - NET_CONN_BUFFERSIZE=1024*32, NET_ENUM_TERMINATOR @@ -182,7 +180,7 @@ public: const char *ErrorString(); void SignalResend(); int State() const { return m_State; } - NETADDR PeerAddress() const { return m_PeerAddr; } + const NETADDR *PeerAddress() const { return &m_PeerAddr; } void ResetErrorString() { m_ErrorString[0] = 0; } const char *ErrorString() const { return m_ErrorString; } @@ -214,7 +212,7 @@ public: void Disconnect(const char *pReason); int State() const { return m_State; } - NETADDR PeerAddress() const { return m_PeerAddr; } + const NETADDR *PeerAddress() const { return &m_PeerAddr; } const char *ErrorString() const { return m_aErrorString; } void Reset(); @@ -249,6 +247,7 @@ public: { MAX_BANMASTERS=16 }; + struct CBanInfo { NETADDR m_Addr; @@ -263,31 +262,12 @@ private: CNetConnection m_Connection; }; - struct CBan - { - public: - CBanInfo m_Info; - - // hash list - CBan *m_pHashNext; - CBan *m_pHashPrev; - - // used or free list - CBan *m_pNext; - CBan *m_pPrev; - }; - - NETSOCKET m_Socket; + class CNetBan *m_pNetBan; CSlot m_aSlots[NET_MAX_CLIENTS]; int m_MaxClients; int m_MaxClientsPerIP; - CBan *m_aBans[256]; - CBan m_BanPool[NET_SERVER_MAXBANS]; - CBan *m_BanPool_FirstFree; - CBan *m_BanPool_FirstUsed; - NETADDR m_aBanmasters[MAX_BANMASTERS]; int m_NumBanmasters; @@ -297,13 +277,11 @@ private: CNetRecvUnpacker m_RecvUnpacker; - void BanRemoveByObject(CBan *pBan); - public: int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); // - bool Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int Flags); + bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags); int Close(); // @@ -314,20 +292,17 @@ public: // int Drop(int ClientID, const char *pReason); - // banning - int BanAdd(NETADDR Addr, int Seconds, const char *pReason); - int BanRemove(NETADDR Addr); - int BanNum(); // caution, slow - int BanGet(int Index, CBanInfo *pInfo); // caution, slow - // status requests - NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } NETSOCKET Socket() const { return m_Socket; } - int NetType() { return m_Socket.type; } + class CNetBan *NetBan() const { return m_pNetBan; } + int NetType() const { return m_Socket.type; } int MaxClients() const { return m_MaxClients; } // void SetMaxClientsPerIP(int Max); + + // int BanmasterAdd(const char *pAddrStr); int BanmasterNum() const; NETADDR* BanmasterGet(int Index); @@ -337,27 +312,13 @@ public: class CNetConsole { - enum - { - MAX_BANS=128, - }; - - int FindBan(NETADDR Addr); - void UpdateBans(); - - struct CBanEntry - { - NETADDR m_Addr; - int m_Expires; - } m_aBans[MAX_BANS]; - int m_NumBans; - struct CSlot { CConsoleNetConnection m_Connection; }; NETSOCKET m_Socket; + class CNetBan *m_pNetBan; CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS]; NETFUNC_NEWCLIENT m_pfnNewClient; @@ -370,7 +331,7 @@ public: void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); // - bool Open(NETADDR BindAddr, int Flags); + bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int Flags); int Close(); // @@ -382,10 +343,9 @@ public: int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); int Drop(int ClientID, const char *pReason); - bool AddBan(NETADDR Addr, int Seconds); - // status requests - NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + class CNetBan *NetBan() const { return m_pNetBan; } }; diff --git a/src/engine/shared/network_console.cpp b/src/engine/shared/network_console.cpp index 13ed3751..ded83f68 100644 --- a/src/engine/shared/network_console.cpp +++ b/src/engine/shared/network_console.cpp @@ -1,15 +1,21 @@ /* (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 <base/system.h> + +#include <engine/console.h> + +#include "netban.h" #include "network.h" -bool CNetConsole::Open(NETADDR BindAddr, int Flags) + +bool CNetConsole::Open(NETADDR BindAddr, CNetBan *pNetBan, int Flags) { // zero out the whole structure mem_zero(this, sizeof(*this)); m_Socket.type = NETTYPE_INVALID; m_Socket.ipv4sock = -1; m_Socket.ipv6sock = -1; + m_pNetBan = pNetBan; // open socket m_Socket = net_tcp_create(BindAddr); @@ -56,7 +62,7 @@ int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) { char aError[256] = { 0 }; int FreeSlot = -1; - + // look for free slot or multiple client for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) { @@ -64,8 +70,7 @@ int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) FreeSlot = i; if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) { - NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); - if(net_addr_comp(pAddr, &PeerAddr) == 0) + if(net_addr_comp(pAddr, m_aSlots[i].m_Connection.PeerAddress()) == 0) { str_copy(aError, "only one client per IP allowed", sizeof(aError)); break; @@ -99,26 +104,16 @@ int CNetConsole::Update() if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0) { - int Index = FindBan(Addr); - if(Index == -1) - AcceptClient(Socket, &Addr); - else + // check if we just should drop the packet + char aBuf[128]; + if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { - char aBuf[128]; - if(m_aBans[Index].m_Expires > -1) - { - int Mins = (m_aBans[Index].m_Expires-time_timestamp()+ 59) / 60; - if(Mins <= 1) - str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute"); - else - str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins); - } - else - str_format(aBuf, sizeof(aBuf), "You have been banned for life"); - + // banned, reply with a message and drop net_tcp_send(Socket, aBuf, str_length(aBuf)); net_tcp_close(Socket); } + else + AcceptClient(Socket, &Addr); } for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) @@ -129,8 +124,6 @@ int CNetConsole::Update() Drop(i, m_aSlots[i].m_Connection.ErrorString()); } - UpdateBans(); - return 0; } @@ -155,65 +148,3 @@ int CNetConsole::Send(int ClientID, const char *pLine) else return -1; } - -int CNetConsole::FindBan(NETADDR Addr) -{ - Addr.port = 0; - for(int i = 0; i < m_NumBans; i++) - if(net_addr_comp(&m_aBans[i].m_Addr, &Addr) == 0) - return i; - - return -1; -} - -bool CNetConsole::AddBan(NETADDR Addr, int Seconds) -{ - if(m_NumBans == MAX_BANS) - return false; - - Addr.port = 0; - int Index = FindBan(Addr); - if(Index == -1) - { - Index = m_NumBans++; - m_aBans[Index].m_Addr = Addr; - } - m_aBans[Index].m_Expires = Seconds>0 ? time_timestamp()+Seconds : -1; - - for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) - { - if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) - { - NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); - PeerAddr.port = 0; - if(net_addr_comp(&Addr, &PeerAddr) == 0) - { - char aBuf[128]; - if(Seconds>0) - { - int Mins = (Seconds + 59) / 60; - if(Mins <= 1) - str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute"); - else - str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins); - } - else - str_format(aBuf, sizeof(aBuf), "You have been banned for life"); - Drop(i, aBuf); - } - } - } - return true; -} - -void CNetConsole::UpdateBans() -{ - int Now = time_timestamp(); - for(int i = 0; i < m_NumBans; ++i) - if(m_aBans[i].m_Expires > 0 && m_aBans[i].m_Expires < Now) - { - m_aBans[i] = m_aBans[m_NumBans-1]; - --m_NumBans; - break; - } -} diff --git a/src/engine/shared/network_console_conn.cpp b/src/engine/shared/network_console_conn.cpp index 75b581fa..9bc163af 100644 --- a/src/engine/shared/network_console_conn.cpp +++ b/src/engine/shared/network_console_conn.cpp @@ -181,6 +181,6 @@ int CConsoleNetConnection::Send(const char *pLine) pData += Send; Length -= Send; } - + return 0; } diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp index 73e3cf17..e2e0d16b 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -1,33 +1,15 @@ /* (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 <base/system.h> -#include <banmaster/banmaster.h> -#include "network.h" -#define MACRO_LIST_LINK_FIRST(Object, First, Prev, Next) \ - { if(First) First->Prev = Object; \ - Object->Prev = (struct CBan *)0; \ - Object->Next = First; \ - First = Object; } - -#define MACRO_LIST_LINK_AFTER(Object, After, Prev, Next) \ - { Object->Prev = After; \ - Object->Next = After->Next; \ - After->Next = Object; \ - if(Object->Next) \ - Object->Next->Prev = Object; \ - } +#include <engine/console.h> +#include <banmaster/banmaster.h> -#define MACRO_LIST_UNLINK(Object, First, Prev, Next) \ - { if(Object->Next) Object->Next->Prev = Object->Prev; \ - if(Object->Prev) Object->Prev->Next = Object->Next; \ - else First = Object->Next; \ - Object->Next = 0; Object->Prev = 0; } +#include "netban.h" +#include "network.h" -#define MACRO_LIST_FIND(Start, Next, Expression) \ - { while(Start && !(Expression)) Start = Start->Next; } -bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int Flags) +bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags) { // zero out the whole structure mem_zero(this, sizeof(*this)); @@ -37,6 +19,8 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int if(!m_Socket.type) return false; + m_pNetBan = pNetBan; + // clamp clients m_MaxClients = MaxClients; if(m_MaxClients > NET_MAX_CLIENTS) @@ -49,17 +33,6 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aSlots[i].m_Connection.Init(m_Socket); - // setup all pointers for bans - for(int i = 1; i < NET_SERVER_MAXBANS-1; i++) - { - m_BanPool[i].m_pNext = &m_BanPool[i+1]; - m_BanPool[i].m_pPrev = &m_BanPool[i-1]; - } - - m_BanPool[0].m_pNext = &m_BanPool[1]; - m_BanPool[NET_SERVER_MAXBANS-1].m_pPrev = &m_BanPool[NET_SERVER_MAXBANS-2]; - m_BanPool_FirstFree = &m_BanPool[0]; - return true; } @@ -95,159 +68,8 @@ int CNetServer::Drop(int ClientID, const char *pReason) return 0; } -int CNetServer::BanGet(int Index, CBanInfo *pInfo) -{ - CBan *pBan; - for(pBan = m_BanPool_FirstUsed; pBan && Index; pBan = pBan->m_pNext, Index--) - {} - - if(!pBan) - return 0; - *pInfo = pBan->m_Info; - return 1; -} - -int CNetServer::BanNum() -{ - int Count = 0; - CBan *pBan; - for(pBan = m_BanPool_FirstUsed; pBan; pBan = pBan->m_pNext) - Count++; - return Count; -} - -void CNetServer::BanRemoveByObject(CBan *pBan) -{ - int IpHash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3]+ - pBan->m_Info.m_Addr.ip[4]+pBan->m_Info.m_Addr.ip[5]+pBan->m_Info.m_Addr.ip[6]+pBan->m_Info.m_Addr.ip[7]+ - pBan->m_Info.m_Addr.ip[8]+pBan->m_Info.m_Addr.ip[9]+pBan->m_Info.m_Addr.ip[10]+pBan->m_Info.m_Addr.ip[11]+ - pBan->m_Info.m_Addr.ip[12]+pBan->m_Info.m_Addr.ip[13]+pBan->m_Info.m_Addr.ip[14]+pBan->m_Info.m_Addr.ip[15])&0xff; - char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&pBan->m_Info.m_Addr, aAddrStr, sizeof(aAddrStr)); - dbg_msg("netserver", "removing ban on %s", aAddrStr); - MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); - MACRO_LIST_UNLINK(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); - MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); -} - -int CNetServer::BanRemove(NETADDR Addr) -{ - int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3]+Addr.ip[4]+Addr.ip[5]+Addr.ip[6]+Addr.ip[7]+ - Addr.ip[8]+Addr.ip[9]+Addr.ip[10]+Addr.ip[11]+Addr.ip[12]+Addr.ip[13]+Addr.ip[14]+Addr.ip[15])&0xff; - CBan *pBan = m_aBans[IpHash]; - - MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); - - if(pBan) - { - BanRemoveByObject(pBan); - return 0; - } - - return -1; -} - -int CNetServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason) -{ - int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3]+Addr.ip[4]+Addr.ip[5]+Addr.ip[6]+Addr.ip[7]+ - Addr.ip[8]+Addr.ip[9]+Addr.ip[10]+Addr.ip[11]+Addr.ip[12]+Addr.ip[13]+Addr.ip[14]+Addr.ip[15])&0xff; - int Stamp = -1; - CBan *pBan; - - // remove the port - Addr.port = 0; - - if(Seconds) - Stamp = time_timestamp() + Seconds; - - // search to see if it already exists - pBan = m_aBans[IpHash]; - MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); - if(pBan) - { - // adjust the ban - pBan->m_Info.m_Expires = Stamp; - return 0; - } - - if(!m_BanPool_FirstFree) - return -1; - - // fetch and clear the new ban - pBan = m_BanPool_FirstFree; - MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); - - // setup the ban info - pBan->m_Info.m_Expires = Stamp; - pBan->m_Info.m_Addr = Addr; - str_copy(pBan->m_Info.m_Reason, pReason, sizeof(pBan->m_Info.m_Reason)); - - // add it to the ban hash - MACRO_LIST_LINK_FIRST(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); - - // insert it into the used list - { - if(m_BanPool_FirstUsed) - { - CBan *pInsertAfter = m_BanPool_FirstUsed; - MACRO_LIST_FIND(pInsertAfter, m_pNext, Stamp < pInsertAfter->m_Info.m_Expires); - - if(pInsertAfter) - pInsertAfter = pInsertAfter->m_pPrev; - else - { - // add to last - pInsertAfter = m_BanPool_FirstUsed; - while(pInsertAfter->m_pNext) - pInsertAfter = pInsertAfter->m_pNext; - } - - if(pInsertAfter) - { - MACRO_LIST_LINK_AFTER(pBan, pInsertAfter, m_pPrev, m_pNext); - } - else - { - MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); - } - } - else - { - MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); - } - } - - // drop banned clients - { - char Buf[128]; - NETADDR BanAddr; - - if(Stamp > -1) - { - int Mins = (Seconds + 59) / 60; - if(Mins <= 1) - str_format(Buf, sizeof(Buf), "You have been banned for 1 minute (%s)", pReason); - else - str_format(Buf, sizeof(Buf), "You have been banned for %d minutes (%s)", Mins, pReason); - } - else - str_format(Buf, sizeof(Buf), "You have been banned for life (%s)", pReason); - - for(int i = 0; i < MaxClients(); i++) - { - BanAddr = m_aSlots[i].m_Connection.PeerAddress(); - BanAddr.port = 0; - - if(net_addr_comp(&Addr, &BanAddr) == 0) - Drop(i, Buf); - } - } - return 0; -} - int CNetServer::Update() { - int Now = time_timestamp(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); @@ -255,13 +77,6 @@ int CNetServer::Update() Drop(i, m_aSlots[i].m_Connection.ErrorString()); } - // remove expired bans - while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires > -1 && m_BanPool_FirstUsed->m_Info.m_Expires < Now) - { - CBan *pBan = m_BanPool_FirstUsed; - BanRemoveByObject(pBan); - } - return 0; } @@ -270,8 +85,6 @@ int CNetServer::Update() */ int CNetServer::Recv(CNetChunk *pChunk) { - unsigned Now = time_timestamp(); - while(1) { NETADDR Addr; @@ -289,36 +102,12 @@ int CNetServer::Recv(CNetChunk *pChunk) if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { - CBan *pBan = 0; - NETADDR BanAddr = Addr; - int IpHash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3]+BanAddr.ip[4]+BanAddr.ip[5]+BanAddr.ip[6]+BanAddr.ip[7]+ - BanAddr.ip[8]+BanAddr.ip[9]+BanAddr.ip[10]+BanAddr.ip[11]+BanAddr.ip[12]+BanAddr.ip[13]+BanAddr.ip[14]+BanAddr.ip[15])&0xff; - int Found = 0; - BanAddr.port = 0; - - // search a ban - for(pBan = m_aBans[IpHash]; pBan; pBan = pBan->m_pHashNext) - { - if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) - break; - } - // check if we just should drop the packet - if(pBan) + char aBuf[128]; + if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { // banned, reply with a message - char BanStr[128]; - if(pBan->m_Info.m_Expires > -1) - { - int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60; - if(Mins <= 1) - str_format(BanStr, sizeof(BanStr), "Banned for 1 minute (%s)", pBan->m_Info.m_Reason); - else - str_format(BanStr, sizeof(BanStr), "Banned for %d minutes (%s)", Mins, pBan->m_Info.m_Reason); - } - else - str_format(BanStr, sizeof(BanStr), "Banned for life (%s)", pBan->m_Info.m_Reason); - CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1); + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); continue; } @@ -336,16 +125,15 @@ int CNetServer::Recv(CNetChunk *pChunk) // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { - Found = 0; + bool Found = false; // check if we already got this client for(int i = 0; i < MaxClients(); i++) { - NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && - net_addr_comp(&PeerAddr, &Addr) == 0) + net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { - Found = 1; // silent ignore.. we got this client already + Found = true; // silent ignore.. we got this client already break; } } @@ -356,7 +144,7 @@ int CNetServer::Recv(CNetChunk *pChunk) CNetChunk Packet; char aBuffer[sizeof(BANMASTER_IPCHECK) + NETADDR_MAXSTRSIZE]; mem_copy(aBuffer, BANMASTER_IPCHECK, sizeof(BANMASTER_IPCHECK)); - net_addr_str(&Addr, aBuffer + sizeof(BANMASTER_IPCHECK), sizeof(aBuffer) - sizeof(BANMASTER_IPCHECK)); + net_addr_str(&Addr, aBuffer + sizeof(BANMASTER_IPCHECK), sizeof(aBuffer) - sizeof(BANMASTER_IPCHECK), 0); Packet.m_ClientID = -1; Packet.m_Flags = NETSENDFLAG_CONNLESS; @@ -378,7 +166,7 @@ int CNetServer::Recv(CNetChunk *pChunk) if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) continue; - OtherAddr = m_aSlots[i].m_Connection.PeerAddress(); + OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); OtherAddr.port = 0; if(!net_addr_comp(&ThisAddr, &OtherAddr)) { @@ -396,7 +184,7 @@ int CNetServer::Recv(CNetChunk *pChunk) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { - Found = 1; + Found = true; m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); if(m_pfnNewClient) m_pfnNewClient(i, m_UserPtr); @@ -417,8 +205,7 @@ int CNetServer::Recv(CNetChunk *pChunk) // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { - NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); - if(net_addr_comp(&PeerAddr, &Addr) == 0) + if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) { diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index d6f83dc8..a9ccdc49 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -30,7 +30,7 @@ public: m_aUserdir[0] = 0; } - int Init(const char *pApplicationName, int NumArgs, const char **ppArguments) + int Init(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments) { // get userdir fs_storage_path(pApplicationName, m_aUserdir, sizeof(m_aUserdir)); @@ -52,14 +52,17 @@ public: } // add save directories - if(m_NumPaths && (!m_aaStoragePaths[TYPE_SAVE][0] || !fs_makedir(m_aaStoragePaths[TYPE_SAVE]))) + if(StorageType != STORAGETYPE_BASIC && m_NumPaths && (!m_aaStoragePaths[TYPE_SAVE][0] || !fs_makedir(m_aaStoragePaths[TYPE_SAVE]))) { char aPath[MAX_PATH_LENGTH]; - fs_makedir(GetPath(TYPE_SAVE, "screenshots", aPath, sizeof(aPath))); - fs_makedir(GetPath(TYPE_SAVE, "screenshots/auto", aPath, sizeof(aPath))); - fs_makedir(GetPath(TYPE_SAVE, "maps", aPath, sizeof(aPath))); + if(StorageType == STORAGETYPE_CLIENT) + { + fs_makedir(GetPath(TYPE_SAVE, "screenshots", aPath, sizeof(aPath))); + fs_makedir(GetPath(TYPE_SAVE, "screenshots/auto", aPath, sizeof(aPath))); + fs_makedir(GetPath(TYPE_SAVE, "maps", aPath, sizeof(aPath))); + fs_makedir(GetPath(TYPE_SAVE, "downloadedmaps", aPath, sizeof(aPath))); + } fs_makedir(GetPath(TYPE_SAVE, "dumps", aPath, sizeof(aPath))); - fs_makedir(GetPath(TYPE_SAVE, "downloadedmaps", aPath, sizeof(aPath))); fs_makedir(GetPath(TYPE_SAVE, "demos", aPath, sizeof(aPath))); fs_makedir(GetPath(TYPE_SAVE, "demos/auto", aPath, sizeof(aPath))); } @@ -377,10 +380,22 @@ public: return !fs_makedir(GetPath(Type, pFoldername, aBuffer, sizeof(aBuffer))); } - static IStorage *Create(const char *pApplicationName, int NumArgs, const char **ppArguments) + virtual void GetCompletePath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize) + { + if(Type < 0 || Type >= m_NumPaths) + { + if(BufferSize > 0) + pBuffer[0] = 0; + return; + } + + GetPath(Type, pDir, pBuffer, BufferSize); + } + + static IStorage *Create(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments) { CStorage *p = new CStorage(); - if(p && p->Init(pApplicationName, NumArgs, ppArguments)) + if(p && p->Init(pApplicationName, StorageType, NumArgs, ppArguments)) { dbg_msg("storage", "initialisation failed"); delete p; @@ -390,4 +405,4 @@ public: } }; -IStorage *CreateStorage(const char *pApplicationName, int NumArgs, const char **ppArguments) { return CStorage::Create(pApplicationName, NumArgs, ppArguments); } +IStorage *CreateStorage(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments) { return CStorage::Create(pApplicationName, StorageType, NumArgs, ppArguments); } |