Upstream tarball 9828
[amule.git] / src / ClientList.cpp
blobb8f0f1c1f357b5b66764dcded240c39874b42972
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "ClientList.h" // Interface declarations.
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/Constants.h>
30 #include <protocol/kad/Client2Client/UDP.h>
31 #include <protocol/kad/Constants.h>
32 #include <protocol/kad2/Client2Client/TCP.h>
34 #include "amule.h" // Needed for theApp
35 #include "ClientTCPSocket.h" // Needed for CClientTCPSocket
36 #include "DownloadQueue.h" // Needed for CDownloadQueue
37 #include "UploadQueue.h" // Needed for CUploadQueue
38 #include "IPFilter.h" // Needed for CIPFIlter
39 #include "updownclient.h" // Needed for CUpDownClient
40 #include "Preferences.h" // Needed for thePrefs
41 #include "Statistics.h" // Needed for theStats
42 #include "Logger.h"
43 #include "GuiEvents.h" // Needed for Notify_*
44 #include "Packet.h"
46 #include <common/Format.h>
48 #include "kademlia/kademlia/Search.h"
49 #include "kademlia/kademlia/SearchManager.h"
50 #include "kademlia/kademlia/UDPFirewallTester.h"
51 #include "kademlia/net/KademliaUDPListener.h"
52 #include "kademlia/routing/Contact.h"
55 /**
56 * CDeletedClient Class
58 * This class / list is a bit overkill, but currently needed to avoid any
59 * exploit possibility. It will keep track of certain clients attributes
60 * for 2 hours, while the CUpDownClient object might be deleted already.
61 * Currently saves: IP, Port, UserHash.
63 class CDeletedClient
65 public:
66 CDeletedClient(CUpDownClient* pClient)
68 m_dwInserted = ::GetTickCount();
69 PortAndHash porthash = { pClient->GetUserPort(), pClient->GetCreditsHash()};
70 m_ItemsList.push_back(porthash);
73 struct PortAndHash
75 uint16 nPort;
76 void* pHash;
79 typedef std::list<PortAndHash> PaHList;
80 PaHList m_ItemsList;
81 uint32 m_dwInserted;
86 CClientList::CClientList()
87 : m_deadSources( true )
89 m_dwLastBannCleanUp = 0;
90 m_dwLastTrackedCleanUp = 0;
91 m_dwLastClientCleanUp = 0;
92 m_pBuddy = NULL;
93 m_nBuddyStatus = Disconnected;
94 #ifdef __WXDEBUG__
95 m_delete_queue_closed = false;
96 #endif
100 CClientList::~CClientList()
102 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.begin();
103 for ( ; it != m_trackedClientsList.end(); ++it ){
104 delete it->second;
107 m_trackedClientsList.clear();
109 wxASSERT(m_clientList.empty());
110 wxASSERT(m_delete_queue.empty());
114 void CClientList::AddClient( CUpDownClient* toadd )
116 // Ensure that only new clients can be added to the list
117 if ( toadd->GetClientState() == CS_NEW ) {
118 // Update the client-state
119 toadd->m_clientState = CS_LISTED;
121 Notify_ClientCtrlAddClient( toadd );
123 // We always add the ID/ptr pair, regardles of the actual ID value
124 m_clientList.insert( IDMapPair( toadd->GetUserIDHybrid(), toadd ) );
126 // We only add the IP if it is valid
127 if ( toadd->GetIP() ) {
128 m_ipList.insert( IDMapPair( toadd->GetIP(), toadd ) );
131 // We only add the hash if it is valid
132 if ( toadd->HasValidHash() ) {
133 m_hashList.insert( HashMapPair( toadd->GetUserHash(), toadd ) );
136 toadd->UpdateStats();
141 void CClientList::AddToDeleteQueue(CUpDownClient* client)
143 RemoveFromKadList( client );
144 RemoveDirectCallback( client );
146 // We have to remove the client from the list immediatly, to avoit it getting
147 // found by functions such as AttachToAlreadyKnown and GetClientsFromIP,
148 // however, if the client isn't on the clientlist, then it is safe to delete
149 // it right now. Otherwise, push it onto the queue.
150 if ( RemoveIDFromList( client ) ) {
151 // Also remove the ip and hash entries
152 RemoveIPFromList( client );
153 RemoveHashFromList( client );
155 wxASSERT(!m_delete_queue_closed);
156 m_delete_queue.push_back( client );
157 } else {
158 delete client;
163 void CClientList::UpdateClientID( CUpDownClient* client, uint32 newID )
165 // Sanity check
166 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserIDHybrid() == newID ) )
167 return;
169 // First remove the ID entry
170 RemoveIDFromList( client );
172 // Add the new entry
173 m_clientList.insert( IDMapPair( newID, client ) );
177 void CClientList::UpdateClientIP( CUpDownClient* client, uint32 newIP )
179 // Sanity check
180 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetIP() == newIP ) )
181 return;
183 // Remove the old IP entry
184 RemoveIPFromList( client );
186 if ( newIP ) {
187 m_ipList.insert( IDMapPair( newIP, client ) );
192 void CClientList::UpdateClientHash( CUpDownClient* client, const CMD4Hash& newHash )
194 // Sanity check
195 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserHash() == newHash ) )
196 return;
199 // Remove the old entry
200 RemoveHashFromList( client );
202 // And add the new one if valid
203 if ( !newHash.IsEmpty() ) {
204 m_hashList.insert( HashMapPair( newHash, client ) );
209 bool CClientList::RemoveIDFromList( CUpDownClient* client )
211 bool result = false;
213 // First remove the ID entry
214 std::pair<IDMap::iterator, IDMap::iterator> range = m_clientList.equal_range( client->GetUserIDHybrid() );
216 for ( ; range.first != range.second; ++range.first ) {
217 if ( client == range.first->second ) {
218 /* erase() will invalidate the iterator, but we're not using it anymore
219 anyway (notice the break;) */
220 m_clientList.erase( range.first );
221 result = true;
223 break;
227 return result;
231 void CClientList::RemoveIPFromList( CUpDownClient* client )
233 // Check if we need to look for the IP entry
234 if ( !client->GetIP() ) {
235 return;
238 // Remove the IP entry
239 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( client->GetIP() );
241 for ( ; range.first != range.second; ++range.first ) {
242 if ( client == range.first->second ) {
243 /* erase() will invalidate the iterator, but we're not using it anymore
244 anyway (notice the break;) */
245 m_ipList.erase( range.first );
246 break;
251 void CClientList::RemoveHashFromList( CUpDownClient* client )
253 // Nothing to remove
254 if ( !client->HasValidHash() ) {
255 return;
258 // Find all items with the specified hash
259 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( client->GetUserHash() );
261 for ( ; range.first != range.second; ++range.first ) {
262 if ( client == range.first->second ) {
263 /* erase() will invalidate the iterator, but we're not using it anymore
264 anyway (notice the break;) */
265 m_hashList.erase( range.first );
266 break;
272 CUpDownClient* CClientList::FindMatchingClient( CUpDownClient* client )
274 typedef std::pair<IDMap::const_iterator, IDMap::const_iterator> IDMapIteratorPair;
275 wxCHECK(client, NULL);
277 const uint32 userIP = client->GetIP();
278 const uint32 userID = client->GetUserIDHybrid();
279 const uint16 userPort = client->GetUserPort();
280 const uint16 userKadPort = client->GetKadPort();
283 // LowID clients need a different set of checks
284 if (client->HasLowID()) {
285 // User is firewalled ... Must do two checks.
286 if (userIP && (userPort || userKadPort)) {
287 IDMapIteratorPair range = m_ipList.equal_range(userIP);
289 for ( ; range.first != range.second; ++range.first ) {
290 CUpDownClient* other = range.first->second;
291 wxASSERT(userIP == other->GetIP());
293 if (userPort && (userPort == other->GetUserPort())) {
294 return other;
295 } else if (userKadPort && (userKadPort == other->GetKadPort())) {
296 return other;
301 const uint32 serverIP = client->GetServerIP();
302 const uint32 serverPort = client->GetServerPort();
303 if (userID && serverIP && serverPort) {
304 IDMapIteratorPair range = m_clientList.equal_range(userID);
306 for (; range.first != range.second; ++range.first) {
307 CUpDownClient* other = range.first->second;
308 wxASSERT(userID == other->GetUserIDHybrid());
310 // For lowid, we also have to check the server
311 if (serverIP == other->GetServerIP()) {
312 if (serverPort == other->GetServerPort()) {
313 return other;
318 } else if (userPort || userKadPort) {
319 // Check by IP first, then by ID
320 struct { const IDMap& map; uint32 value; } toCheck[] = {
321 { m_ipList, userIP }, { m_clientList, userID }
324 for (size_t i = 0; i < itemsof(toCheck); ++i) {
325 if (toCheck[i].value == 0) {
326 // We may not have both (or any) of these values.
327 continue;
330 IDMapIteratorPair range = toCheck[i].map.equal_range(toCheck[i].value);
332 if (userPort) {
333 IDMap::const_iterator it = range.first;
334 for (; it != range.second; ++it) {
335 if (userPort == it->second->GetUserPort()) {
336 return it->second;
341 if (userKadPort) {
342 IDMap::const_iterator it = range.first;
343 for (; it != range.second; ++it) {
344 if (userKadPort == it->second->GetKadPort()) {
345 return it->second;
353 // If anything else fails, then we look at hashes
354 if ( client->HasValidHash() ) {
355 // Find all items with the specified hash
356 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( client->GetUserHash() );
358 // Just return the first item if any
359 if ( range.first != range.second ) {
360 return range.first->second;
364 // Nothing found, must be a new client
365 return NULL;
369 uint32 CClientList::GetClientCount() const
371 return m_clientList.size();
375 void CClientList::DeleteAll()
377 m_ipList.clear();
378 m_hashList.clear();
380 while ( !m_clientList.empty() ) {
381 IDMap::iterator it = m_clientList.begin();
383 // Will call the removal of the item on this same class
384 it->second->Disconnected(wxT("Removed while deleting all from ClientList."));
385 it->second->Safe_Delete();
388 // Clean up the clients now queued for deletion
389 #ifdef __WXDEBUG__
390 m_delete_queue_closed = true;
391 #endif
392 ProcessDeleteQueue();
396 bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientTCPSocket* sender)
398 CUpDownClient* tocheck = (*client);
400 CUpDownClient* found_client = FindMatchingClient( tocheck );
402 if ( tocheck == found_client ) {
403 // We found the same client instance (client may have sent more than one OP_HELLO). do not delete that client!
404 return true;
407 if (found_client != NULL){
408 if (sender) {
409 if (found_client->GetSocket()) {
410 if (found_client->IsConnected()
411 && (found_client->GetIP() != tocheck->GetIP() || found_client->GetUserPort() != tocheck->GetUserPort() ) )
413 // if found_client is connected and has the IS_IDENTIFIED, it's safe to say that the other one is a bad guy
414 if (found_client->IsIdentified()){
415 AddDebugLogLineM( false, logClient, wxT("Client: ") + tocheck->GetUserName() + wxT("(") + tocheck->GetFullIP() + wxT("), Banreason: Userhash invalid"));
416 tocheck->Ban();
417 return false;
420 AddDebugLogLineM( false, logClient, wxT("WARNING! Found matching client, to a currently connected client: ")
421 + tocheck->GetUserName() + wxT("(") + tocheck->GetFullIP()
422 + wxT(") and ") + found_client->GetUserName() + wxT("(") + found_client->GetFullIP() + wxT(")"));
423 return false;
425 found_client->GetSocket()->Safe_Delete();
427 found_client->SetSocket( sender );
428 tocheck->SetSocket( NULL );
430 *client = 0;
431 tocheck->Safe_Delete();
432 *client = found_client;
433 return true;
436 return false;
440 CUpDownClient* CClientList::FindClientByIP( uint32 clientip, uint16 port )
442 // Find all items with the specified ip
443 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
445 for ( ; range.first != range.second; ++range.first ) {
446 CUpDownClient* cur_client = range.first->second;
447 // Check if it's actually the client we want
448 if ( cur_client->GetUserPort() == port ) {
449 return cur_client;
453 return NULL;
457 CUpDownClient* CClientList::FindClientByIP( uint32 clientip )
459 // Find all items with the specified ip
460 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
462 return (range.first != range.second) ? range.first->second : NULL;
466 bool CClientList::IsIPAlreadyKnown(uint32_t ip)
468 // Find all items with the specified ip
469 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range(ip);
470 return range.first != range.second;
474 bool CClientList::ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash)
476 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
478 if ( it != m_trackedClientsList.end() ) {
479 CDeletedClient* pResult = it->second;
481 CDeletedClient::PaHList::iterator it2 = pResult->m_ItemsList.begin();
482 for ( ; it2 != pResult->m_ItemsList.end(); ++it2 ) {
483 if ( it2->nPort == nPort ) {
484 if ( it2->pHash != pNewHash) {
485 return false;
486 } else {
487 break;
492 return true;
496 void CClientList::AddTrackClient(CUpDownClient* toadd)
498 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( toadd->GetIP() );
500 if ( it != m_trackedClientsList.end() ) {
501 CDeletedClient* pResult = it->second;
503 pResult->m_dwInserted = ::GetTickCount();
505 CDeletedClient::PaHList::iterator it2 = pResult->m_ItemsList.begin();
506 for ( ; it2 != pResult->m_ItemsList.end(); ++it2 ) {
507 if ( it2->nPort == toadd->GetUserPort() ) {
508 // already tracked, update
509 it2->pHash = toadd->GetCreditsHash();
510 return;
514 // New client for that IP, add an entry
515 CDeletedClient::PortAndHash porthash = { toadd->GetUserPort(), toadd->GetCreditsHash()};
516 pResult->m_ItemsList.push_back(porthash);
517 } else {
518 m_trackedClientsList[ toadd->GetIP() ] = new CDeletedClient(toadd);
523 uint16 CClientList::GetClientsFromIP(uint32 dwIP)
525 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
527 if ( it != m_trackedClientsList.end() ) {
528 return it->second->m_ItemsList.size();
529 } else {
530 return 0;
535 void CClientList::ProcessDeleteQueue()
537 // Delete pending clients
538 while ( !m_delete_queue.empty() ) {
539 CUpDownClient* toremove = m_delete_queue.front();
540 m_delete_queue.pop_front();
542 // Doing what RemoveClient used to do. Just to be sure...
543 theApp->uploadqueue->RemoveFromUploadQueue( toremove );
544 theApp->uploadqueue->RemoveFromWaitingQueue( toremove );
545 theApp->downloadqueue->RemoveSource( toremove );
547 Notify_ClientCtrlRemoveClient( toremove );
549 delete toremove;
554 void CClientList::Process()
556 const uint32 cur_tick = ::GetTickCount();
558 ProcessDeleteQueue();
560 if (m_dwLastBannCleanUp + BAN_CLEANUP_TIME < cur_tick) {
561 m_dwLastBannCleanUp = cur_tick;
563 ClientMap::iterator it = m_bannedList.begin();
564 while ( it != m_bannedList.end() ) {
565 if ( it->second + CLIENTBANTIME < cur_tick ) {
566 ClientMap::iterator tmp = it++;
568 m_bannedList.erase( tmp );
569 theStats::RemoveBannedClient();
570 } else {
571 ++it;
577 if ( m_dwLastTrackedCleanUp + TRACKED_CLEANUP_TIME < cur_tick ) {
578 m_dwLastTrackedCleanUp = cur_tick;
580 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.begin();
581 while ( it != m_trackedClientsList.end() ) {
582 std::map<uint32, CDeletedClient*>::iterator cur_src = it++;
584 if ( cur_src->second->m_dwInserted + KEEPTRACK_TIME < cur_tick ) {
585 delete cur_src->second;
586 m_trackedClientsList.erase( cur_src );
591 //We need to try to connect to the clients in m_KadList
592 //If connected, remove them from the list and send a message back to Kad so we can send a ACK.
593 //If we don't connect, we need to remove the client..
594 //The sockets timeout should delete this object.
596 // buddy is just a flag that is used to make sure we are still connected or connecting to a buddy.
597 buddyState buddy = Disconnected;
599 std::set<CUpDownClient*>::iterator current_it = m_KadSources.begin();
600 while (current_it != m_KadSources.end()) {
601 CUpDownClient* cur_client = *current_it;
602 ++current_it; // Won't be used anymore till while loop
603 if( !Kademlia::CKademlia::IsRunning() ) {
604 //Clear out this list if we stop running Kad.
605 //Setting the Kad state to KS_NONE causes it to be removed in the switch below.
606 cur_client->SetKadState(KS_NONE);
608 switch (cur_client->GetKadState()) {
609 case KS_QUEUED_FWCHECK:
610 case KS_QUEUED_FWCHECK_UDP:
611 //Another client asked us to try to connect to them to check their firewalled status.
612 cur_client->TryToConnect(true);
613 break;
615 case KS_CONNECTING_FWCHECK:
616 //Ignore this state as we are just waiting for results.
617 break;
619 case KS_FWCHECK_UDP:
620 case KS_CONNECTING_FWCHECK_UDP:
621 // We want a UDP firewallcheck from this client and are just waiting to get connected to send the request
622 break;
624 case KS_CONNECTED_FWCHECK:
625 //We successfully connected to the client.
626 //We now send a ack to let them know.
627 if (cur_client->GetKadVersion() >= 7) {
628 // The result is now sent per TCP instead of UDP, because this will fail if our intern port is unreachable.
629 // But we want the TCP testresult regardless if UDP is firewalled, the new UDP state and test takes care of the rest
630 wxASSERT(cur_client->IsConnected());
631 //AddDebugLogLineM(false, logClient, wxT("Sent OP_KAD_FWTCPCHECK_ACK"));
632 CPacket *packet = new CPacket(OP_KAD_FWTCPCHECK_ACK, 0, OP_EMULEPROT);
633 cur_client->SafeSendPacket(packet);
634 } else {
635 DebugSend(KadFirewalledAckRes, wxUINT32_SWAP_ALWAYS(cur_client->GetIP()), cur_client->GetKadPort());
636 Kademlia::CKademlia::GetUDPListener()->SendNullPacket(KADEMLIA_FIREWALLED_ACK_RES, wxUINT32_SWAP_ALWAYS(cur_client->GetIP()), cur_client->GetKadPort(), 0, NULL);
638 //We are done with this client. Set Kad status to KS_NONE and it will be removed in the next cycle.
639 cur_client->SetKadState(KS_NONE);
640 break;
642 case KS_INCOMING_BUDDY:
643 //A firewalled client wants us to be his buddy.
644 //If we already have a buddy, we set Kad state to KS_NONE and it's removed in the next cycle.
645 //If not, this client will change to KS_CONNECTED_BUDDY when it connects.
646 if( m_nBuddyStatus == Connected ) {
647 cur_client->SetKadState(KS_NONE);
649 break;
651 case KS_QUEUED_BUDDY:
652 //We are firewalled and want to request this client to be a buddy.
653 //But first we check to make sure we are not already trying another client.
654 //If we are not already trying. We try to connect to this client.
655 //If we are already connected to a buddy, we set this client to KS_NONE and it's removed next cycle.
656 //If we are trying to connect to a buddy, we just ignore as the one we are trying may fail and we can then try this one.
657 if( m_nBuddyStatus == Disconnected ) {
658 buddy = Connecting;
659 m_nBuddyStatus = Connecting;
660 cur_client->SetKadState(KS_CONNECTING_BUDDY);
661 cur_client->TryToConnect(true);
662 Notify_ServerUpdateED2KInfo();
663 } else {
664 if( m_nBuddyStatus == Connected ) {
665 cur_client->SetKadState(KS_NONE);
668 break;
670 case KS_CONNECTING_BUDDY:
671 //We are trying to connect to this client.
672 //Although it should NOT happen, we make sure we are not already connected to a buddy.
673 //If we are we set to KS_NONE and it's removed next cycle.
674 //But if we are not already connected, make sure we set the flag to connecting so we know
675 //things are working correctly.
676 if( m_nBuddyStatus == Connected ) {
677 cur_client->SetKadState(KS_NONE);
678 } else {
679 wxASSERT( m_nBuddyStatus == Connecting );
680 buddy = Connecting;
682 break;
684 case KS_CONNECTED_BUDDY:
685 //A potential connected buddy client wanting to me in the Kad network
686 //We set our flag to connected to make sure things are still working correctly.
687 buddy = Connected;
689 //If m_nBuddyStatus is not connected already, we set this client as our buddy!
690 if( m_nBuddyStatus != Connected ) {
691 m_pBuddy = cur_client;
692 m_nBuddyStatus = Connected;
693 Notify_ServerUpdateED2KInfo();
695 if( m_pBuddy == cur_client && theApp->IsFirewalled() && cur_client->SendBuddyPingPong() ) {
696 cur_client->SendBuddyPing();
698 break;
700 default:
701 RemoveFromKadList(cur_client);
705 //We either never had a buddy, or lost our buddy..
706 if( buddy == Disconnected ) {
707 if( m_nBuddyStatus != Disconnected || m_pBuddy ) {
708 if( Kademlia::CKademlia::IsRunning() && theApp->IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true) ) {
709 //We are a lowID client and we just lost our buddy.
710 //Go ahead and instantly try to find a new buddy.
711 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
713 m_pBuddy = NULL;
714 m_nBuddyStatus = Disconnected;
715 Notify_ServerUpdateED2KInfo();
719 if ( Kademlia::CKademlia::IsConnected() ) {
720 // we only need a buddy if direct callback is not available
721 if(Kademlia::CKademlia::IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true)) {
722 // TODO: Kad buddies won't work with RequireCrypt, so it is disabled for now, but should (and will)
723 // be fixed in later version
724 // Update: buddy connections themselves support obfuscation properly since eMule 0.49a and aMule SVN 2008-05-09
725 // (this makes it work fine if our buddy uses require crypt), however callback requests don't support it yet so we
726 // wouldn't be able to answer callback requests with RequireCrypt, protocolchange intended for eMule 0.49b
727 if(m_nBuddyStatus == Disconnected && Kademlia::CKademlia::GetPrefs()->GetFindBuddy() && !thePrefs::IsClientCryptLayerRequired()) {
728 AddDebugLogLineM(false, logKadMain, wxT("Starting BuddySearch"));
729 //We are a firewalled client with no buddy. We have also waited a set time
730 //to try to avoid a false firewalled status.. So lets look for a buddy..
731 if (!Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FINDBUDDY, true, Kademlia::CUInt128(true).XOR(Kademlia::CKademlia::GetPrefs()->GetKadID()))) {
732 //This search ID was already going. Most likely reason is that
733 //we found and lost our buddy very quickly and the last search hadn't
734 //had time to be removed yet. Go ahead and set this to happen again
735 //next time around.
736 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
739 } else {
740 if( m_pBuddy ) {
741 //Lets make sure that if we have a buddy, they are firewalled!
742 //If they are also not firewalled, then someone must have fixed their firewall or stopped saturating their line..
743 //We just set the state of this buddy to KS_NONE and things will be cleared up with the next cycle.
744 if( !m_pBuddy->HasLowID() ) {
745 m_pBuddy->SetKadState(KS_NONE);
749 } else {
750 if( m_pBuddy ) {
751 //We are not connected anymore. Just set this buddy to KS_NONE and things will be cleared out on next cycle.
752 m_pBuddy->SetKadState(KS_NONE);
756 CleanUpClientList();
757 ProcessDirectCallbackList();
761 void CClientList::AddBannedClient(uint32 dwIP)
763 m_bannedList[dwIP] = ::GetTickCount();
764 theStats::AddBannedClient();
768 bool CClientList::IsBannedClient(uint32 dwIP)
770 ClientMap::iterator it = m_bannedList.find( dwIP );
772 if ( it != m_bannedList.end() ) {
773 if ( it->second + CLIENTBANTIME > ::GetTickCount() ) {
774 return true;
775 } else {
776 RemoveBannedClient(dwIP);
779 return false;
783 void CClientList::RemoveBannedClient(uint32 dwIP)
785 m_bannedList.erase(dwIP);
786 theStats::RemoveBannedClient();
790 void CClientList::FilterQueues()
792 // Filter client list
793 for ( IDMap::iterator it = m_ipList.begin(); it != m_ipList.end(); ) {
794 IDMap::iterator tmp = it++; // Don't change this to a ++it!
796 if ( theApp->ipfilter->IsFiltered(tmp->second->GetConnectIP())) {
797 tmp->second->Disconnected(wxT("Filtered by IPFilter"));
798 tmp->second->Safe_Delete();
804 CClientList::SourceList CClientList::GetClientsByHash( const CMD4Hash& hash )
806 SourceList results;
808 // Find all items with the specified hash
809 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( hash );
811 for ( ; range.first != range.second; ++range.first) {
812 results.push_back( range.first->second );
815 return results;
819 CClientList::SourceList CClientList::GetClientsByIP( unsigned long ip )
821 SourceList results;
823 // Find all items with the specified hash
824 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( ip );
826 for ( ; range.first != range.second; range.first++ ) {
827 results.push_back( range.first->second );
830 return results;
834 const CClientList::IDMap& CClientList::GetClientList()
836 return m_clientList;
840 void CClientList::AddDeadSource(const CUpDownClient* client)
842 m_deadSources.AddDeadSource( client );
846 bool CClientList::IsDeadSource(const CUpDownClient* client)
848 return m_deadSources.IsDeadSource( client );
851 bool CClientList::SendChatMessage(uint64 client_id, const wxString& message)
853 CUpDownClient* client = FindClientByIP(IP_FROM_GUI_ID(client_id), PORT_FROM_GUI_ID(client_id));
854 AddDebugLogLineM( false, logClient, wxT("Trying to Send Message.") );
855 if (client) {
856 AddDebugLogLineM( false, logClient, wxT("Sending.") );
857 } else {
858 AddDebugLogLineM( true, logClient,
859 CFormat( wxT("No client (GUI_ID %lli [%s:%llu]) found in CClientList::SendChatMessage(). Creating") )
860 % client_id
861 % Uint32toStringIP(IP_FROM_GUI_ID(client_id))
862 % PORT_FROM_GUI_ID(client_id) );
863 client = new CUpDownClient(PORT_FROM_GUI_ID(client_id),IP_FROM_GUI_ID(client_id),0,0,NULL, true, true);
864 AddClient(client);
866 return client->SendChatMessage(message);
869 void CClientList::SetChatState(uint64 client_id, uint8 state) {
870 CUpDownClient* client = FindClientByIP(IP_FROM_GUI_ID(client_id), PORT_FROM_GUI_ID(client_id));
871 if (client) {
872 client->SetChatState(state);
876 /* Kad stuff */
878 bool CClientList::RequestTCP(Kademlia::CContact* contact, uint8_t connectOptions)
880 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
881 // don't connect ourself
882 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
883 return false;
886 CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
888 if (!pNewClient) {
889 //#warning Do we actually have to check friendstate here?
890 pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true);
891 } else if (pNewClient->GetKadState() != KS_NONE) {
892 return false; // already busy with this client in some way (probably buddy stuff), don't mess with it
895 //Add client to the lists to be processed.
896 pNewClient->SetKadPort(contact->GetUDPPort());
897 pNewClient->SetKadState(KS_QUEUED_FWCHECK);
898 if (contact->GetClientID() != 0) {
899 uint8_t ID[16];
900 contact->GetClientID().ToByteArray(ID);
901 pNewClient->SetUserHash(CMD4Hash(ID));
902 pNewClient->SetConnectOptions(connectOptions, true, false);
904 AddToKadList(pNewClient); // This was a direct adding, but I like to check duplicates
905 //This method checks if this is a dup already.
906 AddClient(pNewClient);
907 return true;
910 void CClientList::RequestBuddy(Kademlia::CContact* contact, uint8_t connectOptions)
912 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
913 // Don't connect to ourself
914 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
915 return;
918 CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
919 if (!pNewClient) {
920 pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true );
921 } else if (pNewClient->GetKadState() != KS_NONE) {
922 return; // already busy with this client in some way (probably fw stuff), don't mess with it
923 } else if (IsKadFirewallCheckIP(nContactIP)) { // doing a kad firewall check with this IP, abort
924 AddDebugLogLineM(false, logKadMain, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP));
925 return;
928 //Add client to the lists to be processed.
929 pNewClient->SetKadPort(contact->GetUDPPort());
930 pNewClient->SetKadState(KS_QUEUED_BUDDY);
931 uint8_t ID[16];
932 contact->GetClientID().ToByteArray(ID);
933 pNewClient->SetUserHash(CMD4Hash(ID));
934 pNewClient->SetConnectOptions(connectOptions, true, false);
935 AddToKadList(pNewClient);
936 //This method checks if this is a dup already.
937 AddClient(pNewClient);
940 bool CClientList::IncomingBuddy(Kademlia::CContact* contact, Kademlia::CUInt128* buddyID)
942 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
943 //If aMule already knows this client, abort this.. It could cause conflicts.
944 //Although the odds of this happening is very small, it could still happen.
945 if (FindClientByIP(nContactIP, contact->GetTCPPort())) {
946 return false;
947 } else if (IsKadFirewallCheckIP(nContactIP)) { // doing a kad firewall check with this IP, abort
948 AddDebugLogLineM(false, logKadMain, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP));
949 return false;
952 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
953 return false; // don't connect ourself
956 //Add client to the lists to be processed.
957 CUpDownClient* pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true );
958 pNewClient->SetKadPort(contact->GetUDPPort());
959 pNewClient->SetKadState(KS_INCOMING_BUDDY);
960 byte ID[16];
961 contact->GetClientID().ToByteArray(ID);
962 pNewClient->SetUserHash(CMD4Hash(ID));
963 buddyID->ToByteArray(ID);
964 pNewClient->SetBuddyID(ID);
965 AddToKadList(pNewClient);
966 AddClient(pNewClient);
967 return true;
970 void CClientList::RemoveFromKadList(CUpDownClient* torem)
972 wxCHECK_RET(torem, wxT("NULL pointer in RemoveFromKadList"));
974 if (m_KadSources.erase(torem)) {
975 if(torem == m_pBuddy) {
976 m_pBuddy = NULL;
977 m_nBuddyStatus = Disconnected;
978 Notify_ServerUpdateED2KInfo();
983 void CClientList::AddToKadList(CUpDownClient* toadd)
985 wxCHECK_RET(toadd, wxT("NULL pointer in AddToKadList"));
987 m_KadSources.insert(toadd); // This will take care of duplicates.
990 bool CClientList::DoRequestFirewallCheckUDP(const Kademlia::CContact& contact)
992 // first make sure we don't know this IP already from somewhere
993 if (IsIPAlreadyKnown(wxUINT32_SWAP_ALWAYS(contact.GetIPAddress()))) {
994 return false;
996 // fine, just create the client object, set the state and wait
997 // TODO: We don't know the client's userhash, this means we cannot build an obfuscated connection, which
998 // again mean that the whole check won't work on "Require Obfuscation" setting, which is not a huge problem,
999 // but certainly not nice. Only somewhat acceptable way to solve this is to use the KadID instead.
1000 CUpDownClient* pNewClient = new CUpDownClient(contact.GetTCPPort(), contact.GetIPAddress(), 0, 0, NULL, false, true);
1001 pNewClient->SetKadState(KS_QUEUED_FWCHECK_UDP);
1002 AddDebugLogLineM(false, logClient, wxT("Selected client for UDP Firewallcheck: ") + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(contact.GetIPAddress())));
1003 AddToKadList(pNewClient);
1004 AddClient(pNewClient);
1005 wxASSERT(!pNewClient->SupportsDirectUDPCallback());
1006 return true;
1009 void CClientList::CleanUpClientList()
1011 // We remove clients which are not needed any more by time
1012 // this check is also done on CUpDownClient::Disconnected, however it will not catch all
1013 // cases (if a client changes the state without beeing connected
1015 // Adding this check directly to every point where any state changes would be more effective,
1016 // is however not compatible with the current code, because there are points where a client has
1017 // no state for some code lines and the code is also not prepared that a client object gets
1018 // invalid while working with it (aka setting a new state)
1019 // so this way is just the easy and safe one to go (as long as amule is basically single threaded)
1020 const uint32 cur_tick = ::GetTickCount();
1021 if (m_dwLastClientCleanUp + CLIENTLIST_CLEANUP_TIME < cur_tick ){
1022 m_dwLastClientCleanUp = cur_tick;
1023 uint32 cDeleted = 0;
1024 IDMap::iterator current_it = m_clientList.begin();
1025 while (current_it != m_clientList.end()) {
1026 CUpDownClient* pCurClient = current_it->second;
1027 ++current_it; // Won't be used till while loop again
1028 // Don't delete sources coming from source seeds for 10 mins,
1029 // to give them a chance to connect and become a useful source.
1030 if (pCurClient->GetSourceFrom() == SF_SOURCE_SEEDS && cur_tick - (uint32)theStats::GetStartTime() < MIN2MS(10)) continue;
1031 if ((pCurClient->GetUploadState() == US_NONE || (pCurClient->GetUploadState() == US_BANNED && !pCurClient->IsBanned()))
1032 && pCurClient->GetDownloadState() == DS_NONE
1033 && pCurClient->GetChatState() == MS_NONE
1034 && pCurClient->GetKadState() == KS_NONE
1035 && pCurClient->GetSocket() == NULL)
1037 cDeleted++;
1038 pCurClient->Disconnected(wxT("Removed during ClientList cleanup."));
1039 pCurClient->Safe_Delete();
1040 } else {
1041 if (!(pCurClient->GetUploadState() == US_NONE || (pCurClient->GetUploadState() == US_BANNED && !pCurClient->IsBanned()))) {
1042 AddDebugLogLineM(false, logProxy,
1043 CFormat(wxT("Debug: Not deleted client %x with up state: %i "))
1044 % (long int)pCurClient % pCurClient->GetUploadState());
1046 if (!(pCurClient->GetDownloadState() == DS_NONE)) {
1047 AddDebugLogLineM(false, logProxy,
1048 CFormat(wxT("Debug: Not deleted client %x with down state: %i "))
1049 % (long int)pCurClient % pCurClient->GetDownloadState());
1051 if (!(pCurClient->GetChatState() == MS_NONE)) {
1052 AddDebugLogLineM(false, logProxy,
1053 CFormat(wxT("Debug: Not deleted client %x with chat state: %i "))
1054 % (long int)pCurClient % pCurClient->GetChatState());
1056 if (!(pCurClient->GetKadState() == KS_NONE)) {
1057 AddDebugLogLineM(false, logProxy,
1058 CFormat(wxT("Debug: Not deleted client %x with kad state: %i ip: %s"))
1059 % (long int)pCurClient % pCurClient->GetKadState() % pCurClient->GetFullIP());
1061 if (!(pCurClient->GetSocket() == NULL)) {
1062 AddDebugLogLineM(false, logProxy,
1063 CFormat(wxT("Debug: Not deleted client %x: has socket")) % (long int)pCurClient);
1065 AddDebugLogLineM(false, logProxy,
1066 CFormat(wxT("Debug: Not deleted client %x with kad version: %i"))
1067 % (long int)pCurClient % pCurClient->GetKadVersion());
1070 AddDebugLogLineM(false, logClient, wxString::Format(wxT("Cleaned ClientList, removed %i not used known clients"), cDeleted));
1074 void CClientList::AddKadFirewallRequest(uint32 ip)
1076 uint32 ticks = ::GetTickCount();
1077 IpAndTicks add = { ip, ticks };
1078 m_firewallCheckRequests.push_front(add);
1079 while (!m_firewallCheckRequests.empty()) {
1080 if (ticks - m_firewallCheckRequests.back().inserted > SEC2MS(180)) {
1081 m_firewallCheckRequests.pop_back();
1082 } else {
1083 break;
1088 bool CClientList::IsKadFirewallCheckIP(uint32 ip) const
1090 uint32 ticks = ::GetTickCount();
1091 for (IpAndTicksList::const_iterator it = m_firewallCheckRequests.begin(); it != m_firewallCheckRequests.end(); ++it) {
1092 if (it->ip == ip && ticks - it->inserted < SEC2MS(180)) {
1093 return true;
1096 return false;
1099 void CClientList::AddDirectCallbackClient(CUpDownClient* toAdd)
1101 wxASSERT(toAdd->GetDirectCallbackTimeout() != 0);
1102 for (DirectCallbackList::const_iterator it = m_currentDirectCallbacks.begin(); it != m_currentDirectCallbacks.end(); ++it) {
1103 if (*it == toAdd) {
1104 wxFAIL; // might happen very rarely on multiple connection tries, could be fixed in the client class, till then it's not much of a problem though
1105 return;
1108 m_currentDirectCallbacks.push_back(toAdd);
1111 void CClientList::ProcessDirectCallbackList()
1113 // we do check if any direct callbacks have timed out by now
1114 const uint32_t cur_tick = ::GetTickCount();
1115 for (DirectCallbackList::iterator it = m_currentDirectCallbacks.begin(); it != m_currentDirectCallbacks.end();) {
1116 DirectCallbackList::iterator it2 = it++;
1117 CUpDownClient* curClient = *it2;
1118 if (curClient->GetDirectCallbackTimeout() < cur_tick) {
1119 wxASSERT(curClient->GetDirectCallbackTimeout() != 0);
1120 // TODO LOGREMOVE
1121 //DebugLog(_T("DirectCallback timed out (%s)"), pCurClient->DbgGetClientInfo());
1122 m_currentDirectCallbacks.erase(it2);
1123 if (curClient->Disconnected(wxT("Direct Callback Timeout"))) {
1124 curClient->Safe_Delete();
1130 void CClientList::AddTrackCallbackRequests(uint32_t ip)
1132 uint32_t now = ::GetTickCount();
1133 IpAndTicks add = { ip, now };
1134 m_directCallbackRequests.push_front(add);
1135 while (!m_directCallbackRequests.empty()) {
1136 if (now - m_directCallbackRequests.back().inserted > MIN2MS(3)) {
1137 m_directCallbackRequests.pop_back();
1138 } else {
1139 break;
1144 bool CClientList::AllowCallbackRequest(uint32_t ip) const
1146 uint32_t now = ::GetTickCount();
1147 for (IpAndTicksList::const_iterator it = m_directCallbackRequests.begin(); it != m_directCallbackRequests.end(); ++it) {
1148 if (it->ip == ip && now - it->inserted < MIN2MS(3)) {
1149 return false;
1152 return true;
1154 // File_checked_for_headers