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", 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");
285 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");
286 Console()->Register(pName: "unban", pParams: "s[ip|entry]", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnban, pUser: this, pHelp: "Unban ip/banlist entry");
287 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");
288 Console()->Register(pName: "unban_all", pParams: "", Flags: CFGFLAG_SERVER | CFGFLAG_MASTER | CFGFLAG_STORE, pfnFunc: ConUnbanAll, pUser: this, pHelp: "Unban all entries");
289 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)");
290 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");
291 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");
292}
293
294void CNetBan::Update()
295{
296 int64_t Now = time_timestamp();
297
298 // remove expired bans
299 char aBuf[256], aNetStr[256];
300 while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now)
301 {
302 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired", NetToString(pData: &m_BanAddrPool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr)));
303 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
304 m_BanAddrPool.Remove(pBan: m_BanAddrPool.First());
305 }
306 while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now)
307 {
308 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s expired", NetToString(pData: &m_BanRangePool.First()->m_Data, pBuffer: aNetStr, BufferSize: sizeof(aNetStr)));
309 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
310 m_BanRangePool.Remove(pBan: m_BanRangePool.First());
311 }
312}
313
314int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason, bool VerbatimReason)
315{
316 return Ban(pBanPool: &m_BanAddrPool, pData: pAddr, Seconds, pReason, VerbatimReason);
317}
318
319int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason)
320{
321 if(pRange->IsValid())
322 return Ban(pBanPool: &m_BanRangePool, pData: pRange, Seconds, pReason, VerbatimReason: false);
323
324 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban failed (invalid range)");
325 return -1;
326}
327
328int CNetBan::UnbanByAddr(const NETADDR *pAddr)
329{
330 return Unban(pBanPool: &m_BanAddrPool, pData: pAddr);
331}
332
333int CNetBan::UnbanByRange(const CNetRange *pRange)
334{
335 if(pRange->IsValid())
336 return Unban(pBanPool: &m_BanRangePool, pData: pRange);
337
338 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban failed (invalid range)");
339 return -1;
340}
341
342int CNetBan::UnbanByIndex(int Index)
343{
344 int Result;
345 char aBuf[256];
346 CBanAddr *pBan = m_BanAddrPool.Get(Index);
347 if(pBan)
348 {
349 NetToString(pData: &pBan->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf));
350 Result = m_BanAddrPool.Remove(pBan);
351 }
352 else
353 {
354 CBanRange *pBanRange = m_BanRangePool.Get(Index: Index - m_BanAddrPool.Num());
355 if(pBanRange)
356 {
357 NetToString(pData: &pBanRange->m_Data, pBuffer: aBuf, BufferSize: sizeof(aBuf));
358 Result = m_BanRangePool.Remove(pBan: pBanRange);
359 }
360 else
361 {
362 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban failed (invalid index)");
363 return -1;
364 }
365 }
366
367 char aMsg[256];
368 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "unbanned index %i (%s)", Index, aBuf);
369 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
370 return Result;
371}
372
373bool CNetBan::IsBanned(const NETADDR *pOrigAddr, char *pBuf, unsigned BufferSize) const
374{
375 NETADDR Addr;
376 const NETADDR *pAddr = pOrigAddr;
377 if(pOrigAddr->type == NETTYPE_WEBSOCKET_IPV4)
378 {
379 Addr = *pOrigAddr;
380 pAddr = &Addr;
381 Addr.type = NETTYPE_IPV4;
382 }
383 else if(pOrigAddr->type == NETTYPE_WEBSOCKET_IPV6)
384 {
385 Addr = *pOrigAddr;
386 pAddr = &Addr;
387 Addr.type = NETTYPE_IPV6;
388 }
389 CNetHash aHash[17];
390 int Length = CNetHash::MakeHashArray(pAddr, aHash);
391
392 // check ban addresses
393 CBanAddr *pBan = m_BanAddrPool.Find(pData: pAddr, pNetHash: &aHash[Length]);
394 if(pBan)
395 {
396 MakeBanInfo(pBan, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER);
397 return true;
398 }
399
400 // check ban ranges
401 for(int i = Length - 1; i >= 0; --i)
402 {
403 for(CBanRange *pBanRange = m_BanRangePool.First(pNetHash: &aHash[i]); pBanRange; pBanRange = pBanRange->m_pHashNext)
404 {
405 if(NetMatch(pRange: &pBanRange->m_Data, pAddr, Start: i, Length))
406 {
407 MakeBanInfo(pBan: pBanRange, pBuf, BuffSize: BufferSize, Type: MSGTYPE_PLAYER);
408 return true;
409 }
410 }
411 }
412
413 return false;
414}
415
416void CNetBan::ConBan(IConsole::IResult *pResult, void *pUser)
417{
418 CNetBan *pThis = static_cast<CNetBan *>(pUser);
419
420 const char *pStr = pResult->GetString(Index: 0);
421 int Minutes = pResult->NumArguments() > 1 ? std::clamp(val: pResult->GetInteger(Index: 1), lo: 0, hi: 525600) : 30;
422 const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "No reason given";
423
424 NETADDR Addr;
425 if(net_addr_from_str(addr: &Addr, string: pStr) == 0)
426 pThis->BanAddr(pAddr: &Addr, Seconds: Minutes * 60, pReason, VerbatimReason: false);
427 else
428 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban error (invalid network address)");
429}
430
431void CNetBan::ConBanRange(IConsole::IResult *pResult, void *pUser)
432{
433 CNetBan *pThis = static_cast<CNetBan *>(pUser);
434
435 const char *pStr1 = pResult->GetString(Index: 0);
436 const char *pStr2 = pResult->GetString(Index: 1);
437 int Minutes = pResult->NumArguments() > 2 ? std::clamp(val: pResult->GetInteger(Index: 2), lo: 0, hi: 525600) : 30;
438 const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "No reason given";
439
440 CNetRange Range;
441 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
442 pThis->BanRange(pRange: &Range, Seconds: Minutes * 60, pReason);
443 else
444 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "ban error (invalid range)");
445}
446
447void CNetBan::ConUnban(IConsole::IResult *pResult, void *pUser)
448{
449 CNetBan *pThis = static_cast<CNetBan *>(pUser);
450
451 const char *pStr = pResult->GetString(Index: 0);
452 if(str_isallnum(str: pStr))
453 pThis->UnbanByIndex(Index: str_toint(str: pStr));
454 else
455 {
456 NETADDR Addr;
457 if(net_addr_from_str(addr: &Addr, string: pStr) == 0)
458 pThis->UnbanByAddr(pAddr: &Addr);
459 else
460 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid network address)");
461 }
462}
463
464void CNetBan::ConUnbanRange(IConsole::IResult *pResult, void *pUser)
465{
466 CNetBan *pThis = static_cast<CNetBan *>(pUser);
467
468 const char *pStr1 = pResult->GetString(Index: 0);
469 const char *pStr2 = pResult->GetString(Index: 1);
470
471 CNetRange Range;
472 if(net_addr_from_str(addr: &Range.m_LB, string: pStr1) == 0 && net_addr_from_str(addr: &Range.m_UB, string: pStr2) == 0)
473 pThis->UnbanByRange(pRange: &Range);
474 else
475 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unban error (invalid range)");
476}
477
478void CNetBan::ConUnbanAll(IConsole::IResult *pResult, void *pUser)
479{
480 CNetBan *pThis = static_cast<CNetBan *>(pUser);
481
482 pThis->UnbanAll();
483 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "unbanned all entries");
484}
485
486void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser)
487{
488 CNetBan *pThis = static_cast<CNetBan *>(pUser);
489
490 const int NumBans = pThis->m_BanAddrPool.Num() + pThis->m_BanRangePool.Num();
491 if(NumBans == 0)
492 {
493 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "The ban list is empty.");
494 return;
495 }
496
497 static constexpr int ENTRIES_PER_PAGE = 20;
498 const int NumPages = std::ceil(x: NumBans / (float)ENTRIES_PER_PAGE);
499 const int Page = pResult->NumArguments() > 0 ? pResult->GetInteger(Index: 0) : 1;
500
501 char aBuf[256], aMsg[256];
502 if(Page <= 0 || Page > NumPages)
503 {
504 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");
505 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
506 return;
507 }
508
509 const int Start = (Page - 1) * ENTRIES_PER_PAGE;
510 const int End = Page * ENTRIES_PER_PAGE;
511 int Count = 0;
512 for(const CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
513 {
514 if(Count < Start)
515 {
516 continue;
517 }
518 else if(Count >= End)
519 {
520 break;
521 }
522 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
523 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
524 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
525 }
526 for(const CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
527 {
528 if(Count < Start)
529 {
530 continue;
531 }
532 else if(Count >= End)
533 {
534 break;
535 }
536 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
537 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
538 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
539 }
540 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);
541 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
542}
543
544void CNetBan::ConBansFind(IConsole::IResult *pResult, void *pUser)
545{
546 CNetBan *pThis = static_cast<CNetBan *>(pUser);
547
548 const char *pStr = pResult->GetString(Index: 0);
549 char aBuf[256], aMsg[256];
550
551 NETADDR Addr;
552 if(net_addr_from_str(addr: &Addr, string: pStr) != 0)
553 {
554 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: "bans_find error (invalid network address)");
555 return;
556 }
557
558 int Count = 0;
559 int Found = 0;
560 // Check first for bans
561 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext, Count++)
562 {
563 if(NetComp(pAddr1: &pBan->m_Data, pAddr2: &Addr) == 0)
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 // check ban ranges
573 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext, Count++)
574 {
575 if(pThis->NetMatch(pRange: &pBan->m_Data, pAddr: &Addr))
576 {
577 pThis->MakeBanInfo(pBan, pBuf: aBuf, BuffSize: sizeof(aBuf), Type: MSGTYPE_LIST);
578 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "#%i %s", Count, aBuf);
579 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
580
581 Found++;
582 }
583 }
584
585 if(Found)
586 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "%i ban records found.", Found);
587 else
588 str_copy(dst: aMsg, src: "No ban records found.", dst_size: sizeof(aMsg));
589
590 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aMsg);
591}
592
593void CNetBan::ConBansSave(IConsole::IResult *pResult, void *pUser)
594{
595 CNetBan *pThis = static_cast<CNetBan *>(pUser);
596
597 char aBuf[256];
598 IOHANDLE File = pThis->Storage()->OpenFile(pFilename: pResult->GetString(Index: 0), Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
599 if(!File)
600 {
601 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "failed to save banlist to '%s'", pResult->GetString(Index: 0));
602 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
603 return;
604 }
605
606 int64_t Now = time_timestamp();
607 char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE];
608 for(CBanAddr *pBan = pThis->m_BanAddrPool.First(); pBan; pBan = pBan->m_pNext)
609 {
610 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
611 net_addr_str(addr: &pBan->m_Data, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
612 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban %s %i %s", aAddrStr1, Min, pBan->m_Info.m_aReason);
613 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
614 io_write_newline(io: File);
615 }
616 for(CBanRange *pBan = pThis->m_BanRangePool.First(); pBan; pBan = pBan->m_pNext)
617 {
618 int Min = pBan->m_Info.m_Expires > -1 ? (pBan->m_Info.m_Expires - Now + 59) / 60 : -1;
619 net_addr_str(addr: &pBan->m_Data.m_LB, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
620 net_addr_str(addr: &pBan->m_Data.m_UB, string: aAddrStr2, max_length: sizeof(aAddrStr2), add_port: false);
621 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "ban_range %s %s %i %s", aAddrStr1, aAddrStr2, Min, pBan->m_Info.m_aReason);
622 io_write(io: File, buffer: aBuf, size: str_length(str: aBuf));
623 io_write_newline(io: File);
624 }
625
626 io_close(io: File);
627 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "saved banlist to '%s'", pResult->GetString(Index: 0));
628 pThis->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "net_ban", pStr: aBuf);
629}
630