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