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