Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

ShakeNeighbour.cpp

Go to the documentation of this file.
00001 //
00002 // ShakeNeighbour.cpp
00003 //
00004 // Copyright (c) Shareaza Development Team, 2002-2005.
00005 // This file is part of SHAREAZA (www.shareaza.com)
00006 //
00007 // Shareaza is free software; you can redistribute it
00008 // and/or modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2 of
00010 // the License, or (at your option) any later version.
00011 //
00012 // Shareaza is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with Shareaza; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 
00022 // CShakeNeighbour reads and sends handshake headers to negotiate the Gnutella or Gnutella2 handshake
00023 // http://wiki.shareaza.com/static/Developers.Code.CShakeNeighbour
00024 
00025 // Copy in the contents of these files here before compiling
00026 #include "StdAfx.h"
00027 #include "Shareaza.h"
00028 #include "Settings.h"
00029 #include "Security.h"
00030 #include "Network.h"
00031 #include "Buffer.h"
00032 #include "HostCache.h"
00033 #include "DiscoveryServices.h"
00034 #include "Neighbours.h"
00035 #include "ShakeNeighbour.h"
00036 #include "G1Neighbour.h"
00037 #include "G2Neighbour.h"
00038 #include "Packet.h"
00039 
00040 // If we are compiling in debug mode, replace the text "THIS_FILE" in the code with the name of this file
00041 #ifdef _DEBUG
00042 #undef THIS_FILE
00043 static char THIS_FILE[]=__FILE__;
00044 #define new DEBUG_NEW
00045 #endif
00046 
00048 // CShakeNeighbour construction
00049 
00050 // Make a new CShakeNeighbour object
00051 CShakeNeighbour::CShakeNeighbour() : CNeighbour( PROTOCOL_NULL ) // Call the CNeighbour constructor first, with no protocol
00052 {
00053         // Set member variables that record headers to false
00054         m_bSentAddress   = FALSE; // We haven't told the remote computer "Listen-IP: 1.2.3.4:5"
00055         m_bG2Send        = FALSE; // The remote computer hasn't said "Content-Type: application/x-gnutella2" yet
00056         m_bG2Accept      = FALSE; // The remote computer hasn't said "Accept: application/x-gnutella2" yet
00057         m_bDeflateSend   = FALSE; // The remote computer hasn't said "Content-Encoding: deflate" yet
00058         m_bDeflateAccept = FALSE; // The remote computer hasn't said "Accept-Encoding: deflate" yet
00059 
00060         //ToDo: Check this - G1 setting?
00061         // Set m_bCanDeflate to true if the checkboxes in Shareaza Settings allow us to send and receive compressed data
00062         m_bCanDeflate = Neighbours.IsG2Leaf() ? ( Settings.Gnutella.DeflateHub2Hub || Settings.Gnutella.DeflateLeaf2Hub ) : ( Settings.Gnutella.DeflateHub2Hub || Settings.Gnutella.DeflateHub2Hub );
00063 
00064         // Start out ultrapeer settings as unknown
00065         m_bUltraPeerSet    = TS_UNKNOWN; // The remote computer hasn't told us if it's ultra or not yet
00066         m_bUltraPeerNeeded = TS_UNKNOWN; // The remote computer hasn't told us if it needs more ultra connections yet
00067         m_bUltraPeerLoaded = TS_UNKNOWN; // May not be in use (do)
00068 }
00069 
00070 // Delete this CShakeNeighbour object
00071 CShakeNeighbour::~CShakeNeighbour()
00072 {
00073         // This virtual method will be redefined by a class that inherits from CShakeNeighbour
00074 }
00075 
00077 // CShakeNeighbour connect to
00078 
00079 // Called by CNeighboursWithConnect::ConnectTo
00080 // Takes an IP address and port number to connect to, the automatic setting (do), and true if (do)
00081 // Connects the socket in this object to the remote computer
00082 // Returns false if the connection could not be made
00083 BOOL CShakeNeighbour::ConnectTo(IN_ADDR* pAddress, WORD nPort, BOOL bAutomatic, BOOL bNoUltraPeer)
00084 {
00085         // Connect the socket in this object to the given ip address and port number
00086         if ( CConnection::ConnectTo( pAddress, nPort ) )
00087         {
00088                 // Have Windows signal our event when the state of the socket changes
00089                 WSAEventSelect(                                   // Associate an event object with a specified set of network events
00090                         m_hSocket,                                    // The socket
00091                         Network.m_pWakeup,                            // Signal this event when the following network events happen
00092                         FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE ); // Connection completed, ready to read or write, or socket closed
00093 
00094                 // Report that we are attempting this connection
00095                 theApp.Message( MSG_DEFAULT, IDS_CONNECTION_ATTEMPTING, (LPCTSTR)m_sAddress, htons( m_pHost.sin_port ) );
00096 
00097         } // ConnectTo reported that the socket could not be made
00098         else
00099         {
00100                 // Report the connection failure
00101                 theApp.Message( MSG_ERROR, IDS_CONNECTION_CONNECT_FAIL, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) );
00102                 return FALSE;
00103         }
00104 
00105         // Initialize more member variables
00106         m_nState                = nrsConnecting;                        // We've connected the socket, and are waiting for the connection to be made
00107         m_bAutomatic    = bAutomatic;                           // Copy the given automatic setting into the member variable (do)
00108         m_bUltraPeerSet = bNoUltraPeer ? TS_FALSE : TS_UNKNOWN; // Set m_bUltraPeerSet to false or unknown (do)
00109 
00110         // Add this CShakeNeighbour object to the list of them
00111         Neighbours.Add( this );
00112 
00113         // The connection was made without error
00114         return TRUE;
00115 }
00116 
00118 // CShakeNeighbour accept from
00119 
00120 // Takes pConnection, the source object to copy from
00121 // Copies the the values from inside pConnection to this CShakeNeighbour object
00122 void CShakeNeighbour::AttachTo(CConnection* pConnection)
00123 {
00124         // Call CConnection::AttachTo to copy pConnection into the CConnection core of this CShakeNeighbour object
00125         CConnection::AttachTo( pConnection );
00126 
00127         // Have Windows signal our event when the state of the socket changes
00128         WSAEventSelect(                      // Associate an event object with a specified set of network events
00129                 m_hSocket,                       // The socket
00130                 Network.m_pWakeup,               // Signal this event when the following network events happen
00131                 FD_READ | FD_WRITE | FD_CLOSE ); // Signal it when the socket is ready to read or write, or closed
00132 
00133         // Put this CShakeNeighbour object into the Handshake1 state (do)
00134         m_nState = nrsHandshake1; // We've finished sending a group of headers, and await the response
00135 
00136         // Add this CShakeNeighbour object to the list of them
00137         Neighbours.Add( this );
00138 }
00139 
00141 // CShakeNeighbour close
00142 
00143 // Called when the socket connection has been dropped
00144 // Takes an error code that explains why
00145 // Records the failure and puts everything away
00146 void CShakeNeighbour::Close(UINT nError)
00147 {
00148         // If we initiated the connection to the remote computer
00149         if ( m_bInitiated )
00150         {
00151                 // And if the remote computer hasn't sent us any handshake headers yet
00152                 if ( m_nState < nrsHandshake2 )
00153                 {
00154                         // Tell the host cache that we didn't get any data from this remote computer at all
00155                         HostCache.OnFailure( &m_pHost.sin_addr, htons( m_pHost.sin_port ) );
00156                 }
00157         }
00158 
00159         // Have CNeighbour remove this object from the list, and put away the socket
00160         CNeighbour::Close( nError );
00161 }
00162 
00164 // CShakeNeighbour connection event
00165 
00166 // CConnection::DoRun calls this when it has just opened a socket to a remote computer
00167 // Sends the remote computer our first big block of Gnutella headers
00168 // Always returns true
00169 BOOL CShakeNeighbour::OnConnected()
00170 {
00171         // This call does nothing (do)
00172         CConnection::OnConnected();
00173 
00174         // Report that this connection was made
00175         theApp.Message( MSG_DEFAULT, IDS_CONNECTION_CONNECTED, (LPCTSTR)m_sAddress );
00176 
00177         // We initiated the connection to this computer, send it our first block of handshake headers
00178         m_pOutput->Print( "GNUTELLA CONNECT/0.6\r\n" ); // Ask to connect
00179         SendPublicHeaders();                            // User agent, ip addresses, Gnutella2 and deflate, advanced Gnutella features
00180         SendHostHeaders();                              // Try ultrapeers
00181         m_pOutput->Print( "\r\n" );                     // A blank line ends this first block of headers
00182 
00183         // We've finished sending a group of headers, and await the response
00184         m_nState = nrsHandshake1;
00185 
00186         // Send the output buffer to the remote computer
00187         OnWrite();
00188 
00189         // Have CConnection::DoRun keep going
00190         return TRUE;
00191 }
00192 
00194 // CShakeNeighbour connection loss event
00195 
00196 // CConnection::DoRun calls this when a socket connection has been refused or lost
00197 // Documents what happened, and puts everything away
00198 void CShakeNeighbour::OnDropped(BOOL bError)
00199 {
00200         // We tried to connect the socket, but are still waiting for the socket connection to be made
00201         if ( m_nState == nrsConnecting )
00202         {
00203                 // Close the connection, citing refused as the reason it didn't work out
00204                 Close( IDS_CONNECTION_REFUSED );
00205 
00206         } // We are somewhere else in the chain of connecting and doing the handshake
00207         else
00208         {
00209                 // Close the connection, citing dropped as the explination of what happened
00210                 Close( IDS_CONNECTION_DROPPED );
00211         }
00212 }
00213 
00215 // CShakeNeighbour read and write events
00216 
00217 // Reads in data from the remote computer, looks at it as the handshake, and replies to it
00218 // Returns true if we should keep talking to the remote computer, false if we should disconnect
00219 BOOL CShakeNeighbour::OnRead()
00220 {
00221         // Copy data from the remote computer that is waiting on our end of the socket into the input buffer
00222         CConnection::OnRead();
00223 
00224         // If we've finished sending a group of headers, read the remote computer's response
00225         if ( m_nState == nrsHandshake1 && ! ReadResponse() ) return FALSE;
00226 
00227         // If the remote computer has sent the first line of its initial group of headers, keep reading them
00228         if ( m_nState == nrsHandshake2 && ! ReadHeaders() ) return FALSE;
00229 
00230         // If we've finished sending a group of headers, read the remote computer's response
00231         if ( m_nState == nrsHandshake1 && ! ReadResponse() ) return FALSE;
00232 
00233         // If the remote computer has sent the first line of its final group of headers, keep reading them
00234         if ( m_nState == nrsHandshake3 && ! ReadHeaders() ) return FALSE;
00235 
00236         // If the remote computer started with "GNUTELLA/0.6", but did not say "200 OK" (do)
00237         if ( m_nState == nrsRejected && ! ReadHeaders() ) return FALSE;
00238 
00239         // Keep talking to the remote computer
00240         return TRUE;
00241 }
00242 
00244 // CShakeNeighbour run event
00245 
00246 // CConnection::DoRun calls this
00247 // Makes sure the handshake hasn't been taking too long
00248 // Returns false to stop talking to this computer, true to keep talking to it
00249 BOOL CShakeNeighbour::OnRun()
00250 {
00251         // Get the number of milliseconds since the computer has been turned on
00252         DWORD nTimeNow = GetTickCount();
00253 
00254         // We connected the socket, and are waiting for the socket connection to be made
00255         switch ( m_nState ) {
00256         case nrsConnecting:
00257 
00258                 // If we've been waiting for the connection to be made longer than the connection timeout in settings
00259                 if ( nTimeNow - m_tConnected > Settings.Connection.TimeoutConnect )
00260                 {
00261                         // Tell discovery services that we're giving up on this one, and close the connection
00262                         DiscoveryServices.OnGnutellaFailed( &m_pHost.sin_addr );
00263                         Close( IDS_CONNECTION_TIMEOUT_CONNECT );
00264                         return FALSE;
00265                 }
00266 
00267                 break;
00268 
00269         // We are exchanging handshake headers with the remote computer, and the most recent thing that's happened is
00270         case nrsHandshake1: // We've sent a complete group of headers
00271         case nrsHandshake2: // The remote computer sent the first line of its initial group of headers to us
00272         case nrsHandshake3: // The remote computer sent the first line of its final group of headers to us
00273         case nrsRejected:   // The remote computer sent us a line with a 503 error code
00274 
00275                 // If the handshake has been going on for longer than the handshake timeout in settings
00276                 if ( nTimeNow - m_tConnected > Settings.Connection.TimeoutHandshake )
00277                 {
00278                         // Close the connection
00279                         Close( IDS_HANDSHAKE_TIMEOUT );
00280                         return FALSE;
00281                 }
00282 
00283                 break;
00284 
00285         // DelayClose was called, it sends the write buffer to the remote computer before closing the socket
00286         case nrsClosing:
00287 
00288                 // Close the connection
00289                 Close( 0 );
00290                 return FALSE;
00291 
00292                 break;
00293         }
00294 
00295         // Have CConnection::DoRun keep talking to this remote computer
00296         return TRUE;
00297 }
00298 
00300 // CShakeNeighbour handshake header dispatch
00301 
00302 // Tell the remote computer we are Shareaza, and setup Gnutella2 or just Gnutella communications
00303 void CShakeNeighbour::SendMinimalHeaders()
00304 {
00305         // Say what program we are with a line like "User-Agent: Shareaza 1.2.3.4\r\n"
00306         CString strHeader = Settings.SmartAgent();
00307         if ( strHeader.GetLength() )
00308         {
00309                 strHeader = _T("User-Agent: ") + strHeader + _T("\r\n");
00310                 m_pOutput->Print( strHeader );
00311         }
00312 
00313         // The user has checked the box to connect to Gnutella2 today
00314         if ( Settings.Gnutella2.EnableToday && ( m_nProtocol != PROTOCOL_G1 ) )
00315         {
00316                 // If we initiated the connection to the remote computer
00317                 if ( m_bInitiated )
00318                 {
00319                         // Tell the remote computer we know how to read Gnutella and Gnutella2 packets
00320                         m_pOutput->Print( "Accept: application/x-gnutella2,application/x-gnutella-packets\r\n" );
00321                 }
00322                 else if ( m_bG2Accept ) // The remote computer contacted us, and accepts Gnutella2 packets
00323                 {
00324                         // Reply by saying we accept them also, and will be sending them
00325                         m_pOutput->Print( "Accept: application/x-gnutella2\r\n" );
00326                         m_pOutput->Print( "Content-Type: application/x-gnutella2\r\n" );
00327                 }
00328         }
00329 }
00330 
00331 // Sends Gnutella headers to the other computer
00332 void CShakeNeighbour::SendPublicHeaders()
00333 {
00334         // Tell the remote we are running Shareza with a header like "User-Agent: Shareaza 2.1.0.97"
00335         CString strHeader = Settings.SmartAgent();
00336         if ( strHeader.GetLength() )
00337         {
00338                 strHeader = _T("User-Agent: ") + strHeader + _T("\r\n");
00339                 m_pOutput->Print( strHeader );
00340         }
00341 
00342         // Tell the remote computer our IP address with a header like "Listen-IP: 67.176.34.172:6346"
00343         m_bSentAddress |= SendMyAddress(); // Returns true if the header is sent, set m_bSentAddress true once its sent
00344 
00345         // Tell the remote computer what IP address it has from here with a header like "Remote-IP: 81.103.192.245"
00346         strHeader.Format( _T("Remote-IP: %s\r\n"), (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) );
00347         m_pOutput->Print( strHeader );
00348 
00349         // If the settings say connect to Gnutella2 and this function got passed Gnutella2 or the unknown network
00350         if ( ( Settings.Gnutella2.EnableToday ) && ( m_nProtocol != PROTOCOL_G1 ) ) // The protocol ID is Gnutella2 or unknown
00351         {
00352                 // If we initiated the connection to the remote computer
00353                 if ( m_bInitiated )
00354                 {
00355                         // Tell the remote computer we accept Gnutella and Gnutella2 packets
00356                         m_pOutput->Print( "Accept: application/x-gnutella2,application/x-gnutella-packets\r\n" );
00357                 }
00358                 else if ( m_bG2Accept ) // The remote computer contacted us, and accepts Gnutella2 packets
00359                 {
00360                         // Tell the remote computer we accept Gnutella2 packets and will be sending it Gnutella2 packets
00361                         m_pOutput->Print( "Accept: application/x-gnutella2\r\n" );                      // We can read Gnutella2 packets
00362                         m_pOutput->Print( "Content-Type: application/x-gnutella2\r\n" );        // You will be getting them from us
00363                 }
00364         }
00365 
00366         // Shareaza Settings allow us to exchange compressed data with this computer
00367         if ( m_bCanDeflate )
00368         {
00369                 // Tell the remote computer we can accept compressed data
00370                 m_pOutput->Print( "Accept-Encoding: deflate\r\n" );
00371 
00372                 // If the remote computer connected to us and accepts compressed data
00373                 if ( ! m_bInitiated && m_bDeflateAccept )
00374                 {
00375                         // Tell it we will be sending compressed data
00376                         m_pOutput->Print( "Content-Encoding: deflate\r\n" );
00377                 }
00378         }
00379 
00380         // If the settings say connect to Gnutella and this function got passed Gnutella or the unknown network
00381         if ( ( Settings.Gnutella1.EnableToday ) && ( m_nProtocol != PROTOCOL_G2 ) ) // The protocol ID is Gnutella or unknown
00382         {
00383                 // Tell the remote computer all the Gnutella features we support
00384                 if ( Settings.Gnutella1.EnableGGEP ) m_pOutput->Print( "GGEP: 0.5\r\n" );                       // We support GGEP blocks
00385                 m_pOutput->Print( "Pong-Caching: 0.1\r\n" );                                                                            // We support pong caching
00386                 if ( Settings.Gnutella1.VendorMsg ) m_pOutput->Print( "Vendor-Message: 0.1\r\n" );      // We support vendor-specific messages
00387                 m_pOutput->Print( "X-Query-Routing: 0.1\r\n" );                                                                         // We support the query routing protocol
00388         }
00389 
00390         // If we initiated the connection to the remote computer and it is not an ultrapeer
00391         if ( m_bInitiated && m_bUltraPeerSet == TS_FALSE )
00392         {
00393                 // Really, we don't know if it's an ultrapeer or not yet
00394                 m_bUltraPeerSet = TS_UNKNOWN;
00395 
00396         } // The remote computer called us, or we called them and it's an ultrapeer or hasn't said yet
00397         else
00398         {
00399                 if ( m_nProtocol == PROTOCOL_G1 )
00400                 {
00401                         // Find out if we are an ultrapeer or at least eligible to become one soon
00402                         if ( Neighbours.IsG1Ultrapeer() || Neighbours.IsG1UltrapeerCapable() )
00403                         {
00404                                 // Tell the remote computer that we are an ultrapeer
00405                                 m_pOutput->Print( "X-Ultrapeer: True\r\n" );
00406 
00407                         } // We are not an ultrapeer nor are we elegible, and the settings say so too
00408                         else if ( Settings.Gnutella1.ClientMode != MODE_ULTRAPEER )
00409                         {
00410                                 // Tell the remote computer that we are not an ultrapeer, we are just a Gnutella leaf node
00411                                 m_pOutput->Print( "X-Ultrapeer: False\r\n" );
00412                         }
00413                 }
00414                 else // This protocol ID this method got passed is unknown or for something other than Gnutella
00415                 {
00416                         // Find out if we are a Gnutella2 hub, or at least eligible to become one soon
00417                         if ( Neighbours.IsG2Hub() || Neighbours.IsG2HubCapable() )
00418                         {
00419                                 // Tell the remote computer that we are a hub
00420                                 m_pOutput->Print( "X-Ultrapeer: True\r\n" );
00421 
00422                         } // We are not a hub nor are we eligible, and the settings say so too
00423                         else if ( Settings.Gnutella2.ClientMode != MODE_HUB )
00424                         {
00425                                 // Tell the remote computer that we are a leaf
00426                                 m_pOutput->Print( "X-Ultrapeer: False\r\n" );
00427                         }
00428                 }
00429         }
00430 }
00431 
00432 // Reply to a remote computer's headers with more headers, confirming Gnutella2 packets and data compression
00433 void CShakeNeighbour::SendPrivateHeaders()
00434 {
00435         // If we haven't told the other computer our IP address yet, do it now with a header like "Listen-IP: 1.2.3.4:5"
00436         if ( ! m_bSentAddress ) m_bSentAddress = SendMyAddress();
00437 
00438         // We initiated the connection to the remote computer, it's going to send us Gnutella2 packets, and it accepts them
00439         if ( m_bInitiated && m_bG2Send && m_bG2Accept && ( m_nProtocol != PROTOCOL_G1 ) )
00440         {
00441                 // Tell it we also accept gnutella2 packets, and we're also going to send them
00442                 m_pOutput->Print( "Accept: application/x-gnutella2\r\n" );
00443                 m_pOutput->Print( "Content-Type: application/x-gnutella2\r\n" );
00444         }
00445 
00446         // If we initiated the connection to the remote computer
00447         if ( m_bInitiated )
00448         {
00449                 // All the data from the remote computer is going to be compressed
00450                 if ( m_bDeflateSend ) 
00451                 {
00452                         // Tell it we accept compressed data too
00453                         m_pOutput->Print( "Accept-Encoding: deflate\r\n" );
00454                 }
00455                 
00456                 // The remote computer accepts compressed data
00457                 if ( m_bDeflateAccept ) 
00458                 {
00459                         // Tell it all the data we send it will be compressed
00460                         m_pOutput->Print( "Content-Encoding: deflate\r\n" );
00461                 }
00462         }
00463 }
00464 
00465 // Takes a line like "GNUTELLA/0.6 503 Need an Ultrapeer"
00466 // Sends it with the "X-Try-Ultrapeers:" header
00467 void CShakeNeighbour::SendHostHeaders(LPCTSTR pszMessage)
00468 {
00469         // Local variables
00470         DWORD nTime = time( NULL );     // The number of seconds since midnight on January 1, 1970
00471         CString strHosts, strHost;      // Text to describe other computers and just one
00472         CHostCacheHost* pHost;          // We'll use this to loop through host objects in the host cache
00473 
00474         // If this method was given a message
00475         if ( pszMessage )
00476         {
00477                 // Send it to the remote computer, along with the minimal headers
00478                 m_pOutput->Print( pszMessage );
00479                 m_pOutput->Print( "\r\n" );
00480                 SendMinimalHeaders(); // Say we are Shareaza, and understand Gnutella2 packets
00481         }
00482 
00483         // Compose text with IP address and online time information to help the remote computer find more Gnutella computers
00484         int nCount = Settings.Gnutella1.PongCount;              // The list can't be longer than the pong count
00485         if ( m_bG2Accept || m_bG2Send || m_bShareaza )  // The remote computer accepts Gnutella2 packets, sends them, or is Shareaza too
00486         {
00487                 // Loop through the Gnutella2 host cache from newest to oldest
00488                 for ( pHost = HostCache.Gnutella2.GetNewest() ; pHost && nCount > 0 ; pHost = pHost->m_pPrevTime )
00489                 {
00490                         // This host is still recent enough to tell another computer about
00491                         if ( pHost->CanQuote( nTime ) )
00492                         {
00493                                 // Compose text for one computer and add it to the string
00494                                 strHost = pHost->ToString();                                            // The host object composes text about itself
00495                                 if ( strHosts.GetLength() ) strHosts += _T(",");        // Separate each computer's info with a comma
00496                                 strHosts += strHost;                                                            // Add this computer's info to the big string
00497                                 nCount--;                                                                                       // Record that we documented another computer
00498                         }
00499                 }
00500         }
00501         else // The remote computer is running Gnutella
00502         {
00503                 // Loop through the Gnutella host cache from newest to oldest
00504                 for ( pHost = HostCache.Gnutella1.GetNewest() ; pHost && nCount > 0 ; pHost = pHost->m_pPrevTime )
00505                 {
00506                         // This host is still recent enough to tell another computer about
00507                         if ( pHost->CanQuote( nTime ) )
00508                         {
00509                                 // Compose text for one computer and add it to the string
00510                                 strHost = pHost->ToString();                                            // Like "24.98.97.155:6348 2004-12-18T23:47Z"
00511                                 if ( strHosts.GetLength() ) strHosts += _T(",");        // Separate each computer's info with a comma
00512                                 strHosts += strHost;                                                            // Add this computer's info to the big string
00513                                 nCount--;                                                                                       // Record that we documented another computer
00514                         }
00515                 }
00516         }
00517 
00518         // If we have any computers to tell the remote computer about
00519         if ( strHosts.GetLength() )
00520         {
00521                 // Send the information in a header like "X-Try-Ultrapeers: 24.98.97.155:6348 2004-12-18T23:47Z," and so on
00522                 m_pOutput->Print( "X-Try-Ultrapeers: " );
00523                 m_pOutput->Print( strHosts );
00524                 m_pOutput->Print( "\r\n" );
00525         }
00526 
00527         // If this method started the handshake with a message line, end it with the blank line
00528         if ( pszMessage ) m_pOutput->Print( "\r\n" ); // Sends a blank line, marking the end of the handshake
00529 }
00530 
00532 // CShakeNeighbour handshake response processor
00533 
00534 // Reads the first line of a new group of handshake headers from the remote computer, and sets the state to have OnRead read more
00535 // Returns true to keep reading the handshake, false if its over or you should stop
00536 BOOL CShakeNeighbour::ReadResponse()
00537 {
00538         // Read one header line from the handshake the remote computer has sent us
00539         CString strLine; // The line
00540         if ( ! m_pInput->ReadLine( strLine ) ) return TRUE; // The line is still arriving, return true to try this method again
00541         if ( strLine.GetLength() > 1024 ) strLine = _T("#LINE_TOO_LONG#"); // Make sure the line isn't too long
00542         theApp.Message( MSG_DEBUG, _T("%s: HANDSHAKE: %s"), (LPCTSTR)m_sAddress, (LPCTSTR)strLine ); // Report handshake lines
00543 
00544         // If we initiated the connection to the remote computer, and now it's responding with "GNUTELLA OK"
00545         if ( strLine == _T("GNUTELLA OK") && m_bInitiated )
00546         {
00547                 // (do)
00548                 OnHandshakeComplete();  // Deletes this CShakeNeighbour object
00549                 return FALSE;                   // Tell the method that called this one not to call us again, we're done here
00550 
00551         } // The line starts "GNUTELLA/0.6", and says something else after that
00552         else if ( strLine.GetLength() > 13 && strLine.Left( 12 ) == _T("GNUTELLA/0.6") )
00553         {
00554                 // It does not say "200 OK" after that
00555                 if ( strLine != _T("GNUTELLA/0.6 200 OK") )
00556                 {
00557                         // Clip out just the part that says why we were rejected, document it, and set the state to rejected
00558                         strLine = strLine.Mid( 13 );
00559                         theApp.Message( MSG_ERROR, IDS_HANDSHAKE_REJECTED, (LPCTSTR)m_sAddress, (LPCTSTR)strLine );
00560                         m_nState = nrsRejected; // Set the neigbour state in this CShakeNeighbour object to rejected
00561 
00562                 } // It does say "200 OK", and the remote computer contacted us
00563                 else if ( ! m_bInitiated )
00564                 {
00565                         // The remote computer connected to us and sent its headers, we replied with ours, and now it's sending the final group
00566                         m_nState = nrsHandshake3; // We're reading the final header group from the remote computer
00567 
00568                 } // It does say "200 OK", and we initiated the connection
00569                 else
00570                 {
00571                         // We connected and sent our headers, now the remote computer is responding
00572                         m_nState = nrsHandshake2; // We're reading the initial header group the remote computer has sent
00573                 }
00574 
00575         } // The remote computer connected to us, and wants to talk with the old 0.4 protocol
00576         else if ( strLine == _T("GNUTELLA CONNECT/0.4") && ! m_bInitiated )
00577         {
00578                 // Gnutella 0.4 is too old for us, close the connection
00579                 DelayClose( IDS_HANDSHAKE_SURPLUS );    // Send the buffer then close the socket, citing the reason the connection didn't work out
00580                 return FALSE;                                                   // Tell the method that called this one to stop calling us
00581 
00582         } // The remote computer connected to us, and wants to talk Gnutella
00583         else if ( strLine.GetLength() > 17 && strLine.Left( 17 ) == _T("GNUTELLA CONNECT/") && ! m_bInitiated )
00584         {
00585                 // We're reading the initial header group the remote computer has sent
00586                 m_nState = nrsHandshake2;
00587 
00588         } // The remote computer connected to us, and said "SHAREAZABETA CONNECT/"
00589         else if ( strLine.GetLength() > 21 && strLine.Left( 21 ) == _T("SHAREAZABETA CONNECT/") && ! m_bInitiated )
00590         {
00591                 // We're reading the initial header group the remote computer has sent
00592                 m_nState = nrsHandshake2;
00593 
00594         } // The remote computer said something else that we aren't expecting here
00595         else
00596         {
00597                 // Close the connection, citing the reason as we can't understand the handshake
00598                 Close( IDS_HANDSHAKE_FAIL );
00599                 return FALSE; // Tell the calling method to stop calling us
00600         }
00601 
00602         // Tell the calling method it can call us some more to read more lines from the handshake
00603         return TRUE;
00604 }
00605 
00607 // CShakeNeighbour handshake header processing
00608 
00609 // Takes a handshake header and value parsed from a line sent by the remote computer
00610 // Reads it and sets member variables to reflect the remote computer's capabilities
00611 // Returns true to keep going, or false to indicate the handshake is over or we should stop trying to read it
00612 BOOL CShakeNeighbour::OnHeaderLine(CString& strHeader, CString& strValue)
00613 {
00614         // Record this header
00615         theApp.Message( MSG_DEBUG, _T("%s: GNUTELLA HEADER: %s: %s"), (LPCTSTR)m_sAddress, (LPCTSTR)strHeader, (LPCTSTR)strValue );
00616 
00617         // It's the "User-Agent" header, which will tell the name and version of the remote program
00618         if ( strHeader.CompareNoCase( _T("User-Agent") ) == 0 )
00619         {
00620                 // Save the name and version of the remote program
00621                 m_sUserAgent = strValue;
00622 
00623                 // If the text contains "Shareaza"
00624                 if ( _tcsistr( m_sUserAgent, _T("Shareaza") ) ) 
00625                 {
00626                         // Record that the remote computer is running Shareaza
00627                         m_bShareaza = TRUE;
00628                 } 
00629 
00630                 // Check if it's an old version of Shareaza
00631                 if ( IsClientObsolete() )
00632                 {
00633                         m_bObsoleteClient = TRUE;
00634                 }
00635 
00636                 // If the remote computer is running a client that is breaking GPL, causing problems, etc.
00637                 // We don't actually ban these clients, but we don't accept them as a leaf. Can still upload, though.
00638                 if ( IsClientBad() )
00639                 {
00640                         // Remember this is a bad client.
00641                         m_bBadClient = TRUE;
00642                 }
00643 
00644                 // Actual leechers and hostile clients. (We do ban these)
00645                 if ( IsClientBanned() )
00646                 {
00647                         // Reject the handshake
00648                         theApp.Message( MSG_ERROR, IDS_HANDSHAKE_REJECTED, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00649                         m_nState = nrsRejected;
00650                         // Ban them and ignore anything else in the headers
00651                         theApp.Message( MSG_ERROR, _T("Banning hostile client %s"), (LPCTSTR)m_sUserAgent );
00652                         Security.Ban( &m_pHost.sin_addr, ban2Hours, FALSE );
00653                         m_bBadClient = TRUE;
00654                         return TRUE;
00655                 }
00656                 
00657                 // If the remote computer is running a client the user has blocked
00658                 if ( IsAgentBlocked() )
00659                 {
00660                         // Record that we're rejecting this handshake, and set the state to rejected
00661                         theApp.Message( MSG_ERROR, IDS_HANDSHAKE_REJECTED, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00662                         m_nState = nrsRejected;
00663                         return TRUE;
00664                 }
00665 
00666         } // The remote computer is telling us our IP address
00667         else if ( strHeader.CompareNoCase( _T("Remote-IP") ) == 0 )
00668         {
00669                 // Give the value, which is text like "1.2.3.4", to the Network object
00670                 Network.AcquireLocalAddress( strValue );
00671 
00672         } // The remote computer is telling us its IP address
00673         else if (       strHeader.CompareNoCase( _T("X-My-Address") ) == 0 ||
00674                                 strHeader.CompareNoCase( _T("Listen-IP") ) == 0 ||
00675                                 strHeader.CompareNoCase( _T("X-Node") ) == 0 ||
00676                                 strHeader.CompareNoCase( _T("Node") ) == 0 )
00677         {
00678                 // Find the index of the first colon in the text
00679                 int nColon = strValue.Find( ':' );
00680                 if ( nColon > 0 ) // There is a colon and it's not at the start of the text
00681                 {
00682                         // Save the default Gnutella port, 6346, in nPort to use it if we can't read the port number from the header value text
00683                         int nPort = GNUTELLA_DEFAULT_PORT;
00684 
00685                         // Mid clips the strValue text from beyond the colon to the end
00686                         // _stscanf is like scanf, and %1u means read the text as a long unsigned number
00687                         // The If block makes sure that _stscanf successfully reads 1 item, and the number it read isn't 0
00688                         if (_stscanf( strValue.Mid( nColon + 1 ), _T("%lu"), &nPort ) == 1 && nPort != 0 )
00689                         {
00690                                 // Save the remote computer port number in the connection object's m_pHost member variable
00691                                 m_pHost.sin_port = htons( nPort ); // Call htons to go from PC little-endian to Internet big-endian byte order
00692                         }
00693                 }
00694 
00695         } // The remote computer is telling us it supports Gnutella pong caching
00696         else if ( strHeader.CompareNoCase( _T("Pong-Caching") ) == 0 )
00697         {
00698                 // Record this ability in the member variable
00699                 m_bPongCaching = TRUE;
00700 
00701         } // The remote computer is telling us it supports vendor-specific Gnutella messages
00702         else if ( strHeader.CompareNoCase( _T("Vendor-Message") ) == 0 )
00703         {
00704                 // Record this ability in the member variable
00705                 m_bVendorMsg = TRUE;
00706 
00707         } // The remote computer is telling us it supports Gnutella query routing
00708         else if ( strHeader.CompareNoCase( _T("X-Query-Routing") ) == 0 )
00709         {
00710                 // Only set m_bQueryRouting true if the value isn't "0" or "0.0"
00711                 m_bQueryRouting = ( strValue != _T("0") && strValue != _T("0.0") );
00712 
00713         } // The remote computer is telling us if it is an hub or a leaf
00714         else if (       strHeader.CompareNoCase( _T("X-Ultrapeer") ) == 0 ||
00715                                 strHeader.CompareNoCase( _T("X-Hub") ) == 0 )
00716         {
00717                 // If the value is the text "True", set m_bUltraPeerSet to true, otherwise set it to false
00718                 m_bUltraPeerSet = ( strValue.CompareNoCase( _T("True") ) == 0 ) ? TS_TRUE : TS_FALSE;
00719 
00720         } // The remote computer is telling us if it needs more connections to ultrapeers or not
00721         else if (       strHeader.CompareNoCase( _T("X-Ultrapeer-Needed") ) == 0 ||
00722                                 strHeader.CompareNoCase( _T("X-Hub-Needed") ) == 0 )
00723         {
00724                 // If the value is the text "True", set m_bUltraPeerNeeded to true, otherwise set it to false
00725                 m_bUltraPeerNeeded = ( strValue.CompareNoCase( _T("True") ) == 0 ) ? TS_TRUE : TS_FALSE;
00726 
00727         } // The remote computer is telling us the "X-Ultrapeer-Loaded" header, which may not be in use (do)
00728         else if (       strHeader.CompareNoCase( _T("X-Ultrapeer-Loaded") ) == 0 ||
00729                                 strHeader.CompareNoCase( _T("X-Hub-Loaded") ) == 0 )
00730         {
00731                 // If the value is the text "True", set m_bUltraPeerLoaded to true, otherwise set it to false
00732                 m_bUltraPeerLoaded = ( strValue.CompareNoCase( _T("True") ) == 0 ) ? TS_TRUE : TS_FALSE;
00733 
00734         } // The remote computer is telling us it understands GGEP blocks
00735         else if ( strHeader.CompareNoCase( _T("GGEP") ) == 0 )
00736         {
00737                 // If we also have GGEP blocks enabled
00738                 if ( Settings.Gnutella1.EnableGGEP )
00739                 {
00740                         // And if the value the remote computer sent isn't "0" or "0.0", set m_bGGEP true
00741                         m_bGGEP = ( strValue != _T("0") && strValue != _T("0.0") );
00742                 }
00743 
00744         } // The remote computer is telling us it accepts and can understand a kind of packets
00745         else if ( strHeader.CompareNoCase( _T("Accept") ) == 0 && Settings.Gnutella2.EnableToday ) // And we're connected to Gnutella2
00746         {
00747                 // Set m_bG2Accept to true if it accepts Gnutella2 or Shareaza packets
00748                 m_bG2Accept |= ( strValue.Find( _T("application/x-gnutella2") ) >= 0 );
00749                 m_bG2Accept |= ( strValue.Find( _T("application/x-shareaza") ) >= 0 );
00750 
00751         } // The remote computer is telling us it is sending a kind of packets
00752         else if ( strHeader.CompareNoCase( _T("Content-Type") ) == 0 && Settings.Gnutella2.EnableToday ) // And we're connected to Gnutella2
00753         {
00754                 // Set m_bG2Send to true if it is sending Gnutella2 or Shareaza packets
00755                 m_bG2Send |= ( strValue.Find( _T("application/x-gnutella2") ) >= 0 );
00756                 m_bG2Send |= ( strValue.Find( _T("application/x-shareaza") ) >= 0 );
00757 
00758         } // The remote computer is telling us it can accept compressed data, and the settings allow us to do compression
00759         else if ( strHeader.CompareNoCase( _T("Accept-Encoding") ) == 0 && m_bCanDeflate ) // Settings allow us to do compression
00760         {
00761                 // Look for the text "deflate", and make m_bDeflateAccept true if it's found
00762                 m_bDeflateAccept |= ( strValue.Find( _T("deflate") ) >= 0 );
00763 
00764         } // The remote computer is telling us it will send compressed data, and the settings allow us to do compression
00765         else if ( strHeader.CompareNoCase( _T("Content-Encoding") ) == 0 && m_bCanDeflate ) // Settings allow us to do compression
00766         {
00767                 // Look for the text "deflate", and make m_bDeflateSend true if it's found
00768                 m_bDeflateSend |= ( strValue.Find( _T("deflate") ) >= 0 );
00769 
00770         } // The remote computer is giving us a list of IP addreses we can try to contact more ultrapeers
00771         else if (       strHeader.CompareNoCase( _T("X-Try-Ultrapeers") ) == 0 ||
00772                                 strHeader.CompareNoCase( _T("X-Try-Hubs") ) == 0 )
00773         {
00774                 // Some clients send bad data here- ignore it.
00775                 if ( ! m_bBadClient )
00776                 {
00777                         int nCount = 0;
00778                         // Append a comma onto the end of the value text once, and then loop forever
00779                         for ( strValue += ',' ; ; ) // for (;;) is the same thing as forever
00780                         {
00781                                 // Get the port number we are listening on from settings, 6346 by default
00782                                 int nPort = Settings.Connection.InPort; // Not used (do)
00783 
00784                                 // Find the first comma in the value text
00785                                 int nPos = strValue.Find( ',' ); // Set nPos to the distance in characters from the start to the comma
00786                                 if ( nPos < 0 ) break;           // If no comma was found, leave the loop
00787 
00788                                 // Move the text before the comma from the value string to a new string for the host
00789                                 CString strHost = strValue.Left( nPos ); // Copy the text up to the comma into strHost
00790                                 strValue = strValue.Mid( nPos + 1 );     // Clip that text and the comma off the start of strValue
00791 
00792                                 // The remote computer accepts Gnutella2 packets, is sending them, or is Shareaza
00793                                 if ( m_bG2Accept || m_bG2Send || m_bShareaza )
00794                                 {
00795                                         // Add the host to the Gnutella2 host cache, sending the text "RAZA" along if the remote computer is Shareaza
00796                                         if ( HostCache.Gnutella2.Add( strHost, 0, m_bShareaza ? SHAREAZA_VENDOR_T : NULL ) ) nCount++; // Count it
00797 
00798                                 } // This is a Gnutella connection, not Gnutella2
00799                                 else
00800                                 {
00801                                         // Add the host to the Gnutella2 host cache, sending the text "RAZA" along if the remote computer is Shareaza
00802                                         if ( HostCache.Gnutella1.Add( strHost, 0, m_bShareaza ? SHAREAZA_VENDOR_T : NULL ) ) nCount++; // Count it
00803                                 }
00804                         }
00805 
00806                         // Tell discovery services the remote computer's IP address, and how many hosts it just told us about
00807                         DiscoveryServices.OnGnutellaAdded( &m_pHost.sin_addr, nCount );
00808                 }
00809         }
00810 
00811         // Report success
00812         return TRUE;
00813 }
00814 
00816 // CShakeNeighbour handshake end of headers
00817 
00818 // Called when CConnection::ReadHeaders calls ReadLine and gets a blank line, meaning a group of headers from the remote computer is done
00819 // Responds to the group of headers from the remote computer by closing the connection, sending response headers, or copying this object for a protocol
00820 // Returns false to delete this object, or true to keep reading headers and negotiating the handshake
00821 BOOL CShakeNeighbour::OnHeadersComplete()
00822 {
00823         // If the remote computer doesn't accept Gnutella2 packets, or it does but we contacted it and it's not going to send them
00824         if ( ! m_bG2Accept || ( m_bInitiated && ! m_bG2Send ) )
00825         {
00826                 // This is a Gnutella connection
00827                 m_nProtocol = PROTOCOL_G1;
00828                 return OnHeadersCompleteG1();
00829         }
00830         else
00831         {
00832                 // This is a G2 connection
00833                 m_nProtocol = PROTOCOL_G2;
00834                 return OnHeadersCompleteG2();
00835         }
00836 }
00837 
00838 // Called when CConnection::ReadHeaders calls ReadLine and gets a blank line, meaning a group of headers from the remote computer is done
00839 // Responds to the group of headers from the remote computer by closing the connection, sending response headers, or turning this into a Gnutella2 object
00840 // Returns false to delete this object, or true to keep reading headers and negotiating the handshake
00841 BOOL CShakeNeighbour::OnHeadersCompleteG2()
00842 {
00843         // Report that a set of Gnutella2 headers from a remote computer are complete
00844         theApp.Message( MSG_DEBUG, _T("Headers Complete: G2 client") );
00845 
00846         // The remote computer replied to our headers with something other than "200 OK"
00847         if ( m_nState == nrsRejected )
00848         {
00849                 // Close the connection
00850                 Close( 0 );   // Don't specify an error
00851                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00852 
00853         } // We've been reading response headers from a remote computer that contacted us
00854         else if ( m_nState == nrsHandshake3 ) // We were reading the final header group from the remote computer
00855         {
00856                 // (do)
00857                 if ( m_bUltraPeerSet == TS_FALSE                                 // The remote computer told us it's a leaf
00858                         && m_nNodeType == ntNode                                     // And this is a connection to a hub like us
00859                         && ( Neighbours.IsG2Hub() || Neighbours.IsG2HubCapable() ) ) // And we're either a hub or capable of becoming one
00860                 {
00861                         // Report this case
00862                         theApp.Message( MSG_DEFAULT, IDS_HANDSHAKE_BACK2LEAF, (LPCTSTR)m_sAddress );
00863 
00864                         // This connection is to a leaf below us
00865                         m_nNodeType = ntLeaf;
00866                 }
00867 
00868                 // Turn this CShakeNeighbour object into a CG2Neighbour object
00869                 OnHandshakeComplete();
00870                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00871 
00872         } // We initiated the connection
00873         else if ( m_bInitiated )
00874         {
00875                 // We'll set this flag to true if we need to tell the remote computer we're a leaf so we can connect
00876                 BOOL bFallback = FALSE;
00877 
00878                 // We are a Gnutella2 hub, or at least we are capable of becomming one
00879                 if ( Neighbours.IsG2Hub() || Neighbours.IsG2HubCapable() )
00880                 {
00881                         // The remote computer sent us a header like "X-Ultrapeer: False"
00882                         if ( m_bUltraPeerSet == TS_FALSE )
00883                         {
00884                                 // This connection is to a leaf below us
00885                                 m_nNodeType = ntLeaf;
00886 
00887                         } // The remote computer sent us headers like "X-Ultrapeer: True" and "X-Ultrapeer-Needed: True"
00888                         else if ( m_bUltraPeerSet == TS_TRUE && m_bUltraPeerNeeded == TS_TRUE ) // && Network.NeedMoreHubs() )
00889                         {
00890                                 // Record that we are both hubs
00891                                 m_nNodeType = ntNode;
00892 
00893                         } // The remote computer is an hub that doesn't need connections to more hubs
00894                         else if ( m_bUltraPeerSet == TS_TRUE && m_bUltraPeerNeeded == TS_FALSE )
00895                         {
00896                                 // If we are connected to any leaves
00897                                 if ( Neighbours.GetCount( PROTOCOL_G2, nrsConnected, ntLeaf ) > 0 )
00898                                 {
00899                                         // Tell the remote computer we can't connect (do)
00900                                         SendHostHeaders( _T("GNUTELLA/0.6 503 I have leaves") );
00901                                         DelayClose( IDS_HANDSHAKE_CANTBEPEER ); // Send the buffer then close the socket
00902                                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00903                                 }
00904 
00905                                 // If we are a Gnutella2 hub
00906                                 if ( Settings.Gnutella2.ClientMode == MODE_HUB )
00907                                 {
00908                                         // We are a hub, and the remote computer doesn't need any more hub connections, tell it we can't connect
00909                                         SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") );
00910                                         DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Send the buffer then close the socket
00911                                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00912                                 }
00913 
00914                                 // We are or can become an ultrapeer, and the remote computer is an ultrapeer that doesn't need any more ultrapeer connections
00915                                 m_nNodeType = ntHub; // Pretend this connection is to a hub above us
00916                                 bFallback = TRUE;    // We'll tell the remote computer we're a leaf so we can still connect
00917                         }
00918 
00919                 } // The remote computer is a hub
00920                 else if ( m_bUltraPeerSet == TS_TRUE )
00921                 {
00922                         // And so are we
00923                         if ( Settings.Gnutella2.ClientMode == MODE_HUB )
00924                         {
00925                                 // (do)
00926                                 SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") );
00927                                 DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Send the buffer then close the socket
00928                                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00929                         }
00930 
00931                         // This connection is to a hub above us
00932                         m_nNodeType = ntHub;
00933 
00934                 } // The remote computer is a leaf, or it hasn't told us yet
00935                 else if ( m_bUltraPeerSet != TS_TRUE )
00936                 {
00937                         // We are a leaf
00938                         if ( Settings.Gnutella2.ClientMode == MODE_LEAF )
00939                         {
00940                                 // Tell the remote computer we can't connect because we need a hub
00941                                 SendHostHeaders( _T("GNUTELLA/0.6 503 Need an Ultrapeer") );
00942                                 DelayClose( IDS_HANDSHAKE_NEEDAPEER ); // Send the buffer then close the socket
00943                                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00944                         }
00945                 }
00946 
00947                 // If the connection doesn't fall into any of those error cases, accept it with a batch of reply headers
00948                 m_pOutput->Print( "GNUTELLA/0.6 200 OK\r\n" );                 // Accept the connection
00949                 SendPrivateHeaders();                                          // Respond to each header, setting up Gnutella2 packets and compression
00950                 if ( bFallback ) m_pOutput->Print( "X-Ultrapeer: False\r\n" ); // Tell it we're a leaf so we can still connect
00951                 m_pOutput->Print( "\r\n" );                                    // Send a blank line to end this group of headers
00952 
00953                 // Turn this CShakeNeighbour object into a CG2Neighbour one, and delete this one
00954                 OnHandshakeComplete();
00955                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00956 
00957         } // not rejected, not handshake3, not initiated (do)
00958         else
00959         {
00960                 // We are a leaf
00961                 if ( Neighbours.IsG2Leaf() )
00962                 {
00963                         // Tell the remote computer we can't connect because we are a shielded leaf right now
00964                         SendHostHeaders( _T("GNUTELLA/0.6 503 Shielded leaf node") );
00965                         DelayClose( IDS_HANDSHAKE_IAMLEAF ); // Send the buffer then close the socket
00966                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
00967 
00968                 } // We are a Gnutella2 hub, or at least we are capable of becomming one
00969                 else if ( Neighbours.IsG2Hub() || Neighbours.IsG2HubCapable() )
00970                 {
00971                         // The remote computer sent us the header "X-Ultrapeer: False"
00972                         if ( m_bUltraPeerSet == TS_FALSE )
00973                         {
00974                                 // This connection is to a leaf below us
00975                                 m_nNodeType = ntLeaf;
00976 
00977                         } // The remote computer sent us the header "X-Ultrapeer: True"
00978                         else if ( m_bUltraPeerSet == TS_TRUE )
00979                         {
00980                                 // Record that we are both hubs
00981                                 m_nNodeType = ntNode;
00982                         }
00983 
00984                 } // The remote computer is an ultrapeer, and we are not running in hub mode
00985                 else if ( m_bUltraPeerSet == TS_TRUE && ( Settings.Gnutella2.ClientMode != MODE_HUB ) )
00986                 {
00987                         // This connection is to a hub above us
00988                         m_nNodeType = ntHub;
00989                 }
00990 
00991                 // If it's a leaf, check version, etc
00992                 if ( m_nNodeType == ntLeaf ) 
00993                 {
00994                         if ( m_bBadClient ) 
00995                         {
00996                                 // We don't allow these to act as a leaf. (resource use, etc)
00997                                 if ( m_bObsoleteClient ) 
00998                                 {
00999                                         theApp.Message( MSG_ERROR, _T("Rejecting obsolete leaf client %s") , (LPCTSTR)m_sUserAgent );
01000                                         m_pOutput->Print( "GNUTELLA/0.6 503 Update your client. www.shareaza.com\r\n" );
01001                                 }
01002                                 else 
01003                                 {
01004                                         theApp.Message( MSG_ERROR, _T("Rejecting bad leaf client %s") , (LPCTSTR)m_sUserAgent );
01005                                         m_pOutput->Print( "GNUTELLA/0.6 503 Refused\r\n" );
01006                                 }
01007                                 SendMinimalHeaders();  
01008                                 DelayClose( IDS_HANDSHAKE_SURPLUS );
01009                                 return FALSE; 
01010                         }
01011                         else if ( m_bObsoleteClient ) 
01012                         {
01013                                 // Check our loading. Old clients consume more resources, so we might not be able to accept it
01014                                 if ( Neighbours.GetCount(PROTOCOL_G2, nrsConnected ,ntLeaf ) > ( Settings.Gnutella2.NumLeafs / 2 ) )
01015                                 {
01016                                         theApp.Message( MSG_ERROR, _T("Rejecting obsolete leaf %s (We are too full)") , (LPCTSTR)m_sUserAgent );
01017                                         m_pOutput->Print( "GNUTELLA/0.6 503 Old client version, please update. www.shareaza.com\r\n" );
01018                                         SendMinimalHeaders();  
01019                                         DelayClose( IDS_HANDSHAKE_SURPLUS );
01020                                         return FALSE;
01021                                 }
01022                         }
01023                         else if ( ! m_bShareaza )
01024                         {
01025                                 // Check to see if we have enough free leaf slots.
01026                                 if ( Neighbours.GetCount(PROTOCOL_G2, nrsConnected ,ntLeaf ) > ( Settings.Gnutella2.NumLeafs - 5 ) )
01027                                 {
01028                                         SendHostHeaders( _T("GNUTELLA/0.6 503 Maximum connections reached") );
01029                                         DelayClose( IDS_HANDSHAKE_SURPLUS );
01030                                         return FALSE;
01031                                 }
01032                         }
01033                 }
01034 
01035                 // If we don't need another connection to the role the remote computer is acting in
01036                 if ((
01037                                 // If the remote computer is a leaf and we don't need any more
01038                                 m_nNodeType == ntLeaf                        // This connection is to a leaf below us
01039                                 && ! Neighbours.NeedMoreHubs( PROTOCOL_G2 )  // And the neighbours object says we don't need more Gnutella2 hubs or leaves
01040                                 && ! Neighbours.NeedMoreLeafs( PROTOCOL_G2 )
01041                         ) || (
01042 
01043                                 // Or, if the remote computer is a hub and we don't need any more
01044                                 m_nNodeType != ntLeaf                        // This connection isn't to a leaf below us
01045                                 && ! Neighbours.NeedMoreHubs( PROTOCOL_G2 )  // And the neighbours object says we don't need any more Gnutella2 hubs
01046                         ))
01047                 {
01048                         // Tell the remote computer that we can't connect because we have too many connections already, and close the connection
01049                         SendHostHeaders( _T("GNUTELLA/0.6 503 Maximum connections reached") ); // Send this error code along with some more IP addresses it can try
01050                         DelayClose( IDS_HANDSHAKE_SURPLUS ); // Close the connection, but not until we've written the buffered outgoing data first
01051                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01052 
01053                 } // Or, if we're both in the same role on the network
01054                 else if ( ( m_nNodeType == ntHub && ( Settings.Gnutella2.ClientMode == MODE_HUB ) ) ||  // We're both hubs
01055                                   ( m_nNodeType == ntLeaf && ( Settings.Gnutella2.ClientMode == MODE_LEAF ) ) ) // We're both leaves
01056                 {
01057                         // Tell the remote computer that we can't connect
01058                         SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") ); // Send the error code along with more IP addresses the remote computer can try
01059                         DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Close the connection, but not until we've written the buffered outgoing data first
01060                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01061 
01062                 } // This connection isn't to a hub above us, and we're a leaf
01063                 else if ( m_nNodeType != ntHub && ( Settings.Gnutella2.ClientMode == MODE_LEAF ) )
01064                 {
01065                         // Tell the remote computer we can't connect because we need a hub
01066                         SendHostHeaders( _T("GNUTELLA/0.6 503 Need an Ultrapeer") );
01067                         DelayClose( IDS_HANDSHAKE_NEEDAPEER ); // Send the buffer then close the socket
01068                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01069 
01070                 } // Otherwise, the connection is probably alright
01071                 else
01072                 {
01073                         // Send reply headers to the remote computer
01074                         m_pOutput->Print( "GNUTELLA/0.6 200 OK\r\n" );  // Start our group of response headers to the other computer with the 200 OK message
01075                         SendPublicHeaders();                                                    // Send the initial Gnutella2 headers
01076                         SendPrivateHeaders();                                                   // Send headers in response to those we got from the remote computer
01077                         SendHostHeaders();                                                              // Send the "X-Try-Ultrapeers" header with a list of other IP addresses running Gnutella
01078 
01079                         // If this connection is up to a hub or to a hub like us, and we need more hubs
01080                         if ( m_nNodeType != ntLeaf && Neighbours.NeedMoreHubs( PROTOCOL_G2 ) )
01081                         {
01082                                 // And if we're a hub, so this connection must be to another hub
01083                                 if ( Settings.Gnutella2.ClientMode != MODE_LEAF )
01084                                 {
01085                                         // Tell the remote computer we want more connections to hubs
01086                                         m_pOutput->Print( "X-Ultrapeer-Needed: True\r\n" );
01087                                 }
01088 
01089                         } // This connection must be down to a leaf
01090                         else
01091                         {
01092                                 // And we're a leaf
01093                                 if ( Settings.Gnutella2.ClientMode != MODE_HUB )
01094                                 {
01095                                         // Tell the remote computer we don't need any more hub connections
01096                                         m_pOutput->Print( "X-Ultrapeer-Needed: False\r\n" );
01097                                 }
01098                         }
01099 
01100                         // End this block of headers with a blank line
01101                         m_pOutput->Print( "\r\n" );
01102                         m_nState = nrsHandshake1; // We've finished sending a group of headers, and await the response
01103                 }
01104         }
01105 
01106         // Have OnRead keep exchanging handshake headers with the remote computer
01107         return TRUE;
01108 }
01109 
01110 // Called when CConnection::ReadHeaders calls ReadLine and gets a blank line, meaning a group of headers from the remote computer is done
01111 // Responds to the group of headers from the remote computer by closing the connection, sending response headers, or turning this into a Gnutella object
01112 // Returns false to delete this object, or true to keep reading headers and negotiating the handshake
01113 BOOL CShakeNeighbour::OnHeadersCompleteG1()
01114 {
01115         // Report that a set of Gnutella headers from a remote computer are complete
01116         theApp.Message( MSG_DEBUG, _T("Headers Complete: G1 client") );
01117 
01118         // Check if Gnutella1 is enabled before connecting to a gnutella client
01119         if ( ! Settings.Gnutella1.EnableToday && m_nState < nrsRejected ) // And make sure the state is before getting rejected
01120         {
01121                 // Tell the remote computer that we're only connecting to Gnutella2 today
01122                 m_pOutput->Print( "GNUTELLA/0.6 503 G2 Required\r\n" );
01123                 SendMinimalHeaders();       // Tell the remote computer we're Shareaza and we can exchange Gnutella2 packets
01124                 m_pOutput->Print( "\r\n" ); // End the group of headers with a blank line
01125 
01126                 // Tell the host cache that this didn't work out
01127                 HostCache.OnFailure( &m_pHost.sin_addr, htons( m_pHost.sin_port ) );
01128 
01129                 // Close the connection citing not Gnutella2 as the reason, but send the departing buffer first
01130                 DelayClose( IDS_HANDSHAKE_NOTG2 );
01131 
01132                 // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01133                 return FALSE;
01134         }
01135 
01136         // The remote computer started with "GNUTELLA/0.6", but did not say "200 OK"
01137         if ( m_nState == nrsRejected )
01138         {
01139                 // Close the connection
01140                 Close( 0 );   // Don't specify an error
01141                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01142 
01143         } // We've been reading response headers from a remote computer that contacted us
01144         else if ( m_nState == nrsHandshake3 ) // We're reading the final header group from the remote computer
01145         {
01146                 // If we're both leaves, and yet somehow we're also either an ultrapeer or can become one (do)
01147                 if ( m_bUltraPeerSet == TS_FALSE && m_nNodeType == ntNode &&               // The remote computer is a hub and so are we
01148                          ( Neighbours.IsG1Ultrapeer() || Neighbours.IsG1UltrapeerCapable() ) ) // And, we're either a Gnutella ultrapeer or we could become one
01149                 {
01150                         // Report that the handshake is back to a leaf (do), and consider this connection to be to a leaf below us
01151                         theApp.Message( MSG_DEFAULT, IDS_HANDSHAKE_BACK2LEAF, (LPCTSTR)m_sAddress );
01152                         m_nNodeType = ntLeaf; // This connection is to a leaf below us
01153                 }
01154 
01155                 // Turn this CShakeNeighbour object into a CG1Neighbour object
01156                 OnHandshakeComplete();
01157                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01158 
01159         } // We initiated the connection to the remote computer
01160         else if ( m_bInitiated )
01161         {
01162                 // We'll set this flag to true if we need to tell the remote computer we're a leaf so we can connect
01163                 BOOL bFallback = FALSE;
01164 
01165                 // We are an ultrapeer or at least we are capable of becomming one
01166                 if ( Neighbours.IsG1Ultrapeer() || Neighbours.IsG1UltrapeerCapable() )
01167                 {
01168                         // The remote computer told us "X-Ultrapeer: False"
01169                         if ( m_bUltraPeerSet == TS_FALSE )
01170                         {
01171                                 // This connection is to a leaf below us
01172                                 m_nNodeType = ntLeaf;
01173 
01174                         } // The remote computer told us it's an ultrapeer and that it needs connections to more ultrapeers
01175                         else if ( m_bUltraPeerSet == TS_TRUE && m_bUltraPeerNeeded == TS_TRUE )
01176                         {
01177                                 // Record that we are both ultrapeers
01178                                 m_nNodeType = ntNode;
01179 
01180                         } // The remote computer is an ultrapeer that doesn't need any more ultrapeer connections
01181                         else if ( m_bUltraPeerSet == TS_TRUE && m_bUltraPeerNeeded == TS_FALSE )
01182                         {
01183                                 // (do)
01184                                 if ( Neighbours.GetCount( PROTOCOL_G1, nrsConnected, ntLeaf ) > 0 )
01185                                 {
01186                                         // Tell the remote computer we can't connect (do)
01187                                         SendHostHeaders( _T("GNUTELLA/0.6 503 I have leaves") );
01188                                         DelayClose( IDS_HANDSHAKE_CANTBEPEER ); // Send the buffer then close the socket
01189                                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01190                                 }
01191 
01192                                 // We are an ultrapeer
01193                                 if ( Settings.Gnutella1.ClientMode == MODE_ULTRAPEER )
01194                                 {
01195                                         // Tell the remote computer we can't connect
01196                                         SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") );
01197                                         DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Send the buffer then close the socket
01198                                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01199                                 }
01200 
01201                                 // We are or can become an ultrapeer, and the remote computer is an ultrapeer that doesn't need any more ultrapeer connections
01202                                 m_nNodeType = ntHub; // Pretend this connection is to a hub above us
01203                                 bFallback = TRUE;    // We'll tell the remote computer we're a leaf so we can still connect
01204                         }
01205 
01206                 } // We're a leaf, and the remote computer is an ultrapeer
01207                 else if ( m_bUltraPeerSet == TS_TRUE )
01208                 {
01209                         // We are an ultrapeer
01210                         if ( Settings.Gnutella1.ClientMode == MODE_ULTRAPEER )
01211                         {
01212                                 // Tell the remote computer we can't connect
01213                                 SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") );
01214                                 DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Send the buffer then close the socket
01215                                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01216                         }
01217 
01218                         // This connection is to a hub above us
01219                         m_nNodeType = ntHub;
01220 
01221                 } // The remote computer is a leaf
01222                 else if ( m_bUltraPeerSet != TS_TRUE )
01223                 {
01224                         // And so are we
01225                         if ( Settings.Gnutella1.ClientMode == MODE_LEAF )
01226                         {
01227                                 // Tell the remote computer that we can't connect because we're both leaves
01228                                 SendHostHeaders( _T("GNUTELLA/0.6 503 Need an Ultrapeer") );
01229                                 DelayClose( IDS_HANDSHAKE_NEEDAPEER ); // Send the buffer, close the socket, cite need a peer as the reason
01230                                 return FALSE;                          // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01231                         }
01232                 }
01233 
01234                 // Tell the remote computer that we accept the connection, and send reply headers
01235                 m_pOutput->Print( "GNUTELLA/0.6 200 OK\r\n" ); // Begin our response headers with the OK message
01236                 SendPrivateHeaders();                          // Respond to each header, setting up Gnutella2 packets and compression
01237 
01238                 // If we are an ultrapeer or could become one, but the remote computer is an ultrapeer that doesn't need any more ultrapeer connections
01239                 if ( bFallback ) m_pOutput->Print( "X-Ultrapeer: False\r\n" ); // Tell it we're a leaf so we can still connect
01240 
01241                 // Send a blank line to end this group of headers
01242                 m_pOutput->Print( "\r\n" );
01243 
01244                 // Turn this CShakeNeighbour object into a CG1Neighbour one, and delete this one
01245                 OnHandshakeComplete();
01246                 return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01247 
01248         } // not rejected, not handshake3, not initiated (do)
01249         else
01250         {
01251                 // We are a leaf
01252                 if ( Neighbours.IsG1Leaf() )
01253                 {
01254                         // Tell the remote computer we can't connect because we are a shielded leaf right now
01255                         SendHostHeaders( _T("GNUTELLA/0.6 503 Shielded leaf node") );
01256                         DelayClose( IDS_HANDSHAKE_IAMLEAF ); // Send the buffer and then close the socket citing our being a leaf as the reason
01257                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01258 
01259                 } // We are an ultrapeer, or at least we are capable of becomming one
01260                 else if ( Neighbours.IsG1Ultrapeer() || Neighbours.IsG1UltrapeerCapable() )
01261                 {
01262                         // The remote computer told us it is a leaf
01263                         if ( m_bUltraPeerSet == TS_FALSE )
01264                         {
01265                                 // This connection is to a leaf below us
01266                                 m_nNodeType = ntLeaf;
01267 
01268                         } // The remote computer told us it is an ultrapeer
01269                         else if ( m_bUltraPeerSet == TS_TRUE )
01270                         {
01271                                 // Record that we are both ultrapeers
01272                                 m_nNodeType = ntNode;
01273                         }
01274 
01275                 } // The remote computer is an ultrapeer, but we are just a leaf
01276                 else if ( m_bUltraPeerSet == TS_TRUE && ( Settings.Gnutella1.ClientMode != MODE_ULTRAPEER ) )
01277                 {
01278                         // This connection is to a hub above us
01279                         m_nNodeType = ntHub;
01280                 }
01281 
01282                 // If we don't need this connection
01283                 if ( ( m_nNodeType == ntLeaf && ! Neighbours.NeedMoreHubs( PROTOCOL_G1 ) &&             // This connection is to a leaf below us, and we don't need more hubs/leaves
01284                          ! Neighbours.NeedMoreLeafs( PROTOCOL_G1 ) ) ||
01285                          ( m_nNodeType != ntLeaf && ! Neighbours.NeedMoreHubs( PROTOCOL_G1 ) ) ||       // This connection is to a hub and we don't need more hubs
01286                          ( ( m_nNodeType != ntHub ) && ( m_bObsoleteClient || m_bBadClient ) ) )        // This is an obsolete version of Shareaza
01287                 {
01288                         // Tell the remote computer we can't connect because we already have too many connections
01289                         SendHostHeaders( _T("GNUTELLA/0.6 503 Maximum connections reached") );
01290                         DelayClose( IDS_HANDSHAKE_SURPLUS ); // Send the buffer then close the socket
01291                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01292 
01293                 } // Weed out this nonsense combination that should never happen
01294                 else if ( ( m_nNodeType == ntHub && ( Settings.Gnutella1.ClientMode == MODE_ULTRAPEER ) ) || // This connection is up to a hub, and we are a hub
01295                                   ( m_nNodeType == ntLeaf && ( Settings.Gnutella1.ClientMode == MODE_LEAF ) ) )      // Or, this connection is down to a leaf like us
01296                 {
01297                         // Tell the remote computer we can't connect
01298                         SendHostHeaders( _T("GNUTELLA/0.6 503 Ultrapeer disabled") );
01299                         DelayClose( IDS_HANDSHAKE_NOULTRAPEER ); // Send the buffer then close the socket
01300                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01301 
01302                 } // Weed out another nonsense combination that should never happen
01303                 else if ( m_nNodeType != ntHub && ( Settings.Gnutella1.ClientMode == MODE_LEAF ) )
01304                 {
01305                         // Tell the remote computer we can't connect
01306                         SendHostHeaders( _T("GNUTELLA/0.6 503 Need an Ultrapeer") );
01307                         DelayClose( IDS_HANDSHAKE_NEEDAPEER ); // Send the buffer then close the socket
01308                         return FALSE; // Return false all the way back to CHandshakes::RunHandshakes, which will delete this object
01309 
01310                 } // Otherwise, the connection is probably alright
01311                 else
01312                 {
01313                         // Send reply headers to the remote computer
01314                         m_pOutput->Print( "GNUTELLA/0.6 200 OK\r\n" );  // Start our group of response headers to the other computer with the 200 OK message
01315                         SendPublicHeaders();                                                    // Send the initial Gnutella headers
01316                         SendPrivateHeaders();                                                   // Send headers in response to those we got from the remote computer
01317                         SendHostHeaders();                                                              // Send the "X-Try-Ultrapeers" header with a list of other IP addresses running Gnutella
01318 
01319                         // If this connection is up to a hub or to a hub like us, and we need more hubs
01320                         if ( m_nNodeType != ntLeaf && Neighbours.NeedMoreHubs( PROTOCOL_G1 ) )
01321                         {
01322                                 // And if we're a hub, so this connection must be to another hub
01323                                 if ( Settings.Gnutella1.ClientMode != MODE_LEAF )
01324                                 {
01325                                         // Tell the remote computer we want more connections to hubs
01326                                         m_pOutput->Print( "X-Ultrapeer-Needed: True\r\n" );
01327                                 }
01328 
01329                         } // This connection must be down to a leaf
01330                         else
01331                         {
01332                                 // And we're a leaf
01333                                 if ( Settings.Gnutella1.ClientMode != MODE_ULTRAPEER )
01334                                 {
01335                                         // Tell the remote computer we don't need any more hub connections
01336                                         m_pOutput->Print( "X-Ultrapeer-Needed: False\r\n" );
01337                                 }
01338                         }
01339 
01340                         // End this block of headers with a blank line
01341                         m_pOutput->Print( "\r\n" );
01342                         m_nState = nrsHandshake1; // We've finished sending a group of headers, and await the response
01343                 }
01344         }
01345 
01346         // Have OnRead keep exchanging handshake headers with the remote computer
01347         return TRUE;
01348 }
01349 
01351 // CShakeNeighbour handshake completed
01352 
01353 // Creates a new GC1Neighbour or CG2Neighbour object based on this one, and deletes this one
01354 void CShakeNeighbour::OnHandshakeComplete()
01355 {
01356         // Remove this CShakeNeighbour object from the list of them the neighbours object keeps
01357         Neighbours.Remove( this );
01358 
01359         // Point the bandwidth meter limits at the numbers from Shareaza Settings
01360         switch ( m_nNodeType ) {
01361         case ntHub:
01362 
01363                 // Point the limits at the settings for connections to hubs above us
01364                 m_mInput.pLimit  = &Settings.Bandwidth.HubIn;
01365                 m_mOutput.pLimit = &Settings.Bandwidth.HubOut;
01366 
01367                 break;
01368 
01369         // This connection is to a leaf below us
01370         case ntLeaf:
01371 
01372                 // Point the limits at the settings for connections to leaves below us
01373                 m_mInput.pLimit  = &Settings.Bandwidth.LeafIn;
01374                 m_mOutput.pLimit = &Settings.Bandwidth.LeafOut;
01375 
01376                 break;
01377 
01378         // We are both hubs
01379         case ntNode:
01380 
01381                 // Point the limits at the settings for peer hub-to-hub connections
01382                 m_mInput.pLimit  = &Settings.Bandwidth.PeerIn;
01383                 m_mOutput.pLimit = &Settings.Bandwidth.PeerOut;
01384 
01385                 break;
01386         }
01387 
01388         // If the remote computer supports compression, setup buffers for arriving and departing compressed data
01389         if ( m_bDeflateSend )   m_pZInput  = new CBuffer(); // The remote computer said "Content-Encoding: deflate", make a buffer for compressed data coming in
01390         if ( m_bDeflateAccept ) m_pZOutput = new CBuffer(); // The remote computer said "Accept-Encoding: deflate", make a buffer for data to compress before sending
01391 
01392         // If Shareaza Settings specify a size for the send buffer
01393         if ( Settings.Connection.SendBuffer ) // By default, this is 2048, which is 2 KB of space
01394         {
01395                 // Tell the socket to use a 2 KB buffer for sends
01396                 setsockopt(                                 // Set a socket option
01397                         m_hSocket,                              // The socket this CShakeNeighbour object inherited from CConnection
01398                         SOL_SOCKET,                             // Define this option at the socket level
01399                         SO_SNDBUF,                              // Specify the total per-socket buffer space reserved for sends
01400                         (LPSTR)&Settings.Connection.SendBuffer, // Use the value from Shareaza Settings, 2 KB by default
01401                         4 );                                    // SendBuffer is a DWORD, occupying 4 bytes of memory
01402         }
01403 
01404         // Make a pointer for a new object that will be a copy of this one, but in a different place on the CConnection inheritance tree
01405         CNeighbour* pNeighbour = NULL;
01406 
01407         // If the remote computer is G2, or can send and understand Gnutella2 packets and isn't G1
01408         if ( ( m_nProtocol == PROTOCOL_G2 ) || ( m_bG2Send && m_bG2Accept && ( m_nProtocol != PROTOCOL_G1 ) ) )
01409         {
01410                 // Record that the remote computer supports query routing
01411                 m_bQueryRouting = TRUE;
01412 
01413                 // Make a new Gnutella2 neighbour object by copying values from this ShakeNeighbour one
01414                 pNeighbour = new CG2Neighbour( this );
01415 
01416         }
01417         else    // The remote computer is just Gnutella, not Gnutella2
01418         {
01419                 // Record that the remote computer is not running Shareaza
01420                 m_bShareaza = FALSE;
01421 
01422                 // Make a new Gnutella neighbour object by copying values from this ShakeNeighbour one
01423                 pNeighbour = new CG1Neighbour( this );
01424         }
01425 
01426         // This connection is to a hub above us
01427         if ( m_nNodeType == ntHub )
01428         {
01429                 // Report that we got a ultrapeer connection
01430                 theApp.Message( MSG_DEFAULT, IDS_HANDSHAKE_GOTPEER );
01431 
01432                 // (do)
01433                 Neighbours.PeerPrune( pNeighbour->m_nProtocol );
01434 
01435         } // This connection is to a leaf below us
01436         else if ( m_nNodeType == ntLeaf )
01437         {
01438                 // Report that we connected to a leaf
01439                 theApp.Message( MSG_DEFAULT, IDS_HANDSHAKE_GOTLEAF, (LPCTSTR)m_sAddress );
01440         }
01441 
01442         // When we copied the object, the pointers to buffers were also copied, null them here so deleting this doesn't delete them
01443         m_pZInput  = NULL;
01444         m_pZOutput = NULL;
01445 
01446         // Delete this CShakeNeighbour object now that it has been turned into a CG1Neighbour or CG2Neighbour object
01447         delete this;
01448 }
01449 
01450 
01452 // CShakeNeighbour IsClientObsolete
01453 
01454 // Checks the user agent to see if it's an outdated client. (An old Shareaza beta, or something)
01455 BOOL CShakeNeighbour::IsClientObsolete()
01456 {
01457         if ( m_sUserAgent.IsEmpty() ) return TRUE;
01458 
01459         if ( _tcsistr( m_sUserAgent, _T("Shareaza") ) )
01460         {
01461                 // Shareaza client
01462 
01463                 // Check for fakes / version hacks.
01464                 if (( _tcsistr( m_sUserAgent, _T("Shareaza 3.0"  ) ) ) ||       // Fakes
01465                         ( _tcsistr( m_sUserAgent, _T("Shareaza 6."   ) ) ) ||
01466                         ( _tcsistr( m_sUserAgent, _T("Shareaza 7."   ) ) ) )
01467                         return TRUE;
01468 
01469                 // Check for old version and betas
01470                 if (( _tcsistr( m_sUserAgent, _T("Shareaza 1."   ) ) ) ||       // Old versions
01471                         ( _tcsistr( m_sUserAgent, _T("Shareaza 2.0." ) ) ) )
01472                         return TRUE;
01473 
01474                 // Assumed to be reasonably current
01475                 return FALSE;
01476         }
01477         else if ( _tcsistr( m_sUserAgent, _T("gnucdna") ) )
01478         {
01479                 // DNA based client
01480 
01481                 // Assumed to be reasonably current
01482                 return FALSE;
01483         }
01484         else if ( _tcsistr( m_sUserAgent, _T("trustyfiles") ) )
01485         {
01486                 // TrustyFiles- assume up to date
01487                 return FALSE;
01488         }
01489         else if ( _tcsistr( m_sUserAgent, _T("adagio") ) )
01490         {
01491                 // Adagio- assume up to date
01492                 return FALSE;
01493         }
01494         else if ( _tcsistr( m_sUserAgent, _T("eTomi") ) )
01495         {
01496                 // GPL violating rip- Uses outdated code
01497                 return TRUE;
01498         }
01499 
01500         return FALSE;
01501 }
01502 
01504 // CShakeNeighbour IsClientBad
01505 
01506 // Checks the user agent to see if it's a GPL breaker, or other trouble-maker
01507 BOOL CShakeNeighbour::IsClientBad()
01508 {
01509         // No user agent- assume OK
01510         if ( m_sUserAgent.IsEmpty() ) return FALSE;
01511 
01512         // Known good clients
01513         if ( _tcsistr( m_sUserAgent, _T("gnucdna") ) )          return FALSE;
01514 
01515         if ( _tcsistr( m_sUserAgent, _T("adagio") ) )           return FALSE;
01516 
01517         if ( _tcsistr( m_sUserAgent, _T("shareaza") ) )         return FALSE;
01518         
01519         if ( _tcsistr( m_sUserAgent, _T("trustyfiles") ) )      return FALSE;
01520 
01521         // GPL breakers- Clients violating the GPL
01522         // See http://www.gnu.org/copyleft/gpl.html
01523         if ( _tcsistr( m_sUserAgent, _T("K-Lite") ) )           return TRUE;
01524 
01525         if ( _tcsistr( m_sUserAgent, _T("SlingerX") ) )         return TRUE;
01526 
01527         if ( _tcsistr( m_sUserAgent, _T("C -3.0.1") ) )         return TRUE;
01528 
01529         if ( _tcsistr( m_sUserAgent, _T("vagaa") ) )            return TRUE;
01530 
01531         if ( _tcsistr( m_sUserAgent, _T("mxie") ) )                     return TRUE;
01532 
01533         // Clients that over-query or otherwise cause problems
01534         //if ( _tcsistr( m_sUserAgent, _T("") ) )                       return TRUE;
01535 
01536 
01537         // Unknown- Assume OK
01538         return FALSE;
01539 }
01540 
01541 
01543 // CShakeNeighbour IsClientBanned
01544 
01545 // Checks the user agent to see if it's a leecher client, or other banned client
01546 BOOL CShakeNeighbour::IsClientBanned()
01547 {
01548         // No user agent- assume OK
01549         if ( m_sUserAgent.IsEmpty() ) return FALSE;
01550 
01551         // Unknown- Assume OK
01552         return FALSE;
01553 }
01554 

Generated on Thu Dec 15 10:39:48 2005 for Shareaza 2.2.1.0 by  doxygen 1.4.2