39 const int c_retries = 10 ;
40 const int c_port_start = 512 ;
41 const int c_port_end = 1024 ;
42 const char * c_cannot_connect_to =
"cannot connect to " ;
48 const Address & local_address ,
bool privileged ,
bool sync_dns ,
49 unsigned int secure_connection_timeout ) :
51 m_local_address(local_address) ,
52 m_privileged(privileged) ,
54 m_sync_dns(sync_dns) ,
55 m_secure_connection_timeout(secure_connection_timeout)
57 G_DEBUG(
"SimpleClient::ctor" ) ;
69 std::string s = m_remote.displayString(
true) ;
70 if( m_s.get() != NULL )
71 s.append( std::string() +
"@" + m_s->asString() ) ;
82 if( m_remote.host() == update.
host() && m_remote.service() == update.
service() && update.
hasAddress() )
84 G_DEBUG(
"GNet::SimpleClient::updateResolverInfo: reusing dns lookup for " << update.
displayString() ) ;
91 if( m_s.get() == NULL )
92 throw NotConnected() ;
98 if( m_s.get() == NULL )
99 throw NotConnected() ;
105 G_DEBUG(
"GNet::SimpleClient::connect: [" << m_remote.str() <<
"]" ) ;
106 if( m_state != Idle )
108 G_WARNING(
"SimpleClient::connect: invalid state" ) ;
112 if( m_remote.hasAddress() )
114 bool immediate = startConnecting() ;
117 immediateConnection() ;
121 setState( Connecting ) ;
124 else if( m_sync_dns )
129 throw DnsError( error ) ;
131 bool immediate = startConnecting() ;
134 immediateConnection() ;
138 setState( Connecting ) ;
143 if( m_resolver.get() == NULL )
145 if( !m_resolver->resolveReq( m_remote.str() ) )
146 throw DnsError( m_remote.str() ) ;
147 setState( Resolving ) ;
151 void GNet::SimpleClient::immediateConnection()
153 G_DEBUG(
"GNet::SimpleClient::connect: immediate connection" ) ;
154 socket().addReadHandler( *
this ) ;
155 socket().addExceptionHandler( *
this ) ;
156 setState( Connected ) ;
163 return error.find( c_cannot_connect_to ) == 0U ;
166 unsigned int GNet::SimpleClient::getRandomPort()
168 static bool first = true ;
175 int r = std::rand() ;
177 r = c_port_start + ( r % (c_port_end - c_port_start) ) ;
179 return static_cast<unsigned int>(r) ;
182 void GNet::SimpleClient::close()
190 return m_state == Connected ;
193 void GNet::SimpleClient::resolveCon(
bool success ,
const Address & address , std::string name_or_reason )
198 std::string peer_name = name_or_reason ;
199 m_remote.update( address , peer_name ) ;
200 bool immediate = startConnecting() ;
201 setState( immediate ? Connected : Connecting ) ;
205 throw DnsError( name_or_reason ) ;
209 bool GNet::SimpleClient::startConnecting()
211 G_DEBUG(
"GNet::SimpleClient::startConnecting: " << m_remote.displayString() ) ;
215 m_s <<=
new StreamSocket( m_remote.address() ) ;
216 if( !socket().valid() )
217 throw ConnectError(
"cannot open socket" ) ;
221 m_sp <<=
new SocketProtocol( *
this , *
this , *m_s.get() , m_secure_connection_timeout ) ;
226 socket().addWriteHandler( *
this ) ;
230 ConnectStatus status = Failure ;
234 for(
int i = 0 ; i < c_retries ; i++ )
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 )
243 status = connectCore( m_remote.address() , &error ) ;
244 if( status != Retry )
248 else if( m_local_address == Address(0U) )
250 status = connectCore( m_remote.address() , &error ) ;
254 if( localBind( m_local_address ) )
255 status = connectCore( m_remote.address() , &error ) ;
260 bool immediate = status == ImmediateSuccess ;
261 if( status != Success )
263 socket().dropWriteHandler() ;
265 if( immediate && m_remote.socks() )
268 socket().addReadHandler( *
this ) ;
269 socket().addExceptionHandler( *
this ) ;
270 setState( Socksing ) ;
277 bool GNet::SimpleClient::localBind( Address local_address )
280 bool bound = socket().bind(local_address) ;
283 G_DEBUG(
"GNet::SimpleClient::bind: bound local address " << local_address.displayString() ) ;
291 std::string & error = *error_p ;
295 bool immediate = false ;
296 if( !socket().connect( remote_address , &immediate ) )
298 G_DEBUG(
"GNet::SimpleClient::connectCore: immediate failure" ) ;
299 error = c_cannot_connect_to + remote_address.displayString() ;
310 return immediate ? ImmediateSuccess : Success ;
316 G_DEBUG(
"GNet::SimpleClient::writeEvent" ) ;
318 if( m_state == Connected )
320 if( m_sp->writeEvent() )
323 else if( m_state == Connecting && socket().hasPeer() )
325 socket().addReadHandler( *
this ) ;
326 socket().addExceptionHandler( *
this ) ;
327 socket().dropWriteHandler() ;
329 if( m_remote.socks() )
331 setState( Socksing ) ;
336 setState( Connected ) ;
341 else if( m_state == Connecting )
343 throw G::Exception( c_cannot_connect_to + m_remote.address().displayString() ) ;
350 if( m_state == Socksing )
352 bool complete = readSocksResponse() ;
355 setState( Connected ) ;
362 if( m_sp.get() != NULL )
367 void GNet::SimpleClient::setState( State new_state )
369 m_state = new_state ;
376 socket().getLocalAddress() :
384 socket().getPeerAddress() :
390 return m_sp->peerCertificate() ;
395 if( m_sp.get() == NULL )
396 throw NotConnected(
"for ssl-connect" ) ;
406 bool rc = m_sp->send( data , offset ) ;
415 void GNet::SimpleClient::sendSocksRequest()
417 unsigned int far_port = m_remote.socksFarPort() ;
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 ;
425 data.append( 1U , 4 ) ;
426 data.append( 1U , 1 ) ;
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 ) ;
435 data.append( m_remote.socksFarHost() ) ;
436 data.append( 1U , 0 ) ;
438 if( static_cast<std::string::size_type>(n) != data.size() )
439 throw SocksError(
"request not sent" ) ;
442 bool GNet::SimpleClient::readSocksResponse()
446 if( rc == 0 || ( rc == -1 && !socket().eWouldBlock() ) )
throw SocksError(
"read error" ) ;
447 else if( rc == -1 )
return false ;
448 if( rc != 8 )
throw SocksError(
"incomplete response" ) ;
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" ) ;
466 m_client.resolveCon( success , address , reason ) ;
static EpochTime now()
Returns the current epoch time.
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
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.
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.
The Address class encapsulates an IP transport address.
A class which acquires the process's special privileges on construction and releases them on destruct...
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
std::string::size_type size_type
A std::size_t type.
virtual void writeEvent()
Final override from GNet::EventHandler.
void addClient(const Connection &simple_client)
Adds a client connection.
static std::string resolve(ResolverInfo &host_and_service, bool udp=false)
Does syncronous name resolution.
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.
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.
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.
void removeClient(const Connection &simple_client)
Removes a client connection.
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.
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.
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.