connectionsocks5proxy.cpp

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

Generated on Sun Apr 27 11:08:13 2008 for gloox by  doxygen 1.5.5