2 // This file is part of the aMule Project.
4 // Copyright (c) 2008 Dévai Tamás ( gonosztopi@amule.org )
5 // Copyright (c) 2008 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "PacketTracking.h"
28 #include "../../amule.h"
29 #include "../../Logger.h"
30 #include "../../OtherFunctions.h"
31 #include "../../NetworkFunctions.h"
32 #include "../../ClientList.h"
33 #include "../../GetTickCount.h"
34 #include <common/Macros.h>
35 #include <protocol/kad/Client2Client/UDP.h>
36 #include <protocol/kad2/Client2Client/UDP.h>
39 using namespace Kademlia
;
42 CPacketTracking::~CPacketTracking()
44 DeleteContents(m_mapTrackPacketsIn
);
47 void CPacketTracking::AddTrackedOutPacket(uint32_t ip
, uint8_t opcode
)
49 // this tracklist tacks _outgoing_ request packets, to make sure incoming answer packets were requested
50 // only track packets which we actually check for later
51 if (!IsTrackedOutListRequestPacket(opcode
)) {
54 uint32_t now
= ::GetTickCount();
55 TrackPackets_Struct track
= { ip
, now
, opcode
};
56 listTrackedRequests
.push_front(track
);
57 while (!listTrackedRequests
.empty()) {
58 if (now
- listTrackedRequests
.back().inserted
> SEC2MS(180)) {
59 listTrackedRequests
.pop_back();
66 bool CPacketTracking::IsTrackedOutListRequestPacket(uint8_t opcode
) throw()
69 case KADEMLIA_BOOTSTRAP_REQ
:
70 case KADEMLIA2_BOOTSTRAP_REQ
:
71 case KADEMLIA_HELLO_REQ
:
72 case KADEMLIA2_HELLO_REQ
:
73 case KADEMLIA2_HELLO_RES
:
76 case KADEMLIA_SEARCH_NOTES_REQ
:
77 case KADEMLIA2_SEARCH_NOTES_REQ
:
78 case KADEMLIA_PUBLISH_REQ
:
79 case KADEMLIA_PUBLISH_NOTES_REQ
:
80 case KADEMLIA2_PUBLISH_KEY_REQ
:
81 case KADEMLIA2_PUBLISH_SOURCE_REQ
:
82 case KADEMLIA2_PUBLISH_NOTES_REQ
:
83 case KADEMLIA_FINDBUDDY_REQ
:
84 case KADEMLIA_CALLBACK_REQ
:
92 bool CPacketTracking::IsOnOutTrackList(uint32_t ip
, uint8_t opcode
, bool dontRemove
)
95 if (!IsTrackedOutListRequestPacket(opcode
)) {
96 wxFAIL
; // code error / bug
99 uint32_t now
= ::GetTickCount();
100 for (TrackedPacketList::iterator it
= listTrackedRequests
.begin(); it
!= listTrackedRequests
.end(); ++it
) {
101 if (it
->ip
== ip
&& it
->opcode
== opcode
&& now
- it
->inserted
< SEC2MS(180)) {
103 listTrackedRequests
.erase(it
);
111 bool CPacketTracking::InTrackListIsAllowedPacket(uint32_t ip
, uint8_t opcode
, bool /*bValidSenderkey*/)
113 // this tracklist tacks _incoming_ request packets and acts as a general flood protection by dropping
114 // too frequent requests from a single IP, avoiding response floods, processing time DOS attacks and slowing down
115 // other possible attacks/behavior (scanning indexed files, fake publish floods, etc)
117 // first figure out if this is a request packet to be tracked and its timelimits
118 // timelimits are chosen by estimating the max. frequency of such packets on normal operation (+ buffer)
119 // (those limits are not meant to be fine to be used by normal usage, but only supposed to be a flood detection)
121 uint32_t allowedPacketsPerMinute
;
122 DEBUG_ONLY( const uint8_t dbgOrgOpcode
= opcode
; )
125 case KADEMLIA_BOOTSTRAP_REQ
:
126 opcode
= KADEMLIA2_BOOTSTRAP_REQ
;
127 case KADEMLIA2_BOOTSTRAP_REQ
:
128 allowedPacketsPerMinute
= 2;
130 case KADEMLIA_HELLO_REQ
:
131 opcode
= KADEMLIA2_HELLO_REQ
;
132 case KADEMLIA2_HELLO_REQ
:
133 allowedPacketsPerMinute
= 3;
136 opcode
= KADEMLIA2_REQ
;
138 allowedPacketsPerMinute
= 10;
140 case KADEMLIA_SEARCH_NOTES_REQ
:
141 opcode
= KADEMLIA2_SEARCH_NOTES_REQ
;
142 case KADEMLIA2_SEARCH_NOTES_REQ
:
143 allowedPacketsPerMinute
= 3;
145 case KADEMLIA_SEARCH_REQ
:
146 opcode
= KADEMLIA2_SEARCH_KEY_REQ
;
147 case KADEMLIA2_SEARCH_KEY_REQ
:
148 allowedPacketsPerMinute
= 3;
150 case KADEMLIA2_SEARCH_SOURCE_REQ
:
151 allowedPacketsPerMinute
= 3;
153 case KADEMLIA_PUBLISH_REQ
:
154 opcode
= KADEMLIA2_PUBLISH_KEY_REQ
;
155 case KADEMLIA2_PUBLISH_KEY_REQ
:
156 allowedPacketsPerMinute
= 3;
158 case KADEMLIA2_PUBLISH_SOURCE_REQ
:
159 allowedPacketsPerMinute
= 2;
161 case KADEMLIA_PUBLISH_NOTES_REQ
:
162 opcode
= KADEMLIA2_PUBLISH_NOTES_REQ
;
163 case KADEMLIA2_PUBLISH_NOTES_REQ
:
164 allowedPacketsPerMinute
= 2;
166 case KADEMLIA_FIREWALLED2_REQ
:
167 opcode
= KADEMLIA_FIREWALLED_REQ
;
168 case KADEMLIA_FIREWALLED_REQ
:
169 allowedPacketsPerMinute
= 2;
171 case KADEMLIA_FINDBUDDY_REQ
:
172 allowedPacketsPerMinute
= 2;
174 case KADEMLIA_CALLBACK_REQ
:
175 allowedPacketsPerMinute
= 1;
178 allowedPacketsPerMinute
= 2;
181 // not any request packets, so its a response packet - no further checks on this point
185 const uint32_t secondsPerPacket
= 60 / allowedPacketsPerMinute
;
186 const uint32_t currentTick
= ::GetTickCount();
188 // time for cleaning up?
189 if (currentTick
- lastTrackInCleanup
> MIN2MS(12)) {
190 InTrackListCleanup();
193 // check for existing entries
194 TrackedPacketInMap::iterator it2
= m_mapTrackPacketsIn
.find(ip
);
195 TrackPacketsIn_Struct
*trackEntry
;
196 if (it2
== m_mapTrackPacketsIn
.end()) {
197 trackEntry
= new TrackPacketsIn_Struct();
198 trackEntry
->m_ip
= ip
;
199 m_mapTrackPacketsIn
[ip
] = trackEntry
;
201 trackEntry
= it2
->second
;
204 // search specific request tracks
205 for (TrackPacketsIn_Struct::TrackedRequestList::iterator it
= trackEntry
->m_trackedRequests
.begin(); it
!= trackEntry
->m_trackedRequests
.end(); ++it
) {
206 if (it
->m_opcode
== opcode
) {
207 // already tracked requests with this opcode, remove already expired request counts
208 if (it
->m_count
> 0 && currentTick
- it
->m_firstAdded
> SEC2MS(secondsPerPacket
)) {
209 uint32_t removeCount
= (currentTick
- it
->m_firstAdded
) / SEC2MS(secondsPerPacket
);
210 if (removeCount
> it
->m_count
) {
212 it
->m_firstAdded
= currentTick
; // for the packet we just process
214 it
->m_count
-= removeCount
;
215 it
->m_firstAdded
+= SEC2MS(secondsPerPacket
) * removeCount
;
218 // we increase the counter in any case, even if we drop the packet later
220 // remember only for easier cleanup
221 trackEntry
->m_lastExpire
= std::max(trackEntry
->m_lastExpire
, it
->m_firstAdded
+ SEC2MS(secondsPerPacket
) * it
->m_count
);
223 // now the actual check if this request is allowed
224 if (it
->m_count
> allowedPacketsPerMinute
* 5) {
225 // this is so far above the limit that it has to be an intentional flood / misuse in any case
226 // so we take the next higher punishment and ban the IP
227 AddDebugLogLineN(logKadPacketTracking
, wxString::Format(wxT("Massive request flood detected for opcode 0x%X (0x%X) from IP "), opcode
, dbgOrgOpcode
) + KadIPToString(ip
) + wxT(" - Banning IP"));
228 theApp
->clientlist
->AddBannedClient(wxUINT32_SWAP_ALWAYS(ip
));
229 return false; // drop packet
230 } else if (it
->m_count
> allowedPacketsPerMinute
) {
231 // over the limit, drop the packet but do nothing else
232 if (!it
->m_dbgLogged
) {
233 it
->m_dbgLogged
= true;
234 AddDebugLogLineN(logKadPacketTracking
, wxString::Format(wxT("Request flood detected for opcode 0x%X (0x%X) from IP "), opcode
, dbgOrgOpcode
) + KadIPToString(ip
) + wxT(" - Dropping packets with this opcode"));
236 return false; // drop packet
238 it
->m_dbgLogged
= false;
244 // add a new entry for this request, no checks needed since 1 is always ok
245 TrackPacketsIn_Struct::TrackedRequestIn_Struct curTrackedRequest
;
246 curTrackedRequest
.m_opcode
= opcode
;
247 curTrackedRequest
.m_dbgLogged
= false;
248 curTrackedRequest
.m_firstAdded
= currentTick
;
249 curTrackedRequest
.m_count
= 1;
250 // remember only for easier cleanup
251 trackEntry
->m_lastExpire
= std::max(trackEntry
->m_lastExpire
, currentTick
+ SEC2MS(secondsPerPacket
));
252 trackEntry
->m_trackedRequests
.push_back(curTrackedRequest
);
256 void CPacketTracking::InTrackListCleanup()
258 const uint32_t currentTick
= ::GetTickCount();
259 DEBUG_ONLY( const uint32_t dbgOldSize
= m_mapTrackPacketsIn
.size(); )
260 lastTrackInCleanup
= currentTick
;
261 for (TrackedPacketInMap::iterator it
= m_mapTrackPacketsIn
.begin(); it
!= m_mapTrackPacketsIn
.end();) {
262 TrackedPacketInMap::iterator it2
= it
++;
263 if (it2
->second
->m_lastExpire
< currentTick
) {
265 m_mapTrackPacketsIn
.erase(it2
);
268 AddDebugLogLineN(logKadPacketTracking
, wxString::Format(wxT("Cleaned up Kad Incoming Requests Tracklist, entries before: %u, after %u"), dbgOldSize
, m_mapTrackPacketsIn
.size()));
271 void CPacketTracking::AddLegacyChallenge(const CUInt128
& contactID
, const CUInt128
& challengeID
, uint32_t ip
, uint8_t opcode
)
273 uint32_t now
= ::GetTickCount();
274 TrackChallenge_Struct sTrack
= { ip
, now
, opcode
, contactID
, challengeID
};
275 listChallengeRequests
.push_front(sTrack
);
276 while (!listChallengeRequests
.empty()) {
277 if (now
- listChallengeRequests
.back().inserted
> SEC2MS(180)) {
278 AddDebugLogLineN(logKadPacketTracking
, wxT("Challenge timed out, client not verified - ") + KadIPToString(listChallengeRequests
.back().ip
));
279 listChallengeRequests
.pop_back();
286 bool CPacketTracking::IsLegacyChallenge(const CUInt128
& challengeID
, uint32_t ip
, uint8_t opcode
, CUInt128
& contactID
)
288 uint32_t now
= ::GetTickCount();
289 DEBUG_ONLY( bool warning
= false; )
290 for (TrackChallengeList::iterator it
= listChallengeRequests
.begin(); it
!= listChallengeRequests
.end();) {
291 TrackChallengeList::iterator it2
= it
++;
292 if (it2
->ip
== ip
&& it2
->opcode
== opcode
&& now
- it2
->inserted
< SEC2MS(180)) {
293 wxASSERT(it2
->challenge
!= 0 || opcode
== KADEMLIA2_PING
);
294 if (it2
->challenge
== 0 || it2
->challenge
== challengeID
) {
295 contactID
= it2
->contactID
;
296 listChallengeRequests
.erase(it2
);
299 DEBUG_ONLY( warning
= true; )
305 AddDebugLogLineN(logKadPacketTracking
, wxT("Wrong challenge answer received, client not verified (") + KadIPToString(ip
) + wxT(")"));
311 bool CPacketTracking::HasActiveLegacyChallenge(uint32_t ip
) const
313 uint32_t now
= ::GetTickCount();
314 for (TrackChallengeList::const_iterator it
= listChallengeRequests
.begin(); it
!= listChallengeRequests
.end(); ++it
) {
315 if (it
->ip
== ip
&& now
- it
->inserted
<= SEC2MS(180)) {