gloox  1.0
connectionsocks5proxy.cpp
1 /*
2  Copyright (c) 2007-2009 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 namespace gloox
42 {
43 
45  const LogSink& logInstance,
46  const std::string& server,
47  int port, bool ip )
48  : ConnectionBase( 0 ), m_connection( connection ),
49  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
50  {
51 // FIXME check return value?
52  prep::idna( server, m_server );
53  m_port = port;
54 
55  if( m_connection )
56  m_connection->registerConnectionDataHandler( this );
57  }
58 
60  ConnectionBase* connection,
61  const LogSink& logInstance,
62  const std::string& server,
63  int port, bool ip )
64  : ConnectionBase( cdh ), m_connection( connection ),
65  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
66  {
67 // FIXME check return value?
68  prep::idna( server, m_server );
69  m_port = port;
70 
71  if( m_connection )
72  m_connection->registerConnectionDataHandler( this );
73  }
74 
76  {
77  if( m_connection )
78  delete m_connection;
79  }
80 
82  {
83  ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
84  return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
85  }
86 
88  {
89  if( m_connection )
90  delete m_connection;
91 
92  m_connection = connection;
93  }
94 
96  {
97 // FIXME CHECKME
98  if( m_connection && m_connection->state() == StateConnected && m_handler )
99  {
101  m_s5state = S5StateConnected;
102  return ConnNoError;
103  }
104 
105  if( m_connection && m_handler )
106  {
108  m_s5state = S5StateConnecting;
109  return m_connection->connect();
110  }
111 
112  return ConnNotConnected;
113  }
114 
116  {
117  if( m_connection )
118  m_connection->disconnect();
119  cleanup();
120  }
121 
123  {
124  if( m_connection )
125  return m_connection->recv( timeout );
126  else
127  return ConnNotConnected;
128  }
129 
131  {
132  if( m_connection )
133  return m_connection->receive();
134  else
135  return ConnNotConnected;
136  }
137 
138  bool ConnectionSOCKS5Proxy::send( const std::string& data )
139  {
140 // if( m_s5state != S5StateConnected )
141 // {
142 // printf( "p data sent: " );
143 // const char* x = data.c_str();
144 // for( unsigned int i = 0; i < data.length(); ++i )
145 // printf( "%02X ", (const char)x[i] );
146 // printf( "\n" );
147 // }
148 
149  if( m_connection )
150  return m_connection->send( data );
151 
152  return false;
153  }
154 
156  {
158  m_s5state = S5StateDisconnected;
159 
160  if( m_connection )
161  m_connection->cleanup();
162  }
163 
164  void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut )
165  {
166  if( m_connection )
167  m_connection->getStatistics( totalIn, totalOut );
168  else
169  {
170  totalIn = 0;
171  totalOut = 0;
172  }
173  }
174 
176  const std::string& data )
177  {
178 // if( m_s5state != S5StateConnected )
179 // {
180 // printf( "data recv: " );
181 // const char* x = data.c_str();
182 // for( unsigned int i = 0; i < data.length(); ++i )
183 // printf( "%02X ", (const char)x[i] );
184 // printf( "\n" );
185 // }
186 
187  if( !m_connection || !m_handler )
188  return;
189 
190  ConnectionError connError = ConnNoError;
191 
192  switch( m_s5state )
193  {
194  case S5StateConnecting:
195  if( data.length() != 2 || data[0] != 0x05 )
196  connError = ConnIoError;
197 
198  if( data[1] == 0x00 ) // no auth
199  {
200  negotiate();
201  }
202  else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
203  {
204  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
205  "authenticating to socks5 proxy as user " + m_proxyUser );
206  m_s5state = S5StateAuthenticating;
207  char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
208  size_t pos = 0;
209  d[pos++] = 0x01;
210  d[pos++] = (char)m_proxyUser.length();
211  strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
212  pos += m_proxyUser.length();
213  d[pos++] = (char)m_proxyPwd.length();
214  strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
215  pos += m_proxyPwd.length();
216 
217  if( !send( std::string( d, pos ) ) )
218  {
219  cleanup();
221  }
222  delete[] d;
223  }
224  else
225  {
226  if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() )
227  connError = ConnProxyNoSupportedAuth;
228  else
229  connError = ConnProxyAuthRequired;
230  }
231  break;
232  case S5StateNegotiating:
233  if( data.length() >= 6 && data[0] == 0x05 )
234  {
235  if( data[1] == 0x00 )
236  {
238  m_s5state = S5StateConnected;
239  m_handler->handleConnect( this );
240  }
241  else // connection refused
242  connError = ConnConnectionRefused;
243  }
244  else
245  connError = ConnIoError;
246  break;
247  case S5StateAuthenticating:
248  if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
249  negotiate();
250  else
251  connError = ConnProxyAuthFailed;
252  break;
253  case S5StateConnected:
254  m_handler->handleReceivedData( this, data );
255  break;
256  default:
257  break;
258  }
259 
260  if( connError != ConnNoError )
261  {
262  m_connection->disconnect();
263  m_handler->handleDisconnect( this, connError );
264  }
265 
266  }
267 
268  void ConnectionSOCKS5Proxy::negotiate()
269  {
270  m_s5state = S5StateNegotiating;
271  char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
272  size_t pos = 0;
273  d[pos++] = 0x05; // SOCKS version 5
274  d[pos++] = 0x01; // command CONNECT
275  d[pos++] = 0x00; // reserved
276  int port = m_port;
277  std::string server = m_server;
278  if( m_ip ) // IP address
279  {
280  d[pos++] = 0x01; // IPv4 address
281  std::string s;
282  const size_t j = server.length();
283  size_t l = 0;
284  for( size_t k = 0; k < j && l < 4; ++k )
285  {
286  if( server[k] != '.' )
287  s += server[k];
288 
289  if( server[k] == '.' || k == j-1 )
290  {
291  d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
292  s = EmptyString;
293  ++l;
294  }
295  }
296  }
297  else // hostname
298  {
299  if( port == -1 )
300  {
301  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
302  if( servers.size() )
303  {
304  const std::pair< std::string, int >& host = *servers.begin();
305  server = host.first;
306  port = host.second;
307  }
308  }
309  d[pos++] = 0x03; // hostname
310  d[pos++] = (char)m_server.length();
311  strncpy( d + pos, m_server.c_str(), m_server.length() );
312  pos += m_server.length();
313  }
314  int nport = htons( port );
315  d[pos++] = static_cast<char>( nport );
316  d[pos++] = static_cast<char>( nport >> 8 );
317 
318  std::string message = "Requesting socks5 proxy connection to " + server + ":"
319  + util::int2string( port );
320  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
321 
322  if( !send( std::string( d, pos ) ) )
323  {
324  cleanup();
326  }
327  delete[] d;
328  }
329 
331  {
332  if( m_connection )
333  {
334  std::string server = m_server;
335  int port = m_port;
336  if( port == -1 )
337  {
338  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
339  if( !servers.empty() )
340  {
341  const std::pair< std::string, int >& host = *servers.begin();
342  server = host.first;
343  port = host.second;
344  }
345  }
346  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
347  "Attempting to negotiate socks5 proxy connection" );
348 
349  const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
350  const char d[4] = {
351  0x05, // SOCKS version 5
352  static_cast<char>( auth ? 0x02 // two methods
353  : 0x01 ), // one method
354  0x00, // method: no auth
355  0x02 // method: username/password auth
356  };
357 
358  if( !send( std::string( d, auth ? 4 : 3 ) ) )
359  {
360  cleanup();
361  if( m_handler )
363  }
364  }
365  }
366 
368  ConnectionError reason )
369  {
370  cleanup();
371  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
372 
373  if( m_handler )
374  m_handler->handleDisconnect( this, reason );
375  }
376 
377 }