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