connectionsocks5proxy.cpp

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

Generated on Sat Nov 10 08:50:27 2007 for gloox by  doxygen 1.5.3-20071008