00001 // 00002 // Handshake.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 // CHandshake figures out what the remote computer wants from the first 7 bytes it sends us 00023 // http://wiki.shareaza.com/static/Developers.Code.CHandshake 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 "Handshake.h" 00031 #include "Neighbours.h" 00032 #include "Downloads.h" 00033 #include "Uploads.h" 00034 #include "UploadTransfer.h" 00035 #include "ChatCore.h" 00036 #include "Network.h" 00037 #include "Buffer.h" 00038 #include "GProfile.h" 00039 #include "BTClients.h" 00040 #include "EDClients.h" 00041 #include "EDPacket.h" 00042 #include "WndMain.h" 00043 #include "WndChat.h" 00044 00045 // If we are compiling in debug mode, replace the text "THIS_FILE" in the code with the name of this file 00046 #ifdef _DEBUG 00047 #undef THIS_FILE 00048 static char THIS_FILE[]=__FILE__; 00049 #define new DEBUG_NEW 00050 #endif 00051 00053 // CHandshake construction 00054 00055 // Make a new CHandshake object 00056 // Initializes the member variables with default values 00057 CHandshake::CHandshake() 00058 { 00059 // We did not connect to the remote computer as part of a push 00060 m_bPushing = FALSE; 00061 00062 // Set pointers so the input and output bandwidth limits are read from the DWORD in settings 00063 m_mInput.pLimit = m_mOutput.pLimit = &Settings.Bandwidth.Request; 00064 } 00065 00066 // Called when a remote computer wants to connect to us 00067 // Make a new CHanshake object given a socket, and a MFC SOCKADDR_IN structure which contains an IP address and port number 00068 // Uses AcceptFrom to make a new object with this socket and ip address 00069 CHandshake::CHandshake(SOCKET hSocket, SOCKADDR_IN* pHost) 00070 { 00071 // We did not connect to the remote computer as part of a push 00072 m_bPushing = FALSE; 00073 00074 // Call CConnection::AcceptFrom to setup this object with the socket and 00075 AcceptFrom( hSocket, pHost ); 00076 00077 // Set pointers so the input and output bandwidth limits are read from the DWORD in settings 00078 m_mInput.pLimit = m_mOutput.pLimit = &Settings.Bandwidth.Request; 00079 00080 // Record that the program accepted this connection 00081 theApp.Message( MSG_DEFAULT, IDS_CONNECTION_ACCEPTED, (LPCTSTR)m_sAddress, htons( m_pHost.sin_port ) ); 00082 } 00083 00084 // Copy a CHandshake object 00085 // Takes pCopy, a pointer to the CHandshake object to make this new one a copy of 00086 CHandshake::CHandshake(CHandshake* pCopy) 00087 { 00088 // Call CConnection::AttachTo to copy the CConnection core of pCopy over to this new CHandshake object 00089 AttachTo( pCopy ); 00090 00091 // Then, copy across the CHandshake member variables, since AttachTo just does the CConnection ones 00092 m_bPushing = pCopy->m_bPushing; // Copy in whether or not we connected to the remote computer as part of a push 00093 m_nIndex = pCopy->m_nIndex; // Copy across the handshake index (do) 00094 00095 // Set pointers so the input and output bandwidth limits are read from the DWORD in settings 00096 m_mInput.pLimit = m_mOutput.pLimit = &Settings.Bandwidth.Request; 00097 } 00098 00099 // Delete this CHandshake object 00100 CHandshake::~CHandshake() 00101 { 00102 // There is nothing the CConnection destructor won't take care of 00103 } 00104 00106 // CHandshake push 00107 00108 // Open a connection to a new remote computer 00109 // Takes the IP address and port number of the remote computer, as well as the index (do) 00110 // Connects this socket to the new computer, but does not make any communications yet 00111 BOOL CHandshake::Push(IN_ADDR* pAddress, WORD nPort, DWORD nIndex) 00112 { 00113 // Report we are about to push open a connection to the given IP address and port number now 00114 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_CONNECT, (LPCTSTR)CString( inet_ntoa( *pAddress ) ), _T("") ); 00115 00116 // Connect the socket in this CHandshake object to the IP address and port number the method got passed 00117 if ( ! ConnectTo( pAddress, nPort ) ) return FALSE; // If the connection was not made, leave now 00118 00119 // Tell Windows to give us a m_pWakeup event if this socket connects, gets data, is ready for writing, or closes 00120 WSAEventSelect( // Specify our event object Handshakes.m_pWakeup to the FD_CONNECT FD_READ FD_WRITE and FD_CLOSE events 00121 m_hSocket, // The socket in this CHandshake object 00122 Handshakes.m_pWakeup, // The MFC CEvent object we've made for the wakeup event 00123 FD_CONNECT | // The connection has been made 00124 FD_READ | // There is data from the remote computer on our end of the socket waiting for us to read it 00125 FD_WRITE | // The remote computer is ready for some data from us (do) 00126 FD_CLOSE ); // The socket has been closed 00127 00128 // Record the push in the member variables 00129 m_bPushing = TRUE; // We connected to this computer as part of a push 00130 m_tConnected = GetTickCount(); // Record that we connected right now 00131 m_nIndex = nIndex; // Copy the given index number into this object (do) 00132 00133 // Report success 00134 return TRUE; 00135 } 00136 00138 // CHandshake run event 00139 00140 // Determine the handshake has been going on for too long 00141 // Returns true or false 00142 BOOL CHandshake::OnRun() 00143 { 00144 // If we've been connected for longer than the handshake timeout from settings 00145 if ( GetTickCount() - m_tConnected > Settings.Connection.TimeoutHandshake ) 00146 { 00147 // Report this, and return false 00148 theApp.Message( MSG_ERROR, IDS_HANDSHAKE_TIMEOUT, (LPCTSTR)m_sAddress ); 00149 return FALSE; 00150 } 00151 00152 // We still have time for the handshake 00153 return TRUE; 00154 } 00155 00157 // CHandshake connection events 00158 00159 // Send the remote computer our client index and guid in a header like "GIV index:guid/" 00160 BOOL CHandshake::OnConnected() 00161 { 00162 // Call CConnection's OnConnected method first, even though it does nothing (do) 00163 CConnection::OnConnected(); 00164 00165 // copy Profile's GUID 00166 GGUID oID( MyProfile.GUID ); 00167 00168 // Compose the GIV string, which is like "GIV index:guid/" with two newlines at the end (do) 00169 CString strGIV; 00170 strGIV.Format( // MFC's CString::Format is like sprintf, "%.2X" formats a byte into 2 hexidecimal characters like "ff" 00171 _T("GIV %u:%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X/\n\n"), 00172 m_nIndex, // Our index on the Gnutella network (do) 00173 int( oID.n[0] ), int( oID.n[1] ), int( oID.n[2] ), int( oID.n[3] ), // Our GUID 00174 int( oID.n[4] ), int( oID.n[5] ), int( oID.n[6] ), int( oID.n[7] ), 00175 int( oID.n[8] ), int( oID.n[9] ), int( oID.n[10] ), int( oID.n[11] ), 00176 int( oID.n[12] ), int( oID.n[13] ), int( oID.n[14] ), int( oID.n[15] ) ); 00177 00178 // Print the string into the output buffer, and write the output buffer to the remote computer 00179 m_pOutput->Print( strGIV ); 00180 OnWrite(); 00181 00182 // Record that we uploaded the giv, and report success 00183 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_GIV, (LPCTSTR)m_sAddress ); 00184 return TRUE; 00185 } 00186 00187 // If we connected to the remote computer as part of a push, record that we couldn't connect to do the upload 00188 void CHandshake::OnDropped(BOOL bError) 00189 { 00190 // If we connected to the remote computer as part of a push 00191 if ( m_bPushing ) 00192 { 00193 // Record a upload connect error 00194 theApp.Message( MSG_ERROR, IDS_UPLOAD_CONNECT_ERROR, (LPCTSTR)m_sAddress ); 00195 } 00196 } 00197 00199 // CHandshake read event 00200 00201 // Reads the first few bytes from the other computer to figure out what it wants 00202 // Returns true if it needs more information, false if it's done 00203 BOOL CHandshake::OnRead() 00204 { 00205 // Read data waiting in the socket into the input buffer 00206 CConnection::OnRead(); 00207 00208 // We need at least 7 bytes of headers from the remote compuer to figure out what network its talking about 00209 if ( m_pInput->m_nLength < 7 ) return TRUE; // Not enough information yet, leave now returning true 00210 00211 // Determine if the remote computer has sent an eDonkey2000 hello packet 00212 if ( m_pInput->m_nLength >= 7 && // 7 or more bytes have arrived 00213 m_pInput->m_pBuffer[0] == ED2K_PROTOCOL_EDONKEY && // The first byte is "e3", indicating eDonkey2000 00214 m_pInput->m_pBuffer[5] == ED2K_C2C_HELLO && // 5 bytes in is "01", a hello for that network 00215 m_pInput->m_pBuffer[6] == 0x10 ) // And after that is "10" 00216 { 00217 // Have the EDClients object accept this CHandshake as a new eDonkey2000 computer 00218 EDClients.OnAccept( this ); 00219 return FALSE; // Return false to indicate that we are done sorting the handshake 00220 } 00221 00222 // See if the remote computer is speaking BitTorrent 00223 if ( m_pInput->m_nLength >= BT_PROTOCOL_HEADER_LEN && // We have at least 20 bytes 00224 memcmp( m_pInput->m_pBuffer, BT_PROTOCOL_HEADER, BT_PROTOCOL_HEADER_LEN ) == 0 ) // They are "\023BitTorrent protocol" 00225 { 00226 // Have the BTClients object accept this CHandshake as a new BitTorrent computer 00227 BTClients.OnAccept( this ); 00228 return FALSE; // Done sorting the handshake 00229 } 00230 00231 // With eDonkey2000 and BitTorrent out of the way, now we can look for text-based handshakes 00232 00233 // Read the first header line 00234 CString strLine; 00235 if ( ! m_pInput->ReadLine( strLine, TRUE ) ) // Read characters until \n, returning false if there is no \n 00236 { 00237 // The remote computer hasn't sent a \n yet, if there are more than 2048 bytes in the first line, abort 00238 return ( m_pInput->m_nLength < 2048 ) ? TRUE : FALSE; // Return false to signal we are done sorting the handshake 00239 } 00240 00241 // The first line the remote computer sent was blank 00242 if ( strLine.IsEmpty() ) 00243 { 00244 // Eat blank lines, just read the next line into strLine and return true 00245 m_pInput->ReadLine( strLine ); // But strLine is never used? (do) 00246 return TRUE; // Keep trying to sort the handshake 00247 } 00248 00249 // Record this handshake line as an application message 00250 theApp.Message( MSG_DEBUG, _T("%s: HANDSHAKE: %s"), (LPCTSTR)m_sAddress, (LPCTSTR)strLine ); 00251 00252 // The first header starts "GET" or "HEAD" 00253 if ( _tcsncmp( strLine, _T("GET"), 3 ) == 0 || _tcsncmp( strLine, _T("HEAD"), 4 ) == 0 ) 00254 { 00255 // The remote computer wants a file from us, accept the connection as an upload 00256 Uploads.OnAccept( this, strLine ); 00257 } 00258 else if ( _tcsnicmp( strLine, _T("GNUTELLA"), 8 ) == 0 ) { Neighbours.OnAccept( this ); } // Gnutella handshake 00259 else if ( _tcsnicmp( strLine, _T("PUSH "), 5 ) == 0 ) { OnAcceptPush(); } // Gnutella2-style push 00260 else if ( _tcsnicmp( strLine, _T("GIV "), 4 ) == 0 ) { OnAcceptGive(); } // Gnutella giv 00261 else if ( _tcsnicmp( strLine, _T("CHAT"), 4 ) == 0 ) // Chat 00262 { 00263 // If the user has setup a valid profile and enabeled chat in the program settings 00264 if ( MyProfile.IsValid() && Settings.Community.ChatEnable ) 00265 { 00266 // Have the chat system accept this connection 00267 ChatCore.OnAccept( this ); 00268 } 00269 else 00270 { 00271 // Otherwise, tell the other computer we can't chat 00272 m_pOutput->Print( "CHAT/0.2 404 Unavailable\r\n\r\n" ); 00273 OnWrite(); 00274 } 00275 } 00276 else 00277 { 00278 // The first header starts with something else, report that we couldn't figure out the handshake 00279 theApp.Message( MSG_ERROR, IDS_HANDSHAKE_FAIL, (LPCTSTR)m_sAddress ); 00280 } 00281 00282 // Return false to indicate that we are done sorting the handshake 00283 return FALSE; 00284 } 00285 00287 // CHandshake accept Gnutella2-style PUSH 00288 00289 // When the first thing a remote computer says is a Gnutella2 "PUSH", this method gets called 00290 // Checks if a child window recognizes the guid 00291 // Returns true or false 00292 BOOL CHandshake::OnAcceptPush() 00293 { 00294 // Make a string for the header line, and variables to hold the GUID in string and binary forms 00295 CString strLine, strGUID; 00296 GGUID pGUID; 00297 00298 // Read the first line from the input buffer, this doesn't remove it so we can call it over and over again 00299 if ( ! m_pInput->ReadLine( strLine ) ) return FALSE; 00300 00301 // Make sure the line has the format "PUSH guid:GUIDinHEXguidINhexGUIDinHEXguidI", which has 10 characters before the 32 for the guid 00302 if ( strLine.GetLength() != 10 + 32 ) 00303 { 00304 // Report the bad push and return reporting error 00305 theApp.Message( MSG_ERROR, IDS_DOWNLOAD_BAD_PUSH, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) ); 00306 return FALSE; 00307 } 00308 00309 // Read the 16 hexidecimal digits of the GUID, copying it into pGUID 00310 for ( int nByte = 0 ; nByte < 16 ; nByte++ ) 00311 { 00312 int nValue; 00313 _stscanf( strLine.Mid( 10 + nByte * 2, 2 ), _T("%X"), &nValue ); 00314 pGUID.n[ nByte ] = (BYTE)nValue; 00315 } 00316 00317 // If a child window recongizes the GUID, accept the push 00318 if ( OnPush( &pGUID ) ) return TRUE; 00319 00320 // Record the fact that we got a push we knew nothing about, and return false to not accept it 00321 theApp.Message( MSG_ERROR, IDS_DOWNLOAD_UNKNOWN_PUSH, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ), _T("Gnutella2") ); 00322 return FALSE; 00323 } 00324 00326 // CHandshake accept Gnutella1-style GIV 00327 00328 // If the first thing the remote computer says to us is a Gnutella "GIV ", this method gets called 00329 // Checks if a child window recognizes the guid 00330 // Returns true or false 00331 BOOL CHandshake::OnAcceptGive() 00332 { 00333 // Local variables for the searching and parsing 00334 CString strLine, strClient, strFile; // Strings for the whole line, the client guid hexidecimal characters, and the file name within it 00335 DWORD nFileIndex = 0xFFFFFFFF; // Start out the file index as -1 to detect if we were able to read it 00336 GGUID pClientID; // We will translate the GUID into binary here 00337 int nPos; // The distance from the start of the string to a colon or slash we will look for 00338 00339 // The first line should be like "GIV 124:d51dff817f895598ff0065537c09d503/my%20song.mp3" 00340 if ( ! m_pInput->ReadLine( strLine ) ) return FALSE; // If the line isn't all there yet, return false to try again later 00341 00342 // If there is a slash in the line 00343 if ( ( nPos = strLine.Find( '/' ) ) > 0 ) // Find returns the 0-based index in the string, -1 if not found 00344 { 00345 // Clip out the part of the line after the slash, URL decode it to turn %20 into spaces, and save that in strFile 00346 strFile = URLDecode( strLine.Mid( nPos + 1 ) ); // Mid takes part after the slash 00347 strLine = strLine.Left( nPos ); // Left removes that part and the slash from strLine 00348 } 00349 00350 // If there is a colon in the line more than 4 character widths in, like "GIV 123:client32characterslong----------" 00351 if ( ( nPos = strLine.Find( ':' ) ) > 4 ) 00352 { 00353 // Read the number before and text after the colon 00354 strClient = strLine.Mid( nPos + 1 ); // Clip out just the "client" part of the example shown above 00355 strLine = strLine.Mid( 4, nPos - 4 ); // Starting at 4 to get beyond the "GIV ", clip out "123", any text before the colon at nPos 00356 _stscanf( strLine, _T("%lu"), &nFileIndex ); // Read "123" as a number, this is the file index 00357 } 00358 00359 // If there was no colon and the file index was not read, or the client text isn't exactly 32 characters long 00360 if ( nFileIndex == 0xFFFFFFFF || strClient.GetLength() != 32 ) 00361 { 00362 // Make a record of this bad push and leave now 00363 theApp.Message( MSG_ERROR, IDS_DOWNLOAD_BAD_PUSH, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) ); 00364 return FALSE; 00365 } 00366 00367 // The client id text is a 16-byte guid written in 32 characters with text like "00" through "ff" for each byte, read it into pClientID 00368 for ( int nByte = 0 ; nByte < 16 ; nByte++ ) 00369 { 00370 // Convert one set of characters like "00" or "ff" into that byte in pClientID 00371 _stscanf( strClient.Mid( nByte * 2, 2 ), _T("%X"), &nPos ); 00372 pClientID.n[ nByte ] = (BYTE)nPos; 00373 } 00374 00375 // If a child window recognizes this guid, return true 00376 if ( OnPush( &pClientID ) ) return TRUE; 00377 00378 // If the file name is longer than 256 characters, change it to the text "Invalid Filename" 00379 if ( strFile.GetLength() > 256 ) strFile = _T("Invalid Filename"); 00380 00381 // Log this unexpected push, and return false 00382 theApp.Message( MSG_ERROR, IDS_DOWNLOAD_UNKNOWN_PUSH, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ), (LPCTSTR)strFile ); 00383 return FALSE; 00384 } 00385 00387 // CHandshake accept a push from a GUID 00388 00389 // Takes the GUID of a remote computer which has sent us a Gnutella2-style PUSH handshake 00390 // Sees if any child windows recognize the GUID 00391 // Returns true or false 00392 BOOL CHandshake::OnPush(GGUID* pGUID) 00393 { 00394 // Make sure the socket is valid 00395 if ( m_hSocket == INVALID_SOCKET ) return FALSE; 00396 00397 // Look for the remote computer's GUID in our list of downloads and the chat interface 00398 if ( Downloads.OnPush( pGUID, this ) ) return TRUE; // Return true if it's found 00399 if ( ChatCore.OnPush( pGUID, this ) ) return TRUE; 00400 00401 // Make sure this is the only thread doing this right now 00402 CSingleLock pWindowLock( &theApp.m_pSection ); 00403 if ( pWindowLock.Lock( 250 ) ) // Don't wait here for more than a quarter second, Lock will return false and so will OnPush 00404 { 00405 // Access granted, get a pointer to the windowing system 00406 if ( CMainWnd* pMainWnd = theApp.SafeMainWnd() ) 00407 { 00408 // Get a pointer to the main Shareaza window 00409 CWindowManager* pWindows = &pMainWnd->m_pWindows; 00410 CChildWnd* pChildWnd = NULL; 00411 00412 // Loop through all of Shareaza's child windows 00413 while ( pChildWnd = pWindows->Find( NULL, pChildWnd ) ) 00414 { 00415 // If a child window recognizes this push request, return true 00416 if ( pChildWnd->OnPush( pGUID, this ) ) return TRUE; 00417 } 00418 } 00419 00420 // Let other threads use theApp.m_pSection 00421 pWindowLock.Unlock(); 00422 } 00423 00424 // No child window recognized a push request, or we waited more than a quarter second and gave up 00425 return FALSE; 00426 }