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.
10//
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.
15//
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
24//
25
26#include "ClientList.h"		// Interface declarations.
27
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>
33
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"
45
46#include <common/Format.h>
47
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"
53
54
55/**
56 * CDeletedClient Class
57 *
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.
62 */
63class CDeletedClient
64{
65public:
66	CDeletedClient(CUpDownClient* pClient)
67	{
68		m_dwInserted = ::GetTickCount();
69		PortAndHash porthash = { pClient->GetUserPort(), pClient->GetCreditsHash()};
70		m_ItemsList.push_back(porthash);
71	}
72
73	struct PortAndHash
74	{
75		uint16 nPort;
76		void* pHash;
77	};
78
79	typedef std::list<PortAndHash> PaHList;
80	PaHList	m_ItemsList;
81	uint32	m_dwInserted;
82};
83
84
85
86CClientList::CClientList()
87	: m_deadSources( true )
88{
89	m_dwLastBannCleanUp = 0;
90	m_dwLastTrackedCleanUp = 0;
91	m_dwLastClientCleanUp = 0;
92	m_nBuddyStatus = Disconnected;
93}
94
95
96CClientList::~CClientList()
97{
98	DeleteContents(m_trackedClientsList);
99
100	wxASSERT(m_clientList.empty());
101}
102
103
104void CClientList::AddClient( CUpDownClient* toadd )
105{
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;
110
111 		//Notify_ClientCtrlAddClient( toadd );
112
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"))) );
115
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")) ) );
119		}
120
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")) ) );
124		}
125
126		toadd->UpdateStats();
127	}
128}
129
130
131void CClientList::RemoveClient(CUpDownClient* client)
132{
133	RemoveFromKadList( client );
134	RemoveDirectCallback( client );
135
136	if ( RemoveIDFromList( client ) ) {
137		// Also remove the ip and hash entries
138		RemoveIPFromList( client );
139		RemoveHashFromList( client );
140	}
141}
142
143
144void CClientList::UpdateClientID( CUpDownClient* client, uint32 newID )
145{
146	// Sanity check
147	if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserIDHybrid() == newID ) )
148		return;
149
150	// First remove the ID entry
151	RemoveIDFromList( client );
152
153	// Add the new entry
154	m_clientList.insert( IDMapPair( newID, CCLIENTREF(client, wxT("CClientList::UpdateClientID")) ) );
155}
156
157
158void CClientList::UpdateClientIP( CUpDownClient* client, uint32 newIP )
159{
160	// Sanity check
161	if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetIP() == newIP ) )
162		return;
163
164	// Remove the old IP entry
165	RemoveIPFromList( client );
166
167	if ( newIP ) {
168		m_ipList.insert( IDMapPair( newIP, CCLIENTREF(client, wxT("CClientList::UpdateClientIP")) ) );
169	}
170}
171
172
173void CClientList::UpdateClientHash( CUpDownClient* client, const CMD4Hash& newHash )
174{
175	// Sanity check
176	if ( ( client->GetClientState() != CS_LISTED ) || ( client->GetUserHash() == newHash ) )
177		return;
178
179
180	// Remove the old entry
181	RemoveHashFromList( client );
182
183	// And add the new one if valid
184	if ( !newHash.IsEmpty() ) {
185		m_hashList.insert( HashMapPair( newHash, CCLIENTREF(client, wxT("CClientList::UpdateClientHash")) ) );
186	}
187}
188
189
190bool CClientList::RemoveIDFromList( CUpDownClient* client )
191{
192	bool result = false;
193
194	// First remove the ID entry
195	std::pair<IDMap::iterator, IDMap::iterator> range = m_clientList.equal_range( client->GetUserIDHybrid() );
196
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;
203
204			break;
205		}
206	}
207
208	return result;
209}
210
211
212void CClientList::RemoveIPFromList( CUpDownClient* client )
213{
214	// Check if we need to look for the IP entry
215	if ( !client->GetIP() ) {
216		return;
217	}
218
219	// Remove the IP entry
220	std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( client->GetIP() );
221
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;
228		}
229	}
230}
231
232void CClientList::RemoveHashFromList( CUpDownClient* client )
233{
234	// Nothing to remove
235	if ( !client->HasValidHash() ) {
236		return;
237	}
238
239	// Find all items with the specified hash
240	std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( client->GetUserHash() );
241
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;
248		}
249	}
250}
251
252
253CUpDownClient* CClientList::FindMatchingClient( CUpDownClient* client )
254{
255	typedef std::pair<IDMap::const_iterator, IDMap::const_iterator> IDMapIteratorPair;
256	wxCHECK(client, NULL);
257
258	const uint32 userIP = client->GetIP();
259	const uint32 userID = client->GetUserIDHybrid();
260	const uint16 userPort = client->GetUserPort();
261	const uint16 userKadPort = client->GetKadPort();
262
263
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);
269
270			for ( ; range.first != range.second; ++range.first ) {
271				CUpDownClient* other = range.first->second.GetClient();
272				wxASSERT(userIP == other->GetIP());
273
274				if (userPort && (userPort == other->GetUserPort())) {
275					return other;
276				  } else if (userKadPort && (userKadPort == other->GetKadPort())) {
277					return other;
278				}
279			}
280		}
281
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);
286
287			for (; range.first != range.second; ++range.first) {
288				CUpDownClient* other = range.first->second.GetClient();
289				wxASSERT(userID == other->GetUserIDHybrid());
290
291				// For lowid, we also have to check the server
292				if (serverIP == other->GetServerIP()) {
293					if (serverPort == other->GetServerPort()) {
294						return other;
295					}
296				}
297			}
298		}
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 }
303		};
304
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;
309			}
310
311			IDMapIteratorPair range = toCheck[i].map.equal_range(toCheck[i].value);
312
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();
318					}
319				}
320			}
321
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();
327					}
328				}
329			}
330		}
331	}
332
333
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() );
338
339		// Just return the first item if any
340		if ( range.first != range.second ) {
341			return range.first->second.GetClient();
342		}
343	}
344
345	// Nothing found, must be a new client
346	return NULL;
347}
348
349
350uint32 CClientList::GetClientCount() const
351{
352	return m_clientList.size();
353}
354
355
356void CClientList::DeleteAll()
357{
358	m_ipList.clear();
359	m_hashList.clear();
360
361	while ( !m_clientList.empty() ) {
362		IDMap::iterator it = m_clientList.begin();
363
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();
367	}
368}
369
370
371bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientTCPSocket* sender)
372{
373	CUpDownClient* tocheck = (*client);
374
375	CUpDownClient* found_client = FindMatchingClient( tocheck );
376
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;
380	}
381
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() ) )
387				{
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;
393					}
394
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;
399				}
400				found_client->GetSocket()->Safe_Delete();
401			}
402			found_client->SetSocket( sender );
403			tocheck->SetSocket( NULL );
404		}
405		*client = 0;
406		tocheck->Safe_Delete();
407		*client = found_client;
408		return true;
409	}
410
411	return false;
412}
413
414
415CUpDownClient* CClientList::FindClientByIP( uint32 clientip, uint16 port )
416{
417	// Find all items with the specified ip
418	std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
419
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;
425		}
426	}
427
428	return NULL;
429}
430
431
432CUpDownClient* CClientList::FindClientByIP( uint32 clientip )
433{
434	// Find all items with the specified ip
435	std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( clientip );
436
437	return (range.first != range.second) ? range.first->second.GetClient() : NULL;
438}
439
440
441CUpDownClient* CClientList::FindClientByECID(uint32 ecid) const
442{
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();
446		}
447	}
448
449	return NULL;
450}
451
452
453bool CClientList::IsIPAlreadyKnown(uint32_t ip)
454{
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;
458}
459
460
461bool CClientList::ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash)
462{
463	std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
464
465	if ( it != m_trackedClientsList.end() ) {
466		CDeletedClient* pResult = it->second;
467
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;
475				}
476			}
477		}
478	}
479	return true;
480}
481
482
483void CClientList::AddTrackClient(CUpDownClient* toadd)
484{
485	std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( toadd->GetIP() );
486
487	if ( it != m_trackedClientsList.end() ) {
488		CDeletedClient* pResult = it->second;
489
490		pResult->m_dwInserted = ::GetTickCount();
491
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;
498			}
499		}
500
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);
506	}
507}
508
509
510uint16 CClientList::GetClientsFromIP(uint32 dwIP)
511{
512	std::map<uint32, CDeletedClient*>::iterator it = m_trackedClientsList.find( dwIP );
513
514	if ( it != m_trackedClientsList.end() ) {
515		return it->second->m_ItemsList.size();
516	} else {
517		return 0;
518	}
519}
520
521
522void CClientList::Process()
523{
524	const uint32 cur_tick = ::GetTickCount();
525
526	if (m_dwLastBannCleanUp + BAN_CLEANUP_TIME < cur_tick) {
527		m_dwLastBannCleanUp = cur_tick;
528
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++;
533
534				m_bannedList.erase( tmp );
535				theStats::RemoveBannedClient();
536			} else {
537				++it;
538			}
539		}
540	}
541
542
543	if ( m_dwLastTrackedCleanUp + TRACKED_CLEANUP_TIME < cur_tick ) {
544		m_dwLastTrackedCleanUp = cur_tick;
545
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++;
549
550			if ( cur_src->second->m_dwInserted + KEEPTRACK_TIME < cur_tick ) {
551				delete cur_src->second;
552				m_trackedClientsList.erase( cur_src );
553			}
554		}
555	}
556
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.
561
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;
564
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);
573		}
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;
580
581			case KS_CONNECTING_FWCHECK:
582				//Ignore this state as we are just waiting for results.
583				break;
584
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;
589
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);
603				}
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;
607
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);
614				}
615				break;
616
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);
632					}
633				}
634				break;
635
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;
647				}
648				break;
649
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;
654
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();
660				}
661				if( m_pBuddy.GetClient() == cur_client && theApp->IsFirewalled() && cur_client->SendBuddyPingPong() ) {
662					cur_client->SendBuddyPing();
663				}
664				break;
665
666			default:
667				RemoveFromKadList(cur_client);
668		}
669	}
670
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();
678			}
679			m_pBuddy.Unlink();
680			m_nBuddyStatus = Disconnected;
681			Notify_ServerUpdateED2KInfo();
682		}
683	}
684
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).XOR(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();
703				}
704			}
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);
712				}
713			}
714		}
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);
719		}
720	}
721
722	CleanUpClientList();
723	ProcessDirectCallbackList();
724}
725
726
727void CClientList::AddBannedClient(uint32 dwIP)
728{
729	m_bannedList[dwIP] = ::GetTickCount();
730	theStats::AddBannedClient();
731}
732
733
734bool CClientList::IsBannedClient(uint32 dwIP)
735{
736	ClientMap::iterator it = m_bannedList.find( dwIP );
737
738	if ( it != m_bannedList.end() ) {
739		if ( it->second + CLIENTBANTIME > ::GetTickCount() ) {
740			return true;
741		} else {
742			RemoveBannedClient(dwIP);
743		}
744	}
745	return false;
746}
747
748
749void CClientList::RemoveBannedClient(uint32 dwIP)
750{
751	m_bannedList.erase(dwIP);
752	theStats::RemoveBannedClient();
753}
754
755
756void CClientList::FilterQueues()
757{
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();
765		}
766	}
767}
768
769
770CClientList::SourceList	CClientList::GetClientsByHash( const CMD4Hash& hash )
771{
772	SourceList results;
773
774	// Find all items with the specified hash
775	std::pair<HashMap::iterator, HashMap::iterator> range = m_hashList.equal_range( hash );
776
777	for ( ; range.first != range.second; ++range.first)  {
778		results.push_back( range.first->second );
779	}
780
781	return results;
782}
783
784
785CClientList::SourceList	CClientList::GetClientsByIP( unsigned long ip )
786{
787	SourceList results;
788
789	// Find all items with the specified hash
790	std::pair<IDMap::iterator, IDMap::iterator> range = m_ipList.equal_range( ip );
791
792	for ( ; range.first != range.second; range.first++ )  {
793		results.push_back( range.first->second );
794	}
795
796	return results;
797}
798
799
800const CClientList::IDMap& CClientList::GetClientList()
801{
802	return m_clientList;
803}
804
805
806void CClientList::AddDeadSource(const CUpDownClient* client)
807{
808	m_deadSources.AddDeadSource( client );
809}
810
811
812bool CClientList::IsDeadSource(const CUpDownClient* client)
813{
814	return m_deadSources.IsDeadSource( client );
815}
816
817bool CClientList::SendChatMessage(uint64 client_id, const wxString& message)
818{
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);
831	}
832	return client->SendChatMessage(message);
833}
834
835void 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);
839	}
840}
841
842/* Kad stuff */
843
844bool CClientList::RequestTCP(Kademlia::CContact* contact, uint8_t connectOptions)
845{
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;
850	}
851
852	CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
853
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
859	}
860
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);
869	}
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;
874}
875
876void CClientList::RequestBuddy(Kademlia::CContact* contact, uint8_t connectOptions)
877{
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;
882	}
883
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;
892	}
893
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);
904}
905
906bool CClientList::IncomingBuddy(Kademlia::CContact* contact, Kademlia::CUInt128* buddyID)
907{
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;
916	}
917
918	if (theApp->GetPublicIP() == nContactIP && thePrefs::GetPort() == contact->GetTCPPort()) {
919		return false; // don't connect ourself
920	}
921
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;
934}
935
936void CClientList::RemoveFromKadList(CUpDownClient* torem)
937{
938	wxCHECK_RET(torem, wxT("NULL pointer in RemoveFromKadList"));
939
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();
945		}
946	}
947}
948
949void CClientList::AddToKadList(CUpDownClient* toadd)
950{
951	wxCHECK_RET(toadd, wxT("NULL pointer in AddToKadList"));
952
953	m_KadSources.insert(CCLIENTREF(toadd, wxT("CClientList::AddToKadList"))); // This will take care of duplicates.
954}
955
956bool CClientList::DoRequestFirewallCheckUDP(const Kademlia::CContact& contact)
957{
958	// first make sure we don't know this IP already from somewhere
959	if (IsIPAlreadyKnown(wxUINT32_SWAP_ALWAYS(contact.GetIPAddress()))) {
960		return false;
961	}
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;
973}
974
975void CClientList::CleanUpClientList()
976{
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
980	//
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)
1002			{
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());
1012				}
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());
1017				}
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());
1022				}
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());
1027				}
1028				if (!(pCurClient->GetSocket() == NULL)) {
1029					AddDebugLogLineN(logProxy,
1030						CFormat(wxT("Debug: Not deleted client %x: has socket")) % (long int)pCurClient);
1031				}
1032				AddDebugLogLineN(logProxy,
1033					CFormat(wxT("Debug: Not deleted client %x with kad version: %i"))
1034						% (long int)pCurClient % pCurClient->GetKadVersion());
1035#endif
1036			}
1037		}
1038		AddDebugLogLineN(logClient, CFormat(wxT("Cleaned ClientList, removed %i not used known clients")) % cDeleted);
1039	}
1040}
1041
1042void CClientList::AddKadFirewallRequest(uint32 ip)
1043{
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;
1052		}
1053	}
1054}
1055
1056bool CClientList::IsKadFirewallCheckIP(uint32 ip) const
1057{
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;
1062		}
1063	}
1064	return false;
1065}
1066
1067void CClientList::AddDirectCallbackClient(CUpDownClient* toAdd)
1068{
1069	wxASSERT(toAdd->GetDirectCallbackTimeout() != 0);
1070	if (toAdd->HasBeenDeleted()) {
1071		return;
1072	}
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;
1077		}
1078	}
1079	m_currentDirectCallbacks.push_back(CCLIENTREF(toAdd, wxT("CClientList::AddDirectCallbackClient")));
1080}
1081
1082void CClientList::ProcessDirectCallbackList()
1083{
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();
1096			}
1097		}
1098	}
1099}
1100
1101void CClientList::AddTrackCallbackRequests(uint32_t ip)
1102{
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;
1111		}
1112	}
1113}
1114
1115bool CClientList::AllowCallbackRequest(uint32_t ip) const
1116{
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;
1121		}
1122	}
1123	return true;
1124}
1125
1126uint32 CClientList::GetBuddyIP()
1127{
1128	return GetBuddy()->GetIP();
1129}
1130
1131uint16 CClientList::GetBuddyPort()
1132{
1133	return GetBuddy()->GetUDPPort();
1134}
1135
1136
1137// File_checked_for_headers
1138