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 }