connectionsocks5proxy.cpp

00001 /*
00002   Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 
00015 #include "config.h"
00016 
00017 #include "gloox.h"
00018 
00019 #include "connectionsocks5proxy.h"
00020 #include "dns.h"
00021 #include "logsink.h"
00022 #include "prep.h"
00023 #include "base64.h"
00024 #include "util.h"
00025 
00026 #include <string>
00027 #include <cstdlib>
00028 
00029 #include <string.h>
00030 
00031 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
00032 # include <netinet/in.h>
00033 #endif
00034 
00035 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
00036 # include <winsock.h>
00037 #elif defined( _WIN32_WCE )
00038 # include <winsock2.h>
00039 #endif
00040 
00041 #include <cstdlib>
00042 
00043 namespace gloox
00044 {
00045 
00046   ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionBase* connection,
00047                                                 const LogSink& logInstance,
00048                                                 const std::string& server,
00049                                                 int port, bool ip )
00050     : ConnectionBase( 0 ), m_connection( connection ),
00051       m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
00052   {
00053 // FIXME check return value?
00054     prep::idna( server, m_server );
00055     m_port = port;
00056 
00057     if( m_connection )
00058       m_connection->registerConnectionDataHandler( this );
00059   }
00060 
00061   ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh,
00062                                                 ConnectionBase* connection,
00063                                                 const LogSink& logInstance,
00064                                                 const std::string& server,
00065                                                 int port, bool ip )
00066     : ConnectionBase( cdh ), m_connection( connection ),
00067       m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
00068   {
00069 // FIXME check return value?
00070     prep::idna( server, m_server );
00071     m_port = port;
00072 
00073     if( m_connection )
00074       m_connection->registerConnectionDataHandler( this );
00075   }
00076 
00077   ConnectionSOCKS5Proxy::~ConnectionSOCKS5Proxy()
00078   {
00079     if( m_connection )
00080       delete m_connection;
00081   }
00082 
00083   ConnectionBase* ConnectionSOCKS5Proxy::newInstance() const
00084   {
00085     ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
00086     return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
00087   }
00088 
00089   void ConnectionSOCKS5Proxy::setConnectionImpl( ConnectionBase* connection )
00090   {
00091     if( m_connection )
00092       delete m_connection;
00093 
00094     m_connection = connection;
00095   }
00096 
00097   ConnectionError ConnectionSOCKS5Proxy::connect()
00098   {
00099 // FIXME CHECKME
00100     if( m_connection && m_connection->state() == StateConnected && m_handler )
00101     {
00102       m_state = StateConnected;
00103       m_s5state = S5StateConnected;
00104       return ConnNoError;
00105     }
00106 
00107     if( m_connection && m_handler )
00108     {
00109       m_state = StateConnecting;
00110       m_s5state = S5StateConnecting;
00111       return m_connection->connect();
00112     }
00113 
00114     return ConnNotConnected;
00115   }
00116 
00117   void ConnectionSOCKS5Proxy::disconnect()
00118   {
00119     if( m_connection )
00120       m_connection->disconnect();
00121     cleanup();
00122   }
00123 
00124   ConnectionError ConnectionSOCKS5Proxy::recv( int timeout )
00125   {
00126     if( m_connection )
00127       return m_connection->recv( timeout );
00128     else
00129       return ConnNotConnected;
00130   }
00131 
00132   ConnectionError ConnectionSOCKS5Proxy::receive()
00133   {
00134     if( m_connection )
00135       return m_connection->receive();
00136     else
00137       return ConnNotConnected;
00138   }
00139 
00140   bool ConnectionSOCKS5Proxy::send( const std::string& data )
00141   {
00142 //     if( m_s5state != S5StateConnected )
00143 //     {
00144 //       printf( "p data sent: " );
00145 //       const char* x = data.c_str();
00146 //       for( unsigned int i = 0; i < data.length(); ++i )
00147 //         printf( "%02X ", (const char)x[i] );
00148 //       printf( "\n" );
00149 //     }
00150 
00151     if( m_connection )
00152       return m_connection->send( data );
00153 
00154     return false;
00155   }
00156 
00157   void ConnectionSOCKS5Proxy::cleanup()
00158   {
00159     m_state = StateDisconnected;
00160     m_s5state = S5StateDisconnected;
00161 
00162     if( m_connection )
00163       m_connection->cleanup();
00164   }
00165 
00166   void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut )
00167   {
00168     if( m_connection )
00169       m_connection->getStatistics( totalIn, totalOut );
00170     else
00171     {
00172       totalIn = 0;
00173       totalOut = 0;
00174     }
00175   }
00176 
00177   void ConnectionSOCKS5Proxy::handleReceivedData( const ConnectionBase* /*connection*/,
00178                                                   const std::string& data )
00179   {
00180 //     if( m_s5state != S5StateConnected )
00181 //     {
00182 //       printf( "data recv: " );
00183 //       const char* x = data.c_str();
00184 //       for( unsigned int i = 0; i < data.length(); ++i )
00185 //         printf( "%02X ", (const char)x[i] );
00186 //       printf( "\n" );
00187 //     }
00188 
00189     if( !m_connection || !m_handler )
00190       return;
00191 
00192     ConnectionError connError = ConnNoError;
00193 
00194     switch( m_s5state  )
00195     {
00196       case S5StateConnecting:
00197         if( data.length() != 2 || data[0] != 0x05 )
00198           connError = ConnIoError;
00199 
00200         if( data[1] == 0x00 ) // no auth
00201         {
00202           negotiate();
00203         }
00204         else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
00205         {
00206           m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
00207                              "authenticating to socks5 proxy as user " + m_proxyUser );
00208           m_s5state = S5StateAuthenticating;
00209           char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
00210           size_t pos = 0;
00211           d[pos++] = 0x01;
00212           d[pos++] = (char)m_proxyUser.length();
00213           strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
00214           pos += m_proxyUser.length();
00215           d[pos++] = (char)m_proxyPwd.length();
00216           strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
00217           pos += m_proxyPwd.length();
00218 
00219           if( !send( std::string( d, pos ) ) )
00220           {
00221             cleanup();
00222             m_handler->handleDisconnect( this, ConnIoError );
00223           }
00224           delete[] d;
00225         }
00226         else
00227         {
00228           if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() )
00229             connError = ConnProxyNoSupportedAuth;
00230           else
00231             connError = ConnProxyAuthRequired;
00232         }
00233         break;
00234       case S5StateNegotiating:
00235         if( data.length() >= 6 && data[0] == 0x05 )
00236         {
00237           if( data[1] == 0x00 )
00238           {
00239             m_state = StateConnected;
00240             m_s5state = S5StateConnected;
00241             m_handler->handleConnect( this );
00242           }
00243           else // connection refused
00244             connError = ConnConnectionRefused;
00245         }
00246         else
00247           connError = ConnIoError;
00248         break;
00249       case S5StateAuthenticating:
00250         if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
00251           negotiate();
00252         else
00253           connError = ConnProxyAuthFailed;
00254         break;
00255       case S5StateConnected:
00256         m_handler->handleReceivedData( this, data );
00257         break;
00258       default:
00259         break;
00260     }
00261 
00262     if( connError != ConnNoError )
00263     {
00264       m_connection->disconnect();
00265       m_handler->handleDisconnect( this, connError );
00266     }
00267 
00268   }
00269 
00270   void ConnectionSOCKS5Proxy::negotiate()
00271   {
00272     m_s5state = S5StateNegotiating;
00273     char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
00274     size_t pos = 0;
00275     d[pos++] = 0x05; // SOCKS version 5
00276     d[pos++] = 0x01; // command CONNECT
00277     d[pos++] = 0x00; // reserved
00278     int port = m_port;
00279     std::string server = m_server;
00280     if( m_ip ) // IP address
00281     {
00282       d[pos++] = 0x01; // IPv4 address
00283       std::string s;
00284       const size_t j = server.length();
00285       size_t l = 0;
00286       for( size_t k = 0; k < j && l < 4; ++k )
00287       {
00288         if( server[k] != '.' )
00289           s += server[k];
00290 
00291         if( server[k] == '.' || k == j-1 )
00292         {
00293           d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
00294           s = EmptyString;
00295           ++l;
00296         }
00297       }
00298     }
00299     else // hostname
00300     {
00301       if( port == -1 )
00302       {
00303         const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
00304         if( servers.size() )
00305         {
00306           const std::pair< std::string, int >& host = *servers.begin();
00307           server = host.first;
00308           port = host.second;
00309         }
00310       }
00311       d[pos++] = 0x03; // hostname
00312       d[pos++] = (char)m_server.length();
00313       strncpy( d + pos, m_server.c_str(), m_server.length() );
00314       pos += m_server.length();
00315     }
00316     int nport = htons( port );
00317     d[pos++] = static_cast<char>( nport );
00318     d[pos++] = static_cast<char>( nport >> 8 );
00319 
00320     std::string message = "Requesting socks5 proxy connection to " + server + ":"
00321         + util::int2string( port );
00322     m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
00323 
00324     if( !send( std::string( d, pos ) ) )
00325     {
00326       cleanup();
00327       m_handler->handleDisconnect( this, ConnIoError );
00328     }
00329     delete[] d;
00330   }
00331 
00332   void ConnectionSOCKS5Proxy::handleConnect( const ConnectionBase* /*connection*/ )
00333   {
00334     if( m_connection )
00335     {
00336       std::string server = m_server;
00337       int port = m_port;
00338       if( port == -1 )
00339       {
00340         const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
00341         if( !servers.empty() )
00342         {
00343           const std::pair< std::string, int >& host = *servers.begin();
00344           server = host.first;
00345           port = host.second;
00346         }
00347       }
00348       m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
00349                          "Attempting to negotiate socks5 proxy connection" );
00350 
00351       const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
00352       const char d[4] = {
00353         0x05,                             // SOCKS version 5
00354         static_cast<char>( auth ? 0x02    // two methods
00355                                 : 0x01 ), // one method
00356         0x00,                             // method: no auth
00357         0x02                              // method: username/password auth
00358       };
00359 
00360       if( !send( std::string( d, auth ? 4 : 3 ) ) )
00361       {
00362         cleanup();
00363         if( m_handler )
00364           m_handler->handleDisconnect( this, ConnIoError );
00365       }
00366     }
00367   }
00368 
00369   void ConnectionSOCKS5Proxy::handleDisconnect( const ConnectionBase* /*connection*/,
00370                                                 ConnectionError reason )
00371   {
00372     cleanup();
00373     m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
00374 
00375     if( m_handler )
00376       m_handler->handleDisconnect( this, reason );
00377   }
00378 
00379 }
Generated on Tue Jun 8 23:37:53 2010 for gloox by  doxygen 1.6.3