E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gsocketprotocol.cpp
1 //
2 // Copyright (C) 2001-2018 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 "gmonitor.h"
24 #include "gtest.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 int Result_ok = GSsl::Protocol::Result_ok ;
36 const int Result_more = GSsl::Protocol::Result_more ;
37 const int Result_read = GSsl::Protocol::Result_read ;
38 const int Result_write = GSsl::Protocol::Result_write ;
39 const int Result_error = GSsl::Protocol::Result_error ;
40 }
41
42 /// \class GNet::SocketProtocolImp
43 /// A pimple-pattern implementation class used by GNet::SocketProtocol.
44 ///
45 class GNet::SocketProtocolImp
46 {
47 public:
48 typedef std::pair<const char *,size_t> Segment ;
49 typedef std::vector<Segment> Segments ;
50 struct Position /// A pointer into the scatter/gather payload of GNet::SocketProtocolImp::send().
51 {
52 size_t segment ;
53 size_t offset ;
54 Position( size_t segment_ , size_t offset_ ) : segment(segment_) , offset(offset_) {}
55 Position() : segment(0U) , offset(0U) {}
56 } ;
57
58 SocketProtocolImp( EventHandler & , ExceptionHandler & , SocketProtocol::Sink & ,
59 StreamSocket & , unsigned int secure_connection_timeout ) ;
60 ~SocketProtocolImp() ;
61 void readEvent() ;
62 bool writeEvent() ;
63 void otherEvent( EventHandler::Reason ) ;
64 bool send( const std::string & data , size_t offset ) ;
65 bool send( const Segments & ) ;
66 void secureConnect() ;
67 void secureAccept() ;
68 std::string peerCertificate() const ;
69 static void setReadBufferSize( size_t ) ;
70
71 private:
72 SocketProtocolImp( const SocketProtocolImp & ) ;
73 void operator=( const SocketProtocolImp & ) ;
74 static GSsl::Protocol * newProtocol( const std::string & ) ;
75 static void log( int level , const std::string & line ) ;
76 bool failed() const ;
77 void rawReadEvent() ;
78 bool rawWriteEvent() ;
79 void rawOtherEvent() ;
80 bool rawSend( const Segments & , Position , bool = false ) ;
81 bool rawSendImp( const Segments & , Position , Position & ) ;
82 void sslReadImp() ;
83 bool sslSend( const Segments & segments , Position pos ) ;
84 bool sslSendImp() ;
85 bool sslSendImp( const Segments & segments , Position pos , Position & ) ;
86 void secureConnectImp() ;
87 void secureAcceptImp() ;
88 void logSecure( const std::string & ) const ;
89 void logCertificate( const std::string & , const std::string & ) const ;
90 void logFlowControl( const char * what ) ;
91 void onSecureConnectionTimeout() ;
92 static size_t size( const Segments & ) ;
93 static bool finished( const Segments & , Position ) ;
94 static Position newPosition( const Segments & , Position , size_t ) ;
95 static const char * chunk_p( const Segments & , Position ) ;
96 static size_t chunk_n( const Segments & , Position ) ;
97
98 private:
99 enum State { State_raw , State_connecting , State_accepting , State_writing , State_idle } ;
100 EventHandler & m_handler ;
101 ExceptionHandler & m_eh ;
102 SocketProtocol::Sink & m_sink ;
103 StreamSocket & m_socket ;
104 unsigned int m_secure_connection_timeout ;
105 Segments m_segments ;
106 Position m_position ;
107 std::string m_data_copy ;
108 bool m_failed ;
109 GSsl::Protocol * m_ssl ;
110 State m_state ;
111 std::vector<char> m_read_buffer ;
112 ssize_t m_read_buffer_n ;
113 Timer<SocketProtocolImp> m_secure_connection_timer ;
114 std::string m_peer_certificate ;
115 static size_t m_read_buffer_size ;
116 } ;
117
118 namespace GNet
119 {
120 std::ostream & operator<<( std::ostream & stream , const SocketProtocolImp::Position & pos )
121 {
122 return stream << "(" << pos.segment << "," << pos.offset << ")" ;
123 }
124 std::ostream & operator<<( std::ostream & stream , const SocketProtocolImp::Segment & segment )
125 {
126 return stream << "(" << (const void*)(segment.first) << ":" << segment.second << ")" ;
127 }
128 std::ostream & operator<<( std::ostream & stream , const SocketProtocolImp::Segments & segments )
129 {
130 stream << "[" ;
131 const char * sep = "" ;
132 for( size_t i = 0U ; i < segments.size() ; i++ , sep = "," )
133 stream << sep << segments.at(i) ;
134 stream << "]" ;
135 return stream ;
136 }
137 }
138
139 size_t GNet::SocketProtocolImp::m_read_buffer_size = G::limits::net_buffer ;
140
141 GNet::SocketProtocolImp::SocketProtocolImp( EventHandler & handler , ExceptionHandler & eh ,
142 SocketProtocol::Sink & sink , StreamSocket & socket ,
143 unsigned int secure_connection_timeout ) :
144 m_handler(handler) ,
145 m_eh(eh) ,
146 m_sink(sink) ,
147 m_socket(socket) ,
148 m_secure_connection_timeout(secure_connection_timeout) ,
149 m_failed(false) ,
150 m_ssl(nullptr) ,
151 m_state(State_raw) ,
152 m_read_buffer(m_read_buffer_size) ,
153 m_read_buffer_n(0) ,
154 m_secure_connection_timer(*this,&SocketProtocolImp::onSecureConnectionTimeout,eh)
155 {
156 G_ASSERT( m_read_buffer.size() == m_read_buffer_size ) ;
157 }
158
159 GNet::SocketProtocolImp::~SocketProtocolImp()
160 {
161 delete m_ssl ;
162 }
163
164 void GNet::SocketProtocolImp::setReadBufferSize( size_t n )
165 {
166 m_read_buffer_size = n ;
167 if( m_read_buffer_size == 0U )
168 m_read_buffer_size = 1U ;
169 }
170
171 void GNet::SocketProtocolImp::onSecureConnectionTimeout()
172 {
173 G_DEBUG( "GNet::SocketProtocolImp::onSecureConnectionTimeout: timed out" ) ;
174 throw SocketProtocol::SecureConnectionTimeout() ;
175 }
176
177 void GNet::SocketProtocolImp::readEvent()
178 {
179 G_DEBUG( "SocketProtocolImp::readEvent: read event: " << m_socket.asString() << ": state=" << m_state ) ;
180 if( m_state == State_raw )
181 rawReadEvent() ;
182 else if( m_state == State_connecting )
183 secureConnectImp() ;
184 else if( m_state == State_accepting )
185 secureAcceptImp() ;
186 else if( m_state == State_writing )
187 sslSendImp() ;
188 else // State_idle
189 sslReadImp() ;
190 }
191
192 bool GNet::SocketProtocolImp::writeEvent()
193 {
194 G_DEBUG( "GNet::SocketProtocolImp::writeEvent: write event: " << m_socket.asString() << ": state=" << m_state ) ;
195 bool rc = false ; // was true
196 if( m_state == State_raw )
197 rc = rawWriteEvent() ;
198 else if( m_state == State_connecting )
199 secureConnectImp() ;
200 else if( m_state == State_accepting )
201 secureAcceptImp() ;
202 else if( m_state == State_writing )
203 rc = sslSendImp() ;
204 else // State_idle
205 sslReadImp() ;
206 return rc ;
207 }
208
209 void GNet::SocketProtocolImp::otherEvent( EventHandler::Reason reason )
210 {
211 if( m_state == State_raw && reason == EventHandler::reason_closed && !G::Test::enabled("socket-protocol-noflush") )
212 rawOtherEvent() ;
213 else
214 throw G::Exception( "socket disconnect event" , EventHandler::str(reason) ) ;
215 }
216
217 size_t GNet::SocketProtocolImp::size( const Segments & segments )
218 {
219 size_t n = 0U ;
220 for( Segments::const_iterator p = segments.begin() ; p != segments.end() ; ++p )
221 {
222 G_ASSERT( (*p).first != nullptr ) ;
223 G_ASSERT( (*p).second != 0U ) ; // for chunk_p()
224 n += (*p).second ;
225 }
226 return n ;
227 }
228
229 GNet::SocketProtocolImp::Position GNet::SocketProtocolImp::newPosition( const Segments & s , Position pos , size_t offset )
230 {
231 G_ASSERT( pos.segment < s.size() ) ;
232 G_ASSERT( (pos.offset+offset) <= s.at(pos.segment).second ) ; // because chunk_p()
233 pos.offset += offset ;
234 if( pos.offset >= s.at(pos.segment).second ) // in practice if==
235 {
236 pos.segment++ ;
237 pos.offset = 0U ;
238 }
239 return pos ;
240 }
241
242 const char * GNet::SocketProtocolImp::chunk_p( const Segments & s , Position pos )
243 {
244 G_ASSERT( pos.segment < s.size() ) ;
245 G_ASSERT( pos.offset < s[pos.segment].second ) ;
246
247 const Segment & segment = s.at( pos.segment ) ;
248 return segment.first + pos.offset ;
249 }
250
251 size_t GNet::SocketProtocolImp::chunk_n( const Segments & s , Position pos )
252 {
253 G_ASSERT( pos.segment < s.size() ) ;
254 G_ASSERT( pos.offset < s[pos.segment].second ) ;
255
256 const Segment & segment = s.at( pos.segment ) ;
257 return segment.second - pos.offset ;
258 }
259
260 bool GNet::SocketProtocolImp::send( const std::string & data , size_t offset )
261 {
262 if( data.empty() || offset >= data.length() )
263 return true ;
264
265 bool rc = true ;
266 if( m_state == State_raw )
267 {
268 Segments segments( 1U ) ;
269 segments[0].first = data.data() ;
270 segments[0].second = data.size() ;
271 rc = rawSend( segments , Position(0U,offset) , true/*copy*/ ) ;
272 }
273 else if( m_state == State_connecting || m_state == State_accepting )
274 {
275 throw SocketProtocol::SendError( "still busy negotiating" ) ;
276 }
277 else if( m_state == State_writing )
278 {
279 throw SocketProtocol::SendError( "still busy sending the last packet" ) ;
280 }
281 else
282 {
283 // make a copy here because we have to do retries with the
284 // same pointer -- prefer the Segments overload
285 m_data_copy = data.substr( offset ) ;
286
287 Segments segments( 1U ) ;
288 segments[0].first = m_data_copy.data() ;
289 segments[0].second = m_data_copy.size() ;
290 rc = sslSend( segments , Position() ) ;
291 }
292 return rc ;
293 }
294
295 bool GNet::SocketProtocolImp::send( const Segments & segments )
296 {
297 if( segments.empty() || size(segments) == 0U )
298 return true ;
299
300 bool rc = true ;
301 if( m_state == State_raw )
302 {
303 rc = rawSend( segments , Position() ) ;
304 }
305 else if( m_state == State_connecting || m_state == State_accepting )
306 {
307 throw SocketProtocol::SendError( "still busy negotiating" ) ;
308 }
309 else if( m_state == State_writing )
310 {
311 throw SocketProtocol::SendError( "still busy sending the last packet" ) ;
312 }
313 else
314 {
315 rc = sslSend( segments , Position() ) ;
316 }
317 return rc ;
318 }
319
320 void GNet::SocketProtocolImp::secureConnect()
321 {
322 G_DEBUG( "SocketProtocolImp::secureConnect" ) ;
323 G_ASSERT( m_ssl == nullptr ) ;
324
325 m_ssl = newProtocol( "client" ) ;
326 m_state = State_connecting ;
327 if( m_secure_connection_timeout != 0U )
328 m_secure_connection_timer.startTimer( m_secure_connection_timeout ) ;
329 secureConnectImp() ;
330 }
331
332 void GNet::SocketProtocolImp::secureConnectImp()
333 {
334 G_DEBUG( "SocketProtocolImp::secureConnectImp" ) ;
335 G_ASSERT( m_ssl != nullptr ) ;
336 G_ASSERT( m_state == State_connecting ) ;
337
338 GSsl::Protocol::Result rc = m_ssl->connect( m_socket ) ;
339 G_DEBUG( "SocketProtocolImp::secureConnectImp: result=" << GSsl::Protocol::str(rc) ) ;
340 if( rc == Result_error )
341 {
342 m_socket.dropWriteHandler() ;
343 m_state = State_raw ;
344 throw SocketProtocol::ReadError( "ssl connect" ) ;
345 }
346 else if( rc == Result_read )
347 {
348 m_socket.dropWriteHandler() ;
349 }
350 else if( rc == Result_write )
351 {
352 m_socket.addWriteHandler( m_handler , m_eh ) ;
353 }
354 else
355 {
356 m_socket.dropWriteHandler() ;
357 m_state = State_idle ;
358 if( m_secure_connection_timeout != 0U )
359 m_secure_connection_timer.cancelTimer() ;
360 m_peer_certificate = m_ssl->peerCertificate() ;
361 logSecure( m_peer_certificate ) ;
362 G_DEBUG( "SocketProtocolImp::secureConnectImp: calling onSecure: " << G::Str::printable(m_peer_certificate) ) ;
363 m_sink.onSecure( m_peer_certificate ) ;
364 }
365 }
366
367 void GNet::SocketProtocolImp::secureAccept()
368 {
369 G_DEBUG( "SocketProtocolImp::secureAccept" ) ;
370 G_ASSERT( m_ssl == nullptr ) ;
371
372 m_ssl = newProtocol( "server" ) ;
373 m_state = State_accepting ;
374 secureAcceptImp() ;
375 }
376
377 void GNet::SocketProtocolImp::secureAcceptImp()
378 {
379 G_DEBUG( "SocketProtocolImp::secureAcceptImp" ) ;
380 G_ASSERT( m_ssl != nullptr ) ;
381 G_ASSERT( m_state == State_accepting ) ;
382
383 GSsl::Protocol::Result rc = m_ssl->accept( m_socket ) ;
384 G_DEBUG( "SocketProtocolImp::secureAcceptImp: result=" << GSsl::Protocol::str(rc) ) ;
385 if( rc == Result_error )
386 {
387 m_socket.dropWriteHandler() ;
388 m_state = State_raw ;
389 throw SocketProtocol::ReadError( "ssl accept" ) ;
390 }
391 else if( rc == Result_read )
392 {
393 m_socket.dropWriteHandler() ;
394 }
395 else if( rc == Result_write )
396 {
397 m_socket.addWriteHandler( m_handler , m_eh ) ;
398 }
399 else
400 {
401 m_socket.dropWriteHandler() ;
402 m_state = State_idle ;
403 m_peer_certificate = m_ssl->peerCertificate() ;
404 logSecure( m_peer_certificate ) ;
405 G_DEBUG( "SocketProtocolImp::secureAcceptImp: calling onSecure: " << G::Str::printable(m_peer_certificate) ) ;
406 m_sink.onSecure( m_peer_certificate ) ;
407 }
408 }
409
410 bool GNet::SocketProtocolImp::sslSend( const Segments & segments , Position pos )
411 {
412 if( !finished(m_segments,m_position) )
413 throw SocketProtocol::SendError( "still busy sending the last packet" ) ;
414
415 G_ASSERT( m_state == State_idle ) ;
416 m_state = State_writing ;
417
418 Position pos_out ;
419 bool all_sent = sslSendImp( segments , pos , pos_out ) ;
420 if( !all_sent && failed() )
421 {
422 m_segments.clear() ;
423 m_position = Position() ;
424 throw SocketProtocol::SendError() ;
425 }
426 if( all_sent )
427 {
428 m_segments.clear() ;
429 m_position = Position() ;
430 }
431 else
432 {
433 m_segments = segments ;
434 m_position = pos_out ;
435 }
436 return all_sent ;
437 }
438
439 bool GNet::SocketProtocolImp::sslSendImp()
440 {
441 return sslSendImp( m_segments , m_position , m_position ) ;
442 }
443
444 bool GNet::SocketProtocolImp::sslSendImp( const Segments & segments , Position pos , Position & pos_out )
445 {
446 while( !finished(segments,pos) )
447 {
448 const char * chunk_data = chunk_p( segments , pos ) ;
449 size_t chunk_size = chunk_n( segments , pos ) ;
450
451 ssize_t nsent = 0 ;
452 GSsl::Protocol::Result result = m_ssl->write( chunk_data , chunk_size , nsent ) ;
453 if( result == Result_error )
454 {
455 m_socket.dropWriteHandler() ;
456 m_state = State_idle ;
457 m_failed = true ;
458 return false ; // failed
459 }
460 else if( result == Result_read )
461 {
462 m_socket.dropWriteHandler() ;
463 return false ; // not all sent - retry ssl write() on read event
464 }
465 else if( result == Result_write )
466 {
467 m_socket.addWriteHandler( m_handler , m_eh ) ;
468 return false ; // not all sent - retry ssl write() on write event
469 }
470 else // Result_ok
471 {
472 // continue to next chunk
473 G_ASSERT( nsent >= 0 ) ;
474 pos_out = pos = newPosition( segments , pos , nsent >= 0 ? static_cast<size_t>(nsent) : size_t(0U) ) ;
475 }
476 }
477 m_state = State_idle ;
478 return true ; // all sent
479 }
480
481 void GNet::SocketProtocolImp::sslReadImp()
482 {
483 G_DEBUG( "SocketProtocolImp::sslReadImp" ) ;
484 G_ASSERT( m_state == State_idle ) ;
485 G_ASSERT( m_ssl != nullptr ) ;
486
487 GSsl::Protocol::Result rc = GSsl::Protocol::Result_more ;
488 for( int sanity = 0 ; rc == Result_more && sanity < 1000 ; sanity++ )
489 {
490 rc = m_ssl->read( &m_read_buffer[0] , m_read_buffer.size() , m_read_buffer_n ) ;
491 G_DEBUG( "SocketProtocolImp::sslReadImp: result=" << GSsl::Protocol::str(rc) ) ;
492 if( rc == Result_error )
493 {
494 m_socket.dropWriteHandler() ;
495 m_state = State_idle ;
496 throw SocketProtocol::ReadError( "ssl read" ) ;
497 }
498 else if( rc == Result_read )
499 {
500 m_socket.dropWriteHandler() ;
501 }
502 else if( rc == Result_write )
503 {
504 m_socket.addWriteHandler( m_handler , m_eh ) ;
505 }
506 else // Result_ok, Result_more
507 {
508 G_ASSERT( rc == GSsl::Protocol::Result_ok || rc == Result_more ) ;
509 G_ASSERT( m_read_buffer_n >= 0 ) ;
510 m_socket.dropWriteHandler() ;
511 m_state = State_idle ;
512 size_t n = static_cast<size_t>(m_read_buffer_n) ;
513 m_read_buffer_n = 0 ;
514 G_DEBUG( "SocketProtocolImp::sslReadImp: calling onData(): " << n ) ;
515 if( n != 0U ) m_sink.onData( &m_read_buffer[0] , n ) ;
516 }
517 if( rc == Result_more )
518 {
519 G_DEBUG( "SocketProtocolImp::sslReadImp: more available to read" ) ;
520 }
521 }
522 }
523
524 void GNet::SocketProtocolImp::rawOtherEvent()
525 {
526 // got a clean shutdown indication on windows -- no read events will
527 // follow but there might be data to read -- so try reading in a loop
528 G_DEBUG( "GNet::SocketProtocolImp::rawOtherEvent: clearing receive queue" ) ;
529 for(;;)
530 {
531 const ssize_t rc = m_socket.read( &m_read_buffer[0] , m_read_buffer.size() ) ;
532 G_DEBUG_GROUP( "io" , "GNet::SocketProtocolImp::rawOtherEvent: read " << m_socket.asString() << ": " << rc ) ;
533 if( rc <= 0 )
534 {
535 m_socket.shutdown() ;
536 throw SocketProtocol::ReadError( m_socket.reason() ) ;
537 }
538 G_ASSERT( static_cast<size_t>(rc) <= m_read_buffer.size() ) ;
539 m_sink.onData( &m_read_buffer[0] , static_cast<size_t>(rc) ) ;
540 }
541 }
542
543 void GNet::SocketProtocolImp::rawReadEvent()
544 {
545 const ssize_t rc = m_socket.read( &m_read_buffer[0] , m_read_buffer.size() ) ;
546 G_DEBUG_GROUP( "io" , "GNet::SocketProtocolImp::rawReadEvent: read " << m_socket.asString() << ": " << rc ) ;
547
548 if( rc == 0 || ( rc == -1 && !m_socket.eWouldBlock() ) )
549 {
550 throw SocketProtocol::ReadError( m_socket.reason() ) ;
551 }
552 else if( rc != -1 )
553 {
554 G_ASSERT( static_cast<size_t>(rc) <= m_read_buffer.size() ) ;
555 m_sink.onData( &m_read_buffer[0] , static_cast<size_t>(rc) ) ;
556 }
557 else
558 {
559 G_DEBUG( "GNet::SocketProtocolImp::rawReadEvent: read event read nothing" ) ;
560 ; // -1 && eWouldBlock() -- no-op (esp. for windows)
561 }
562 }
563
564 bool GNet::SocketProtocolImp::rawSend( const Segments & segments , Position pos , bool do_copy )
565 {
566 G_ASSERT( !do_copy || segments.size() == 1U ) ; // copy => one segment
567
568 if( !finished(m_segments,m_position) )
569 throw SocketProtocol::SendError( "still busy sending the last packet" ) ;
570
571 Position pos_out ;
572 bool all_sent = rawSendImp( segments , pos , pos_out ) ;
573 if( !all_sent && failed() )
574 {
575 m_segments.clear() ;
576 m_position = Position() ;
577 m_data_copy.clear() ;
578 throw SocketProtocol::SendError() ;
579 }
580 if( all_sent )
581 {
582 m_segments.clear() ;
583 m_position = Position() ;
584 m_data_copy.clear() ;
585 }
586 else if( do_copy )
587 {
588 // keep the residue in m_segments/m_position/m_data_copy
589 G_ASSERT( segments.size() == 1U ) ; // precondition
590 G_ASSERT( pos_out.offset < segments[0].second ) ; // since not all sent
591 m_segments = segments ;
592 m_data_copy.assign( segments[0].first+pos_out.offset , segments[0].second-pos_out.offset ) ;
593 m_segments[0].first = m_data_copy.data() ;
594 m_segments[0].second = m_data_copy.size() ;
595 m_position = Position() ;
596
597 m_socket.addWriteHandler( m_handler , m_eh ) ;
598 logFlowControl( "asserted" ) ;
599 }
600 else
601 {
602 // record the new write position
603 m_segments = segments ;
604 m_data_copy.clear() ;
605 m_position = pos_out ;
606
607 m_socket.addWriteHandler( m_handler , m_eh ) ;
608 logFlowControl( "asserted" ) ;
609 }
610 return all_sent ;
611 }
612
613 bool GNet::SocketProtocolImp::rawWriteEvent()
614 {
615 m_socket.dropWriteHandler() ;
616 logFlowControl( "released" ) ;
617 bool all_sent = rawSendImp( m_segments , m_position , m_position ) ;
618 if( !all_sent && failed() )
619 {
620 m_segments.clear() ;
621 m_position = Position() ;
622 m_data_copy.clear() ;
623 throw SocketProtocol::SendError() ;
624 }
625 if( all_sent )
626 {
627 m_segments.clear() ;
628 m_position = Position() ;
629 m_data_copy.clear() ;
630 }
631 else
632 {
633 logFlowControl( "reasserted" ) ;
634 m_socket.addWriteHandler( m_handler , m_eh ) ;
635 }
636 return all_sent ;
637 }
638
639 bool GNet::SocketProtocolImp::rawSendImp( const Segments & segments , Position pos , Position & pos_out )
640 {
641 while( !finished(segments,pos) )
642 {
643 const char * chunk_data = chunk_p( segments , pos ) ;
644 size_t chunk_size = chunk_n( segments , pos ) ;
645
646 ssize_t rc = m_socket.write( chunk_data , chunk_size ) ;
647 G_DEBUG_GROUP( "io" , "GNet::SocketProtocolImp::rawSendImp: write " << m_socket.asString() << ": " << rc ) ;
648 if( rc < 0 && ! m_socket.eWouldBlock() )
649 {
650 // fatal error, eg. disconnection
651 pos_out = Position() ;
652 m_failed = true ;
653 return false ; // failed()
654 }
655 else if( rc < 0 || static_cast<size_t>(rc) < chunk_size )
656 {
657 // flow control asserted -- return the position where we stopped
658 size_t nsent = rc > 0 ? static_cast<size_t>(rc) : 0U ;
659 pos_out = newPosition( segments , pos , nsent ) ;
660 G_ASSERT( !finished(segments,pos_out) ) ;
661 return false ; // not all sent
662 }
663 else
664 {
665 pos = newPosition( segments , pos , static_cast<size_t>(rc) ) ;
666 }
667 }
668 return true ; // all sent
669 }
670
671 GSsl::Protocol * GNet::SocketProtocolImp::newProtocol( const std::string & profile_name )
672 {
673 GSsl::Library * library = GSsl::Library::instance() ;
674 if( library == nullptr )
675 throw G::Exception( "SocketProtocolImp::newProtocol: no tls library available" ) ;
676
677 return new GSsl::Protocol( library->profile(profile_name) ) ;
678 }
679
680 bool GNet::SocketProtocolImp::finished( const Segments & segments , Position pos )
681 {
682 return pos.segment == segments.size() ;
683 }
684
685 bool GNet::SocketProtocolImp::failed() const
686 {
687 return m_failed ;
688 }
689
690 std::string GNet::SocketProtocolImp::peerCertificate() const
691 {
692 return m_peer_certificate ;
693 }
694
695 void GNet::SocketProtocolImp::log( int level , const std::string & log_line )
696 {
697 if( level == 1 )
698 G_DEBUG( "GNet::SocketProtocolImp::log: tls: " << log_line ) ;
699 else if( level == 2 )
700 G_LOG( "GNet::SocketProtocolImp::log: tls: " << log_line ) ;
701 else
702 G_WARNING( "GNet::SocketProtocolImp::log: tls: " << log_line ) ;
703 }
704
705 void GNet::SocketProtocolImp::logSecure( const std::string & certificate ) const
706 {
707 std::pair<std::string,bool> rc( std::string() , false ) ;
708 if( GNet::Monitor::instance() )
709 rc = GNet::Monitor::instance()->findCertificate( certificate ) ;
710 if( rc.second ) // is new
711 logCertificate( rc.first , certificate ) ;
712
713 G_LOG( "GNet::SocketProtocolImp: tls/ssl protocol established with "
714 << m_socket.getPeerAddress().second.displayString()
715 << (rc.first.empty()?"":" certificate ") << rc.first ) ;
716 }
717
718 void GNet::SocketProtocolImp::logCertificate( const std::string & certid , const std::string & certificate ) const
719 {
720 G::StringArray lines ;
721 lines.reserve( 30U ) ;
722 G::Str::splitIntoFields( certificate , lines , "\n" ) ;
723 for( G::StringArray::iterator line_p = lines.begin() ; line_p != lines.end() ; ++line_p )
724 {
725 if( !(*line_p).empty() )
726 {
727 G_LOG( "GNet::SocketProtocolImp: certificate " << certid << ": " << *line_p ) ;
728 }
729 }
730 }
731
732 void GNet::SocketProtocolImp::logFlowControl( const char * what )
733 {
734 G_LOG_GROUP( "flow-control" , "GNet::SocketProtocolImp::send: flow control " << what << ": " << m_socket.asString() ) ;
735 }
736
737 //
738
739 GNet::SocketProtocol::SocketProtocol( EventHandler & handler , ExceptionHandler & eh ,
740 Sink & sink , StreamSocket & socket , unsigned int secure_connection_timeout ) :
741 m_imp( new SocketProtocolImp(handler,eh,sink,socket,secure_connection_timeout) )
742 {
743 }
744
745 GNet::SocketProtocol::~SocketProtocol()
746 {
747 delete m_imp ;
748 }
749
750 void GNet::SocketProtocol::readEvent()
751 {
752 m_imp->readEvent() ;
753 }
754
755 bool GNet::SocketProtocol::writeEvent()
756 {
757 return m_imp->writeEvent() ;
758 }
759
760 void GNet::SocketProtocol::otherEvent( EventHandler::Reason reason )
761 {
762 m_imp->otherEvent( reason ) ;
763 }
764
765 bool GNet::SocketProtocol::send( const std::string & data , size_t offset )
766 {
767 return m_imp->send( data , offset ) ;
768 }
769
770 bool GNet::SocketProtocol::send( const std::vector<std::pair<const char *,size_t> > & data )
771 {
772 return m_imp->send( data ) ;
773 }
774
775 bool GNet::SocketProtocol::secureConnectCapable()
776 {
777 return GSsl::Library::enabledAs( "client" ) ;
778 }
779
780 void GNet::SocketProtocol::secureConnect()
781 {
782 m_imp->secureConnect() ;
783 }
784
785 bool GNet::SocketProtocol::secureAcceptCapable()
786 {
787 return GSsl::Library::enabledAs( "server" ) ;
788 }
789
790 void GNet::SocketProtocol::secureAccept()
791 {
792 m_imp->secureAccept() ;
793 }
794
795 std::string GNet::SocketProtocol::peerCertificate() const
796 {
797 return m_imp->peerCertificate() ;
798 }
799
800 void GNet::SocketProtocol::setReadBufferSize( size_t n )
801 {
802 SocketProtocolImp::setReadBufferSize( n ) ;
803 }
804
805 //
806
807 GNet::SocketProtocolSink::~SocketProtocolSink()
808 {
809 }
810
GSsl::Library::instance
static Library * instance()
Returns a pointer to a library object, if any.
Definition: gssl.cpp:72
gssl.h
An interface to an underlying TLS library.
GNet::Socket::shutdown
void shutdown(int how=1)
Modifies the local socket state so that so that new sends (1 or 2) and/or receives (0 or 2) will fail...
Definition: gsocket.cpp:249
GNet::Socket::reason
std::string reason() const
Returns the reason for the previous error.
Definition: gsocket.cpp:260
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
GNet::SocketProtocol::readEvent
void readEvent()
Called on receipt of a read event.
Definition: gsocketprotocol.cpp:750
GNet::StreamSocket::read
virtual ssize_type read(char *buffer, size_type buffer_length) override
Override from Socket::read().
Definition: gsocket.cpp:290
GNet
Network classes.
Definition: gaddress.h:29
glog.h
GNet::SocketProtocol::otherEvent
void otherEvent(EventHandler::Reason)
Called on receipt of an 'other' event.
Definition: gsocketprotocol.cpp:760
GNet::SocketProtocolSink::onData
virtual void onData(const char *, size_t)=0
Called when data is read from the socket.
gsocketprotocol.h
GSsl::Protocol
A TLS protocol class.
Definition: gssl.h:64
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
GSsl::Library::enabledAs
static bool enabledAs(const std::string &profile_name)
A static convenience function that returns true if there is an enabled() Library instance() that has ...
Definition: gssl.cpp:109
GSsl::Protocol::str
static std::string str(Result result)
Converts a result enumeration into a printable string.
Definition: gssl.cpp:182
gtest.h
GNet::SocketProtocolSink::~SocketProtocolSink
virtual ~SocketProtocolSink()
Destructor.
Definition: gsocketprotocol.cpp:807
GNet::SocketProtocolSink::onSecure
virtual void onSecure(const std::string &peer_certificate)=0
Called once the secure socket protocol has been successfully negotiated.
GNet::SocketProtocolImp
A pimple-pattern implementation class used by GNet::SocketProtocol.
Definition: gsocketprotocol.cpp:45
GNet::Socket::dropWriteHandler
void dropWriteHandler()
Reverses addWriteHandler().
Definition: gsocket.cpp:232
GNet::ExceptionHandler
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket events...
Definition: gexceptionhandler.h:44
GNet::Socket::getPeerAddress
std::pair< bool, Address > getPeerAddress() const
Retrieves address of socket's peer.
Definition: gsocket.cpp:199
GNet::SocketProtocol::peerCertificate
std::string peerCertificate() const
Returns the peer's TLS/SSL certificate or the empty string.
Definition: gsocketprotocol.cpp:795
glimits.h
GNet::StreamSocket
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:246
gstr.h
gassert.h
GNet::SocketProtocol::~SocketProtocol
~SocketProtocol()
Destructor.
Definition: gsocketprotocol.cpp:745
gtimer.h
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:69
GNet::SocketProtocol::secureAccept
void secureAccept()
Waits for the TLS/SSL handshake protocol, acting as a server.
Definition: gsocketprotocol.cpp:790
GSsl::Protocol::read
Result read(char *buffer, size_t buffer_size_in, ssize_t &data_size_out)
Reads user data into the supplied buffer.
Definition: gssl.cpp:201
GSsl::Protocol::accept
Result accept(G::ReadWrite &io)
Starts the protocol passively (as a server).
Definition: gssl.cpp:196
GNet::Monitor::instance
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:113
GSsl::Protocol::connect
Result connect(G::ReadWrite &io)
Starts the protocol actively (as a client).
Definition: gssl.cpp:191
GNet::SocketProtocol::send
bool send(const std::string &data, size_t offset=0U)
Sends data.
Definition: gsocketprotocol.cpp:765
GSsl::Library::profile
const Profile & profile(const std::string &profile_name) const
Returns an opaque reference to the named profile.
Definition: gssl.cpp:102
GNet::EventHandler
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:47
GSsl::Protocol::peerCertificate
std::string peerCertificate() const
Returns the peer certificate in PEM format.
Definition: gssl.cpp:167
GNet::EventHandler::str
static std::string str(Reason)
Returns a printable description of the other-event reason.
Definition: geventhandler.cpp:50
GNet::Socket::addWriteHandler
void addWriteHandler(EventHandler &, ExceptionHandler &)
Adds this socket to the event source list so that the given handler receives write events when flow c...
Definition: gsocket.cpp:215
GNet::StreamSocket::write
virtual ssize_type write(const char *buf, size_type len) override
Override from Socket::write().
Definition: gsocket.cpp:306
GNet::SocketProtocolImp::Position
A pointer into the scatter/gather payload of GNet::SocketProtocolImp::send().
Definition: gsocketprotocol.cpp:50
gmonitor.h
GSsl::Protocol::write
Result write(const char *buffer, size_t data_size_in, ssize_t &data_size_out)
Writes user data.
Definition: gssl.cpp:206
GNet::SocketProtocol::secureAcceptCapable
static bool secureAcceptCapable()
Returns true if the implementation supports TLS/SSL and a "server" profile has been configured...
Definition: gsocketprotocol.cpp:785
G::Exception
A general-purpose exception class derived from std::exception and containing a std::string.
Definition: gexception.h:43
GNet::SocketProtocol::secureConnect
void secureConnect()
Initiates the TLS/SSL handshake, acting as a client.
Definition: gsocketprotocol.cpp:780
GNet::Monitor::findCertificate
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:148
GSsl::Library
A singleton class for initialising the underlying TLS library.
Definition: gssl.h:234
GNet::SocketProtocol::SocketProtocol
SocketProtocol(EventHandler &, ExceptionHandler &, Sink &, StreamSocket &, unsigned int secure_connection_timeout)
Constructor. The references are kept.
Definition: gsocketprotocol.cpp:739
GNet::Timer
A timer class template in which the timeout is delivered to the specified method. ...
Definition: gtimer.h:115
GNet::SocketProtocol::secureConnectCapable
static bool secureConnectCapable()
Returns true if the implementation supports TLS/SSL and a "client" profile has been configured...
Definition: gsocketprotocol.cpp:775
GNet::Socket::eWouldBlock
virtual bool eWouldBlock() override
Returns true if the previous socket operation failed because the socket would have blocked...
Definition: gsocket_unix.cpp:93
GNet::SocketProtocolSink
An interface used by GNet::SocketProtocol to deliver data from a socket.
Definition: gsocketprotocol.h:134
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &separators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:975
GNet::SocketProtocol::setReadBufferSize
static void setReadBufferSize(size_t n)
Sets the read buffer size. Used in testing.
Definition: gsocketprotocol.cpp:800
GNet::SocketProtocol::writeEvent
bool writeEvent()
Called on receipt of a write event.
Definition: gsocketprotocol.cpp:755
GNet::Socket::asString
std::string asString() const
Returns the socket handle as a string.
Definition: gsocket.cpp:242