39 const char * c_cannot_connect_to =
"cannot connect to " ;
45 bool bind_local_address ,
const Address & local_address ,
46 bool sync_dns ,
unsigned int secure_connection_timeout ) :
48 m_remote_location(remote) ,
49 m_bind_local_address(bind_local_address) ,
50 m_local_address(local_address) ,
52 m_sync_dns(sync_dns) ,
53 m_secure_connection_timeout(secure_connection_timeout) ,
56 G_DEBUG(
"SimpleClient::ctor" ) ;
68 std::string s = m_remote_location.displayString() ;
69 if( m_socket.get() != nullptr )
70 s.append( std::string() +
"@" + m_socket->asString() ) ;
76 return m_remote_location ;
81 if( m_remote_location.host() == update.
host() && m_remote_location.service() == update.
service() && update.
resolved() )
83 G_DEBUG(
"GNet::SimpleClient::updateLocation: reusing dns lookup for " << update.
displayString() ) ;
84 m_remote_location = update ;
90 if( m_socket.get() == nullptr )
91 throw NotConnected() ;
92 return *m_socket.get() ;
97 if( m_socket.get() == nullptr )
98 throw NotConnected() ;
99 return *m_socket.get() ;
106 G_DEBUG(
"GNet::SimpleClient::connect: [" << m_remote_location.displayString() <<
"]" ) ;
107 if( m_state != Idle )
108 throw ConnectError(
"wrong state" ) ;
110 m_remote_location.resolveTrivially() ;
111 if( m_remote_location.resolved() )
113 setState( Connecting ) ;
120 throw DnsError( error ) ;
122 setState( Connecting ) ;
127 setState( Resolving ) ;
128 m_resolver.start( m_remote_location ) ;
138 void GNet::SimpleClient::onResolved( std::string error ,
Location location )
143 throw DnsError( error ) ;
145 G_DEBUG(
"GNet::SimpleClient::onResolved: " << location.
displayString() ) ;
146 m_remote_location.update( location.
address() , location.
name() ) ;
147 setState( Connecting ) ;
158 void GNet::SimpleClient::startConnecting()
162 G_DEBUG(
"GNet::SimpleClient::startConnecting: local: " << m_local_address.displayString() ) ;
163 G_DEBUG(
"GNet::SimpleClient::startConnecting: remote: " << m_remote_location.displayString() ) ;
165 setState( Testing ) ;
169 m_socket.reset(
new StreamSocket(m_remote_location.address().domain()) ) ;
170 socket().addWriteHandler( *
this ) ;
174 m_sp.reset(
new SocketProtocol(*
this,*
this,*m_socket.get(),m_secure_connection_timeout) ) ;
178 if( m_bind_local_address )
179 bindLocalAddress( m_local_address ) ;
183 bool immediate = false ;
184 if( !socket().connect( m_remote_location.address() , &immediate ) )
185 throw ConnectError( c_cannot_connect_to + m_remote_location.address().displayString() ) ;
191 socket().dropWriteHandler() ;
192 m_on_connect_timer.startTimer( 0U ) ;
202 void GNet::SimpleClient::onConnectTimer()
204 G_DEBUG(
"GNet::SimpleClient::onConnectTimer: immediate connection" ) ;
210 G_DEBUG(
"GNet::SimpleClient::writeEvent" ) ;
214 void GNet::SimpleClient::onWriteable()
216 if( m_state == Connected )
218 if( m_sp->writeEvent() )
221 else if( m_state == Testing )
223 socket().dropWriteHandler() ;
224 setState( Connecting ) ;
225 m_on_connect_timer.startTimer( 2U , 100000U ) ;
227 else if( m_state == Connecting && socket().hasPeer() )
229 socket().dropWriteHandler() ;
230 socket().addReadHandler( *
this ) ;
231 socket().addExceptionHandler( *
this ) ;
233 if( m_remote_location.socks() )
235 setState( Socksing ) ;
240 setState( Connected ) ;
245 else if( m_state == Connecting )
247 socket().dropWriteHandler() ;
248 throw ConnectError( c_cannot_connect_to + m_remote_location.address().displayString() ) ;
254 G_ASSERT( m_sp.get() != nullptr ) ;
255 if( m_state == Socksing )
257 bool complete = readSocksResponse() ;
260 setState( Connected ) ;
267 if( m_sp.get() != nullptr )
274 return error.find( c_cannot_connect_to ) == 0U ;
277 void GNet::SimpleClient::close()
285 return m_state == Connected ;
288 void GNet::SimpleClient::bindLocalAddress(
const Address & local_address )
292 socket().bind( local_address ) ;
295 if( local_address.
isLoopback() && !m_remote_location.address().isLoopback() )
296 G_WARNING_ONCE(
"GNet::SimpleClient::bindLocalAddress: binding the loopback address for "
297 "outgoing connections may result in connection failures" ) ;
300 void GNet::SimpleClient::setState( State new_state )
302 m_state = new_state ;
308 m_socket.get() !=
nullptr ?
309 socket().getLocalAddress() :
316 m_socket.get() !=
nullptr ?
317 socket().getPeerAddress() :
323 return m_sp->peerCertificate() ;
328 if( m_sp.get() == nullptr )
329 throw NotConnected(
"for ssl-connect" ) ;
339 bool rc = m_sp->send( data , offset ) ;
348 void GNet::SimpleClient::sendSocksRequest()
350 unsigned int far_port = m_remote_location.socksFarPort() ;
352 g_port_t far_port_n = htons( static_cast<g_port_t>(far_port) ) ;
353 g_port_t far_port_lo = far_port_n & 0xffU ;
354 g_port_t far_port_hi = (far_port_n>>8U) & g_port_t(0xffU) ;
358 data.append( 1U , 4 ) ;
359 data.append( 1U , 1 ) ;
360 data.append( 1U , static_cast<char>(far_port_lo) ) ;
361 data.append( 1U , static_cast<char>(far_port_hi) ) ;
362 data.append( 1U , 0 ) ;
363 data.append( 1U , 0 ) ;
364 data.append( 1U , 0 ) ;
365 data.append( 1U , 1 ) ;
366 data.append( userid ) ;
367 data.append( 1U , 0 ) ;
368 data.append( m_remote_location.socksFarHost() ) ;
369 data.append( 1U , 0 ) ;
370 GNet::Socket::ssize_type n = socket().write( data.data() , data.size() ) ;
371 if( static_cast<std::string::size_type>(n) != data.size() )
372 throw SocksError(
"request not sent" ) ;
375 bool GNet::SimpleClient::readSocksResponse()
378 GNet::Socket::ssize_type rc = socket().read( buffer ,
sizeof(buffer) ) ;
379 if( rc == 0 || ( rc == -1 && !socket().eWouldBlock() ) )
throw SocksError(
"read error" ) ;
380 else if( rc == -1 )
return false ;
381 if( rc != 8 )
throw SocksError(
"incomplete response" ) ;
382 if( buffer[0] != 0 )
throw SocksError(
"invalid response" ) ;
383 if( buffer[1] !=
'Z' )
throw SocksError(
"request rejected" ) ;
384 G_LOG(
"GNet::SimpleClient::readSocksResponse: " << logId() <<
": socks connection completed" ) ;
virtual void writeEvent() override
Override from GNet::EventHandler.
virtual void readEvent() override
Override from GNet::EventHandler.
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.
The GNet::Address class encapsulates a TCP/UDP transport address.
bool isLoopback() const
Returns true if this is a loopback address.
virtual std::string peerCertificate() const override
Returns the peer's TLS certificate.
A class which acquires the process's special privileges on construction and releases them on destruct...
void addClient(const Connection &simple_client)
Adds a client connection.
bool connected() const
Returns true if connected to the peer.
virtual void onConnectImp()
An alternative to onConnect() for private implementation classes.
std::string displayString() const
Returns a string representation for logging and debug.
A class that holds a host/service name pair and the preferred address family (if any), and also the results of a name-to-address lookup, ie.
void connect()
Initates a connection to the remote server.
A derivation of GNet::Socket for a stream socket.
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
std::string name() const
Returns the remote canonical name.
static bool enabled()
Returns true if test features are enabled.
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Location remoteLocation() const
Returns a Location structure containing the result of host() and service() name lookup if available...
virtual std::pair< bool, Address > localAddress() const override
Override from Connection.
Address address() const
Returns the remote address.
SimpleClient(const Location &remote_info, bool bind_local_address=false, const Address &local_address=Address::defaultAddress(), bool sync_dns=synchronousDnsDefault(), unsigned int secure_connection_timeout=0U)
Constructor.
bool resolved() const
Returns true after update() has been called.
A base class for classes that handle asynchronous events from the event loop.
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.
std::string service() const
Returns the remote service name, as passed in to the constructor.
void removeClient(const Connection &simple_client)
Removes a client connection.
static bool synchronousDnsDefault()
Returns true if DNS queries should normally be synchronous on this platform.
virtual void onSendImp()
Called from within send().
static bool async()
Returns true if the resolver supports asynchronous operation.
virtual ~SimpleClient()
Destructor.
virtual std::pair< bool, Address > peerAddress() const override
Override from Connection.
static bool connectError(const std::string &reason)
Returns true if the reason string implies the SimpleClient::connect() failed.
static std::string resolve(Location &)
Does syncronous name resolution.
void updateLocation(const Location &)
Updates the constructor's Location object with the given one as long as both objects have the same ho...
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.