Upstream tarball 20080802
[amule.git] / src / kademlia / routing / RoutingZone.cpp
blob47c1fda0aa8b3aba7e92dedac697b81b2e930fa9
1 //
2 // This file is part of aMule Project
3 //
4 // Copyright (c) 2004-2008 Angel Vidal ( kry@amule.org )
5 // Copyright (c) 2004-2008 aMule Project ( http://www.amule-project.net )
6 // Copyright (C)2003 Barry Dunne (http://www.emule-project.net)
7 // Copyright (C)2007-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either
12 // version 2 of the License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 // This work is based on the java implementation of the Kademlia protocol.
24 // Kademlia: Peer-to-peer routing based on the XOR metric
25 // Copyright (C) 2002 Petar Maymounkov [petar@post.harvard.edu]
26 // http://kademlia.scs.cs.nyu.edu
28 // Note To Mods //
30 Please do not change anything here and release it..
31 There is going to be a new forum created just for the Kademlia side of the client..
32 If you feel there is an error or a way to improve something, please
33 post it in the forum first and let us look at it.. If it is a real improvement,
34 it will be added to the offical client.. Changing something without knowing
35 what all it does can cause great harm to the network if released in mass form..
36 Any mod that changes anything within the Kademlia side will not be allowed to advertise
37 there client on the eMule forum..
40 /**
41 * The *Zone* is just a node in a binary tree of *Zone*s.
42 * Each zone is either an internal node or a leaf node.
43 * Internal nodes have "bin == null" and "subZones[i] != null",
44 * leaf nodes have "subZones[i] == null" and "bin != null".
46 * All key unique id's are relative to the center (self), which
47 * is considered to be 000..000
49 #include "RoutingZone.h"
51 #include <protocol/kad/Client2Client/UDP.h>
52 #include <protocol/kad2/Client2Client/UDP.h>
53 #include <common/Macros.h>
55 #include "Contact.h"
56 #include "RoutingBin.h"
57 #include "../kademlia/Defines.h"
58 #include "../kademlia/SearchManager.h"
59 #include "../kademlia/UDPFirewallTester.h"
60 #include "../net/KademliaUDPListener.h"
61 #include "../utils/KadUDPKey.h"
62 #include "../../amule.h"
63 #include "../../CFile.h"
64 #include "../../Logger.h"
65 #include "../../NetworkFunctions.h"
66 #include "../../IPFilter.h"
67 #include "../../RandomFunctions.h"
69 #include <cmath>
71 ////////////////////////////////////////
72 using namespace Kademlia;
73 ////////////////////////////////////////
75 // This is just a safety precaution
76 #define CONTACT_FILE_LIMIT 500
78 wxString CRoutingZone::m_filename;
79 CUInt128 CRoutingZone::me((uint32_t)0);
81 CRoutingZone::CRoutingZone()
83 // Can only create routing zone after prefs
84 // Set our KadID for creating the contact tree
85 me = CKademlia::GetPrefs()->GetKadID();
86 // Set the preference file name.
87 m_filename = theApp->ConfigDir + wxT("nodes.dat");
88 Init(NULL, 0, CUInt128((uint32_t)0));
91 void CRoutingZone::Init(CRoutingZone *super_zone, int level, const CUInt128& zone_index)
93 // Init all Zone vars
94 // Set this zone's parent
95 m_superZone = super_zone;
96 // Set this zone's level
97 m_level = level;
98 // Set this zone's CUInt128 index
99 m_zoneIndex = zone_index;
100 // Mark this zone as having no leafs.
101 m_subZones[0] = NULL;
102 m_subZones[1] = NULL;
103 // Create a new contact bin as this is a leaf.
104 m_bin = new CRoutingBin();
106 // Set timer so that zones closer to the root are processed earlier.
107 m_nextSmallTimer = time(NULL) + m_zoneIndex.Get32BitChunk(3);
109 // Start this zone.
110 StartTimer();
112 // If we are initializing the root node, read in our saved contact list.
113 if ((m_superZone == NULL) && (m_filename.Length() > 0)) {
114 ReadFile();
118 CRoutingZone::~CRoutingZone()
120 // Root node is processed first so that we can write our contact list and delete all branches.
121 if ((m_superZone == NULL) && (m_filename.Length() > 0)) {
122 WriteFile();
125 // If this zone is a leaf, delete our contact bin.
126 if (IsLeaf()) {
127 delete m_bin;
128 } else {
129 // If this zone is a branch, delete its leafs.
130 delete m_subZones[0];
131 delete m_subZones[1];
135 void CRoutingZone::ReadFile(const wxString& specialNodesdat)
137 if (m_superZone != NULL || (m_filename.IsEmpty() && specialNodesdat.IsEmpty())) {
138 wxFAIL;
139 return;
142 bool doHaveVerifiedContacts = false;
143 // Read in the saved contact list
144 try {
145 uint32_t numContacts = 0;
146 uint32_t validContacts = 0;
147 CFile file;
148 if (CPath::FileExists(specialNodesdat.IsEmpty() ? m_filename : specialNodesdat) && file.Open(m_filename, CFile::read)) {
149 // Get how many contacts in the saved list.
150 // NOTE: Older clients put the number of contacts here...
151 // Newer clients always have 0 here to prevent older clients from reading it.
152 numContacts = file.ReadUInt32();
153 uint32_t fileVersion = 0;
154 if (numContacts == 0) {
155 if (file.GetLength() >= 8) {
156 fileVersion = file.ReadUInt32();
157 if (fileVersion == 3) {
158 uint32_t bootstrapEdition = file.ReadUInt32();
159 if (bootstrapEdition == 1) {
160 // this is a special bootstrap-only nodes.dat, handle it in a separate reading function
161 ReadBootstrapNodesDat(file);
162 file.Close();
163 return;
166 if (fileVersion >= 1 && fileVersion <= 3) {
167 numContacts = file.ReadUInt32();
171 if (numContacts != 0 && numContacts * 25 <= (file.GetLength() - file.GetPosition())) {
172 for (uint32_t i = 0; i < numContacts; i++) {
173 CUInt128 id = file.ReadUInt128();
174 uint32_t ip = file.ReadUInt32();
175 uint16_t udpPort = file.ReadUInt16();
176 uint16_t tcpPort = file.ReadUInt16();
177 uint8_t type = 0;
178 uint8_t contactVersion = 0;
179 if (fileVersion >= 1) {
180 contactVersion = file.ReadUInt8();
181 } else {
182 type = file.ReadUInt8();
184 CKadUDPKey kadUDPKey;
185 bool verified = false;
186 if (fileVersion >= 2) {
187 kadUDPKey.ReadFromFile(file);
188 verified = file.ReadUInt8() != 0;
189 if (verified) {
190 doHaveVerifiedContacts = true;
193 // IP appears valid
194 if (type < 4) {
195 if(IsGoodIPPort(wxUINT32_SWAP_ALWAYS(ip),udpPort)) {
196 if (!theApp->ipfilter->IsFiltered(wxUINT32_SWAP_ALWAYS(ip)) &&
197 !(udpPort == 53 && contactVersion <= 5 /*No DNS Port without encryption*/)) {
198 // This was not a dead contact, inc counter if add was successful
199 if (AddUnfiltered(id, ip, udpPort, tcpPort, contactVersion, kadUDPKey, verified, false, true, false)) {
200 validContacts++;
207 file.Close();
208 AddLogLineM(false, wxString::Format(wxPLURAL("Read %u Kad contact", "Read %u Kad contacts", validContacts), validContacts));
209 if (!doHaveVerifiedContacts) {
210 AddDebugLogLineM(false, logKadRouting, wxT("No verified contacts found in nodes.dat - might be an old file version. Setting all contacts verified for this time to speed up Kad bootstrapping."));
211 SetAllContactsVerified();
214 if (validContacts == 0) {
215 AddLogLineM(true, _("No contacts found, please bootstrap, or download a nodes.dat file."));
217 } catch (const CSafeIOException& e) {
218 AddDebugLogLineM(false, logKadRouting, wxT("IO error in CRoutingZone::readFile: ") + e.what());
222 void CRoutingZone::ReadBootstrapNodesDat(CFileDataIO& file)
224 // Bootstrap versions of nodes.dat files are in the style of version 1 nodes.dats. The difference is that
225 // they will contain more contacts, 500-1000 instead of 50, and those contacts are not added into the routing table
226 // but used to sent Bootstrap packets to. The advantage is that on a list with a high ratio of dead nodes,
227 // we will be able to bootstrap faster than on a normal nodes.dat and more important, if we would deliver
228 // a normal nodes.dat with eMule, those 50 nodes would be kinda DDOSed because everyone adds them to their routing
229 // table, while with this style, we don't actually add any of the contacts to our routing table in the end and we
230 // ask only one of those 1000 contacts one time (well or more until we find an alive one).
231 if (!CKademlia::s_bootstrapList.empty()){
232 wxFAIL;
233 return;
235 uint32_t numContacts = file.ReadUInt32();
236 if (numContacts != 0 && numContacts * 25 == (file.GetLength() - file.GetPosition())) {
237 uint32_t validContacts = 0;
238 while (numContacts) {
239 CUInt128 id = file.ReadUInt128();
240 uint32_t ip = file.ReadUInt32();
241 uint16_t udpPort = file.ReadUInt16();
242 uint16_t tcpPort = file.ReadUInt16();
243 uint8_t contactVersion = file.ReadUInt8();
245 if (::IsGoodIPPort(wxUINT32_SWAP_ALWAYS(ip), udpPort)) {
246 if (!theApp->ipfilter->IsFiltered(wxUINT32_SWAP_ALWAYS(ip)) &&
247 !(udpPort == 53 && contactVersion <= 5) &&
248 (contactVersion > 1)) // only kad2 nodes
250 // we want the 50 nodes closest to our own ID (provides randomness between different users and gets has good chances to get a bootstrap with close Nodes which is a nice start for our routing table)
251 CUInt128 distance = me;
252 distance ^= id;
253 validContacts++;
254 // don't bother if we already have 50 and the farest distance is smaller than this contact
255 if (CKademlia::s_bootstrapList.size() < 50 || CKademlia::s_bootstrapList.back()->GetDistance() > distance) {
256 // look where to put this contact into the proper position
257 bool inserted = false;
258 CContact* contact = new CContact(id, ip, udpPort, tcpPort, contactVersion, 0, false, me);
259 for (ContactList::iterator it = CKademlia::s_bootstrapList.begin(); it != CKademlia::s_bootstrapList.end(); ++it) {
260 if ((*it)->GetDistance() > distance) {
261 CKademlia::s_bootstrapList.insert(it, contact);
262 inserted = true;
263 break;
266 if (!inserted) {
267 wxASSERT(CKademlia::s_bootstrapList.size() < 50);
268 CKademlia::s_bootstrapList.push_back(contact);
269 } else if (CKademlia::s_bootstrapList.size() > 50) {
270 delete CKademlia::s_bootstrapList.back();
271 CKademlia::s_bootstrapList.pop_back();
276 numContacts--;
278 AddLogLineM(false, wxString::Format(wxPLURAL("Read %u Kad contact", "Read %u Kad contacts", CKademlia::s_bootstrapList.size()), CKademlia::s_bootstrapList.size()));
279 AddDebugLogLineM(false, logKadRouting, wxString::Format(wxT("Loaded Bootstrap nodes.dat, selected %u out of %u valid contacts"), CKademlia::s_bootstrapList.size(), validContacts));
281 if (CKademlia::s_bootstrapList.size() == 0) {
282 AddLogLineM(true, _("No contacts found, please bootstrap, or download a nodes.dat file."));
286 void CRoutingZone::WriteFile()
288 // don't overwrite a bootstrap nodes.dat with an empty one, if we didn't finish probing
289 if (!CKademlia::s_bootstrapList.empty() && GetNumContacts() == 0) {
290 AddDebugLogLineM(false, logKadRouting, wxT("Skipped storing nodes.dat, because we have an unfinished bootstrap of the nodes.dat version and no contacts in our routing table"));
291 return;
294 // The bootstrap method gets a very nice sample of contacts to save.
295 ContactList contacts;
296 GetBootstrapContacts(&contacts, 200);
297 ContactList::size_type numContacts = contacts.size();
298 numContacts = std::min<ContactList::size_type>(numContacts, CONTACT_FILE_LIMIT); // safety precaution, should not be above
299 if (numContacts < 25) {
300 AddLogLineM(false, wxString::Format(wxPLURAL("Only %d Kad contact available, nodes.dat not written", "Only %d Kad contacts available, nodes.dat not written", numContacts), numContacts));
301 return;
303 try {
304 unsigned int count = 0;
305 CFile file;
306 if (file.Open(m_filename, CFile::write)) {
307 // Start file with 0 to prevent older clients from reading it.
308 file.WriteUInt32(0);
309 // Now tag it with a version which happens to be 2.
310 file.WriteUInt32(2);
311 // file.WriteUInt32(0); // if we would use version >= 3 this would mean that this is a normal nodes.dat
312 file.WriteUInt32(numContacts);
313 for (ContactList::const_iterator it = contacts.begin(); it != contacts.end(); ++it) {
314 CContact *c = *it;
315 count++;
316 if (count > CONTACT_FILE_LIMIT) {
317 // This should never happen
318 wxFAIL;
319 break;
321 file.WriteUInt128(c->GetClientID());
322 file.WriteUInt32(c->GetIPAddress());
323 file.WriteUInt16(c->GetUDPPort());
324 file.WriteUInt16(c->GetTCPPort());
325 file.WriteUInt8(c->GetVersion());
326 c->GetUDPKey().StoreToFile(file);
327 file.WriteUInt8(c->IsIPVerified() ? 1 : 0);
330 AddLogLineM(false, wxString::Format(wxPLURAL("Wrote %d Kad contact", "Wrote %d Kad contacts", count), count));
331 } catch (const CIOFailureException& e) {
332 AddDebugLogLineM(true, logKadRouting, wxT("IO failure in CRoutingZone::writeFile: ") + e.what());
336 #if 0
337 void CRoutingZone::WriteBootstrapFile()
339 AddDebugLogLineM(true, logKadRouting, wxT("Writing special bootstrap nodes.dat - not intended for normal use"));
340 try {
341 // Write a saved contact list.
342 CUInt128 id;
343 CFile file;
344 if (file.Open(m_filename, CFile::write)) {
345 // The bootstrap method gets a very nice sample of contacts to save.
346 ContactMap mapContacts;
347 CUInt128 random(CUInt128((uint32_t)0), 0);
348 CUInt128 distance = random;
349 distance ^= me;
350 GetClosestTo(2, random, distance, 1200, &mapContacts, false, false);
351 // filter out Kad1 nodes
352 for (ContactMap::iterator it = mapContacts.begin(); it != mapContacts.end(); ) {
353 ContactMap::iterator itCur = it++;
354 CContact* contact = itCur->second;
355 if (contact->GetVersion() <= 1) {
356 mapContacts.erase(itCur);
359 // Start file with 0 to prevent older clients from reading it.
360 file.WriteUInt32(0);
361 // Now tag it with a version which happens to be 3.
362 file.WriteUInt32(3);
363 file.WriteUInt32(1); // using version >= 3, this means that this is not a normal nodes.dat
364 file.WriteUInt32((uint32_t)mapContacts.size());
365 for (ContactMap::const_iterator it = mapContacts.begin(); it != mapContacts.end(); ++it)
367 CContact* contact = it->second;
368 file.WriteUInt128(contact->GetClientID());
369 file.WriteUInt32(contact->GetIPAddress());
370 file.WriteUInt16(contact->GetUDPPort());
371 file.WriteUInt16(contact->GetTCPPort());
372 file.WriteUInt8(contact->GetVersion());
374 file.Close();
375 AddDebugLogLineM(false, logKadRouting, wxString::Format(wxT("Wrote %u contacts to bootstrap file."), mapContacts.size()));
376 } else {
377 AddDebugLogLineM(true, logKadRouting, wxT("Unable to store Kad file: ") + m_filename);
379 } catch (const CIOFailureException& e) {
380 AddDebugLogLineM(false, logKadRouting, wxT("CFileException in CRoutingZone::writeFile") + e.what());
383 #endif
385 bool CRoutingZone::CanSplit() const throw()
387 // Max levels allowed.
388 if (m_level >= 127) {
389 return false;
392 // Check if this zone is allowed to split.
393 return ((m_zoneIndex < KK || m_level < KBASE) && m_bin->GetSize() == K);
396 // Returns true if a contact was added or updated, false if the routing table was not touched.
397 bool CRoutingZone::Add(const CUInt128& id, uint32_t ip, uint16_t port, uint16_t tport, uint8_t version, const CKadUDPKey& key, bool& ipVerified, bool update, bool fromNodesDat, bool fromHello)
399 if (IsGoodIPPort(wxUINT32_SWAP_ALWAYS(ip), port)) {
400 if (!theApp->ipfilter->IsFiltered(wxUINT32_SWAP_ALWAYS(ip)) && !(port == 53 && version <= 5) /*No DNS Port without encryption*/) {
401 return AddUnfiltered(id, ip, port, tport, version, key, ipVerified, update, fromNodesDat, fromHello);
404 return false;
407 // Returns true if a contact was added or updated, false if the routing table was not touched.
408 bool CRoutingZone::AddUnfiltered(const CUInt128& id, uint32_t ip, uint16_t port, uint16_t tport, uint8_t version, const CKadUDPKey& key, bool& ipVerified, bool update, bool fromNodesDat, bool fromHello)
410 if (id != me) {
411 CContact *contact = new CContact(id, ip, port, tport, version, key, ipVerified);
412 if (fromNodesDat) {
413 contact->CheckIfKad2(); // do not test nodes which we loaded from our nodes.dat for Kad2 again
414 } else if (fromHello) {
415 contact->SetReceivedHelloPacket();
417 if (Add(contact, update, ipVerified)) {
418 wxASSERT(!update);
419 return true;
420 } else {
421 delete contact;
422 return update;
425 return false;
428 bool CRoutingZone::Add(CContact *contact, bool& update, bool& outIpVerified)
430 // If we're not a leaf, call add on the correct branch.
431 if (!IsLeaf()) {
432 return m_subZones[contact->GetDistance().GetBitNumber(m_level)]->Add(contact, update, outIpVerified);
433 } else {
434 // Do we already have a contact with this KadID?
435 CContact *contactUpdate = m_bin->GetContact(contact->GetClientID());
436 if (contactUpdate) {
437 if (update) {
438 if (contactUpdate->GetUDPKey().GetKeyValue(theApp->GetPublicIP(false)) != 0 && contactUpdate->GetUDPKey().GetKeyValue(theApp->GetPublicIP(false)) != contact->GetUDPKey().GetKeyValue(theApp->GetPublicIP(false))) {
439 // if our existing contact has a UDPSender-Key (which should be the case for all > = 0.49a clients)
440 // except if our IP has changed recently, we demand that the key is the same as the key we received
441 // from the packet which wants to update this contact in order to make sure this is not a try to
442 // hijack this entry
443 AddDebugLogLineM(false, logKadRouting, wxT("Sender (") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress())) + wxT(") tried to update contact entry but failed to provide the proper sender key (Sent Empty: ") + (contact->GetUDPKey().GetKeyValue(theApp->GetPublicIP(false)) == 0 ? wxT("Yes") : wxT("No")) + wxT(") for the entry (") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contactUpdate->GetIPAddress())) + wxT(") - denying update"));
444 update = false;
445 } else if (contactUpdate->GetVersion() >= 1 && contactUpdate->GetVersion() < 6 && contactUpdate->GetReceivedHelloPacket()) {
446 // legacy kad2 contacts are allowed only to update their RefreshTimer to avoid having them hijacked/corrupted by an attacker
447 // (kad1 contacts do not have this restriction as they might turn out as kad2 later on)
448 // only exception is if we didn't received a HELLO packet from this client yet
449 if (contactUpdate->GetIPAddress() == contact->GetIPAddress() && contactUpdate->GetTCPPort() == contact->GetTCPPort() && contactUpdate->GetVersion() == contact->GetVersion() && contactUpdate->GetUDPPort() == contact->GetUDPPort()) {
450 wxASSERT(!contact->IsIPVerified()); // legacy kad2 nodes should be unable to verify their IP on a HELLO
451 outIpVerified = contactUpdate->IsIPVerified();
452 m_bin->SetAlive(contactUpdate);
453 AddDebugLogLineM(false, logKadRouting, wxString::Format(wxT("Updated kad contact refreshtimer only for legacy kad2 contact (") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contactUpdate->GetIPAddress())) + wxT(", %u)"), contactUpdate->GetVersion()));
454 } else {
455 AddDebugLogLineM(false, logKadRouting, wxString::Format(wxT("Rejected value update for legacy kad2 contact (") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contactUpdate->GetIPAddress())) + wxT(" -> ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress())) + wxT(", %u -> %u)"), contactUpdate->GetVersion(), contact->GetVersion()));
456 update = false;
458 } else {
459 #ifdef __DEBUG__
460 // just for outlining, get removed anyway
461 //debug logging stuff - remove later
462 if (contact->GetUDPKey().GetKeyValue(theApp->GetPublicIP(false)) == 0) {
463 if (contact->GetVersion() >= 6 && contact->GetType() < 2) {
464 AddDebugLogLineM(false, logKadRouting, wxT("Updating > 0.49a + type < 2 contact without valid key stored ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress())));
466 } else {
467 AddDebugLogLineM(false, logKadRouting, wxT("Updating contact, passed key check ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress())));
470 if (contactUpdate->GetVersion() >= 1 && contactUpdate->GetVersion() < 6) {
471 wxASSERT(!contactUpdate->GetReceivedHelloPacket());
472 AddDebugLogLineM(false, logKadRouting, wxString::Format(wxT("Accepted update for legacy kad2 contact, because of first HELLO (") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contactUpdate->GetIPAddress())) + wxT(" -> ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact->GetIPAddress())) + wxT(", %u -> %u)"), contactUpdate->GetVersion(), contact->GetVersion()));
474 #endif
475 // All other nodes (Kad1, Kad2 > 0.49a with UDPKey checked or not set, first hello updates) are allowed to do full updates
476 // do not let Kad1 responses overwrite Kad2 ones
477 if (m_bin->ChangeContactIPAddress(contactUpdate, contact->GetIPAddress()) && contact->GetVersion() >= contactUpdate->GetVersion()) {
478 contactUpdate->SetUDPPort(contact->GetUDPPort());
479 contactUpdate->SetTCPPort(contact->GetTCPPort());
480 contactUpdate->SetVersion(contact->GetVersion());
481 contactUpdate->SetUDPKey(contact->GetUDPKey());
482 // don't unset the verified flag (will clear itself on ipchanges)
483 if (!contactUpdate->IsIPVerified()) {
484 contactUpdate->SetIPVerified(contact->IsIPVerified());
486 outIpVerified = contactUpdate->IsIPVerified();
487 m_bin->SetAlive(contactUpdate);
488 if (contact->GetReceivedHelloPacket()) {
489 contactUpdate->SetReceivedHelloPacket();
491 } else {
492 update = false;
496 return false;
497 } else if (m_bin->GetRemaining()) {
498 update = false;
499 // This bin is not full, so add the new contact
500 return m_bin->AddContact(contact);
501 } else if (CanSplit()) {
502 // This bin was full and split, call add on the correct branch.
503 Split();
504 return m_subZones[contact->GetDistance().GetBitNumber(m_level)]->Add(contact, update, outIpVerified);
505 } else {
506 update = false;
507 return false;
512 CContact *CRoutingZone::GetContact(const CUInt128& id) const throw()
514 if (IsLeaf()) {
515 return m_bin->GetContact(id);
516 } else {
517 CUInt128 distance = CKademlia::GetPrefs()->GetKadID();
518 distance ^= id;
519 return m_subZones[distance.GetBitNumber(m_level)]->GetContact(id);
523 CContact *CRoutingZone::GetContact(uint32_t ip, uint16_t port, bool tcpPort) const throw()
525 if (IsLeaf()) {
526 return m_bin->GetContact(ip, port, tcpPort);
527 } else {
528 CContact *contact = m_subZones[0]->GetContact(ip, port, tcpPort);
529 return (contact != NULL) ? contact : m_subZones[1]->GetContact(ip, port, tcpPort);
533 CContact *CRoutingZone::GetRandomContact(uint32_t maxType, uint32_t minKadVersion) const throw()
535 if (IsLeaf()) {
536 return m_bin->GetRandomContact(maxType, minKadVersion);
537 } else {
538 unsigned zone = GetRandomUint16() & 1 /* GetRandomUint16() % 2 */;
539 CContact *contact = m_subZones[zone]->GetRandomContact(maxType, minKadVersion);
540 return (contact != NULL) ? contact : m_subZones[1 - zone]->GetRandomContact(maxType, minKadVersion);
544 void CRoutingZone::GetClosestTo(uint32_t maxType, const CUInt128& target, const CUInt128& distance, uint32_t maxRequired, ContactMap *result, bool emptyFirst, bool inUse) const
546 // If leaf zone, do it here
547 if (IsLeaf()) {
548 m_bin->GetClosestTo(maxType, target, maxRequired, result, emptyFirst, inUse);
549 return;
552 // otherwise, recurse in the closer-to-the-target subzone first
553 int closer = distance.GetBitNumber(m_level);
554 m_subZones[closer]->GetClosestTo(maxType, target, distance, maxRequired, result, emptyFirst, inUse);
556 // if still not enough tokens found, recurse in the other subzone too
557 if (result->size() < maxRequired) {
558 m_subZones[1-closer]->GetClosestTo(maxType, target, distance, maxRequired, result, false, inUse);
562 void CRoutingZone::GetAllEntries(ContactList *result, bool emptyFirst) const
564 if (IsLeaf()) {
565 m_bin->GetEntries(result, emptyFirst);
566 } else {
567 m_subZones[0]->GetAllEntries(result, emptyFirst);
568 m_subZones[1]->GetAllEntries(result, false);
572 void CRoutingZone::TopDepth(int depth, ContactList *result, bool emptyFirst) const
574 if (IsLeaf()) {
575 m_bin->GetEntries(result, emptyFirst);
576 } else if (depth <= 0) {
577 RandomBin(result, emptyFirst);
578 } else {
579 m_subZones[0]->TopDepth(depth-1, result, emptyFirst);
580 m_subZones[1]->TopDepth(depth-1, result, false);
584 void CRoutingZone::RandomBin(ContactList *result, bool emptyFirst) const
586 if (IsLeaf()) {
587 m_bin->GetEntries(result, emptyFirst);
588 } else {
589 m_subZones[rand()&1]->RandomBin(result, emptyFirst);
593 uint32_t CRoutingZone::GetMaxDepth() const throw()
595 if (IsLeaf()) {
596 return 0;
598 return 1 + std::max(m_subZones[0]->GetMaxDepth(), m_subZones[1]->GetMaxDepth());
601 void CRoutingZone::Split()
603 StopTimer();
605 m_subZones[0] = GenSubZone(0);
606 m_subZones[1] = GenSubZone(1);
608 ContactList entries;
609 m_bin->GetEntries(&entries);
610 m_bin->m_dontDeleteContacts = true;
611 delete m_bin;
612 m_bin = NULL;
614 for (ContactList::const_iterator it = entries.begin(); it != entries.end(); ++it) {
615 if (!m_subZones[(*it)->GetDistance().GetBitNumber(m_level)]->m_bin->AddContact(*it)) {
616 delete *it;
621 uint32_t CRoutingZone::Consolidate()
623 uint32_t mergeCount = 0;
625 if (IsLeaf()) {
626 return mergeCount;
629 wxASSERT(m_bin == NULL);
631 if (!m_subZones[0]->IsLeaf()) {
632 mergeCount += m_subZones[0]->Consolidate();
634 if (!m_subZones[1]->IsLeaf()) {
635 mergeCount += m_subZones[1]->Consolidate();
638 if (m_subZones[0]->IsLeaf() && m_subZones[1]->IsLeaf() && GetNumContacts() < K / 2) {
639 m_bin = new CRoutingBin();
641 m_subZones[0]->StopTimer();
642 m_subZones[1]->StopTimer();
644 ContactList list0;
645 ContactList list1;
646 m_subZones[0]->m_bin->GetEntries(&list0);
647 m_subZones[1]->m_bin->GetEntries(&list1);
649 m_subZones[0]->m_bin->m_dontDeleteContacts = true;
650 m_subZones[1]->m_bin->m_dontDeleteContacts = true;
652 delete m_subZones[0];
653 delete m_subZones[1];
655 m_subZones[0] = NULL;
656 m_subZones[1] = NULL;
658 for (ContactList::const_iterator it = list0.begin(); it != list0.end(); ++it) {
659 m_bin->AddContact(*it);
661 for (ContactList::const_iterator it = list1.begin(); it != list1.end(); ++it) {
662 m_bin->AddContact(*it);
665 StartTimer();
667 mergeCount++;
669 return mergeCount;
672 CRoutingZone *CRoutingZone::GenSubZone(unsigned side)
674 wxASSERT(side <= 1);
676 CUInt128 newIndex(m_zoneIndex);
677 newIndex <<= 1;
678 newIndex += side;
679 return new CRoutingZone(this, m_level + 1, newIndex);
682 void CRoutingZone::StartTimer()
684 // Start filling the tree, closest bins first.
685 m_nextBigTimer = time(NULL) + SEC(10);
686 CKademlia::AddEvent(this);
689 void CRoutingZone::StopTimer()
691 CKademlia::RemoveEvent(this);
694 bool CRoutingZone::OnBigTimer() const
696 if (IsLeaf() && (m_zoneIndex < KK || m_level < KBASE || m_bin->GetRemaining() >= (K * 0.8))) {
697 RandomLookup();
698 return true;
701 return false;
704 //This is used when we find a leaf and want to know what this sample looks like.
705 //We fall back two levels and take a sample to try to minimize any areas of the
706 //tree that will give very bad results.
707 uint32_t CRoutingZone::EstimateCount() const throw()
709 if (!IsLeaf()) {
710 return 0;
713 if (m_level < KBASE) {
714 return (uint32_t)(pow(2.0, (int)m_level) * K);
717 CRoutingZone* curZone = m_superZone->m_superZone->m_superZone;
719 // Find out how full this part of the tree is.
720 float modify = ((float)curZone->GetNumContacts()) / (float)(K * 2);
722 // First calculate users assuming the tree is full.
723 // Modify count by bin size.
724 // Modify count by how full the tree is.
726 // LowIDModififier
727 // Modify count by assuming 20% of the users are firewalled and can't be a contact for < 0.49b nodes
728 // Modify count by actual statistics of Firewalled ratio for >= 0.49b if we are not firewalled ourself
729 // Modify count by 40% for >= 0.49b if we are firewalled ourself (the actual Firewalled count at this date on kad is 35-55%)
730 const float firewalledModifyOld = 1.20;
731 float firewalledModifyNew = 0;
732 if (CUDPFirewallTester::IsFirewalledUDP(true)) {
733 firewalledModifyNew = 1.40; // we are firewalled and can't get the real statistics, assume 40% firewalled >=0.49b nodes
734 } else if (CKademlia::GetPrefs()->StatsGetFirewalledRatio(true) > 0) {
735 firewalledModifyNew = 1.0 + (CKademlia::GetPrefs()->StatsGetFirewalledRatio(true)); // apply the firewalled ratio to the modify
736 wxASSERT(firewalledModifyNew > 1.0 && firewalledModifyNew < 1.90);
738 float newRatio = CKademlia::GetPrefs()->StatsGetKadV8Ratio();
739 float firewalledModifyTotal = 0;
740 if (newRatio > 0 && firewalledModifyNew > 0) { // weight the old and the new modifier based on how many new contacts we have
741 firewalledModifyTotal = (newRatio * firewalledModifyNew) + ((1 - newRatio) * firewalledModifyOld);
742 } else {
743 firewalledModifyTotal = firewalledModifyOld;
745 wxASSERT(firewalledModifyTotal > 1.0 && firewalledModifyTotal < 1.90);
747 return (uint32_t)(pow(2.0, (int)m_level - 2) * (float)K * modify * firewalledModifyTotal);
750 void CRoutingZone::OnSmallTimer()
752 if (!IsLeaf()) {
753 return;
756 CContact *c = NULL;
757 time_t now = time(NULL);
758 ContactList entries;
760 // Remove dead entries
761 m_bin->GetEntries(&entries);
762 for (ContactList::iterator it = entries.begin(); it != entries.end(); ++it) {
763 c = *it;
764 if (c->GetType() == 4) {
765 if ((c->GetExpireTime() > 0) && (c->GetExpireTime() <= now)) {
766 if (!c->InUse()) {
767 m_bin->RemoveContact(c);
768 delete c;
770 continue;
773 if(c->GetExpireTime() == 0) {
774 c->SetExpireTime(now);
778 c = m_bin->GetOldest();
779 if (c != NULL) {
780 if (c->GetExpireTime() >= now || c->GetType() == 4) {
781 m_bin->PushToBottom(c);
782 c = NULL;
786 if (c != NULL) {
787 c->CheckingType();
788 if (c->GetVersion() >= 6) {
789 DebugSend(Kad2HelloReq, c->GetIPAddress(), c->GetUDPPort());
790 CUInt128 clientID = c->GetClientID();
791 CKademlia::GetUDPListener()->SendMyDetails(KADEMLIA2_HELLO_REQ, c->GetIPAddress(), c->GetUDPPort(), c->GetVersion(), c->GetUDPKey(), &clientID, false);
792 if (c->GetVersion() >= 8) {
793 // FIXME:
794 // This is a bit of a work around for statistic values. Normally we only count values from incoming HELLO_REQs for
795 // the firewalled statistics in order to get numbers from nodes which have us on their routing table,
796 // however if we send a HELLO due to the timer, the remote node won't send a HELLO_REQ itself anymore (but
797 // a HELLO_RES which we don't count), so count those statistics here. This isn't really accurate, but it should
798 // do fair enough. Maybe improve it later for example by putting a flag into the contact and make the answer count
799 CKademlia::GetPrefs()->StatsIncUDPFirewalledNodes(false);
800 CKademlia::GetPrefs()->StatsIncTCPFirewalledNodes(false);
802 } else if (c->GetVersion() >= 2) {
803 DebugSend(Kad2HelloReq, c->GetIPAddress(), c->GetUDPPort());
804 CKademlia::GetUDPListener()->SendMyDetails(KADEMLIA2_HELLO_REQ, c->GetIPAddress(), c->GetUDPPort(), c->GetVersion(), 0, NULL, false);
805 wxASSERT(c->GetUDPKey() == CKadUDPKey(0));
806 } else {
807 DebugSend(KadHelloReq, c->GetIPAddress(), c->GetUDPPort());
808 CKademlia::GetUDPListener()->SendMyDetails(KADEMLIA_HELLO_REQ, c->GetIPAddress(), c->GetUDPPort(), 0, 0, NULL, false);
809 if (c->CheckIfKad2()) {
810 DebugSend(Kad2HelloReq, c->GetIPAddress(), c->GetUDPPort());
811 CKademlia::GetUDPListener()->SendMyDetails(KADEMLIA2_HELLO_REQ, c->GetIPAddress(), c->GetUDPPort(), 1, 0, NULL, false);
817 void CRoutingZone::RandomLookup() const
819 // Look-up a random client in this zone
820 CUInt128 prefix(m_zoneIndex);
821 prefix <<= 128 - m_level;
822 CUInt128 random(prefix, m_level);
823 random ^= me;
824 CSearchManager::FindNode(random, false);
827 uint32_t CRoutingZone::GetNumContacts() const throw()
829 if (IsLeaf()) {
830 return m_bin->GetSize();
831 } else {
832 return m_subZones[0]->GetNumContacts() + m_subZones[1]->GetNumContacts();
836 void CRoutingZone::GetNumContacts(uint32_t& nInOutContacts, uint32_t& nInOutFilteredContacts, uint8_t minVersion) const
838 if (IsLeaf()) {
839 m_bin->GetNumContacts(nInOutContacts, nInOutFilteredContacts, minVersion);
840 } else {
841 m_subZones[0]->GetNumContacts(nInOutContacts, nInOutFilteredContacts, minVersion);
842 m_subZones[1]->GetNumContacts(nInOutContacts, nInOutFilteredContacts, minVersion);
846 uint32_t CRoutingZone::GetBootstrapContacts(ContactList *results, uint32_t maxRequired) const
848 wxASSERT(m_superZone == NULL);
850 results->clear();
852 uint32_t count = 0;
853 ContactList top;
854 TopDepth(LOG_BASE_EXPONENT, &top);
855 if (top.size() > 0) {
856 for (ContactList::const_iterator it = top.begin(); it != top.end(); ++it) {
857 results->push_back(*it);
858 count++;
859 if (count == maxRequired) {
860 break;
865 return count;
868 bool CRoutingZone::VerifyContact(const CUInt128& id, uint32_t ip)
870 CContact* contact = GetContact(id);
871 if (contact == NULL) {
872 return false;
873 } else if (ip != contact->GetIPAddress()) {
874 return false;
875 } else {
876 if (contact->IsIPVerified()) {
877 AddDebugLogLineM(false, logKadRouting, wxT("Sender already verified (sender: ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(ip)) + wxT(")"));
878 } else {
879 contact->SetIPVerified(true);
881 return true;
885 void CRoutingZone::SetAllContactsVerified()
887 if (IsLeaf()) {
888 m_bin->SetAllContactsVerified();
889 } else {
890 m_subZones[0]->SetAllContactsVerified();
891 m_subZones[1]->SetAllContactsVerified();