42 const std::string & sasl_config ,
const std::string & challenge_hostname )
44 bool with_apop = false ;
54 m_verifier(verifier) ,
57 m_sasl(newSaslServer(secrets,config.sasl_server_config,config.sasl_server_challenge_hostname)) ,
59 m_fsm(State::Start,State::End,State::s_Same,State::s_Any) ,
60 m_peer_address(peer_address) ,
63 m_fsm( Event::Quit , State::s_Any , State::End , &ServerProtocol::doQuit ) ;
64 m_fsm( Event::Unknown , State::Processing , State::s_Same , &ServerProtocol::doIgnore ) ;
65 m_fsm( Event::Unknown , State::s_Any , State::s_Same , &ServerProtocol::doUnknown ) ;
66 m_fsm( Event::Rset , State::Start , State::s_Same , &ServerProtocol::doRset ) ;
67 m_fsm( Event::Rset , State::s_Any , State::Idle , &ServerProtocol::doRset ) ;
68 m_fsm( Event::Noop , State::s_Any , State::s_Same , &ServerProtocol::doNoop ) ;
69 m_fsm( Event::Help , State::s_Any , State::s_Same , &ServerProtocol::doHelp ) ;
70 m_fsm( Event::Expn , State::s_Any , State::s_Same , &ServerProtocol::doExpn ) ;
71 m_fsm( Event::Vrfy , State::Start , State::VrfyStart , &ServerProtocol::doVrfy , State::s_Same ) ;
72 m_fsm( Event::VrfyReply , State::VrfyStart , State::Start , &ServerProtocol::doVrfyReply ) ;
73 m_fsm( Event::Vrfy , State::Idle , State::VrfyIdle , &ServerProtocol::doVrfy , State::s_Same ) ;
74 m_fsm( Event::VrfyReply , State::VrfyIdle , State::Idle , &ServerProtocol::doVrfyReply ) ;
75 m_fsm( Event::Vrfy , State::GotMail , State::VrfyGotMail, &ServerProtocol::doVrfy , State::s_Same ) ;
76 m_fsm( Event::VrfyReply , State::VrfyGotMail, State::GotMail , &ServerProtocol::doVrfyReply ) ;
77 m_fsm( Event::Vrfy , State::GotRcpt , State::VrfyGotRcpt, &ServerProtocol::doVrfy , State::s_Same ) ;
78 m_fsm( Event::VrfyReply , State::VrfyGotRcpt, State::GotRcpt , &ServerProtocol::doVrfyReply ) ;
79 m_fsm( Event::Ehlo , State::s_Any , State::Idle , &ServerProtocol::doEhlo , State::s_Same ) ;
80 m_fsm( Event::Helo , State::s_Any , State::Idle , &ServerProtocol::doHelo , State::s_Same ) ;
81 m_fsm( Event::Mail , State::Idle , State::GotMail , &ServerProtocol::doMail , State::Idle ) ;
82 m_fsm( Event::Rcpt , State::GotMail , State::RcptTo1 , &ServerProtocol::doRcpt , State::s_Same ) ;
83 m_fsm( Event::RcptReply , State::RcptTo1 , State::GotRcpt , &ServerProtocol::doRcptToReply , State::GotMail ) ;
84 m_fsm( Event::Rcpt , State::GotRcpt , State::RcptTo2 , &ServerProtocol::doRcpt , State::s_Same ) ;
85 m_fsm( Event::RcptReply , State::RcptTo2 , State::GotRcpt , &ServerProtocol::doRcptToReply ) ;
86 m_fsm( Event::DataFail , State::GotMail , State::MustReset , &ServerProtocol::doBadDataCommand ) ;
87 m_fsm( Event::DataFail , State::GotRcpt , State::MustReset , &ServerProtocol::doBadDataCommand ) ;
88 m_fsm( Event::Data , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
89 m_fsm( Event::Data , State::GotRcpt , State::Data , &ServerProtocol::doData ) ;
90 m_fsm( Event::DataContent , State::Data , State::Data , &ServerProtocol::doDataContent ) ;
91 m_fsm( Event::Bdat , State::Idle , State::MustReset , &ServerProtocol::doBdatOutOfSequence ) ;
92 m_fsm( Event::Bdat , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
93 m_fsm( Event::BdatLast , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
94 m_fsm( Event::BdatLastZero , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
95 m_fsm( Event::Bdat , State::GotRcpt , State::BdatData , &ServerProtocol::doBdatFirst , State::MustReset ) ;
96 m_fsm( Event::BdatLast , State::GotRcpt , State::BdatDataLast , &ServerProtocol::doBdatFirstLast , State::MustReset ) ;
97 m_fsm( Event::BdatLastZero , State::GotRcpt , State::BdatChecking , &ServerProtocol::doBdatFirstLastZero ) ;
98 m_fsm( Event::BdatContent , State::BdatData , State::BdatIdle , &ServerProtocol::doBdatContent , State::BdatData ) ;
99 m_fsm( Event::Bdat , State::BdatIdle , State::BdatData , &ServerProtocol::doBdatMore , State::MustReset ) ;
100 m_fsm( Event::BdatLast , State::BdatIdle , State::BdatDataLast , &ServerProtocol::doBdatMoreLast , State::MustReset ) ;
101 m_fsm( Event::BdatLastZero , State::BdatIdle , State::BdatChecking , &ServerProtocol::doBdatMoreLastZero ) ;
102 m_fsm( Event::BdatContent , State::BdatDataLast , State::BdatChecking , &ServerProtocol::doBdatContentLast , State::BdatDataLast ) ;
103 m_fsm( Event::BdatCheck , State::BdatChecking , State::BdatProcessing , &ServerProtocol::doBdatCheck , State::Idle ) ;
104 m_fsm( Event::Done , State::BdatProcessing , State::Idle , &ServerProtocol::doBdatComplete ) ;
105 m_fsm( Event::Eot , State::Data , State::Processing , &ServerProtocol::doEot , State::Idle ) ;
106 m_fsm( Event::Done , State::Processing , State::Idle , &ServerProtocol::doComplete ) ;
107 m_fsm( Event::Auth , State::Idle , State::Auth , &ServerProtocol::doAuth , State::Idle ) ;
108 m_fsm( Event::AuthData, State::Auth , State::Auth , &ServerProtocol::doAuthData , State::Idle ) ;
109 if( m_config.tls_starttls )
111 m_with_starttls = true ;
112 m_fsm( Event::StartTls , State::Idle , State::StartingTls , &ServerProtocol::doStartTls , State::Idle ) ;
113 m_fsm( Event::Secure , State::StartingTls , State::Idle , &ServerProtocol::doSecure ) ;
115 else if( m_config.tls_connection )
117 m_fsm.
reset( State::StartingTls ) ;
118 m_fsm( Event::Secure , State::StartingTls , State::Start , &ServerProtocol::doSecureGreeting ) ;
126 m_pm.processedSignal().disconnect() ;
127 m_verifier.doneSignal().disconnect() ;
132 return m_change_signal ;
141 m_fsm.state() == State::Processing ||
143 m_fsm.state() == State::VrfyStart ||
144 m_fsm.state() == State::VrfyIdle ||
145 m_fsm.state() == State::VrfyGotMail ||
146 m_fsm.state() == State::VrfyGotRcpt ||
147 m_fsm.state() == State::RcptTo1 ||
148 m_fsm.state() == State::RcptTo2 ;
152bool GSmtp::ServerProtocol::rcptState()
const
155 m_fsm.state() == State::RcptTo1 ||
156 m_fsm.state() == State::RcptTo2 ;
160bool GSmtp::ServerProtocol::sendFlush()
const
165 if( !m_session_esmtp || !m_config.with_pipelining )
175 Event e = m_fsm.event() ;
179 e == Event::RcptReply ||
189 m_fsm.state() == State::Data ||
190 m_fsm.state() == State::BdatData ||
191 m_fsm.state() == State::BdatDataLast ;
204 if( m_config.tls_connection )
205 m_sender->protocolSecure() ;
207 sendGreeting( m_text.greeting() , m_enabled ) ;
210void GSmtp::ServerProtocol::applyEvent( Event event , EventData event_data )
212 State new_state = m_fsm.apply( *
this , event , event_data ) ;
213 if( new_state == State::s_Any )
214 throw Done(
"protocol error" ) ;
218 const std::string & protocol ,
const std::string & cipher )
220 m_certificate = certificate ;
221 m_protocol = protocol ;
224 applyEvent( Event::Secure ) ;
227void GSmtp::ServerProtocol::doSecure( EventData ,
bool & )
229 G_DEBUG(
"GSmtp::ServerProtocol::doSecure" ) ;
233void GSmtp::ServerProtocol::doSecureGreeting( EventData ,
bool & )
236 sendGreeting( m_text.greeting() , m_enabled ) ;
239void GSmtp::ServerProtocol::doStartTls( EventData ,
bool & ok )
243 sendOutOfSequence() ;
255 G_ASSERT( std::get<2>(args) == 2U || ( inDataState() && std::get<2>(args) == 0U ) ) ;
256 G_DEBUG(
"GSmtp::ServerProtocol::apply: apply "
257 "[" <<
G::Str::printable(G::sv_substr_noexcept(std::string_view(std::get<0>(args),std::get<1>(args)),0U,10U))
258 << (std::get<1>(args)>10U?
"...":
"") <<
"] "
259 "state=" <<
static_cast<int>(m_fsm.state()) <<
" "
260 "more=" << std::get<5>(args) <<
" "
261 "busy=" << inBusyState() ) ;
268 m_apply_data = &args ;
269 m_apply_more = std::get<5>( args ) ;
270 G::ScopeExit clear_on_return( [&](){ m_apply_data = nullptr ; } ) ;
273 G::ScopeExit emit_on_return( [&](){ m_change_signal.emit() ; } ) ;
278 EventData event_data( std::get<0>(args) , std::get<1>(args) ) ;
281 Event
event = Event::Unknown ;
282 State state = m_fsm.state() ;
283 if( state == State::Data && isEndOfText(args) )
287 else if( state == State::Data )
289 event = Event::DataContent ;
291 else if( state == State::BdatData || state == State::BdatDataLast )
293 event = Event::BdatContent ;
295 else if( state == State::Auth )
297 event = Event::AuthData ;
301 G_LOG(
"GSmtp::ServerProtocol: rx<<: \"" <<
G::Str::printable(str(event_data)) <<
"\"" ) ;
302 event = commandEvent( event_data ) ;
306 State new_state = m_fsm.apply( *
this , event , event_data ) ;
307 if( new_state == State::s_Any )
309 sendOutOfSequence() ;
314 return !inBusyState() ;
317void GSmtp::ServerProtocol::doDataContent( EventData ,
bool & )
319 G_ASSERT( m_apply_data !=
nullptr ) ;
320 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
322 const char * ptr = std::get<0>( *m_apply_data ) ;
323 std::size_t size = std::get<1>( *m_apply_data ) ;
324 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
326 if( isEscaped(*m_apply_data) )
327 m_pm.addContent( ptr+1 , size+eolsize-1U ) ;
329 m_pm.addContent( ptr , size+eolsize ) ;
334void GSmtp::ServerProtocol::doEot( EventData ,
bool & ok )
336 G_LOG(
"GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
337 G_LOG(
"GSmtp::ServerProtocol: rx<<: \".\"" ) ;
339 if( messageAddContentFailed() )
345 else if( messageAddContentTooBig() )
353 m_pm.process( m_sasl->id() , m_peer_address.hostPartString() , m_certificate ) ;
357void GSmtp::ServerProtocol::protocolMessageProcessed(
const ProtocolMessage::ProcessedInfo & info )
359 G_ASSERT( info.response.find_first_of(
"\r\t",0U,2U) == std::string::npos ) ;
360 G_ASSERT( info.success == info.response.empty() ) ;
361 G_ASSERT( info.response_code >= 0 ) ;
362 G_DEBUG(
"GSmtp::ServerProtocol::protocolMessageProcessed: ok=" << (info.success?1:0) <<
" msgid=" << info.id.str()
363 <<
" rc=" << info.response_code <<
" rsp=[" << info.response <<
"] reason=[" << info.reason <<
"]" ) ;
365 std::string response = info.response ;
370 .append(
G::Str::fromInt((info.response_code<400||info.response_code>=600)?452:info.response_code)) ;
372 applyEvent( Event::Done , {response.data(),response.size()} ) ;
373 m_change_signal.emit() ;
376void GSmtp::ServerProtocol::doComplete( EventData event_data ,
bool & )
379 sendCompletionReply( event_data.empty() , code(event_data) , str(event_data) ) ;
382void GSmtp::ServerProtocol::doQuit( EventData ,
bool & )
386 m_sender->protocolShutdown( m_config.shutdown_how_on_quit ) ;
390void GSmtp::ServerProtocol::doBadDataCommand( EventData ,
bool & )
392 sendBadDataOutOfSequence() ;
396void GSmtp::ServerProtocol::doBdatOutOfSequence( EventData ,
bool & )
398 sendOutOfSequence() ;
402void GSmtp::ServerProtocol::doBdatFirst( EventData event_data ,
bool & ok )
404 doBdatImp( event_data , ok ,
true ,
false ,
false ) ;
407void GSmtp::ServerProtocol::doBdatFirstLast( EventData event_data ,
bool & ok )
409 doBdatImp( event_data , ok ,
true ,
true ,
false ) ;
412void GSmtp::ServerProtocol::doBdatFirstLastZero( EventData event_data ,
bool & ok )
414 doBdatImp( event_data , ok ,
true ,
true ,
true ) ;
417void GSmtp::ServerProtocol::doBdatMore( EventData event_data ,
bool & ok )
419 doBdatImp( event_data , ok ,
false ,
false ,
false ) ;
422void GSmtp::ServerProtocol::doBdatMoreLast( EventData event_data ,
bool & ok )
424 doBdatImp( event_data , ok ,
false ,
true ,
false ) ;
427void GSmtp::ServerProtocol::doBdatMoreLastZero( EventData event_data ,
bool & ok )
429 doBdatImp( event_data , ok ,
false ,
true ,
true ) ;
432void GSmtp::ServerProtocol::doBdatImp( std::string_view bdat_line ,
bool & ok ,
bool first ,
bool last ,
bool zero )
434 G_ASSERT( !zero || last ) ;
437 std::string received_line = m_text.received( m_session_peer_name , m_sasl->authenticated() ,
438 m_secure , m_protocol , m_cipher ) ;
439 if( !received_line.empty() )
440 m_pm.addReceived( received_line ) ;
445 if( messageAddContentFailed() )
451 else if( messageAddContentTooBig() )
459 applyEvent( Event::BdatCheck ) ;
464 auto pair = parseBdatSize( bdat_line ) ;
465 std::size_t bdat_size = pair.first ;
466 bool bdat_ok = pair.second ;
467 if( !bdat_ok || ( bdat_size == 0U && !last ) )
469 G_DEBUG(
"GSmtp::ServerProtocol::doBdatImp: bad bdat command: ok=" << bdat_ok <<
" size=" << bdat_size <<
" last=" << last ) ;
471 sendInvalidArgument() ;
475 m_bdat_arg = bdat_size ;
477 m_sender->protocolExpect( m_bdat_arg ) ;
482void GSmtp::ServerProtocol::doBdatContent( EventData ,
bool & complete )
484 G_ASSERT( m_apply_data !=
nullptr ) ;
485 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
487 const char * ptr = std::get<0>( *m_apply_data ) ;
488 std::size_t size = std::get<1>( *m_apply_data ) ;
489 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
491 G_ASSERT( eolsize == 0U ) ;
492 G_ASSERT( (m_bdat_sum+size+eolsize) <= m_bdat_arg ) ;
494 std::size_t fullsize = size + eolsize ;
495 m_bdat_sum += fullsize ;
496 complete = m_bdat_sum >= m_bdat_arg ;
498 G_DEBUG(
"GSmtp::ServerProtocol: rx<<: [" << fullsize <<
" bytes (" << m_bdat_sum <<
"/" << m_bdat_arg <<
")]" ) ;
499 m_pm.addContent( ptr , fullsize ) ;
503 std::ostringstream ss ;
504 ss << m_bdat_sum <<
" bytes received" ;
509void GSmtp::ServerProtocol::doBdatContentLast( EventData ,
bool & complete )
511 G_ASSERT( m_apply_data !=
nullptr ) ;
512 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
514 const char * ptr = std::get<0>( *m_apply_data ) ;
515 std::size_t size = std::get<1>( *m_apply_data ) ;
516 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
518 G_ASSERT( (m_bdat_sum+size+eolsize) <= m_bdat_arg ) ;
520 std::size_t fullsize = size + eolsize ;
521 m_bdat_sum += fullsize ;
522 complete = m_bdat_sum >= m_bdat_arg ;
524 G_DEBUG(
"GSmtp::ServerProtocol: rx<<: [" << fullsize <<
" bytes (" << m_bdat_sum <<
"/" << m_bdat_arg <<
")]" ) ;
525 m_pm.addContent( ptr , fullsize ) ;
529 applyEvent( Event::BdatCheck ) ;
533void GSmtp::ServerProtocol::doBdatCheck( EventData ,
bool & ok )
535 if( messageAddContentFailed() )
541 else if( messageAddContentTooBig() )
549 m_pm.process( m_sasl->id() , m_peer_address.hostPartString() , m_certificate ) ;
553bool GSmtp::ServerProtocol::messageAddContentFailed()
555 bool failed = m_pm.addContent(
nullptr , 0U ) == GStore::NewMessage::Status::Error ;
557 G_WARNING(
"GSmtp::ServerProtocol::messageAddContentFailed: failed to save message content" ) ;
561bool GSmtp::ServerProtocol::messageAddContentTooBig()
563 bool too_big = m_pm.addContent(
nullptr , 0U ) == GStore::NewMessage::Status::TooBig ;
565 G_WARNING(
"GSmtp::ServerProtocol::messageAddContentTooBig: message content too big" ) ;
569void GSmtp::ServerProtocol::doBdatComplete( EventData event_data ,
bool & )
572 sendCompletionReply( event_data.empty() , code(event_data) , str(event_data) ) ;
575void GSmtp::ServerProtocol::doIgnore( EventData ,
bool & )
579void GSmtp::ServerProtocol::doNoop( EventData ,
bool & )
584void GSmtp::ServerProtocol::doExpn( EventData ,
bool & )
586 sendNotImplemented() ;
589void GSmtp::ServerProtocol::doHelp( EventData ,
bool & )
591 sendNotImplemented() ;
594void GSmtp::ServerProtocol::doVrfy( EventData event_data ,
bool & predicate )
596 if( !m_config.with_vrfy )
601 else if( m_config.mail_requires_authentication &&
602 !m_sasl->authenticated() &&
603 !m_sasl->trusted(m_peer_address.wildcards(),m_peer_address.hostPartString()) )
606 sendAuthRequired( m_config.mail_requires_encryption && !m_secure && m_with_starttls ) ;
608 else if( m_config.mail_requires_encryption && !m_secure )
611 sendEncryptionRequired( m_with_starttls ) ;
615 std::string to = parseVrfy( str(event_data) ) ;
619 sendNotVerified(
"invalid mailbox" ,
false ) ;
623 verify( Verifier::Command::VRFY , to , to ) ;
628void GSmtp::ServerProtocol::verify( Verifier::Command command ,
const std::string & to_address ,
629 const std::string & to_raw_address ,
const std::string & from_address )
631 Verifier::Request request ;
632 request.command = command ;
633 request.raw_address = to_raw_address ;
634 request.address = to_address ;
635 request.client_ip = m_peer_address ;
636 request.from_address = from_address ;
637 request.auth_mechanism = m_sasl->authenticated() ? m_sasl->mechanism() : std::string(
"NONE") ;
638 request.auth_extra = m_sasl->id() ;
639 m_verifier_raw_address = request.raw_address ;
640 m_verifier.verify( request ) ;
643void GSmtp::ServerProtocol::verifyDone( Verifier::Command command ,
const VerifierStatus & status )
645 G_DEBUG(
"GSmtp::ServerProtocol::verifyDone: verify done: [" << status.str() <<
"]" ) ;
647 throw Done(
"address verifier abort" ) ;
649 Event
event = command == Verifier::Command::RCPT ? Event::RcptReply : Event::VrfyReply ;
652 std::string status_str = status.str() ;
653 State new_state = m_fsm.apply( *
this , event , {status_str.data(),status_str.size()} ) ;
654 if( new_state == State::s_Any )
655 throw Done(
"protocol error" ) ;
657 m_change_signal.emit() ;
660void GSmtp::ServerProtocol::doVrfyReply( EventData event_data ,
bool & )
664 if( status.is_valid && status.is_local )
665 sendVerified( status.full_name ) ;
666 else if( status.is_valid )
667 sendWillAccept( status.recipient ) ;
669 sendNotVerified( status.response , status.temporary ) ;
672void GSmtp::ServerProtocol::doEhlo( EventData event_data ,
bool & predicate )
674 std::string smtp_peer_name = parseHeloPeerName( str(event_data) ) ;
675 if( smtp_peer_name.empty() )
678 sendMissingParameter() ;
682 m_session_esmtp = true ;
683 m_session_peer_name = smtp_peer_name ;
686 G_ASSERT( !m_sasl->authenticated() ) ;
688 ServerSend::Advertise advertise ;
689 advertise.hello = m_text.hello( m_session_peer_name ) ;
690 advertise.max_size = m_config.max_size ;
691 advertise.mechanisms = mechanisms() ;
692 advertise.starttls = m_with_starttls && !m_secure ;
693 advertise.vrfy = m_config.with_vrfy ;
694 advertise.chunking = advertise.binarymime = m_config.with_chunking ;
695 advertise.pipelining = m_config.with_pipelining ;
696 advertise.smtputf8 = m_config.with_smtputf8 ;
697 sendEhloReply( advertise ) ;
701void GSmtp::ServerProtocol::doHelo( EventData event_data ,
bool & predicate )
703 std::string smtp_peer_name = parseHeloPeerName( str(event_data) ) ;
704 if( smtp_peer_name.empty() )
707 sendMissingParameter() ;
711 m_session_peer_name = smtp_peer_name ;
717void GSmtp::ServerProtocol::doAuth( EventData event_data ,
bool & predicate )
721 std::string initial_response = G::sv_to_string( word.next()() ) ;
722 bool got_initial_response = !initial_response.empty() ;
724 G_DEBUG(
"ServerProtocol::doAuth: [" << mechanism <<
"], [" << initial_response <<
"]" ) ;
726 if( m_sasl->authenticated() )
728 G_WARNING(
"GSmtp::ServerProtocol: too many AUTH requests" ) ;
730 sendOutOfSequence() ;
733 else if( mechanisms().empty() && !m_secure && !mechanisms(
true).empty() )
735 G_WARNING(
"GSmtp::ServerProtocol: rejecting authentication attempt without encryption" ) ;
737 sendInsecureAuth( m_with_starttls ) ;
739 else if( mechanisms().empty() )
741 G_WARNING(
"GSmtp::ServerProtocol: client protocol error: AUTH requested but not advertised" ) ;
743 sendNotImplemented() ;
745 else if( !m_sasl->init(m_secure,mechanism) )
747 G_WARNING(
"GSmtp::ServerProtocol: request for unsupported server AUTH mechanism: " << mechanism ) ;
749 sendBadMechanism( m_sasl->preferredMechanism(m_secure) ) ;
751 else if( got_initial_response && m_sasl->mustChallenge() )
753 G_WARNING(
"GSmtp::ServerProtocol: unexpected initial-response with a server-first AUTH mechanism" ) ;
755 sendInvalidArgument() ;
757 else if( got_initial_response && ( initial_response !=
"=" && !
G::Base64::valid(initial_response) ) )
759 G_WARNING(
"GSmtp::ServerProtocol: invalid base64 encoding of AUTH parameter" ) ;
761 sendInvalidArgument() ;
763 else if( got_initial_response )
765 std::string s = initial_response ==
"=" ? std::string() :
G::Base64::decode(initial_response) ;
767 std::string next_challenge = m_sasl->apply( s , done ) ;
771 sendAuthDone( m_sasl->authenticated() ) ;
775 sendChallenge( next_challenge ) ;
780 sendChallenge( m_sasl->initialChallenge() ) ;
784void GSmtp::ServerProtocol::doAuthData( EventData event_data ,
bool & predicate )
786 G_LOG(
"GSmtp::ServerProtocol: rx<<: [authentication response not logged]" ) ;
787 if( event_data.size() == 1U && event_data[0] ==
'*' )
790 sendAuthenticationCancelled() ;
794 G_WARNING(
"GSmtp::ServerProtocol: invalid base64 encoding of authentication response" ) ;
796 sendAuthDone(
false ) ;
801 std::string next_challenge = m_sasl->apply(
G::Base64::decode(event_data) , done ) ;
805 sendAuthDone( m_sasl->authenticated() ) ;
809 sendChallenge( next_challenge ) ;
814void GSmtp::ServerProtocol::doMail( EventData event_data ,
bool & predicate )
816 std::string_view mail_line = event_data ;
823 else if( m_config.mail_requires_authentication &&
824 !m_sasl->authenticated() &&
825 !m_sasl->trusted(m_peer_address.wildcards(),m_peer_address.hostPartString()) )
827 G_LOG(
"GSmtp::ServerProtocol::doMail: server authentication enabled "
828 "but not a trusted address: " << m_peer_address.hostPartString() ) ;
830 sendAuthRequired( m_config.mail_requires_encryption && !m_secure && m_with_starttls ) ;
832 else if( m_config.mail_requires_encryption && !m_secure )
835 sendEncryptionRequired( m_with_starttls ) ;
839 auto mail_command = parseMailFrom( mail_line , m_config.parser_config ) ;
841 if( mail_command.invalid_nobrackets )
843 else if( mail_command.invalid_spaces )
844 warnInvalidSpaces() ;
846 if( !mail_command.error.empty() )
849 sendBadFrom( mail_command.error ) ;
851 else if( m_config.max_size && mail_command.size > m_config.max_size )
857 else if( mail_command.utf8_mailbox_part && !mail_command.smtputf8 && m_config.smtputf8_strict )
860 sendBadFrom(
"invalid character in mailbox name: not using smptutf8" ) ;
864 sendMailReply( mail_command.raw_address ) ;
865 ProtocolMessage::FromInfo from_info ;
866 from_info.auth = mail_command.auth ;
867 from_info.body = mail_command.body ;
868 from_info.smtputf8 = mail_command.smtputf8 ;
869 from_info.address_style = mail_command.address_style ;
870 m_pm.setFrom( mail_command.address , from_info ) ;
875void GSmtp::ServerProtocol::doRcpt( EventData event_data ,
bool & predicate )
877 std::string_view rcpt_line = event_data ;
878 auto rcpt_command = parseRcptTo( rcpt_line , m_config.parser_config ) ;
879 if( rcpt_command.invalid_nobrackets )
881 else if( rcpt_command.invalid_spaces )
882 warnInvalidSpaces() ;
883 if( !rcpt_command.error.empty() )
886 sendBadTo( {} , rcpt_command.error , false ) ;
888 else if( rcpt_command.utf8_mailbox_part && !m_pm.fromInfo().smtputf8 && m_config.smtputf8_strict )
891 sendBadTo( {} ,
"invalid character in mailbox name: not using smtputf8" , false ) ;
895 verify( Verifier::Command::RCPT , rcpt_command.address , rcpt_command.raw_address , m_pm.from() ) ;
899void GSmtp::ServerProtocol::doRcptToReply( EventData event_data ,
bool & predicate )
905 bool ok = m_pm.addTo( ProtocolMessage::ToInfo(status) ) ;
906 G_ASSERT( status.is_valid || !ok ) ;
909 const std::string & recipient = m_verifier_raw_address ;
912 sendRcptReply( recipient , status.is_local ) ;
917 sendBadTo( recipient , status.response , status.temporary ) ;
921void GSmtp::ServerProtocol::doUnknown( EventData event_data ,
bool & )
923 sendUnrecognised( str(event_data) ) ;
927void GSmtp::ServerProtocol::clear()
935 m_verifier.cancel() ;
938void GSmtp::ServerProtocol::doRset( EventData ,
bool & )
946void GSmtp::ServerProtocol::doNoRecipients( EventData ,
bool & )
951void GSmtp::ServerProtocol::doData( EventData ,
bool & )
953 std::string received_line = m_text.received( m_session_peer_name , m_sasl->authenticated() ,
954 m_secure , m_protocol , m_cipher ) ;
956 if( !received_line.empty() )
957 m_pm.addReceived( received_line ) ;
962bool GSmtp::ServerProtocol::isEndOfText(
const ApplyArgsTuple & args )
const
964 std::size_t eolsize = std::get<2>( args ) ;
965 std::size_t linesize = std::get<3>( args ) ;
966 char c0 = std::get<4>( args ) ;
967 return linesize == 1U && eolsize == 2U && c0 ==
'.' ;
970bool GSmtp::ServerProtocol::isEscaped(
const ApplyArgsTuple & args )
const
972 std::size_t size = std::get<1>( args ) ;
973 std::size_t linesize = std::get<3>( args ) ;
974 char c0 = std::get<4>( args ) ;
975 return size > 1U && size == linesize && c0 ==
'.' ;
978GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( std::string_view line )
const
981 std::string_view word = t() ;
993 if(
G::Str::imatch(word,
"STARTTLS"_sv) && m_with_starttls )
return Event::StartTls ;
995 if(
G::Str::imatch(word,
"BDAT"_sv) && m_config.with_chunking )
return bdatEvent(line) ;
996 return Event::Unknown ;
999GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::dataEvent( std::string_view )
const
1003 return Event::DataFail ;
1005 return Event::Data ;
1008GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::bdatEvent( std::string_view line )
const
1010 bool last = parseBdatLast(line).second ? parseBdatLast(line).first : false ;
1011 std::size_t size = parseBdatSize(line).second ? parseBdatSize(line).first : 0L ;
1012 if( last && size == 0U )
1013 return Event::BdatLastZero ;
1015 return Event::BdatLast ;
1017 return Event::Bdat ;
1020void GSmtp::ServerProtocol::badClientEvent()
1022 m_client_error_count++ ;
1023 if( m_config.client_error_limit && m_client_error_count >= m_config.client_error_limit )
1025 std::string reason =
"too many protocol errors from the client" ;
1026 G_DEBUG(
"GSmtp::ServerProtocol::badClientEvent: " << reason <<
": dropping the connection" ) ;
1027 throw Done( reason ) ;
1031int GSmtp::ServerProtocol::code( EventData sv )
1036std::string GSmtp::ServerProtocol::str( EventData sv )
1043 return m_sasl->mechanisms( m_secure ) ;
1046G::StringArray GSmtp::ServerProtocol::mechanisms(
bool secure )
const
1048 return m_sasl->mechanisms( secure ) ;
1051void GSmtp::ServerProtocol::warnInvalidSpaces()
const
1053 const std::string & help = m_config.parser_config.allow_spaces_help ;
1058void GSmtp::ServerProtocol::warnNoBrackets()
const
1060 const std::string & help = m_config.parser_config.allow_nobrackets_help ;
1065void GSmtp::ServerProtocol::warning(
const std::string & help )
1067 G_WARNING_ONCE(
"GSmtp::ServerProtocol::warning: invalid smtp command syntax from remote client: " << help ) ;
1072GSmtp::ServerProtocol::Config::Config()
static std::unique_ptr< SaslServer > newSaslServer(const SaslServerSecrets &, bool allow_pop, const std::string &config, const std::string &challenge_domain)
A factory function for a SaslServer.
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
An interface used by the ServerProtocol class to assemble and process an incoming message.
virtual ProcessedSignal & processedSignal()=0
Returns a signal which is raised once process() has completed.
An interface used by GSmtp::ServerProtocol to provide response text strings.
G::Slot::Signal & changeSignal() noexcept
A signal that is emitted at the end of apply() or whenever the protocol state might have changed by s...
bool inBusyState() const
Returns true if in a state where the protocol is waiting for an asynchronous filter of address-verifi...
bool apply(const ApplyArgsTuple &)
Called on receipt of a complete line of text from the client, or possibly a line fragment iff this ob...
void setSender(ServerSender &)
Sets the ServerSender interface, overriding the constructor parameter.
void init()
Starts the protocol.
~ServerProtocol() override
Destructor.
void secure(const std::string &certificate, const std::string &protocol, const std::string &cipher)
To be called when the transport protocol successfully goes into secure mode.
bool inDataState() const
Returns true if currently in a data-transfer state meaning that the next apply() does not need to con...
ServerProtocol(ServerSender &, Verifier &, ProtocolMessage &, const GAuth::SaslServerSecrets &secrets, Text &text, const GNet::Address &peer_address, const Config &config, bool enabled)
Constructor.
A simple mix-in class for GSmtp::ServerProtocol that sends protocol responses via a GSmtp::ServerSend...
void setSender(ServerSender *)
Sets the sender interface pointer.
An interface used by ServerProtocol to send protocol responses.
static VerifierStatus parse(const std::string &str)
Parses a str() string into a structure.
An asynchronous interface that verifies recipient 'to' addresses.
virtual G::Slot::Signal< Command, const VerifierStatus & > & doneSignal()=0
Returns a signal that is emit()ed when the verify() request is complete.
static std::string decode(std::string_view, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
static bool valid(std::string_view, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
A class that calls an exit function at the end of its scope.
State reset(State new_state)
Sets the current state. Returns the old state.
static int toInt(std::string_view s)
Converts string 's' to an int.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::string fromInt(int i)
Converts int 'i' to a string.
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 ...
static std::string upper(std::string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static bool replace(std::string &s, std::string_view from, std::string_view to, std::size_t *pos_p=nullptr)
A std::string_view overload.
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
bool enabled() noexcept
Returns true if pop code is built in.
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
std::vector< std::string > StringArray
A std::vector of std::strings.
A configuration structure for GSmtp::ServerProtocol.
A slot holder, with connect() and emit() methods.