E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gserverprotocol.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gserverprotocol.cpp
19 //
20
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "gsaslserverfactory.h"
24 #include "gsocketprotocol.h"
25 #include "gserverprotocol.h"
26 #include "gxtext.h"
27 #include "gbase64.h"
28 #include "gdate.h"
29 #include "gtime.h"
30 #include "gdatetime.h"
31 #include "gstr.h"
32 #include "glog.h"
33 #include "gassert.h"
34 #include <string>
35
36 GSmtp::ServerProtocol::ServerProtocol( GNet::ExceptionHandler & eh , Sender & sender ,
37 Verifier & verifier , ProtocolMessage & pmessage , const GAuth::Secrets & secrets , Text & text ,
38 GNet::Address peer_address , Config config ) :
39 GNet::TimerBase(eh) ,
40 m_sender(sender) ,
41 m_verifier(verifier) ,
42 m_text(text) ,
43 m_message(pmessage) ,
44 m_sasl(GAuth::SaslServerFactory::newSaslServer(secrets,false/*apop*/)) ,
45 m_config(config) ,
46 m_fsm(sStart,sEnd,s_Same,s_Any) ,
47 m_with_starttls(false) ,
48 m_peer_address(peer_address) ,
49 m_secure(false) ,
50 m_bad_client_count(0U) ,
51 m_bad_client_limit(8U) ,
52 m_session_authenticated(false)
53 {
54 m_message.doneSignal().connect( G::Slot::slot(*this,&ServerProtocol::processDone) ) ;
55 verifier.doneSignal().connect( G::Slot::slot(*this,&ServerProtocol::verifyDone) ) ;
56
57 // (dont send anything to the peer from this ctor -- the Sender object is not fuly constructed)
58
59 m_fsm.addTransition( eQuit , s_Any , sEnd , &GSmtp::ServerProtocol::doQuit ) ;
60 m_fsm.addTransition( eUnknown , sProcessing , s_Same , &GSmtp::ServerProtocol::doIgnore ) ;
61 m_fsm.addTransition( eUnknown , s_Any , s_Same , &GSmtp::ServerProtocol::doUnknown ) ;
62 m_fsm.addTransition( eRset , sStart , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
63 m_fsm.addTransition( eRset , s_Any , sIdle , &GSmtp::ServerProtocol::doRset ) ;
64 m_fsm.addTransition( eNoop , s_Any , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
65 m_fsm.addTransition( eHelp , s_Any , s_Same , &GSmtp::ServerProtocol::doHelp ) ;
66 m_fsm.addTransition( eExpn , s_Any , s_Same , &GSmtp::ServerProtocol::doExpn ) ;
67 m_fsm.addTransition( eVrfy , sStart , sVrfyStart , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
68 m_fsm.addTransition( eVrfyReply , sVrfyStart , sStart , &GSmtp::ServerProtocol::doVrfyReply ) ;
69 m_fsm.addTransition( eVrfy , sIdle , sVrfyIdle , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
70 m_fsm.addTransition( eVrfyReply , sVrfyIdle , sIdle , &GSmtp::ServerProtocol::doVrfyReply ) ;
71 m_fsm.addTransition( eVrfy , sGotMail , sVrfyGotMail, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
72 m_fsm.addTransition( eVrfyReply , sVrfyGotMail, sGotMail , &GSmtp::ServerProtocol::doVrfyReply ) ;
73 m_fsm.addTransition( eVrfy , sGotRcpt , sVrfyGotRcpt, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
74 m_fsm.addTransition( eVrfyReply , sVrfyGotRcpt, sGotRcpt , &GSmtp::ServerProtocol::doVrfyReply ) ;
75 m_fsm.addTransition( eEhlo , s_Any , sIdle , &GSmtp::ServerProtocol::doEhlo , s_Same ) ;
76 m_fsm.addTransition( eHelo , s_Any , sIdle , &GSmtp::ServerProtocol::doHelo , s_Same ) ;
77 m_fsm.addTransition( eMail , sIdle , sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ;
78 m_fsm.addTransition( eRcpt , sGotMail , sVrfyTo1 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
79 m_fsm.addTransition( eVrfyReply , sVrfyTo1 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply , sGotMail ) ;
80 m_fsm.addTransition( eRcpt , sGotRcpt , sVrfyTo2 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
81 m_fsm.addTransition( eVrfyReply , sVrfyTo2 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply ) ;
82 m_fsm.addTransition( eData , sGotMail , sIdle , &GSmtp::ServerProtocol::doNoRecipients ) ;
83 m_fsm.addTransition( eData , sGotRcpt , sData , &GSmtp::ServerProtocol::doData ) ;
84 m_fsm.addTransition( eContent , sData , sData , &GSmtp::ServerProtocol::doContent , sDiscarding ) ;
85 m_fsm.addTransition( eEot , sData , sProcessing , &GSmtp::ServerProtocol::doEot ) ;
86 m_fsm.addTransition( eDone , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
87 m_fsm.addTransition( eTimeout , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
88 m_fsm.addTransition( eContent , sDiscarding , sDiscarding , &GSmtp::ServerProtocol::doDiscard ) ;
89 m_fsm.addTransition( eEot , sDiscarding , sIdle , &GSmtp::ServerProtocol::doDiscarded ) ;
90
91 if( m_sasl->active() )
92 {
93 m_fsm.addTransition( eAuth , sIdle , sAuth , &GSmtp::ServerProtocol::doAuth , sIdle ) ;
94 m_fsm.addTransition( eAuthData, sAuth , sAuth , &GSmtp::ServerProtocol::doAuthData , sIdle ) ;
95 }
96
97 m_with_starttls = GNet::SocketProtocol::secureAcceptCapable() && m_config.advertise_tls_if_possible ;
98 if( m_with_starttls )
99 {
100 m_fsm.addTransition( eStartTls , sIdle , sStartingTls , &GSmtp::ServerProtocol::doStartTls , sIdle ) ;
101 m_fsm.addTransition( eSecure , sStartingTls , sIdle , &GSmtp::ServerProtocol::doSecure ) ;
102 }
103 }
104
105 void GSmtp::ServerProtocol::init()
106 {
107 sendGreeting( m_text.greeting() ) ;
108 }
109
110 GSmtp::ServerProtocol::~ServerProtocol()
111 {
112 m_message.doneSignal().disconnect() ;
113 m_verifier.doneSignal().disconnect() ;
114 }
115
116 void GSmtp::ServerProtocol::secure( const std::string & certificate )
117 {
118 State new_state = m_fsm.apply( *this , eSecure , std::make_pair(certificate.data(),certificate.size()) ) ;
119 if( new_state == s_Any )
120 throw ProtocolDone( "protocol error" ) ;
121 }
122
123 void GSmtp::ServerProtocol::doSecure( EventData certificate , bool & )
124 {
125 G_DEBUG( "GSmtp::ServerProtocol::doSecure" ) ;
126 m_secure = true ;
127 m_certificate = std::string(certificate.first,certificate.second) ;
128 }
129
130 void GSmtp::ServerProtocol::doStartTls( EventData , bool & ok )
131 {
132 if( m_secure )
133 {
134 send( "503 command out of sequence" ) ;
135 ok = false ;
136 }
137 else
138 {
139 send( "220 ready to start tls" , true ) ;
140 }
141 }
142
143 void GSmtp::ServerProtocol::sendGreeting( const std::string & text )
144 {
145 send( std::string("220 ") + text ) ;
146 }
147
148 bool GSmtp::ServerProtocol::apply( const char * line_data , size_t line_size , size_t /*eolsize*/ )
149 {
150 Event event = eUnknown ;
151 State state = m_fsm.state() ;
152 EventData event_data( line_data , line_size ) ;
153 if( (state == sData || state == sDiscarding) && isEndOfText(line_data,line_size) )
154 {
155 event = eEot ;
156 }
157 else if( state == sData || state == sDiscarding )
158 {
159 event = eContent ;
160 }
161 else if( state == sAuth )
162 {
163 event = eAuthData ;
164 }
165 else
166 {
167 std::string line( line_data , line_size ) ;
168 G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
169 event = commandEvent( commandWord(line) ) ;
170 m_buffer = commandLine( line ) ;
171 event_data = EventData( m_buffer.data() , m_buffer.size() ) ;
172 }
173
174 State new_state = m_fsm.apply( *this , event , event_data ) ;
175 if( new_state == s_Any )
176 sendOutOfSequence( std::string(line_data,line_size) ) ;
177
178 return true ; // see GNet::LineBuffer::apply()
179 }
180
181 void GSmtp::ServerProtocol::doContent( EventData event_data , bool & ok )
182 {
183 if( isEscaped(event_data.first,event_data.second) )
184 ok = m_message.addText( event_data.first+1 , event_data.second-1U ) ;
185 else
186 ok = m_message.addText( event_data.first , event_data.second ) ;
187
188 // moves to discard state if not ok - discard state throws if so configured
189 if( !ok && m_config.disconnect_on_max_size )
190 sendTooBig( true ) ;
191 }
192
193 void GSmtp::ServerProtocol::doEot( EventData event_data , bool & )
194 {
195 std::string line( event_data.first , event_data.second ) ;
196 G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
197 G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
198 if( m_config.filter_timeout != 0U )
199 {
200 G_DEBUG( "GSmtp::ServerProtocol: starting filter timer: " << m_config.filter_timeout ) ;
201 startTimer( m_config.filter_timeout ) ;
202 }
203 m_message.process( m_sasl->id() , m_peer_address.hostPartString() , m_certificate ) ;
204 }
205
206 void GSmtp::ServerProtocol::processDone( bool success , unsigned long id , std::string response , std::string reason )
207 {
208 G_IGNORE_PARAMETER( unsigned long , id ) ;
209 G_IGNORE_PARAMETER( std::string , reason ) ;
210 G_DEBUG( "GSmtp::ServerProtocol::processDone: " << (success?1:0) << " " << id << " [" << response << "] [" << reason << "]" ) ;
211 G_ASSERT( success == response.empty() ) ;
212
213 State new_state = m_fsm.apply( *this , eDone , std::make_pair(response.data(),response.size()) ) ;
214 if( new_state == s_Any )
215 throw ProtocolDone( "protocol error" ) ;
216 }
217
218 void GSmtp::ServerProtocol::onTimeout()
219 {
220 G_WARNING( "GSmtp::ServerProtocol::onTimeout: message filter timed out after " << m_config.filter_timeout << "s" ) ;
221 std::string reason = "timed out" ;
222 State new_state = m_fsm.apply( *this , eTimeout , std::make_pair(reason.data(),reason.size()) ) ;
223 if( new_state == s_Any )
224 throw ProtocolDone( "protocol error" ) ;
225 }
226
227 void GSmtp::ServerProtocol::doComplete( EventData event_data , bool & )
228 {
229 reset() ;
230 const bool empty = event_data.second == 0U ;
231 sendCompletionReply( empty , std::string(event_data.first,event_data.second) ) ;
232 }
233
234 void GSmtp::ServerProtocol::doQuit( EventData , bool & )
235 {
236 reset() ;
237 sendClosing() ; // never deletes this
238 throw ProtocolDone() ;
239 }
240
241 void GSmtp::ServerProtocol::doDiscard( EventData , bool & )
242 {
243 if( m_config.disconnect_on_max_size )
244 {
245 reset() ;
246 sendClosing() ; // never deletes this
247 throw ProtocolDone() ;
248 }
249 }
250
251 void GSmtp::ServerProtocol::doIgnore( EventData , bool & )
252 {
253 }
254
255 void GSmtp::ServerProtocol::doNoop( EventData , bool & )
256 {
257 sendOk() ;
258 }
259
260 void GSmtp::ServerProtocol::doNothing( EventData , bool & )
261 {
262 }
263
264 void GSmtp::ServerProtocol::doDiscarded( EventData , bool & )
265 {
266 reset() ;
267 sendTooBig() ;
268 }
269
270 void GSmtp::ServerProtocol::doExpn( EventData , bool & )
271 {
272 sendNotImplemented() ;
273 }
274
275 void GSmtp::ServerProtocol::doHelp( EventData , bool & )
276 {
277 sendNotImplemented() ;
278 }
279
280 void GSmtp::ServerProtocol::doVrfy( EventData event_data , bool & predicate )
281 {
282 std::string line( event_data.first , event_data.second ) ;
283 if( m_config.with_vrfy )
284 {
285 std::string to = parseRcptParameter( line ) ;
286 if( to.empty() )
287 {
288 predicate = false ;
289 sendNotVerified( "invalid mailbox" , false ) ;
290 }
291 else
292 {
293 verify( to , "" ) ;
294 }
295 }
296 else
297 {
298 predicate = false ;
299 sendNotImplemented() ;
300 }
301 }
302
303 void GSmtp::ServerProtocol::verify( const std::string & to , const std::string & from )
304 {
305 std::string mechanism = m_sasl->active() ? m_sasl->mechanism() : std::string() ;
306 std::string id = m_sasl->active() ? m_sasl->id() : std::string() ;
307 if( m_sasl->active() && !m_session_authenticated )
308 mechanism = "NONE" ;
309 m_verifier.verify( to , from , m_peer_address , mechanism , id ) ;
310 }
311
312 void GSmtp::ServerProtocol::verifyDone( std::string mbox , VerifierStatus status )
313 {
314 if( status.abort )
315 throw ProtocolDone( "verifier abort" ) ;
316
317 std::string status_str = status.str( mbox ) ;
318 State new_state = m_fsm.apply( *this , eVrfyReply , std::make_pair(status_str.data(),status_str.size()) ) ;
319 if( new_state == s_Any )
320 throw ProtocolDone( "protocol error" ) ;
321 }
322
323 void GSmtp::ServerProtocol::doVrfyReply( EventData event_data , bool & )
324 {
325 std::string line( event_data.first , event_data.second ) ;
326 std::string mbox ;
327 VerifierStatus status = VerifierStatus::parse( line , mbox ) ;
328
329 if( status.is_valid && status.is_local )
330 sendVerified( status.full_name ) ; // 250
331 else if( status.is_valid )
332 sendWillAccept( mbox ) ; // 252
333 else
334 sendNotVerified( status.response , status.temporary ) ; // 550 or 450
335 }
336
337 std::string GSmtp::ServerProtocol::parseRcptParameter( const std::string & line ) const
338 {
339 std::string to ;
340 size_t pos = line.find_first_of( " \t" ) ;
341 if( pos != std::string::npos )
342 to = line.substr(pos) ;
343
344 G::Str::trim( to , " \t" ) ;
345 return to ;
346 }
347
348 void GSmtp::ServerProtocol::doEhlo( EventData event_data , bool & predicate )
349 {
350 std::string line( event_data.first , event_data.second ) ;
351 std::string smtp_peer_name = parsePeerName( line ) ;
352 if( smtp_peer_name.empty() )
353 {
354 predicate = false ;
355 sendMissingParameter() ;
356 }
357 else
358 {
359 m_session_peer_name = smtp_peer_name ;
360 m_session_authenticated = false ;
361 reset() ;
362 sendEhloReply() ;
363 }
364 }
365
366 void GSmtp::ServerProtocol::doHelo( EventData event_data , bool & predicate )
367 {
368 std::string line( event_data.first , event_data.second ) ;
369 std::string smtp_peer_name = parsePeerName( line ) ;
370 if( smtp_peer_name.empty() )
371 {
372 predicate = false ;
373 sendMissingParameter() ;
374 }
375 else
376 {
377 m_session_peer_name = smtp_peer_name ;
378 reset() ;
379 sendHeloReply() ;
380 }
381 }
382
383 bool GSmtp::ServerProtocol::authenticationRequiresEncryption() const
384 {
385 bool encryption_required_by_user = m_config.authentication_requires_encryption ;
386 bool encryption_required_by_sasl = m_sasl->active() && m_sasl->requiresEncryption() ;
387 return encryption_required_by_user || encryption_required_by_sasl ;
388 }
389
390 void GSmtp::ServerProtocol::doAuth( EventData event_data , bool & predicate )
391 {
392 G::StringArray word_array ;
393 G::Str::splitIntoTokens( std::string(event_data.first,event_data.second) , word_array , " \t" ) ;
394
395 std::string mechanism = word_array.size() > 1U ? word_array[1U] : std::string() ;
396 G::Str::toUpper( mechanism ) ;
397 std::string initial_response = word_array.size() > 2U ? word_array[2U] : std::string() ;
398 bool got_initial_response = word_array.size() > 2U ;
399
400 G_DEBUG( "ServerProtocol::doAuth: [" << mechanism << "], [" << initial_response << "]" ) ;
401
402 if( !m_secure && authenticationRequiresEncryption() )
403 {
404 G_WARNING( "GSmtp::ServerProtocol: rejecting authentication attempt without encryption" ) ;
405 predicate = false ; // => idle
406 send( "504 Unsupported authentication mechanism" ) ; // since none until encryption
407 }
408 else if( m_session_authenticated )
409 {
410 G_WARNING( "GSmtp::ServerProtocol: too many AUTH requests" ) ;
411 predicate = false ; // => idle
412 sendOutOfSequence(std::string(event_data.first,event_data.second)) ; // see RFC-2554 "Restrictions"
413 }
414 else if( ! m_sasl->init(mechanism) )
415 {
416 G_WARNING( "GSmtp::ServerProtocol: request for unsupported server AUTH mechanism: " << mechanism ) ;
417 predicate = false ; // => idle
418 send( "504 Unsupported authentication mechanism" ) ;
419 }
420 else if( got_initial_response && ! G::Base64::valid(initial_response) )
421 {
422 G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of AUTH parameter" ) ;
423 predicate = false ; // => idle
424 send( "501 Invalid argument" ) ;
425 }
426 else if( got_initial_response )
427 {
428 std::string s = initial_response == "=" ? std::string() : G::Base64::decode(initial_response) ;
429 bool done = false ;
430 std::string next_challenge = m_sasl->apply( s , done ) ;
431 if( done )
432 {
433 predicate = false ; // => idle
434 m_session_authenticated = m_sasl->authenticated() ;
435 sendAuthDone( m_sasl->authenticated() ) ;
436 }
437 else
438 {
439 sendChallenge( next_challenge ) ;
440 }
441 }
442 else
443 {
444 sendChallenge( m_sasl->initialChallenge() ) ;
445 }
446 }
447
448 void GSmtp::ServerProtocol::sendAuthDone( bool ok )
449 {
450 if( ok )
451 send( "235 Authentication sucessful" ) ;
452 else
453 send( "535 Authentication failed" ) ;
454 }
455
456 void GSmtp::ServerProtocol::doAuthData( EventData event_data , bool & predicate )
457 {
458 G_LOG( "GSmtp::ServerProtocol: rx<<: [authentication response not logged]" ) ;
459 std::string line( event_data.first , event_data.second ) ;
460 if( line == "*" )
461 {
462 predicate = false ; // => idle
463 send( "501 authentication cancelled" ) ;
464 }
465 else if( ! G::Base64::valid(line) )
466 {
467 G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of authentication response" ) ;
468 predicate = false ; // => idle
469 sendAuthDone( false ) ;
470 }
471 else
472 {
473 bool done = false ;
474 std::string next_challenge = m_sasl->apply( G::Base64::decode(line) , done ) ;
475 if( done )
476 {
477 predicate = false ; // => idle
478 m_session_authenticated = m_sasl->authenticated() ;
479 sendAuthDone( m_sasl->authenticated() ) ;
480 }
481 else
482 {
483 sendChallenge( next_challenge ) ;
484 }
485 }
486 }
487
488 void GSmtp::ServerProtocol::sendChallenge( const std::string & challenge )
489 {
490 send( std::string("334 ") + G::Base64::encode(challenge,std::string()) ) ;
491 }
492
493 void GSmtp::ServerProtocol::doMail( EventData event_data , bool & predicate )
494 {
495 std::string line( event_data.first , event_data.second ) ;
496 if( !m_session_authenticated && m_sasl->active() && !m_sasl->trusted(m_peer_address) )
497 {
498 predicate = false ;
499 sendAuthRequired() ;
500 }
501 else if( !m_secure && m_config.mail_requires_encryption )
502 {
503 predicate = false ;
504 sendEncryptionRequired() ;
505 }
506 else if( m_config.max_size && parseMailSize(line) > m_config.max_size )
507 {
508 predicate = false ;
509 sendTooBig() ;
510 }
511 else
512 {
513 m_message.clear() ;
514 std::pair<std::string,std::string> from_pair = parseMailFrom( line ) ;
515 bool ok = from_pair.second.empty() && m_message.setFrom( from_pair.first , parseMailAuth(line) ) ;
516 predicate = ok ;
517 if( ok )
518 {
519 sendMailReply() ;
520 }
521 else
522 {
523 sendBadFrom( from_pair.second ) ;
524 }
525 }
526 }
527
528 void GSmtp::ServerProtocol::doRcpt( EventData event_data , bool & predicate )
529 {
530 std::pair<std::string,std::string> to_pair = parseRcptTo( std::string(event_data.first,event_data.second) ) ;
531 std::string reason = to_pair.second ;
532 bool ok = reason.empty() ;
533
534 if( ok )
535 {
536 verify( to_pair.first , m_message.from() ) ;
537 }
538 else
539 {
540 predicate = false ;
541 sendBadTo( reason , false ) ;
542 }
543 }
544
545 void GSmtp::ServerProtocol::doVrfyToReply( EventData event_data , bool & predicate )
546 {
547 std::string to ;
548 VerifierStatus status = VerifierStatus::parse( std::string(event_data.first,event_data.second) , to ) ;
549
550 bool ok = m_message.addTo( to , status ) ;
551 if( ok )
552 {
553 sendRcptReply() ;
554 }
555 else
556 {
557 predicate = false ;
558 sendBadTo( G::Str::printable(status.response) , status.temporary ) ;
559 }
560 }
561
562 void GSmtp::ServerProtocol::doUnknown( EventData event_data , bool & )
563 {
564 sendUnrecognised( std::string(event_data.first,event_data.second) ) ;
565 }
566
567 void GSmtp::ServerProtocol::reset()
568 {
569 // cancel the current message transaction -- ehlo/quit session unaffected
570 cancelTimer() ;
571 m_message.clear() ;
572 m_verifier.cancel() ;
573 }
574
575 void GSmtp::ServerProtocol::doRset( EventData , bool & )
576 {
577 reset() ;
578 m_message.reset() ;
579 sendRsetReply() ;
580 }
581
582 void GSmtp::ServerProtocol::doNoRecipients( EventData , bool & )
583 {
584 sendNoRecipients() ;
585 }
586
587 void GSmtp::ServerProtocol::doData( EventData , bool & )
588 {
589 std::string received_line = m_text.received( m_session_peer_name , m_session_authenticated , m_secure ) ;
590 if( received_line.length() )
591 m_message.addReceived( received_line ) ;
592
593 sendDataReply() ;
594 }
595
596 void GSmtp::ServerProtocol::sendOutOfSequence( const std::string & )
597 {
598 send( "503 command out of sequence -- use RSET to resynchronise" ) ;
599 badClientEvent() ;
600 }
601
602 void GSmtp::ServerProtocol::sendMissingParameter()
603 {
604 send( "501 parameter required" ) ;
605 }
606
607 bool GSmtp::ServerProtocol::isEndOfText( const char * line , size_t line_size ) const
608 {
609 return line_size == 1U && *line == '.' ;
610 }
611
612 bool GSmtp::ServerProtocol::isEscaped( const char * line , size_t line_size ) const
613 {
614 return line_size > 1U && *line == '.' ;
615 }
616
617 std::string GSmtp::ServerProtocol::commandWord( const std::string & line_in ) const
618 {
619 std::string line( line_in ) ;
620 G::Str::trimLeft( line , " \t" ) ;
621
622 size_t pos = line.find_first_of( " \t" ) ;
623 std::string command = line.substr( 0U , pos ) ;
624
625 G::Str::toUpper( command ) ;
626 return command ;
627 }
628
629 std::string GSmtp::ServerProtocol::commandLine( const std::string & line_in ) const
630 {
631 std::string line( line_in ) ;
632 G::Str::trimLeft( line , " \t" ) ;
633 return line ;
634 }
635
636 GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command ) const
637 {
638 if( command == "QUIT" ) return eQuit ;
639 if( command == "HELO" ) return eHelo ;
640 if( command == "EHLO" ) return eEhlo ;
641 if( command == "RSET" ) return eRset ;
642 if( command == "DATA" ) return eData ;
643 if( command == "RCPT" ) return eRcpt ;
644 if( command == "MAIL" ) return eMail ;
645 if( command == "VRFY" ) return eVrfy ;
646 if( command == "NOOP" ) return eNoop ;
647 if( command == "EXPN" ) return eExpn ;
648 if( command == "HELP" ) return eHelp ;
649 if( command == "STARTTLS" ) return eStartTls ;
650 if( m_sasl->active() && command == "AUTH" ) return eAuth ;
651 return eUnknown ;
652 }
653
654 void GSmtp::ServerProtocol::sendClosing()
655 {
656 send( "221 closing connection" ) ;
657 m_sender.protocolShutdown() ;
658 }
659
660 void GSmtp::ServerProtocol::sendVerified( const std::string & user )
661 {
662 send( "250 " + user ) ;
663 }
664
665 void GSmtp::ServerProtocol::sendNotVerified( const std::string & response , bool temporary )
666 {
667 send( (temporary?"450":"550") + std::string(1U,' ') + response ) ;
668 }
669
670 void GSmtp::ServerProtocol::sendWillAccept( const std::string & user )
671 {
672 send( "252 cannot verify but will accept: " + G::Str::printable(user) ) ;
673 }
674
675 void GSmtp::ServerProtocol::sendUnrecognised( const std::string & line )
676 {
677 send( "500 command unrecognized: \"" + G::Str::printable(line) + std::string(1U,'\"') ) ;
678 badClientEvent() ;
679 }
680
681 void GSmtp::ServerProtocol::sendNotImplemented()
682 {
683 send( "502 command not implemented" ) ;
684 }
685
686 void GSmtp::ServerProtocol::sendAuthRequired()
687 {
688 std::string more_help = authenticationRequiresEncryption() && !m_secure ? ": use starttls" : "" ;
689 send( "530 authentication required" + more_help ) ;
690 }
691
692 void GSmtp::ServerProtocol::sendEncryptionRequired()
693 {
694 send( "530 encryption required: use starttls" ) ;
695 }
696
697 void GSmtp::ServerProtocol::sendNoRecipients()
698 {
699 send( "554 no valid recipients" ) ;
700 }
701
702 void GSmtp::ServerProtocol::sendTooBig( bool disconnecting )
703 {
704 std::string s = "552 message exceeds fixed maximum message size" ;
705 if( disconnecting ) s.append( ", disconnecting" ) ;
706 send( s ) ;
707 }
708
709 void GSmtp::ServerProtocol::sendDataReply()
710 {
711 send( "354 start mail input -- end with <CRLF>.<CRLF>" ) ;
712 }
713
714 void GSmtp::ServerProtocol::sendRsetReply()
715 {
716 send( "250 state reset" ) ;
717 }
718
719 void GSmtp::ServerProtocol::sendMailReply()
720 {
721 sendOk() ;
722 }
723
724 void GSmtp::ServerProtocol::sendCompletionReply( bool ok , const std::string & reason )
725 {
726 if( ok )
727 sendOk() ;
728 else
729 send( "452 " + reason ) ;
730 }
731
732 void GSmtp::ServerProtocol::sendRcptReply()
733 {
734 sendOk() ;
735 }
736
737 void GSmtp::ServerProtocol::sendBadFrom( std::string reason )
738 {
739 std::string msg = "553 mailbox name not allowed" ;
740 if( ! reason.empty() )
741 {
742 msg.append( ": " ) ;
743 msg.append( reason ) ;
744 }
745 send( msg ) ;
746 }
747
748 void GSmtp::ServerProtocol::sendBadTo( const std::string & text , bool temporary )
749 {
750 send( (temporary?"450":"550") + std::string(text.empty()?"":" ") + text ) ;
751 }
752
753 void GSmtp::ServerProtocol::sendEhloReply()
754 {
755 std::ostringstream ss ;
756 ss << "250-" << m_text.hello(m_session_peer_name) << crlf() ;
757
758 if( m_config.max_size != 0U )
759 ss << "250-SIZE " << m_config.max_size << crlf() ;
760
761 if( m_sasl->active() && !( authenticationRequiresEncryption() && !m_secure ) )
762 ss << "250-AUTH " << m_sasl->mechanisms() << crlf() ;
763
764 if( m_with_starttls && !m_secure )
765 ss << "250-STARTTLS" << crlf() ;
766
767 if( m_config.with_vrfy )
768 ss << "250-VRFY" << crlf() ; // see RFC-2821 3.5.2
769
770 ss << "250 8BITMIME" ;
771 send( ss.str() ) ;
772 }
773
774 void GSmtp::ServerProtocol::sendHeloReply()
775 {
776 sendOk() ;
777 }
778
779 void GSmtp::ServerProtocol::sendOk()
780 {
781 send( "250 OK" ) ;
782 }
783
784 const std::string & GSmtp::ServerProtocol::crlf()
785 {
786 static const std::string s( "\015\012" ) ;
787 return s ;
788 }
789
790 void GSmtp::ServerProtocol::send( std::string line , bool go_secure )
791 {
792 G_LOG( "GSmtp::ServerProtocol: tx>>: \"" << G::Str::printable(line) << "\"" ) ;
793 line.append( crlf() ) ;
794 m_sender.protocolSend( line , go_secure ) ;
795 }
796
797 size_t GSmtp::ServerProtocol::parseMailSize( const std::string & line ) const
798 {
799 std::string parameter = parseMailParameter( line , "SIZE=" ) ;
800 if( parameter.empty() || !G::Str::isULong(parameter) )
801 return 0U ;
802 else
803 return static_cast<size_t>( G::Str::toULong(parameter,G::Str::Limited()) ) ;
804 }
805
806 std::string GSmtp::ServerProtocol::parseMailAuth( const std::string & line ) const
807 {
808 return parseMailParameter( line , "AUTH=" ) ;
809 }
810
811 std::string GSmtp::ServerProtocol::parseMailParameter( const std::string & line , const std::string & key ) const
812 {
813 std::string result ;
814 size_t end = line.find( '>' ) ;
815 if( end != std::string::npos )
816 {
817 G::StringArray parameters = G::Str::splitIntoTokens( line.substr(end) , " " ) ;
818 for( G::StringArray::iterator p = parameters.begin() ; p != parameters.end() ; ++p )
819 {
820 size_t pos = G::Str::upper(*p).find( key ) ;
821 if( pos == 0U && (*p).length() > key.size() )
822 {
823 result = G::Xtext::encode( G::Xtext::decode( (*p).substr(key.size()) ) ) ; // ensure valid xtext
824 break ;
825 }
826 }
827 }
828 return result ;
829 }
830
831 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseMailFrom( const std::string & line ) const
832 {
833 // eg. MAIL FROM:<me@localhost>
834 return parseAddress( line ) ;
835 }
836
837 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseRcptTo( const std::string & line ) const
838 {
839 // eg. RCPT TO:<@first.net,@second.net:you@last.net>
840 // eg. RCPT TO:<Postmaster>
841 return parseAddress( line ) ;
842 }
843
844 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseAddress( const std::string & line ) const
845 {
846 size_t start = line.find( '<' ) ;
847 size_t end = line.find( '>' ) ;
848 if( start == std::string::npos || end == std::string::npos || end < start )
849 {
850 std::string reason( "missing or invalid angle brackets in mailbox name" ) ;
851 return std::make_pair(std::string(),reason) ;
852 }
853
854 std::string s = line.substr( start + 1U , end - start - 1U ) ;
855 G::Str::trim( s , " \t" ) ;
856
857 // strip source route
858 if( s.length() > 0U && s.at(0U) == '@' )
859 {
860 size_t colon_pos = s.find( ':' ) ;
861 if( colon_pos == std::string::npos )
862 {
863 std::string reason( "invalid mailbox name: no colon after leading at character" ) ;
864 return std::make_pair(std::string(),reason) ;
865 }
866 s = s.substr( colon_pos + 1U ) ;
867 }
868
869 return std::make_pair(s,std::string()) ;
870 }
871
872 std::string GSmtp::ServerProtocol::parsePeerName( const std::string & line ) const
873 {
874 size_t pos = line.find_first_of( " \t" ) ;
875 if( pos == std::string::npos )
876 return std::string() ;
877
878 std::string smtp_peer_name = line.substr( pos + 1U ) ;
879 G::Str::trim( smtp_peer_name , " \t" ) ;
880 return smtp_peer_name ;
881 }
882
883 void GSmtp::ServerProtocol::badClientEvent()
884 {
885 m_bad_client_count++ ;
886 if( m_bad_client_limit && m_bad_client_count >= m_bad_client_limit )
887 {
888 std::string reason = "too many protocol errors from the client" ;
889 G_DEBUG( "GSmtp::ServerProtocol::badClientEvent: " << reason << ": dropping the connection" ) ;
890 throw ProtocolDone( reason ) ;
891 }
892 }
893
894 // ===
895
896 GSmtp::ServerProtocolText::ServerProtocolText( const std::string & code_ident , const std::string & thishost ,
897 const GNet::Address & peer_address ) :
898 m_code_ident(code_ident) ,
899 m_thishost(thishost) ,
900 m_peer_address(peer_address)
901 {
902 }
903
904 std::string GSmtp::ServerProtocolText::greeting() const
905 {
906 return m_thishost + " -- " + m_code_ident + " -- Service ready" ;
907 }
908
909 std::string GSmtp::ServerProtocolText::hello( const std::string & ) const
910 {
911 return m_thishost + " says hello" ;
912 }
913
914 std::string GSmtp::ServerProtocolText::received( const std::string & smtp_peer_name ,
915 bool authenticated , bool secure ) const
916 {
917 return receivedLine( smtp_peer_name , m_peer_address.hostPartString() , m_thishost , authenticated , secure ) ;
918 }
919
920 std::string GSmtp::ServerProtocolText::receivedLine( const std::string & smtp_peer_name ,
921 const std::string & peer_address , const std::string & thishost ,
922 bool authenticated , bool secure )
923 {
924 const G::EpochTime t = G::DateTime::now() ;
925 const G::DateTime::BrokenDownTime tm = G::DateTime::local(t) ;
926 const std::string zone = G::DateTime::offsetString(G::DateTime::offset(t)) ;
927 const G::Date date( tm ) ;
928 const G::Time time( tm ) ;
929 const std::string esmtp = std::string("ESMTP") + (secure?"S":"") + (authenticated?"A":"") ; // RFC-3848
930 const std::string peer_name = G::Str::escaped(G::Str::printable(smtp_peer_name),'\\'," ","s") ;
931
932 std::ostringstream ss ;
933 ss
934 << "Received: from " << peer_name
935 << " ("
936 << "[" << peer_address << "]"
937 << ") by " << thishost << " with " << esmtp << " ; "
938 << date.weekdayName(true) << ", "
939 << date.monthday() << " "
940 << date.monthName(true) << " "
941 << date.yyyy() << " "
942 << time.hhmmss(":") << " "
943 << zone ;
944 return ss.str() ;
945 }
946
947 // ===
948
949 GSmtp::ServerProtocol::Text::~Text()
950 {
951 }
952
953 // ===
954
955 GSmtp::ServerProtocol::Sender::~Sender()
956 {
957 }
958
959 // ===
960
961 GSmtp::ServerProtocol::Config::Config( bool with_vrfy_ , unsigned int filter_timeout_ ,
962 size_t max_size_ , bool authentication_requires_encryption_ , bool mail_requires_encryption_ ,
963 bool advertise_tls_if_possible_ ) :
964 with_vrfy(with_vrfy_) ,
965 filter_timeout(filter_timeout_) ,
966 max_size(max_size_) ,
967 authentication_requires_encryption(authentication_requires_encryption_) ,
968 mail_requires_encryption(mail_requires_encryption_) ,
969 disconnect_on_max_size(false) ,
970 advertise_tls_if_possible(advertise_tls_if_possible_)
971 {
972 }
973
974 /// \file gserverprotocol.cpp
GSmtp::ServerProtocol::secure
void secure(const std::string &certificate)
To be called when the transport protocol goes into secure mode.
Definition: gserverprotocol.cpp:116
G::Time::hhmmss
std::string hhmmss(const char *sep=nullptr) const
Returns the hhmmss string.
Definition: gtime.cpp:80
GSmtp::ServerProtocol::Sender
An interface used by ServerProtocol to send protocol replies.
Definition: gserverprotocol.h:62
G::Str::toULong
static unsigned long toULong(const std::string &s, Limited)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:509
G::EpochTime
A subsecond-resolution timestamp based on a time_t.
Definition: gdatetime.h:39
G::Base64::valid
static bool valid(const std::string &)
Returns true if the string can be decoded without any strict errors.
Definition: gbase64.cpp:66
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
GSmtp::ServerProtocolText::ServerProtocolText
ServerProtocolText(const std::string &code_ident, const std::string &thishost, const GNet::Address &peer_address)
Constructor.
Definition: gserverprotocol.cpp:896
G::Date
A date (dd/mm/yyyy) class.
Definition: gdate.h:39
GNet
Network classes.
Definition: gaddress.h:29
G::Date::monthName
std::string monthName(bool brief=false) const
Returns the month as a string (in english).
Definition: gdate.cpp:170
GSmtp::ServerProtocol::Text
An interface used by ServerProtocol to provide response text strings.
Definition: gserverprotocol.h:76
glog.h
G::DateTime::offset
static Offset offset(EpochTime epoch_time)
Returns the offset between UTC and localtime as at 'epoch_time'.
Definition: gdatetime.cpp:139
G::Xtext::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:107
gdate.h
G::DateTime::local
static BrokenDownTime local(EpochTime epoch_time)
Converts from epoch time to local broken-down-time.
Definition: gdatetime.cpp:131
GSmtp::Verifier::doneSignal
virtual G::Slot::Signal2< std::string, VerifierStatus > & doneSignal()=0
Returns a signal that is emit()ed when the verify() request is complete.
gsocketprotocol.h
GNet::Address
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:54
G::Xtext::encode
static std::string encode(const std::string &)
Encodes the given string.
Definition: gxtext.cpp:86
GSmtp::ProtocolMessage::doneSignal
virtual G::Slot::Signal4< bool, unsigned long, std::string, std::string > & doneSignal()=0
Returns a signal which is raised once process() has completed.
G::Date::monthday
int monthday() const
Returns the day of the month.
Definition: gdate.cpp:111
gtime.h
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::Base64::decode
static std::string decode(const std::string &, bool strict=false)
Decodes the given string.
Definition: gbase64.cpp:57
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:908
G::Time
A simple time-of-day (hh/mm/ss) class.
Definition: gtime.h:38
GSmtp::VerifierStatus::parse
static VerifierStatus parse(const std::string &str, std::string &to_ref)
Parses a str() string into a structure and a recipient 'to' address (by reference).
Definition: gverifierstatus.cpp:44
GSmtp::ServerProtocolText::received
virtual std::string received(const std::string &smtp_peer_name_from_helo, bool authenticated, bool secure) const override
Override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:914
G::Str::trim
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:246
GSmtp::Verifier
An asynchronous interface that verifies recipient 'to' addresses.
Definition: gverifier.h:43
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime_unix.cpp:26
G::Str::trimLeft
static void trimLeft(std::string &s, const std::string &ws, size_type limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:224
GNet::ExceptionHandler
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket events...
Definition: gexceptionhandler.h:44
GSmtp::ServerProtocol::init
void init()
Starts the protocol. Use only once after construction.
Definition: gserverprotocol.cpp:105
gserverprotocol.h
G::Str::escaped
static std::string escaped(const std::string &, char c_escape, const std::string &specials_in, const std::string &specials_out)
Returns the escape()d string.
Definition: gstr.cpp:41
gstr.h
gassert.h
GAuth::Secrets
A simple interface for a store of secrets used in authentication.
Definition: gsecrets.h:40
G
Low-level classes.
Definition: garg.h:29
GSmtp::ServerProtocol::~ServerProtocol
virtual ~ServerProtocol()
Destructor.
Definition: gserverprotocol.cpp:110
G::Slot::slot
Slot0 slot(T &object, void(T::*fn)())
A slot factory function overloaded for a zero-parameter callback.
Definition: gslot.h:209
G::Date::yyyy
std::string yyyy() const
Returns the year as a four-digit decimal string.
Definition: gdate.cpp:192
G::Str::upper
static std::string upper(const std::string &s)
Returns a copy of 's' in which all Latin-1 lowercase characters have been replaced by uppercase chara...
Definition: gstr.cpp:619
GSmtp::ServerProtocolText::hello
virtual std::string hello(const std::string &smtp_peer_name_from_helo) const override
Override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:909
GSmtp::ServerProtocolText::receivedLine
static std::string receivedLine(const std::string &smtp_peer_name_from_helo, const std::string &peer_address, const std::string &thishost, bool authenticated, bool secure)
Returns a standard "Received:" line.
Definition: gserverprotocol.cpp:920
G::Date::weekdayName
std::string weekdayName(bool brief=false) const
Returns an english string representation of the day of the week.
Definition: gdate.cpp:153
G::Str::Limited
Overload discrimiator for G::Str::toUWhatever()
Definition: gstr.h:50
gxtext.h
gdatetime.h
G::DateTime::offsetString
static std::string offsetString(Offset offset)
Converts the given utc/localtime offset into a five-character "+/-hhmm" string.
Definition: gdatetime.cpp:158
GSmtp::ServerProtocol::Config
A structure containing configuration parameters for ServerProtocol.
Definition: gserverprotocol.h:84
GNet::SocketProtocol::secureAcceptCapable
static bool secureAcceptCapable()
Returns true if the implementation supports TLS/SSL and a "server" profile has been configured...
Definition: gsocketprotocol.cpp:785
GSmtp::ProtocolMessage
An interface used by the ServerProtocol class to assemble and process an incoming message...
Definition: gprotocolmessage.h:63
gsaslserverfactory.h
gbase64.h
GSmtp::ServerProtocolText::greeting
virtual std::string greeting() const override
Override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:904
G::StateMachine::addTransition
void addTransition(Event event, State from, State to, Action action)
Adds a transition.
Definition: gstatemachine.h:177
gsmtp.h
GSmtp::ServerProtocol::apply
bool apply(const char *line_data, size_t line_size, size_t eolsize)
Called on receipt of a line of text from the remote client Returns true.
Definition: gserverprotocol.cpp:148
GSmtp::ServerProtocol::onTimeout
virtual void onTimeout() override
Override from GNet::TimerBase.
Definition: gserverprotocol.cpp:218
GAuth
SASL authentication classes.
Definition: gcram.h:30
G::Str::isULong
static bool isULong(const std::string &s)
Returns true if the string can be converted into an unsigned long without throwing an exception...
Definition: gstr.cpp:312
G::Base64::encode
static std::string encode(const std::string &s, const std::string &line_break)
Encodes the given string.
Definition: gbase64.cpp:52
GSmtp::ServerProtocol::ServerProtocol
ServerProtocol(GNet::ExceptionHandler &, Sender &, Verifier &, ProtocolMessage &, const GAuth::Secrets &secrets, Text &text, GNet::Address peer_address, Config config)
Constructor.
Definition: gserverprotocol.cpp:36
G::Str::toUpper
static void toUpper(std::string &s)
Replaces all Latin-1 lowercase characters in string 's' by uppercase characters.
Definition: gstr.cpp:614