35 const size_t c_buffer_size = G::limits::net_buffer ;
36 const int Result_ok = GSsl::Protocol::Result_ok ;
37 const int Result_more = GSsl::Protocol::Result_more ;
38 const int Result_read = GSsl::Protocol::Result_read ;
39 const int Result_write = GSsl::Protocol::Result_write ;
40 const int Result_error = GSsl::Protocol::Result_error ;
49 typedef std::pair<const char *,size_t> Segment ;
50 typedef std::vector<Segment> Segments ;
55 Position(
size_t segment_ ,
size_t offset_ ) : segment(segment_) , offset(offset_) {}
56 Position() : segment(0U) , offset(0U) {}
60 unsigned int secure_connection_timeout ) ;
64 bool send(
const std::string & data ,
size_t offset ) ;
65 bool send(
const Segments & ) ;
68 bool sslEnabled()
const ;
69 std::string peerCertificate()
const ;
75 static void log(
int level ,
const std::string & line ) ;
79 bool rawWriteEvent() ;
80 bool rawSend(
const Segments & ,
Position ,
bool ) ;
83 void sslConnectImp() ;
85 void logSecure(
const std::string & )
const ;
86 void logCertificate(
const std::string & ,
const std::string & )
const ;
87 void logFlowControl(
const char * what ) ;
88 void onSecureConnectionTimeout() ;
89 static size_t size(
const Segments & ) ;
90 static bool finished(
const Segments & ,
Position ) ;
92 static const char * chunk_p(
const Segments & ,
Position ) ;
93 static size_t chunk_n(
const Segments & ,
Position ) ;
96 enum State { State_raw , State_connecting , State_accepting , State_writing , State_idle } ;
100 unsigned int m_secure_connection_timeout ;
101 Segments m_segments ;
103 std::string m_raw_copy ;
104 std::string m_ssl_send_data ;
108 std::vector<char> m_read_buffer ;
109 GSsl::Protocol::ssize_type m_read_buffer_n ;
111 std::string m_peer_certificate ;
116 std::ostream & operator<<( std::ostream & stream ,
const SocketProtocolImp::Position & pos )
118 return stream <<
"(" << pos.segment <<
"," << pos.offset <<
")" ;
120 std::ostream & operator<<( std::ostream & stream ,
const SocketProtocolImp::Segment & segment )
122 return stream <<
"(" << (
const void*)(segment.first) <<
":" << segment.second <<
")" ;
124 std::ostream & operator<<( std::ostream & stream ,
const SocketProtocolImp::Segments & segments )
127 const char * sep =
"" ;
128 for(
size_t i = 0U ; i < segments.size() ; i++ , sep =
"," )
129 stream << sep << segments.at(i) ;
135 GNet::SocketProtocolImp::SocketProtocolImp( EventHandler & handler ,
136 SocketProtocol::Sink & sink , StreamSocket & socket ,
137 unsigned int secure_connection_timeout ) :
141 m_secure_connection_timeout(secure_connection_timeout) ,
145 m_read_buffer(c_buffer_size) ,
147 m_secure_connection_timer(*this,&SocketProtocolImp::onSecureConnectionTimeout,handler)
149 G_ASSERT( m_read_buffer.size() == c_buffer_size ) ;
152 GNet::SocketProtocolImp::~SocketProtocolImp()
157 void GNet::SocketProtocolImp::onSecureConnectionTimeout()
159 G_DEBUG(
"GNet::SocketProtocolImp::onSecureConnectionTimeout: timed out" ) ;
160 throw SocketProtocol::SecureConnectionTimeout() ;
163 void GNet::SocketProtocolImp::readEvent()
165 G_DEBUG(
"SocketProtocolImp::readEvent: read event in state=" << m_state ) ;
166 if( m_state == State_raw )
168 else if( m_state == State_connecting )
170 else if( m_state == State_accepting )
172 else if( m_state == State_writing )
178 bool GNet::SocketProtocolImp::writeEvent()
180 G_DEBUG(
"GNet::SocketProtocolImp::writeEvent: write event in state=" << m_state ) ;
182 if( m_state == State_raw )
183 rc = rawWriteEvent() ;
184 else if( m_state == State_connecting )
186 else if( m_state == State_accepting )
188 else if( m_state == State_writing )
195 size_t GNet::SocketProtocolImp::size(
const Segments & segments )
198 for( Segments::const_iterator p = segments.begin() ; p != segments.end() ; ++p )
200 G_ASSERT( (*p).first !=
nullptr ) ;
201 G_ASSERT( (*p).second != 0U ) ;
209 G_ASSERT( pos.segment < s.size() ) ;
210 G_ASSERT( (pos.offset+offset) <= s.at(pos.segment).second ) ;
211 pos.offset += offset ;
212 if( pos.offset >= s.at(pos.segment).second )
220 const char * GNet::SocketProtocolImp::chunk_p(
const Segments & s , Position pos )
222 G_ASSERT( pos.segment < s.size() ) ;
223 G_ASSERT( pos.offset < s[pos.segment].second ) ;
225 const Segment & segment = s.at( pos.segment ) ;
226 return segment.first + pos.offset ;
229 size_t GNet::SocketProtocolImp::chunk_n(
const Segments & s , Position pos )
231 G_ASSERT( pos.segment < s.size() ) ;
232 G_ASSERT( pos.offset < s[pos.segment].second ) ;
234 const Segment & segment = s.at( pos.segment ) ;
235 return segment.second - pos.offset ;
238 bool GNet::SocketProtocolImp::send(
const Segments & segments )
241 if( m_state != State_raw )
242 throw std::runtime_error(
"scatter/gather overload not implemented for tls" ) ;
244 if( size(segments) == 0U )
247 return rawSend( segments , Position() ,
false ) ;
250 bool GNet::SocketProtocolImp::send(
const std::string & data ,
size_t offset )
252 if( data.empty() || offset >= data.length() )
256 if( m_state == State_raw )
258 Segments segments( 1U ) ;
259 segments[0].first = data.data() ;
260 segments[0].second = data.size() ;
261 rc = rawSend( segments , Position(0U,offset) ,
true ) ;
263 else if( m_state == State_connecting || m_state == State_accepting )
265 throw SocketProtocol::SendError(
"still busy negotiating" ) ;
267 else if( m_state == State_writing )
272 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
276 m_state = State_writing ;
277 m_ssl_send_data.append( data.substr(offset) ) ;
283 void GNet::SocketProtocolImp::log(
int level ,
const std::string & log_line )
286 G_DEBUG(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
287 else if( level == 2 )
288 G_LOG(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
290 G_WARNING(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
293 GSsl::Protocol * GNet::SocketProtocolImp::newProtocol(
const std::string & profile_name )
296 if( library ==
nullptr )
297 throw G::Exception(
"SocketProtocolImp::newProtocol: internal error: no library instance" ) ;
302 void GNet::SocketProtocolImp::sslConnect()
304 G_DEBUG(
"SocketProtocolImp::sslConnect" ) ;
305 G_ASSERT( m_ssl ==
nullptr ) ;
307 m_ssl = newProtocol(
"client" ) ;
308 m_state = State_connecting ;
309 if( m_secure_connection_timeout != 0U )
310 m_secure_connection_timer.startTimer( m_secure_connection_timeout ) ;
314 void GNet::SocketProtocolImp::sslConnectImp()
316 G_DEBUG(
"SocketProtocolImp::sslConnectImp" ) ;
317 G_ASSERT( m_ssl !=
nullptr ) ;
318 G_ASSERT( m_state == State_connecting ) ;
320 GSsl::Protocol::Result rc = m_ssl->
connect( m_socket ) ;
322 if( rc == Result_error )
325 m_state = State_raw ;
326 throw SocketProtocol::ReadError(
"ssl connect" ) ;
328 else if( rc == Result_read )
332 else if( rc == Result_write )
339 m_state = State_idle ;
340 if( m_secure_connection_timeout != 0U )
341 m_secure_connection_timer.cancelTimer() ;
343 logSecure( m_peer_certificate ) ;
344 G_DEBUG(
"SocketProtocolImp::sslConnectImp: calling onSecure: " <<
G::Str::printable(m_peer_certificate) ) ;
345 m_sink.
onSecure( m_peer_certificate ) ;
349 void GNet::SocketProtocolImp::sslAccept()
351 G_DEBUG(
"SocketProtocolImp::sslAccept" ) ;
352 G_ASSERT( m_ssl ==
nullptr ) ;
354 m_ssl = newProtocol(
"server" ) ;
355 m_state = State_accepting ;
359 void GNet::SocketProtocolImp::sslAcceptImp()
361 G_DEBUG(
"SocketProtocolImp::sslAcceptImp" ) ;
362 G_ASSERT( m_ssl !=
nullptr ) ;
363 G_ASSERT( m_state == State_accepting ) ;
365 GSsl::Protocol::Result rc = m_ssl->
accept( m_socket ) ;
367 if( rc == Result_error )
370 m_state = State_raw ;
371 throw SocketProtocol::ReadError(
"ssl accept" ) ;
373 else if( rc == Result_read )
377 else if( rc == Result_write )
384 m_state = State_idle ;
386 logSecure( m_peer_certificate ) ;
387 G_DEBUG(
"SocketProtocolImp::sslAcceptImp: calling onSecure: " <<
G::Str::printable(m_peer_certificate) ) ;
388 m_sink.
onSecure( m_peer_certificate ) ;
392 bool GNet::SocketProtocolImp::sslEnabled()
const
394 return m_state == State_writing || m_state == State_idle ;
397 bool GNet::SocketProtocolImp::sslSendImp()
399 G_ASSERT( m_state == State_writing ) ;
402 GSsl::Protocol::ssize_type n = 0 ;
403 GSsl::Protocol::Result result = m_ssl->
write( m_ssl_send_data.data() , m_ssl_send_data.size() , n ) ;
404 if( result == Result_error )
407 m_state = State_idle ;
408 throw SocketProtocol::SendError(
"ssl write" ) ;
410 else if( result == Result_read )
414 else if( result == Result_write )
421 if( n < 0 )
throw SocketProtocol::SendError(
"ssl arithmetic underflow" ) ;
422 size_t un =
static_cast<size_t>(n) ;
423 rc = un == m_ssl_send_data.size() ;
424 m_ssl_send_data.erase( 0U , un ) ;
425 m_state = State_idle ;
430 void GNet::SocketProtocolImp::sslReadImp()
432 G_DEBUG(
"SocketProtocolImp::sslReadImp" ) ;
433 G_ASSERT( m_state == State_idle ) ;
434 G_ASSERT( m_ssl !=
nullptr ) ;
436 std::vector<char> more_data ;
437 GSsl::Protocol::Result rc = GSsl::Protocol::Result_more ;
438 for(
int sanity = 0 ; rc == Result_more && sanity < 1000 ; sanity++ )
440 rc = m_ssl->
read( &m_read_buffer[0] , m_read_buffer.size() , m_read_buffer_n ) ;
442 if( rc == Result_error )
445 m_state = State_idle ;
446 throw SocketProtocol::ReadError(
"ssl read" ) ;
448 else if( rc == Result_read )
452 else if( rc == Result_write )
458 G_ASSERT( rc == Result_ok || rc == Result_more ) ;
460 m_state = State_idle ;
461 size_t n =
static_cast<size_t>(m_read_buffer_n) ;
462 m_read_buffer_n = 0 ;
463 G_DEBUG(
"SocketProtocolImp::sslReadImp: calling onData(): " << n ) ;
464 m_sink.
onData( &m_read_buffer[0] , n ) ;
466 if( rc == Result_more )
467 G_DEBUG(
"SocketProtocolImp::sslReadImp: more available to read" ) ;
471 void GNet::SocketProtocolImp::rawReadEvent()
473 const size_t read_buffer_size =
G::Test::enabled(
"small-client-input-buffer") ? 3 : m_read_buffer.size() ;
474 const ssize_t rc = m_socket.
read( &m_read_buffer[0] , read_buffer_size ) ;
476 if( rc == 0 || ( rc == -1 && !m_socket.
eWouldBlock() ) )
478 throw SocketProtocol::ReadError() ;
482 G_ASSERT( static_cast<size_t>(rc) <= read_buffer_size ) ;
483 m_sink.
onData( &m_read_buffer[0] , static_cast<size_t>(rc) ) ;
487 G_DEBUG(
"GNet::SocketProtocolImp::rawReadEvent: read event read nothing" ) ;
492 bool GNet::SocketProtocolImp::finished(
const Segments & segments , Position pos )
494 return pos.segment == segments.size() ;
497 bool GNet::SocketProtocolImp::rawSend(
const Segments & segments , Position pos ,
bool do_copy )
499 G_ASSERT( !do_copy || segments.size() == 1U ) ;
501 if( !finished(m_segments,m_raw_pos) )
502 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
505 bool all_sent = rawSendImp( segments , pos , pos_out ) ;
506 if( !all_sent && failed() )
509 m_raw_pos = Position() ;
511 throw SocketProtocol::SendError() ;
516 m_raw_pos = Position() ;
522 m_segments = segments ;
523 m_raw_pos = pos_out ;
524 G_ASSERT( m_segments.empty() || m_segments[0].first != m_raw_copy.data() ) ;
530 G_ASSERT( segments.size() == 1U ) ;
531 G_ASSERT( pos_out.offset < segments[0].second ) ;
532 m_raw_copy.assign( segments[0].first+pos_out.offset , segments[0].second-pos_out.offset ) ;
533 m_segments[0].first = m_raw_copy.data() ;
534 m_segments[0].second = m_raw_copy.size() ;
535 m_raw_pos = Position() ;
539 logFlowControl(
"asserted" ) ;
544 bool GNet::SocketProtocolImp::rawWriteEvent()
547 logFlowControl(
"released" ) ;
549 bool all_sent = rawSendImp( m_segments , m_raw_pos , m_raw_pos ) ;
550 if( !all_sent && failed() )
553 m_raw_pos = Position() ;
555 throw SocketProtocol::SendError() ;
560 m_raw_pos = Position() ;
566 logFlowControl(
"reasserted" ) ;
571 bool GNet::SocketProtocolImp::rawSendImp(
const Segments & segments , Position pos , Position & pos_out )
573 while( !finished(segments,pos) )
575 const char * chunk_data = chunk_p( segments , pos ) ;
576 size_t chunk_size = chunk_n( segments , pos ) ;
578 ssize_t rc = m_socket.
write( chunk_data , chunk_size ) ;
583 pos_out = Position() ;
586 else if( rc < 0 || static_cast<size_t>(rc) < chunk_size )
589 size_t nsent = rc > 0 ?
static_cast<size_t>(rc) : 0U ;
590 pos_out = position( segments , pos , nsent ) ;
591 G_ASSERT( !finished(segments,pos_out) ) ;
596 pos = position( segments , pos , static_cast<size_t>(rc) ) ;
602 bool GNet::SocketProtocolImp::failed()
const
607 void GNet::SocketProtocolImp::logSecure(
const std::string & certificate )
const
609 std::pair<std::string,bool> rc( std::string() ,
false ) ;
613 logCertificate( rc.first , certificate ) ;
615 G_LOG(
"GNet::SocketProtocolImp: tls/ssl protocol established with "
617 << (rc.first.empty()?
"":
" certificate ") << rc.first ) ;
620 void GNet::SocketProtocolImp::logCertificate(
const std::string & certid ,
const std::string & certificate )
const
623 lines.reserve( 30U ) ;
625 for( G::StringArray::iterator line_p = lines.begin() ; line_p != lines.end() ; ++line_p )
627 if( !(*line_p).empty() )
629 G_LOG(
"GNet::SocketProtocolImp: certificate " << certid <<
": " << *line_p ) ;
634 void GNet::SocketProtocolImp::logFlowControl(
const char * what )
638 G_LOG(
"GNet::SocketProtocolImp::send: @" << m_socket.
asString() <<
": flow control " << what ) ;
641 std::string GNet::SocketProtocolImp::peerCertificate()
const
643 return m_peer_certificate ;
649 unsigned int secure_connection_timeout ) :
666 return m_imp->writeEvent() ;
671 return m_imp->send( data , offset ) ;
676 return m_imp->send( data ) ;
686 m_imp->sslConnect() ;
696 return m_imp->sslEnabled() ;
701 return m_imp->peerCertificate() ;
static Library * instance()
Returns a pointer to a library object, if any.
An interface to an underlying TLS library.
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
void readEvent()
Called on receipt of a read event.
virtual bool eWouldBlock()
Returns true if the previous socket operation failed because the socket would have blocked...
void addWriteHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives write events when flow c...
virtual void onData(const char *, size_t)=0
Called when data is read from the socket.
std::vector< std::string > StringArray
A std::vector of std::strings.
static std::string str(Result result)
Converts a result enumeration into a printable string.
virtual ~SocketProtocolSink()
Destructor.
virtual void onSecure(const std::string &peer_certificate)=0
Called once the secure socket protocol has been successfully negotiated.
void sslAccept()
Accepts the TLS/SSL protocol.
std::pair< std::string, bool > peerCertificate(int format=0)
Returns the peer certificate and a verified flag.
A pimple-pattern implementation class used by GNet::SocketProtocol.
void dropWriteHandler()
Reverses addWriteHandler().
std::pair< bool, Address > getPeerAddress() const
Retrieves address of socket's peer.
std::string peerCertificate() const
Returns the peer's TLS/SSL certificate or the empty string.
A derivation of GNet::Socket for a stream socket.
~SocketProtocol()
Destructor.
static bool enabled()
Returns true if test features are enabled.
Result accept(G::ReadWrite &io)
Starts the protocol passively (as a server).
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Result connect(G::ReadWrite &io)
Starts the protocol actively (as a client).
bool send(const std::string &data, size_t offset=0U)
Sends data.
const Profile & profile(const std::string &profile_name) const
Returns an opaque reference to the named profile.
SocketProtocol(EventHandler &, Sink &, StreamSocket &, unsigned int secure_connection_timeout)
Constructor. The references are kept.
A base class for classes that handle asynchronous events from the event loop.
virtual ssize_type read(char *buffer, size_type buffer_length)
Override from Socket::read().
A pointer into the scatter/gather payload of GNet::SocketProtocolImp::send().
virtual ssize_type write(const char *buf, size_type len)
Override from Socket::write().
static bool sslCapable()
Returns true if the implementation supports TLS/SSL.
void sslConnect()
Initiates the TLS/SSL protocol.
A general-purpose exception class derived from std::exception and containing a std::string.
Result read(char *buffer, size_type buffer_size_in, ssize_type &data_size_out)
Reads user data into the supplied buffer.
std::pair< std::string, bool > findCertificate(const std::string &certificate)
Returns a short id for the given certificate and a boolean flag to indicate if it is a new certificat...
A singleton class for initialising the underlying TLS library.
bool enabled() const
Returns true if this is a real TLS library and the constructor's active parameter was set...
Result write(const char *buffer, size_type data_size_in, ssize_type &data_size_out)
Writes user data.
A timer class template in which the timeout is delivered to the specified method. ...
to deliver data from a socket.
bool sslEnabled() const
Returns true if TLS/SSL is active.
bool writeEvent()
Called on receipt of a write event.
std::string asString() const
Returns the socket handle as a string.
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &seperators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.