1 | #include <base/math.h> |
2 | |
3 | #include <engine/console.h> |
4 | #include <engine/shared/config.h> |
5 | #include <engine/storage.h> |
6 | |
7 | #include "netban.h" |
8 | |
9 | CNetBan::CNetHash::CNetHash(const NETADDR *pAddr) |
10 | { |
11 | if(pAddr->type == NETTYPE_IPV4) |
12 | m_Hash = (pAddr->ip[0] + pAddr->ip[1] + pAddr->ip[2] + pAddr->ip[3]) & 0xFF; |
13 | else |
14 | m_Hash = (pAddr->ip[0] + pAddr->ip[1] + pAddr->ip[2] + pAddr->ip[3] + pAddr->ip[4] + pAddr->ip[5] + pAddr->ip[6] + pAddr->ip[7] + |
15 | pAddr->ip[8] + pAddr->ip[9] + pAddr->ip[10] + pAddr->ip[11] + pAddr->ip[12] + pAddr->ip[13] + pAddr->ip[14] + pAddr->ip[15]) & |
16 | 0xFF; |
17 | m_HashIndex = 0; |
18 | } |
19 | |
20 | CNetBan::CNetHash::CNetHash(const CNetRange *pRange) |
21 | { |
22 | m_Hash = 0; |
23 | m_HashIndex = 0; |
24 | for(int i = 0; pRange->m_LB.ip[i] == pRange->m_UB.ip[i]; ++i) |
25 | { |
26 | m_Hash += pRange->m_LB.ip[i]; |
27 | ++m_HashIndex; |
28 | } |
29 | m_Hash &= 0xFF; |
30 | } |
31 | |
32 | int CNetBan::CNetHash::MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]) |
33 | { |
34 | int Length = pAddr->type == NETTYPE_IPV4 ? 4 : 16; |
35 | aHash[0].m_Hash = 0; |
36 | aHash[0].m_HashIndex = 0; |
37 | for(int i = 1, Sum = 0; i <= Length; ++i) |
38 | { |
39 | Sum += pAddr->ip[i - 1]; |
40 | aHash[i].m_Hash = Sum & 0xFF; |
41 | aHash[i].m_HashIndex = i % Length; |
42 | } |
43 | return Length; |
44 | } |
45 | |
46 | template<class T, int HashCount> |
47 | void CNetBan::CBanPool<T, HashCount>::InsertUsed(CBan<T> *pBan) |
48 | { |
49 | if(m_pFirstUsed) |
50 | { |
51 | for(CBan<T> *p = m_pFirstUsed;; p = p->m_pNext) |
52 | { |
53 | if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && pBan->m_Info.m_Expires <= p->m_Info.m_Expires)) |
54 | { |
55 | // insert before |
56 | pBan->m_pNext = p; |
57 | pBan->m_pPrev = p->m_pPrev; |
58 | if(p->m_pPrev) |
59 | p->m_pPrev->m_pNext = pBan; |
60 | else |
61 | m_pFirstUsed = pBan; |
62 | p->m_pPrev = pBan; |
63 | break; |
64 | } |
65 | |
66 | if(!p->m_pNext) |
67 | { |
68 | // last entry |
69 | p->m_pNext = pBan; |
70 | pBan->m_pPrev = p; |
71 | pBan->m_pNext = 0; |
72 | break; |
73 | } |
74 | } |
75 | } |
76 | else |
77 | { |
78 | m_pFirstUsed = pBan; |
79 | pBan->m_pNext = pBan->m_pPrev = 0; |
80 | } |
81 | } |
82 | |
83 | template<class T, int HashCount> |
84 | typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash) |
85 | { |
86 | if(!m_pFirstFree) |
87 | return 0; |
88 | |
89 | // create new ban |
90 | CBan<T> *pBan = m_pFirstFree; |
91 | pBan->m_Data = *pData; |
92 | pBan->m_Info = *pInfo; |
93 | pBan->m_NetHash = *pNetHash; |
94 | if(pBan->m_pNext) |
95 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; |
96 | if(pBan->m_pPrev) |
97 | pBan->m_pPrev->m_pNext = pBan->m_pNext; |
98 | else |
99 | m_pFirstFree = pBan->m_pNext; |
100 | |
101 | // add it to the hash list |
102 | if(m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]) |
103 | m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]->m_pHashPrev = pBan; |
104 | pBan->m_pHashPrev = 0; |
105 | pBan->m_pHashNext = m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; |
106 | m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash] = pBan; |
107 | |
108 | // insert it into the used list |
109 | InsertUsed(pBan); |
110 | |
111 | // update ban count |
112 | ++m_CountUsed; |
113 | |
114 | return pBan; |
115 | } |
116 | |
117 | template<class T, int HashCount> |
118 | int CNetBan::CBanPool<T, HashCount>::Remove(CBan<T> *pBan) |
119 | { |
120 | if(pBan == 0) |
121 | return -1; |
122 | |
123 | // remove from hash list |
124 | if(pBan->m_pHashNext) |
125 | pBan->m_pHashNext->m_pHashPrev = pBan->m_pHashPrev; |
126 | if(pBan->m_pHashPrev) |
127 | pBan->m_pHashPrev->m_pHashNext = pBan->m_pHashNext; |
128 | else |
129 | m_aapHashList[pBan->m_NetHash.m_HashIndex][pBan->m_NetHash.m_Hash] = pBan->m_pHashNext; |
130 | pBan->m_pHashNext = pBan->m_pHashPrev = 0; |
131 | |
132 | // remove from used list |
133 | if(pBan->m_pNext) |
134 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; |
135 | if(pBan->m_pPrev) |
136 | pBan->m_pPrev->m_pNext = pBan->m_pNext; |
137 | else |
138 | m_pFirstUsed = pBan->m_pNext; |
139 | |
140 | // add to recycle list |
141 | if(m_pFirstFree) |
142 | m_pFirstFree->m_pPrev = pBan; |
143 | pBan->m_pPrev = 0; |
144 | pBan->m_pNext = m_pFirstFree; |
145 | m_pFirstFree = pBan; |
146 | |
147 | // update ban count |
148 | --m_CountUsed; |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | template<class T, int HashCount> |
154 | void CNetBan::CBanPool<T, HashCount>::Update(CBan<CDataType> *pBan, const CBanInfo *pInfo) |
155 | { |
156 | pBan->m_Info = *pInfo; |
157 | |
158 | // remove from used list |
159 | if(pBan->m_pNext) |
160 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; |
161 | if(pBan->m_pPrev) |
162 | pBan->m_pPrev->m_pNext = pBan->m_pNext; |
163 | else |
164 | m_pFirstUsed = pBan->m_pNext; |
165 | |
166 | // insert it into the used list |
167 | InsertUsed(pBan); |
168 | } |
169 | |
170 | void CNetBan::UnbanAll() |
171 | { |
172 | m_BanAddrPool.Reset(); |
173 | m_BanRangePool.Reset(); |
174 | } |
175 | |
176 | template<class T, int HashCount> |
177 | void CNetBan::CBanPool<T, HashCount>::Reset() |
178 | { |
179 | mem_zero(m_aapHashList, sizeof(m_aapHashList)); |
180 | mem_zero(m_aBans, sizeof(m_aBans)); |
181 | m_pFirstUsed = 0; |
182 | m_CountUsed = 0; |
183 | |
184 | for(int i = 1; i < MAX_BANS - 1; ++i) |
185 | { |
186 | m_aBans[i].m_pNext = &m_aBans[i + 1]; |
187 | m_aBans[i].m_pPrev = &m_aBans[i - 1]; |
188 | } |
189 | |
190 | m_aBans[0].m_pNext = &m_aBans[1]; |
191 | m_aBans[MAX_BANS - 1].m_pPrev = &m_aBans[MAX_BANS - 2]; |
192 | m_pFirstFree = &m_aBans[0]; |
193 | } |
194 | |
195 | template<class T, int HashCount> |
196 | typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Get(int Index) const |
197 | { |
198 | if(Index < 0 || Index >= Num()) |
199 | return 0; |
200 | |
201 | for(CNetBan::CBan<T> *pBan = m_pFirstUsed; pBan; pBan = pBan->m_pNext, --Index) |
202 | { |
203 | if(Index == 0) |
204 | return pBan; |
205 | } |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | template<class T> |
211 | int CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason) |
212 | { |
213 | // do not ban localhost |
214 | if(NetMatch(pData, &m_LocalhostIpV4) || NetMatch(pData, &m_LocalhostIpV6)) |
215 | { |
216 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban failed (localhost)" ); |
217 | return -1; |
218 | } |
219 | |
220 | int64_t Stamp = Seconds > 0 ? time_timestamp() + Seconds : static_cast<int64_t>(CBanInfo::EXPIRES_NEVER); |
221 | |
222 | // set up info |
223 | CBanInfo Info = {.m_Expires: 0}; |
224 | Info.m_Expires = Stamp; |
225 | str_copy(dst&: Info.m_aReason, src: pReason); |
226 | |
227 | // check if it already exists |
228 | CNetHash NetHash(pData); |
229 | CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); |
230 | if(pBan) |
231 | { |
232 | // adjust the ban |
233 | pBanPool->Update(pBan, &Info); |
234 | char aBuf[256]; |
235 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); |
236 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
237 | return 1; |
238 | } |
239 | |
240 | // add ban and print result |
241 | pBan = pBanPool->Add(pData, &Info, &NetHash); |
242 | if(pBan) |
243 | { |
244 | char aBuf[256]; |
245 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD); |
246 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
247 | return 0; |
248 | } |
249 | else |
250 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban failed (full banlist)" ); |
251 | return -1; |
252 | } |
253 | |
254 | template<class T> |
255 | int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData) |
256 | { |
257 | CNetHash NetHash(pData); |
258 | CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); |
259 | if(pBan) |
260 | { |
261 | char aBuf[256]; |
262 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM); |
263 | pBanPool->Remove(pBan); |
264 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
265 | return 0; |
266 | } |
267 | else |
268 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "unban failed (invalid entry)" ); |
269 | return -1; |
270 | } |
271 | |
272 | void CNetBan::Init(IConsole *pConsole, IStorage *pStorage) |
273 | { |
274 | m_pConsole = pConsole; |
275 | m_pStorage = pStorage; |
276 | m_BanAddrPool.Reset(); |
277 | m_BanRangePool.Reset(); |
278 | |
279 | net_host_lookup(hostname: "localhost" , addr: &m_LocalhostIpV4, types: NETTYPE_IPV4); |
280 | net_host_lookup(hostname: "localhost" , addr: &m_LocalhostIpV6, types: NETTYPE_IPV6); |
281 | |
282 | Console()->Register(pName: "ban" , pParams: "s[ip|id] ?i[minutes] r[reason]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConBan, pUser: this, pHelp: "Ban ip for x minutes for any reason" ); |
283 | Console()->Register(pName: "ban_range" , pParams: "s[first ip] s[last ip] ?i[minutes] r[reason]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConBanRange, pUser: this, pHelp: "Ban ip range for x minutes for any reason" ); |
284 | Console()->Register(pName: "unban" , pParams: "s[ip|entry]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnban, pUser: this, pHelp: "Unban ip/banlist entry" ); |
285 | Console()->Register(pName: "unban_range" , pParams: "s[first ip] s[last ip]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnbanRange, pUser: this, pHelp: "Unban ip range" ); |
286 | Console()->Register(pName: "unban_all" , pParams: "" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnbanAll, pUser: this, pHelp: "Unban all entries" ); |
287 | Console()->Register(pName: "bans" , pParams: "?i[page]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER, pfnFunc: ConBans, pUser: this, pHelp: "Show banlist (page 0 by default, 20 entries per page)" ); |
288 | Console()->Register(pName: "bans_save" , pParams: "s[file]" , Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConBansSave, pUser: this, pHelp: "Save banlist in a file" ); |
289 | } |
290 | |
291 | void CNetBan::Update() |
292 | { |
293 | int64_t Now = time_timestamp(); |
294 | |
295 | // remove expired bans |
296 | char aBuf[256], aNetStr[256]; |
297 | while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now) |
298 | { |
299 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired" , NetToString(pData: &m_BanAddrPool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr))); |
300 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
301 | m_BanAddrPool.Remove(pBan: m_BanAddrPool.First()); |
302 | } |
303 | while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now) |
304 | { |
305 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired" , NetToString(pData: &m_BanRangePool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr))); |
306 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
307 | m_BanRangePool.Remove(pBan: m_BanRangePool.First()); |
308 | } |
309 | } |
310 | |
311 | int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason) |
312 | { |
313 | return Ban(pBanPool: &m_BanAddrPool, pData: pAddr, Seconds, pReason); |
314 | } |
315 | |
316 | int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason) |
317 | { |
318 | if(pRange->IsValid()) |
319 | return Ban(pBanPool: &m_BanRangePool, pData: pRange, Seconds, pReason); |
320 | |
321 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban failed (invalid range)" ); |
322 | return -1; |
323 | } |
324 | |
325 | int CNetBan::UnbanByAddr(const NETADDR *pAddr) |
326 | { |
327 | return Unban(pBanPool: &m_BanAddrPool, pData: pAddr); |
328 | } |
329 | |
330 | int CNetBan::UnbanByRange(const CNetRange *pRange) |
331 | { |
332 | if(pRange->IsValid()) |
333 | return Unban(pBanPool: &m_BanRangePool, pData: pRange); |
334 | |
335 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban failed (invalid range)" ); |
336 | return -1; |
337 | } |
338 | |
339 | int CNetBan::UnbanByIndex(int Index) |
340 | { |
341 | int Result; |
342 | char aBuf[256]; |
343 | CBanAddr *pBan = m_BanAddrPool.Get(Index); |
344 | if(pBan) |
345 | { |
346 | NetToString(pData: &pBan->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
347 | Result = m_BanAddrPool.Remove(pBan); |
348 | } |
349 | else |
350 | { |
351 | CBanRange *pBanRange = m_BanRangePool.Get(Index: Index - m_BanAddrPool.Num()); |
352 | if(pBanRange) |
353 | { |
354 | NetToString(pData: &pBanRange->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
355 | Result = m_BanRangePool.Remove(pBan: pBanRange); |
356 | } |
357 | else |
358 | { |
359 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "unban failed (invalid index)" ); |
360 | return -1; |
361 | } |
362 | } |
363 | |
364 | char aMsg[256]; |
365 | str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "unbanned index %i (%s)" , Index, aBuf); |
366 | Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aMsg); |
367 | return Result; |
368 | } |
369 | |
370 | bool CNetBan::IsBanned(const NETADDR *pOrigAddr, char *pBuf, unsigned BufferSize) const |
371 | { |
372 | NETADDR Addr; |
373 | const NETADDR *pAddr = pOrigAddr; |
374 | if(pOrigAddr->type == NETTYPE_WEBSOCKET_IPV4) |
375 | { |
376 | mem_copy(dest: &Addr, source: pOrigAddr, size: sizeof(NETADDR)); |
377 | pAddr = &Addr; |
378 | Addr.type = NETTYPE_IPV4; |
379 | } |
380 | CNetHash aHash[17]; |
381 | int Length = CNetHash::MakeHashArray(pAddr, aHash); |
382 | |
383 | // check ban addresses |
384 | CBanAddr *pBan = m_BanAddrPool.Find(pData: pAddr, pNetHash: &aHash[Length]); |
385 | if(pBan) |
386 | { |
387 | MakeBanInfo(pBan, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER); |
388 | return true; |
389 | } |
390 | |
391 | // check ban ranges |
392 | for(int i = Length - 1; i >= 0; --i) |
393 | { |
394 | for(CBanRange *pBanRange = m_BanRangePool.First(pNetHash: &aHash[i]); pBanRange; pBanRange = pBanRange->m_pHashNext) |
395 | { |
396 | if(NetMatch(pRange: &pBanRange->m_Data, pAddr, Start: i, Length)) |
397 | { |
398 | MakeBanInfo(pBan: pBanRange, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER); |
399 | return true; |
400 | } |
401 | } |
402 | } |
403 | |
404 | return false; |
405 | } |
406 | |
407 | void CNetBan::ConBan(IConsole::IResult *pResult, void *pUser) |
408 | { |
409 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
410 | |
411 | const char *pStr = pResult->GetString(Index: 0); |
412 | int Minutes = pResult->NumArguments() > 1 ? clamp(val: pResult->GetInteger(Index: 1), lo: 0, hi: 525600) : 30; |
413 | const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "No reason given" ; |
414 | |
415 | NETADDR Addr; |
416 | if(net_addr_from_str(addr: &Addr, string: pStr) == 0) |
417 | pThis->BanAddr(pAddr: &Addr, Seconds: Minutes * 60, pReason); |
418 | else |
419 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban error (invalid network address)" ); |
420 | } |
421 | |
422 | void CNetBan::ConBanRange(IConsole::IResult *pResult, void *pUser) |
423 | { |
424 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
425 | |
426 | const char *pStr1 = pResult->GetString(Index: 0); |
427 | const char *pStr2 = pResult->GetString(Index: 1); |
428 | int Minutes = pResult->NumArguments() > 2 ? clamp(val: pResult->GetInteger(Index: 2), lo: 0, hi: 525600) : 30; |
429 | const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "No reason given" ; |
430 | |
431 | CNetRange Range; |
432 | if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0) |
433 | pThis->BanRange(pRange: &Range, Seconds: Minutes * 60, pReason); |
434 | else |
435 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "ban error (invalid range)" ); |
436 | } |
437 | |
438 | void CNetBan::ConUnban(IConsole::IResult *pResult, void *pUser) |
439 | { |
440 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
441 | |
442 | const char *pStr = pResult->GetString(Index: 0); |
443 | if(str_isallnum(str: pStr)) |
444 | pThis->UnbanByIndex(Index: str_toint(str: pStr)); |
445 | else |
446 | { |
447 | NETADDR Addr; |
448 | if(net_addr_from_str(addr: &Addr, string: pStr) == 0) |
449 | pThis->UnbanByAddr(pAddr: &Addr); |
450 | else |
451 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "unban error (invalid network address)" ); |
452 | } |
453 | } |
454 | |
455 | void CNetBan::ConUnbanRange(IConsole::IResult *pResult, void *pUser) |
456 | { |
457 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
458 | |
459 | const char *pStr1 = pResult->GetString(Index: 0); |
460 | const char *pStr2 = pResult->GetString(Index: 1); |
461 | |
462 | CNetRange Range; |
463 | if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0) |
464 | pThis->UnbanByRange(pRange: &Range); |
465 | else |
466 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "unban error (invalid range)" ); |
467 | } |
468 | |
469 | void CNetBan::ConUnbanAll(IConsole::IResult *pResult, void *pUser) |
470 | { |
471 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
472 | |
473 | pThis->UnbanAll(); |
474 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: "unbanned all entries" ); |
475 | } |
476 | |
477 | void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser) |
478 | { |
479 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
480 | |
481 | int Page = pResult->NumArguments() > 0 ? pResult->GetInteger(Index: 0) : 0; |
482 | static const int s_EntriesPerPage = 20; |
483 | const int Start = Page * s_EntriesPerPage; |
484 | const int End = (Page + 1) * s_EntriesPerPage; |
485 | |
486 | int Count = 0; |
487 | char aBuf[256], aMsg[256]; |
488 | for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++) |
489 | { |
490 | if(Count < Start || Count >= End) |
491 | { |
492 | continue; |
493 | } |
494 | pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST); |
495 | str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s" , Count, aBuf); |
496 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aMsg); |
497 | } |
498 | for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++) |
499 | { |
500 | if(Count < Start || Count >= End) |
501 | { |
502 | continue; |
503 | } |
504 | pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST); |
505 | str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s" , Count, aBuf); |
506 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aMsg); |
507 | } |
508 | str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "%d %s, showing entries %d - %d" , Count, Count == 1 ? "ban" : "bans" , Start, End - 1); |
509 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aMsg); |
510 | } |
511 | |
512 | void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser) |
513 | { |
514 | CNetBan *pThis = static_cast<CNetBan *>(pUser); |
515 | |
516 | char aBuf[256]; |
517 | IOHANDLE File = pThis->Storage()->OpenFile(pFilename: pResult->GetString(Index: 0), Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE); |
518 | if(!File) |
519 | { |
520 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "failed to save banlist to '%s'" , pResult->GetString(Index: 0)); |
521 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
522 | return; |
523 | } |
524 | |
525 | int64_t Now = time_timestamp(); |
526 | char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; |
527 | for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext) |
528 | { |
529 | int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1; |
530 | net_addr_str(addr: &pBan->m_Data, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false); |
531 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s %i %s" , aAddrStr1, Min, pBan->m_Info.m_aReason); |
532 | io_write(io: File, buffer: aBuf, size: str_length(str: aBuf)); |
533 | io_write_newline(io: File); |
534 | } |
535 | for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext) |
536 | { |
537 | int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1; |
538 | net_addr_str(addr: &pBan->m_Data.m_LB, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false); |
539 | net_addr_str(addr: &pBan->m_Data.m_UB, string: aAddrStr2, max_length: sizeof(aAddrStr2), add_port: false); |
540 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban_range %s %s %i %s" , aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason); |
541 | io_write(io: File, buffer: aBuf, size: str_length(str: aBuf)); |
542 | io_write_newline(io: File); |
543 | } |
544 | |
545 | io_close(io: File); |
546 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "saved banlist to '%s'" , pResult->GetString(Index: 0)); |
547 | pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban" , pStr: aBuf); |
548 | } |
549 | |