gloox  0.9.9.12
connectionsocks5proxy.cpp
1 /*
2  Copyright (c) 2007-2008 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 "gloox.h"
16 
17 #include "connectionsocks5proxy.h"
18 #include "dns.h"
19 #include "logsink.h"
20 #include "prep.h"
21 #include "base64.h"
22 
23 #include <string>
24 
25 #include <string.h>
26 
27 #if !defined( _WIN32 ) && !defined( _WIN32_WCE )
28 # include <netinet/in.h>
29 #endif
30 
31 #ifdef _WIN32
32 # include <winsock.h>
33 #elif defined( _WIN32_WCE )
34 # include <winsock2.h>
35 #endif
36 
37 #ifndef _WIN32_WCE
38 # include <sstream>
39 #endif
40 
41 #include <cstdlib>
42 
43 namespace gloox
44 {
45 
47  const std::string& server, int port, bool ip )
48  : ConnectionBase( 0 ), m_connection( connection ),
49  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
50  {
51  m_server = prep::idna( server );
52  m_port = port;
53 
54  if( m_connection )
55  m_connection->registerConnectionDataHandler( this );
56  }
57 
59  const LogSink& logInstance,
60  const std::string& server, int port, bool ip )
61  : ConnectionBase( cdh ), m_connection( connection ),
62  m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
63  {
64  m_server = prep::idna( server );
65  m_port = port;
66 
67  if( m_connection )
68  m_connection->registerConnectionDataHandler( this );
69  }
70 
72  {
73  if( m_connection )
74  delete m_connection;
75  }
76 
78  {
79  ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
80  return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
81  }
82 
84  {
85  if( m_connection )
86  delete m_connection;
87 
88  m_connection = connection;
89  }
90 
92  {
93  if( m_connection && m_handler )
94  {
95  m_state = StateConnecting;
96  m_s5state = S5StateConnecting;
97  return m_connection->connect();
98  }
99 
100  return ConnNotConnected;
101  }
102 
104  {
105  if( m_connection )
106  m_connection->disconnect();
107  cleanup();
108  }
109 
111  {
112  if( m_connection )
113  return m_connection->recv( timeout );
114  else
115  return ConnNotConnected;
116  }
117 
119  {
120  if( m_connection )
121  return m_connection->receive();
122  else
123  return ConnNotConnected;
124  }
125 
126  bool ConnectionSOCKS5Proxy::send( const std::string& data )
127  {
128 // if( m_s5state != S5StateConnected )
129 // {
130 // printf( "p data sent: " );
131 // const char* x = data.c_str();
132 // for( unsigned int i = 0; i < data.length(); ++i )
133 // printf( "%02X ", (const char)x[i] );
134 // printf( "\n" );
135 // }
136 
137  if( m_connection )
138  return m_connection->send( data );
139 
140  return false;
141  }
142 
144  {
145  m_state = StateDisconnected;
146  m_s5state = S5StateDisconnected;
147 
148  if( m_connection )
149  m_connection->cleanup();
150  }
151 
152  void ConnectionSOCKS5Proxy::getStatistics( int &totalIn, int &totalOut )
153  {
154  if( m_connection )
155  m_connection->getStatistics( totalIn, totalOut );
156  else
157  {
158  totalIn = 0;
159  totalOut = 0;
160  }
161  }
162 
164  const std::string& data )
165  {
166 // if( m_s5state != S5StateConnected )
167 // {
168 // printf( "data recv: " );
169 // const char* x = data.c_str();
170 // for( unsigned int i = 0; i < data.length(); ++i )
171 // printf( "%02X ", (const char)x[i] );
172 // printf( "\n" );
173 // }
174 
175  if( !m_connection || !m_handler )
176  return;
177 
178  switch( m_s5state )
179  {
180  case S5StateConnecting:
181  if( data.length() != 2 || data[0] != 0x05 )
182  {
183  m_connection->disconnect();
184  m_handler->handleDisconnect( this, ConnIoError );
185  }
186  if( data[1] == 0x00 ) // no auth
187  {
188  negotiate();
189  }
190  else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPassword.empty() ) // user/password auth
191  {
193  "authenticating to socks5 proxy as user " + m_proxyUser );
194  m_s5state = S5StateAuthenticating;
195  char* d = new char[3 + m_proxyUser.length() + m_proxyPassword.length()];
196  int pos = 0;
197  d[pos++] = 0x01;
198  d[pos++] = m_proxyUser.length();
199  strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
200  pos += m_proxyUser.length();
201  d[pos++] = m_proxyPassword.length();
202  strncpy( d + pos, m_proxyPassword.c_str(), m_proxyPassword.length() );
203  pos += m_proxyPassword.length();
204 
205  if( !send( std::string( d, pos ) ) )
206  {
207  cleanup();
208  m_handler->handleDisconnect( this, ConnIoError );
209  }
210  delete[] d;
211  }
212  else if( data[1] == (char)0xFF && !m_proxyUser.empty() && !m_proxyPassword.empty() )
213  {
214  m_connection->disconnect();
215  m_handler->handleDisconnect( this, ConnProxyNoSupportedAuth );
216  }
217  else if( data[1] == (char)0xFF && ( m_proxyUser.empty() || m_proxyPassword.empty() ) )
218  {
219  m_connection->disconnect();
220  m_handler->handleDisconnect( this, ConnProxyAuthRequired );
221  }
222  else
223  {
224  m_connection->disconnect();
225  m_handler->handleDisconnect( this, ConnProxyAuthRequired );
226  }
227  break;
228  case S5StateNegotiating:
229  if( data.length() >= 6 && data[0] == 0x05 )
230  {
231  if( data[1] == 0x00 )
232  {
233  m_state = StateConnected;
234  m_s5state = S5StateConnected;
235  m_handler->handleConnect( this );
236  }
237  else // connection refused
238  {
239  m_connection->disconnect();
240  m_handler->handleDisconnect( this, ConnConnectionRefused );
241  }
242  }
243  else
244  {
245  m_connection->disconnect();
246  m_handler->handleDisconnect( this, ConnIoError );
247  }
248  break;
249  case S5StateAuthenticating:
250  if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
251  {
252  negotiate();
253  }
254  else
255  {
256  m_connection->disconnect();
257  m_handler->handleDisconnect( this, ConnProxyAuthFailed );
258  }
259  break;
260  case S5StateConnected:
261  m_handler->handleReceivedData( this, data );
262  break;
263  default:
264  break;
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  int 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  int j = server.length();
283  int l = 0;
284  for( int 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++] = atoi( s.c_str() ) & 0x0FF;
292  s = "";
293  ++l;
294  }
295  }
296  }
297  else // hostname
298  {
299  if( port == -1 )
300  {
301  DNS::HostMap servers = DNS::resolve( m_server, m_logInstance );
302  if( servers.size() )
303  {
304  server = (*(servers.begin())).first;
305  port = (*(servers.begin())).second;
306  }
307  }
308  d[pos++] = 0x03; // hostname
309  d[pos++] = m_server.length();
310  strncpy( d + pos, m_server.c_str(), m_server.length() );
311  pos += m_server.length();
312  }
313  int nport = htons( port );
314  d[pos++] = nport;
315  d[pos++] = nport >> 8;
316 
317 #ifndef _WIN32_WCE
318  std::ostringstream oss;
319  oss << "requesting socks5 proxy connection to " << server << ":" << port;
320  m_logInstance.log( LogLevelDebug, LogAreaClassConnectionSOCKS5Proxy, oss.str() );
321 #endif
322 
323  if( !send( std::string( d, pos ) ) )
324  {
325  cleanup();
326  m_handler->handleDisconnect( this, ConnIoError );
327  }
328  delete[] d;
329  }
330 
332  {
333  if( m_connection )
334  {
335  std::string server = m_server;
336  int port = m_port;
337  if( port == -1 )
338  {
339  DNS::HostMap servers = DNS::resolve( m_server, m_logInstance );
340  if( servers.size() )
341  {
342  server = (*(servers.begin())).first;
343  port = (*(servers.begin())).second;
344  }
345  }
347  "attempting to negotiate socks5 proxy connection" );
348 
349  bool auth = !m_proxyUser.empty() && !m_proxyPassword.empty();
350  char *d = new char[auth ? 4 : 3];
351  d[0] = 0x05; // SOCKS version 5
352  if( auth )
353  {
354  d[1] = 0x02; // two methods
355  d[3] = 0x02; // method: username/password auth
356  }
357  else
358  d[1] = 0x01; // one method
359  d[2] = 0x00; // method: no auth
360 
361  if( !send( std::string( d, auth ? 4 : 3 ) ) )
362  {
363  cleanup();
364  if( m_handler )
365  m_handler->handleDisconnect( this, ConnIoError );
366  }
367  delete[] d;
368  }
369  }
370 
372  ConnectionError reason )
373  {
374  cleanup();
375  m_logInstance.log( LogLevelDebug, LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
376 
377  if( m_handler )
378  m_handler->handleDisconnect( this, reason );
379  }
380 
381 }