gsimpleclient.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gsimpleclient.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gaddress.h"
24 #include "gsocket.h"
25 #include "gdatetime.h"
26 #include "gexception.h"
27 #include "gresolver.h"
28 #include "groot.h"
29 #include "gmonitor.h"
30 #include "gsimpleclient.h"
31 #include "gassert.h"
32 #include "gtest.h"
33 #include "gdebug.h"
34 #include "glog.h"
35 #include <cstdlib>
36 
37 namespace
38 {
39  const int c_retries = 10 ; // number of retries when using a privileged local port number
40  const int c_port_start = 512 ;
41  const int c_port_end = 1024 ;
42  const char * c_cannot_connect_to = "cannot connect to " ;
43 }
44 
45 // ==
46 
48  const Address & local_address , bool privileged , bool sync_dns ,
49  unsigned int secure_connection_timeout ) :
50  m_remote(remote) ,
51  m_local_address(local_address) ,
52  m_privileged(privileged) ,
53  m_state(Idle) ,
54  m_sync_dns(sync_dns) ,
55  m_secure_connection_timeout(secure_connection_timeout)
56 {
57  G_DEBUG( "SimpleClient::ctor" ) ;
58  if( Monitor::instance() ) Monitor::instance()->addClient( *this ) ;
59 }
60 
62 {
64  close() ;
65 }
66 
67 std::string GNet::SimpleClient::logId() const
68 {
69  std::string s = m_remote.displayString(true) ;
70  if( m_s.get() != NULL )
71  s.append( std::string() + "@" + m_s->asString() ) ; // cf. ServerPeer::logId()
72  return s ;
73 }
74 
76 {
77  return m_remote ;
78 }
79 
81 {
82  if( m_remote.host() == update.host() && m_remote.service() == update.service() && update.hasAddress() )
83  {
84  G_DEBUG( "GNet::SimpleClient::updateResolverInfo: reusing dns lookup for " << update.displayString() ) ;
85  m_remote = update ;
86  }
87 }
88 
90 {
91  if( m_s.get() == NULL )
92  throw NotConnected() ;
93  return *m_s.get() ;
94 }
95 
97 {
98  if( m_s.get() == NULL )
99  throw NotConnected() ;
100  return *m_s.get() ;
101 }
102 
104 {
105  G_DEBUG( "GNet::SimpleClient::connect: [" << m_remote.str() << "]" ) ;
106  if( m_state != Idle )
107  {
108  G_WARNING( "SimpleClient::connect: invalid state" ) ;
109  return ;
110  }
111 
112  if( m_remote.hasAddress() )
113  {
114  bool immediate = startConnecting() ;
115  if( immediate )
116  {
117  immediateConnection() ; // calls onConnect()
118  }
119  else
120  {
121  setState( Connecting ) ;
122  }
123  }
124  else if( m_sync_dns )
125  {
126  std::string error = Resolver::resolve( m_remote ) ;
127  if( !error.empty() )
128  {
129  throw DnsError( error ) ;
130  }
131  bool immediate = startConnecting() ;
132  if( immediate )
133  {
134  immediateConnection() ; // calls onConnect()
135  }
136  else
137  {
138  setState( Connecting ) ;
139  }
140  }
141  else
142  {
143  if( m_resolver.get() == NULL )
144  m_resolver <<= new ClientResolver( *this ) ;
145  if( !m_resolver->resolveReq( m_remote.str() ) )
146  throw DnsError( m_remote.str() ) ;
147  setState( Resolving ) ;
148  }
149 }
150 
151 void GNet::SimpleClient::immediateConnection()
152 {
153  G_DEBUG( "GNet::SimpleClient::connect: immediate connection" ) ;
154  socket().addReadHandler( *this ) ;
155  socket().addExceptionHandler( *this ) ;
156  setState( Connected ) ;
157  onConnectImp() ; // from within connect()
158  onConnect() ; // from within connect()
159 }
160 
161 bool GNet::SimpleClient::canRetry( const std::string & error )
162 {
163  return error.find( c_cannot_connect_to ) == 0U ;
164 }
165 
166 unsigned int GNet::SimpleClient::getRandomPort()
167 {
168  static bool first = true ;
169  if( first )
170  {
171  std::srand( static_cast<unsigned int>(G::DateTime::now()) ) ;
172  first = false ;
173  }
174 
175  int r = std::rand() ;
176  if( r < 0 ) r = -r ;
177  r = c_port_start + ( r % (c_port_end - c_port_start) ) ;
178  G_ASSERT( r > 0 ) ;
179  return static_cast<unsigned int>(r) ;
180 }
181 
182 void GNet::SimpleClient::close()
183 {
184  m_sp <<= 0 ;
185  m_s <<= 0 ;
186 }
187 
189 {
190  return m_state == Connected ;
191 }
192 
193 void GNet::SimpleClient::resolveCon( bool success , const Address & address , std::string name_or_reason )
194 {
195  if( success )
196  {
197  G_DEBUG( "GNet::SimpleClient::resolveCon: " << address.displayString() ) ;
198  std::string peer_name = name_or_reason ;
199  m_remote.update( address , peer_name ) ;
200  bool immediate = startConnecting() ;
201  setState( immediate ? Connected : Connecting ) ;
202  }
203  else
204  {
205  throw DnsError( name_or_reason ) ;
206  }
207 }
208 
209 bool GNet::SimpleClient::startConnecting()
210 {
211  G_DEBUG( "GNet::SimpleClient::startConnecting: " << m_remote.displayString() ) ;
212 
213  // create and open a socket
214  //
215  m_s <<= new StreamSocket( m_remote.address() ) ;
216  if( !socket().valid() )
217  throw ConnectError( "cannot open socket" ) ;
218 
219  // create a socket protocol object
220  //
221  m_sp <<= new SocketProtocol( *this , *this , *m_s.get() , m_secure_connection_timeout ) ;
222 
223  // specifiy this as a 'write' event handler for the socket
224  // (before the connect() in case it is reentrant)
225  //
226  socket().addWriteHandler( *this ) ;
227 
228  // bind a local address to the socket and connect
229  //
230  ConnectStatus status = Failure ;
231  std::string error ;
232  if( m_privileged )
233  {
234  for( int i = 0 ; i < c_retries ; i++ )
235  {
236  unsigned int port = getRandomPort() ;
237  m_local_address.setPort( port ) ;
238  G_DEBUG( "GNet::SimpleClient::startConnecting: trying to bind " << m_local_address.displayString() ) ;
239  status = localBind( m_local_address ) ? Success : Retry ;
240  if( status == Retry )
241  continue ;
242 
243  status = connectCore( m_remote.address() , &error ) ;
244  if( status != Retry )
245  break ;
246  }
247  }
248  else if( m_local_address == Address(0U) )
249  {
250  status = connectCore( m_remote.address() , &error ) ;
251  }
252  else
253  {
254  if( localBind( m_local_address ) )
255  status = connectCore( m_remote.address() , &error ) ;
256  }
257 
258  // deal with immediate connection (typically if connecting locally)
259  //
260  bool immediate = status == ImmediateSuccess ;
261  if( status != Success )
262  {
263  socket().dropWriteHandler() ;
264  }
265  if( immediate && m_remote.socks() )
266  {
267  immediate = false ;
268  socket().addReadHandler( *this ) ;
269  socket().addExceptionHandler( *this ) ;
270  setState( Socksing ) ;
271  sendSocksRequest() ;
272  }
273 
274  return immediate ;
275 }
276 
277 bool GNet::SimpleClient::localBind( Address local_address )
278 {
279  G::Root claim_root ;
280  bool bound = socket().bind(local_address) ;
281  if( bound )
282  {
283  G_DEBUG( "GNet::SimpleClient::bind: bound local address " << local_address.displayString() ) ;
284  }
285  return bound ;
286 }
287 
288 GNet::SimpleClient::ConnectStatus GNet::SimpleClient::connectCore( Address remote_address , std::string *error_p )
289 {
290  G_ASSERT( error_p != NULL ) ;
291  std::string & error = *error_p ;
292 
293  // initiate the connection
294  //
295  bool immediate = false ;
296  if( !socket().connect( remote_address , &immediate ) )
297  {
298  G_DEBUG( "GNet::SimpleClient::connectCore: immediate failure" ) ;
299  error = c_cannot_connect_to + remote_address.displayString() ; // see canRetry()
300 
301  // we should return Failure here, but Microsoft's stack
302  // will happily bind the same local address more than once,
303  // so it is the connect that fails, not the bind, if
304  // the port was already in use
305  //
306  return Retry ;
307  }
308  else
309  {
310  return immediate ? ImmediateSuccess : Success ;
311  }
312 }
313 
315 {
316  G_DEBUG( "GNet::SimpleClient::writeEvent" ) ;
317 
318  if( m_state == Connected )
319  {
320  if( m_sp->writeEvent() )
321  onSendComplete() ;
322  }
323  else if( m_state == Connecting && socket().hasPeer() )
324  {
325  socket().addReadHandler( *this ) ;
326  socket().addExceptionHandler( *this ) ;
327  socket().dropWriteHandler() ;
328 
329  if( m_remote.socks() )
330  {
331  setState( Socksing ) ;
332  sendSocksRequest() ;
333  }
334  else
335  {
336  setState( Connected ) ;
337  onConnectImp() ;
338  onConnect() ;
339  }
340  }
341  else if( m_state == Connecting )
342  {
343  throw G::Exception( c_cannot_connect_to + m_remote.address().displayString() ) ; // see canRetry()
344  }
345 }
346 
348 {
349  G_ASSERT( m_sp.get() != NULL ) ;
350  if( m_state == Socksing )
351  {
352  bool complete = readSocksResponse() ;
353  if( complete )
354  {
355  setState( Connected ) ;
356  onConnectImp() ;
357  onConnect() ;
358  }
359  }
360  else
361  {
362  if( m_sp.get() != NULL )
363  m_sp->readEvent() ;
364  }
365 }
366 
367 void GNet::SimpleClient::setState( State new_state )
368 {
369  m_state = new_state ;
370 }
371 
372 std::pair<bool,GNet::Address> GNet::SimpleClient::localAddress() const
373 {
374  return
375  m_s.get() != NULL ?
376  socket().getLocalAddress() :
377  std::make_pair(false,GNet::Address::invalidAddress()) ;
378 }
379 
380 std::pair<bool,GNet::Address> GNet::SimpleClient::peerAddress() const
381 {
382  return
383  m_s.get() != NULL ?
384  socket().getPeerAddress() :
385  std::make_pair(false,GNet::Address::invalidAddress()) ;
386 }
387 
389 {
390  return m_sp->peerCertificate() ;
391 }
392 
394 {
395  if( m_sp.get() == NULL )
396  throw NotConnected( "for ssl-connect" ) ;
397  m_sp->sslConnect() ;
398 }
399 
401 {
402 }
403 
404 bool GNet::SimpleClient::send( const std::string & data , std::string::size_type offset )
405 {
406  bool rc = m_sp->send( data , offset ) ;
407  onSendImp() ; // allow derived classes to implement a response timeout
408  return rc ;
409 }
410 
412 {
413 }
414 
415 void GNet::SimpleClient::sendSocksRequest()
416 {
417  unsigned int far_port = m_remote.socksFarPort() ;
418  if( !Address::validPort(far_port) ) throw SocksError("invalid port") ;
419  g_port_t far_port_n = htons( static_cast<g_port_t>(far_port) ) ;
420  g_port_t far_port_lo = far_port_n & 0xffU ;
421  g_port_t far_port_hi = (far_port_n>>8) & 0xffU ;
422 
423  std::string userid ; // TODO - socks userid
424  std::string data ;
425  data.append( 1U , 4 ) ; // version 4
426  data.append( 1U , 1 ) ; // connect request
427  data.append( 1U , static_cast<char>(far_port_lo) ) ;
428  data.append( 1U , static_cast<char>(far_port_hi) ) ;
429  data.append( 1U , 0 ) ;
430  data.append( 1U , 0 ) ;
431  data.append( 1U , 0 ) ;
432  data.append( 1U , 1 ) ;
433  data.append( userid ) ;
434  data.append( 1U , 0 ) ; // NUL
435  data.append( m_remote.socksFarHost() ) ;
436  data.append( 1U , 0 ) ; // NUL
437  GNet::Socket::ssize_type n = socket().write( data.data() , data.size() ) ;
438  if( static_cast<std::string::size_type>(n) != data.size() ) // TODO - socks flow control
439  throw SocksError( "request not sent" ) ;
440 }
441 
442 bool GNet::SimpleClient::readSocksResponse()
443 {
444  char buffer[8] ;
445  GNet::Socket::ssize_type rc = socket().read( buffer , sizeof(buffer) ) ;
446  if( rc == 0 || ( rc == -1 && !socket().eWouldBlock() ) ) throw SocksError( "read error" ) ;
447  else if( rc == -1 ) return false ; // go again
448  if( rc != 8 ) throw SocksError( "incomplete response" ) ; // TODO - socks response reassembly
449  if( buffer[0] != 0 ) throw SocksError( "invalid response" ) ;
450  if( buffer[1] != 'Z' ) throw SocksError( "request rejected" ) ;
451  G_LOG( "GNet::SimpleClient::readSocksResponse: " << logId() << ": socks connection completed" ) ;
452  return true ;
453 }
454 
455 // ===
456 
458  Resolver(client) ,
459  m_client(client)
460 {
461 }
462 
463 void GNet::ClientResolver::resolveCon( bool success , const Address &address ,
464  std::string reason )
465 {
466  m_client.resolveCon( success , address , reason ) ;
467 }
468 
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime.cpp:34
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
Definition: gsimpleclient.h:81
bool send(const std::string &data, std::string::size_type offset=0)
Returns true if all sent, or false if flow control was asserted.
void sslConnect()
Starts TLS/SSL client-side negotiation.
A resolver class which calls SimpleClient::resolveCon() when done.
Definition: gsimpleclient.h:48
virtual std::pair< bool, Address > peerAddress() const
Override from Connection.
virtual std::string peerCertificate() const
Returns the peer's TLS certificate.
A class for asynchronous TCP name-to-address resolution.
Definition: gresolver.h:45
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
virtual void writeEvent()
Final override from GNet::EventHandler.
void addClient(const Connection &simple_client)
Adds a client connection.
Definition: gmonitor.cpp:115
static std::string resolve(ResolverInfo &host_and_service, bool udp=false)
Does syncronous name resolution.
Definition: gresolver.cpp:27
static Address invalidAddress()
Returns an invalid address.
bool connected() const
Returns true if connected to the peer.
virtual void onConnectImp()
An alternative to onConnect() for private implementation classes.
ClientResolver(SimpleClient &)
Constructor.
SimpleClient(const ResolverInfo &remote_info, const Address &local_address=Address(0U), bool privileged=false, bool sync_dns=synchronousDnsDefault(), unsigned int secure_connection_timeout=0U)
Constructor.
void updateResolverInfo(const ResolverInfo &)
Updates the constructor's ResolverInfo object with the given one as long as both objects have the sam...
static bool canRetry(const std::string &reason)
Parses the given failure reason and returns true if the client can reasonably retry at some later tim...
bool hasAddress() const
Returns true after update() has been called.
void connect()
Initates a connection to the remote server.
A derivation of Socket for a stream socket.
Definition: gsocket.h:270
#define G_ASSERT(test)
Definition: gassert.h:30
std::string displayString(bool with_port=true, bool with_scope_id=false) const
Returns a string which represents the address for debugging and diagnostics purposes.
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:105
#define G_LOG(expr)
Definition: glog.h:98
static bool validPort(unsigned int n)
Returns true if the port number is within the valid range.
std::string logId() const
Returns a identification string for logging purposes.
#define G_DEBUG(expr)
Definition: glog.h:95
void removeClient(const Connection &simple_client)
Removes a client connection.
Definition: gmonitor.cpp:121
void resolveCon(bool success, const Address &address, std::string reason)
From Resolver.
virtual void onSendImp()
Called from within send().
virtual ~SimpleClient()
Destructor.
A general-purpose exception class derived from std::exception and containing a std::string.
Definition: gexception.h:44
std::string displayString(bool simple=false) const
Returns a string representation for logging and debug.
ResolverInfo resolverInfo() const
Returns a ResolverInfo structure containing the result of host() and service() name lookup if availab...
std::string service() const
Returns the remote service name, as passed in to the constructor.
virtual std::pair< bool, Address > localAddress() const
Override from Connection.
virtual void readEvent()
Final override from GNet::EventHandler.
ssize_t ssize_type
Definition: gsocket.h:64
StreamSocket & socket()
Returns a reference to the socket. Throws if not connected.
std::string host() const
Returns the remote host name, as passed in to the constructor.
#define G_WARNING(expr)
Definition: glog.h:107