- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
47 GNet::SimpleClient::SimpleClient( const ResolverInfo & remote ,
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
61 GNet::SimpleClient::~SimpleClient()
62 {
63 if( Monitor::instance() ) Monitor::instance()->removeClient( *this ) ;
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
75 GNet::ResolverInfo GNet::SimpleClient::resolverInfo() const
76 {
77 return m_remote ;
78 }
79
80 void GNet::SimpleClient::updateResolverInfo( const ResolverInfo & update )
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
89 GNet::StreamSocket & GNet::SimpleClient::socket()
90 {
91 if( m_s.get() == NULL )
92 throw NotConnected() ;
93 return *m_s.get() ;
94 }
95
96 const GNet::StreamSocket & GNet::SimpleClient::socket() const
97 {
98 if( m_s.get() == NULL )
99 throw NotConnected() ;
100 return *m_s.get() ;
101 }
102
103 void GNet::SimpleClient::connect()
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
188 bool GNet::SimpleClient::connected() const
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
314 void GNet::SimpleClient::writeEvent()
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
347 void GNet::SimpleClient::readEvent()
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
388 std::string GNet::SimpleClient::peerCertificate() const
389 {
390 return m_sp->peerCertificate() ;
391 }
392
393 void GNet::SimpleClient::sslConnect()
394 {
395 if( m_sp.get() == NULL )
396 throw NotConnected( "for ssl-connect" ) ;
397 m_sp->sslConnect() ;
398 }
399
400 void GNet::SimpleClient::onConnectImp()
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
411 void GNet::SimpleClient::onSendImp()
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
457 GNet::ClientResolver::ClientResolver( SimpleClient & client ) :
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
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime.cpp:34
GNet::SimpleClient
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
Definition: gsimpleclient.h:81
GNet::SimpleClient::send
bool send(const std::string &data, std::string::size_type offset=0)
Returns true if all sent, or false if flow control was asserted.
Definition: gsimpleclient.cpp:404
GNet::SimpleClient::sslConnect
void sslConnect()
Starts TLS/SSL client-side negotiation.
Definition: gsimpleclient.cpp:393
glog.h
GNet::ClientResolver
A resolver class which calls SimpleClient::resolveCon() when done.
Definition: gsimpleclient.h:48
GNet::SimpleClient::peerAddress
virtual std::pair< bool, Address > peerAddress() const
Override from Connection.
Definition: gsimpleclient.cpp:380
GNet::SimpleClient::peerCertificate
virtual std::string peerCertificate() const
Returns the peer's TLS certificate.
Definition: gsimpleclient.cpp:388
GNet::SimpleClient::ConnectStatus
ConnectStatus
Definition: gsimpleclient.h:84
GNet::Resolver
A class for asynchronous TCP name-to-address resolution.
Definition: gresolver.h:45
GNet::Address
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
G::Root
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
GNet::ResolverInfo
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
md5::size_type
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
GNet::SimpleClient::writeEvent
virtual void writeEvent()
Final override from GNet::EventHandler.
Definition: gsimpleclient.cpp:314
GNet::Monitor::addClient
void addClient(const Connection &simple_client)
Adds a client connection.
Definition: gmonitor.cpp:115
GNet::Resolver::resolve
static std::string resolve(ResolverInfo &host_and_service, bool udp=false)
Does syncronous name resolution.
Definition: gresolver.cpp:27
GNet::Address::invalidAddress
static Address invalidAddress()
Returns an invalid address.
Definition: gaddress_ipv4.cpp:458
GNet::SimpleClient::connected
bool connected() const
Returns true if connected to the peer.
Definition: gsimpleclient.cpp:188
groot.h
gtest.h
GNet::SimpleClient::onConnectImp
virtual void onConnectImp()
An alternative to onConnect() for private implementation classes.
Definition: gsimpleclient.cpp:400
GNet::ClientResolver::ClientResolver
ClientResolver(SimpleClient &)
Constructor.
Definition: gsimpleclient.cpp:457
GNet::SimpleClient::SimpleClient
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.
Definition: gsimpleclient.cpp:47
GNet::SimpleClient::updateResolverInfo
void updateResolverInfo(const ResolverInfo &)
Updates the constructor's ResolverInfo object with the given one as long as both objects have the sam...
Definition: gsimpleclient.cpp:80
GNet::SimpleClient::canRetry
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...
Definition: gsimpleclient.cpp:161
gsimpleclient.h
GNet::ResolverInfo::hasAddress
bool hasAddress() const
Returns true after update() has been called.
Definition: gresolverinfo.cpp:88
GNet::SimpleClient::connect
void connect()
Initates a connection to the remote server.
Definition: gsimpleclient.cpp:103
GNet::StreamSocket
A derivation of Socket for a stream socket.
Definition: gsocket.h:270
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
GNet::Address::displayString
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.
Definition: gaddress_ipv4.cpp:581
gassert.h
GNet::Monitor::instance
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:105
G_LOG
#define G_LOG(expr)
Definition: glog.h:98
GNet::Address::validPort
static bool validPort(unsigned int n)
Returns true if the port number is within the valid range.
Definition: gaddress_ipv4.cpp:621
GNet::SimpleClient::logId
std::string logId() const
Returns a identification string for logging purposes.
Definition: gsimpleclient.cpp:67
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
gsocket.h
GNet::Monitor::removeClient
void removeClient(const Connection &simple_client)
Removes a client connection.
Definition: gmonitor.cpp:121
gmonitor.h
GNet::ClientResolver::resolveCon
void resolveCon(bool success, const Address &address, std::string reason)
From Resolver.
Definition: gsimpleclient.cpp:463
gdatetime.h
GNet::SimpleClient::onSendImp
virtual void onSendImp()
Called from within send().
Definition: gsimpleclient.cpp:411
gexception.h
GNet::SimpleClient::~SimpleClient
virtual ~SimpleClient()
Destructor.
Definition: gsimpleclient.cpp:61
G::Exception
A general-purpose exception class derived from std::exception and containing a std::string.
Definition: gexception.h:44
GNet::ResolverInfo::displayString
std::string displayString(bool simple=false) const
Returns a string representation for logging and debug.
Definition: gresolverinfo.cpp:116
GNet::SimpleClient::resolverInfo
ResolverInfo resolverInfo() const
Returns a ResolverInfo structure containing the result of host() and service() name lookup if availab...
Definition: gsimpleclient.cpp:75
GNet::ResolverInfo::service
std::string service() const
Returns the remote service name, as passed in to the constructor.
Definition: gresolverinfo.cpp:83
gaddress.h
GNet::SimpleClient::localAddress
virtual std::pair< bool, Address > localAddress() const
Override from Connection.
Definition: gsimpleclient.cpp:372
GNet::SimpleClient::readEvent
virtual void readEvent()
Final override from GNet::EventHandler.
Definition: gsimpleclient.cpp:347
GNet::Socket::ssize_type
ssize_t ssize_type
Definition: gsocket.h:64
GNet::SimpleClient::socket
StreamSocket & socket()
Returns a reference to the socket. Throws if not connected.
Definition: gsimpleclient.cpp:89
GNet::ResolverInfo::host
std::string host() const
Returns the remote host name, as passed in to the constructor.
Definition: gresolverinfo.cpp:78
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107
gdebug.h
gresolver.h