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