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

Handshakes.cpp

Go to the documentation of this file.
00001 //
00002 // Handshakes.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 // CHandshakes listens for remote computers that want to connect to us
00023 // http://wiki.shareaza.com/static/Developers.Code.CHandshakes
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 "Handshakes.h"
00030 #include "Connection.h"
00031 #include "Handshake.h"
00032 #include "Network.h"
00033 #include "Security.h"
00034 #include "Datagrams.h"
00035 #include "DiscoveryServices.h"
00036 #include "Transfers.h"
00037 #include "Uploads.h"
00038 
00039 // If we are compiling in debug mode, replace the text "THIS_FILE" in the code with the name of this file
00040 #ifdef _DEBUG
00041 #undef THIS_FILE
00042 static char THIS_FILE[]=__FILE__;
00043 #define new DEBUG_NEW
00044 #endif
00045 
00046 // When the program runs, it makes a single global CHandshakes object with this line of code
00047 CHandshakes Handshakes;
00048 
00050 // CHandshakes construction
00051 
00052 // Make the CHandshakes object
00053 CHandshakes::CHandshakes()
00054 {
00055         // Zero counts
00056         m_nStableCount  = 0; // We haven't listened for and accepted any connections yet
00057         m_tStableTime   = 0; // No time recorded yet
00058 
00059         // Null values for the socket and thread handles
00060         m_hSocket = INVALID_SOCKET;
00061         m_hThread = NULL;
00062 }
00063 
00064 // Delete the CHandshakes object
00065 CHandshakes::~CHandshakes()
00066 {
00067         // Have the Disconnect method put everything away
00068         Disconnect();
00069 
00070         // Make sure it set the socket to invalid, and the thread to null
00071         ASSERT( m_hSocket == INVALID_SOCKET );
00072         ASSERT( m_hThread == NULL );
00073 }
00074 
00076 // CHandshakes listen
00077 
00078 // Use the socket in the CHandshakes object to listen for remote computers who want to connect to us
00079 // Returns true if we're listening, false if it didn't work
00080 BOOL CHandshakes::Listen()
00081 {
00082         // Make sure only one thread can execute the code of this method at a time
00083         CSingleLock pLock( &m_pSection, TRUE ); // When the method exits, local pLock will be destructed, and the lock released
00084 
00085         // Setup m_hSocket as a new TCP socket
00086         if ( m_hSocket != INVALID_SOCKET ) return FALSE; // Make sure the socket hasn't been created yet
00087         m_hSocket = socket(     // Create a socket
00088                 PF_INET,                // Specify the Internet address family
00089                 SOCK_STREAM,    // Use TCP and not UDP
00090                 IPPROTO_TCP );
00091         if ( m_hSocket == INVALID_SOCKET ) return FALSE; // Now, make sure it has been created
00092 
00093         // Get our computer's Internet IP address and port number from the network object
00094         SOCKADDR_IN saListen = Network.m_pHost; // This is the address of our computer as visible to remote computers on the Internet
00095 
00096         // If the program connection settings disallow binding, zero the 4 bytes of the IP address
00097         if ( ! Settings.Connection.InBind ) saListen.sin_addr.S_un.S_addr = 0; // S_addr is the IP address formatted as a single u_long
00098 
00099         // Loop to try 5 times
00100         BOOL bBound = FALSE; // We're not bound to this socket yet
00101         for ( int nAttempt = 0 ; nAttempt < 5 ; nAttempt++ )
00102         {
00103                 // Bind our IP address to the socket, Windows Firewall may pop a message to the user when a program calls bind
00104                 bBound = bind(                          // Call bind to associate our local address wiht this socket
00105                         m_hSocket,                              // The socket in this CHandshakes object
00106                         (SOCKADDR*)&saListen,   // Our Internet IP address, the one we want to listen on, and how big it is
00107                         sizeof(saListen)
00108                         ) == 0;                                 // If bind succeeds, it returns 0, and bBound becomes true
00109                 if ( bBound ) break;            // We're done trying to bind, leave the loop
00110 
00111                 // Record that we weren't able to listen on this port
00112                 theApp.Message( MSG_ERROR, IDS_NETWORK_CANT_LISTEN, (LPCTSTR)CString( inet_ntoa( saListen.sin_addr ) ), htons( saListen.sin_port ) );
00113 
00114                 // If this is not our first attempt
00115                 if ( nAttempt )
00116                 {
00117                         // Choose a port number randomly, and store it it Network and saListen
00118                         int nPort = Network.RandomPort();
00119                         Network.m_pHost.sin_port = saListen.sin_port = htons( nPort );
00120                 }
00121                 else // This is still our first time here in this loop
00122                 {
00123                         // Zero the 4 numbers of the IP address (do) Why would we want to call bind with a zeroed address?
00124                         saListen.sin_addr.S_un.S_addr = 0;
00125                 }
00126         }
00127 
00128         // If our record of our IP address on the Internet in the network object is zeroed
00129         if ( Network.m_pHost.sin_addr.S_un.S_addr == 0 )
00130         {
00131                 // Ask the socket what it thinks our IP address on this end is
00132                 int nSockLen = sizeof(SOCKADDR_IN);     // The number of bytes an MFC SOCKADDR_IN structure takes
00133                 getsockname(                                            // Retrieves the local name for a socket
00134                         m_hSocket,                                              // The socket
00135                         (SOCKADDR*)&Network.m_pHost,    // Have getsockname write the answer right into Network.m_pHost
00136                         &nSockLen );                                    // Tell getsockname how much space it has to write there
00137         }
00138 
00139         // If we were able to bind the socket to our local address
00140         if ( bBound )
00141         {
00142                 // Report that we are now listening on our IP address
00143                 theApp.Message( MSG_DEFAULT, IDS_NETWORK_LISTENING_TCP, (LPCTSTR)CString( inet_ntoa( Network.m_pHost.sin_addr ) ), htons( Network.m_pHost.sin_port ) );
00144         }
00145 
00146         // Set it up so that when a remote computer connects to us, the m_pWakeup event is fired
00147         WSAEventSelect(         // Specify an event object to associate with the specified set of FD_XXX network events
00148                 m_hSocket,              // Our listening socket
00149                 m_pWakeup,              // Our event, a CEvent object member variable
00150                 FD_ACCEPT );    // The network event to trigger this is us accepting a remote computer's connection
00151 
00152         // Have the socket wait, listening for remote computer on the Internet to connect to it
00153         listen(                 // Place a socket in a state in which it is listening for an incoming connection
00154                 m_hSocket,      // Our socket
00155                 256 );          // Maximum length of the queue of pending connections, let 256 computers try to call us at once (do)
00156 
00157         // Create a new thread to run the ThreadStart method, passing it a pointer to this C
00158         m_hThread =                     // Save the new thread handle
00159                 AfxBeginThread( // Create a new thread
00160                 ThreadStart,    // Have it start running the ThreadStart method
00161                 this                    // ThreadStart gets one parameter passed to it, a pointer to this CHandshakes object
00162                 )->m_hThread;   // AfxBeginThread returns a pointer to the new thread object, copy the thread handle from within it
00163 
00164         // Report success
00165         return TRUE;
00166 }
00167 
00169 // CHandshakes disconnect
00170 
00171 // Closes the socket and kills the thread
00172 void CHandshakes::Disconnect()
00173 {
00174         // If the socket in this CHandshakes object is valid
00175         if ( m_hSocket != INVALID_SOCKET )
00176         {
00177                 // Close it and set it invalid
00178                 closesocket( m_hSocket );
00179                 m_hSocket = INVALID_SOCKET;
00180         }
00181 
00182         // If this CHandshakes object has a thread handle
00183         if ( m_hThread != NULL )
00184         {
00185                 // Set the state of the wakeup event to signaled, releasing any threads that are waiting on it
00186                 m_pWakeup.SetEvent();
00187 
00188                 // Loop 10 times
00189                 int nAttempt = 10;
00190         for ( ; nAttempt > 0 ; nAttempt-- )
00191                 {
00192                         // Stay in the loop while the thread is still active
00193                         DWORD nCode;
00194                         if ( ! GetExitCodeThread( m_hThread, &nCode ) ) break;  // If the call fails, leave the loop
00195                         if ( nCode != STILL_ACTIVE ) break;                                             // If the thread is not still active, leave the loop
00196 
00197                         // Have this thread pause on this line of code and do nothing for a tenth of a second
00198                         Sleep( 100 );
00199                 }
00200 
00201                 // If we waited a whole second and checked 10 times for the thread to close but always it was still active
00202                 if ( nAttempt == 0 )
00203                 {
00204                         // Terminate it manually
00205                         TerminateThread( m_hThread, 0 );
00206                         theApp.Message( MSG_DEBUG, _T("WARNING: Terminating CHandshakes thread.") );
00207 
00208                         // Wait a tenth of a second
00209                         Sleep( 100 );
00210                 }
00211 
00212                 // The thread is gone now, set the handle to null
00213                 m_hThread = NULL;
00214         }
00215 
00216         // Make sure only one thread can execute the remaining code of this method at a time
00217         CSingleLock pLock( &m_pSection, TRUE ); // When the method exits, local pLock will be destructed, and the lock released
00218 
00219         // Zero the statistics about how many connections we've received
00220         m_nStableCount  = 0; // Reset our count of how many connections we've accepted
00221         m_tStableTime   = 0;
00222 
00223         // Delete all the handshake objects in the list, and the list itself
00224         for ( POSITION pos = GetIterator() ; pos ; ) delete GetNext( pos );     // Delete each handshake object
00225         m_pList.RemoveAll();                                                                                            // Remove all the pointers
00226 }
00227 
00229 // CHandshakes push connection
00230 
00231 // Takes an IP address and port number, and the Gnutella index (do)
00232 // Connects to the remote computer, and makes a CHandshake object about it in m_pList
00233 // Returns true if the connection is made, false if we couldn't connect
00234 BOOL CHandshakes::PushTo(IN_ADDR* pAddress, WORD nPort, DWORD nIndex)
00235 {
00236         // Get exclusive access to the transfers part of the program
00237         CSingleLock pLock1( &Transfers.m_pSection );
00238         if ( pLock1.Lock( 250 ) ) // If we're waiting more than a quarter second, just give up
00239         {
00240                 // If we can't allow any more uploads to this address
00241                 if ( ! Uploads.AllowMoreTo( pAddress ) )
00242                 {
00243                         // Report that we're too busy to do the push, and leave now
00244                         CString strAddress = inet_ntoa( *pAddress );
00245                         theApp.Message( MSG_ERROR, IDS_UPLOAD_PUSH_BUSY, (LPCTSTR)strAddress );
00246                         return FALSE;
00247                 }
00248 
00249                 // We're done, unlock exclusive access to the next thread that wants in can get in
00250                 pLock1.Unlock();
00251         }
00252 
00253         // Make sure only one thread can execute the code of this method at a time
00254         CSingleLock pLock2( &m_pSection, TRUE );
00255 
00256         // Make a new CHandshake object, and open a connection to the computer at pAddress and pPort
00257         CHandshake* pHandshake = new CHandshake();
00258         if ( pHandshake->Push( pAddress, nPort, nIndex ) )
00259         {
00260                 // Add the new CHandshake object to the end of the m_pList list
00261                 m_pList.AddTail( pHandshake );
00262                 return TRUE;
00263         }
00264         else // We couldn't connect to that IP address and port number
00265         {
00266                 // Delete the handshake object we just made, and report failure
00267                 delete pHandshake;
00268                 return FALSE;
00269         }
00270 }
00271 
00273 // CHandshakes connection test
00274 
00275 // Takes an IP address
00276 // Searches the list of handshake objects to see if we are connected to it
00277 // Returns true if its in the list, false if we couldn't find it
00278 BOOL CHandshakes::IsConnectedTo(IN_ADDR* pAddress)
00279 {
00280         // Make sure only one thread can execute the code of this method at a time
00281         CSingleLock pLock( &m_pSection, TRUE );
00282 
00283         // Loop for each CHandshake object in the m_pList of them
00284         for ( POSITION pos = GetIterator() ; pos ; )
00285         {
00286                 // Get the handshake object here in the list, and move the position iterator to the next one
00287                 CHandshake* pHandshake = GetNext( pos );
00288 
00289                 // If the IP address in the list handshake object matches the one given this method, we've found it
00290                 if ( pHandshake->m_pHost.sin_addr.S_un.S_addr == pAddress->S_un.S_addr ) return TRUE;
00291         }
00292 
00293         // We didn't find it
00294         return FALSE;
00295 }
00296 
00298 // CHandshakes list modification
00299 
00300 // Takes pOld, a pointer to a handshake object in the list, and pNew, a pointer to one to replace it
00301 // Replaces the old pointer with the new one in the list, and deletes the object at the end of the old pointer
00302 void CHandshakes::Substitute(CHandshake* pOld, CHandshake* pNew)
00303 {
00304         // Find the position of the pointer to the old handshake object in the m_pList list
00305         POSITION pos = m_pList.Find( pOld );
00306         ASSERT( pos != NULL ); // Make sure we found something
00307 
00308         // Set the new pointer at this position, overwriting the old pointer there
00309         m_pList.SetAt( pos, pNew );
00310 
00311         // Delete the old handshake object, since the list doesn't have a pointer to it anymore
00312         delete pOld;
00313 }
00314 
00315 // Takes a pointer to a handshake object in the list
00316 // Removes the pointer from the list and deletes the object
00317 void CHandshakes::Remove(CHandshake* pHandshake)
00318 {
00319         // Find the position of the handshake pointer in the m_pList list
00320         POSITION pos = m_pList.Find( pHandshake );
00321         ASSERT( pos != NULL ); // Make sure we found it
00322 
00323         // Remove its pointer from the list, and delete the object
00324         m_pList.RemoveAt( pos );
00325         delete pHandshake;
00326 }
00327 
00329 // CHandshakes thread run
00330 
00331 // Before it exits, the Listen method creates a new thread to run on this method
00332 // It passes pParam, which is actually a pointer to the CHandshakes object
00333 // This method runs the thread, it is the start, middle, and end of the thread
00334 // The return value of this method is the thread's exit value
00335 UINT CHandshakes::ThreadStart(LPVOID pParam)
00336 {
00337         // Cast the parameter back into what it was, a pointer to this CHandhshakes object, and call the OnRun method
00338         ((CHandshakes*)pParam)->OnRun();
00339 
00340         // When OnRun is done, this thread's work is complete, have it exit with the default return code 0
00341         return 0; // This is the end of the thread
00342 }
00343 
00344 // The thread runs this method
00345 // Accept incoming connections from remote computers and figure out what they want
00346 void CHandshakes::OnRun()
00347 {
00348         // Loop while the socket is valid
00349         while ( m_hSocket != INVALID_SOCKET )
00350         {
00351                 // Wait for a computer to call us, which fires the wakeup event
00352                 WaitForSingleObject( m_pWakeup, 1000 ); // Give up after a second
00353 
00354                 // Accept the connection from the remote computer, making a new CHandshake object for it in the list
00355                 while ( AcceptConnection() );
00356 
00357                 // Send and receive data with each remote computer in the list
00358                 RunHandshakes();
00359 
00360                 // Loop to accept some more connections from computers that have called our listening socket
00361                 while ( AcceptConnection() );
00362 
00363                 // If we've listened for and accepted at least one connection, update the discovery services
00364                 RunStableUpdate();
00365         }
00366 }
00367 
00369 // CHandshakes run the handshake
00370 
00371 // Send and receive data with each remote computer in the list
00372 void CHandshakes::RunHandshakes()
00373 {
00374         // Make sure only one thread can execute the code of this method at a time
00375         CSingleLock pLock( &m_pSection, TRUE );
00376 
00377         // Loop through each CHandshake object in the m_pList list of pointers to them
00378         for ( POSITION posNext = GetIterator() ; posNext ; )
00379         {
00380                 // Loop on one handshake object in the list
00381                 POSITION posThis = posNext;                                             // Save the current position, as GetNext will move it to the next one
00382                 CHandshake* pHandshake = GetNext( posNext );    // Get the pointer at this position, and move posNext forward
00383 
00384                 // Send and receive data write with the remote computer, deleting the handshake object if we loose the connection
00385                 if (
00386                         // Read and write data through the socket
00387                         ! pHandshake->DoRun() // If we've lost the connection, DoRun will return false, and we'll go to the next line
00388 
00389                         // If the position and pointer still match
00390                         && m_pList.GetAt( posThis ) == pHandshake ) // (do) How could this not be so?
00391                 {
00392                         // Delete the handshake object and remove it from the list
00393                         delete pHandshake;
00394                         m_pList.RemoveAt( posThis );
00395                 }
00396         }
00397 }
00398 
00400 // CHandshakes accept a connection
00401 
00402 // We're listening on m_hSocket, a remote computer calls us, this method accepts the connection, making a new CHandshake object in the list for it
00403 // Returns true or false if we accepted the connection
00404 BOOL CHandshakes::AcceptConnection()
00405 {
00406         // Local variables to receive the IP address and port number of the remote computer
00407         SOCKADDR_IN pHost;
00408         int nHost = sizeof(pHost);
00409 
00410         // Accept the connection in a new socket, hSocket
00411         SOCKET hSocket =                // Make a new local socket here
00412                 WSAAccept(                      // Conditionally accepts an incoming connection from a remote computer
00413                 m_hSocket,                      // The socket listening for connections
00414                 (SOCKADDR*)&pHost,      // Have WSAAccept tell us what it thinks the IP address and port number of the remote computer is
00415                 &nHost,                         // The number of bytes it has to write there
00416                 AcceptCheck,            // Call this function, and it will tell you if we wish to accept this connection or not
00417                 (DWORD)this );          // Give the AcceptCheck function a pointer to this CHandshakes object as its parameter
00418         if ( hSocket == INVALID_SOCKET ) return FALSE; // AcceptCheck refused the connection, or it didn't work, leave now
00419 
00420         // We've listened for and accepted one more stable connection
00421         InterlockedIncrement( (PLONG)&m_nStableCount ); // Use an interlocked function to do this in a thread-safe way
00422 
00423         // If the remote computer's IP address is blocked or banned
00424         if ( Security.IsDenied( &pHost.sin_addr ) )
00425         {
00426                 // Close the socket we just accepted the connection with
00427                 closesocket( hSocket );
00428 
00429                 // Report that this connection was denied for security reasons
00430                 CString strHost = inet_ntoa( pHost.sin_addr );
00431                 theApp.Message( MSG_ERROR, IDS_NETWORK_SECURITY_DENIED, (LPCTSTR)strHost );
00432         }
00433         else // The IP address is not blocked
00434         {
00435                 // Make a new handshake object with the received socket and IP address, and add it to the list
00436                 CreateHandshake( hSocket, &pHost );
00437         }
00438 
00439         // Report success
00440         return TRUE;
00441 }
00442 
00443 // Takes a local socket we just made accepting a connection, and the IP address and port number of the remote computer
00444 // Makes a new handshake object with the socket, and adds it to the list
00445 void CHandshakes::CreateHandshake(SOCKET hSocket, SOCKADDR_IN* pHost)
00446 {
00447         // Make sure only one thread can execute the code of this method at a time
00448         CSingleLock pLock( &m_pSection, TRUE );
00449 
00450         // Setup the socket so when there is data to read or write, or it closes, the m_pWakeup event happens
00451         WSAEventSelect(                                                 // Associate the m_pWakeup event with the FD_READ, FD_WRITE, and FD_CLOSE events
00452                 hSocket,                                                        // The local socket we just made when accepting a new connection
00453                 m_pWakeup,                                                      // The handshakes object's wakeup event
00454                 FD_READ | FD_WRITE | FD_CLOSE );        // Make the event happen when the socket is ready to read, write, or has closed
00455 
00456         // Make a new handshake object with the received socket and IP address, and add it to the list
00457         m_pList.AddTail( new CHandshake( hSocket, pHost ) );
00458 }
00459 
00461 // CHandshakes inbound connection security check callback
00462 
00463 // This is the WSAAccept condition function, that's why it has so many weird parameters
00464 // Makes sure we know the remote IP address, and it's not on the security watch list
00465 // Returns CF_ACCEPT or CF_REJECT to tell WSAAccept what to do
00466 int CALLBACK CHandshakes::AcceptCheck(IN LPWSABUF lpCallerId, IN LPWSABUF lpCallerData, IN OUT LPQOS lpSQOS, IN OUT LPQOS lpGQOS, IN LPWSABUF lpCalleeId, OUT LPWSABUF lpCalleeData, OUT GROUP FAR * g, IN DWORD dwCallbackData)
00467 {
00468         // If the address of the remote computer is unknown or too short, reject the connection
00469         if ( lpCallerId == NULL )                    return CF_REJECT; // WSAAccept didn't get the remote computer's IP and port
00470         if ( lpCallerId->len < sizeof(SOCKADDR_IN) ) return CF_REJECT; // The IP and port aren't long enough
00471 
00472         // Copy out the IP address and port number of the remote computer
00473         SOCKADDR_IN* pHost = (SOCKADDR_IN*)lpCallerId->buf;
00474         
00475         // If the remote computer's IP address is on the list of blocked IPs
00476         if ( Security.IsDenied( &pHost->sin_addr ) )
00477         {
00478                 // Record we are rejecting this connection because it is on the watch list, and tell WSAAccept to not connect
00479                 CString strHost = inet_ntoa( pHost->sin_addr );
00480                 theApp.Message( MSG_ERROR, IDS_NETWORK_SECURITY_DENIED, (LPCTSTR)strHost );
00481                 return CF_REJECT;
00482         }
00483         else // The IP address is not on that watch list
00484         {
00485                 // Tell WSAAccept we should try to accept this connection
00486                 return CF_ACCEPT;
00487         }
00488 }
00489 
00491 // CHandshakes update stable state
00492 
00493 // If we've accepted at least one connection, update the discovery services (do)
00494 void CHandshakes::RunStableUpdate()
00495 {
00496         // If we've listened for and accepted at least one stable connection
00497         if ( m_nStableCount > 0 )
00498         {
00499                 // If there isn't a record of when we first connected yet, set it to the current time.
00500                 if ( m_tStableTime == 0 ) m_tStableTime = (DWORD)time( NULL ); // The function time( NULL ) resolves to the number of seconds since 1970
00501 
00502                 // Update the discovery services (do)
00503                 DiscoveryServices.Update();
00504         }
00505 }

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