- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gserverprotocol.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 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 "gserverprotocol.h"
25 #include "gbase64.h"
26 #include "gssl.h"
27 #include "gdate.h"
28 #include "gtime.h"
29 #include "gdatetime.h"
30 #include "gstr.h"
31 #include "glog.h"
32 #include "gassert.h"
33 #include <string>
34
35 GSmtp::ServerProtocol::ServerProtocol( Sender & sender , Verifier & verifier , ProtocolMessage & pmessage ,
36 const GAuth::Secrets & secrets , Text & text , GNet::Address peer_address ,
37 const std::string & peer_socket_name , Config config ) :
38 m_sender(sender) ,
39 m_verifier(verifier) ,
40 m_pmessage(pmessage) ,
41 m_text(text) ,
42 m_peer_address(peer_address) ,
43 m_peer_socket_name(peer_socket_name) ,
44 m_fsm(sStart,sEnd,s_Same,s_Any) ,
45 m_authenticated(false) ,
46 m_secure(false) ,
47 m_sasl(GAuth::SaslServerFactory::newSaslServer(secrets,false,true)) ,
48 m_with_vrfy(config.with_vrfy) ,
49 m_preprocessor_timeout(config.preprocessor_timeout) ,
50 m_bad_client_count(0U) ,
51 m_bad_client_limit(8U) ,
52 m_disconnect_on_overflow(config.disconnect_on_overflow)
53 {
54 m_pmessage.doneSignal().connect( G::slot(*this,&ServerProtocol::processDone) ) ;
55 verifier.doneSignal().connect( G::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 , s_Any , s_Same , &GSmtp::ServerProtocol::doUnknown ) ;
61 m_fsm.addTransition( eRset , sStart , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
62 m_fsm.addTransition( eRset , s_Any , sIdle , &GSmtp::ServerProtocol::doRset ) ;
63 m_fsm.addTransition( eNoop , s_Any , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
64 m_fsm.addTransition( eHelp , s_Any , s_Same , &GSmtp::ServerProtocol::doHelp ) ;
65 m_fsm.addTransition( eExpn , s_Any , s_Same , &GSmtp::ServerProtocol::doExpn ) ;
66 m_fsm.addTransition( eVrfy , sStart , sVrfyStart , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
67 m_fsm.addTransition( eVrfyReply , sVrfyStart , sStart , &GSmtp::ServerProtocol::doVrfyReply ) ;
68 m_fsm.addTransition( eVrfy , sIdle , sVrfyIdle , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
69 m_fsm.addTransition( eVrfyReply , sVrfyIdle , sIdle , &GSmtp::ServerProtocol::doVrfyReply ) ;
70 m_fsm.addTransition( eVrfy , sGotMail , sVrfyGotMail, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
71 m_fsm.addTransition( eVrfyReply , sVrfyGotMail, sGotMail , &GSmtp::ServerProtocol::doVrfyReply ) ;
72 m_fsm.addTransition( eVrfy , sGotRcpt , sVrfyGotRcpt, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
73 m_fsm.addTransition( eVrfyReply , sVrfyGotRcpt, sGotRcpt , &GSmtp::ServerProtocol::doVrfyReply ) ;
74 m_fsm.addTransition( eEhlo , s_Any , sIdle , &GSmtp::ServerProtocol::doEhlo , s_Same ) ;
75 m_fsm.addTransition( eHelo , s_Any , sIdle , &GSmtp::ServerProtocol::doHelo , s_Same ) ;
76 m_fsm.addTransition( eMail , sIdle , sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ;
77 m_fsm.addTransition( eRcpt , sGotMail , sVrfyTo1 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
78 m_fsm.addTransition( eVrfyReply , sVrfyTo1 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply , sGotMail ) ;
79 m_fsm.addTransition( eRcpt , sGotRcpt , sVrfyTo2 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
80 m_fsm.addTransition( eVrfyReply , sVrfyTo2 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply ) ;
81 m_fsm.addTransition( eData , sGotMail , sIdle , &GSmtp::ServerProtocol::doNoRecipients ) ;
82 m_fsm.addTransition( eData , sGotRcpt , sData , &GSmtp::ServerProtocol::doData ) ;
83 m_fsm.addTransition( eContent , sData , sData , &GSmtp::ServerProtocol::doContent , sDiscarding ) ;
84 m_fsm.addTransition( eEot , sData , sProcessing , &GSmtp::ServerProtocol::doEot ) ;
85 m_fsm.addTransition( eDone , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
86 m_fsm.addTransition( eTimeout , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
87 m_fsm.addTransition( eContent , sDiscarding , sDiscarding , &GSmtp::ServerProtocol::doDiscard ) ;
88 m_fsm.addTransition( eEot , sDiscarding , sIdle , &GSmtp::ServerProtocol::doDiscarded ) ;
89
90 #ifndef USE_NO_AUTH
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 #endif
97
98 GSsl::Library * ssl = GSsl::Library::instance() ;
99 m_with_ssl = ssl != NULL && ssl->enabled(true) ;
100 if( m_with_ssl )
101 {
102 m_fsm.addTransition( eStartTls , sIdle , sStartingTls , &GSmtp::ServerProtocol::doStartTls , sIdle ) ;
103 m_fsm.addTransition( eSecure , sStartingTls , sIdle , &GSmtp::ServerProtocol::doSecure ) ;
104 }
105 }
106
107 void GSmtp::ServerProtocol::init()
108 {
109 sendGreeting( m_text.greeting() ) ;
110 }
111
112 GSmtp::ServerProtocol::~ServerProtocol()
113 {
114 m_pmessage.doneSignal().disconnect() ;
115 m_verifier.doneSignal().disconnect() ;
116 }
117
118 void GSmtp::ServerProtocol::secure( const std::string & certificate )
119 {
120 State new_state = m_fsm.apply( *this , eSecure , certificate ) ;
121 if( new_state == s_Any )
122 throw ProtocolDone( "protocol error" ) ;
123 }
124
125 void GSmtp::ServerProtocol::doSecure( const std::string & certificate , bool & )
126 {
127 G_DEBUG( "GSmtp::ServerProtocol::doSecure" ) ;
128 m_secure = true ;
129 m_certificate = certificate ;
130 }
131
132 void GSmtp::ServerProtocol::doStartTls( const std::string & , bool & ok )
133 {
134 if( m_secure )
135 {
136 send( "503 command out of sequence" ) ;
137 ok = false ;
138 }
139 else
140 {
141 send( "220 ready to start tls" , true ) ;
142 }
143 }
144
145 void GSmtp::ServerProtocol::sendGreeting( const std::string & text )
146 {
147 send( std::string("220 ") + text ) ;
148 }
149
150 void GSmtp::ServerProtocol::apply( const std::string & line )
151 {
152 Event event = eUnknown ;
153 State state = m_fsm.state() ;
154 const std::string * event_data = &line ;
155 if( (state == sData || state == sDiscarding) && isEndOfText(line) )
156 {
157 event = eEot ;
158 }
159 else if( state == sData || state == sDiscarding )
160 {
161 event = eContent ;
162 }
163 else if( state == sAuth )
164 {
165 event = eAuthData ;
166 }
167 else
168 {
169 G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
170 event = commandEvent( commandWord(line) ) ;
171 m_buffer = commandLine(line) ;
172 event_data = &m_buffer ;
173 }
174
175 State new_state = m_fsm.apply( *this , event , *event_data ) ;
176 if( new_state == s_Any )
177 sendOutOfSequence( line ) ;
178 }
179
180 void GSmtp::ServerProtocol::doContent( const std::string & line , bool & ok )
181 {
182 if( isEscaped(line) )
183 ok = m_pmessage.addText( line.substr(1U) ) ; // temporary string constructed, but rare
184 else
185 ok = m_pmessage.addText( line ) ;
186
187 if( !ok && m_disconnect_on_overflow )
188 sendTooBig( true ) ;
189 }
190
191 void GSmtp::ServerProtocol::doEot( const std::string & line , bool & )
192 {
193 G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
194 G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
195 if( m_preprocessor_timeout != 0U )
196 {
197 G_DEBUG( "GSmtp::ServerProtocol: starting preprocessor timer: " << m_preprocessor_timeout ) ;
198 startTimer( m_preprocessor_timeout ) ;
199 }
200 m_pmessage.process( m_sasl->id() , m_peer_address.displayString(false) , m_peer_socket_name , m_certificate ) ;
201 }
202
203 void GSmtp::ServerProtocol::processDone( bool success , unsigned long , std::string reason )
204 {
205 G_DEBUG( "GSmtp::ServerProtocol::processDone: " << success << ", \"" << reason << "\"" ) ;
206
207 reason = success ? std::string() : ( reason.empty() ? std::string("error") : reason ) ; // just in case
208
209 State new_state = m_fsm.apply( *this , eDone , reason ) ;
210 if( new_state == s_Any )
211 throw ProtocolDone( "protocol error" ) ;
212 }
213
214 void GSmtp::ServerProtocol::onTimeout()
215 {
216 G_WARNING( "GSmtp::ServerProtocol::onTimeout: message processing timed out" ) ;
217 State new_state = m_fsm.apply( *this , eTimeout , "message processing timed out" ) ;
218 if( new_state == s_Any )
219 throw ProtocolDone( "protocol error" ) ;
220 }
221
222 void GSmtp::ServerProtocol::onTimeoutException( std::exception & e )
223 {
224 G_IGNORE_PARAMETER(std::exception,e) ;
225 G_DEBUG( "GSmtp::ServerProtocol::onTimeoutException: exception: " << e.what() ) ;
226 throw ;
227 }
228
229 void GSmtp::ServerProtocol::doComplete( const std::string & reason , bool & )
230 {
231 reset() ;
232 sendCompletionReply( reason.empty() , reason ) ;
233 }
234
235 void GSmtp::ServerProtocol::doQuit( const std::string & , bool & )
236 {
237 reset() ;
238 sendClosing() ; // never deletes this
239 throw ProtocolDone() ;
240 }
241
242 void GSmtp::ServerProtocol::doDiscard( const std::string & , bool & )
243 {
244 if( m_disconnect_on_overflow )
245 {
246 reset() ;
247 sendClosing() ; // never deletes this
248 throw ProtocolDone() ;
249 }
250 }
251
252 void GSmtp::ServerProtocol::doNoop( const std::string & , bool & )
253 {
254 sendOk() ;
255 }
256
257 void GSmtp::ServerProtocol::doNothing( const std::string & , bool & )
258 {
259 }
260
261 void GSmtp::ServerProtocol::doDiscarded( const std::string & , bool & )
262 {
263 reset() ;
264 sendTooBig() ;
265 }
266
267 void GSmtp::ServerProtocol::doExpn( const std::string & , bool & )
268 {
269 sendNotImplemented() ;
270 }
271
272 void GSmtp::ServerProtocol::doHelp( const std::string & , bool & )
273 {
274 sendNotImplemented() ;
275 }
276
277 void GSmtp::ServerProtocol::doVrfy( const std::string & line , bool & predicate )
278 {
279 if( m_with_vrfy )
280 {
281 std::string to = parseToParameter( line ) ;
282 if( to.empty() )
283 {
284 predicate = false ;
285 sendNotVerified( to , false ) ;
286 }
287 else
288 {
289 verify( to , "" ) ;
290 }
291 }
292 else
293 {
294 predicate = false ;
295 sendNotImplemented() ;
296 }
297 }
298
299 void GSmtp::ServerProtocol::verify( const std::string & to , const std::string & from )
300 {
301 std::string mechanism = m_sasl->active() ? m_sasl->mechanism() : std::string() ;
302 std::string id = m_sasl->active() ? m_sasl->id() : std::string() ;
303 if( m_sasl->active() && !m_authenticated )
304 mechanism = "NONE" ;
305 m_verifier.verify( to , from , m_peer_address , mechanism , id ) ;
306 }
307
308 void GSmtp::ServerProtocol::verifyDone( std::string mbox , VerifierStatus status )
309 {
310 State new_state = m_fsm.apply( *this , eVrfyReply , status.str(mbox) ) ;
311 if( new_state == s_Any )
312 throw ProtocolDone( "protocol error" ) ;
313 }
314
315 void GSmtp::ServerProtocol::doVrfyReply( const std::string & line , bool & )
316 {
317 std::string mbox ;
318 VerifierStatus rc = VerifierStatus::parse( line , mbox ) ;
319
320 if( rc.is_valid && rc.is_local )
321 sendVerified( rc.full_name ) ; // 250
322 else if( rc.is_valid )
323 sendWillAccept( mbox ) ; // 252
324 else
325 sendNotVerified( mbox , rc.temporary ) ; // 550 or 450
326 }
327
328 std::string GSmtp::ServerProtocol::parseToParameter( const std::string & line ) const
329 {
330 std::string to ;
331 size_t pos = line.find_first_of( " \t" ) ;
332 if( pos != std::string::npos )
333 to = line.substr(pos) ;
334
335 G::Str::trim( to , " \t" ) ;
336 return to ;
337 }
338
339 void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate )
340 {
341 std::string smtp_peer_name = parsePeerName( line ) ;
342 if( smtp_peer_name.empty() )
343 {
344 predicate = false ;
345 sendMissingParameter() ;
346 }
347 else
348 {
349 m_smtp_peer_name = smtp_peer_name ;
350 reset() ;
351 sendEhloReply() ;
352 }
353 }
354
355 void GSmtp::ServerProtocol::doHelo( const std::string & line , bool & predicate )
356 {
357 std::string smtp_peer_name = parsePeerName( line ) ;
358 if( smtp_peer_name.empty() )
359 {
360 predicate = false ;
361 sendMissingParameter() ;
362 }
363 else
364 {
365 m_smtp_peer_name = smtp_peer_name ;
366 reset() ;
367 sendHeloReply() ;
368 }
369 }
370
371 bool GSmtp::ServerProtocol::sensitive() const
372 {
373 // true if the sasl implementation is sensitive and so needs to be encrypted
374 return m_sasl->active() && m_sasl->requiresEncryption() && !m_secure ;
375 }
376
377 #ifndef USE_NO_AUTH
378 void GSmtp::ServerProtocol::doAuth( const std::string & line , bool & predicate )
379 {
380 G::StringArray word_array ;
381 G::Str::splitIntoTokens( line , word_array , " \t" ) ;
382
383 std::string mechanism = word_array.size() > 1U ? word_array[1U] : std::string() ;
384 G::Str::toUpper( mechanism ) ;
385 std::string initial_response = word_array.size() > 2U ? word_array[2U] : std::string() ;
386 bool got_initial_response = word_array.size() > 2U ;
387
388 G_DEBUG( "ServerProtocol::doAuth: [" << mechanism << "], [" << initial_response << "]" ) ;
389
390 if( sensitive() )
391 {
392 G_WARNING( "GSmtp::ServerProtocol: rejecting authentication attempt without encryption" ) ;
393 predicate = false ; // => idle
394 send( "504 Unsupported authentication mechanism" ) ;
395 }
396 else if( m_authenticated )
397 {
398 G_WARNING( "GSmtp::ServerProtocol: too many AUTHs" ) ;
399 predicate = false ; // => idle
400 sendOutOfSequence(line) ; // see RFC2554 "Restrictions"
401 }
402 else if( ! m_sasl->init(mechanism) )
403 {
404 G_WARNING( "GSmtp::ServerProtocol: request for unsupported AUTH mechanism: " << mechanism ) ;
405 predicate = false ; // => idle
406 send( "504 Unsupported authentication mechanism" ) ;
407 }
408 else if( got_initial_response && ! G::Base64::valid(initial_response) )
409 {
410 G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of AUTH parameter" ) ;
411 predicate = false ; // => idle
412 send( "501 Invalid argument" ) ;
413 }
414 else if( got_initial_response )
415 {
416 std::string s = initial_response == "=" ? std::string() : G::Base64::decode(initial_response) ;
417 bool done = false ;
418 std::string next_challenge = m_sasl->apply( s , done ) ;
419 if( done )
420 {
421 predicate = false ; // => idle
422 m_authenticated = m_sasl->authenticated() ;
423 sendAuthDone( m_sasl->authenticated() ) ;
424 }
425 else
426 {
427 sendChallenge( next_challenge ) ;
428 }
429 }
430 else
431 {
432 sendChallenge( m_sasl->initialChallenge() ) ;
433 }
434 }
435 #endif
436
437 void GSmtp::ServerProtocol::sendAuthDone( bool ok )
438 {
439 if( ok )
440 send( "235 Authentication sucessful" ) ;
441 else
442 send( "535 Authentication failed" ) ;
443 }
444
445 #ifndef USE_NO_AUTH
446 void GSmtp::ServerProtocol::doAuthData( const std::string & line , bool & predicate )
447 {
448 G_LOG( "GSmtp::ServerProtocol: rx<<: [authentication response not logged]" ) ;
449 if( line == "*" )
450 {
451 predicate = false ; // => idle
452 send( "501 authentication cancelled" ) ;
453 }
454 else if( ! G::Base64::valid(line) )
455 {
456 G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of authentication response" ) ;
457 predicate = false ; // => idle
458 sendAuthDone( false ) ;
459 }
460 else
461 {
462 bool done = false ;
463 std::string next_challenge = m_sasl->apply( G::Base64::decode(line) , done ) ;
464 if( done )
465 {
466 predicate = false ; // => idle
467 m_authenticated = m_sasl->authenticated() ;
468 sendAuthDone( m_sasl->authenticated() ) ;
469 }
470 else
471 {
472 sendChallenge( next_challenge ) ;
473 }
474 }
475 }
476 #endif
477
478 void GSmtp::ServerProtocol::sendChallenge( const std::string & s )
479 {
480 send( std::string("334 ") + G::Base64::encode(s,std::string()) ) ;
481 }
482
483 void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate )
484 {
485 if( m_sasl->active() && !m_authenticated && ( !m_sasl->trusted(m_peer_address) || sensitive() ) )
486 {
487 predicate = false ;
488 sendAuthRequired() ;
489 }
490 else
491 {
492 m_pmessage.clear() ;
493 std::pair<std::string,std::string> from_pair = parseFrom( line ) ;
494 bool ok = from_pair.second.empty() && m_pmessage.setFrom( from_pair.first ) ;
495 predicate = ok ;
496 if( ok )
497 {
498 sendMailReply() ;
499 }
500 else
501 {
502 sendBadFrom( from_pair.second ) ;
503 }
504 }
505 }
506
507 void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate )
508 {
509 std::pair<std::string,std::string> to_pair = parseTo( line ) ;
510 std::string reason = to_pair.second ;
511 bool ok = reason.empty() ;
512
513 if( ok )
514 {
515 verify( to_pair.first , m_pmessage.from() ) ;
516 }
517 else
518 {
519 predicate = false ;
520 sendBadTo( reason , false ) ;
521 }
522 }
523
524 void GSmtp::ServerProtocol::doVrfyToReply( const std::string & line , bool & predicate )
525 {
526 std::string to ;
527 VerifierStatus status = VerifierStatus::parse( line , to ) ;
528
529 bool ok = m_pmessage.addTo( to , status ) ;
530 if( ok )
531 {
532 sendRcptReply() ;
533 }
534 else
535 {
536 predicate = false ;
537 sendBadTo( G::Str::printable(status.reason) , status.temporary ) ;
538 }
539 }
540
541 void GSmtp::ServerProtocol::doUnknown( const std::string & line , bool & )
542 {
543 sendUnrecognised( line ) ;
544 }
545
546 void GSmtp::ServerProtocol::reset()
547 {
548 cancelTimer() ;
549 m_pmessage.clear() ;
550 m_verifier.reset() ;
551 m_bad_client_count = 0U ;
552 }
553
554 void GSmtp::ServerProtocol::doRset( const std::string & , bool & )
555 {
556 reset() ;
557 m_pmessage.reset() ;
558 sendRsetReply() ;
559 }
560
561 void GSmtp::ServerProtocol::doNoRecipients( const std::string & , bool & )
562 {
563 sendNoRecipients() ;
564 }
565
566 void GSmtp::ServerProtocol::doData( const std::string & , bool & )
567 {
568 std::string received_line = m_text.received(m_smtp_peer_name) ;
569 if( received_line.length() )
570 m_pmessage.addReceived( received_line ) ;
571
572 sendDataReply() ;
573 }
574
575 void GSmtp::ServerProtocol::sendOutOfSequence( const std::string & )
576 {
577 send( "503 command out of sequence -- use RSET to resynchronise" ) ;
578 badClientEvent() ;
579 }
580
581 void GSmtp::ServerProtocol::sendMissingParameter()
582 {
583 send( "501 parameter required" ) ;
584 }
585
586 bool GSmtp::ServerProtocol::isEndOfText( const std::string & line ) const
587 {
588 return line.length() == 1U && line[0U] == '.' ;
589 }
590
591 bool GSmtp::ServerProtocol::isEscaped( const std::string & line ) const
592 {
593 return line.length() > 1U && line[0U] == '.' ;
594 }
595
596 std::string GSmtp::ServerProtocol::commandWord( const std::string & line_in ) const
597 {
598 std::string line( line_in ) ;
599 G::Str::trimLeft( line , " \t" ) ;
600
601 size_t pos = line.find_first_of( " \t" ) ;
602 std::string command = line.substr( 0U , pos ) ;
603
604 G::Str::toUpper( command ) ;
605 return command ;
606 }
607
608 std::string GSmtp::ServerProtocol::commandLine( const std::string & line_in ) const
609 {
610 std::string line( line_in ) ;
611 G::Str::trimLeft( line , " \t" ) ;
612 return line ;
613 }
614
615 GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command ) const
616 {
617 if( command == "QUIT" ) return eQuit ;
618 if( command == "HELO" ) return eHelo ;
619 if( command == "EHLO" ) return eEhlo ;
620 if( command == "RSET" ) return eRset ;
621 if( command == "DATA" ) return eData ;
622 if( command == "RCPT" ) return eRcpt ;
623 if( command == "MAIL" ) return eMail ;
624 if( command == "VRFY" ) return eVrfy ;
625 if( command == "NOOP" ) return eNoop ;
626 if( command == "EXPN" ) return eExpn ;
627 if( command == "HELP" ) return eHelp ;
628 if( command == "STARTTLS" ) return eStartTls ;
629 if( m_sasl->active() && command == "AUTH" ) return eAuth ;
630 return eUnknown ;
631 }
632
633 void GSmtp::ServerProtocol::sendClosing()
634 {
635 send( "221 closing connection" ) ;
636 }
637
638 void GSmtp::ServerProtocol::sendVerified( const std::string & user )
639 {
640 send( std::string("250 ") + user ) ;
641 }
642
643 void GSmtp::ServerProtocol::sendNotVerified( const std::string & user , bool temporary )
644 {
645 send( std::string() + (temporary?"450":"550") + " no such mailbox: " + G::Str::printable(user) ) ;
646 }
647
648 void GSmtp::ServerProtocol::sendWillAccept( const std::string & user )
649 {
650 send( std::string("252 cannot verify but will accept: ") + G::Str::printable(user) ) ;
651 }
652
653 void GSmtp::ServerProtocol::sendUnrecognised( const std::string & line )
654 {
655 send( "500 command unrecognized: \"" + G::Str::printable(line) + std::string("\"") ) ;
656 badClientEvent() ;
657 }
658
659 void GSmtp::ServerProtocol::sendNotImplemented()
660 {
661 send( "502 command not implemented" ) ;
662 }
663
664 void GSmtp::ServerProtocol::sendAuthRequired()
665 {
666 std::string more_help = sensitive() ? ": use starttls" : "" ;
667 send( std::string() + "530 authentication required" + more_help ) ;
668 }
669
670 void GSmtp::ServerProtocol::sendNoRecipients()
671 {
672 send( "554 no valid recipients" ) ;
673 }
674
675 void GSmtp::ServerProtocol::sendTooBig( bool disconnecting )
676 {
677 send( disconnecting ? "554 message too big, disconnecting" : "554 message too big" ) ;
678 }
679
680 void GSmtp::ServerProtocol::sendDataReply()
681 {
682 send( "354 start mail input -- end with <CRLF>.<CRLF>" ) ;
683 }
684
685 void GSmtp::ServerProtocol::sendRsetReply()
686 {
687 send( "250 state reset" ) ;
688 }
689
690 void GSmtp::ServerProtocol::sendMailReply()
691 {
692 sendOk() ;
693 }
694
695 void GSmtp::ServerProtocol::sendCompletionReply( bool ok , const std::string & reason )
696 {
697 if( ok )
698 sendOk() ;
699 else
700 send( std::string("452 message processing failed: ") + reason ) ;
701 }
702
703 void GSmtp::ServerProtocol::sendRcptReply()
704 {
705 sendOk() ;
706 }
707
708 void GSmtp::ServerProtocol::sendBadFrom( std::string reason )
709 {
710 std::string msg("553 mailbox name not allowed") ;
711 if( ! reason.empty() )
712 {
713 msg.append( ": " ) ;
714 msg.append( reason ) ;
715 }
716 send( msg ) ;
717 }
718
719 void GSmtp::ServerProtocol::sendBadTo( const std::string & text , bool temporary )
720 {
721 send( std::string() + (temporary?"450":"550") + " mailbox unavailable: " + text ) ;
722 }
723
724 void GSmtp::ServerProtocol::sendEhloReply()
725 {
726 std::ostringstream ss ;
727 ss << "250-" << m_text.hello(m_smtp_peer_name) << crlf() ;
728
729 // dont advertise authentication if the sasl server implementation does not like plaintext dialogs
730 if( m_sasl->active() && !sensitive() )
731 ss << "250-AUTH " << m_sasl->mechanisms() << crlf() ;
732
733 if( m_with_ssl && !m_secure )
734 ss << "250-STARTTLS" << crlf() ;
735
736 if( m_with_vrfy )
737 ss << "250-VRFY" << crlf() ; // see RFC2821-3.5.2
738
739 ss << "250 8BITMIME" ;
740 send( ss.str() ) ;
741 }
742
743 void GSmtp::ServerProtocol::sendHeloReply()
744 {
745 sendOk() ;
746 }
747
748 void GSmtp::ServerProtocol::sendOk()
749 {
750 send( "250 OK" ) ;
751 }
752
753 const std::string & GSmtp::ServerProtocol::crlf()
754 {
755 static const std::string s( "\015\012" ) ;
756 return s ;
757 }
758
759 void GSmtp::ServerProtocol::send( std::string line , bool go_secure )
760 {
761 G_LOG( "GSmtp::ServerProtocol: tx>>: \"" << G::Str::printable(line) << "\"" ) ;
762 line.append( crlf() ) ;
763 m_sender.protocolSend( line , go_secure ) ;
764 }
765
766 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseFrom( const std::string & line ) const
767 {
768 // eg. MAIL FROM:<me@localhost>
769 return parse( line ) ;
770 }
771
772 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseTo( const std::string & line ) const
773 {
774 // eg. RCPT TO:<@first.co.uk,@second.co.uk:you@final.co.uk>
775 // eg. RCPT TO:<Postmaster>
776 return parse( line ) ;
777 }
778
779 std::pair<std::string,std::string> GSmtp::ServerProtocol::parse( const std::string & line ) const
780 {
781 size_t start = line.find( '<' ) ;
782 size_t end = line.find( '>' ) ;
783 if( start == std::string::npos || end == std::string::npos || end < start )
784 {
785 std::string reason( "missing or invalid angle brackets in mailbox name" ) ;
786 return std::make_pair(std::string(),reason) ;
787 }
788
789 std::string s = line.substr( start + 1U , end - start - 1U ) ;
790 G::Str::trim( s , " \t" ) ;
791
792 // strip source route
793 if( s.length() > 0U && s.at(0U) == '@' )
794 {
795 size_t colon_pos = s.find( ':' ) ;
796 if( colon_pos == std::string::npos )
797 {
798 std::string reason( "invalid mailbox name: no colon after leading at character" ) ;
799 return std::make_pair(std::string(),reason) ;
800 }
801 s = s.substr( colon_pos + 1U ) ;
802 }
803
804 return std::make_pair(s,std::string()) ;
805 }
806
807 std::string GSmtp::ServerProtocol::parsePeerName( const std::string & line ) const
808 {
809 size_t pos = line.find_first_of( " \t" ) ;
810 if( pos == std::string::npos )
811 return std::string() ;
812
813 std::string smtp_peer_name = line.substr( pos + 1U ) ;
814 G::Str::trim( smtp_peer_name , " \t" ) ;
815 return smtp_peer_name ;
816 }
817
818 void GSmtp::ServerProtocol::badClientEvent()
819 {
820 m_bad_client_count++ ;
821 if( m_bad_client_limit && m_bad_client_count >= m_bad_client_limit )
822 {
823 std::string reason = "too many protocol errors from the client" ;
824 G_DEBUG( "GSmtp::ServerProtocol::badClientEvent: " << reason << ": dropping the connection" ) ;
825 throw ProtocolDone( reason ) ;
826 }
827 }
828
829 // ===
830
831 GSmtp::ServerProtocolText::ServerProtocolText( const std::string & ident , const std::string & thishost ,
832 const GNet::Address & peer_address , const std::string & peer_socket_name ) :
833 m_ident(ident) ,
834 m_thishost(thishost) ,
835 m_peer_address(peer_address) ,
836 m_peer_socket_name(peer_socket_name)
837 {
838 }
839
840 std::string GSmtp::ServerProtocolText::greeting() const
841 {
842 return m_thishost + " -- " + m_ident + " -- Service ready" ;
843 }
844
845 std::string GSmtp::ServerProtocolText::hello( const std::string & ) const
846 {
847 return m_thishost + " says hello" ;
848 }
849
850 std::string GSmtp::ServerProtocolText::received( const std::string & smtp_peer_name ) const
851 {
852 return receivedLine( smtp_peer_name , m_peer_address.displayString(false) , m_peer_socket_name , m_thishost ) ;
853 }
854
855 std::string GSmtp::ServerProtocolText::shortened( std::string prefix , std::string s )
856 {
857 // strip off the windows sid and ad domain
858 std::string::size_type pos = s.find_last_of( "=\\" ) ;
859 return
860 s.empty() || pos == std::string::npos || (pos+1U) == s.length() ?
861 std::string() :
862 ( prefix + G::Str::printable(s.substr(pos+1U),'^') ) ;
863 }
864
865 std::string GSmtp::ServerProtocolText::receivedLine( const std::string & smtp_peer_name ,
866 const std::string & peer_address , const std::string & peer_socket_name , const std::string & thishost )
867 {
868 const G::DateTime::EpochTime t = G::DateTime::now() ;
869 const G::DateTime::BrokenDownTime tm = G::DateTime::local(t) ;
870 const std::string zone = G::DateTime::offsetString(G::DateTime::offset(t)) ;
871 const G::Date date( tm ) ;
872 const G::Time time( tm ) ;
873
874 std::ostringstream ss ;
875 ss
876 << "Received: from " << smtp_peer_name
877 << " ("
878 << "[" << peer_address << "]"
879 << shortened( "," , peer_socket_name )
880 << ") by " << thishost << " with ESMTP ; "
881 << date.weekdayName(true) << ", "
882 << date.monthday() << " "
883 << date.monthName(true) << " "
884 << date.yyyy() << " "
885 << time.hhmmss(":") << " "
886 << zone ;
887 return ss.str() ;
888 }
889
890 // ===
891
892 GSmtp::ServerProtocol::Text::~Text()
893 {
894 }
895
896 // ===
897
898 GSmtp::ServerProtocol::Sender::~Sender()
899 {
900 }
901
902 // ===
903
904 GSmtp::ServerProtocol::Config::Config( bool b , unsigned int i ) :
905 with_vrfy(b) ,
906 preprocessor_timeout(i) ,
907 disconnect_on_overflow(true) // (too harsh?)
908 {
909 }
910
GSmtp::ServerProtocol::secure
void secure(const std::string &certificate)
To be called when the transport protocol goes into secure mode.
Definition: gserverprotocol.cpp:118
GSmtp::ServerProtocolText::ServerProtocolText
ServerProtocolText(const std::string &ident, const std::string &thishost, const GNet::Address &peer_address, const std::string &peer_socket_name)
Constructor.
Definition: gserverprotocol.cpp:831
GSmtp::ServerProtocol::Sender
An interface used by ServerProtocol to send protocol replies.
Definition: gserverprotocol.h:63
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime.cpp:34
GSsl::Library::instance
static Library * instance()
Returns a pointer to a library object, if any.
Definition: gssl_openssl.cpp:254
gssl.h
G::Base64::valid
static bool valid(const std::string &)
Returns true if the string can be decoded.
Definition: gbase64.cpp:166
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:507
GSmtp::ServerProtocol::onTimeout
virtual void onTimeout()
Final override from GNet::AbstractTimer.
Definition: gserverprotocol.cpp:214
G::DateTime::EpochTime
std::time_t EpochTime
Definition: gdatetime.h:41
G::Date
A date (dd/mm/yyyy) class.
Definition: gdate.h:40
G::Date::monthName
std::string monthName(bool brief=false) const
Returns the month as a string (in english).
Definition: gdate.cpp:170
GSmtp::ServerProtocol::onTimeoutException
virtual void onTimeoutException(std::exception &)
Final override from GNet::AbstractTimer.
Definition: gserverprotocol.cpp:222
GSmtp::ServerProtocol::Text
An interface used by ServerProtocol to provide response text strings.
Definition: gserverprotocol.h:70
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:71
GSmtp::ServerProtocolText::received
virtual std::string received(const std::string &smtp_peer_name_from_helo) const
Final override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:850
GSmtp::ServerProtocol::ServerProtocol
ServerProtocol(Sender &sender, Verifier &verifier, ProtocolMessage &pmessage, const GAuth::Secrets &secrets, Text &text, GNet::Address peer_address, const std::string &peer_socket_name, Config config)
Constructor.
Definition: gserverprotocol.cpp:35
gdate.h
G::DateTime::local
static BrokenDownTime local(EpochTime epoch_time)
Converts from epoch time to local broken-down-time.
Definition: gdatetime.cpp:63
G::Signal3::connect
void connect(Slot3< P1, P2, P3 > slot)
Definition: gslot.h:369
GNet::Address
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
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:44
G::slot
Slot0 slot(T &object, void(T::*fn)())
Part of the slot/signal system.
Definition: gslot.h:156
GSmtp::ServerProtocol::apply
void apply(const std::string &line)
Called on receipt of a string from the client.
Definition: gserverprotocol.cpp:150
md5::size_type
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
G::Time
A simple time-of-day (hh/mm/ss) class.
Definition: gtime.h:39
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, Strings &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:714
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:42
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:133
GSmtp::Verifier
An asynchronous interface that verifies recipient 'to' addresses.
Definition: gverifier.h:44
GSmtp::ServerProtocol::Text::~Text
virtual ~Text()
Definition: gserverprotocol.cpp:892
GSmtp::ServerProtocol::Config::Config
Config(bool, unsigned int)
Definition: gserverprotocol.cpp:904
GSmtp::Verifier::doneSignal
virtual G::Signal2< std::string, VerifierStatus > & doneSignal()=0
Returns a signal that is emit()ed when the verify() request is complete.
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:111
GSmtp::ServerProtocol::init
void init()
Starts the protocol. Use only once after construction.
Definition: gserverprotocol.cpp:107
gserverprotocol.h
gstr.h
gassert.h
GAuth::Secrets
A simple interface to a store of secrets as used in authentication.
Definition: gsecrets.h:44
G
Low-level classes.
GSmtp::ServerProtocol::~ServerProtocol
virtual ~ServerProtocol()
Destructor.
Definition: gserverprotocol.cpp:112
G_LOG
#define G_LOG(expr)
Definition: glog.h:98
G::Date::yyyy
std::string yyyy() const
Returns the year as a four-digit decimal string.
Definition: gdate.cpp:192
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_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
GSmtp::ProtocolMessage::doneSignal
virtual G::Signal3< bool, unsigned long, std::string > & doneSignal()=0
Returns a signal which is raised once process() has completed.
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:81
GSmtp::ServerProtocol::Config
A structure containing configuration parameters for ServerProtocol.
Definition: gserverprotocol.h:79
GSmtp::ProtocolMessage
An interface used by the ServerProtocol class to assemble and process an incoming message...
Definition: gprotocolmessage.h:64
gsaslserverfactory.h
GSsl::Library
A RAII class for initialising the underlying ssl library.
Definition: gssl.h:147
gbase64.h
GSsl::Library::enabled
bool enabled(bool for_serving=false) const
Returns true if this is a real and enabled ssl library.
Definition: gssl_openssl.cpp:259
G::StateMachine::addTransition
void addTransition(Event event, State from, State to, Action action)
Adds a transition.
Definition: gstatemachine.h:179
gsmtp.h
G::DateTime::BrokenDownTime
struct std::tm BrokenDownTime
Definition: gdatetime.h:43
G::Time::hhmmss
std::string hhmmss(const char *sep=NULL) const
Returns a hhmmss string.
Definition: gtime.cpp:80
GSmtp::ServerProtocol::Sender::~Sender
virtual ~Sender()
Definition: gserverprotocol.cpp:898
G::Base64::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gbase64.cpp:131
GAuth
SASL authentication classes.
GSmtp::ServerProtocolText::hello
virtual std::string hello(const std::string &smtp_peer_name_from_helo) const
Final override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:845
G::Base64::encode
static std::string encode(const std::string &s, const std::string &line_break)
Encodes the given string.
Definition: gbase64.cpp:68
GSmtp::ServerProtocolText::receivedLine
static std::string receivedLine(const std::string &smtp_peer_name_from_helo, const std::string &peer_address, const std::string &peer_socket_name, const std::string &thishost)
Returns a standard "Received:" line.
Definition: gserverprotocol.cpp:865
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107
GSmtp::ServerProtocolText::greeting
virtual std::string greeting() const
Final override from GSmtp::ServerProtocol::Text.
Definition: gserverprotocol.cpp:840
G::Str::toUpper
static void toUpper(std::string &s)
Replaces all lowercase characters in string 's' by uppercase characters.
Definition: gstr.cpp:422