BOOL CManagedSearch::ExecuteDonkeyMesh(const DWORD /*tTicks*/, const DWORD tSecs) { ASSUME_LOCK( SearchManager.m_pSection ); CQuickLock oLock( HostCache.eDonkey.m_pSection ); for ( CHostCacheIterator i = HostCache.eDonkey.Begin() ; i != HostCache.eDonkey.End() ; ++i ) { CHostCacheHostPtr pHost = (*i); ASSERT( pHost->m_nProtocol == PROTOCOL_ED2K ); // If this host is a neighbour, don't UDP to it if ( Neighbours.Get( pHost->m_pAddress ) ) continue; // Make sure this host can be queried (now) if ( ! pHost->CanQuery( tSecs ) ) continue; // Never requery eDonkey2000 servers DWORD tLastQuery; if ( m_pNodes.Lookup( pHost->m_pAddress.s_addr, tLastQuery ) ) continue; // Set the last query time for this host for this search m_pNodes.SetAt( pHost->m_pAddress.s_addr, tSecs ); // Record the query time on the host, for all searches pHost->m_tQuery = tSecs; // Create a packet in the appropriate format if ( CPacket* pPacket = m_pSearch->ToEDPacket( TRUE, pHost->m_nUDPFlags ) ) { // Send the datagram if possible if ( Datagrams.Send( &pHost->m_pAddress, pHost->m_nPort + 4, pPacket, TRUE ) ) { theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Sending UDP query to %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ) ); return TRUE; } } } return FALSE; }
// Takes a GUID, and a neighbour to except from the packet we will make // Makes a Gnutella2 query web packet, containing the IP addresses of the computers we are connected to and from the Gnutella2 host cache CG2Packet* CNeighboursWithG2::CreateQueryWeb(const Hashes::Guid& oGUID, bool bWithHubs, CNeighbour* pExcept, bool bDone) { // Make a new Gnutella2 Query Ack packet CG2Packet* pPacket = CG2Packet::New( G2_PACKET_QUERY_ACK, TRUE ); if ( ! pPacket ) return NULL; // Start it with the text "TS" and the time now const DWORD tNow = static_cast< DWORD >( time( NULL ) ); // Number of seconds since 1970 pPacket->WritePacket( G2_PACKET_TIMESTAMP, 4 ); pPacket->WriteLongBE( tNow ); // Write in header information about us pPacket->WritePacket( G2_PACKET_FROM_ADDRESS, 4 ); pPacket->WriteLongLE( Network.m_pHost.sin_addr.S_un.S_addr ); pPacket->WritePacket( G2_PACKET_RETRY_AFTER, 4 ); pPacket->WriteLongBE( Settings.Gnutella2.QueryThrottle ); if ( bDone ) { pPacket->WritePacket( G2_PACKET_QUERY_DONE, 8 ); pPacket->WriteLongLE( Network.m_pHost.sin_addr.S_un.S_addr ); pPacket->WriteShortBE( htons( Network.m_pHost.sin_port ) ); if ( bWithHubs ) { const WORD nLeafs = (WORD)GetCount( PROTOCOL_G2, nrsConnected, ntLeaf ); pPacket->WriteShortBE( nLeafs ); // Loop through the connected computers for ( POSITION pos = GetIterator() ; pos ; ) { // Get the neighbour object at this position, and move pos to the next one CG2Neighbour* pNeighbour = (CG2Neighbour*)GetNext( pos ); // If this neighbour is running Gnutella2 software if ( pNeighbour->m_nProtocol == PROTOCOL_G2 && // The remote computer is running Gnutella2 software, and pNeighbour->m_nNodeType != ntLeaf && // Our connection to it is not down to a leaf, and pNeighbour->m_nState >= nrsConnected && // We've finished the handshake with it, and pNeighbour != pExcept ) // This isn't the computer the caller warned us to except { // Write information about this connected computer into the packet pPacket->WritePacket( G2_PACKET_QUERY_DONE, 8 ); pPacket->WriteLongLE( pNeighbour->m_pHost.sin_addr.S_un.S_addr ); pPacket->WriteShortBE( htons( pNeighbour->m_pHost.sin_port ) ); pPacket->WriteShortBE( (WORD)pNeighbour->m_nLeafCount ); } } // Will put up to 3 or 25 IP addresses in the packet: // If caller didn't give us a computer to ignore make nCount 3, if it did give us an except make nCount 25 int nCount = ( pExcept == NULL ) ? 3 : 25; CQuickLock oLock( HostCache.Gnutella2.m_pSection ); // Loop, starting with the newest entry in the Gnutella2 host cache, then stepping to the one before that for ( CHostCacheIterator i = HostCache.Gnutella2.Begin() ; i != HostCache.Gnutella2.End() ; ++i ) { CHostCacheHostPtr pHost = (*i); // If this host cache entry is good if ( pHost->CanQuote( tNow ) && // If this host cache entry hasn't expired, and Get( pHost->m_pAddress ) == NULL && // We're connected to that IP address right now, and HubHorizonPool.Find( &pHost->m_pAddress ) == NULL ) // The IP address is also in the hub horizon pool { // Add the IP address to the packet we're making, to encourage recipient to try this address pPacket->WritePacket( G2_PACKET_QUERY_SEARCH, 10 ); pPacket->WriteLongLE( pHost->m_pAddress.S_un.S_addr ); pPacket->WriteShortBE( pHost->m_nPort ); pPacket->WriteLongBE( pHost->Seen() ); // Lower the count, if it is then 0, leave the loop if ( ! --nCount ) break; } } // Give the packet we're making to our own hub horizon pool HubHorizonPool.AddHorizonHubs( pPacket ); } else // No hubs { pPacket->WriteShortBE( 0 ); } } // Finish the packet with a 0 byte and the guid the caller gave us, and return it pPacket->WriteByte( 0 ); pPacket->Write( oGUID ); return pPacket; }
void CEDClients::RunGlobalStatsRequests(DWORD tNow) { // Don't send stat requests or time out servers if we're not stable if ( Network.IsFirewalled(CHECK_UDP) ) return; if ( m_nLastServerKey != 0 ) { // We are waiting for a response if ( tNow > m_tLastServerStats + Settings.Connection.TimeoutHandshake ) { // Timed out m_nLastServerKey = 0; theApp.Message( MSG_DEBUG, _T("Time-out waiting for ed2k server status") ); CQuickLock pLock( HostCache.eDonkey.m_pSection ); CHostCacheHostPtr pHost = HostCache.eDonkey.Find( &m_pLastServer ); if ( pHost ) { pHost->m_tFailure = pHost->m_tStats; pHost->m_nFailures ++; // If we've had multiple failures, remove the host if ( pHost->m_nFailures > 3 ) { theApp.Message( MSG_INFO, _T("Removing ed2k server %s"), pHost->m_sName ); HostCache.eDonkey.Remove( pHost ); } } // Reset the timer so we query another server right away // m_tLastServerStats = 0; } } if ( tNow > m_tLastServerStats + Settings.eDonkey.StatsGlobalThrottle ) // Limit requests to every 30 minutes { // Get the current time (in seconds) const DWORD tSecs = static_cast< DWORD >( time( NULL ) ); CQuickLock oLock( HostCache.eDonkey.m_pSection ); // Loop through servers in the host cache for ( CHostCacheIterator i = HostCache.eDonkey.Begin() ; i != HostCache.eDonkey.End() ; ++i ) { CHostCacheHostPtr pHost = (*i); //CString str; //str.Format( _T(" -Name:%s Last Stats:%d UDP flags:%08X"), pHost->m_sName, pHost->m_tStats, pHost->m_nUDPFlags ); //theApp.Message( MSG_INFO, str ); // Check if this server could be asked for stats if ( ( pHost->CanQuery( tSecs ) ) && // If it hasn't been searched recently ( ( tSecs > pHost->m_tStats + Settings.eDonkey.StatsServerThrottle ) || // AND we have not checked this host in a week ( ( pHost->m_nFailures > 0 ) && ( tSecs > pHost->m_tStats + 8*60*60 ) ) ) && // OR last check failed, have not checked in 8 hours ( pHost->m_nUDPFlags == 0 || m_bAllServersDone ) ) // AND it has no flags set OR we have checked all servers { CSingleLock pLock( &Network.m_pSection ); if ( ! pLock.Lock( 200 ) ) continue; // Don't ask current neighbours for stats if ( ! Neighbours.Get( pHost->m_pAddress ) ) { // Send a request for stats to this server if ( ! pHost->m_sName.IsEmpty() ) theApp.Message( MSG_INFO, _T("Sending status request to ed2k server %s"), pHost->m_sName ); else theApp.Message( MSG_INFO, _T("Sending status request to ed2k server %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ) ); RequestServerStatus( &pHost->m_pAddress, pHost->m_nPort ); pHost->m_tStats = tSecs; m_tLastServerStats = tNow; m_pLastServer = pHost->m_pAddress; return; } } } m_bAllServersDone = TRUE; // We have checked all known servers, we may go back and re-query any that didn't respond. m_tLastServerStats = tNow; // Try again later. (we don't want to keep running this section, it's a little slow) } }
BOOL CEDClients::OnServerStatus(const SOCKADDR_IN* pHost, CEDPacket* pPacket) { DWORD nLen = pPacket->GetRemaining(); if ( nLen < 4 ) return FALSE; // Read in and check the key value to make sure we requested this update DWORD nKey = pPacket->ReadLongLE(); CQuickLock oLock( HostCache.eDonkey.m_pSection ); CHostCacheHostPtr pServer = HostCache.eDonkey.Find( &pHost->sin_addr ); if ( pServer == NULL ) { theApp.Message( MSG_WARNING, _T("eDonkey server %s:%u status received, but server not found in host cache"), (LPCTSTR)CString( inet_ntoa( pHost->sin_addr ) ), htons( pHost->sin_port ) ); return FALSE; } if ( pServer->m_nKeyValue != nKey ) { theApp.Message( MSG_WARNING, _T("eDonkey server %s:%u status received, but server key does not match"), (LPCTSTR)CString( inet_ntoa( pHost->sin_addr ) ), htons( pHost->sin_port ) ); return FALSE; } // Assume UDP is stable Datagrams.SetStable(); // Read in the status packet DWORD nUsers = 0, nFiles = 0, nMaxUsers = 0, nFileLimit = 1000, nUDPFlags = 0; if ( nLen >= 12 ) { // Current users and files indexed nUsers = pPacket->ReadLongLE(); nFiles = pPacket->ReadLongLE(); } if ( nLen >= 16 ) { // Maximum users allowed nMaxUsers = pPacket->ReadLongLE(); } if ( nLen >= 24 ) { // Client file limit. (Maximum files you can send to the server) nFileLimit = pPacket->ReadLongLE(); // Soft limit. (Files over this are ignored) pPacket->ReadLongLE(); // 'Hard' limit. (Obey previous, it saves bandwidth) } if ( nLen >= 28 ) { // UDP Flags. (This is important, it determines search types, etc) nUDPFlags = pPacket->ReadLongLE(); } if ( nLen >= 32 ) { // Low ID users. pPacket->ReadLongLE(); // We don't use this } if ( nLen >= 40 ) { // UDP Obfuscation Port pPacket->ReadShortLE(); // We don't use this // TCP Obfuscation Port pPacket->ReadShortLE(); // We don't use this // Server Key pPacket->ReadLongLE(); // We don't use this } // Update the server variables pServer->m_tAck = 0; pServer->m_nFailures = 0; pServer->m_tFailure = 0; pServer->m_bCheckedLocally = TRUE; pServer->m_nUserCount = nUsers; pServer->m_nUserLimit = nMaxUsers; pServer->m_nFileLimit = nFileLimit; pServer->m_nUDPFlags = nUDPFlags; if ( pServer->Seen() < pServer->m_tStats ) HostCache.eDonkey.Update( pServer, 0, pServer->m_tStats ); if ( nUDPFlags & ED2K_SERVER_UDP_UNICODE ) pServer->m_nTCPFlags |= ED2K_SERVER_TCP_UNICODE; if ( nUDPFlags & ED2K_SERVER_UDP_GETSOURCES2 ) pServer->m_nTCPFlags |= ED2K_SERVER_TCP_GETSOURCES2; HostCache.eDonkey.Update( pServer ); theApp.Message( MSG_DEBUG, _T("eDonkey server %s:%u UDP flags: %s"), (LPCTSTR)CString( inet_ntoa( pHost->sin_addr ) ), htons( pHost->sin_port ), GetED2KServerUDPFlags( nUDPFlags ) ); return TRUE; }
// As the program runs, CNetwork::OnRun calls this method periodically and repeatedly // Counts how many connections we have for each network and in each role, and connects to more from the host cache or disconnects from some void CNeighboursWithConnect::Maintain() { // Get the time const DWORD tTimer = GetTickCount(); // Time in ticks (milliseconds) const DWORD tNow = static_cast< DWORD >( time( NULL ) ); // Time in seconds // Don't initiate neighbour connections too quickly if connections are limited if ( Settings.Connection.ConnectThrottle && tTimer >= m_tLastConnect && tTimer <= m_tLastConnect + Settings.Connection.ConnectThrottle ) return; DWORD nCount[ PROTOCOL_LAST ][3] = {}, nLimit[ PROTOCOL_LAST ][3] = {}; // Loop down the list of connected neighbours, sorting each by network and role and counting it // Also prune leaf to leaf connections, which shouldn't happen for ( POSITION pos = GetIterator() ; pos ; ) { // Get the next neighbour in the list CNeighbour* pNeighbour = GetNext( pos ); // We're done with the handshake and connected to this remote computer if ( pNeighbour->m_nState == nrsConnected ) { if ( pNeighbour->m_nNodeType != ntHub && m_bG2Leaf && pNeighbour->m_nProtocol == PROTOCOL_G2 ) { // We're both Gnutella2 leaves: // Two leaves shouldn't connect, disconnect pNeighbour->Close( IDS_CONNECTION_PEERPRUNE ); } else if ( pNeighbour->m_nNodeType != ntHub && m_bG1Leaf && pNeighbour->m_nProtocol == PROTOCOL_G1 ) { // We're both Gnutella leaves: // Two leaves shouldn't connect, disconnect pNeighbour->Close( IDS_CONNECTION_PEERPRUNE ); } else if ( pNeighbour->m_nNodeType != ntLeaf ) { // This connection is to a hub above us, or a hub just like us: // Count one more hub for this connection's protocol only if we've been connected for several seconds. if ( tTimer > pNeighbour->m_tConnected + 8000 ) nCount[ pNeighbour->m_nProtocol ][ ntHub ]++; } else { // We must be a hub, and this connection must be down to a leaf: // Count one more leaf for this connection's protocol nCount[ pNeighbour->m_nProtocol ][ ntLeaf ]++; } } else if ( pNeighbour->m_nState < nrsConnected ) { // We're still going through the handshake with this remote computer // Count one more connection in the 0 column for this protocol nCount[ pNeighbour->m_nProtocol ][ ntNode ]++; // ntNode is 0 } } // Set our "promoted to hub" timer if ( ! m_bG2Hub ) m_tHubG2Promotion = 0; // If we're not a hub, time promoted is 0 else if ( m_tHubG2Promotion == 0 ) m_tHubG2Promotion = tNow; // If we've just been promoted, set the timer // Check if we have verified if we make a good G2 hub if ( ! Settings.Gnutella2.HubVerified && m_tHubG2Promotion > 0 && Network.IsConnected() ) { // If we have been a hub for at least 8 hours if ( tNow > m_tHubG2Promotion + 8 * 60 * 60 ) { // And we're loaded ( 75% capacity ), then we probably make a pretty good hub if ( nCount[ PROTOCOL_G2 ][ ntHub ] > ( Settings.Gnutella2.NumLeafs * 3 / 4 ) ) Settings.Gnutella2.HubVerified = true; } } if ( ! Settings.Gnutella1.Enabled ) { // Set the limit as no Gnutella hub or leaf connections allowed at all nLimit[ PROTOCOL_G1 ][ ntHub ] = nLimit[ PROTOCOL_G1 ][ ntLeaf ] = 0; } else if ( m_bG1Leaf ) // We're a leaf on the Gnutella network { nLimit[ PROTOCOL_G1 ][ ntHub ] = Settings.Gnutella1.NumHubs; // 3 ultrapeers by default } else // We're an ultrapeer on the Gnutella network { // Set the limit for Gnutella ultrapeer connections as whichever number from settings is bigger, peers or hubs nLimit[ PROTOCOL_G1 ][ ntHub ] = max( Settings.Gnutella1.NumPeers, Settings.Gnutella1.NumHubs ); // Defaults are 32 and 3 // Set the limit for Gnutella leaf connections from settings nLimit[ PROTOCOL_G1 ][ ntLeaf ] = Settings.Gnutella1.NumLeafs; // 50 leaves by default } if ( ! Settings.Gnutella2.Enabled ) { // Set the limit as no Gnutella2 hub or leaf connections allowed at all nLimit[ PROTOCOL_G2 ][ ntHub ] = nLimit[ PROTOCOL_G2 ][ ntLeaf ] = 0; } else if ( m_bG2Leaf ) // We're a leaf on the Gnutella2 network { // Set the limit for Gnutella2 hub connections from settings, should be no more than 3 nLimit[ PROTOCOL_G2 ][ ntHub ] = Settings.Gnutella2.NumHubs; // 2 hubs by default } else // We're a hub on the Gnutella2 network { // Set the limit for G2 hub connections as whichever number from settings is bigger, peers or hubs nLimit[ PROTOCOL_G2 ][ ntHub ] = max( Settings.Gnutella2.NumPeers, Settings.Gnutella2.NumHubs ); // Defaults are 6 and 2 // Set the limit for G2 leaf connections from user settings nLimit[ PROTOCOL_G2 ][ ntLeaf ] = Settings.Gnutella2.NumLeafs; // 300 leaves by default } nLimit[ PROTOCOL_ED2K ][ ntHub ] = Settings.eDonkey.Enabled ? Settings.eDonkey.NumServers : 0; // 1 server by default nLimit[ PROTOCOL_DC ][ ntHub ] = Settings.DC.Enabled ? Settings.DC.NumServers : 0; // 1 hub by default // Add the count of connections where we don't know the network yet to the 0 column of both Gnutella and Gnutella2 nCount[ PROTOCOL_G1 ][ ntNode ] += nCount[ PROTOCOL_NULL ][ ntNode ]; nCount[ PROTOCOL_G2 ][ ntNode ] += nCount[ PROTOCOL_NULL ][ ntNode ]; // Connect to more computers or disconnect from some to get the connection counts where settings wants them to be for ( PROTOCOLID nProtocol = PROTOCOL_NULL ; nProtocol < PROTOCOL_LAST ; ++nProtocol ) // Loop once for each protocol { // If we're connected to a hub of this protocol, store the tick count now in m_tPresent for this protocol if ( nCount[ nProtocol ][ ntHub ] > 0 ) m_tPresent[ nProtocol ] = tNow; // If we don't have enough hubs for this protocol if ( nCount[ nProtocol ][ ntHub ] < nLimit[ nProtocol ][ ntHub ] ) { // Don't try to connect to G1 right away, wait a few seconds to reduce the number of connections if ( nProtocol != PROTOCOL_G2 && Settings.Gnutella2.Enabled ) { if ( ! Network.ReadyToTransfer( tTimer ) ) return; } // We are going to try to connect to a computer running Gnutella or Gnutella2 software DWORD nAttempt; if ( nProtocol == PROTOCOL_ED2K ) { // For ed2k we try one attempt at a time to begin with, but we can step up to // 2 at a time after a few seconds if the FastConnect option is selected. if ( Settings.eDonkey.FastConnect && Network.ReadyToTransfer( tTimer ) ) nAttempt = 2; else nAttempt = 1; } else if ( nProtocol == PROTOCOL_DC ) { // DC++ Slow connecting nAttempt = 1; } else { // For Gnutella and Gnutella2, try connection to the number of free slots multiplied by the connect factor from settings nAttempt = ( nLimit[ nProtocol ][ ntHub ] - nCount[ nProtocol ][ ntHub ] ); nAttempt *= Settings.Gnutella.ConnectFactor; } // Lower the needed hub number to avoid hitting Windows XP Service Pack 2's half open connection limit nAttempt = min( nAttempt, ( Settings.Downloads.MaxConnectingSources - 1 ) ); CHostCacheList* pCache = HostCache.ForProtocol( nProtocol ); CSingleLock oLock( &pCache->m_pSection, FALSE ); if ( ! oLock.Lock( 250 ) ) return; // Handle priority servers // Loop into the host cache until we have as many handshaking connections as we need hub connections for ( CHostCacheIterator i = pCache->Begin() ; i != pCache->End() && nCount[ nProtocol ][ ntNode ] < nAttempt ; ++i ) { CHostCacheHostPtr pHost = (*i); // If we can connect to this priority host, try it if ( pHost->m_bPriority && pHost->CanConnect( tNow ) && pHost->ConnectTo( TRUE ) ) { m_tPriority[ nProtocol ] = tNow; pHost->m_nFailures = 0; pHost->m_tFailure = 0; pHost->m_bCheckedLocally = TRUE; // Count that we now have one more connection, and we don't know its network role yet nCount[ nProtocol ][ ntNode ]++; // Prevent queries while we connect with this computer (do) pHost->m_tQuery = tNow; // If settings wants to limit how frequently this method can run if ( Settings.Connection.ConnectThrottle ) { m_tLastConnect = tTimer; return; } } } // 10 second delay between priority and regular servers if ( tNow > m_tPriority[ nProtocol ] + 10 ) { // Handle regular servers, if we need more connections for this network, get IP addresses from the host cache and try to connect to them for ( CHostCacheIterator i = pCache->Begin() ; i != pCache->End() && nCount[ nProtocol ][ ntNode ] < nAttempt ; ++i ) { CHostCacheHostPtr pHost = (*i); // If we can connect to this IP address from the host cache, try it if ( ! pHost->m_bPriority && pHost->CanConnect( tNow ) && pHost->ConnectTo( TRUE ) ) { // Make sure the connection we just made matches the protocol we're looping for right now //ASSERT( pHost->m_nProtocol == nProtocol ); pHost->m_nFailures = 0; pHost->m_tFailure = 0; pHost->m_bCheckedLocally = TRUE; // Count that we now have one more handshaking connection for this network nCount[ nProtocol ][ ntNode ]++; // Prevent queries while we log on (do) pHost->m_tQuery = tNow; // If settings wants to limit how frequently this method can run if ( Settings.Connection.ConnectThrottle ) { m_tLastConnect = tTimer; return; } } } } // If we don't have any handshaking connections for this network, and we've been connected to a hub for more than 30 seconds if ( nCount[ nProtocol ][ ntNode ] == 0 || // We don't have any handshaking connections for this network, or tNow > m_tPresent[ nProtocol ] + 30 ) // We've been connected to a hub for more than 30 seconds { const DWORD tDiscoveryLastExecute = DiscoveryServices.LastExecute(); if ( nProtocol == PROTOCOL_G2 && Settings.Gnutella2.Enabled ) { // We're looping for Gnutella2 right now // Execute the discovery services (do) if ( pCache->IsEmpty() && tNow >= tDiscoveryLastExecute + 8 ) DiscoveryServices.Execute( TRUE, PROTOCOL_G2, 1 ); } else if ( nProtocol == PROTOCOL_G1 && Settings.Gnutella1.Enabled ) { // We're looping for Gnutella right now // If the Gnutella host cache is empty (do), execute discovery services (do) if ( pCache->IsEmpty() && tNow >= tDiscoveryLastExecute + 8 ) DiscoveryServices.Execute( TRUE, PROTOCOL_G1, 1 ); } } } else if ( nCount[ nProtocol ][ ntHub ] > nLimit[ nProtocol ][ ntHub ] ) // We're over the limit we just calculated { // Otherwise we have too many hub connections for this protocol, // so find the hub we connected to most recently to remove it. CNeighbour* pNewest = NULL; for ( POSITION pos = GetIterator() ; pos ; ) { // Loop through the list of neighbours CNeighbour* pNeighbour = GetNext( pos ); // If this is a hub connection that connected to us recently if ( ( pNeighbour->m_nNodeType != ntLeaf ) && // If this connection isn't down to a leaf, and ( pNeighbour->m_nProtocol == nProtocol ) && // This connection is for the protocol we're looping on right now, and ( pNeighbour->m_bAutomatic || // The neighbour is automatic, or ! pNeighbour->m_bInitiated || // The neighbour connected to us, or nLimit[ nProtocol ][ ntHub ] == 0 ) ) // We're not supposed to be connected to this network at all { // If this is the newest hub, remember it. if ( pNewest == NULL || pNeighbour->m_tConnected > pNewest->m_tConnected ) pNewest = pNeighbour; } } // Disconnect from one hub if ( pNewest ) pNewest->Close(); // Close the connection } // If we're over our leaf connection limit for this network if ( nCount[ nProtocol ][ ntLeaf ] > nLimit[ nProtocol ][ ntLeaf ] ) { // Find the leaf we most recently connected to CNeighbour* pNewest = NULL; for ( POSITION pos = GetIterator() ; pos ; ) { // Loop for each neighbour in the list CNeighbour* pNeighbour = GetNext( pos ); // This connection is down to a leaf and the protocol is correct if ( pNeighbour->m_nNodeType == ntLeaf && pNeighbour->m_nProtocol == nProtocol ) { // If we haven't found the newest yet, or this connection is younger than the current newest, this is it if ( pNewest == NULL || pNeighbour->m_tConnected > pNewest->m_tConnected ) pNewest = pNeighbour; } } // Disconnect from one leaf if ( pNewest ) pNewest->Close(); // Close the connection } } }
BOOL CManagedSearch::ExecuteG2Mesh(const DWORD /*tTicks*/, const DWORD tSecs) { ASSUME_LOCK( SearchManager.m_pSection ); // Look at all known Gnutella2 hubs, newest first CQuickLock oLock( HostCache.Gnutella2.m_pSection ); for ( CHostCacheIterator i = HostCache.Gnutella2.Begin() ; i != HostCache.Gnutella2.End() ; ++i ) { CHostCacheHostPtr pHost = (*i); // Must be Gnutella2 ASSERT( pHost->m_nProtocol == PROTOCOL_G2 ); // If this host is a neighbour, don't UDP to it if ( Neighbours.Get( pHost->m_pAddress ) ) continue; // If this host can't be queried now, don't query it if ( ! pHost->CanQuery( tSecs ) ) continue; // Check if we have an appropriate query key for this host, // and if so, record the receiver address SOCKADDR_IN* pReceiver = NULL; if ( pHost->m_nKeyValue == 0 ) { // We already know we don't have a key, pretty simple } else if ( ! Network.IsFirewalled(CHECK_UDP) ) { // If we are "stable", we have to TX/RX our own UDP traffic, // so we must have a query key for the local addess if ( pHost->m_nKeyHost == Network.m_pHost.sin_addr.S_un.S_addr ) pReceiver = &Network.m_pHost; else pHost->m_nKeyValue = 0; } else { // Make sure we have a query key via one of our neighbours, // and ensure we have queried this neighbour if ( CNeighbour* pNeighbour = Neighbours.Get( *(IN_ADDR*)&pHost->m_nKeyHost ) ) { DWORD nTemp; if ( m_pNodes.Lookup( pHost->m_nKeyHost, nTemp ) ) pReceiver = &pNeighbour->m_pHost; else continue; } else { pHost->m_nKeyValue = 0; } } // Now, if we still have a query key, send the query if ( pHost->m_nKeyValue != 0 ) { DWORD tLastQuery; ASSERT( pReceiver != NULL ); // Lookup the host if ( m_pNodes.Lookup( pHost->m_pAddress.s_addr, tLastQuery ) ) { // Check per-hub re-query time DWORD nFrequency; if ( m_nPriority >= spLowest ) { // Low priority "auto find" sources if ( m_pSearch->m_oSHA1 ) // Has SHA1- probably exists on G2 nFrequency = 16 * 60 * 60; else // Reduce frequency if no SHA1. nFrequency = 32 * 60 * 60; } else nFrequency = Settings.Gnutella2.RequeryDelay * ( m_nPriority + 1 ); if ( tSecs - tLastQuery < nFrequency ) continue; } // Set the last query time for this host for this search m_pNodes.SetAt( pHost->m_pAddress.s_addr, tSecs ); // Record the query time on the host, for all searches pHost->m_tQuery = tSecs; if ( pHost->m_tAck == 0 ) pHost->m_tAck = tSecs; // Try to create a packet m_pSearch->m_bAndG1 = ( Settings.Gnutella1.Enabled && m_bAllowG1 ); if ( CPacket* pPacket = m_pSearch->ToG2Packet( pReceiver, pHost->m_nKeyValue ) ) { if ( Datagrams.Send( &pHost->m_pAddress, pHost->m_nPort, pPacket, TRUE, this, TRUE ) ) { theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Querying %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ) ); return TRUE; } } } else if ( tSecs - pHost->m_tKeyTime >= max( Settings.Gnutella2.QueryThrottle * 5ul, 5ul * 60ul ) ) { // Timing wise, we can request a query key now -- // but first we must figure out who should be the receiver CNeighbour* pCacheHub = NULL; pReceiver = NULL; if ( ! Network.IsFirewalled( CHECK_UDP ) ) { // If we are stable, we must be the receiver pReceiver = &Network.m_pHost; } else { // Otherwise, we need to find a neighbour G2 hub who has acked this query already for ( POSITION pos = Neighbours.GetIterator() ; pos ; pCacheHub = NULL ) { pCacheHub = Neighbours.GetNext( pos ); DWORD nTemp; if ( m_pNodes.Lookup( pCacheHub->m_pHost.sin_addr.s_addr, nTemp ) ) { if ( pCacheHub->m_nProtocol == PROTOCOL_G2 && pCacheHub->m_nNodeType == ntHub ) { pReceiver = &pCacheHub->m_pHost; if ( ! ((CG2Neighbour*)pCacheHub)->m_bCachedKeys ) pCacheHub = NULL; break; } } } } // If we found a receiver, we can ask for the query key if ( pCacheHub != NULL ) { // The receiver is a cache-capable hub, so we ask it to return a cached key, or fetch a fresh one if ( CG2Packet* pPacket = CG2Packet::New( G2_PACKET_QUERY_KEY_REQ, TRUE ) ) { pPacket->WritePacket( G2_PACKET_QUERY_ADDRESS, 6 ); pPacket->WriteLongLE( pHost->m_pAddress.S_un.S_addr ); pPacket->WriteShortBE( pHost->m_nPort ); if ( pCacheHub->Send( pPacket ) ) { if ( pHost->m_tAck == 0 ) pHost->m_tAck = tSecs; pHost->m_tKeyTime = tSecs; pHost->m_nKeyValue = 0; theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Requesting query key from %s through %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ), (LPCTSTR)CString( inet_ntoa( pReceiver->sin_addr ) ) ); return TRUE; } } } else if ( pReceiver != NULL ) { // We need to transmit directly to the remote query host if ( CG2Packet* pPacket = CG2Packet::New( G2_PACKET_QUERY_KEY_REQ, TRUE ) ) { if ( pReceiver != &Network.m_pHost ) { // We are not the receiver, so include receiver address pPacket->WritePacket( G2_PACKET_REQUEST_ADDRESS, 6 ); pPacket->WriteLongLE( pReceiver->sin_addr.S_un.S_addr ); pPacket->WriteShortBE( ntohs( pReceiver->sin_port ) ); } if ( Datagrams.Send( &pHost->m_pAddress, pHost->m_nPort, pPacket, TRUE, NULL, FALSE ) ) { if ( pHost->m_tAck == 0 ) pHost->m_tAck = tSecs; pHost->m_tKeyTime = tSecs; pHost->m_nKeyValue = 0; if ( pReceiver == &Network.m_pHost ) { theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Requesting query key from %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ) ); } else { theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Requesting query key from %s for %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ), (LPCTSTR)CString( inet_ntoa( pReceiver->sin_addr ) ) ); } return TRUE; } } } } } return FALSE; }