1 | #include <engine/console.h> |
2 | #include <engine/shared/config.h> |
3 | |
4 | #include "econ.h" |
5 | #include "netban.h" |
6 | |
7 | CEcon::CEcon() : |
8 | m_Ready(false) |
9 | { |
10 | } |
11 | |
12 | int 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 | |
30 | int 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 | |
44 | void 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 | |
52 | void 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 | |
99 | void 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 | |
156 | void 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 | |
173 | void CEcon::Shutdown() |
174 | { |
175 | if(!m_Ready) |
176 | return; |
177 | |
178 | m_NetConsole.Close(); |
179 | } |
180 | |