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 = 0;
73 break;
74 }
75 }
76 }
77 else
78 {
79 m_pFirstUsed = pBan;
80 pBan->m_pNext = pBan->m_pPrev = 0;
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 = 0;
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 = 0;
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 = 0;
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 = 0;
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 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban failed (invalid entry)");
271 return -1;
272}
273
274void CNetBan::Init(IConsole *pConsole, IStorage *pStorage)
275{
276 m_pConsole = pConsole;
277 m_pStorage = pStorage;
278 m_BanAddrPool.Reset();
279 m_BanRangePool.Reset();
280
281 net_host_lookup(hostname: "localhost", addr: &m_LocalhostIpV4, types: NETTYPE_IPV4);
282 net_host_lookup(hostname: "localhost", addr: &m_LocalhostIpV6, types: NETTYPE_IPV6);
283
284 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");
285 Console()->Register(pName: "unban", pParams: "s[ip|entry]", Flags: CFGFLAG_SERVER | 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_STORE, pfnFunc: ConUnbanRange, pUser: this, pHelp: "Unban ip range");
287 Console()->Register(pName: "unban_all", pParams: "", Flags: CFGFLAG_SERVER | CFGFLAG_STORE, pfnFunc: ConUnbanAll, pUser: this, pHelp: "Unban all entries");
288 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)");
289 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");
290 Console()->Register(pName: "bans_save", pParams: "s[file]", Flags: CFGFLAG_SERVER | 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::ConBanRange(IConsole::IResult *pResult, void *pUser)
416{
417 CNetBan *pThis = static_cast<CNetBan *>(pUser);
418
419 const char *pStr1 = pResult->GetString(Index: 0);
420 const char *pStr2 = pResult->GetString(Index: 1);
421 int Minutes = pResult->NumArguments() > 2 ? std::clamp(val: pResult->GetInteger(Index: 2), lo: 0, hi: 525600) : 30;
422 const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "No reason given";
423
424 CNetRange Range;
425 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
426 pThis->BanRange(pRange: &Range, Seconds: Minutes * 60, pReason);
427 else
428 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban error (invalid range)");
429}
430
431void CNetBan::ConUnban(IConsole::IResult *pResult, void *pUser)
432{
433 CNetBan *pThis = static_cast<CNetBan *>(pUser);
434
435 const char *pStr = pResult->GetString(Index: 0);
436 if(str_isallnum(str: pStr))
437 pThis->UnbanByIndex(Index: str_toint(str: pStr));
438 else
439 {
440 NETADDR Addr;
441 if(net_addr_from_str(addr: &Addr, string: pStr) == 0)
442 pThis->UnbanByAddr(pAddr: &Addr);
443 else
444 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid network address)");
445 }
446}
447
448void CNetBan::ConUnbanRange(IConsole::IResult *pResult, void *pUser)
449{
450 CNetBan *pThis = static_cast<CNetBan *>(pUser);
451
452 const char *pStr1 = pResult->GetString(Index: 0);
453 const char *pStr2 = pResult->GetString(Index: 1);
454
455 CNetRange Range;
456 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
457 pThis->UnbanByRange(pRange: &Range);
458 else
459 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid range)");
460}
461
462void CNetBan::ConUnbanAll(IConsole::IResult *pResult, void *pUser)
463{
464 CNetBan *pThis = static_cast<CNetBan *>(pUser);
465
466 pThis->UnbanAll();
467 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unbanned all entries");
468}
469
470void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser)
471{
472 CNetBan *pThis = static_cast<CNetBan *>(pUser);
473
474 const int NumBans = pThis->m_BanAddrPool.Num() + pThis->m_BanRangePool.Num();
475 if(NumBans == 0)
476 {
477 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "The ban list is empty.");
478 return;
479 }
480
481 static constexpr int ENTRIES_PER_PAGE = 20;
482 const int NumPages = std::ceil(x: NumBans / (float)ENTRIES_PER_PAGE);
483 const int Page = pResult->NumArguments() > 0 ? pResult->GetInteger(Index: 0) : 1;
484
485 char aBuf[256], aMsg[256];
486 if(Page <= 0 || Page > NumPages)
487 {
488 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");
489 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
490 return;
491 }
492
493 const int Start = (Page - 1) * ENTRIES_PER_PAGE;
494 const int End = Page * ENTRIES_PER_PAGE;
495 int Count = 0;
496 for(const CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
497 {
498 if(Count < Start)
499 {
500 continue;
501 }
502 else if(Count >= End)
503 {
504 break;
505 }
506 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
507 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
508 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
509 }
510 for(const CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
511 {
512 if(Count < Start)
513 {
514 continue;
515 }
516 else if(Count >= End)
517 {
518 break;
519 }
520 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
521 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
522 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
523 }
524 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);
525 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
526}
527
528void CNetBan::ConBansFind(IConsole::IResult *pResult, void *pUser)
529{
530 CNetBan *pThis = static_cast<CNetBan *>(pUser);
531
532 const char *pStr = pResult->GetString(Index: 0);
533 char aBuf[256], aMsg[256];
534
535 NETADDR Addr;
536 if(net_addr_from_str(addr: &Addr, string: pStr) != 0)
537 {
538 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "bans_find error (invalid network address)");
539 return;
540 }
541
542 int Count = 0;
543 int Found = 0;
544 // Check first for bans
545 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
546 {
547 if(NetComp(pAddr1: &pBan->m_Data, pAddr2: &Addr) == 0)
548 {
549 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
550 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
551 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
552
553 Found++;
554 }
555 }
556 // check ban ranges
557 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
558 {
559 if(pThis->NetMatch(pRange: &pBan->m_Data, pAddr: &Addr))
560 {
561 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
562 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
563 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
564
565 Found++;
566 }
567 }
568
569 if(Found)
570 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "%i ban records found.", Found);
571 else
572 str_copy(dst: aMsg, src: "No ban records found.", dst_size: sizeof(aMsg));
573
574 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
575}
576
577void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser)
578{
579 CNetBan *pThis = static_cast<CNetBan *>(pUser);
580
581 char aBuf[256];
582 IOHANDLE File = pThis->Storage()->OpenFile(pFilename: pResult->GetString(Index: 0), Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
583 if(!File)
584 {
585 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "failed to save banlist to '%s'", pResult->GetString(Index: 0));
586 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
587 return;
588 }
589
590 int64_t Now = time_timestamp();
591 char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE];
592 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext)
593 {
594 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
595 net_addr_str(addr: &pBan->m_Data, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
596 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s %i %s", aAddrStr1, Min, pBan->m_Info.m_aReason);
597 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
598 io_write_newline(io: File);
599 }
600 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext)
601 {
602 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
603 net_addr_str(addr: &pBan->m_Data.m_LB, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
604 net_addr_str(addr: &pBan->m_Data.m_UB, string: aAddrStr2, max_length: sizeof(aAddrStr2), add_port: false);
605 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban_range %s %s %i %s", aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason);
606 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
607 io_write_newline(io: File);
608 }
609
610 io_close(io: File);
611 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "saved banlist to '%s'", pResult->GetString(Index: 0));
612 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
613}
614