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