Workaround for crash on closing the last search tab
[amule.git] / src / ClientList.cpp
blobf26cce4341decce250eb9c8cf393de79799175af
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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.
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_nBuddyStatus = Disconnected;
96 CClientList::~CClientList()
98 DeleteContents(m_trackedClientsList);
100 wxASSERT(m_clientList.empty());
104 void CClientList::AddClient( CUpDownClient* toadd )
106 // Ensure that only new clients can be added to the list
107 if ( toadd->GetClientState() == CS_NEW ) {
108 // Update the client-state
109 toadd->m_clientState = CS_LISTED;
111 //Notify_ClientCtrlAddClient( toadd );
113 // We always add the ID/ptr pair, regardles of the actual ID value
114 m_clientList.insert( IDMapPair( toadd->GetUserIDHybrid(), CCLIENTREF(toadd, wxT("CClientList::AddClient m_clientList.insert"))) );
116 // We only add the IP if it is valid
117 if ( toadd->GetIP() ) {
118 m_ipList.insert( IDMapPair( toadd->GetIP(), CCLIENTREF(toadd, wxT("CClientList::AddClient m_ipList.insert")) ) );
121 // We only add the hash if it is valid
122 if ( toadd->HasValidHash() ) {
123 m_hashList.insert( HashMapPair( toadd->GetUserHash(), CCLIENTREF(toadd, wxT("CClientList::AddClient m_hashList.insert")) ) );
126 toadd->UpdateStats();
131 void CClientList::RemoveClient(CUpDownClient* client)
133 RemoveFromKadList( client );
134 RemoveDirectCallback( client );
136 if ( RemoveIDFromList( client ) ) {
137 // Also remove the ip and hash entries
138 RemoveIPFromList( client );
139 RemoveHashFromList( client );
144 void CClientList::UpdateClientID( CUpDownClient* client, uint32 newID )
146 // Sanity check
147 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserIDHybrid() == newID ) )
148 return;
150 // First remove the ID entry
151 RemoveIDFromList( client );
153 // Add the new entry
154 m_clientList.insert( IDMapPair( newID, CCLIENTREF(client, wxT("CClientList::UpdateClientID")) ) );
158 void CClientList::UpdateClientIP( CUpDownClient* client, uint32 newIP )
160 // Sanity check
161 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetIP() == newIP ) )
162 return;
164 // Remove the old IP entry
165 RemoveIPFromList( client );
167 if ( newIP ) {
168 m_ipList.insert( IDMapPair( newIP, CCLIENTREF(client, wxT("CClientList::UpdateClientIP")) ) );
173 void CClientList::UpdateClientHash( CUpDownClient* client, const CMD4Hash& newHash )
175 // Sanity check
176 if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserHash() == newHash ) )
177 return;
180 // Remove the old entry
181 RemoveHashFromList( client );
183 // And add the new one if valid
184 if ( !newHash.IsEmpty() ) {
185 m_hashList.insert( HashMapPair( newHash, CCLIENTREF(client, wxT("CClientList::UpdateClientHash")) ) );
190 bool CClientList::RemoveIDFromList( CUpDownClient* client )
192 bool result = false;
194 // First remove the ID entry
195 std::pair<IDMap::iterator, IDMap::iterator> range = m_clientList.equal_range( client->GetUserIDHybrid() );
197 for ( ; range.first != range.second; ++range.first ) {
198 if ( client == range.first->second.GetClient() ) {
199 /* erase() will invalidate the iterator, but we're not using it anymore
200 anyway (notice the break;) */
201 m_clientList.erase( range.first );
202 result = true;
204 break;
208 return result;
212 void CClientList::RemoveIPFromList( CUpDownClient* client )
214 // Check if we need to look for the IP entry
215 if ( !client->GetIP() ) {
216 return;
219 // Remove the IP entry
220 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( client->GetIP() );
222 for ( ; range.first != range.second; ++range.first ) {
223 if ( client == range.first->second.GetClient() ) {
224 /* erase() will invalidate the iterator, but we're not using it anymore
225 anyway (notice the break;) */
226 m_ipList.erase( range.first );
227 break;
232 void CClientList::RemoveHashFromList( CUpDownClient* client )
234 // Nothing to remove
235 if ( !client->HasValidHash() ) {
236 return;
239 // Find all items with the specified hash
240 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( client->GetUserHash() );
242 for ( ; range.first != range.second; ++range.first ) {
243 if ( client == range.first->second.GetClient() ) {
244 /* erase() will invalidate the iterator, but we're not using it anymore
245 anyway (notice the break;) */
246 m_hashList.erase( range.first );
247 break;
253 CUpDownClient* CClientList::FindMatchingClient( CUpDownClient* client )
255 typedef std::pair<IDMap::const_iterator, IDMap::const_iterator> IDMapIteratorPair;
256 wxCHECK(client, NULL);
258 const uint32 userIP = client->GetIP();
259 const uint32 userID = client->GetUserIDHybrid();
260 const uint16 userPort = client->GetUserPort();
261 const uint16 userKadPort = client->GetKadPort();
264 // LowID clients need a different set of checks
265 if (client->HasLowID()) {
266 // User is firewalled ... Must do two checks.
267 if (userIP && (userPort || userKadPort)) {
268 IDMapIteratorPair range = m_ipList.equal_range(userIP);
270 for ( ; range.first != range.second; ++range.first ) {
271 CUpDownClient* other = range.first->second.GetClient();
272 wxASSERT(userIP == other->GetIP());
274 if (userPort && (userPort == other->GetUserPort())) {
275 return other;
276 } else if (userKadPort && (userKadPort == other->GetKadPort())) {
277 return other;
282 const uint32 serverIP = client->GetServerIP();
283 const uint32 serverPort = client->GetServerPort();
284 if (userID && serverIP && serverPort) {
285 IDMapIteratorPair range = m_clientList.equal_range(userID);
287 for (; range.first != range.second; ++range.first) {
288 CUpDownClient* other = range.first->second.GetClient();
289 wxASSERT(userID == other->GetUserIDHybrid());
291 // For lowid, we also have to check the server
292 if (serverIP == other->GetServerIP()) {
293 if (serverPort == other->GetServerPort()) {
294 return other;
299 } else if (userPort || userKadPort) {
300 // Check by IP first, then by ID
301 struct { const IDMap& map; uint32 value; } toCheck[] = {
302 { m_ipList, userIP }, { m_clientList, userID }
305 for (size_t i = 0; i < itemsof(toCheck); ++i) {
306 if (toCheck[i].value == 0) {
307 // We may not have both (or any) of these values.
308 continue;
311 IDMapIteratorPair range = toCheck[i].map.equal_range(toCheck[i].value);
313 if (userPort) {
314 IDMap::const_iterator it = range.first;
315 for (; it != range.second; ++it) {
316 if (userPort == it->second.GetUserPort()) {
317 return it->second.GetClient();
322 if (userKadPort) {
323 IDMap::const_iterator it = range.first;
324 for (; it != range.second; ++it) {
325 if (userKadPort == it->second.GetClient()->GetKadPort()) {
326 return it->second.GetClient();
334 // If anything else fails, then we look at hashes
335 if ( client->HasValidHash() ) {
336 // Find all items with the specified hash
337 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( client->GetUserHash() );
339 // Just return the first item if any
340 if ( range.first != range.second ) {
341 return range.first->second.GetClient();
345 // Nothing found, must be a new client
346 return NULL;
350 uint32 CClientList::GetClientCount() const
352 return m_clientList.size();
356 void CClientList::DeleteAll()
358 m_ipList.clear();
359 m_hashList.clear();
361 while ( !m_clientList.empty() ) {
362 IDMap::iterator it = m_clientList.begin();
364 // Will call the removal of the item on this same class
365 it->second.GetClient()->Disconnected(wxT("Removed while deleting all from ClientList."));
366 it->second.GetClient()->Safe_Delete();
371 bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientTCPSocket* sender)
373 CUpDownClient* tocheck = (*client);
375 CUpDownClient* found_client = FindMatchingClient( tocheck );
377 if ( tocheck == found_client ) {
378 // We found the same client instance (client may have sent more than one OP_HELLO). do not delete that client!
379 return true;
382 if (found_client != NULL){
383 if (sender) {
384 if (found_client->GetSocket()) {
385 if (found_client->IsConnected()
386 && (found_client->GetIP() != tocheck->GetIP() || found_client->GetUserPort() != tocheck->GetUserPort() ) )
388 // if found_client is connected and has the IS_IDENTIFIED, it's safe to say that the other one is a bad guy
389 if (found_client->IsIdentified()){
390 AddDebugLogLineN(logClient, wxT("Client: ") + tocheck->GetUserName() + wxT("(") + tocheck->GetFullIP() + wxT("), Banreason: Userhash invalid"));
391 tocheck->Ban();
392 return false;
395 AddDebugLogLineN(logClient, wxT("WARNING! Found matching client, to a currently connected client: ")
396 + tocheck->GetUserName() + wxT("(") + tocheck->GetFullIP()
397 + wxT(") and ") + found_client->GetUserName() + wxT("(") + found_client->GetFullIP() + wxT(")"));
398 return false;
400 found_client->GetSocket()->Safe_Delete();
402 found_client->SetSocket( sender );
403 tocheck->SetSocket( NULL );
405 *client = 0;
406 tocheck->Safe_Delete();
407 *client = found_client;
408 return true;
411 return false;
415 CUpDownClient* CClientList::FindClientByIP( uint32 clientip, uint16 port )
417 // Find all items with the specified ip
418 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
420 for ( ; range.first != range.second; ++range.first ) {
421 CUpDownClient* cur_client = range.first->second.GetClient();
422 // Check if it's actually the client we want
423 if ( cur_client->GetUserPort() == port ) {
424 return cur_client;
428 return NULL;
432 CUpDownClient* CClientList::FindClientByIP( uint32 clientip )
434 // Find all items with the specified ip
435 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
437 return (range.first != range.second) ? range.first->second.GetClient() : NULL;
441 CUpDownClient* CClientList::FindClientByECID(uint32 ecid) const
443 for (IDMap::const_iterator it = m_clientList.begin(); it != m_clientList.end(); ++it) {
444 if (it->second.ECID() == ecid) {
445 return it->second.GetClient();
449 return NULL;
453 bool CClientList::IsIPAlreadyKnown(uint32_t ip)
455 // Find all items with the specified ip
456 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range(ip);
457 return range.first != range.second;
461 bool CClientList::ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash)
463 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
465 if ( it != m_trackedClientsList.end() ) {
466 CDeletedClient* pResult = it->second;
468 CDeletedClient::PaHList::iterator it2 = pResult->m_ItemsList.begin();
469 for ( ; it2 != pResult->m_ItemsList.end(); ++it2 ) {
470 if ( it2->nPort == nPort ) {
471 if ( it2->pHash != pNewHash) {
472 return false;
473 } else {
474 break;
479 return true;
483 void CClientList::AddTrackClient(CUpDownClient* toadd)
485 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( toadd->GetIP() );
487 if ( it != m_trackedClientsList.end() ) {
488 CDeletedClient* pResult = it->second;
490 pResult->m_dwInserted = ::GetTickCount();
492 CDeletedClient::PaHList::iterator it2 = pResult->m_ItemsList.begin();
493 for ( ; it2 != pResult->m_ItemsList.end(); ++it2 ) {
494 if ( it2->nPort == toadd->GetUserPort() ) {
495 // already tracked, update
496 it2->pHash = toadd->GetCreditsHash();
497 return;
501 // New client for that IP, add an entry
502 CDeletedClient::PortAndHash porthash = { toadd->GetUserPort(), toadd->GetCreditsHash()};
503 pResult->m_ItemsList.push_back(porthash);
504 } else {
505 m_trackedClientsList[ toadd->GetIP() ] = new CDeletedClient(toadd);
510 uint16 CClientList::GetClientsFromIP(uint32 dwIP)
512 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
514 if ( it != m_trackedClientsList.end() ) {
515 return it->second->m_ItemsList.size();
516 } else {
517 return 0;
522 void CClientList::Process()
524 const uint32 cur_tick = ::GetTickCount();
526 if (m_dwLastBannCleanUp + BAN_CLEANUP_TIME < cur_tick) {
527 m_dwLastBannCleanUp = cur_tick;
529 ClientMap::iterator it = m_bannedList.begin();
530 while ( it != m_bannedList.end() ) {
531 if ( it->second + CLIENTBANTIME < cur_tick ) {
532 ClientMap::iterator tmp = it++;
534 m_bannedList.erase( tmp );
535 theStats::RemoveBannedClient();
536 } else {
537 ++it;
543 if ( m_dwLastTrackedCleanUp + TRACKED_CLEANUP_TIME < cur_tick ) {
544 m_dwLastTrackedCleanUp = cur_tick;
546 std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.begin();
547 while ( it != m_trackedClientsList.end() ) {
548 std::map<uint32, CDeletedClient*>::iterator cur_src = it++;
550 if ( cur_src->second->m_dwInserted + KEEPTRACK_TIME < cur_tick ) {
551 delete cur_src->second;
552 m_trackedClientsList.erase( cur_src );
557 //We need to try to connect to the clients in m_KadList
558 //If connected, remove them from the list and send a message back to Kad so we can send a ACK.
559 //If we don't connect, we need to remove the client..
560 //The sockets timeout should delete this object.
562 // buddy is just a flag that is used to make sure we are still connected or connecting to a buddy.
563 buddyState buddy = Disconnected;
565 CClientRefSet::iterator current_it = m_KadSources.begin();
566 while (current_it != m_KadSources.end()) {
567 CUpDownClient* cur_client = current_it->GetClient();
568 ++current_it; // Won't be used anymore till while loop
569 if( !Kademlia::CKademlia::IsRunning() ) {
570 //Clear out this list if we stop running Kad.
571 //Setting the Kad state to KS_NONE causes it to be removed in the switch below.
572 cur_client->SetKadState(KS_NONE);
574 switch (cur_client->GetKadState()) {
575 case KS_QUEUED_FWCHECK:
576 case KS_QUEUED_FWCHECK_UDP:
577 //Another client asked us to try to connect to them to check their firewalled status.
578 cur_client->TryToConnect(true);
579 break;
581 case KS_CONNECTING_FWCHECK:
582 //Ignore this state as we are just waiting for results.
583 break;
585 case KS_FWCHECK_UDP:
586 case KS_CONNECTING_FWCHECK_UDP:
587 // We want a UDP firewallcheck from this client and are just waiting to get connected to send the request
588 break;
590 case KS_CONNECTED_FWCHECK:
591 //We successfully connected to the client.
592 //We now send a ack to let them know.
593 if (cur_client->GetKadVersion() >= 7) {
594 // The result is now sent per TCP instead of UDP, because this will fail if our intern port is unreachable.
595 // But we want the TCP testresult regardless if UDP is firewalled, the new UDP state and test takes care of the rest
596 wxASSERT(cur_client->IsConnected());
597 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_KAD_FWTCPCHECK_ACK to ") + Uint32toStringIP(cur_client->GetIP()));
598 CPacket *packet = new CPacket(OP_KAD_FWTCPCHECK_ACK, 0, OP_EMULEPROT);
599 cur_client->SafeSendPacket(packet);
600 } else {
601 AddDebugLogLineN(logClientKadUDP, wxT("KadFirewalledAckRes to ") + Uint32_16toStringIP_Port(cur_client->GetIP(), cur_client->GetKadPort()));
602 Kademlia::CKademlia::GetUDPListener()->SendNullPacket(KADEMLIA_FIREWALLED_ACK_RES, wxUINT32_SWAP_ALWAYS(cur_client->GetIP()), cur_client->GetKadPort(), 0, NULL);
604 //We are done with this client. Set Kad status to KS_NONE and it will be removed in the next cycle.
605 cur_client->SetKadState(KS_NONE);
606 break;
608 case KS_INCOMING_BUDDY:
609 //A firewalled client wants us to be his buddy.
610 //If we already have a buddy, we set Kad state to KS_NONE and it's removed in the next cycle.
611 //If not, this client will change to KS_CONNECTED_BUDDY when it connects.
612 if( m_nBuddyStatus == Connected ) {
613 cur_client->SetKadState(KS_NONE);
615 break;
617 case KS_QUEUED_BUDDY:
618 //We are firewalled and want to request this client to be a buddy.
619 //But first we check to make sure we are not already trying another client.
620 //If we are not already trying. We try to connect to this client.
621 //If we are already connected to a buddy, we set this client to KS_NONE and it's removed next cycle.
622 //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.
623 if( m_nBuddyStatus == Disconnected ) {
624 buddy = Connecting;
625 m_nBuddyStatus = Connecting;
626 cur_client->SetKadState(KS_CONNECTING_BUDDY);
627 cur_client->TryToConnect(true);
628 Notify_ServerUpdateED2KInfo();
629 } else {
630 if( m_nBuddyStatus == Connected ) {
631 cur_client->SetKadState(KS_NONE);
634 break;
636 case KS_CONNECTING_BUDDY:
637 //We are trying to connect to this client.
638 //Although it should NOT happen, we make sure we are not already connected to a buddy.
639 //If we are we set to KS_NONE and it's removed next cycle.
640 //But if we are not already connected, make sure we set the flag to connecting so we know
641 //things are working correctly.
642 if( m_nBuddyStatus == Connected ) {
643 cur_client->SetKadState(KS_NONE);
644 } else {
645 wxASSERT( m_nBuddyStatus == Connecting );
646 buddy = Connecting;
648 break;
650 case KS_CONNECTED_BUDDY:
651 //A potential connected buddy client wanting to me in the Kad network
652 //We set our flag to connected to make sure things are still working correctly.
653 buddy = Connected;
655 //If m_nBuddyStatus is not connected already, we set this client as our buddy!
656 if( m_nBuddyStatus != Connected ) {
657 m_pBuddy.Link(cur_client CLIENT_DEBUGSTRING("CClientList::Process KS_CONNECTED_BUDDY m_pBuddy.Link"));
658 m_nBuddyStatus = Connected;
659 Notify_ServerUpdateED2KInfo();
661 if( m_pBuddy.GetClient() == cur_client && theApp->IsFirewalled() && cur_client->SendBuddyPingPong() ) {
662 cur_client->SendBuddyPing();
664 break;
666 default:
667 RemoveFromKadList(cur_client);
671 //We either never had a buddy, or lost our buddy..
672 if( buddy == Disconnected ) {
673 if( m_nBuddyStatus != Disconnected || m_pBuddy.IsLinked() ) {
674 if( Kademlia::CKademlia::IsRunning() && theApp->IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true) ) {
675 //We are a lowID client and we just lost our buddy.
676 //Go ahead and instantly try to find a new buddy.
677 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
679 m_pBuddy.Unlink();
680 m_nBuddyStatus = Disconnected;
681 Notify_ServerUpdateED2KInfo();
685 if ( Kademlia::CKademlia::IsConnected() ) {
686 // we only need a buddy if direct callback is not available
687 if(Kademlia::CKademlia::IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true)) {
688 // TODO: Kad buddies won't work with RequireCrypt, so it is disabled for now, but should (and will)
689 // be fixed in later version
690 // Update: buddy connections themselves support obfuscation properly since eMule 0.49a and aMule SVN 2008-05-09
691 // (this makes it work fine if our buddy uses require crypt), however callback requests don't support it yet so we
692 // wouldn't be able to answer callback requests with RequireCrypt, protocolchange intended for eMule 0.49b
693 if(m_nBuddyStatus == Disconnected && Kademlia::CKademlia::GetPrefs()->GetFindBuddy() && !thePrefs::IsClientCryptLayerRequired()) {
694 AddDebugLogLineN(logKadMain, wxT("Starting BuddySearch"));
695 //We are a firewalled client with no buddy. We have also waited a set time
696 //to try to avoid a false firewalled status.. So lets look for a buddy..
697 if (!Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FINDBUDDY, true, Kademlia::CUInt128(true) ^ (Kademlia::CKademlia::GetPrefs()->GetKadID()))) {
698 //This search ID was already going. Most likely reason is that
699 //we found and lost our buddy very quickly and the last search hadn't
700 //had time to be removed yet. Go ahead and set this to happen again
701 //next time around.
702 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
705 } else {
706 if (m_pBuddy.IsLinked()) {
707 //Lets make sure that if we have a buddy, they are firewalled!
708 //If they are also not firewalled, then someone must have fixed their firewall or stopped saturating their line..
709 //We just set the state of this buddy to KS_NONE and things will be cleared up with the next cycle.
710 if( !m_pBuddy.HasLowID() ) {
711 m_pBuddy.GetClient()->SetKadState(KS_NONE);
715 } else {
716 if (m_pBuddy.IsLinked()) {
717 //We are not connected anymore. Just set this buddy to KS_NONE and things will be cleared out on next cycle.
718 m_pBuddy.GetClient()->SetKadState(KS_NONE);
722 CleanUpClientList();
723 ProcessDirectCallbackList();
727 void CClientList::AddBannedClient(uint32 dwIP)
729 m_bannedList[dwIP] = ::GetTickCount();
730 theStats::AddBannedClient();
734 bool CClientList::IsBannedClient(uint32 dwIP)
736 ClientMap::iterator it = m_bannedList.find( dwIP );
738 if ( it != m_bannedList.end() ) {
739 if ( it->second + CLIENTBANTIME > ::GetTickCount() ) {
740 return true;
741 } else {
742 RemoveBannedClient(dwIP);
745 return false;
749 void CClientList::RemoveBannedClient(uint32 dwIP)
751 m_bannedList.erase(dwIP);
752 theStats::RemoveBannedClient();
756 void CClientList::FilterQueues()
758 // Filter client list
759 for ( IDMap::iterator it = m_ipList.begin(); it != m_ipList.end(); ) {
760 IDMap::iterator tmp = it++; // Don't change this to a ++it!
761 CUpDownClient* client = tmp->second.GetClient();
762 if ( theApp->ipfilter->IsFiltered(client->GetConnectIP())) {
763 client->Disconnected(wxT("Filtered by IPFilter"));
764 client->Safe_Delete();
770 CClientList::SourceList CClientList::GetClientsByHash( const CMD4Hash& hash )
772 SourceList results;
774 // Find all items with the specified hash
775 std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( hash );
777 for ( ; range.first != range.second; ++range.first) {
778 results.push_back( range.first->second );
781 return results;
785 CClientList::SourceList CClientList::GetClientsByIP( unsigned long ip )
787 SourceList results;
789 // Find all items with the specified hash
790 std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( ip );
792 for ( ; range.first != range.second; range.first++ ) {
793 results.push_back( range.first->second );
796 return results;
800 const CClientList::IDMap& CClientList::GetClientList()
802 return m_clientList;
806 void CClientList::AddDeadSource(const CUpDownClient* client)
808 m_deadSources.AddDeadSource( client );
812 bool CClientList::IsDeadSource(const CUpDownClient* client)
814 return m_deadSources.IsDeadSource( client );
817 bool CClientList::SendChatMessage(uint64 client_id, const wxString& message)
819 CUpDownClient* client = FindClientByIP(IP_FROM_GUI_ID(client_id), PORT_FROM_GUI_ID(client_id));
820 AddDebugLogLineN( logClient, wxT("Trying to Send Message.") );
821 if (client) {
822 AddDebugLogLineN( logClient, wxT("Sending.") );
823 } else {
824 AddDebugLogLineC( logClient,
825 CFormat( wxT("No client (GUI_ID %lli [%s:%llu]) found in CClientList::SendChatMessage(). Creating") )
826 % client_id
827 % Uint32toStringIP(IP_FROM_GUI_ID(client_id))
828 % PORT_FROM_GUI_ID(client_id) );
829 client = new CUpDownClient(PORT_FROM_GUI_ID(client_id),IP_FROM_GUI_ID(client_id),0,0,NULL, true, true);
830 AddClient(client);
832 return client->SendChatMessage(message);
835 void CClientList::SetChatState(uint64 client_id, uint8 state) {
836 CUpDownClient* client = FindClientByIP(IP_FROM_GUI_ID(client_id), PORT_FROM_GUI_ID(client_id));
837 if (client) {
838 client->SetChatState(state);
842 /* Kad stuff */
844 bool CClientList::RequestTCP(Kademlia::CContact* contact, uint8_t connectOptions)
846 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
847 // don't connect ourself
848 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
849 return false;
852 CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
854 if (!pNewClient) {
855 //#warning Do we actually have to check friendstate here?
856 pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true);
857 } else if (pNewClient->GetKadState() != KS_NONE) {
858 return false; // already busy with this client in some way (probably buddy stuff), don't mess with it
861 //Add client to the lists to be processed.
862 pNewClient->SetKadPort(contact->GetUDPPort());
863 pNewClient->SetKadState(KS_QUEUED_FWCHECK);
864 if (contact->GetClientID() != 0) {
865 uint8_t ID[16];
866 contact->GetClientID().ToByteArray(ID);
867 pNewClient->SetUserHash(CMD4Hash(ID));
868 pNewClient->SetConnectOptions(connectOptions, true, false);
870 AddToKadList(pNewClient); // This was a direct adding, but I like to check duplicates
871 //This method checks if this is a dup already.
872 AddClient(pNewClient);
873 return true;
876 void CClientList::RequestBuddy(Kademlia::CContact* contact, uint8_t connectOptions)
878 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
879 // Don't connect to ourself
880 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
881 return;
884 CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
885 if (!pNewClient) {
886 pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true );
887 } else if (pNewClient->GetKadState() != KS_NONE) {
888 return; // already busy with this client in some way (probably fw stuff), don't mess with it
889 } else if (IsKadFirewallCheckIP(nContactIP)) { // doing a kad firewall check with this IP, abort
890 AddDebugLogLineN(logKadMain, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP));
891 return;
894 //Add client to the lists to be processed.
895 pNewClient->SetKadPort(contact->GetUDPPort());
896 pNewClient->SetKadState(KS_QUEUED_BUDDY);
897 uint8_t ID[16];
898 contact->GetClientID().ToByteArray(ID);
899 pNewClient->SetUserHash(CMD4Hash(ID));
900 pNewClient->SetConnectOptions(connectOptions, true, false);
901 AddToKadList(pNewClient);
902 //This method checks if this is a dup already.
903 AddClient(pNewClient);
906 bool CClientList::IncomingBuddy(Kademlia::CContact* contact, Kademlia::CUInt128* buddyID)
908 uint32_t nContactIP = wxUINT32_SWAP_ALWAYS(contact->GetIPAddress());
909 //If aMule already knows this client, abort this.. It could cause conflicts.
910 //Although the odds of this happening is very small, it could still happen.
911 if (FindClientByIP(nContactIP, contact->GetTCPPort())) {
912 return false;
913 } else if (IsKadFirewallCheckIP(nContactIP)) { // doing a kad firewall check with this IP, abort
914 AddDebugLogLineN(logKadMain, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP));
915 return false;
918 if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
919 return false; // don't connect ourself
922 //Add client to the lists to be processed.
923 CUpDownClient* pNewClient = new CUpDownClient(contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, NULL, false, true );
924 pNewClient->SetKadPort(contact->GetUDPPort());
925 pNewClient->SetKadState(KS_INCOMING_BUDDY);
926 byte ID[16];
927 contact->GetClientID().ToByteArray(ID);
928 pNewClient->SetUserHash(CMD4Hash(ID));
929 buddyID->ToByteArray(ID);
930 pNewClient->SetBuddyID(ID);
931 AddToKadList(pNewClient);
932 AddClient(pNewClient);
933 return true;
936 void CClientList::RemoveFromKadList(CUpDownClient* torem)
938 wxCHECK_RET(torem, wxT("NULL pointer in RemoveFromKadList"));
940 if (m_KadSources.erase(CCLIENTREF(torem, wxEmptyString))) {
941 if (torem == m_pBuddy.GetClient()) {
942 m_pBuddy.Unlink();
943 m_nBuddyStatus = Disconnected;
944 Notify_ServerUpdateED2KInfo();
949 void CClientList::AddToKadList(CUpDownClient* toadd)
951 wxCHECK_RET(toadd, wxT("NULL pointer in AddToKadList"));
953 m_KadSources.insert(CCLIENTREF(toadd, wxT("CClientList::AddToKadList"))); // This will take care of duplicates.
956 bool CClientList::DoRequestFirewallCheckUDP(const Kademlia::CContact& contact)
958 // first make sure we don't know this IP already from somewhere
959 if (IsIPAlreadyKnown(wxUINT32_SWAP_ALWAYS(contact.GetIPAddress()))) {
960 return false;
962 // fine, just create the client object, set the state and wait
963 // TODO: We don't know the client's userhash, this means we cannot build an obfuscated connection, which
964 // again mean that the whole check won't work on "Require Obfuscation" setting, which is not a huge problem,
965 // but certainly not nice. Only somewhat acceptable way to solve this is to use the KadID instead.
966 CUpDownClient* pNewClient = new CUpDownClient(contact.GetTCPPort(), contact.GetIPAddress(), 0, 0, NULL, false, true);
967 pNewClient->SetKadState(KS_QUEUED_FWCHECK_UDP);
968 AddDebugLogLineN(logClient, wxT("Selected client for UDP Firewallcheck: ") + KadIPToString(contact.GetIPAddress()));
969 AddToKadList(pNewClient);
970 AddClient(pNewClient);
971 wxASSERT(!pNewClient->SupportsDirectUDPCallback());
972 return true;
975 void CClientList::CleanUpClientList()
977 // We remove clients which are not needed any more by time
978 // this check is also done on CUpDownClient::Disconnected, however it will not catch all
979 // cases (if a client changes the state without beeing connected
981 // Adding this check directly to every point where any state changes would be more effective,
982 // is however not compatible with the current code, because there are points where a client has
983 // no state for some code lines and the code is also not prepared that a client object gets
984 // invalid while working with it (aka setting a new state)
985 // so this way is just the easy and safe one to go (as long as amule is basically single threaded)
986 const uint32 cur_tick = ::GetTickCount();
987 if (m_dwLastClientCleanUp + CLIENTLIST_CLEANUP_TIME < cur_tick ){
988 m_dwLastClientCleanUp = cur_tick;
989 DEBUG_ONLY( uint32 cDeleted = 0; )
990 IDMap::iterator current_it = m_clientList.begin();
991 while (current_it != m_clientList.end()) {
992 CUpDownClient* pCurClient = current_it->second.GetClient();
993 ++current_it; // Won't be used till while loop again
994 // Don't delete sources coming from source seeds for 10 mins,
995 // to give them a chance to connect and become a useful source.
996 if (pCurClient->GetSourceFrom() == SF_SOURCE_SEEDS && cur_tick - (uint32)theStats::GetStartTime() < MIN2MS(10)) continue;
997 if ((pCurClient->GetUploadState() == US_NONE || (pCurClient->GetUploadState() == US_BANNED && !pCurClient->IsBanned()))
998 && pCurClient->GetDownloadState() == DS_NONE
999 && pCurClient->GetChatState() == MS_NONE
1000 && pCurClient->GetKadState() == KS_NONE
1001 && pCurClient->GetSocket() == NULL)
1003 DEBUG_ONLY( cDeleted++; )
1004 pCurClient->Disconnected(wxT("Removed during ClientList cleanup."));
1005 pCurClient->Safe_Delete();
1006 #ifdef __DEBUG__
1007 } else {
1008 if (!(pCurClient->GetUploadState() == US_NONE || (pCurClient->GetUploadState() == US_BANNED && !pCurClient->IsBanned()))) {
1009 AddDebugLogLineN(logProxy,
1010 CFormat(wxT("Debug: Not deleted client %x with up state: %i "))
1011 % (long int)pCurClient % pCurClient->GetUploadState());
1013 if (!(pCurClient->GetDownloadState() == DS_NONE)) {
1014 AddDebugLogLineN(logProxy,
1015 CFormat(wxT("Debug: Not deleted client %x with down state: %i "))
1016 % (long int)pCurClient % pCurClient->GetDownloadState());
1018 if (!(pCurClient->GetChatState() == MS_NONE)) {
1019 AddDebugLogLineN(logProxy,
1020 CFormat(wxT("Debug: Not deleted client %x with chat state: %i "))
1021 % (long int)pCurClient % pCurClient->GetChatState());
1023 if (!(pCurClient->GetKadState() == KS_NONE)) {
1024 AddDebugLogLineN(logProxy,
1025 CFormat(wxT("Debug: Not deleted client %x with kad state: %i ip: %s"))
1026 % (long int)pCurClient % (int)pCurClient->GetKadState() % pCurClient->GetFullIP());
1028 if (!(pCurClient->GetSocket() == NULL)) {
1029 AddDebugLogLineN(logProxy,
1030 CFormat(wxT("Debug: Not deleted client %x: has socket")) % (long int)pCurClient);
1032 AddDebugLogLineN(logProxy,
1033 CFormat(wxT("Debug: Not deleted client %x with kad version: %i"))
1034 % (long int)pCurClient % pCurClient->GetKadVersion());
1035 #endif
1038 AddDebugLogLineN(logClient, CFormat(wxT("Cleaned ClientList, removed %i not used known clients")) % cDeleted);
1042 void CClientList::AddKadFirewallRequest(uint32 ip)
1044 uint32 ticks = ::GetTickCount();
1045 IpAndTicks add = { ip, ticks };
1046 m_firewallCheckRequests.push_front(add);
1047 while (!m_firewallCheckRequests.empty()) {
1048 if (ticks - m_firewallCheckRequests.back().inserted > SEC2MS(180)) {
1049 m_firewallCheckRequests.pop_back();
1050 } else {
1051 break;
1056 bool CClientList::IsKadFirewallCheckIP(uint32 ip) const
1058 uint32 ticks = ::GetTickCount();
1059 for (IpAndTicksList::const_iterator it = m_firewallCheckRequests.begin(); it != m_firewallCheckRequests.end(); ++it) {
1060 if (it->ip == ip && ticks - it->inserted < SEC2MS(180)) {
1061 return true;
1064 return false;
1067 void CClientList::AddDirectCallbackClient(CUpDownClient* toAdd)
1069 wxASSERT(toAdd->GetDirectCallbackTimeout() != 0);
1070 if (toAdd->HasBeenDeleted()) {
1071 return;
1073 for (DirectCallbackList::const_iterator it = m_currentDirectCallbacks.begin(); it != m_currentDirectCallbacks.end(); ++it) {
1074 if (it->GetClient() == toAdd) {
1075 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
1076 return;
1079 m_currentDirectCallbacks.push_back(CCLIENTREF(toAdd, wxT("CClientList::AddDirectCallbackClient")));
1082 void CClientList::ProcessDirectCallbackList()
1084 // we do check if any direct callbacks have timed out by now
1085 const uint32_t cur_tick = ::GetTickCount();
1086 for (DirectCallbackList::iterator it = m_currentDirectCallbacks.begin(); it != m_currentDirectCallbacks.end();) {
1087 DirectCallbackList::iterator it2 = it++;
1088 CUpDownClient* curClient = it2->GetClient();
1089 if (curClient->GetDirectCallbackTimeout() < cur_tick) {
1090 wxASSERT(curClient->GetDirectCallbackTimeout() != 0);
1091 // TODO LOGREMOVE
1092 //DebugLog(_T("DirectCallback timed out (%s)"), pCurClient->DbgGetClientInfo());
1093 m_currentDirectCallbacks.erase(it2);
1094 if (curClient->Disconnected(wxT("Direct Callback Timeout"))) {
1095 curClient->Safe_Delete();
1101 void CClientList::AddTrackCallbackRequests(uint32_t ip)
1103 uint32_t now = ::GetTickCount();
1104 IpAndTicks add = { ip, now };
1105 m_directCallbackRequests.push_front(add);
1106 while (!m_directCallbackRequests.empty()) {
1107 if (now - m_directCallbackRequests.back().inserted > MIN2MS(3)) {
1108 m_directCallbackRequests.pop_back();
1109 } else {
1110 break;
1115 bool CClientList::AllowCallbackRequest(uint32_t ip) const
1117 uint32_t now = ::GetTickCount();
1118 for (IpAndTicksList::const_iterator it = m_directCallbackRequests.begin(); it != m_directCallbackRequests.end(); ++it) {
1119 if (it->ip == ip && now - it->inserted < MIN2MS(3)) {
1120 return false;
1123 return true;
1126 uint32 CClientList::GetBuddyIP()
1128 return GetBuddy()->GetIP();
1131 uint16 CClientList::GetBuddyPort()
1133 return GetBuddy()->GetUDPPort();
1137 // File_checked_for_headers