2 // This file is part of the aMule Project.
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 )
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
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
43 #include "GuiEvents.h" // Needed for Notify_*
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"
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.
66 CDeletedClient(CUpDownClient
* pClient
)
68 m_dwInserted
= ::GetTickCount();
69 PortAndHash porthash
= { pClient
->GetUserPort(), pClient
->GetCreditsHash()};
70 m_ItemsList
.push_back(porthash
);
79 typedef std::list
<PortAndHash
> PaHList
;
86 CClientList::CClientList()
87 : m_deadSources( true )
89 m_dwLastBannCleanUp
= 0;
90 m_dwLastTrackedCleanUp
= 0;
91 m_dwLastClientCleanUp
= 0;
93 m_nBuddyStatus
= Disconnected
;
95 m_delete_queue_closed
= false;
100 CClientList::~CClientList()
102 DeleteContents(m_trackedClientsList
);
104 wxASSERT(m_clientList
.empty());
105 wxASSERT(m_delete_queue
.empty());
109 void CClientList::AddClient( CUpDownClient
* toadd
)
111 // Ensure that only new clients can be added to the list
112 if ( toadd
->GetClientState() == CS_NEW
) {
113 // Update the client-state
114 toadd
->m_clientState
= CS_LISTED
;
116 //Notify_ClientCtrlAddClient( toadd );
118 // We always add the ID/ptr pair, regardles of the actual ID value
119 m_clientList
.insert( IDMapPair( toadd
->GetUserIDHybrid(), toadd
) );
121 // We only add the IP if it is valid
122 if ( toadd
->GetIP() ) {
123 m_ipList
.insert( IDMapPair( toadd
->GetIP(), toadd
) );
126 // We only add the hash if it is valid
127 if ( toadd
->HasValidHash() ) {
128 m_hashList
.insert( HashMapPair( toadd
->GetUserHash(), toadd
) );
131 toadd
->UpdateStats();
136 void CClientList::AddToDeleteQueue(CUpDownClient
* client
)
138 RemoveFromKadList( client
);
139 RemoveDirectCallback( client
);
141 // We have to remove the client from the list immediatly, to avoit it getting
142 // found by functions such as AttachToAlreadyKnown and GetClientsFromIP,
143 // however, if the client isn't on the clientlist, then it is safe to delete
144 // it right now. Otherwise, push it onto the queue.
145 if ( RemoveIDFromList( client
) ) {
146 // Also remove the ip and hash entries
147 RemoveIPFromList( client
);
148 RemoveHashFromList( client
);
150 wxASSERT(!m_delete_queue_closed
);
151 m_delete_queue
.push_back( client
);
158 void CClientList::UpdateClientID( CUpDownClient
* client
, uint32 newID
)
161 if ( ( client
->GetClientState() != CS_LISTED
) || ( client
->GetUserIDHybrid() == newID
) )
164 // First remove the ID entry
165 RemoveIDFromList( client
);
168 m_clientList
.insert( IDMapPair( newID
, client
) );
172 void CClientList::UpdateClientIP( CUpDownClient
* client
, uint32 newIP
)
175 if ( ( client
->GetClientState() != CS_LISTED
) || ( client
->GetIP() == newIP
) )
178 // Remove the old IP entry
179 RemoveIPFromList( client
);
182 m_ipList
.insert( IDMapPair( newIP
, client
) );
187 void CClientList::UpdateClientHash( CUpDownClient
* client
, const CMD4Hash
& newHash
)
190 if ( ( client
->GetClientState() != CS_LISTED
) || ( client
->GetUserHash() == newHash
) )
194 // Remove the old entry
195 RemoveHashFromList( client
);
197 // And add the new one if valid
198 if ( !newHash
.IsEmpty() ) {
199 m_hashList
.insert( HashMapPair( newHash
, client
) );
204 bool CClientList::RemoveIDFromList( CUpDownClient
* client
)
208 // First remove the ID entry
209 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_clientList
.equal_range( client
->GetUserIDHybrid() );
211 for ( ; range
.first
!= range
.second
; ++range
.first
) {
212 if ( client
== range
.first
->second
) {
213 /* erase() will invalidate the iterator, but we're not using it anymore
214 anyway (notice the break;) */
215 m_clientList
.erase( range
.first
);
226 void CClientList::RemoveIPFromList( CUpDownClient
* client
)
228 // Check if we need to look for the IP entry
229 if ( !client
->GetIP() ) {
233 // Remove the IP entry
234 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_ipList
.equal_range( client
->GetIP() );
236 for ( ; range
.first
!= range
.second
; ++range
.first
) {
237 if ( client
== range
.first
->second
) {
238 /* erase() will invalidate the iterator, but we're not using it anymore
239 anyway (notice the break;) */
240 m_ipList
.erase( range
.first
);
246 void CClientList::RemoveHashFromList( CUpDownClient
* client
)
249 if ( !client
->HasValidHash() ) {
253 // Find all items with the specified hash
254 std::pair
<HashMap::iterator
, HashMap::iterator
> range
= m_hashList
.equal_range( client
->GetUserHash() );
256 for ( ; range
.first
!= range
.second
; ++range
.first
) {
257 if ( client
== range
.first
->second
) {
258 /* erase() will invalidate the iterator, but we're not using it anymore
259 anyway (notice the break;) */
260 m_hashList
.erase( range
.first
);
267 CUpDownClient
* CClientList::FindMatchingClient( CUpDownClient
* client
)
269 typedef std::pair
<IDMap::const_iterator
, IDMap::const_iterator
> IDMapIteratorPair
;
270 wxCHECK(client
, NULL
);
272 const uint32 userIP
= client
->GetIP();
273 const uint32 userID
= client
->GetUserIDHybrid();
274 const uint16 userPort
= client
->GetUserPort();
275 const uint16 userKadPort
= client
->GetKadPort();
278 // LowID clients need a different set of checks
279 if (client
->HasLowID()) {
280 // User is firewalled ... Must do two checks.
281 if (userIP
&& (userPort
|| userKadPort
)) {
282 IDMapIteratorPair range
= m_ipList
.equal_range(userIP
);
284 for ( ; range
.first
!= range
.second
; ++range
.first
) {
285 CUpDownClient
* other
= range
.first
->second
;
286 wxASSERT(userIP
== other
->GetIP());
288 if (userPort
&& (userPort
== other
->GetUserPort())) {
290 } else if (userKadPort
&& (userKadPort
== other
->GetKadPort())) {
296 const uint32 serverIP
= client
->GetServerIP();
297 const uint32 serverPort
= client
->GetServerPort();
298 if (userID
&& serverIP
&& serverPort
) {
299 IDMapIteratorPair range
= m_clientList
.equal_range(userID
);
301 for (; range
.first
!= range
.second
; ++range
.first
) {
302 CUpDownClient
* other
= range
.first
->second
;
303 wxASSERT(userID
== other
->GetUserIDHybrid());
305 // For lowid, we also have to check the server
306 if (serverIP
== other
->GetServerIP()) {
307 if (serverPort
== other
->GetServerPort()) {
313 } else if (userPort
|| userKadPort
) {
314 // Check by IP first, then by ID
315 struct { const IDMap
& map
; uint32 value
; } toCheck
[] = {
316 { m_ipList
, userIP
}, { m_clientList
, userID
}
319 for (size_t i
= 0; i
< itemsof(toCheck
); ++i
) {
320 if (toCheck
[i
].value
== 0) {
321 // We may not have both (or any) of these values.
325 IDMapIteratorPair range
= toCheck
[i
].map
.equal_range(toCheck
[i
].value
);
328 IDMap::const_iterator it
= range
.first
;
329 for (; it
!= range
.second
; ++it
) {
330 if (userPort
== it
->second
->GetUserPort()) {
337 IDMap::const_iterator it
= range
.first
;
338 for (; it
!= range
.second
; ++it
) {
339 if (userKadPort
== it
->second
->GetKadPort()) {
348 // If anything else fails, then we look at hashes
349 if ( client
->HasValidHash() ) {
350 // Find all items with the specified hash
351 std::pair
<HashMap::iterator
, HashMap::iterator
> range
= m_hashList
.equal_range( client
->GetUserHash() );
353 // Just return the first item if any
354 if ( range
.first
!= range
.second
) {
355 return range
.first
->second
;
359 // Nothing found, must be a new client
364 uint32
CClientList::GetClientCount() const
366 return m_clientList
.size();
370 void CClientList::DeleteAll()
375 while ( !m_clientList
.empty() ) {
376 IDMap::iterator it
= m_clientList
.begin();
378 // Will call the removal of the item on this same class
379 it
->second
->Disconnected(wxT("Removed while deleting all from ClientList."));
380 it
->second
->Safe_Delete();
383 // Clean up the clients now queued for deletion
385 m_delete_queue_closed
= true;
387 ProcessDeleteQueue();
391 bool CClientList::AttachToAlreadyKnown(CUpDownClient
** client
, CClientTCPSocket
* sender
)
393 CUpDownClient
* tocheck
= (*client
);
395 CUpDownClient
* found_client
= FindMatchingClient( tocheck
);
397 if ( tocheck
== found_client
) {
398 // We found the same client instance (client may have sent more than one OP_HELLO). do not delete that client!
402 if (found_client
!= NULL
){
404 if (found_client
->GetSocket()) {
405 if (found_client
->IsConnected()
406 && (found_client
->GetIP() != tocheck
->GetIP() || found_client
->GetUserPort() != tocheck
->GetUserPort() ) )
408 // if found_client is connected and has the IS_IDENTIFIED, it's safe to say that the other one is a bad guy
409 if (found_client
->IsIdentified()){
410 AddDebugLogLineN(logClient
, wxT("Client: ") + tocheck
->GetUserName() + wxT("(") + tocheck
->GetFullIP() + wxT("), Banreason: Userhash invalid"));
415 AddDebugLogLineN(logClient
, wxT("WARNING! Found matching client, to a currently connected client: ")
416 + tocheck
->GetUserName() + wxT("(") + tocheck
->GetFullIP()
417 + wxT(") and ") + found_client
->GetUserName() + wxT("(") + found_client
->GetFullIP() + wxT(")"));
420 found_client
->GetSocket()->Safe_Delete();
422 found_client
->SetSocket( sender
);
423 tocheck
->SetSocket( NULL
);
426 tocheck
->Safe_Delete();
427 *client
= found_client
;
435 CUpDownClient
* CClientList::FindClientByIP( uint32 clientip
, uint16 port
)
437 // Find all items with the specified ip
438 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_ipList
.equal_range( clientip
);
440 for ( ; range
.first
!= range
.second
; ++range
.first
) {
441 CUpDownClient
* cur_client
= range
.first
->second
;
442 // Check if it's actually the client we want
443 if ( cur_client
->GetUserPort() == port
) {
452 CUpDownClient
* CClientList::FindClientByIP( uint32 clientip
)
454 // Find all items with the specified ip
455 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_ipList
.equal_range( clientip
);
457 return (range
.first
!= range
.second
) ? range
.first
->second
: NULL
;
461 CUpDownClient
* CClientList::FindClientByECID(uint32 ecid
) const
463 for (IDMap::const_iterator it
= m_clientList
.begin(); it
!= m_clientList
.end(); it
++) {
464 if (it
->second
->ECID() == ecid
) {
473 bool CClientList::IsIPAlreadyKnown(uint32_t ip
)
475 // Find all items with the specified ip
476 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_ipList
.equal_range(ip
);
477 return range
.first
!= range
.second
;
481 bool CClientList::ComparePriorUserhash(uint32 dwIP
, uint16 nPort
, void* pNewHash
)
483 std::map
<uint32
, CDeletedClient
*>::iterator it
= m_trackedClientsList
.find( dwIP
);
485 if ( it
!= m_trackedClientsList
.end() ) {
486 CDeletedClient
* pResult
= it
->second
;
488 CDeletedClient::PaHList::iterator it2
= pResult
->m_ItemsList
.begin();
489 for ( ; it2
!= pResult
->m_ItemsList
.end(); ++it2
) {
490 if ( it2
->nPort
== nPort
) {
491 if ( it2
->pHash
!= pNewHash
) {
503 void CClientList::AddTrackClient(CUpDownClient
* toadd
)
505 std::map
<uint32
, CDeletedClient
*>::iterator it
= m_trackedClientsList
.find( toadd
->GetIP() );
507 if ( it
!= m_trackedClientsList
.end() ) {
508 CDeletedClient
* pResult
= it
->second
;
510 pResult
->m_dwInserted
= ::GetTickCount();
512 CDeletedClient::PaHList::iterator it2
= pResult
->m_ItemsList
.begin();
513 for ( ; it2
!= pResult
->m_ItemsList
.end(); ++it2
) {
514 if ( it2
->nPort
== toadd
->GetUserPort() ) {
515 // already tracked, update
516 it2
->pHash
= toadd
->GetCreditsHash();
521 // New client for that IP, add an entry
522 CDeletedClient::PortAndHash porthash
= { toadd
->GetUserPort(), toadd
->GetCreditsHash()};
523 pResult
->m_ItemsList
.push_back(porthash
);
525 m_trackedClientsList
[ toadd
->GetIP() ] = new CDeletedClient(toadd
);
530 uint16
CClientList::GetClientsFromIP(uint32 dwIP
)
532 std::map
<uint32
, CDeletedClient
*>::iterator it
= m_trackedClientsList
.find( dwIP
);
534 if ( it
!= m_trackedClientsList
.end() ) {
535 return it
->second
->m_ItemsList
.size();
542 void CClientList::ProcessDeleteQueue()
544 // Delete pending clients
545 while ( !m_delete_queue
.empty() ) {
546 CUpDownClient
* toremove
= m_delete_queue
.front();
547 m_delete_queue
.pop_front();
549 // Doing what RemoveClient used to do. Just to be sure...
550 theApp
->uploadqueue
->RemoveFromUploadQueue( toremove
);
551 theApp
->uploadqueue
->RemoveFromWaitingQueue( toremove
);
552 theApp
->downloadqueue
->RemoveSource( toremove
);
554 //Notify_ClientCtrlRemoveClient( toremove );
561 void CClientList::Process()
563 const uint32 cur_tick
= ::GetTickCount();
565 ProcessDeleteQueue();
567 if (m_dwLastBannCleanUp
+ BAN_CLEANUP_TIME
< cur_tick
) {
568 m_dwLastBannCleanUp
= cur_tick
;
570 ClientMap::iterator it
= m_bannedList
.begin();
571 while ( it
!= m_bannedList
.end() ) {
572 if ( it
->second
+ CLIENTBANTIME
< cur_tick
) {
573 ClientMap::iterator tmp
= it
++;
575 m_bannedList
.erase( tmp
);
576 theStats::RemoveBannedClient();
584 if ( m_dwLastTrackedCleanUp
+ TRACKED_CLEANUP_TIME
< cur_tick
) {
585 m_dwLastTrackedCleanUp
= cur_tick
;
587 std::map
<uint32
, CDeletedClient
*>::iterator it
= m_trackedClientsList
.begin();
588 while ( it
!= m_trackedClientsList
.end() ) {
589 std::map
<uint32
, CDeletedClient
*>::iterator cur_src
= it
++;
591 if ( cur_src
->second
->m_dwInserted
+ KEEPTRACK_TIME
< cur_tick
) {
592 delete cur_src
->second
;
593 m_trackedClientsList
.erase( cur_src
);
598 //We need to try to connect to the clients in m_KadList
599 //If connected, remove them from the list and send a message back to Kad so we can send a ACK.
600 //If we don't connect, we need to remove the client..
601 //The sockets timeout should delete this object.
603 // buddy is just a flag that is used to make sure we are still connected or connecting to a buddy.
604 buddyState buddy
= Disconnected
;
606 std::set
<CUpDownClient
*>::iterator current_it
= m_KadSources
.begin();
607 while (current_it
!= m_KadSources
.end()) {
608 CUpDownClient
* cur_client
= *current_it
;
609 ++current_it
; // Won't be used anymore till while loop
610 if( !Kademlia::CKademlia::IsRunning() ) {
611 //Clear out this list if we stop running Kad.
612 //Setting the Kad state to KS_NONE causes it to be removed in the switch below.
613 cur_client
->SetKadState(KS_NONE
);
615 switch (cur_client
->GetKadState()) {
616 case KS_QUEUED_FWCHECK
:
617 case KS_QUEUED_FWCHECK_UDP
:
618 //Another client asked us to try to connect to them to check their firewalled status.
619 cur_client
->TryToConnect(true);
622 case KS_CONNECTING_FWCHECK
:
623 //Ignore this state as we are just waiting for results.
627 case KS_CONNECTING_FWCHECK_UDP
:
628 // We want a UDP firewallcheck from this client and are just waiting to get connected to send the request
631 case KS_CONNECTED_FWCHECK
:
632 //We successfully connected to the client.
633 //We now send a ack to let them know.
634 if (cur_client
->GetKadVersion() >= 7) {
635 // The result is now sent per TCP instead of UDP, because this will fail if our intern port is unreachable.
636 // But we want the TCP testresult regardless if UDP is firewalled, the new UDP state and test takes care of the rest
637 wxASSERT(cur_client
->IsConnected());
638 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_KAD_FWTCPCHECK_ACK to ") + Uint32toStringIP(cur_client
->GetIP()));
639 CPacket
*packet
= new CPacket(OP_KAD_FWTCPCHECK_ACK
, 0, OP_EMULEPROT
);
640 cur_client
->SafeSendPacket(packet
);
642 AddDebugLogLineN(logClientKadUDP
, wxT("KadFirewalledAckRes to ") + Uint32_16toStringIP_Port(cur_client
->GetIP(), cur_client
->GetKadPort()));
643 Kademlia::CKademlia::GetUDPListener()->SendNullPacket(KADEMLIA_FIREWALLED_ACK_RES
, wxUINT32_SWAP_ALWAYS(cur_client
->GetIP()), cur_client
->GetKadPort(), 0, NULL
);
645 //We are done with this client. Set Kad status to KS_NONE and it will be removed in the next cycle.
646 cur_client
->SetKadState(KS_NONE
);
649 case KS_INCOMING_BUDDY
:
650 //A firewalled client wants us to be his buddy.
651 //If we already have a buddy, we set Kad state to KS_NONE and it's removed in the next cycle.
652 //If not, this client will change to KS_CONNECTED_BUDDY when it connects.
653 if( m_nBuddyStatus
== Connected
) {
654 cur_client
->SetKadState(KS_NONE
);
658 case KS_QUEUED_BUDDY
:
659 //We are firewalled and want to request this client to be a buddy.
660 //But first we check to make sure we are not already trying another client.
661 //If we are not already trying. We try to connect to this client.
662 //If we are already connected to a buddy, we set this client to KS_NONE and it's removed next cycle.
663 //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.
664 if( m_nBuddyStatus
== Disconnected
) {
666 m_nBuddyStatus
= Connecting
;
667 cur_client
->SetKadState(KS_CONNECTING_BUDDY
);
668 cur_client
->TryToConnect(true);
669 Notify_ServerUpdateED2KInfo();
671 if( m_nBuddyStatus
== Connected
) {
672 cur_client
->SetKadState(KS_NONE
);
677 case KS_CONNECTING_BUDDY
:
678 //We are trying to connect to this client.
679 //Although it should NOT happen, we make sure we are not already connected to a buddy.
680 //If we are we set to KS_NONE and it's removed next cycle.
681 //But if we are not already connected, make sure we set the flag to connecting so we know
682 //things are working correctly.
683 if( m_nBuddyStatus
== Connected
) {
684 cur_client
->SetKadState(KS_NONE
);
686 wxASSERT( m_nBuddyStatus
== Connecting
);
691 case KS_CONNECTED_BUDDY
:
692 //A potential connected buddy client wanting to me in the Kad network
693 //We set our flag to connected to make sure things are still working correctly.
696 //If m_nBuddyStatus is not connected already, we set this client as our buddy!
697 if( m_nBuddyStatus
!= Connected
) {
698 m_pBuddy
= cur_client
;
699 m_nBuddyStatus
= Connected
;
700 Notify_ServerUpdateED2KInfo();
702 if( m_pBuddy
== cur_client
&& theApp
->IsFirewalled() && cur_client
->SendBuddyPingPong() ) {
703 cur_client
->SendBuddyPing();
708 RemoveFromKadList(cur_client
);
712 //We either never had a buddy, or lost our buddy..
713 if( buddy
== Disconnected
) {
714 if( m_nBuddyStatus
!= Disconnected
|| m_pBuddy
) {
715 if( Kademlia::CKademlia::IsRunning() && theApp
->IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true) ) {
716 //We are a lowID client and we just lost our buddy.
717 //Go ahead and instantly try to find a new buddy.
718 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
721 m_nBuddyStatus
= Disconnected
;
722 Notify_ServerUpdateED2KInfo();
726 if ( Kademlia::CKademlia::IsConnected() ) {
727 // we only need a buddy if direct callback is not available
728 if(Kademlia::CKademlia::IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true)) {
729 // TODO: Kad buddies won't work with RequireCrypt, so it is disabled for now, but should (and will)
730 // be fixed in later version
731 // Update: buddy connections themselves support obfuscation properly since eMule 0.49a and aMule SVN 2008-05-09
732 // (this makes it work fine if our buddy uses require crypt), however callback requests don't support it yet so we
733 // wouldn't be able to answer callback requests with RequireCrypt, protocolchange intended for eMule 0.49b
734 if(m_nBuddyStatus
== Disconnected
&& Kademlia::CKademlia::GetPrefs()->GetFindBuddy() && !thePrefs::IsClientCryptLayerRequired()) {
735 AddDebugLogLineN(logKadMain
, wxT("Starting BuddySearch"));
736 //We are a firewalled client with no buddy. We have also waited a set time
737 //to try to avoid a false firewalled status.. So lets look for a buddy..
738 if (!Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FINDBUDDY
, true, Kademlia::CUInt128(true).XOR(Kademlia::CKademlia::GetPrefs()->GetKadID()))) {
739 //This search ID was already going. Most likely reason is that
740 //we found and lost our buddy very quickly and the last search hadn't
741 //had time to be removed yet. Go ahead and set this to happen again
743 Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
748 //Lets make sure that if we have a buddy, they are firewalled!
749 //If they are also not firewalled, then someone must have fixed their firewall or stopped saturating their line..
750 //We just set the state of this buddy to KS_NONE and things will be cleared up with the next cycle.
751 if( !m_pBuddy
->HasLowID() ) {
752 m_pBuddy
->SetKadState(KS_NONE
);
758 //We are not connected anymore. Just set this buddy to KS_NONE and things will be cleared out on next cycle.
759 m_pBuddy
->SetKadState(KS_NONE
);
764 ProcessDirectCallbackList();
768 void CClientList::AddBannedClient(uint32 dwIP
)
770 m_bannedList
[dwIP
] = ::GetTickCount();
771 theStats::AddBannedClient();
775 bool CClientList::IsBannedClient(uint32 dwIP
)
777 ClientMap::iterator it
= m_bannedList
.find( dwIP
);
779 if ( it
!= m_bannedList
.end() ) {
780 if ( it
->second
+ CLIENTBANTIME
> ::GetTickCount() ) {
783 RemoveBannedClient(dwIP
);
790 void CClientList::RemoveBannedClient(uint32 dwIP
)
792 m_bannedList
.erase(dwIP
);
793 theStats::RemoveBannedClient();
797 void CClientList::FilterQueues()
799 // Filter client list
800 for ( IDMap::iterator it
= m_ipList
.begin(); it
!= m_ipList
.end(); ) {
801 IDMap::iterator tmp
= it
++; // Don't change this to a ++it!
803 if ( theApp
->ipfilter
->IsFiltered(tmp
->second
->GetConnectIP())) {
804 tmp
->second
->Disconnected(wxT("Filtered by IPFilter"));
805 tmp
->second
->Safe_Delete();
811 CClientList::SourceList
CClientList::GetClientsByHash( const CMD4Hash
& hash
)
815 // Find all items with the specified hash
816 std::pair
<HashMap::iterator
, HashMap::iterator
> range
= m_hashList
.equal_range( hash
);
818 for ( ; range
.first
!= range
.second
; ++range
.first
) {
819 results
.push_back( range
.first
->second
);
826 CClientList::SourceList
CClientList::GetClientsByIP( unsigned long ip
)
830 // Find all items with the specified hash
831 std::pair
<IDMap::iterator
, IDMap::iterator
> range
= m_ipList
.equal_range( ip
);
833 for ( ; range
.first
!= range
.second
; range
.first
++ ) {
834 results
.push_back( range
.first
->second
);
841 const CClientList::IDMap
& CClientList::GetClientList()
847 void CClientList::AddDeadSource(const CUpDownClient
* client
)
849 m_deadSources
.AddDeadSource( client
);
853 bool CClientList::IsDeadSource(const CUpDownClient
* client
)
855 return m_deadSources
.IsDeadSource( client
);
858 bool CClientList::SendChatMessage(uint64 client_id
, const wxString
& message
)
860 CUpDownClient
* client
= FindClientByIP(IP_FROM_GUI_ID(client_id
), PORT_FROM_GUI_ID(client_id
));
861 AddDebugLogLineN( logClient
, wxT("Trying to Send Message.") );
863 AddDebugLogLineN( logClient
, wxT("Sending.") );
865 AddDebugLogLineC( logClient
,
866 CFormat( wxT("No client (GUI_ID %lli [%s:%llu]) found in CClientList::SendChatMessage(). Creating") )
868 % Uint32toStringIP(IP_FROM_GUI_ID(client_id
))
869 % PORT_FROM_GUI_ID(client_id
) );
870 client
= new CUpDownClient(PORT_FROM_GUI_ID(client_id
),IP_FROM_GUI_ID(client_id
),0,0,NULL
, true, true);
873 return client
->SendChatMessage(message
);
876 void CClientList::SetChatState(uint64 client_id
, uint8 state
) {
877 CUpDownClient
* client
= FindClientByIP(IP_FROM_GUI_ID(client_id
), PORT_FROM_GUI_ID(client_id
));
879 client
->SetChatState(state
);
885 bool CClientList::RequestTCP(Kademlia::CContact
* contact
, uint8_t connectOptions
)
887 uint32_t nContactIP
= wxUINT32_SWAP_ALWAYS(contact
->GetIPAddress());
888 // don't connect ourself
889 if (theApp
->GetPublicIP() == nContactIP
&& thePrefs::GetPort() == contact
->GetTCPPort()) {
893 CUpDownClient
* pNewClient
= FindClientByIP(nContactIP
, contact
->GetTCPPort());
896 //#warning Do we actually have to check friendstate here?
897 pNewClient
= new CUpDownClient(contact
->GetTCPPort(), contact
->GetIPAddress(), 0, 0, NULL
, false, true);
898 } else if (pNewClient
->GetKadState() != KS_NONE
) {
899 return false; // already busy with this client in some way (probably buddy stuff), don't mess with it
902 //Add client to the lists to be processed.
903 pNewClient
->SetKadPort(contact
->GetUDPPort());
904 pNewClient
->SetKadState(KS_QUEUED_FWCHECK
);
905 if (contact
->GetClientID() != 0) {
907 contact
->GetClientID().ToByteArray(ID
);
908 pNewClient
->SetUserHash(CMD4Hash(ID
));
909 pNewClient
->SetConnectOptions(connectOptions
, true, false);
911 AddToKadList(pNewClient
); // This was a direct adding, but I like to check duplicates
912 //This method checks if this is a dup already.
913 AddClient(pNewClient
);
917 void CClientList::RequestBuddy(Kademlia::CContact
* contact
, uint8_t connectOptions
)
919 uint32_t nContactIP
= wxUINT32_SWAP_ALWAYS(contact
->GetIPAddress());
920 // Don't connect to ourself
921 if (theApp
->GetPublicIP() == nContactIP
&& thePrefs::GetPort() == contact
->GetTCPPort()) {
925 CUpDownClient
* pNewClient
= FindClientByIP(nContactIP
, contact
->GetTCPPort());
927 pNewClient
= new CUpDownClient(contact
->GetTCPPort(), contact
->GetIPAddress(), 0, 0, NULL
, false, true );
928 } else if (pNewClient
->GetKadState() != KS_NONE
) {
929 return; // already busy with this client in some way (probably fw stuff), don't mess with it
930 } else if (IsKadFirewallCheckIP(nContactIP
)) { // doing a kad firewall check with this IP, abort
931 AddDebugLogLineN(logKadMain
, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP
));
935 //Add client to the lists to be processed.
936 pNewClient
->SetKadPort(contact
->GetUDPPort());
937 pNewClient
->SetKadState(KS_QUEUED_BUDDY
);
939 contact
->GetClientID().ToByteArray(ID
);
940 pNewClient
->SetUserHash(CMD4Hash(ID
));
941 pNewClient
->SetConnectOptions(connectOptions
, true, false);
942 AddToKadList(pNewClient
);
943 //This method checks if this is a dup already.
944 AddClient(pNewClient
);
947 bool CClientList::IncomingBuddy(Kademlia::CContact
* contact
, Kademlia::CUInt128
* buddyID
)
949 uint32_t nContactIP
= wxUINT32_SWAP_ALWAYS(contact
->GetIPAddress());
950 //If aMule already knows this client, abort this.. It could cause conflicts.
951 //Although the odds of this happening is very small, it could still happen.
952 if (FindClientByIP(nContactIP
, contact
->GetTCPPort())) {
954 } else if (IsKadFirewallCheckIP(nContactIP
)) { // doing a kad firewall check with this IP, abort
955 AddDebugLogLineN(logKadMain
, wxT("Kad TCP firewallcheck / Buddy request collision for IP ") + Uint32toStringIP(nContactIP
));
959 if (theApp
->GetPublicIP() == nContactIP
&& thePrefs::GetPort() == contact
->GetTCPPort()) {
960 return false; // don't connect ourself
963 //Add client to the lists to be processed.
964 CUpDownClient
* pNewClient
= new CUpDownClient(contact
->GetTCPPort(), contact
->GetIPAddress(), 0, 0, NULL
, false, true );
965 pNewClient
->SetKadPort(contact
->GetUDPPort());
966 pNewClient
->SetKadState(KS_INCOMING_BUDDY
);
968 contact
->GetClientID().ToByteArray(ID
);
969 pNewClient
->SetUserHash(CMD4Hash(ID
));
970 buddyID
->ToByteArray(ID
);
971 pNewClient
->SetBuddyID(ID
);
972 AddToKadList(pNewClient
);
973 AddClient(pNewClient
);
977 void CClientList::RemoveFromKadList(CUpDownClient
* torem
)
979 wxCHECK_RET(torem
, wxT("NULL pointer in RemoveFromKadList"));
981 if (m_KadSources
.erase(torem
)) {
982 if(torem
== m_pBuddy
) {
984 m_nBuddyStatus
= Disconnected
;
985 Notify_ServerUpdateED2KInfo();
990 void CClientList::AddToKadList(CUpDownClient
* toadd
)
992 wxCHECK_RET(toadd
, wxT("NULL pointer in AddToKadList"));
994 m_KadSources
.insert(toadd
); // This will take care of duplicates.
997 bool CClientList::DoRequestFirewallCheckUDP(const Kademlia::CContact
& contact
)
999 // first make sure we don't know this IP already from somewhere
1000 if (IsIPAlreadyKnown(wxUINT32_SWAP_ALWAYS(contact
.GetIPAddress()))) {
1003 // fine, just create the client object, set the state and wait
1004 // TODO: We don't know the client's userhash, this means we cannot build an obfuscated connection, which
1005 // again mean that the whole check won't work on "Require Obfuscation" setting, which is not a huge problem,
1006 // but certainly not nice. Only somewhat acceptable way to solve this is to use the KadID instead.
1007 CUpDownClient
* pNewClient
= new CUpDownClient(contact
.GetTCPPort(), contact
.GetIPAddress(), 0, 0, NULL
, false, true);
1008 pNewClient
->SetKadState(KS_QUEUED_FWCHECK_UDP
);
1009 AddDebugLogLineN(logClient
, wxT("Selected client for UDP Firewallcheck: ") + KadIPToString(contact
.GetIPAddress()));
1010 AddToKadList(pNewClient
);
1011 AddClient(pNewClient
);
1012 wxASSERT(!pNewClient
->SupportsDirectUDPCallback());
1016 void CClientList::CleanUpClientList()
1018 // We remove clients which are not needed any more by time
1019 // this check is also done on CUpDownClient::Disconnected, however it will not catch all
1020 // cases (if a client changes the state without beeing connected
1022 // Adding this check directly to every point where any state changes would be more effective,
1023 // is however not compatible with the current code, because there are points where a client has
1024 // no state for some code lines and the code is also not prepared that a client object gets
1025 // invalid while working with it (aka setting a new state)
1026 // so this way is just the easy and safe one to go (as long as amule is basically single threaded)
1027 const uint32 cur_tick
= ::GetTickCount();
1028 if (m_dwLastClientCleanUp
+ CLIENTLIST_CLEANUP_TIME
< cur_tick
){
1029 m_dwLastClientCleanUp
= cur_tick
;
1030 DEBUG_ONLY( uint32 cDeleted
= 0; )
1031 IDMap::iterator current_it
= m_clientList
.begin();
1032 while (current_it
!= m_clientList
.end()) {
1033 CUpDownClient
* pCurClient
= current_it
->second
;
1034 ++current_it
; // Won't be used till while loop again
1035 // Don't delete sources coming from source seeds for 10 mins,
1036 // to give them a chance to connect and become a useful source.
1037 if (pCurClient
->GetSourceFrom() == SF_SOURCE_SEEDS
&& cur_tick
- (uint32
)theStats::GetStartTime() < MIN2MS(10)) continue;
1038 if ((pCurClient
->GetUploadState() == US_NONE
|| (pCurClient
->GetUploadState() == US_BANNED
&& !pCurClient
->IsBanned()))
1039 && pCurClient
->GetDownloadState() == DS_NONE
1040 && pCurClient
->GetChatState() == MS_NONE
1041 && pCurClient
->GetKadState() == KS_NONE
1042 && pCurClient
->GetSocket() == NULL
)
1044 DEBUG_ONLY( cDeleted
++; )
1045 pCurClient
->Disconnected(wxT("Removed during ClientList cleanup."));
1046 pCurClient
->Safe_Delete();
1049 if (!(pCurClient
->GetUploadState() == US_NONE
|| (pCurClient
->GetUploadState() == US_BANNED
&& !pCurClient
->IsBanned()))) {
1050 AddDebugLogLineN(logProxy
,
1051 CFormat(wxT("Debug: Not deleted client %x with up state: %i "))
1052 % (long int)pCurClient
% pCurClient
->GetUploadState());
1054 if (!(pCurClient
->GetDownloadState() == DS_NONE
)) {
1055 AddDebugLogLineN(logProxy
,
1056 CFormat(wxT("Debug: Not deleted client %x with down state: %i "))
1057 % (long int)pCurClient
% pCurClient
->GetDownloadState());
1059 if (!(pCurClient
->GetChatState() == MS_NONE
)) {
1060 AddDebugLogLineN(logProxy
,
1061 CFormat(wxT("Debug: Not deleted client %x with chat state: %i "))
1062 % (long int)pCurClient
% pCurClient
->GetChatState());
1064 if (!(pCurClient
->GetKadState() == KS_NONE
)) {
1065 AddDebugLogLineN(logProxy
,
1066 CFormat(wxT("Debug: Not deleted client %x with kad state: %i ip: %s"))
1067 % (long int)pCurClient
% pCurClient
->GetKadState() % pCurClient
->GetFullIP());
1069 if (!(pCurClient
->GetSocket() == NULL
)) {
1070 AddDebugLogLineN(logProxy
,
1071 CFormat(wxT("Debug: Not deleted client %x: has socket")) % (long int)pCurClient
);
1073 AddDebugLogLineN(logProxy
,
1074 CFormat(wxT("Debug: Not deleted client %x with kad version: %i"))
1075 % (long int)pCurClient
% pCurClient
->GetKadVersion());
1079 AddDebugLogLineN(logClient
, CFormat(wxT("Cleaned ClientList, removed %i not used known clients")) % cDeleted
);
1083 void CClientList::AddKadFirewallRequest(uint32 ip
)
1085 uint32 ticks
= ::GetTickCount();
1086 IpAndTicks add
= { ip
, ticks
};
1087 m_firewallCheckRequests
.push_front(add
);
1088 while (!m_firewallCheckRequests
.empty()) {
1089 if (ticks
- m_firewallCheckRequests
.back().inserted
> SEC2MS(180)) {
1090 m_firewallCheckRequests
.pop_back();
1097 bool CClientList::IsKadFirewallCheckIP(uint32 ip
) const
1099 uint32 ticks
= ::GetTickCount();
1100 for (IpAndTicksList::const_iterator it
= m_firewallCheckRequests
.begin(); it
!= m_firewallCheckRequests
.end(); ++it
) {
1101 if (it
->ip
== ip
&& ticks
- it
->inserted
< SEC2MS(180)) {
1108 void CClientList::AddDirectCallbackClient(CUpDownClient
* toAdd
)
1110 wxASSERT(toAdd
->GetDirectCallbackTimeout() != 0);
1111 if (toAdd
->HasBeenDeleted()) {
1114 for (DirectCallbackList::const_iterator it
= m_currentDirectCallbacks
.begin(); it
!= m_currentDirectCallbacks
.end(); ++it
) {
1116 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
1120 m_currentDirectCallbacks
.push_back(toAdd
);
1123 void CClientList::ProcessDirectCallbackList()
1125 // we do check if any direct callbacks have timed out by now
1126 const uint32_t cur_tick
= ::GetTickCount();
1127 for (DirectCallbackList::iterator it
= m_currentDirectCallbacks
.begin(); it
!= m_currentDirectCallbacks
.end();) {
1128 DirectCallbackList::iterator it2
= it
++;
1129 CUpDownClient
* curClient
= *it2
;
1130 if (curClient
->GetDirectCallbackTimeout() < cur_tick
) {
1131 wxASSERT(curClient
->GetDirectCallbackTimeout() != 0);
1133 //DebugLog(_T("DirectCallback timed out (%s)"), pCurClient->DbgGetClientInfo());
1134 m_currentDirectCallbacks
.erase(it2
);
1135 if (curClient
->Disconnected(wxT("Direct Callback Timeout"))) {
1136 curClient
->Safe_Delete();
1142 void CClientList::AddTrackCallbackRequests(uint32_t ip
)
1144 uint32_t now
= ::GetTickCount();
1145 IpAndTicks add
= { ip
, now
};
1146 m_directCallbackRequests
.push_front(add
);
1147 while (!m_directCallbackRequests
.empty()) {
1148 if (now
- m_directCallbackRequests
.back().inserted
> MIN2MS(3)) {
1149 m_directCallbackRequests
.pop_back();
1156 bool CClientList::AllowCallbackRequest(uint32_t ip
) const
1158 uint32_t now
= ::GetTickCount();
1159 for (IpAndTicksList::const_iterator it
= m_directCallbackRequests
.begin(); it
!= m_directCallbackRequests
.end(); ++it
) {
1160 if (it
->ip
== ip
&& now
- it
->inserted
< MIN2MS(3)) {
1166 // File_checked_for_headers