Upstream tarball 10110
[amule.git] / src / kademlia / net / PacketTracking.cpp
blobd705514018c1b1bafccca3a8f1ae3a449f4ba4cd
1 //
2 // This file is part of the aMule Project.
3 //
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 )
7 //
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.
21 //
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)) {
52 return;
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();
60 } else {
61 break;
66 bool CPacketTracking::IsTrackedOutListRequestPacket(uint8_t opcode) throw()
68 switch (opcode) {
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:
74 case KADEMLIA_REQ:
75 case KADEMLIA2_REQ:
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:
85 case KADEMLIA2_PING:
86 return true;
87 default:
88 return false;
92 bool CPacketTracking::IsOnOutTrackList(uint32_t ip, uint8_t opcode, bool dontRemove)
94 #ifdef __DEBUG__
95 if (!IsTrackedOutListRequestPacket(opcode)) {
96 wxFAIL; // code error / bug
98 #endif
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)) {
102 if (!dontRemove) {
103 listTrackedRequests.erase(it);
105 return true;
108 return false;
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; )
124 switch (opcode) {
125 case KADEMLIA_BOOTSTRAP_REQ:
126 opcode = KADEMLIA2_BOOTSTRAP_REQ;
127 case KADEMLIA2_BOOTSTRAP_REQ:
128 allowedPacketsPerMinute = 2;
129 break;
130 case KADEMLIA_HELLO_REQ:
131 opcode = KADEMLIA2_HELLO_REQ;
132 case KADEMLIA2_HELLO_REQ:
133 allowedPacketsPerMinute = 3;
134 break;
135 case KADEMLIA_REQ:
136 opcode = KADEMLIA2_REQ;
137 case KADEMLIA2_REQ:
138 allowedPacketsPerMinute = 10;
139 break;
140 case KADEMLIA_SEARCH_NOTES_REQ:
141 opcode = KADEMLIA2_SEARCH_NOTES_REQ;
142 case KADEMLIA2_SEARCH_NOTES_REQ:
143 allowedPacketsPerMinute = 3;
144 break;
145 case KADEMLIA_SEARCH_REQ:
146 opcode = KADEMLIA2_SEARCH_KEY_REQ;
147 case KADEMLIA2_SEARCH_KEY_REQ:
148 allowedPacketsPerMinute = 3;
149 break;
150 case KADEMLIA2_SEARCH_SOURCE_REQ:
151 allowedPacketsPerMinute = 3;
152 break;
153 case KADEMLIA_PUBLISH_REQ:
154 opcode = KADEMLIA2_PUBLISH_KEY_REQ;
155 case KADEMLIA2_PUBLISH_KEY_REQ:
156 allowedPacketsPerMinute = 3;
157 break;
158 case KADEMLIA2_PUBLISH_SOURCE_REQ:
159 allowedPacketsPerMinute = 2;
160 break;
161 case KADEMLIA_PUBLISH_NOTES_REQ:
162 opcode = KADEMLIA2_PUBLISH_NOTES_REQ;
163 case KADEMLIA2_PUBLISH_NOTES_REQ:
164 allowedPacketsPerMinute = 2;
165 break;
166 case KADEMLIA_FIREWALLED2_REQ:
167 opcode = KADEMLIA_FIREWALLED_REQ;
168 case KADEMLIA_FIREWALLED_REQ:
169 allowedPacketsPerMinute = 2;
170 break;
171 case KADEMLIA_FINDBUDDY_REQ:
172 allowedPacketsPerMinute = 2;
173 break;
174 case KADEMLIA_CALLBACK_REQ:
175 allowedPacketsPerMinute = 1;
176 break;
177 case KADEMLIA2_PING:
178 allowedPacketsPerMinute = 2;
179 break;
180 default:
181 // not any request packets, so its a response packet - no further checks on this point
182 return true;
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;
200 } else {
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) {
211 it->m_count = 0;
212 it->m_firstAdded = currentTick; // for the packet we just process
213 } else {
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
219 it->m_count++;
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
237 } else {
238 it->m_dbgLogged = false;
240 return true;
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);
253 return true;
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) {
264 delete it2->second;
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();
280 } else {
281 break;
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);
297 return true;
298 } else {
299 DEBUG_ONLY( warning = true; )
303 #ifdef __DEBUG__
304 if (warning) {
305 AddDebugLogLineN(logKadPacketTracking, wxT("Wrong challenge answer received, client not verified (") + KadIPToString(ip) + wxT(")"));
307 #endif
308 return false;
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)) {
316 return true;
319 return false;