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