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