diff options
Diffstat (limited to 'src/engine/shared')
| -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 |
4 files changed, 402 insertions, 0 deletions
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; +} + |