gloox  1.0.9
connectionsocks5proxy.cpp
1 /*
2  Copyright (c) 2007-2013 by Jakob Schroeter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 
15 #include "config.h"
16 
17 #include "gloox.h"
18 
19 #include "connectionsocks5proxy.h"
20 #include "dns.h"
21 #include "logsink.h"
22 #include "prep.h"
23 #include "base64.h"
24 #include "util.h"
25 
26 #include <string>
27 #include <cstdlib>
28 
29 #include <string.h>
30 
31 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
32 # include <netinet/in.h>
33 #endif
34 
35 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
36 # include <winsock.h>
37 #elif defined( _WIN32_WCE )
38 # include <winsock2.h>
39 #endif
40 
41 #include <cstdlib>
42 
43 namespace gloox
44 {
45 
47  const LogSink& logInstance,
48  const std::string& server,
49  int port, bool ip )
50  : ConnectionBase( 0 ), m_connection( connection ),
51  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
52  {
53 // FIXME check return value?
54  prep::idna( server, m_server );
55  m_port = port;
56 
57  if( m_connection )
58  m_connection->registerConnectionDataHandler( this );
59  }
60 
62  ConnectionBase* connection,
63  const LogSink& logInstance,
64  const std::string& server,
65  int port, bool ip )
66  : ConnectionBase( cdh ), m_connection( connection ),
67  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
68  {
69 // FIXME check return value?
70  prep::idna( server, m_server );
71  m_port = port;
72 
73  if( m_connection )
74  m_connection->registerConnectionDataHandler( this );
75  }
76 
78  {
79  if( m_connection )
80  delete m_connection;
81  }
82 
84  {
85  ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
86  return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
87  }
88 
90  {
91  if( m_connection )
92  delete m_connection;
93 
94  m_connection = connection;
95  }
96 
98  {
99 // FIXME CHECKME
100  if( m_connection && m_connection->state() == StateConnected && m_handler )
101  {
103  m_s5state = S5StateConnected;
104  return ConnNoError;
105  }
106 
107  if( m_connection && m_handler )
108  {
110  m_s5state = S5StateConnecting;
111  return m_connection->connect();
112  }
113 
114  return ConnNotConnected;
115  }
116 
118  {
119  if( m_connection )
120  m_connection->disconnect();
121  cleanup();
122  }
123 
125  {
126  if( m_connection )
127  return m_connection->recv( timeout );
128  else
129  return ConnNotConnected;
130  }
131 
133  {
134  if( m_connection )
135  return m_connection->receive();
136  else
137  return ConnNotConnected;
138  }
139 
140  bool ConnectionSOCKS5Proxy::send( const std::string& data )
141  {
142 // if( m_s5state != S5StateConnected )
143 // {
144 // printf( "p data sent: " );
145 // const char* x = data.c_str();
146 // for( unsigned int i = 0; i < data.length(); ++i )
147 // printf( "%02X ", (const char)x[i] );
148 // printf( "\n" );
149 // }
150 
151  if( m_connection )
152  return m_connection->send( data );
153 
154  return false;
155  }
156 
158  {
160  m_s5state = S5StateDisconnected;
161 
162  if( m_connection )
163  m_connection->cleanup();
164  }
165 
166  void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut )
167  {
168  if( m_connection )
169  m_connection->getStatistics( totalIn, totalOut );
170  else
171  {
172  totalIn = 0;
173  totalOut = 0;
174  }
175  }
176 
178  const std::string& data )
179  {
180 // if( m_s5state != S5StateConnected )
181 // {
182 // printf( "data recv: " );
183 // const char* x = data.c_str();
184 // for( unsigned int i = 0; i < data.length(); ++i )
185 // printf( "%02X ", (const char)x[i] );
186 // printf( "\n" );
187 // }
188 
189  if( !m_connection || !m_handler )
190  return;
191 
192  ConnectionError connError = ConnNoError;
193 
194  switch( m_s5state )
195  {
196  case S5StateConnecting:
197  if( data.length() != 2 || data[0] != 0x05 )
198  connError = ConnIoError;
199 
200  if( data[1] == 0x00 ) // no auth
201  {
202  negotiate();
203  }
204  else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
205  {
206  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
207  "authenticating to socks5 proxy as user " + m_proxyUser );
208  m_s5state = S5StateAuthenticating;
209  char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
210  size_t pos = 0;
211  d[pos++] = 0x01;
212  d[pos++] = (char)m_proxyUser.length();
213  strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
214  pos += m_proxyUser.length();
215  d[pos++] = (char)m_proxyPwd.length();
216  strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
217  pos += m_proxyPwd.length();
218 
219  if( !send( std::string( d, pos ) ) )
220  {
221  cleanup();
223  }
224  delete[] d;
225  }
226  else
227  {
228  if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() )
229  connError = ConnProxyNoSupportedAuth;
230  else
231  connError = ConnProxyAuthRequired;
232  }
233  break;
234  case S5StateNegotiating:
235  if( data.length() >= 6 && data[0] == 0x05 )
236  {
237  if( data[1] == 0x00 )
238  {
240  m_s5state = S5StateConnected;
241  m_handler->handleConnect( this );
242  }
243  else // connection refused
244  connError = ConnConnectionRefused;
245  }
246  else
247  connError = ConnIoError;
248  break;
249  case S5StateAuthenticating:
250  if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
251  negotiate();
252  else
253  connError = ConnProxyAuthFailed;
254  break;
255  case S5StateConnected:
256  m_handler->handleReceivedData( this, data );
257  break;
258  default:
259  break;
260  }
261 
262  if( connError != ConnNoError )
263  {
264  m_connection->disconnect();
265  m_handler->handleDisconnect( this, connError );
266  }
267 
268  }
269 
270  void ConnectionSOCKS5Proxy::negotiate()
271  {
272  m_s5state = S5StateNegotiating;
273  char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
274  size_t pos = 0;
275  d[pos++] = 0x05; // SOCKS version 5
276  d[pos++] = 0x01; // command CONNECT
277  d[pos++] = 0x00; // reserved
278  int port = m_port;
279  std::string server = m_server;
280  if( m_ip ) // IP address
281  {
282  d[pos++] = 0x01; // IPv4 address
283  std::string s;
284  const size_t j = server.length();
285  size_t l = 0;
286  for( size_t k = 0; k < j && l < 4; ++k )
287  {
288  if( server[k] != '.' )
289  s += server[k];
290 
291  if( server[k] == '.' || k == j-1 )
292  {
293  d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
294  s = EmptyString;
295  ++l;
296  }
297  }
298  }
299  else // hostname
300  {
301  if( port == -1 )
302  {
303  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
304  if( servers.size() )
305  {
306  const std::pair< std::string, int >& host = *servers.begin();
307  server = host.first;
308  port = host.second;
309  }
310  }
311  d[pos++] = 0x03; // hostname
312  d[pos++] = (char)m_server.length();
313  strncpy( d + pos, m_server.c_str(), m_server.length() );
314  pos += m_server.length();
315  }
316  int nport = htons( port );
317  d[pos++] = static_cast<char>( nport );
318  d[pos++] = static_cast<char>( nport >> 8 );
319 
320  std::string message = "Requesting socks5 proxy connection to " + server + ":"
321  + util::int2string( port );
322  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
323 
324  if( !send( std::string( d, pos ) ) )
325  {
326  cleanup();
328  }
329  delete[] d;
330  }
331 
333  {
334  if( m_connection )
335  {
336  std::string server = m_server;
337  int port = m_port;
338  if( port == -1 )
339  {
340  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
341  if( !servers.empty() )
342  {
343  const std::pair< std::string, int >& host = *servers.begin();
344  server = host.first;
345  port = host.second;
346  }
347  }
348  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
349  "Attempting to negotiate socks5 proxy connection" );
350 
351  const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
352  const char d[4] = {
353  0x05, // SOCKS version 5
354  static_cast<char>( auth ? 0x02 // two methods
355  : 0x01 ), // one method
356  0x00, // method: no auth
357  0x02 // method: username/password auth
358  };
359 
360  if( !send( std::string( d, auth ? 4 : 3 ) ) )
361  {
362  cleanup();
363  if( m_handler )
365  }
366  }
367  }
368 
370  ConnectionError reason )
371  {
372  cleanup();
373  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
374 
375  if( m_handler )
376  m_handler->handleDisconnect( this, reason );
377  }
378 
379 }
An abstract base class for a connection.
static HostMap resolve(const std::string &service, const std::string &proto, const std::string &domain, const LogSink &logInstance)
Definition: dns.cpp:201
std::map< std::string, int > HostMap
Definition: dns.h:68
virtual ConnectionBase * newInstance() const
virtual void cleanup()
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)=0
void setConnectionImpl(ConnectionBase *connection)
ConnectionError
Definition: gloox.h:676
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)
virtual void disconnect()=0
virtual ConnectionError connect()=0
This is an abstract base class to receive events from a ConnectionBase-derived object.
virtual ConnectionError receive()
virtual ConnectionBase * newInstance() const =0
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)=0
const std::string & server() const
virtual bool send(const std::string &data)=0
virtual void handleConnect(const ConnectionBase *connection)=0
virtual void getStatistics(long int &totalIn, long int &totalOut)
virtual void handleConnect(const ConnectionBase *connection)
virtual ConnectionError receive()=0
void registerConnectionDataHandler(ConnectionDataHandler *cdh)
virtual ConnectionError recv(int timeout=-1)=0
bool idna(const std::string &domain, std::string &out)
Definition: prep.cpp:107
virtual void getStatistics(long int &totalIn, long int &totalOut)=0
ConnectionSOCKS5Proxy(ConnectionBase *connection, const LogSink &logInstance, const std::string &server, int port=-1, bool ip=false)
void dbg(LogArea area, const std::string &message) const
Definition: logsink.h:66
ConnectionState m_state
ConnectionDataHandler * m_handler
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)
ConnectionState state() const
virtual ConnectionError recv(int timeout=-1)
An implementation of log sink and source.
Definition: logsink.h:38
const std::string EmptyString
Definition: gloox.cpp:122
virtual bool send(const std::string &data)
virtual ConnectionError connect()