44 enum State { State_raw , State_connecting , State_accepting , State_writing , State_idle } ;
49 unsigned int m_secure_connection_timeout ;
51 std::string m_raw_residue ;
52 std::string m_ssl_send_data ;
57 char m_read_buffer[c_buffer_size] ;
61 std::string m_peer_certificate ;
79 static void log(
int level ,
const std::string & line ) ;
83 bool rawWriteEvent() ;
87 void sslConnectImp() ;
89 void logSecure(
const std::string & )
const ;
90 void logCertificate(
const std::string & ,
const std::string & )
const ;
91 void logFlowControlReleased() ;
92 void logFlowControlAsserted() ;
93 void logFlowControlReasserted() ;
94 void onSecureConnectionTimeout() ;
103 m_secure_connection_timeout(secure_connection_timeout) ,
104 m_credentials(credentials) ,
109 m_read_buffer_size(sizeof(m_read_buffer)) ,
111 m_secure_connection_timer(*this,&
SocketProtocolImp::onSecureConnectionTimeout,handler)
120 void GNet::SocketProtocolImp::onSecureConnectionTimeout()
122 G_DEBUG(
"GNet::SocketProtocolImp::onSecureConnectionTimeout: timed out" ) ;
123 throw SocketProtocol::SecureConnectionTimeout() ;
128 G_DEBUG(
"SocketProtocolImp::readEvent: state=" << m_state ) ;
129 if( m_state == State_raw )
131 else if( m_state == State_connecting )
133 else if( m_state == State_accepting )
135 else if( m_state == State_writing )
143 G_DEBUG(
"GNet::SocketProtocolImp::writeEvent: state=" << m_state ) ;
145 if( m_state == State_raw )
146 rc = rawWriteEvent() ;
147 else if( m_state == State_connecting )
149 else if( m_state == State_accepting )
151 else if( m_state == State_idle )
160 if( data.empty() || offset >= data.length() )
164 if( m_state == State_raw )
166 rc = rawSend( data , offset ) ;
168 else if( m_state == State_connecting || m_state == State_accepting )
170 throw SocketProtocol::SendError(
"still busy negotiating" ) ;
172 else if( m_state == State_writing )
181 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
185 m_state = State_writing ;
186 m_ssl_send_data.append( data.substr(offset) ) ;
192 void GNet::SocketProtocolImp::log(
int level ,
const std::string & log_line )
195 G_DEBUG(
"ssl: " << log_line ) ;
196 else if( level == 1 )
197 G_DEBUG(
"SocketProtocolImp::log: " << log_line ) ;
199 G_WARNING(
"GNet::SocketProtocolImp::log: " << log_line ) ;
205 if( library == NULL )
206 throw G::Exception(
"SocketProtocolImp::newProtocol: internal error: no library instance" ) ;
213 G_DEBUG(
"SocketProtocolImp::sslConnect" ) ;
216 m_ssl = newProtocol() ;
217 m_state = State_connecting ;
218 if( m_secure_connection_timeout != 0U )
219 m_secure_connection_timer.startTimer( m_secure_connection_timeout ) ;
223 void GNet::SocketProtocolImp::sslConnectImp()
225 G_DEBUG(
"SocketProtocolImp::sslConnectImp" ) ;
227 G_ASSERT( m_state == State_connecting ) ;
232 m_socket.dropWriteHandler() ;
233 m_state = State_raw ;
234 throw SocketProtocol::ReadError(
"ssl connect" ) ;
238 m_socket.dropWriteHandler() ;
242 m_socket.addWriteHandler( m_handler ) ;
246 m_socket.dropWriteHandler() ;
247 m_state = State_idle ;
248 if( m_secure_connection_timeout != 0U )
249 m_secure_connection_timer.cancelTimer() ;
250 m_peer_certificate = m_ssl->peerCertificate().first ;
251 logSecure( m_peer_certificate ) ;
253 m_sink.onSecure( m_peer_certificate ) ;
259 G_DEBUG(
"SocketProtocolImp::sslAccept" ) ;
261 m_ssl = newProtocol() ;
262 m_state = State_accepting ;
266 void GNet::SocketProtocolImp::sslAcceptImp()
268 G_DEBUG(
"SocketProtocolImp::sslAcceptImp" ) ;
270 G_ASSERT( m_state == State_accepting ) ;
275 m_socket.dropWriteHandler() ;
276 m_state = State_raw ;
277 throw SocketProtocol::ReadError(
"ssl accept" ) ;
281 m_socket.dropWriteHandler() ;
285 m_socket.addWriteHandler( m_handler ) ;
289 m_socket.dropWriteHandler() ;
290 m_state = State_idle ;
291 m_peer_certificate = m_ssl->peerCertificate().first ;
292 logSecure( m_peer_certificate ) ;
294 m_sink.onSecure( m_peer_certificate ) ;
300 return m_state == State_writing || m_state == State_idle ;
303 bool GNet::SocketProtocolImp::sslSendImp()
305 G_ASSERT( m_state == State_writing ) ;
311 m_socket.dropWriteHandler() ;
312 m_state = State_idle ;
313 throw SocketProtocol::SendError(
"ssl write" ) ;
317 m_socket.dropWriteHandler() ;
321 m_socket.addWriteHandler( m_handler ) ;
325 m_socket.dropWriteHandler() ;
326 if( n < 0 )
throw SocketProtocol::SendError(
"ssl arithmetic underflow" ) ;
328 rc = un == m_ssl_send_data.size() ;
329 m_ssl_send_data.erase( 0U , un ) ;
330 m_state = State_idle ;
335 void GNet::SocketProtocolImp::sslReadImp()
337 G_DEBUG(
"SocketProtocolImp::sslReadImp" ) ;
340 for(
int sanity = 0 ; sanity < 1000 ; sanity++ )
346 m_socket.dropWriteHandler() ;
347 m_state = State_idle ;
348 throw SocketProtocol::ReadError(
"ssl read" ) ;
352 m_socket.dropWriteHandler() ;
356 m_socket.addWriteHandler( m_handler ) ;
360 m_socket.dropWriteHandler() ;
361 m_state = State_idle ;
363 m_read_buffer_n = 0 ;
364 G_DEBUG(
"SocketProtocolImp::sslReadImp: calling onData(): " << n ) ;
365 m_sink.onData( m_read_buffer , static_cast<std::string::size_type>(n) ) ;
368 G_DEBUG(
"SocketProtocolImp::sslReadImp: more available to read from the ssl layer without i/o" ) ;
374 void GNet::SocketProtocolImp::rawReadEvent()
376 char buffer[c_buffer_size] = {
'\0' } ;
377 const size_t buffer_size =
G::Test::enabled(
"small-client-input-buffer") ? 3 :
sizeof(buffer) ;
378 const ssize_t rc = m_socket.read( buffer , buffer_size ) ;
380 if( rc == 0 || ( rc == -1 && !m_socket.eWouldBlock() ) )
382 throw SocketProtocol::ReadError() ;
386 G_ASSERT( static_cast<size_t>(rc) <= buffer_size ) ;
387 m_sink.onData( buffer , static_cast<std::string::size_type>(rc) ) ;
397 bool all_sent = rawSendImp( data , offset , m_raw_residue ) ;
398 if( !all_sent && failed() )
399 throw SocketProtocol::SendError() ;
402 m_socket.addWriteHandler( m_handler ) ;
403 logFlowControlAsserted() ;
408 bool GNet::SocketProtocolImp::rawWriteEvent()
410 m_socket.dropWriteHandler() ;
411 logFlowControlReleased() ;
412 bool all_sent = rawSendImp( m_raw_residue , 0 , m_raw_residue ) ;
413 if( !all_sent && failed() )
414 throw SocketProtocol::SendError() ;
417 m_socket.addWriteHandler( m_handler ) ;
418 logFlowControlReasserted() ;
424 std::string & residue )
426 if( data.length() <= offset )
429 ssize_t rc = m_socket.write( data.data()+offset , data.length()-offset ) ;
430 if( rc < 0 && ! m_socket.eWouldBlock() )
437 else if( rc < 0 || static_cast<std::string::size_type>(rc) < (data.length()-offset) )
444 if( (sent+offset) != 0U )
445 residue.erase( 0U , sent+offset ) ;
452 m_n += data.length() ;
458 bool GNet::SocketProtocolImp::failed()
const
463 void GNet::SocketProtocolImp::logSecure(
const std::string & certificate )
const
465 std::pair<std::string,bool> rc( std::string() ,
false ) ;
469 logCertificate( rc.first , certificate ) ;
471 G_LOG(
"GNet::SocketProtocolImp: tls/ssl protocol established with "
472 << m_socket.getPeerAddress().second.displayString()
473 << (rc.first.empty()?
"":
" certificate ") << rc.first ) ;
476 void GNet::SocketProtocolImp::logCertificate(
const std::string & certid ,
const std::string & certificate )
const
480 for( G::Strings::iterator line_p = lines.begin() ; line_p != lines.end() ; ++line_p )
482 if( !(*line_p).empty() )
484 G_LOG(
"GNet::SocketProtocolImp: certificate " << certid <<
": " << *line_p ) ;
489 void GNet::SocketProtocolImp::logFlowControlAsserted()
493 G_LOG(
"GNet::SocketProtocolImp::send: @" << m_socket.asString() <<
": flow control asserted" ) ;
496 void GNet::SocketProtocolImp::logFlowControlReleased()
500 G_LOG(
"GNet::SocketProtocolImp::send: @" << m_socket.asString() <<
": flow control released" ) ;
503 void GNet::SocketProtocolImp::logFlowControlReasserted()
507 G_LOG(
"GNet::SocketProtocolImp::send: @" << m_socket.asString() <<
": flow control reasserted" ) ;
512 return m_peer_certificate ;
518 unsigned int secure_connection_timeout ) :
535 return m_imp->writeEvent() ;
540 return m_imp->send( data , offset ) ;
550 m_imp->sslConnect() ;
560 return m_imp->sslEnabled() ;
565 return m_imp->peerCertificate() ;
static Library * instance()
Returns a pointer to a library object, if any.
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.
std::list< std::string > Strings
A std::list of std::strings.
static void splitIntoFields(const std::string &in, Strings &out, const std::string &seperators, char escape= '\0', bool discard_bogus_escapes=true)
Splits the string into fields.
The Socket class encapsulates a non-blocking Unix socket file descriptor or a Windows 'SOCKET' handle...
std::string::size_type size_type
A std::size_t type.
SocketProtocolImp(EventHandler &, SocketProtocol::Sink &, StreamSocket &, unsigned int secure_connection_timeout, const Socket::Credentials &)
static std::string str(Result result)
Converts a result enumeration into a printable string.
virtual ~SocketProtocolSink()
Destructor.
void sslAccept()
Accepts the TLS/SSL protocol.
A private implementation class used by GNet::SocketProtocol.
std::string peerCertificate() const
Returns the peer's TLS/SSL certificate or the empty string.
A derivation of Socket for a stream socket.
~SocketProtocol()
Destructor.
static bool enabled()
Returns true if test features are enabled.
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
SocketProtocol(EventHandler &, Sink &, StreamSocket &, unsigned int secure_connection_timeout)
Constructor. The references are kept.
bool send(const std::string &data, std::string::size_type offset)
A base class for classes that handle asynchronous socket events.
std::string peerCertificate() const
bool send(const std::string &data, std::string::size_type offset=0U)
Sends data.
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.
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 RAII class for initialising the underlying ssl library.
bool enabled(bool for_serving=false) const
Returns true if this is a real and enabled ssl library.
A credentials class that allows SocketProtocol to call Socket::fd().
A timer class template in which the timeout is delivered to the specified method. ...
An interface used by GNet::SocketProtocol 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.