1 | #ifndef ENGINE_SHARED_NETBAN_H |
2 | #define ENGINE_SHARED_NETBAN_H |
3 | |
4 | #include <base/system.h> |
5 | #include <engine/console.h> |
6 | |
7 | inline 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 | |
12 | class CNetRange |
13 | { |
14 | public: |
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 | |
21 | inline 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 | |
26 | class CNetBan |
27 | { |
28 | protected: |
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 = 128, |
81 | }; |
82 | int64_t 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 | |
163 | public: |
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 | |
196 | template<class T> |
197 | void 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 | |