1#ifndef ENGINE_SHARED_NETBAN_H
2#define ENGINE_SHARED_NETBAN_H
3
4#include <base/system.h>
5
6#include <engine/console.h>
7
8inline int NetComp(const NETADDR *pAddr1, const NETADDR *pAddr2)
9{
10 return mem_comp(a: pAddr1, b: pAddr2, size: pAddr1->type == NETTYPE_IPV4 ? 8 : 20);
11}
12
13class CNetRange
14{
15public:
16 NETADDR m_LB;
17 NETADDR m_UB;
18
19 bool IsValid() const { return m_LB.type == m_UB.type && NetComp(pAddr1: &m_LB, pAddr2: &m_UB) < 0; }
20};
21
22inline int NetComp(const CNetRange *pRange1, const CNetRange *pRange2)
23{
24 return NetComp(pAddr1: &pRange1->m_LB, pAddr2: &pRange2->m_LB) || NetComp(pAddr1: &pRange1->m_UB, pAddr2: &pRange2->m_UB);
25}
26
27class CNetBan
28{
29protected:
30 bool NetMatch(const NETADDR *pAddr1, const NETADDR *pAddr2) const
31 {
32 return NetComp(pAddr1, pAddr2) == 0;
33 }
34
35 bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr, int Start, int Length) const
36 {
37 return pRange->m_LB.type == pAddr->type && (Start == 0 || mem_comp(a: &pRange->m_LB.ip[0], b: &pAddr->ip[0], size: Start) == 0) &&
38 mem_comp(a: &pRange->m_LB.ip[Start], b: &pAddr->ip[Start], size: Length - Start) <= 0 && mem_comp(a: &pRange->m_UB.ip[Start], b: &pAddr->ip[Start], size: Length - Start) >= 0;
39 }
40
41 bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr) const
42 {
43 return NetMatch(pRange, pAddr, Start: 0, Length: pRange->m_LB.type == NETTYPE_IPV4 ? 4 : 16);
44 }
45
46 const char *NetToString(const NETADDR *pData, char *pBuffer, unsigned BufferSize) const
47 {
48 char aAddrStr[NETADDR_MAXSTRSIZE];
49 net_addr_str(addr: pData, string: aAddrStr, max_length: sizeof(aAddrStr), add_port: false);
50 str_format(buffer: pBuffer, buffer_size: BufferSize, format: "<{'%s'}>", aAddrStr);
51 return pBuffer;
52 }
53
54 const char *NetToString(const CNetRange *pData, char *pBuffer, unsigned BufferSize) const
55 {
56 char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE];
57 net_addr_str(addr: &pData->m_LB, string: aAddrStr1, max_length: sizeof(aAddrStr1), add_port: false);
58 net_addr_str(addr: &pData->m_UB, string: aAddrStr2, max_length: sizeof(aAddrStr2), add_port: false);
59 str_format(buffer: pBuffer, buffer_size: BufferSize, format: "<{'%s' - '%s'}>", aAddrStr1, aAddrStr2);
60 return pBuffer;
61 }
62
63 class CNetHash
64 {
65 public:
66 int m_Hash;
67 int m_HashIndex; // matching parts for ranges, 0 for addr
68
69 CNetHash() = default;
70 CNetHash(const NETADDR *pAddr);
71 CNetHash(const CNetRange *pRange);
72
73 static int MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]);
74 };
75
76 struct CBanInfo
77 {
78 enum
79 {
80 EXPIRES_NEVER = -1,
81 REASON_LENGTH = 128,
82 };
83 int64_t m_Expires;
84 char m_aReason[REASON_LENGTH];
85 bool m_VerbatimReason;
86 };
87
88 template<class T>
89 struct CBan
90 {
91 T m_Data;
92 CBanInfo m_Info;
93 CNetHash m_NetHash;
94
95 // hash list
96 CBan *m_pHashNext;
97 CBan *m_pHashPrev;
98
99 // used or free list
100 CBan *m_pNext;
101 CBan *m_pPrev;
102 };
103
104 template<class T, int HashCount>
105 class CBanPool
106 {
107 public:
108 typedef T CDataType;
109
110 CBan<CDataType> *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash);
111 int Remove(CBan<CDataType> *pBan);
112 void Update(CBan<CDataType> *pBan, const CBanInfo *pInfo);
113 void Reset();
114
115 int Num() const { return m_CountUsed; }
116 bool IsFull() const { return m_CountUsed == MAX_BANS; }
117
118 CBan<CDataType> *First() const { return m_pFirstUsed; }
119 CBan<CDataType> *First(const CNetHash *pNetHash) const { return m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; }
120 CBan<CDataType> *Find(const CDataType *pData, const CNetHash *pNetHash) const
121 {
122 for(CBan<CDataType> *pBan = m_aapHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; pBan; pBan = pBan->m_pHashNext)
123 {
124 if(NetComp(&pBan->m_Data, pData) == 0)
125 return pBan;
126 }
127
128 return nullptr;
129 }
130 CBan<CDataType> *Get(int Index) const;
131
132 private:
133 enum
134 {
135 MAX_BANS = 2048,
136 };
137
138 CBan<CDataType> *m_aapHashList[HashCount][256];
139 CBan<CDataType> m_aBans[MAX_BANS];
140 CBan<CDataType> *m_pFirstFree;
141 CBan<CDataType> *m_pFirstUsed;
142 int m_CountUsed;
143
144 void InsertUsed(CBan<CDataType> *pBan);
145 };
146
147 typedef CBanPool<NETADDR, 1> CBanAddrPool;
148 typedef CBanPool<CNetRange, 16> CBanRangePool;
149 typedef CBan<NETADDR> CBanAddr;
150 typedef CBan<CNetRange> CBanRange;
151
152 template<class T>
153 void MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const;
154 template<class T>
155 int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason, bool VerbatimReason);
156 template<class T>
157 int Unban(T *pBanPool, const typename T::CDataType *pData);
158
159 class IConsole *m_pConsole;
160 class IStorage *m_pStorage;
161 CBanAddrPool m_BanAddrPool;
162 CBanRangePool m_BanRangePool;
163 NETADDR m_LocalhostIpV4, m_LocalhostIpV6;
164
165public:
166 enum
167 {
168 MSGTYPE_PLAYER = 0,
169 MSGTYPE_LIST,
170 MSGTYPE_BANADD,
171 MSGTYPE_BANREM,
172 };
173
174 class IConsole *Console() const { return m_pConsole; }
175 class IStorage *Storage() const { return m_pStorage; }
176
177 virtual ~CNetBan() = default;
178 void Init(class IConsole *pConsole, class IStorage *pStorage);
179 void Update();
180
181 virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason, bool VerbatimReason);
182 virtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason);
183 int UnbanByAddr(const NETADDR *pAddr);
184 int UnbanByRange(const CNetRange *pRange);
185 int UnbanByIndex(int Index);
186 void UnbanAll();
187 bool IsBanned(const NETADDR *pOrigAddr, char *pBuf, unsigned BufferSize) const;
188
189 static void ConBan(class IConsole::IResult *pResult, void *pUser);
190 static void ConBanRange(class IConsole::IResult *pResult, void *pUser);
191 static void ConUnban(class IConsole::IResult *pResult, void *pUser);
192 static void ConUnbanRange(class IConsole::IResult *pResult, void *pUser);
193 static void ConUnbanAll(class IConsole::IResult *pResult, void *pUser);
194 static void ConBans(class IConsole::IResult *pResult, void *pUser);
195 static void ConBansFind(class IConsole::IResult *pResult, void *pUser);
196 static void ConBansSave(class IConsole::IResult *pResult, void *pUser);
197};
198
199template<class T>
200void CNetBan::MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const
201{
202 if(pBan == nullptr || pBuf == nullptr)
203 {
204 if(BuffSize > 0)
205 pBuf[0] = 0;
206 return;
207 }
208
209 // build type based part
210 char aBuf[256];
211 if(Type == MSGTYPE_PLAYER)
212 str_copy(dst&: aBuf, src: "You have been banned");
213 else
214 {
215 char aTemp[256];
216 switch(Type)
217 {
218 case MSGTYPE_LIST:
219 str_format(aBuf, sizeof(aBuf), "%s banned", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp)));
220 break;
221 case MSGTYPE_BANADD:
222 str_format(aBuf, sizeof(aBuf), "banned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp)));
223 break;
224 case MSGTYPE_BANREM:
225 str_format(aBuf, sizeof(aBuf), "unbanned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp)));
226 break;
227 default:
228 aBuf[0] = 0;
229 }
230 }
231
232 // add info part
233 if(!pBan->m_Info.m_VerbatimReason && pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER)
234 {
235 int Mins = ((pBan->m_Info.m_Expires - time_timestamp()) + 59) / 60;
236 if(Mins <= 1)
237 str_format(pBuf, BuffSize, "%s for 1 minute (%s)", aBuf, pBan->m_Info.m_aReason);
238 else
239 str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason);
240 }
241 else
242 str_format(pBuf, BuffSize, "%s (%s)", aBuf, pBan->m_Info.m_aReason);
243}
244
245#endif
246