1#include "netban.h"
2
3#include <base/math.h>
4
5#include <engine/console.h>
6#include <engine/shared/config.h>
7#include <engine/storage.h>
8
9CNetBan::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
20CNetBan::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
32int 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
46template<class T, int HashCount>
47void 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
83template<class T, int HashCount>
84typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash)
85{
86 if(!m_pFirstFree)
87 return nullptr;
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
117template<class T, int HashCount>
118int CNetBan::CBanPool<T, HashCount>::Remove(CBan<T> *pBan)
119{
120 if(pBan == nullptr)
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
153template<class T, int HashCount>
154void 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
170void CNetBan::UnbanAll()
171{
172 m_BanAddrPool.Reset();
173 m_BanRangePool.Reset();
174}
175
176template<class T, int HashCount>
177void 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
195template<class T, int HashCount>
196typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Get(int Index) const
197{
198 if(Index < 0 || Index >= Num())
199 return nullptr;
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 nullptr;
208}
209
210template<class T>
211int CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason, bool VerbatimReason)
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 Info.m_VerbatimReason = VerbatimReason;
226 str_copy(dst&: Info.m_aReason, src: pReason);
227
228 // check if it already exists
229 CNetHash NetHash(pData);
230 CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash);
231 if(pBan)
232 {
233 // adjust the ban
234 pBanPool->Update(pBan, &Info);
235 char aBuf[256];
236 MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST);
237 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
238 return 1;
239 }
240
241 // add ban and print result
242 pBan = pBanPool->Add(pData, &Info, &NetHash);
243 if(pBan)
244 {
245 char aBuf[256];
246 MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD);
247 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
248 return 0;
249 }
250 else
251 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban failed (full banlist)");
252 return -1;
253}
254
255template<class T>
256int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData)
257{
258 CNetHash NetHash(pData);
259 CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash);
260 if(pBan)
261 {
262 char aBuf[256];
263 MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM);
264 pBanPool->Remove(pBan);
265 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
266 return 0;
267 }
268 else
269 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban failed (invalid entry)");
270 return -1;
271}
272
273void CNetBan::Init(IConsole *pConsole, IStorage *pStorage)
274{
275 m_pConsole = pConsole;
276 m_pStorage = pStorage;
277 m_BanAddrPool.Reset();
278 m_BanRangePool.Reset();
279
280 net_host_lookup(hostname: "localhost", addr: &m_LocalhostIpV4, types: NETTYPE_IPV4);
281 net_host_lookup(hostname: "localhost", addr: &m_LocalhostIpV6, types: NETTYPE_IPV6);
282
283 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");
284 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");
285 Console()->Register(pName: "unban", pParams: "s[ip|entry]", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnban, pUser: this, pHelp: "Unban ip/banlist entry");
286 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");
287 Console()->Register(pName: "unban_all", pParams: "", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnbanAll, pUser: this, pHelp: "Unban all entries");
288 Console()->Register(pName: "bans", pParams: "?i[page]", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER, pfnFunc: ConBans, pUser: this, pHelp: "Show banlist (page 1 by default, 20 entries per page)");
289 Console()->Register(pName: "bans_find", pParams: "s[ip]", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER, pfnFunc: ConBansFind, pUser: this, pHelp: "Find all ban records for the specified IP address");
290 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");
291}
292
293void CNetBan::Update()
294{
295 int64_t Now = time_timestamp();
296
297 // remove expired bans
298 char aBuf[256], aNetStr[256];
299 while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now)
300 {
301 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired", NetToString(pData: &m_BanAddrPool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr)));
302 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
303 m_BanAddrPool.Remove(pBan: m_BanAddrPool.First());
304 }
305 while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now)
306 {
307 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired", NetToString(pData: &m_BanRangePool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr)));
308 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
309 m_BanRangePool.Remove(pBan: m_BanRangePool.First());
310 }
311}
312
313int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason, bool VerbatimReason)
314{
315 return Ban(pBanPool: &m_BanAddrPool, pData: pAddr, Seconds, pReason, VerbatimReason);
316}
317
318int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason)
319{
320 if(pRange->IsValid())
321 return Ban(pBanPool: &m_BanRangePool, pData: pRange, Seconds, pReason, VerbatimReason: false);
322
323 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban failed (invalid range)");
324 return -1;
325}
326
327int CNetBan::UnbanByAddr(const NETADDR *pAddr)
328{
329 return Unban(pBanPool: &m_BanAddrPool, pData: pAddr);
330}
331
332int CNetBan::UnbanByRange(const CNetRange *pRange)
333{
334 if(pRange->IsValid())
335 return Unban(pBanPool: &m_BanRangePool, pData: pRange);
336
337 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban failed (invalid range)");
338 return -1;
339}
340
341int CNetBan::UnbanByIndex(int Index)
342{
343 int Result;
344 char aBuf[256];
345 CBanAddr *pBan = m_BanAddrPool.Get(Index);
346 if(pBan)
347 {
348 NetToString(pData: &pBan->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf));
349 Result = m_BanAddrPool.Remove(pBan);
350 }
351 else
352 {
353 CBanRange *pBanRange = m_BanRangePool.Get(Index: Index - m_BanAddrPool.Num());
354 if(pBanRange)
355 {
356 NetToString(pData: &pBanRange->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf));
357 Result = m_BanRangePool.Remove(pBan: pBanRange);
358 }
359 else
360 {
361 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban failed (invalid index)");
362 return -1;
363 }
364 }
365
366 char aMsg[256];
367 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "unbanned index %i (%s)", Index, aBuf);
368 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
369 return Result;
370}
371
372bool CNetBan::IsBanned(const NETADDR *pOrigAddr, char *pBuf, unsigned BufferSize) const
373{
374 NETADDR Addr;
375 const NETADDR *pAddr = pOrigAddr;
376 if(pOrigAddr->type == NETTYPE_WEBSOCKET_IPV4)
377 {
378 Addr = *pOrigAddr;
379 pAddr = &Addr;
380 Addr.type = NETTYPE_IPV4;
381 }
382 else if(pOrigAddr->type == NETTYPE_WEBSOCKET_IPV6)
383 {
384 Addr = *pOrigAddr;
385 pAddr = &Addr;
386 Addr.type = NETTYPE_IPV6;
387 }
388 CNetHash aHash[17];
389 int Length = CNetHash::MakeHashArray(pAddr, aHash);
390
391 // check ban addresses
392 CBanAddr *pBan = m_BanAddrPool.Find(pData: pAddr, pNetHash: &aHash[Length]);
393 if(pBan)
394 {
395 MakeBanInfo(pBan, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER);
396 return true;
397 }
398
399 // check ban ranges
400 for(int i = Length - 1; i >= 0; --i)
401 {
402 for(CBanRange *pBanRange = m_BanRangePool.First(pNetHash: &aHash[i]); pBanRange; pBanRange = pBanRange->m_pHashNext)
403 {
404 if(NetMatch(pRange: &pBanRange->m_Data, pAddr, Start: i, Length))
405 {
406 MakeBanInfo(pBan: pBanRange, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER);
407 return true;
408 }
409 }
410 }
411
412 return false;
413}
414
415void CNetBan::ConBan(IConsole::IResult *pResult, void *pUser)
416{
417 CNetBan *pThis = static_cast<CNetBan *>(pUser);
418
419 const char *pStr = pResult->GetString(Index: 0);
420 int Minutes = pResult->NumArguments() > 1 ? std::clamp(val: pResult->GetInteger(Index: 1), lo: 0, hi: 525600) : 30;
421 const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "No reason given";
422
423 NETADDR Addr;
424 if(net_addr_from_str(addr: &Addr, string: pStr) == 0)
425 pThis->BanAddr(pAddr: &Addr, Seconds: Minutes * 60, pReason, VerbatimReason: false);
426 else
427 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban error (invalid network address)");
428}
429
430void CNetBan::ConBanRange(IConsole::IResult *pResult, void *pUser)
431{
432 CNetBan *pThis = static_cast<CNetBan *>(pUser);
433
434 const char *pStr1 = pResult->GetString(Index: 0);
435 const char *pStr2 = pResult->GetString(Index: 1);
436 int Minutes = pResult->NumArguments() > 2 ? std::clamp(val: pResult->GetInteger(Index: 2), lo: 0, hi: 525600) : 30;
437 const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "No reason given";
438
439 CNetRange Range;
440 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
441 pThis->BanRange(pRange: &Range, Seconds: Minutes * 60, pReason);
442 else
443 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban error (invalid range)");
444}
445
446void CNetBan::ConUnban(IConsole::IResult *pResult, void *pUser)
447{
448 CNetBan *pThis = static_cast<CNetBan *>(pUser);
449
450 const char *pStr = pResult->GetString(Index: 0);
451 if(str_isallnum(str: pStr))
452 pThis->UnbanByIndex(Index: str_toint(str: pStr));
453 else
454 {
455 NETADDR Addr;
456 if(net_addr_from_str(addr: &Addr, string: pStr) == 0)
457 pThis->UnbanByAddr(pAddr: &Addr);
458 else
459 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid network address)");
460 }
461}
462
463void CNetBan::ConUnbanRange(IConsole::IResult *pResult, void *pUser)
464{
465 CNetBan *pThis = static_cast<CNetBan *>(pUser);
466
467 const char *pStr1 = pResult->GetString(Index: 0);
468 const char *pStr2 = pResult->GetString(Index: 1);
469
470 CNetRange Range;
471 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
472 pThis->UnbanByRange(pRange: &Range);
473 else
474 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid range)");
475}
476
477void CNetBan::ConUnbanAll(IConsole::IResult *pResult, void *pUser)
478{
479 CNetBan *pThis = static_cast<CNetBan *>(pUser);
480
481 pThis->UnbanAll();
482 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unbanned all entries");
483}
484
485void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser)
486{
487 CNetBan *pThis = static_cast<CNetBan *>(pUser);
488
489 const int NumBans = pThis->m_BanAddrPool.Num() + pThis->m_BanRangePool.Num();
490 if(NumBans == 0)
491 {
492 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "The ban list is empty.");
493 return;
494 }
495
496 static constexpr int ENTRIES_PER_PAGE = 20;
497 const int NumPages = std::ceil(x: NumBans / (float)ENTRIES_PER_PAGE);
498 const int Page = pResult->NumArguments() > 0 ? pResult->GetInteger(Index: 0) : 1;
499
500 char aBuf[256], aMsg[256];
501 if(Page <= 0 || Page > NumPages)
502 {
503 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");
504 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
505 return;
506 }
507
508 const int Start = (Page - 1) * ENTRIES_PER_PAGE;
509 const int End = Page * ENTRIES_PER_PAGE;
510 int Count = 0;
511 for(const CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
512 {
513 if(Count < Start)
514 {
515 continue;
516 }
517 else if(Count >= End)
518 {
519 break;
520 }
521 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
522 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
523 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
524 }
525 for(const CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
526 {
527 if(Count < Start)
528 {
529 continue;
530 }
531 else if(Count >= End)
532 {
533 break;
534 }
535 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
536 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
537 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
538 }
539 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);
540 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
541}
542
543void CNetBan::ConBansFind(IConsole::IResult *pResult, void *pUser)
544{
545 CNetBan *pThis = static_cast<CNetBan *>(pUser);
546
547 const char *pStr = pResult->GetString(Index: 0);
548 char aBuf[256], aMsg[256];
549
550 NETADDR Addr;
551 if(net_addr_from_str(addr: &Addr, string: pStr) != 0)
552 {
553 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "bans_find error (invalid network address)");
554 return;
555 }
556
557 int Count = 0;
558 int Found = 0;
559 // Check first for bans
560 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
561 {
562 if(NetComp(pAddr1: &pBan->m_Data, pAddr2: &Addr) == 0)
563 {
564 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
565 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
566 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
567
568 Found++;
569 }
570 }
571 // check ban ranges
572 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
573 {
574 if(pThis->NetMatch(pRange: &pBan->m_Data, pAddr: &Addr))
575 {
576 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
577 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
578 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
579
580 Found++;
581 }
582 }
583
584 if(Found)
585 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "%i ban records found.", Found);
586 else
587 str_copy(dst: aMsg, src: "No ban records found.", dst_size: sizeof(aMsg));
588
589 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
590}
591
592void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser)
593{
594 CNetBan *pThis = static_cast<CNetBan *>(pUser);
595
596 char aBuf[256];
597 IOHANDLE File = pThis->Storage()->OpenFile(pFilename: pResult->GetString(Index: 0), Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
598 if(!File)
599 {
600 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "failed to save banlist to '%s'", pResult->GetString(Index: 0));
601 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
602 return;
603 }
604
605 int64_t Now = time_timestamp();
606 char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE];
607 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext)
608 {
609 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
610 net_addr_str(addr: &pBan->m_Data, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
611 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s %i %s", aAddrStr1, Min, pBan->m_Info.m_aReason);
612 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
613 io_write_newline(io: File);
614 }
615 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext)
616 {
617 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
618 net_addr_str(addr: &pBan->m_Data.m_LB, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
619 net_addr_str(addr: &pBan->m_Data.m_UB, string: aAddrStr2, max_length: sizeof(aAddrStr2), add_port: false);
620 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban_range %s %s %i %s", aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason);
621 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
622 io_write_newline(io: File);
623 }
624
625 io_close(io: File);
626 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "saved banlist to '%s'", pResult->GetString(Index: 0));
627 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
628}
629