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