1#include <engine/console.h>
2#include <engine/shared/config.h>
3
4#include "econ.h"
5#include "netban.h"
6
7CEcon::CEcon() :
8 m_Ready(false)
9{
10}
11
12int CEcon::NewClientCallback(int ClientId, void *pUser)
13{
14 CEcon *pThis = (CEcon *)pUser;
15
16 char aAddrStr[NETADDR_MAXSTRSIZE];
17 net_addr_str(addr: pThis->m_NetConsole.ClientAddr(ClientId), string: aAddrStr, max_length: sizeof(aAddrStr), add_port: true);
18 char aBuf[128];
19 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "client accepted. cid=%d addr=%s'", ClientId, aAddrStr);
20 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "econ", pStr: aBuf);
21
22 pThis->m_aClients[ClientId].m_State = CClient::STATE_CONNECTED;
23 pThis->m_aClients[ClientId].m_TimeConnected = time_get();
24 pThis->m_aClients[ClientId].m_AuthTries = 0;
25
26 pThis->m_NetConsole.Send(ClientId, pLine: "Enter password:");
27 return 0;
28}
29
30int CEcon::DelClientCallback(int ClientId, const char *pReason, void *pUser)
31{
32 CEcon *pThis = (CEcon *)pUser;
33
34 char aAddrStr[NETADDR_MAXSTRSIZE];
35 net_addr_str(addr: pThis->m_NetConsole.ClientAddr(ClientId), string: aAddrStr, max_length: sizeof(aAddrStr), add_port: true);
36 char aBuf[256];
37 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "client dropped. cid=%d addr=%s reason='%s'", ClientId, aAddrStr, pReason);
38 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "econ", pStr: aBuf);
39
40 pThis->m_aClients[ClientId].m_State = CClient::STATE_EMPTY;
41 return 0;
42}
43
44void CEcon::ConLogout(IConsole::IResult *pResult, void *pUserData)
45{
46 CEcon *pThis = static_cast<CEcon *>(pUserData);
47
48 if(pThis->m_UserClientId >= 0 && pThis->m_UserClientId < NET_MAX_CONSOLE_CLIENTS && pThis->m_aClients[pThis->m_UserClientId].m_State != CClient::STATE_EMPTY)
49 pThis->m_NetConsole.Drop(ClientId: pThis->m_UserClientId, pReason: "Logout");
50}
51
52void CEcon::Init(CConfig *pConfig, IConsole *pConsole, CNetBan *pNetBan)
53{
54 m_pConfig = pConfig;
55 m_pConsole = pConsole;
56
57 for(auto &Client : m_aClients)
58 Client.m_State = CClient::STATE_EMPTY;
59
60 m_Ready = false;
61 m_UserClientId = -1;
62
63 if(g_Config.m_EcPort == 0)
64 return;
65
66 if(g_Config.m_EcPassword[0] == 0)
67 {
68 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "econ", pStr: "ec_password is required to be set for econ to be enabled.");
69 return;
70 }
71
72 NETADDR BindAddr;
73 if(g_Config.m_EcBindaddr[0] == '\0')
74 {
75 mem_zero(block: &BindAddr, size: sizeof(BindAddr));
76 }
77 else if(net_host_lookup(hostname: g_Config.m_EcBindaddr, addr: &BindAddr, types: NETTYPE_ALL) != 0)
78 {
79 char aBuf[256];
80 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The configured bindaddr '%s' cannot be resolved.", g_Config.m_Bindaddr);
81 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "econ", pStr: aBuf);
82 }
83 BindAddr.type = NETTYPE_ALL;
84 BindAddr.port = g_Config.m_EcPort;
85
86 if(m_NetConsole.Open(BindAddr, pNetBan))
87 {
88 m_NetConsole.SetCallbacks(pfnNewClient: NewClientCallback, pfnDelClient: DelClientCallback, pUser: this);
89 m_Ready = true;
90 char aBuf[128];
91 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "bound to %s:%d", g_Config.m_EcBindaddr, g_Config.m_EcPort);
92 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "econ", pStr: aBuf);
93 Console()->Register(pName: "logout", pParams: "", Flags: CFGFLAG_ECON, pfnFunc: ConLogout, pUser: this, pHelp: "Logout of econ");
94 }
95 else
96 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "econ", pStr: "couldn't open socket. port might already be in use");
97}
98
99void CEcon::Update()
100{
101 if(!m_Ready)
102 return;
103
104 m_NetConsole.Update();
105
106 char aBuf[NET_MAX_PACKETSIZE];
107 int ClientId;
108
109 while(m_NetConsole.Recv(pLine: aBuf, MaxLength: (int)(sizeof(aBuf)) - 1, pClientId: &ClientId))
110 {
111 dbg_assert(m_aClients[ClientId].m_State != CClient::STATE_EMPTY, "got message from empty slot");
112 if(m_aClients[ClientId].m_State == CClient::STATE_CONNECTED)
113 {
114 if(str_comp(a: aBuf, b: g_Config.m_EcPassword) == 0)
115 {
116 m_aClients[ClientId].m_State = CClient::STATE_AUTHED;
117 m_NetConsole.Send(ClientId, pLine: "Authentication successful. External console access granted.");
118
119 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "cid=%d authed", ClientId);
120 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "econ", pStr: aBuf);
121 }
122 else
123 {
124 m_aClients[ClientId].m_AuthTries++;
125 char aMsg[128];
126 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "Wrong password %d/%d.", m_aClients[ClientId].m_AuthTries, MAX_AUTH_TRIES);
127 m_NetConsole.Send(ClientId, pLine: aMsg);
128 if(m_aClients[ClientId].m_AuthTries >= MAX_AUTH_TRIES)
129 {
130 if(!g_Config.m_EcBantime)
131 m_NetConsole.Drop(ClientId, pReason: "Too many authentication tries");
132 else
133 m_NetConsole.NetBan()->BanAddr(pAddr: m_NetConsole.ClientAddr(ClientId), Seconds: g_Config.m_EcBantime * 60, pReason: "Too many authentication tries");
134 }
135 }
136 }
137 else if(m_aClients[ClientId].m_State == CClient::STATE_AUTHED)
138 {
139 char aFormatted[256];
140 str_format(buffer: aFormatted, buffer_size: sizeof(aFormatted), format: "cid=%d cmd='%s'", ClientId, aBuf);
141 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "server", pStr: aFormatted);
142 m_UserClientId = ClientId;
143 Console()->ExecuteLine(pStr: aBuf);
144 m_UserClientId = -1;
145 }
146 }
147
148 for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; ++i)
149 {
150 if(m_aClients[i].m_State == CClient::STATE_CONNECTED &&
151 time_get() > m_aClients[i].m_TimeConnected + g_Config.m_EcAuthTimeout * time_freq())
152 m_NetConsole.Drop(ClientId: i, pReason: "authentication timeout");
153 }
154}
155
156void CEcon::Send(int ClientId, const char *pLine)
157{
158 if(!m_Ready)
159 return;
160
161 if(ClientId == -1)
162 {
163 for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
164 {
165 if(m_aClients[i].m_State == CClient::STATE_AUTHED)
166 m_NetConsole.Send(ClientId: i, pLine);
167 }
168 }
169 else if(ClientId >= 0 && ClientId < NET_MAX_CONSOLE_CLIENTS && m_aClients[ClientId].m_State == CClient::STATE_AUTHED)
170 m_NetConsole.Send(ClientId, pLine);
171}
172
173void CEcon::Shutdown()
174{
175 if(!m_Ready)
176 return;
177
178 m_NetConsole.Close();
179}
180