Upstream tarball 10095
[amule.git] / src / kademlia / routing / RoutingBin.cpp
blob755d420ee722e0343ca7ab9da10f525d1d2dfbfb
1 //
2 // This file is part of aMule Project
3 //
4 // Copyright (c) 2004-2008 Angel Vidal ( kry@amule.org )
5 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2003-2008 Barry Dunne ( http://www.emule-project.net )
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 // This work is based on the java implementation of the Kademlia protocol.
23 // Kademlia: Peer-to-peer routing based on the XOR metric
24 // Copyright (c) 2002-2008 Petar Maymounkov ( petar@post.harvard.edu )
25 // http://kademlia.scs.cs.nyu.edu
27 // Note To Mods //
29 Please do not change anything here and release it..
30 There is going to be a new forum created just for the Kademlia side of the client..
31 If you feel there is an error or a way to improve something, please
32 post it in the forum first and let us look at it.. If it is a real improvement,
33 it will be added to the offical client.. Changing something without knowing
34 what all it does can cause great harm to the network if released in mass form..
35 Any mod that changes anything within the Kademlia side will not be allowed to advertise
36 there client on the eMule forum..
39 #include "RoutingBin.h"
40 #include "../../Logger.h"
41 #include "../../NetworkFunctions.h"
42 #include "../../RandomFunctions.h"
44 ////////////////////////////////////////
45 using namespace Kademlia;
46 ////////////////////////////////////////
48 CRoutingBin::GlobalTrackingMap CRoutingBin::s_globalContactIPs;
49 CRoutingBin::GlobalTrackingMap CRoutingBin::s_globalContactSubnets;
51 #define MAX_CONTACTS_SUBNET 10
52 #define MAX_CONTACTS_IP 1
54 CRoutingBin::~CRoutingBin()
56 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
57 AdjustGlobalTracking((*it)->GetIPAddress(), false);
58 if (!m_dontDeleteContacts) {
59 delete *it;
63 m_entries.clear();
66 bool CRoutingBin::AddContact(CContact *contact)
68 wxASSERT(contact != NULL);
70 uint32_t sameSubnets = 0;
71 // Check if we already have a contact with this ID in the list.
72 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
73 if (contact->GetClientID() == (*it)->GetClientID()) {
74 return false;
76 if ((contact->GetIPAddress() & 0xFFFFFF00) == ((*it)->GetIPAddress() & 0xFFFFFF00)) {
77 sameSubnets++;
80 // Several checks to make sure that we don't store multiple contacts from the same IP or too many contacts from the same subnet
81 // This is supposed to add a bit of protection against several attacks and raise the resource needs (IPs) for a successful contact on the attacker side
82 // Such IPs are not banned from Kad, they still can index, search, etc so multiple KAD clients behind one IP still work
84 if (!CheckGlobalIPLimits(contact->GetIPAddress(), contact->GetUDPPort())) {
85 return false;
88 // no more than 2 IPs from the same /24 netmask in one bin, except if its a LANIP (if we don't accept LANIPs they already have been filtered before)
89 if (sameSubnets >= 2 && !::IsLanIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress()))) {
90 AddDebugLogLineN(logKadRouting, wxT("Ignored kad contact (IP=") + KadIPPortToString(contact->GetIPAddress(), contact->GetUDPPort()) + wxT(") - too many contact with the same subnet in RoutingBin"));
91 return false;
94 // If not full, add to the end of list
95 if (m_entries.size() < K) {
96 m_entries.push_back(contact);
97 AdjustGlobalTracking(contact->GetIPAddress(), true);
98 return true;
100 return false;
103 void CRoutingBin::SetAlive(CContact *contact)
105 wxASSERT(contact != NULL);
106 // Check if we already have a contact with this ID in the list.
107 CContact *test = GetContact(contact->GetClientID());
108 wxASSERT(contact == test);
109 if (test) {
110 // Mark contact as being alive.
111 test->UpdateType();
112 // Move to the end of the list
113 PushToBottom(test);
117 void CRoutingBin::SetTCPPort(uint32_t ip, uint16_t port, uint16_t tcpPort)
119 // Find contact with IP/Port
120 for (ContactList::iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
121 CContact *c = *it;
122 if ((ip == c->GetIPAddress()) && (port == c->GetUDPPort())) {
123 // Set TCPPort and mark as alive.
124 c->SetTCPPort(tcpPort);
125 c->UpdateType();
126 // Move to the end of the list
127 PushToBottom(c);
128 break;
133 CContact *CRoutingBin::GetContact(const CUInt128 &id) const throw()
135 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
136 if ((*it)->GetClientID() == id) {
137 return *it;
140 return NULL;
143 CContact *CRoutingBin::GetContact(uint32_t ip, uint16_t port, bool tcpPort) const throw()
145 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
146 CContact *contact = *it;
147 if ((contact->GetIPAddress() == ip)
148 && ((!tcpPort && port == contact->GetUDPPort()) || (tcpPort && port == contact->GetTCPPort()) || port == 0)) {
149 return contact;
152 return NULL;
155 void CRoutingBin::GetNumContacts(uint32_t& nInOutContacts, uint32_t& nInOutFilteredContacts, uint8_t minVersion) const throw()
157 // count all nodes which meet the search criteria and also report those who don't
158 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
159 if ((*it)->GetVersion() >= minVersion) {
160 nInOutContacts++;
161 } else {
162 nInOutFilteredContacts++;
167 void CRoutingBin::GetEntries(ContactList *result, bool emptyFirst) const
169 // Clear results if requested first.
170 if (emptyFirst) {
171 result->clear();
174 // Append all entries to the results.
175 if (m_entries.size() > 0) {
176 result->insert(result->end(), m_entries.begin(), m_entries.end());
180 void CRoutingBin::GetClosestTo(uint32_t maxType, const CUInt128 &target, uint32_t maxRequired, ContactMap *result, bool emptyFirst, bool inUse) const
182 // Empty list if requested.
183 if (emptyFirst) {
184 result->clear();
187 // No entries, no closest.
188 if (m_entries.size() == 0) {
189 return;
192 // First put results in sort order for target so we can insert them correctly.
193 // We don't care about max results at this time.
194 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
195 if ((*it)->GetType() <= maxType && (*it)->IsIPVerified()) {
196 CUInt128 targetDistance((*it)->GetClientID() ^ target);
197 (*result)[targetDistance] = *it;
198 // This list will be used for an unknown time, Inc in use so it's not deleted.
199 if (inUse) {
200 (*it)->IncUse();
205 // Remove any extra results by least wanted first.
206 while (result->size() > maxRequired) {
207 // Dec in use count.
208 if (inUse) {
209 (--result->end())->second->DecUse();
211 // Remove from results
212 result->erase(--result->end());
216 void CRoutingBin::AdjustGlobalTracking(uint32_t ip, bool increase)
218 // IP
219 uint32_t sameIPCount = 0;
220 GlobalTrackingMap::const_iterator itIP = s_globalContactIPs.find(ip);
221 if (itIP != s_globalContactIPs.end()) {
222 sameIPCount = itIP->second;
224 if (increase) {
225 if (sameIPCount >= MAX_CONTACTS_IP) {
226 AddDebugLogLineN(logKadRouting, wxT("Global IP Tracking inconsistency on increase (") + KadIPToString(ip) + wxT(")"));
227 wxFAIL;
229 sameIPCount++;
230 } else /* if (!increase) */ {
231 if (sameIPCount == 0) {
232 AddDebugLogLineN(logKadRouting, wxT("Global IP Tracking inconsistency on decrease (") + KadIPToString(ip) + wxT(")"));
233 wxFAIL;
235 sameIPCount--;
237 if (sameIPCount != 0) {
238 s_globalContactIPs[ip] = sameIPCount;
239 } else {
240 s_globalContactIPs.erase(ip);
243 // Subnet
244 uint32_t sameSubnetCount = 0;
245 GlobalTrackingMap::const_iterator itSubnet = s_globalContactSubnets.find(ip & 0xFFFFFF00);
246 if (itSubnet != s_globalContactSubnets.end()) {
247 sameSubnetCount = itSubnet->second;
249 if (increase) {
250 if (sameSubnetCount >= MAX_CONTACTS_SUBNET && !::IsLanIP(wxUINT32_SWAP_ALWAYS(ip))) {
251 AddDebugLogLineN(logKadRouting, wxT("Global Subnet Tracking inconsistency on increase (") + KadIPToString(ip) + wxT("/24)"));
252 wxFAIL;
254 sameSubnetCount++;
255 } else /* if (!increase) */ {
256 if (sameSubnetCount == 0) {
257 AddDebugLogLineN(logKadRouting, wxT("Global Subnet Tracking inconsistency on decrease (") + KadIPToString(ip) + wxT("/24)"));
258 wxFAIL;
260 sameSubnetCount--;
262 if (sameSubnetCount != 0) {
263 s_globalContactSubnets[ip & 0xFFFFFF00] = sameSubnetCount;
264 } else {
265 s_globalContactSubnets.erase(ip & 0xFFFFFF00);
269 bool CRoutingBin::ChangeContactIPAddress(CContact *contact, uint32_t newIP)
271 // Called if we want to update an indexed contact with a new IP. We have to check if we actually allow such a change
272 // and if adjust our tracking. Rejecting a change will in the worst case lead a node contact to become invalid and purged later,
273 // but it also protects against a flood of malicous update requests from one IP which would be able to "reroute" all
274 // contacts to itself and by that making them useless
275 if (contact->GetIPAddress() == newIP) {
276 return true;
279 wxASSERT(GetContact(contact->GetClientID()) == contact);
281 // no more than 1 KadID per IP
282 uint32_t sameIPCount = 0;
283 GlobalTrackingMap::const_iterator itIP = s_globalContactIPs.find(newIP);
284 if (itIP != s_globalContactIPs.end()) {
285 sameIPCount = itIP->second;
287 if (sameIPCount >= MAX_CONTACTS_IP) {
288 AddDebugLogLineN(logKadRouting, wxT("Rejected kad contact IP change on update (old IP=") + KadIPToString(contact->GetIPAddress()) + wxT(", requested IP=") + KadIPToString(newIP) + wxT(") - too many contacts with the same IP (global)"));
289 return false;
292 if ((contact->GetIPAddress() & 0xFFFFFF00) != (newIP & 0xFFFFFF00)) {
293 // no more than 10 IPs from the same /24 netmask global, except if it's a LAN IP (if we don't accept LAN IPs they already have been filtered before)
294 uint32_t sameSubnetGlobalCount = 0;
295 GlobalTrackingMap::const_iterator itGlobalSubnet = s_globalContactSubnets.find(newIP & 0xFFFFFF00);
296 if (itGlobalSubnet != s_globalContactSubnets.end()) {
297 sameSubnetGlobalCount = itGlobalSubnet->second;
299 if (sameSubnetGlobalCount >= MAX_CONTACTS_SUBNET && !::IsLanIP(wxUINT32_SWAP_ALWAYS(newIP))) {
300 AddDebugLogLineN(logKadRouting, wxT("Rejected kad contact IP change on update (old IP=") + KadIPToString(contact->GetIPAddress()) + wxT(", requested IP=") + KadIPToString(newIP) + wxT(") - too many contacts with the same Subnet (global)"));
301 return false;
304 // no more than 2 IPs from the same /24 netmask in one bin, except if it's a LAN IP (if we don't accept LAN IPs they already have been filtered before)
305 uint32_t sameSubnets = 0;
306 // Check if we already have a contact with this ID in the list.
307 for (ContactList::const_iterator itContact = m_entries.begin(); itContact != m_entries.end(); ++itContact) {
308 if ((newIP & 0xFFFFFF00) == ((*itContact)->GetIPAddress() & 0xFFFFFF00)) {
309 sameSubnets++;
312 if (sameSubnets >= 2 && !::IsLanIP(wxUINT32_SWAP_ALWAYS(newIP))) {
313 AddDebugLogLineN(logKadRouting, wxT("Rejected kad contact IP change on update (old IP=") + KadIPToString(contact->GetIPAddress()) + wxT(", requested IP=") + KadIPToString(newIP) + wxT(") - too many contacts with the same Subnet (local)"));
314 return false;
318 // everything fine
319 AddDebugLogLineN(logKadRouting, wxT("Index contact IP change allowed ") + KadIPToString(contact->GetIPAddress()) + wxT(" -> ") + KadIPToString(newIP));
320 AdjustGlobalTracking(contact->GetIPAddress(), false);
321 contact->SetIPAddress(newIP);
322 AdjustGlobalTracking(contact->GetIPAddress(), true);
323 return true;
326 void CRoutingBin::PushToBottom(CContact *contact)
328 wxASSERT(GetContact(contact->GetClientID()) == contact);
330 RemoveContact(contact, true);
331 m_entries.push_back(contact);
334 CContact *CRoutingBin::GetRandomContact(uint32_t maxType, uint32_t minKadVersion) const
336 if (m_entries.empty()) {
337 return NULL;
340 // Find contact
341 CContact *lastFit = NULL;
342 uint32_t randomStartPos = GetRandomUint16() % m_entries.size();
343 uint32_t index = 0;
345 for (ContactList::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
346 if ((*it)->GetType() <= maxType && (*it)->GetVersion() >= minKadVersion) {
347 if (index >= randomStartPos) {
348 return *it;
349 } else {
350 lastFit = *it;
353 index++;
356 return lastFit;
359 void CRoutingBin::SetAllContactsVerified()
361 for (ContactList::iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
362 (*it)->SetIPVerified(true);
366 bool CRoutingBin::CheckGlobalIPLimits(uint32_t ip, uint16_t DEBUG_ONLY(port))
368 // no more than 1 KadID per IP
369 uint32_t sameIPCount = 0;
370 GlobalTrackingMap::const_iterator itIP = s_globalContactIPs.find(ip);
371 if (itIP != s_globalContactIPs.end()) {
372 sameIPCount = itIP->second;
374 if (sameIPCount >= MAX_CONTACTS_IP) {
375 AddDebugLogLineN(logKadRouting, wxT("Ignored kad contact (IP=") + KadIPPortToString(ip, port) + wxT(") - too many contacts with the same IP (global)"));
376 return false;
378 // no more than 10 IPs from the same /24 netmask global, except if its a LANIP (if we don't accept LANIPs they already have been filtered before)
379 uint32_t sameSubnetGlobalCount = 0;
380 GlobalTrackingMap::const_iterator itSubnet = s_globalContactSubnets.find(ip & 0xFFFFFF00);
381 if (itSubnet != s_globalContactSubnets.end()) {
382 sameSubnetGlobalCount = itSubnet->second;
384 if (sameSubnetGlobalCount >= MAX_CONTACTS_SUBNET && !::IsLanIP(wxUINT32_SWAP_ALWAYS(ip))) {
385 AddDebugLogLineN(logKadRouting, wxT("Ignored kad contact (IP=") + KadIPPortToString(ip, port) + wxT(") - too many contacts with the same subnet (global)"));
386 return false;
388 return true;