2 // This file is part of aMule Project
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
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
) {
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()) {
76 if ((contact
->GetIPAddress() & 0xFFFFFF00) == ((*it
)->GetIPAddress() & 0xFFFFFF00)) {
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())) {
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"));
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);
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
);
110 // Mark contact as being alive.
112 // Move to the end of the list
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
) {
122 if ((ip
== c
->GetIPAddress()) && (port
== c
->GetUDPPort())) {
123 // Set TCPPort and mark as alive.
124 c
->SetTCPPort(tcpPort
);
126 // Move to the end of the list
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
) {
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)) {
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
) {
162 nInOutFilteredContacts
++;
167 void CRoutingBin::GetEntries(ContactList
*result
, bool emptyFirst
) const
169 // Clear results if requested first.
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.
187 // No entries, no closest.
188 if (m_entries
.size() == 0) {
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.
205 // Remove any extra results by least wanted first.
206 while (result
->size() > maxRequired
) {
209 (--result
->end())->second
->DecUse();
211 // Remove from results
212 result
->erase(--result
->end());
216 void CRoutingBin::AdjustGlobalTracking(uint32_t ip
, bool increase
)
219 uint32_t sameIPCount
= 0;
220 GlobalTrackingMap::const_iterator itIP
= s_globalContactIPs
.find(ip
);
221 if (itIP
!= s_globalContactIPs
.end()) {
222 sameIPCount
= itIP
->second
;
225 if (sameIPCount
>= MAX_CONTACTS_IP
) {
226 AddDebugLogLineN(logKadRouting
, wxT("Global IP Tracking inconsistency on increase (") + KadIPToString(ip
) + wxT(")"));
230 } else /* if (!increase) */ {
231 if (sameIPCount
== 0) {
232 AddDebugLogLineN(logKadRouting
, wxT("Global IP Tracking inconsistency on decrease (") + KadIPToString(ip
) + wxT(")"));
237 if (sameIPCount
!= 0) {
238 s_globalContactIPs
[ip
] = sameIPCount
;
240 s_globalContactIPs
.erase(ip
);
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
;
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)"));
255 } else /* if (!increase) */ {
256 if (sameSubnetCount
== 0) {
257 AddDebugLogLineN(logKadRouting
, wxT("Global Subnet Tracking inconsistency on decrease (") + KadIPToString(ip
) + wxT("/24)"));
262 if (sameSubnetCount
!= 0) {
263 s_globalContactSubnets
[ip
& 0xFFFFFF00] = sameSubnetCount
;
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
) {
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)"));
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)"));
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)) {
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)"));
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);
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()) {
341 CContact
*lastFit
= NULL
;
342 uint32_t randomStartPos
= GetRandomUint16() % m_entries
.size();
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
) {
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)"));
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)"));