gloox  1.0.20
connectionsocks5proxy.cpp
1 /*
2  Copyright (c) 2007-2017 by Jakob Schröter <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_connection || !m_handler )
179  return;
180 
181  if( m_s5state != S5StateConnected )
182  m_proxyHandshakeBuffer += data;
183 
184  ConnectionError connError = ConnNoError;
185 
186  switch( m_s5state )
187  {
188  case S5StateConnecting:
189  if( m_proxyHandshakeBuffer.length() < 2 )
190  return;
191 
192  if( m_proxyHandshakeBuffer.length() != 2 || m_proxyHandshakeBuffer[0] != 0x05 )
193  connError = ConnIoError;
194 
195  if( m_proxyHandshakeBuffer[1] == 0x00 ) // no auth
196  {
197  negotiate();
198  }
199  else if( m_proxyHandshakeBuffer[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
200  {
201  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
202  "authenticating to socks5 proxy as user " + m_proxyUser );
203  m_s5state = S5StateAuthenticating;
204  char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
205  size_t pos = 0;
206  d[pos++] = 0x01;
207  d[pos++] = static_cast<char>( m_proxyUser.length() );
208  strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
209  pos += m_proxyUser.length();
210  d[pos++] = static_cast<char>( m_proxyPwd.length() );
211  strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
212  pos += m_proxyPwd.length();
213 
214  if( !send( std::string( d, pos ) ) )
215  {
216  cleanup();
218  }
219  delete[] d;
220  }
221  else
222  {
223  if( m_proxyHandshakeBuffer[1] == static_cast<char>( 0xFF ) && !m_proxyUser.empty() && !m_proxyPwd.empty() )
224  connError = ConnProxyNoSupportedAuth;
225  else
226  connError = ConnProxyAuthRequired;
227  }
228 
229  m_proxyHandshakeBuffer = "";
230  break;
231  case S5StateNegotiating:
232  if( m_proxyHandshakeBuffer.length() < 6 )
233  return;
234 
235  if( m_proxyHandshakeBuffer.length() >= 6 && m_proxyHandshakeBuffer[0] == 0x05 )
236  {
237  if( m_proxyHandshakeBuffer[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 
249  m_proxyHandshakeBuffer = "";
250  break;
251  case S5StateAuthenticating:
252  if( m_proxyHandshakeBuffer.length() < 2 )
253  return;
254 
255  if( m_proxyHandshakeBuffer.length() == 2 && m_proxyHandshakeBuffer[0] == 0x01 && m_proxyHandshakeBuffer[1] == 0x00 )
256  negotiate();
257  else
258  connError = ConnProxyAuthFailed;
259 
260  m_proxyHandshakeBuffer = "";
261  break;
262  case S5StateConnected:
263  m_handler->handleReceivedData( this, data );
264  break;
265  default:
266  break;
267  }
268 
269  if( connError != ConnNoError )
270  {
271  m_connection->disconnect();
272  m_handler->handleDisconnect( this, connError );
273  }
274 
275  }
276 
277  void ConnectionSOCKS5Proxy::negotiate()
278  {
279  m_s5state = S5StateNegotiating;
280  char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
281  size_t pos = 0;
282  d[pos++] = 0x05; // SOCKS version 5
283  d[pos++] = 0x01; // command CONNECT
284  d[pos++] = 0x00; // reserved
285  int port = m_port;
286  std::string server = m_server;
287  if( m_ip ) // IP address
288  {
289  d[pos++] = 0x01; // IPv4 address
290  std::string s;
291  const size_t j = server.length();
292  size_t l = 0;
293  for( size_t k = 0; k < j && l < 4; ++k )
294  {
295  if( server[k] != '.' )
296  s += server[k];
297 
298  if( server[k] == '.' || k == j-1 )
299  {
300  d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
301  s = EmptyString;
302  ++l;
303  }
304  }
305  }
306  else // hostname
307  {
308  if( port == -1 )
309  {
310  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
311  if( servers.size() )
312  {
313  const std::pair< std::string, int >& host = *servers.begin();
314  server = host.first;
315  port = host.second;
316  }
317  }
318  d[pos++] = 0x03; // hostname
319  d[pos++] = static_cast<char>( m_server.length() );
320  strncpy( d + pos, m_server.c_str(), m_server.length() );
321  pos += m_server.length();
322  }
323  int nport = htons( port );
324  d[pos++] = static_cast<char>( nport );
325  d[pos++] = static_cast<char>( nport >> 8 );
326 
327  std::string message = "Requesting socks5 proxy connection to " + server + ":"
328  + util::int2string( port );
329  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
330 
331  if( !send( std::string( d, pos ) ) )
332  {
333  cleanup();
335  }
336  delete[] d;
337  }
338 
340  {
341  if( m_connection )
342  {
343  std::string server = m_server;
344  int port = m_port;
345  if( port == -1 )
346  {
347  const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
348  if( !servers.empty() )
349  {
350  const std::pair< std::string, int >& host = *servers.begin();
351  server = host.first;
352  port = host.second;
353  }
354  }
355  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
356  "Attempting to negotiate socks5 proxy connection" );
357 
358  const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
359  const char d[4] = {
360  0x05, // SOCKS version 5
361  static_cast<char>( auth ? 0x02 // two methods
362  : 0x01 ), // one method
363  0x00, // method: no auth
364  0x02 // method: username/password auth
365  };
366 
367  if( !send( std::string( d, auth ? 4 : 3 ) ) )
368  {
369  cleanup();
370  if( m_handler )
372  }
373  }
374  }
375 
377  ConnectionError reason )
378  {
379  cleanup();
380  m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
381 
382  if( m_handler )
383  m_handler->handleDisconnect( this, reason );
384  }
385 
386 }
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:202
void dbg(LogArea area, const std::string &message) const
Definition: logsink.h:66
std::map< std::string, int > HostMap
Definition: dns.h:68
ConnectionState state() const
virtual void cleanup()
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)=0
void setConnectionImpl(ConnectionBase *connection)
ConnectionError
Definition: gloox.h:683
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
The namespace for the gloox library.
Definition: adhoc.cpp:27
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)
virtual ConnectionBase * newInstance() const
ConnectionState m_state
ConnectionDataHandler * m_handler
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)
const std::string & server() 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:124
virtual bool send(const std::string &data)
virtual ConnectionError connect()