void CNeighboursConnections::DisconnectYoungest(DiscoveryProtocol nProtocol, int nType, bool bCore) { CNeighbour* pNode = 0; bool bKeepManual = true; time_t tNow = time(0); while(1) { for(QList<CNeighbour*>::const_iterator i = m_lNodes.begin(); i != m_lNodes.end(); i++) { if((*i)->m_nState == nsConnected && (*i)->m_nProtocol == nProtocol) { if( bKeepManual && !(*i)->m_bAutomatic && tNow - (*i)->m_tConnected < 120 ) continue; if( nProtocol == dpG2 ) { if( ((CG2Node*)(*i))->m_nType != nType // if node type is not requested type || (!bCore && ((CG2Node*)(*i))->m_bG2Core) ) // or we don't want to disconnect "our" nodes { continue; } } if(pNode == 0) { pNode = (*i); } else { if((*i)->m_tConnected > pNode->m_tConnected) { pNode = (*i); } } } } if(pNode) { // we found a node to disconnect pNode->Close(); break; } else if(bKeepManual) { // no node to disconnect, try manually connected nodes as well bKeepManual = false; } else { // nothing to do here... break; } } }
// If we've been demoted to the leaf role for a protocol, this function trims peers after we get a hub (do) // Takes a protocol like PROTOCOL_G1 or PROTOCOL_G2 // If we don't need any more hub connections, closes them all (do) void CNeighboursWithConnect::PeerPrune(PROTOCOLID nProtocol) { // True if we need more hub connections for the requested protocol BOOL bNeedMore = NeedMoreHubs( nProtocol ); // True if we need more hub connections for either Gnutella or Gnutella2 BOOL bNeedMoreAnyProtocol = NeedMoreHubs( PROTOCOL_NULL ); // Loop through all the neighbours in the list for ( POSITION pos = GetIterator() ; pos ; ) { // Get the neighbour at this position in the list, and move to the next one CNeighbour* pNeighbour = GetNext( pos ); // This neighbour is on the network the caller wants us to prune, and if ( pNeighbour->m_nProtocol == nProtocol ) { // Our connection to this neighbour is not up to a hub, and if ( pNeighbour->m_nNodeType != ntHub ) { // Either we don't need any more hubs, or we're done with the handshake so we know it wont' be a hub, then drop this connection if ( ! bNeedMore || pNeighbour->m_nState == nrsConnected ) pNeighbour->Close( IDS_CONNECTION_PEERPRUNE ); } } else if ( pNeighbour->m_nProtocol == PROTOCOL_NULL ) { // This must be a Gnutella or Gnutella2 computer in the middle of the handshake // If we initiated the connection, we know it's not a leaf trying to contact us, it's probably a hub if ( pNeighbour->m_bInitiated ) { // If we don't need any more hubs, on any protocol, drop this connection if ( ! bNeedMoreAnyProtocol ) pNeighbour->Close( IDS_CONNECTION_PEERPRUNE ); } } } }
void CHostCacheWnd::OnHostCacheDisconnect() { CSingleLock pLock( &Network.m_pSection, TRUE ); POSITION pos = m_wndList.GetFirstSelectedItemPosition(); while( pos ) { int nItem = m_wndList.GetNextSelectedItem( pos ); if ( CHostCacheHost* pHost = GetItem( nItem ) ) { CNeighbour* pNeighbour = Neighbours.Get( &pHost->m_pAddress ); if ( pNeighbour ) pNeighbour->Close(); } } }
void CRemote::PageNetworkNetwork(int nID, bool* pbConnect, LPCTSTR pszName) { CSingleLock pLock( &Network.m_pSection ); CString str; str.Format( L"%i", nID ); if ( GetKey( L"connect" ) == str ) { *pbConnect = TRUE; Network.Connect( TRUE ); } else if ( GetKey( L"disconnect" ) == str ) { *pbConnect = FALSE; if ( SafeLock( pLock ) ) { for ( POSITION pos = Neighbours.GetIterator(); pos != NULL; ) { CNeighbour* pNeighbour = Neighbours.GetNext( pos ); if ( pNeighbour->m_nProtocol == PROTOCOL_NULL || pNeighbour->m_nProtocol == nID ) pNeighbour->Close( IDS_CONNECTION_CLOSED ); } pLock.Unlock(); } } Add( L"network_id", str ); Add( L"network_caption", pszName ); if ( *pbConnect ) Add( L"network_connected", L"true" ); Output( L"networkNetStart" ); pLock.Lock(); for ( POSITION pos = Neighbours.GetIterator(); pos != NULL; ) { CNeighbour* pNeighbour = Neighbours.GetNext( pos ); if ( pNeighbour->m_nProtocol != nID ) continue; pNeighbour->Measure(); str.Format( L"%p", pNeighbour ); Add( L"row_id", str ); Add( L"row_address", pNeighbour->m_sAddress ); // Add( L"row_mode", Neighbours.GetName( pNeighbour ) ); // ToDo Add( L"row_agent", pNeighbour->m_sUserAgent ); // Add( L"row_nick", Neighbours.GetNick( pNeighbour ) ); // ToDo str.Format( L"%u -/- %u", pNeighbour->m_nInputCount, pNeighbour->m_nOutputCount ); Add( L"row_packets", str ); str.Format( L"%s -/- %s", (LPCTSTR)Settings.SmartSpeed( pNeighbour->m_mInput.nMeasure ), (LPCTSTR)Settings.SmartSpeed( pNeighbour->m_mOutput.nMeasure ) ); Add( L"row_bandwidth", str ); str.Format( L"%s -/- %s", (LPCTSTR)Settings.SmartVolume( pNeighbour->m_mInput.nTotal ), (LPCTSTR)Settings.SmartVolume( pNeighbour->m_mOutput.nTotal ) ); Add( L"row_total", str ); switch ( pNeighbour->m_nState ) { case nrsConnecting: LoadString( str, IDS_NEIGHBOUR_CONNECTING ); break; case nrsHandshake1: case nrsHandshake2: case nrsHandshake3: LoadString( str, IDS_NEIGHBOUR_HANDSHAKING ); break; case nrsRejected: LoadString( str, IDS_NEIGHBOUR_REJECTED ); break; case nrsClosing: LoadString( str, IDS_NEIGHBOUR_CLOSING ); break; case nrsConnected: { const DWORD tNow = ( GetTickCount() - pNeighbour->m_tConnected ) / 1000; // Seconds if ( tNow > 86400 ) str.Format( L"%u:%.2u:%.2u:%.2u", tNow / 86400, ( tNow / 3600 ) % 24, ( tNow / 60 ) % 60, tNow % 60 ); else str.Format( L"%u:%.2u:%.2u", tNow / 3600, ( tNow / 60 ) % 60, tNow % 60 ); } break; case nrsNull: default: LoadString( str, IDS_NEIGHBOUR_UNKNOWN ); break; } Add( L"row_time", str ); if ( pNeighbour->GetUserCount() ) { if ( pNeighbour->GetUserLimit() ) str.Format( L"%u/%u", pNeighbour->GetUserCount(), pNeighbour->GetUserLimit() ); else str.Format( L"%u", pNeighbour->GetUserCount() ); Add( L"row_leaves", str ); } if ( pNeighbour->m_nProtocol == PROTOCOL_G1 ) { // CG1Neighbour* pG1 = reinterpret_cast<CG1Neighbour*>(pNeighbour); switch ( pNeighbour->m_nNodeType ) { case ntNode: LoadString( str, IDS_NEIGHBOUR_G1PEER ); break; case ntHub: LoadString( str, IDS_NEIGHBOUR_G1ULTRA ); break; case ntLeaf: LoadString( str, IDS_NEIGHBOUR_G1LEAF ); break; } Add( L"row_mode", str ); str.Empty(); } else if ( pNeighbour->m_nProtocol == PROTOCOL_G2 ) { CG2Neighbour* pG2 = static_cast<CG2Neighbour*>(pNeighbour); switch ( pNeighbour->m_nNodeType ) { case ntNode: LoadString( str, IDS_NEIGHBOUR_G2PEER ); break; case ntHub: LoadString( str, IDS_NEIGHBOUR_G2HUB ); break; case ntLeaf: LoadString( str, IDS_NEIGHBOUR_G2LEAF ); break; } Add( L"row_mode", str ); str.Empty(); if ( pG2->m_pProfile ) str = pG2->m_pProfile->GetNick(); } else if ( pNeighbour->m_nProtocol == PROTOCOL_ED2K ) { CEDNeighbour* pED2K = static_cast<CEDNeighbour*>(pNeighbour); if ( pED2K->m_nClientID > 0 ) LoadString( str, CEDPacket::IsLowID( pED2K->m_nClientID ) ? IDS_NEIGHBOUR_ED2K_LOWID : IDS_NEIGHBOUR_ED2K_HIGHID ); else str = L"eDonkey2000"; Add( L"row_mode", str ); str = pED2K->m_sServerName; } else if ( pNeighbour->m_nProtocol == PROTOCOL_DC ) { str = pNeighbour->m_sServerName; } Add( L"row_nick", str ); str = pNeighbour->m_sAddress + L" - " + str; Add( L"row_caption", str ); Output( L"networkRow" ); Prepare( L"row_" ); } Output( L"networkNetEnd" ); Prepare( L"network_" ); }
// 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 } } }