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  */
63 class CDeletedClient
64 {
65 public:
CDeletedClient(CUpDownClient * pClient)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 
CClientList()86 CClientList::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 
~CClientList()96 CClientList::~CClientList()
97 {
98 	DeleteContents(m_trackedClientsList);
99 
100 	wxASSERT(m_clientList.empty());
101 }
102 
103 
AddClient(CUpDownClient * toadd)104 void 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 
RemoveClient(CUpDownClient * client)131 void 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 
UpdateClientID(CUpDownClient * client,uint32 newID)144 void 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 
UpdateClientIP(CUpDownClient * client,uint32 newIP)158 void 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 
UpdateClientHash(CUpDownClient * client,const CMD4Hash & newHash)173 void 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 
RemoveIDFromList(CUpDownClient * client)190 bool 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 
RemoveIPFromList(CUpDownClient * client)212 void 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 
RemoveHashFromList(CUpDownClient * client)232 void 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 
FindMatchingClient(CUpDownClient * client)253 CUpDownClient* 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 
GetClientCount() const350 uint32 CClientList::GetClientCount() const
351 {
352 	return m_clientList.size();
353 }
354 
355 
DeleteAll()356 void 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 
AttachToAlreadyKnown(CUpDownClient ** client,CClientTCPSocket * sender)371 bool 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 
FindClientByIP(uint32 clientip,uint16 port)415 CUpDownClient* 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 
FindClientByIP(uint32 clientip)432 CUpDownClient* 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 
FindClientByECID(uint32 ecid) const441 CUpDownClient* 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 
IsIPAlreadyKnown(uint32_t ip)453 bool 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 
ComparePriorUserhash(uint32 dwIP,uint16 nPort,void * pNewHash)461 bool 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 
AddTrackClient(CUpDownClient * toadd)483 void 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 
GetClientsFromIP(uint32 dwIP)510 uint16 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 
Process()522 void 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) ^ (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 
AddBannedClient(uint32 dwIP)727 void CClientList::AddBannedClient(uint32 dwIP)
728 {
729 	m_bannedList[dwIP] = ::GetTickCount();
730 	theStats::AddBannedClient();
731 }
732 
733 
IsBannedClient(uint32 dwIP)734 bool 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 
RemoveBannedClient(uint32 dwIP)749 void CClientList::RemoveBannedClient(uint32 dwIP)
750 {
751 	m_bannedList.erase(dwIP);
752 	theStats::RemoveBannedClient();
753 }
754 
755 
FilterQueues()756 void 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 
GetClientsByHash(const CMD4Hash & hash)770 CClientList::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 
GetClientsByIP(unsigned long ip)785 CClientList::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 
GetClientList()800 const CClientList::IDMap& CClientList::GetClientList()
801 {
802 	return m_clientList;
803 }
804 
805 
AddDeadSource(const CUpDownClient * client)806 void CClientList::AddDeadSource(const CUpDownClient* client)
807 {
808 	m_deadSources.AddDeadSource( client );
809 }
810 
811 
IsDeadSource(const CUpDownClient * client)812 bool CClientList::IsDeadSource(const CUpDownClient* client)
813 {
814 	return m_deadSources.IsDeadSource( client );
815 }
816 
SendChatMessage(uint64 client_id,const wxString & message)817 bool 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 
SetChatState(uint64 client_id,uint8 state)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);
839 	}
840 }
841 
842 /* Kad stuff */
843 
RequestTCP(Kademlia::CContact * contact,uint8_t connectOptions)844 bool 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 
RequestBuddy(Kademlia::CContact * contact,uint8_t connectOptions)876 void 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 
IncomingBuddy(Kademlia::CContact * contact,Kademlia::CUInt128 * buddyID)906 bool 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 
RemoveFromKadList(CUpDownClient * torem)936 void 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 
AddToKadList(CUpDownClient * toadd)949 void 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 
DoRequestFirewallCheckUDP(const Kademlia::CContact & contact)956 bool 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 
CleanUpClientList()975 void 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 
AddKadFirewallRequest(uint32 ip)1042 void 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 
IsKadFirewallCheckIP(uint32 ip) const1056 bool 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 
AddDirectCallbackClient(CUpDownClient * toAdd)1067 void 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 
ProcessDirectCallbackList()1082 void 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 
AddTrackCallbackRequests(uint32_t ip)1101 void 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 
AllowCallbackRequest(uint32_t ip) const1115 bool 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 
GetBuddyIP()1126 uint32 CClientList::GetBuddyIP()
1127 {
1128 	return GetBuddy()->GetIP();
1129 }
1130 
GetBuddyPort()1131 uint16 CClientList::GetBuddyPort()
1132 {
1133 	return GetBuddy()->GetUDPPort();
1134 }
1135 
1136 
1137 // File_checked_for_headers
1138