gsocketprotocol.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 // gsocketprotocol.cpp
19 //
20 
21 #include "gdef.h"
22 #include "glimits.h"
23 #include "gnet.h"
24 #include "gmonitor.h"
25 #include "gtimer.h"
26 #include "gssl.h"
27 #include "gsocketprotocol.h"
28 #include "gstr.h"
29 #include "gtest.h"
30 #include "gassert.h"
31 #include "glog.h"
32 
33 namespace
34 {
35  const size_t c_buffer_size = G::limits::net_buffer ;
36 }
37 
42 {
43 private:
44  enum State { State_raw , State_connecting , State_accepting , State_writing , State_idle } ;
45 
46  EventHandler & m_handler ;
47  SocketProtocol::Sink & m_sink ;
48  StreamSocket & m_socket ;
49  unsigned int m_secure_connection_timeout ;
50  const Socket::Credentials & m_credentials ;
51  std::string m_raw_residue ;
52  std::string m_ssl_send_data ;
53  bool m_failed ;
54  unsigned long m_n ;
55  GSsl::Protocol * m_ssl ;
56  State m_state ;
57  char m_read_buffer[c_buffer_size] ;
58  GSsl::Protocol::size_type m_read_buffer_size ;
59  GSsl::Protocol::ssize_type m_read_buffer_n ;
60  Timer<SocketProtocolImp> m_secure_connection_timer ;
61  std::string m_peer_certificate ;
62 
63 public:
65  unsigned int secure_connection_timeout , const Socket::Credentials & ) ;
67  void readEvent() ;
68  bool writeEvent() ;
69  bool send( const std::string & data , std::string::size_type offset ) ;
70  void sslConnect() ;
71  void sslAccept() ;
72  bool sslEnabled() const ;
73  std::string peerCertificate() const ;
74 
75 private:
77  void operator=( const SocketProtocolImp & ) ;
78  static GSsl::Protocol * newProtocol() ;
79  static void log( int level , const std::string & line ) ;
80  bool failed() const ;
81  bool rawSendImp( const std::string & , std::string::size_type , std::string & ) ;
82  void rawReadEvent() ;
83  bool rawWriteEvent() ;
84  bool rawSend( const std::string & data , std::string::size_type offset ) ;
85  void sslReadImp() ;
86  bool sslSendImp() ;
87  void sslConnectImp() ;
88  void sslAcceptImp() ;
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() ;
95 } ;
96 
98  SocketProtocol::Sink & sink , StreamSocket & socket ,
99  unsigned int secure_connection_timeout , const Socket::Credentials & credentials ) :
100  m_handler(handler) ,
101  m_sink(sink) ,
102  m_socket(socket) ,
103  m_secure_connection_timeout(secure_connection_timeout) ,
104  m_credentials(credentials) ,
105  m_failed(false) ,
106  m_n(0UL) ,
107  m_ssl(NULL) ,
108  m_state(State_raw) ,
109  m_read_buffer_size(sizeof(m_read_buffer)) ,
110  m_read_buffer_n(0) ,
111  m_secure_connection_timer(*this,&SocketProtocolImp::onSecureConnectionTimeout,handler)
112 {
113 }
114 
116 {
117  delete m_ssl ;
118 }
119 
120 void GNet::SocketProtocolImp::onSecureConnectionTimeout()
121 {
122  G_DEBUG( "GNet::SocketProtocolImp::onSecureConnectionTimeout: timed out" ) ;
123  throw SocketProtocol::SecureConnectionTimeout() ;
124 }
125 
127 {
128  G_DEBUG( "SocketProtocolImp::readEvent: state=" << m_state ) ;
129  if( m_state == State_raw )
130  rawReadEvent() ;
131  else if( m_state == State_connecting )
132  sslConnectImp() ;
133  else if( m_state == State_accepting )
134  sslAcceptImp() ;
135  else if( m_state == State_writing )
136  sslSendImp() ;
137  else // State_idle
138  sslReadImp() ;
139 }
140 
142 {
143  G_DEBUG( "GNet::SocketProtocolImp::writeEvent: state=" << m_state ) ;
144  bool rc = true ;
145  if( m_state == State_raw )
146  rc = rawWriteEvent() ;
147  else if( m_state == State_connecting )
148  sslConnectImp() ;
149  else if( m_state == State_accepting )
150  sslAcceptImp() ;
151  else if( m_state == State_idle )
152  sslReadImp() ;
153  else
154  sslSendImp() ;
155  return rc ;
156 }
157 
158 bool GNet::SocketProtocolImp::send( const std::string & data , std::string::size_type offset )
159 {
160  if( data.empty() || offset >= data.length() )
161  return true ;
162 
163  bool rc = true ;
164  if( m_state == State_raw )
165  {
166  rc = rawSend( data , offset ) ;
167  }
168  else if( m_state == State_connecting || m_state == State_accepting )
169  {
170  throw SocketProtocol::SendError( "still busy negotiating" ) ;
171  }
172  else if( m_state == State_writing )
173  {
174  // throw here rather than add to the pending buffer because openssl
175  // requires that the parameters stay the same -- we could use double
176  // buffering, with a buffer switch and a call to sslSendImp()
177  // rather than returning to the idle state, but in practice
178  // we rely on the client code taking account of the return value
179  // from send() and waiting for onSendComplete() when required
180  //
181  throw SocketProtocol::SendError( "still busy sending the last packet" ) ;
182  }
183  else
184  {
185  m_state = State_writing ;
186  m_ssl_send_data.append( data.substr(offset) ) ;
187  rc = sslSendImp() ;
188  }
189  return rc ;
190 }
191 
192 void GNet::SocketProtocolImp::log( int level , const std::string & log_line )
193 {
194  if( level == 0 )
195  G_DEBUG( "ssl: " << log_line ) ;
196  else if( level == 1 )
197  G_DEBUG( "SocketProtocolImp::log: " << log_line ) ;
198  else
199  G_WARNING( "GNet::SocketProtocolImp::log: " << log_line ) ;
200 }
201 
202 GSsl::Protocol * GNet::SocketProtocolImp::newProtocol()
203 {
204  GSsl::Library * library = GSsl::Library::instance() ;
205  if( library == NULL )
206  throw G::Exception( "SocketProtocolImp::newProtocol: internal error: no library instance" ) ;
207 
208  return new GSsl::Protocol( *library , log ) ;
209 }
210 
212 {
213  G_DEBUG( "SocketProtocolImp::sslConnect" ) ;
214  G_ASSERT( m_ssl == NULL ) ;
215 
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 ) ;
220  sslConnectImp() ;
221 }
222 
223 void GNet::SocketProtocolImp::sslConnectImp()
224 {
225  G_DEBUG( "SocketProtocolImp::sslConnectImp" ) ;
226  G_ASSERT( m_ssl != NULL ) ;
227  G_ASSERT( m_state == State_connecting ) ;
228  GSsl::Protocol::Result rc = m_ssl->connect( m_socket.fd(m_credentials) ) ;
229  G_DEBUG( "SocketProtocolImp::sslConnectImp: result=" << GSsl::Protocol::str(rc) ) ;
230  if( rc == GSsl::Protocol::Result_error )
231  {
232  m_socket.dropWriteHandler() ;
233  m_state = State_raw ;
234  throw SocketProtocol::ReadError( "ssl connect" ) ;
235  }
236  else if( rc == GSsl::Protocol::Result_read )
237  {
238  m_socket.dropWriteHandler() ;
239  }
240  else if( rc == GSsl::Protocol::Result_write )
241  {
242  m_socket.addWriteHandler( m_handler ) ;
243  }
244  else
245  {
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 ) ;
252  G_DEBUG( "SocketProtocolImp::sslConnectImp: calling onSecure: " << G::Str::printable(m_peer_certificate) ) ;
253  m_sink.onSecure( m_peer_certificate ) ;
254  }
255 }
256 
258 {
259  G_DEBUG( "SocketProtocolImp::sslAccept" ) ;
260  G_ASSERT( m_ssl == NULL ) ;
261  m_ssl = newProtocol() ;
262  m_state = State_accepting ;
263  sslAcceptImp() ;
264 }
265 
266 void GNet::SocketProtocolImp::sslAcceptImp()
267 {
268  G_DEBUG( "SocketProtocolImp::sslAcceptImp" ) ;
269  G_ASSERT( m_ssl != NULL ) ;
270  G_ASSERT( m_state == State_accepting ) ;
271  GSsl::Protocol::Result rc = m_ssl->accept( m_socket.fd(m_credentials) ) ;
272  G_DEBUG( "SocketProtocolImp::sslAcceptImp: result=" << GSsl::Protocol::str(rc) ) ;
273  if( rc == GSsl::Protocol::Result_error )
274  {
275  m_socket.dropWriteHandler() ;
276  m_state = State_raw ;
277  throw SocketProtocol::ReadError( "ssl accept" ) ;
278  }
279  else if( rc == GSsl::Protocol::Result_read )
280  {
281  m_socket.dropWriteHandler() ;
282  }
283  else if( rc == GSsl::Protocol::Result_write )
284  {
285  m_socket.addWriteHandler( m_handler ) ;
286  }
287  else
288  {
289  m_socket.dropWriteHandler() ;
290  m_state = State_idle ;
291  m_peer_certificate = m_ssl->peerCertificate().first ;
292  logSecure( m_peer_certificate ) ;
293  G_DEBUG( "SocketProtocolImp::sslAcceptImp: calling onSecure: " << G::Str::printable(m_peer_certificate) ) ;
294  m_sink.onSecure( m_peer_certificate ) ;
295  }
296 }
297 
299 {
300  return m_state == State_writing || m_state == State_idle ;
301 }
302 
303 bool GNet::SocketProtocolImp::sslSendImp()
304 {
305  G_ASSERT( m_state == State_writing ) ;
306  bool rc = false ;
308  GSsl::Protocol::Result result = m_ssl->write( m_ssl_send_data.data() , m_ssl_send_data.size() , n ) ;
309  if( result == GSsl::Protocol::Result_error )
310  {
311  m_socket.dropWriteHandler() ;
312  m_state = State_idle ;
313  throw SocketProtocol::SendError( "ssl write" ) ;
314  }
315  else if( result == GSsl::Protocol::Result_read )
316  {
317  m_socket.dropWriteHandler() ;
318  }
319  else if( result == GSsl::Protocol::Result_write )
320  {
321  m_socket.addWriteHandler( m_handler ) ;
322  }
323  else
324  {
325  m_socket.dropWriteHandler() ;
326  if( n < 0 ) throw SocketProtocol::SendError( "ssl arithmetic underflow" ) ;
327  std::string::size_type un = static_cast<std::string::size_type>(n) ;
328  rc = un == m_ssl_send_data.size() ;
329  m_ssl_send_data.erase( 0U , un ) ;
330  m_state = State_idle ;
331  }
332  return rc ;
333 }
334 
335 void GNet::SocketProtocolImp::sslReadImp()
336 {
337  G_DEBUG( "SocketProtocolImp::sslReadImp" ) ;
338  G_ASSERT( m_state == State_idle ) ;
339  G_ASSERT( m_ssl != NULL ) ;
340  for( int sanity = 0 ; sanity < 1000 ; sanity++ )
341  {
342  GSsl::Protocol::Result rc = m_ssl->read( m_read_buffer , m_read_buffer_size , m_read_buffer_n ) ;
343  G_DEBUG( "SocketProtocolImp::sslReadImp: result=" << GSsl::Protocol::str(rc) ) ;
344  if( rc == GSsl::Protocol::Result_error )
345  {
346  m_socket.dropWriteHandler() ;
347  m_state = State_idle ;
348  throw SocketProtocol::ReadError( "ssl read" ) ;
349  }
350  else if( rc == GSsl::Protocol::Result_read )
351  {
352  m_socket.dropWriteHandler() ;
353  }
354  else if( rc == GSsl::Protocol::Result_write )
355  {
356  m_socket.addWriteHandler( m_handler ) ;
357  }
358  else // Result_ok, Result_more
359  {
360  m_socket.dropWriteHandler() ;
361  m_state = State_idle ;
362  GSsl::Protocol::ssize_type n = m_read_buffer_n ;
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) ) ;
366  }
367  if( rc == GSsl::Protocol::Result_more )
368  G_DEBUG( "SocketProtocolImp::sslReadImp: more available to read from the ssl layer without i/o" ) ;
369  else
370  break ;
371  }
372 }
373 
374 void GNet::SocketProtocolImp::rawReadEvent()
375 {
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 ) ;
379 
380  if( rc == 0 || ( rc == -1 && !m_socket.eWouldBlock() ) )
381  {
382  throw SocketProtocol::ReadError() ;
383  }
384  else if( rc != -1 )
385  {
386  G_ASSERT( static_cast<size_t>(rc) <= buffer_size ) ;
387  m_sink.onData( buffer , static_cast<std::string::size_type>(rc) ) ;
388  }
389  else
390  {
391  ; // -1 && eWouldBlock() -- no-op (esp. for windows)
392  }
393 }
394 
395 bool GNet::SocketProtocolImp::rawSend( const std::string & data , std::string::size_type offset )
396 {
397  bool all_sent = rawSendImp( data , offset , m_raw_residue ) ;
398  if( !all_sent && failed() )
399  throw SocketProtocol::SendError() ;
400  if( !all_sent )
401  {
402  m_socket.addWriteHandler( m_handler ) ;
403  logFlowControlAsserted() ;
404  }
405  return all_sent ;
406 }
407 
408 bool GNet::SocketProtocolImp::rawWriteEvent()
409 {
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() ;
415  if( !all_sent )
416  {
417  m_socket.addWriteHandler( m_handler ) ;
418  logFlowControlReasserted() ;
419  }
420  return all_sent ;
421 }
422 
423 bool GNet::SocketProtocolImp::rawSendImp( const std::string & data , std::string::size_type offset ,
424  std::string & residue )
425 {
426  if( data.length() <= offset )
427  return true ; // nothing to do
428 
429  ssize_t rc = m_socket.write( data.data()+offset , data.length()-offset ) ;
430  if( rc < 0 && ! m_socket.eWouldBlock() )
431  {
432  // fatal error, eg. disconnection
433  m_failed = true ;
434  residue.erase() ;
435  return false ;
436  }
437  else if( rc < 0 || static_cast<std::string::size_type>(rc) < (data.length()-offset) )
438  {
439  // flow control asserted
440  std::string::size_type sent = rc > 0 ? static_cast<size_t>(rc) : 0U ;
441  m_n += sent ;
442 
443  residue = data ;
444  if( (sent+offset) != 0U )
445  residue.erase( 0U , sent+offset ) ;
446 
447  return false ;
448  }
449  else
450  {
451  // all sent
452  m_n += data.length() ;
453  residue.erase() ;
454  return true ;
455  }
456 }
457 
458 bool GNet::SocketProtocolImp::failed() const
459 {
460  return m_failed ;
461 }
462 
463 void GNet::SocketProtocolImp::logSecure( const std::string & certificate ) const
464 {
465  std::pair<std::string,bool> rc( std::string() , false ) ;
467  rc = GNet::Monitor::instance()->findCertificate( certificate ) ;
468  if( rc.second ) // is new
469  logCertificate( rc.first , certificate ) ;
470 
471  G_LOG( "GNet::SocketProtocolImp: tls/ssl protocol established with "
472  << m_socket.getPeerAddress().second.displayString()
473  << (rc.first.empty()?"":" certificate ") << rc.first ) ;
474 }
475 
476 void GNet::SocketProtocolImp::logCertificate( const std::string & certid , const std::string & certificate ) const
477 {
478  G::Strings lines ;
479  G::Str::splitIntoFields( certificate , lines , "\n" ) ;
480  for( G::Strings::iterator line_p = lines.begin() ; line_p != lines.end() ; ++line_p )
481  {
482  if( !(*line_p).empty() )
483  {
484  G_LOG( "GNet::SocketProtocolImp: certificate " << certid << ": " << *line_p ) ;
485  }
486  }
487 }
488 
489 void GNet::SocketProtocolImp::logFlowControlAsserted()
490 {
491  const bool log = G::Test::enabled("log-flow-control") ;
492  if( log )
493  G_LOG( "GNet::SocketProtocolImp::send: @" << m_socket.asString() << ": flow control asserted" ) ;
494 }
495 
496 void GNet::SocketProtocolImp::logFlowControlReleased()
497 {
498  const bool log = G::Test::enabled("log-flow-control") ;
499  if( log )
500  G_LOG( "GNet::SocketProtocolImp::send: @" << m_socket.asString() << ": flow control released" ) ;
501 }
502 
503 void GNet::SocketProtocolImp::logFlowControlReasserted()
504 {
505  const bool log = G::Test::enabled("log-flow-control") ;
506  if( log )
507  G_LOG( "GNet::SocketProtocolImp::send: @" << m_socket.asString() << ": flow control reasserted" ) ;
508 }
509 
511 {
512  return m_peer_certificate ;
513 }
514 
515 //
516 
518  unsigned int secure_connection_timeout ) :
519  m_imp( new SocketProtocolImp(handler,sink,socket,secure_connection_timeout,Socket::Credentials("")) )
520 {
521 }
522 
524 {
525  delete m_imp ;
526 }
527 
529 {
530  m_imp->readEvent() ;
531 }
532 
534 {
535  return m_imp->writeEvent() ;
536 }
537 
538 bool GNet::SocketProtocol::send( const std::string & data , std::string::size_type offset )
539 {
540  return m_imp->send( data , offset ) ;
541 }
542 
544 {
545  return GSsl::Library::instance() != NULL && GSsl::Library::instance()->enabled() ;
546 }
547 
549 {
550  m_imp->sslConnect() ;
551 }
552 
554 {
555  m_imp->sslAccept() ;
556 }
557 
559 {
560  return m_imp->sslEnabled() ;
561 }
562 
564 {
565  return m_imp->peerCertificate() ;
566 }
567 
568 //
569 
571 {
572 }
573 
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.
Definition: gstr.cpp:507
void readEvent()
Called on receipt of a read event.
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
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.
Definition: gstr.cpp:765
An SSL protocol class.
Definition: gssl.h:58
The Socket class encapsulates a non-blocking Unix socket file descriptor or a Windows 'SOCKET' handle...
Definition: gsocket.h:60
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
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.
Definition: gsocket.h:270
#define G_ASSERT(test)
Definition: gassert.h:30
~SocketProtocol()
Destructor.
size_t size_type
Definition: gssl.h:61
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:46
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:105
#define G_LOG(expr)
Definition: glog.h:98
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.
Definition: geventhandler.h:54
std::string peerCertificate() const
#define G_DEBUG(expr)
Definition: glog.h:95
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.
Definition: gexception.h:44
ssize_t ssize_type
Definition: gssl.h:62
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...
Definition: gmonitor.cpp:140
A RAII class for initialising the underlying ssl library.
Definition: gssl.h:147
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().
Definition: gsocket.h:66
A timer class template in which the timeout is delivered to the specified method. ...
Definition: gtimer.h:103
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.
#define G_WARNING(expr)
Definition: glog.h:107