BOOL CNetwork::RoutePacket(CG2Packet* pPacket) { GGUID pGUID; if ( ! pPacket->GetTo( &pGUID ) || pGUID == MyProfile.GUID ) return FALSE; CNeighbour* pOrigin = NULL; SOCKADDR_IN pEndpoint; if ( GetNodeRoute( &pGUID, &pOrigin, &pEndpoint ) ) { if ( pOrigin != NULL ) { if ( pOrigin->m_nProtocol == PROTOCOL_G1 && pPacket->IsType( G2_PACKET_PUSH ) ) { CG1Neighbour* pG1 = (CG1Neighbour*)pOrigin; pPacket->SkipCompound(); pG1->SendG2Push( &pGUID, pPacket ); } else { pOrigin->Send( pPacket, FALSE, TRUE ); } } else { Datagrams.Send( &pEndpoint, pPacket, FALSE ); } Statistics.Current.Gnutella2.Routed++; } return TRUE; }
void CNeighbourTipCtrl::OnTimer(UINT_PTR nIDEvent) { CCoolTipCtrl::OnTimer( nIDEvent ); if ( m_pGraph == NULL ) return; CSingleLock pLock( &Network.m_pSection ); if ( ! pLock.Lock( 100 ) ) return; CNeighbour* pNeighbour = Neighbours.Get( m_nNeighbour ); if ( pNeighbour == NULL ) return; pNeighbour->Measure(); const DWORD nIn = pNeighbour->m_mInput.nMeasure; const DWORD nOut = pNeighbour->m_mOutput.nMeasure; m_pItemIn->Add( nIn ); m_pItemOut->Add( nOut ); m_pGraph->m_nMaximum = max( m_pGraph->m_nMaximum, nIn ); m_pGraph->m_nMaximum = max( m_pGraph->m_nMaximum, nOut ); m_pGraph->m_nUpdates++; CRect rcWndTip; SystemParametersInfo( SPI_GETWORKAREA, 0, rcWndTip, 0 ); rcWndTip.top += 90; InvalidateRect( &rcWndTip ); }
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; } } }
BOOL CNetwork::SendPush(GGUID* pGUID, DWORD nIndex) { CSingleLock pLock( &Network.m_pSection ); if ( ! pLock.Lock( 250 ) ) return TRUE; if ( ! IsListening() ) return FALSE; GGUID pGUID2 = *pGUID; SOCKADDR_IN pEndpoint; CNeighbour* pOrigin; int nCount = 0; while ( GetNodeRoute( &pGUID2, &pOrigin, &pEndpoint ) ) { if ( pOrigin != NULL && pOrigin->m_nProtocol == PROTOCOL_G1 ) { CG1Packet* pPacket = CG1Packet::New( G1_PACKET_PUSH, Settings.Gnutella1.MaximumTTL - 1 ); pPacket->Write( pGUID, 16 ); pPacket->WriteLongLE( nIndex ); pPacket->WriteLongLE( m_pHost.sin_addr.S_un.S_addr ); pPacket->WriteShortLE( htons( m_pHost.sin_port ) ); pOrigin->Send( pPacket ); } else { CG2Packet* pPacket = CG2Packet::New( G2_PACKET_PUSH, TRUE ); pPacket->WritePacket( G2_PACKET_TO, 16 ); pPacket->Write( pGUID, 16 ); pPacket->WriteByte( 0 ); pPacket->WriteLongLE( m_pHost.sin_addr.S_un.S_addr ); pPacket->WriteShortBE( htons( m_pHost.sin_port ) ); if ( pOrigin != NULL ) { pOrigin->Send( pPacket ); } else { Datagrams.Send( &pEndpoint, pPacket ); } } pGUID2.n[15] ++; nCount++; } return nCount > 0; }
void CNeighboursWithConnect::MaintainNodeStatus() { BOOL bG2Leaf = FALSE; BOOL bG2Hub = FALSE; BOOL bG1Leaf = FALSE; BOOL bG1Ultrapeer = FALSE; DWORD tEstablish = GetTickCount() - 1500; DWORD nStableCount = 0; DWORD nBandwidthIn = 0; DWORD nBandwidthOut = 0; for ( POSITION pos = GetIterator() ; pos ; ) { CNeighbour* pNeighbour = GetNext( pos ); // We're done with the handshake with this neighbour if ( pNeighbour->m_nState == nrsConnected ) { pNeighbour->Measure(); nBandwidthIn += pNeighbour->m_mInput.nMeasure; nBandwidthOut += pNeighbour->m_mOutput.nMeasure; if ( pNeighbour->m_tConnected < tEstablish ) nStableCount++; // We're connected to this neighbour and exchanging Gnutella or Gnutella2 packets if ( pNeighbour->m_nProtocol == PROTOCOL_G2 ) { // If our connection to this remote computer is up to a hub, we are a leaf, if it's down to a leaf, we are a hub if ( pNeighbour->m_nNodeType == ntHub ) bG2Leaf = TRUE; else bG2Hub = TRUE; } else if ( pNeighbour->m_nProtocol == PROTOCOL_G1 ) { // If our connection to this remote computer is up to a hub, we are a leaf, if it's down to a leaf, we are an ultrapeer if ( pNeighbour->m_nNodeType == ntHub ) bG1Leaf = TRUE; else bG1Ultrapeer = TRUE; } } } m_bG2Leaf = bG2Leaf; m_bG2Hub = bG2Hub; m_bG1Leaf = bG1Leaf; m_bG1Ultrapeer = bG1Ultrapeer; m_nStableCount = nStableCount; m_nBandwidthIn = nBandwidthIn; m_nBandwidthOut = nBandwidthOut; }
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(); } } }
// The program calls OnRun on a regular interval // Calls DoRun on neighbours in the list, and totals statistics from them void CNeighboursBase::OnRun() { // Spend no more than 100 ms here at once const DWORD nStop = GetTickCount() + 100; // Have the loop test each neighbour's run cookie count against the next number m_nRunCookie++; // The first time this runs, it will take the value from 5 to 6 bool bUpdated = true; // Indicate if stats were updated // Loop until all updates have been processed while ( bUpdated && GetTickCount() < nStop ) { // Make sure this thread is the only one accessing the network object CSingleLock pLock( &Network.m_pSection ); if ( ! pLock.Lock( 100 ) ) continue; // Indicate if stats were updated bUpdated = false; // Loop through the neighbours in the list for ( POSITION pos = GetIterator(); pos; ) { // Get the neighbour at this position, and move pos to the next position in the m_pUniques map CNeighbour* pNeighbour = GetNext( pos ); // If this neighbour doesn't have the new run cookie count yet, we need to run it if ( pNeighbour->m_nRunCookie != m_nRunCookie ) { // Give it the current run cookie count so we don't run it twice, even if GetNext is weird or broken pNeighbour->m_nRunCookie = m_nRunCookie; // Send and receive data with this remote computer through the socket pNeighbour->DoRun(); // Calls CConnection::DoRun // We found a neighbour with a nonmatching run cookie count, updated it, and processed it // Defer any other updates until next run through the loop, allowing the network object to be unlocked bUpdated = true; // Set bUpdated to true break; // Break out of the for loop } } } }
CNeighbour* CNeighboursConnections::ConnectTo(CEndPoint& oAddress, DiscoveryProtocol nProtocol, bool bAutomatic) { ASSUME_LOCK(m_pSection); CNeighbour* pNode = 0; switch(nProtocol) { case dpG2: pNode = new CG2Node(); break; default: Q_ASSERT_X(0, "CNeighbours::ConnectTo", "Unknown protocol"); } pNode->m_bAutomatic = bAutomatic; pNode->ConnectTo(oAddress); pNode->moveToThread(&NetworkThread); AddNode(pNode); return pNode; }
// 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 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_" ); }
BOOL CNetwork::RouteHits(CQueryHit* pHits, CPacket* pPacket) { SOCKADDR_IN pEndpoint; CNeighbour* pOrigin; if ( ! QueryRoute->Lookup( &pHits->m_pSearchID, &pOrigin, &pEndpoint ) ) return FALSE; BOOL bWrapped = FALSE; if ( pPacket->m_nProtocol == PROTOCOL_G1 ) { CG1Packet* pG1 = (CG1Packet*)pPacket; if ( ! pG1->Hop() ) return FALSE; } else if ( pPacket->m_nProtocol == PROTOCOL_G2 ) { CG2Packet* pG2 = (CG2Packet*)pPacket; if ( pG2->IsType( G2_PACKET_HIT ) && pG2->m_nLength > 17 ) { BYTE* pHops = pG2->m_pBuffer + pG2->m_nLength - 17; if ( *pHops > Settings.Gnutella1.MaximumTTL ) return FALSE; (*pHops) ++; } else if ( pG2->IsType( G2_PACKET_HIT_WRAP ) ) { if ( ! pG2->SeekToWrapped() ) return FALSE; GNUTELLAPACKET* pG1 = (GNUTELLAPACKET*)( pPacket->m_pBuffer + pPacket->m_nPosition ); if ( pG1->m_nTTL == 0 ) return FALSE; pG1->m_nTTL --; pG1->m_nHops ++; bWrapped = TRUE; } } if ( pOrigin != NULL ) { if ( pOrigin->m_nProtocol == pPacket->m_nProtocol ) { pOrigin->Send( pPacket, FALSE, FALSE ); // Dont buffer } else if ( pOrigin->m_nProtocol == PROTOCOL_G1 && pPacket->m_nProtocol == PROTOCOL_G2 ) { if ( ! bWrapped ) return FALSE; pPacket = CG1Packet::New( (GNUTELLAPACKET*)( pPacket->m_pBuffer + pPacket->m_nPosition ) ); pOrigin->Send( pPacket, TRUE, TRUE ); } else if ( pOrigin->m_nProtocol == PROTOCOL_G2 && pPacket->m_nProtocol == PROTOCOL_G1 ) { pPacket = CG2Packet::New( G2_PACKET_HIT_WRAP, (CG1Packet*)pPacket ); pOrigin->Send( pPacket, TRUE, FALSE ); // Dont buffer } else { // Should not happen either (logic flaw) return FALSE; } } else if ( pPacket->m_nProtocol == PROTOCOL_G2 ) { if ( pEndpoint.sin_addr.S_un.S_addr == Network.m_pHost.sin_addr.S_un.S_addr ) return FALSE; Datagrams.Send( &pEndpoint, (CG2Packet*)pPacket, FALSE ); } else { if ( pEndpoint.sin_addr.S_un.S_addr == Network.m_pHost.sin_addr.S_un.S_addr ) return FALSE; pPacket = CG2Packet::New( G2_PACKET_HIT_WRAP, (CG1Packet*)pPacket ); Datagrams.Send( &pEndpoint, (CG2Packet*)pPacket, TRUE ); } if ( pPacket->m_nProtocol == PROTOCOL_G1 ) Statistics.Current.Gnutella1.Routed++; else if ( pPacket->m_nProtocol == PROTOCOL_G2 ) Statistics.Current.Gnutella2.Routed++; return TRUE; }
UINT CNeighbour::ThreadStart(LPVOID pParam) { CNeighbour* pNeighbour = (CNeighbour*)pParam; pNeighbour->OnRun(); return 0; }
// 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; }
BOOL CManagedSearch::ExecuteNeighbours(const DWORD tTicks, const DWORD tSecs) { ASSUME_LOCK( SearchManager.m_pSection ); int nCount = 0; for ( POSITION pos = Neighbours.GetIterator() ; pos ; ) { CNeighbour* pNeighbour = Neighbours.GetNext( pos ); const DWORD& nAddress = pNeighbour->m_pHost.sin_addr.S_un.S_addr; // Must be connected if ( pNeighbour->m_nState != nrsConnected ) continue; // Must be stable for 15 seconds if ( tTicks < pNeighbour->m_tConnected + 15000 ) continue; // Request more ed2k results (if appropriate) // If we've queried this neighbour 'recently' // and it's an ed2k server and has more results // and this search is the one with results waiting // and we've waited a little while to ensure search is still active if ( m_bAllowED2K && pNeighbour->m_nProtocol == PROTOCOL_ED2K && tSecs - pNeighbour->m_tLastQuery < 86400 && // 1 day pNeighbour->m_oMoreResultsGUID && IsEqualGUID( pNeighbour->m_oMoreResultsGUID ) && m_tMoreResults + 10000 < tTicks ) { // Request more results pNeighbour->Send( CEDPacket::New( ED2K_C2S_MORERESULTS ) ); ((CEDNeighbour*)pNeighbour)->m_pQueries.AddTail( pNeighbour->m_oMoreResultsGUID ); // Reset "more results" indicator pNeighbour->m_oMoreResultsGUID.clear(); // Set timer m_tMoreResults = tTicks; m_pNodes.SetAt( nAddress, tSecs ); theApp.Message( MSG_DEBUG | MSG_FACILITY_SEARCH, _T("Asking ed2k neighbour for additional search results") ); continue; } // Check enabled networks, and do not hammer neighbours for search results switch ( pNeighbour->m_nProtocol ) { case PROTOCOL_G2: if ( ! m_bAllowG2 || tSecs < pNeighbour->m_tLastQuery + Settings.Gnutella2.QueryThrottle ) continue; break; case PROTOCOL_G1: if ( ! m_bAllowG1 || tSecs < pNeighbour->m_tLastQuery + Settings.Gnutella1.QueryThrottle ) continue; break; case PROTOCOL_ED2K: if ( ! m_bAllowED2K || tSecs < pNeighbour->m_tLastQuery + Settings.eDonkey.QueryThrottle ) continue; break; case PROTOCOL_DC: if ( ! m_bAllowDC || ( ! m_pSearch->m_oTiger && m_pSearch->m_sSearch.IsEmpty() ) || tSecs < pNeighbour->m_tLastQuery + Settings.DC.QueryThrottle ) continue; break; default: continue; } // Create the appropriate packet type CPacket* pPacket = NULL; if ( pNeighbour->m_nProtocol == PROTOCOL_G1 ) { DWORD nTTL; BOOL bKnownHost = m_pG1Nodes.Lookup( nAddress, nTTL ); if ( pNeighbour->m_bExtProbes ) { // Neighbour supports X-Ext-Probes if ( bKnownHost ) { if ( nTTL >= pNeighbour->GetMaxTTL() ) continue; // X-Max-TTL reached if ( m_nHits >= Settings.Gnutella.MaxResults ) continue; // Maximum hits reached nTTL++; } else // It's the first step { nTTL = 1; } } else { if ( bKnownHost ) continue; // We under pledge of X-Requeries nTTL = pNeighbour->GetMaxTTL(); // Single query with max available TTL } m_pG1Nodes.SetAt( nAddress, nTTL ); pPacket = m_pSearch->ToG1Packet( nTTL ); } else if ( pNeighbour->m_nProtocol == PROTOCOL_G2 ) { m_pSearch->m_bAndG1 = ( Settings.Gnutella1.Enabled && m_bAllowG1 ); pPacket = m_pSearch->ToG2Packet( ! Network.IsFirewalled(CHECK_UDP) ? &Network.m_pHost : NULL, 0 ); } else if ( pNeighbour->m_nProtocol == PROTOCOL_ED2K ) { CEDNeighbour* pEDNeighbour = static_cast< CEDNeighbour* >( pNeighbour ); pPacket = m_pSearch->ToEDPacket( FALSE, pEDNeighbour->m_nTCPFlags ); } else if ( pNeighbour->m_nProtocol == PROTOCOL_DC ) { CDCNeighbour* pDCNeighbour = static_cast< CDCNeighbour* >( pNeighbour ); m_pSearch->m_pMyHub = pDCNeighbour->m_pHost; m_pSearch->m_sMyHub = pDCNeighbour->m_sServerName; m_pSearch->m_sMyNick = pDCNeighbour->m_sNick; pPacket = m_pSearch->ToDCPacket(); } // Try to send the search if ( pPacket != NULL ) { // Set the last query time for this host for this search m_pNodes.SetAt( nAddress, tSecs ); if ( pNeighbour->SendQuery( m_pSearch, pPacket, TRUE ) ) { // Reset the last "search more" sent to this neighbour (if applicable) pNeighbour->m_oMoreResultsGUID.clear(); m_tMoreResults = 0; // Display message in system window theApp.Message( MSG_INFO, IDS_NETWORK_SEARCH_SENT, m_pSearch->m_sSearch.GetLength() ? (LPCTSTR)m_pSearch->m_sSearch : _T("URN"), (LPCTSTR)CString( inet_ntoa( pNeighbour->m_pHost.sin_addr ) ) ); // Save GUID of latest text search if needed if ( pNeighbour->m_nProtocol == PROTOCOL_ED2K ) { // Set the "last ED2K search" value if we sent a text search (to find the search later). if ( ! m_pSearch->m_oED2K ) SearchManager.m_oLastSearch = m_pSearch->m_oGUID; } else if ( pNeighbour->m_nProtocol == PROTOCOL_DC ) { SearchManager.m_oLastSearch = m_pSearch->m_oGUID; } } pPacket->Release(); } nCount++; } return ( nCount > 0 ); }