diff options
| -rw-r--r-- | src/engine/server/econ.cpp | 0 | ||||
| -rw-r--r-- | src/engine/server/econ.h | 0 | ||||
| -rw-r--r-- | src/engine/server/server.cpp | 116 | ||||
| -rw-r--r-- | src/engine/server/server.h | 25 | ||||
| -rw-r--r-- | src/engine/shared/config_variables.h | 4 | ||||
| -rw-r--r-- | src/engine/shared/network.h | 78 | ||||
| -rw-r--r-- | src/engine/shared/network_console.cpp | 144 | ||||
| -rw-r--r-- | src/engine/shared/network_console_conn.cpp | 176 |
8 files changed, 539 insertions, 4 deletions
diff --git a/src/engine/server/econ.cpp b/src/engine/server/econ.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/engine/server/econ.cpp diff --git a/src/engine/server/econ.h b/src/engine/server/econ.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/engine/server/econ.h diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 2b174610..8e151035 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -317,8 +317,15 @@ int CServer::Init() m_aClients[i].m_Snapshots.Init(); } + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + m_aEconClients[i].m_State = CEconClient::STATE_EMPTY; + } + m_CurrentGameTick = 0; + m_UseEcon = 0; + return 0; } @@ -611,6 +618,30 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) return 0; } +int CServer::NewConsoleClientCallback(int EconID, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + pThis->m_aEconClients[EconID].m_State = CEconClient::STATE_CONNECTED; + pThis->m_NetConsole.SetTimeout(EconID, g_Config.m_SvEconAuthTimeout); + return 0; +} + +int CServer::DelConsoleClientCallback(int EconID, const char *pReason, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + + NETADDR Addr = pThis->m_NetConsole.ClientAddr(EconID); + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "econ client dropped. eid=%d addr=%s reason='%s'", EconID, aAddrStr, pReason); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); + + pThis->m_aEconClients[EconID].m_State = CEconClient::STATE_EMPTY; + return 0; +} + + void CServer::SendMap(int ClientID) { CMsgPacker Msg(NETMSG_MAP_CHANGE); @@ -633,7 +664,12 @@ void CServer::SendRconLine(int ClientID, const char *pLine) SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); } -void CServer::SendRconLineAuthed(const char *pLine, void *pUser) +void CServer::SendEconLine(int EconID, const char *pLine) +{ + m_NetConsole.Send(EconID, pLine); +} + +void CServer::SendConsoleLineAuthed(const char *pLine, void *pUser) { CServer *pThis = (CServer *)pUser; static volatile int ReentryGuard = 0; @@ -648,6 +684,15 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser) pThis->SendRconLine(i, pLine); } + if(pThis->m_UseEcon) + { + for(i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(pThis->m_aEconClients[i].m_State == CEconClient::STATE_AUTHED) + pThis->SendEconLine(i, pLine); + } + } + ReentryGuard--; } @@ -1084,6 +1129,47 @@ void CServer::PumpNetwork() else ProcessClientPacket(&Packet); } + + if(m_UseEcon) + EconPumpNetwork(); +} + +void CServer::EconPumpNetwork() +{ + m_NetConsole.Update(); + + char aBuf[NET_MAX_PACKETSIZE]; + int EconID; + + while(m_NetConsole.Recv(aBuf, sizeof(aBuf) - 1, &EconID)) + { + dbg_assert(m_aEconClients[EconID].m_State != CEconClient::STATE_EMPTY, "got message from empty slot"); + if(m_aEconClients[EconID].m_State == CEconClient::STATE_CONNECTED) + { + if(str_comp(aBuf, g_Config.m_SvRconPassword) == 0) + { + m_aEconClients[EconID].m_State = CEconClient::STATE_AUTHED; + m_NetConsole.Send(EconID, "Authentication successful. Remote console access granted."); + m_NetConsole.SetTimeout(EconID, g_Config.m_SvEconTimeout); + + str_format(aBuf, sizeof(aBuf), "EconID=%d authed", EconID); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + } + else + { + m_NetConsole.Send(EconID, "Wrong password"); + } + } + else if(m_aEconClients[EconID].m_State == CEconClient::STATE_AUTHED) + { + char aFormatted[256]; + str_format(aFormatted, sizeof(aBuf), "eid=%d cmd='%s'", EconID, aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aFormatted); + m_RconClientID = EconID; + Console()->ExecuteLine(aBuf); + m_RconClientID = -1; + } + } } char *CServer::GetMapName() @@ -1158,7 +1244,7 @@ int CServer::Run() m_pStorage = Kernel()->RequestInterface<IStorage>(); // - Console()->RegisterPrintCallback(SendRconLineAuthed, this); + Console()->RegisterPrintCallback(SendConsoleLineAuthed, this); // load map if(!LoadMap(g_Config.m_SvMap)) @@ -1181,7 +1267,6 @@ int CServer::Run() BindAddr.port = g_Config.m_SvPort; } - if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0)) { dbg_msg("server", "couldn't open socket. port might already be in use"); @@ -1190,6 +1275,31 @@ int CServer::Run() m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + if(g_Config.m_SvEconPort && g_Config.m_SvRconPassword[0]) + { + dbg_msg("econ", "binding econ to %s:%d", g_Config.m_SvEconBindaddr, g_Config.m_SvEconPort); + if(g_Config.m_SvEconBindaddr[0] && net_host_lookup(g_Config.m_SvEconBindaddr, &BindAddr, NETTYPE_ALL) == 0) + { + BindAddr.port = g_Config.m_SvEconPort; + } + else + { + mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.type = NETTYPE_ALL; + BindAddr.port = g_Config.m_SvEconPort; + } + + if(m_NetConsole.Open(BindAddr, 0)) + { + m_NetConsole.SetCallbacks(NewConsoleClientCallback, DelConsoleClientCallback, this); + m_UseEcon = 1; + } + else + { + dbg_msg("econ", "couldn't open econ socket. port might already be in use"); + } + } + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 5b6e038d..d744b9ff 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -109,10 +109,26 @@ public: CClient m_aClients[MAX_CLIENTS]; + class CEconClient + { + public: + enum + { + STATE_EMPTY=0, + STATE_CONNECTED, + STATE_AUTHED + }; + + int m_State; + }; + + CEconClient m_aEconClients[NET_MAX_CONSOLE_CLIENTS]; + CSnapshotDelta m_SnapshotDelta; CSnapshotBuilder m_SnapshotBuilder; CSnapIDPool m_IDPool; CNetServer m_NetServer; + CNetConsole m_NetConsole; IEngineMap *m_pMap; @@ -123,6 +139,8 @@ public: int m_RconClientID; int m_RconAuthLevel; + int m_UseEcon; + int64 m_Lastheartbeat; //static NETADDR4 master_server; @@ -168,10 +186,14 @@ public: static int NewClientCallback(int ClientID, void *pUser); static int DelClientCallback(int ClientID, const char *pReason, void *pUser); + static int NewConsoleClientCallback(int EconID, void *pUser); + static int DelConsoleClientCallback(int EconID, const char *pReason, void *pUser); + void SendMap(int ClientID); void SendConnectionReady(int ClientID); void SendRconLine(int ClientID, const char *pLine); - static void SendRconLineAuthed(const char *pLine, void *pUser); + void SendEconLine(int EconID, const char *pLine); + static void SendConsoleLineAuthed(const char *pLine, void *pUser); void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID); void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID); @@ -185,6 +207,7 @@ public: int BanAdd(NETADDR Addr, int Seconds, const char *pReason); int BanRemove(NETADDR Addr); + void EconPumpNetwork(); void PumpNetwork(); diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 213ebf26..fd318a7d 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -86,6 +86,10 @@ MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remo MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)") MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication") MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick") +MACRO_CONFIG_STR(SvEconBindaddr, sv_econ_bindaddr, 128, "localhost", CFGFLAG_SERVER, "Address to bind the external console to. Anything but 'localhost' is dangerous") +MACRO_CONFIG_INT(SvEconPort, sv_econ_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the external console") +MACRO_CONFIG_INT(SvEconAuthTimeout, sv_econ_auth_timeout, 30, 1, 120, CFGFLAG_SERVER, "Time in seconds before the the econ authentification times out") +MACRO_CONFIG_INT(SvEconTimeout, sv_econ_timeout, 300, 1, 3600, CFGFLAG_SERVER, "Time in seconds before the econ connection times out") MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode") MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems") diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index 228ba6dd..d0b78d05 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -49,6 +49,7 @@ enum NET_MAX_CHUNKHEADERSIZE = 5, NET_PACKETHEADERSIZE = 3, NET_MAX_CLIENTS = 16, + NET_MAX_CONSOLE_CLIENTS = 16, NET_MAX_SEQUENCE = 1<<10, NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, @@ -192,6 +193,41 @@ public: int AckSequence() const { return m_Ack; } }; +class CConsoleNetConnection +{ +private: + unsigned m_State; + + NETADDR m_PeerAddr; + NETSOCKET m_Socket; + + char m_aBuffer[NET_MAX_PACKETSIZE]; + char *m_pBufferPos; + + char m_aErrorString[256]; + + int m_Timeout; + int64 m_LastRecvTime; + +public: + void Init(NETSOCKET Socket); + void Init(NETSOCKET Socket, const NETADDR *pAddr); + int Connect(const NETADDR *pAddr); + void Disconnect(const char *pReason); + + int State() const { return m_State; } + NETADDR PeerAddress() const { return m_PeerAddr; } + const char *ErrorString() const { return m_aErrorString; } + + void SetTimeout(int Timeout) { m_Timeout = Timeout; } + int Timeout() const { return m_Timeout; } + + void Reset(); + int Update(); + int Send(const char *pLine); + int Recv(char *pLine, int MaxLength); +}; + class CNetRecvUnpacker { public: @@ -292,6 +328,48 @@ public: void SetMaxClientsPerIP(int Max); }; +class CNetConsole +{ +private: + struct CSlot + { + CConsoleNetConnection m_Connection; + }; + + NETSOCKET m_Socket; + CSlot m_aSlots[NET_MAX_CLIENTS]; + + NETFUNC_NEWCLIENT m_pfnNewClient; + NETFUNC_DELCLIENT m_pfnDelClient; + void *m_UserPtr; + + CNetRecvUnpacker m_RecvUnpacker; + +public: + int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + + // + bool Open(NETADDR BindAddr, int Flags); + int Close(); + + // + int Broadcast(const char *pLine); + int Recv(char *pLine, int MaxLength, int *pClientID = 0); + int Send(int ClientID, const char *pLine); + int Update(); + + // + int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); + int Drop(int ClientID, const char *pReason); + + // + void SetTimeout(int ClientID, int Timeout) { m_aSlots[ClientID].m_Connection.SetTimeout(Timeout); } + + // status requests + int Timeout(int ClientID) { return m_aSlots[ClientID].m_Connection.Timeout(); } + NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } +}; + // client side diff --git a/src/engine/shared/network_console.cpp b/src/engine/shared/network_console.cpp new file mode 100644 index 00000000..3e50a1ac --- /dev/null +++ b/src/engine/shared/network_console.cpp @@ -0,0 +1,144 @@ +/* (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 "network.h" + +bool CNetConsole::Open(NETADDR BindAddr, int Flags) +{ + // zero out the whole structure + mem_zero(this, sizeof(*this)); + + // open socket + m_Socket = net_tcp_create(&BindAddr); + if(!m_Socket.type) + return false; + if(net_tcp_listen(m_Socket, NET_MAX_CONSOLE_CLIENTS)) + return false; + net_tcp_set_non_blocking(m_Socket); + + for(int i = 0; i < NET_MAX_CLIENTS; i++) + m_aSlots[i].m_Connection.Init(m_Socket); + + return true; +} + +int CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) +{ + m_pfnNewClient = pfnNewClient; + m_pfnDelClient = pfnDelClient; + m_UserPtr = pUser; + return 0; +} + +int CNetConsole::Close() +{ + // TODO: implement me + return 0; +} + +int CNetConsole::Drop(int ClientID, const char *pReason) +{ + NETADDR Addr = ClientAddr(ClientID); + + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + + if(m_pfnDelClient) + m_pfnDelClient(ClientID, pReason, m_UserPtr); + + m_aSlots[ClientID].m_Connection.Disconnect(pReason); + + return 0; +} + +int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) +{ + char aError[256] = { 0 }; + int FreeSlot = -1; + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) + 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) + { + str_copy(aError, "Only one client per IP allowed", sizeof(aError)); + break; + } + } + } + + if(!aError[0] && FreeSlot != -1) + { + m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr); + if(m_pfnNewClient) + m_pfnNewClient(FreeSlot, m_UserPtr); + return 1; + } + + if(!aError[0]) + { + str_copy(aError, "No free slot available", sizeof(aError)); + } + + dbg_msg("netconsole", "refused client, reason=\"%s\"", aError); + + net_tcp_send(Socket, aError, str_length(aError)); + net_tcp_close(Socket); + + return 0; +} + +int CNetConsole::Update() +{ + NETSOCKET Socket; + NETADDR Addr; + + while(net_tcp_accept(m_Socket, &Socket, &Addr) > 0) + { + AcceptClient(Socket, &Addr); + } + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) + m_aSlots[i].m_Connection.Update(); + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + Drop(i, m_aSlots[i].m_Connection.ErrorString()); + } + + return 0; +} + +int CNetConsole::Recv(char *pLine, int MaxLength, int *pClientID) +{ + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) + { + if(pClientID) + *pClientID = i; + return 1; + } + } + return 0; +} + +int CNetConsole::Broadcast(const char *pLine) +{ + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) + Send(i, pLine); + } + return 0; +} + +int CNetConsole::Send(int ClientID, const char *pLine) +{ + return m_aSlots[ClientID].m_Connection.Send(pLine); +} + diff --git a/src/engine/shared/network_console_conn.cpp b/src/engine/shared/network_console_conn.cpp new file mode 100644 index 00000000..4dd971a6 --- /dev/null +++ b/src/engine/shared/network_console_conn.cpp @@ -0,0 +1,176 @@ +/* (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 "config.h" +#include "network.h" + +void CConsoleNetConnection::Reset() +{ + m_State = NET_CONNSTATE_OFFLINE; + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + m_aErrorString[0] = 0; + + m_aBuffer[0] = 0; + m_pBufferPos = 0; + m_Timeout = 0; +} + +void CConsoleNetConnection::Init(NETSOCKET Socket, const NETADDR *pAddr) +{ + Reset(); + + m_Socket = Socket; + net_tcp_set_non_blocking(m_Socket); + + m_LastRecvTime = time_get(); + + m_PeerAddr = *pAddr; + m_State = NET_CONNSTATE_ONLINE; +} + +void CConsoleNetConnection::Init(NETSOCKET Socket) +{ + Reset(); + + m_Socket = Socket; + net_tcp_set_non_blocking(m_Socket); + + m_LastRecvTime = time_get(); +} + +int CConsoleNetConnection::Connect(const NETADDR *pAddr) +{ + if(State() != NET_CONNSTATE_OFFLINE) + return -1; + + // init connection + Reset(); + m_PeerAddr = *pAddr; + net_tcp_connect(m_Socket, pAddr); + m_State = NET_CONNSTATE_ONLINE; + return 0; +} + +void CConsoleNetConnection::Disconnect(const char *pReason) +{ + if(State() == NET_CONNSTATE_OFFLINE) + return; + + if(pReason) + { + char aBuf[sizeof(pReason) + 4]; + str_format(aBuf, sizeof(aBuf), "%s", pReason); + Send(aBuf); + } + + net_tcp_close(m_Socket); + + Reset(); +} + +int CConsoleNetConnection::Update() +{ + if(m_Timeout && time_get() > m_LastRecvTime + m_Timeout * time_freq()) + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "timeout", sizeof(m_aErrorString)); + return -1; + } + + if(State() == NET_CONNSTATE_ONLINE) + { + char aBuf[NET_MAX_PACKETSIZE]; + + int Bytes = net_tcp_recv(m_Socket, aBuf, sizeof(aBuf) - 1); + + if(Bytes > 0) + { + aBuf[Bytes - 1] = 0; + + if(!m_pBufferPos) + m_aBuffer[0] = 0; + else if(m_pBufferPos != m_aBuffer) + mem_move(m_pBufferPos, m_aBuffer, str_length(m_pBufferPos) + 1); // +1 for the \0 + m_pBufferPos = m_aBuffer; + + str_append(m_aBuffer, aBuf, sizeof(m_aBuffer)); + + m_LastRecvTime = time_get(); + } + else if(Bytes < 0) + { + if(net_would_block()) // no data received + return 0; + + m_State = NET_CONNSTATE_ERROR; // error + str_copy(m_aErrorString, "connection failure", sizeof(m_aErrorString)); + return -1; + } + else + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "remote end closed the connection", sizeof(m_aErrorString)); + return -1; + } + } + + return 0; +} + +int CConsoleNetConnection::Recv(char *pLine, int MaxLength) +{ + if(State() == NET_CONNSTATE_ONLINE) + { + if(m_pBufferPos && *m_pBufferPos) + { + char *pResult = m_pBufferPos; + + while(*m_pBufferPos && *m_pBufferPos != '\r' && *m_pBufferPos != '\n') + m_pBufferPos++; + + if(*m_pBufferPos) // haven't reached the end of the buffer? + { + if(*m_pBufferPos == '\r' && *(m_pBufferPos + 1) == '\n') + { + *m_pBufferPos = 0; + m_pBufferPos += 2; + } + else + { + *m_pBufferPos = 0; + m_pBufferPos++; + } + } + else + { + m_pBufferPos = 0; + } + + str_copy(pLine, pResult, MaxLength); + return 1; + } + } + return 0; +} + +int CConsoleNetConnection::Send(const char *pLine) +{ + if(State() != NET_CONNSTATE_ONLINE) + return -1; + + int Length = str_length(pLine); + char aBuf[1024]; + str_copy(aBuf, pLine, sizeof(aBuf) - 2); + aBuf[Length + 1] = '\n'; + aBuf[Length + 2] = '\0'; + + if(net_tcp_send(m_Socket, aBuf, Length + 2) < 0) + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "Failed to send packet", sizeof(m_aErrorString)); + return -1; + } + + return 0; +} + |