E-MailRelay
gsmtpserverprotocol.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gsmtpserverprotocol.h
19///
20
21#ifndef G_SMTP_SERVER_PROTOCOL_H
22#define G_SMTP_SERVER_PROTOCOL_H
23
24#include "gdef.h"
25#include "gprotocolmessage.h"
26#include "geventhandler.h"
27#include "gaddress.h"
28#include "gverifier.h"
29#include "gverifierstatus.h"
30#include "gsaslserver.h"
31#include "gsaslserversecrets.h"
32#include "gstatemachine.h"
33#include "gexception.h"
34#include <utility>
35#include <memory>
36
37namespace GSmtp
38{
39 class ServerProtocol ;
40 class ServerProtocolText ;
41}
42
43//| \class GSmtp::ServerProtocol
44/// Implements the SMTP server-side protocol.
45///
46/// Uses the ProtocolMessage class as its down-stream interface,
47/// used for assembling and processing the incoming email
48/// messages.
49///
50/// Uses the ServerProtocol::Sender as its "sideways" interface
51/// to talk back to the email-sending client.
52///
53/// \see GSmtp::ProtocolMessage, RFC-2821
54///
56{
57public:
58 G_EXCEPTION( ProtocolDone , "smtp protocol done" ) ;
59
60 class Sender /// An interface used by ServerProtocol to send protocol replies.
61 {
62 public:
63 virtual void protocolSend( const std::string & s , bool go_secure ) = 0 ;
64 ///< Called when the protocol class wants to send
65 ///< data down the socket.
66
67 virtual void protocolShutdown() = 0 ;
68 ///< Called on receipt of a quit command after the quit
69 ///< response has been sent allowing the socket to be
70 ///< shut down.
71
72 virtual ~Sender() = default ;
73 ///< Destructor.
74 } ;
75
76 class Text /// An interface used by ServerProtocol to provide response text strings.
77 {
78 public:
79 virtual std::string greeting() const = 0 ;
80 ///< Returns a system identifier for the initial greeting.
81
82 virtual std::string hello( const std::string & smtp_peer_name ) const = 0 ;
83 ///< Returns a hello response.
84
85 virtual std::string received( const std::string & smtp_peer_name , bool auth , bool secure ,
86 const std::string & protocol , const std::string & cipher ) const = 0 ;
87 ///< Returns a complete 'Received' line.
88
89 virtual ~Text() = default ;
90 ///< Destructor.
91 } ;
92
93 struct Config /// A structure containing configuration parameters for ServerProtocol.
94 {
95 bool with_vrfy{false} ;
96 unsigned int filter_timeout{0U} ;
97 std::size_t max_size{0U} ;
98 bool authentication_requires_encryption{false} ;
99 bool mail_requires_encryption{false} ;
100 bool disconnect_on_max_size{false} ;
101 bool tls_starttls{false} ;
102 bool tls_connection{false} ; // smtps
103 bool ignore_eager_quit{false} ;
104 bool allow_pipelining{false} ;
105
106 Config() ;
107 Config( bool with_vrfy , unsigned int filter_timeout , std::size_t max_size ,
108 bool authentication_requires_encryption ,
109 bool mail_requires_encryption ,
110 bool tls_starttls ,
111 bool tls_connection ) ;
112 Config & set_with_vrfy( bool = true ) ;
113 Config & set_filter_timeout( unsigned int ) ;
114 Config & set_max_size( std::size_t ) ;
115 Config & set_authentication_requires_encryption( bool = true ) ;
116 Config & set_mail_requires_encryption( bool = true ) ;
117 Config & set_disconnect_on_max_size( bool = true ) ;
118 Config & set_tls_starttls( bool = true ) ;
119 Config & set_tls_connection( bool = true ) ;
120 Config & set_ignore_eager_quit( bool = true ) ;
121 Config & set_allow_pipelining( bool = true ) ;
122 } ;
123
125 const GAuth::SaslServerSecrets & secrets , const std::string & sasl_server_config ,
126 Text & text , const GNet::Address & peer_address , const Config & config ) ;
127 ///< Constructor.
128 ///<
129 ///< The Verifier interface is used to verify recipient
130 ///< addresses. See GSmtp::Verifier.
131 ///<
132 ///< The ProtocolMessage interface is used to assemble and
133 ///< process an incoming message.
134 ///<
135 ///< The Sender interface is used to send protocol
136 ///< replies back to the client.
137 ///<
138 ///< The Text interface is used to get informational text
139 ///< for returning to the client.
140 ///<
141 ///< Exceptions thrown out of event-loop and timer callbacks
142 ///< are delivered to the given exception sink.
143 ///<
144 ///< All references are kept.
145
146 void init() ;
147 ///< Starts the protocol. Use only once after construction.
148 ///< The implementation uses the Sender interface to either
149 ///< send the plaintext SMTP greeting or start the TLS
150 ///< handshake.
151
152 virtual ~ServerProtocol() ;
153 ///< Destructor.
154
155 bool inDataState() const ;
156 ///< Returns true if currently in the data-transfer state.
157 ///< This can be used to enable the GNet::LineBuffer
158 ///< 'fragments' option.
159
160 bool halfDuplexBusy() const ;
161 ///< Returns true if the protocol has received a command
162 ///< but not yet sent a response. This can be used to
163 ///< enforce half-duplex operation on a badly-behaved
164 ///< client that is attempting to do command pipelining.
165
166 bool halfDuplexBusy( const char * , std::size_t ) const ;
167 ///< This overload is used for a newly-received network
168 ///< packet. If it returns true then the packet should be
169 ///< queued up and only apply()d after then next use
170 ///< of Sender::protocolSend().
171
172 bool apply( const char * line_data , std::size_t line_size , std::size_t eolsize , std::size_t linesize , char c0 ) ;
173 ///< Called on receipt of a line of text from the remote
174 ///< client. As an optimisation this can also be a
175 ///< GNet::LineBuffer line fragment iff this object is
176 ///< currently inDataState(). Returns true. Throws
177 ///< ProtocolDone at the end of the protocol.
178
179 void secure( const std::string & certificate , const std::string & protocol , const std::string & cipher ) ;
180 ///< To be called when the transport protocol goes
181 ///< into secure mode.
182
183private:
184 enum class Event
185 {
186 eQuit ,
187 eHelo ,
188 eEhlo ,
189 eRset ,
190 eNoop ,
191 eExpn ,
192 eData ,
193 eRcpt ,
194 eMail ,
195 eStartTls ,
196 eSecure ,
197 eVrfy ,
198 eVrfyReply ,
199 eHelp ,
200 eAuth ,
201 eAuthData ,
202 eContent ,
203 eEot ,
204 eDone ,
205 eUnknown
206 } ;
207 enum class State
208 {
209 sStart ,
210 sEnd ,
211 sIdle ,
212 sGotMail ,
213 sGotRcpt ,
214 sVrfyStart ,
215 sVrfyIdle ,
216 sVrfyGotMail ,
217 sVrfyGotRcpt ,
218 sVrfyTo1 ,
219 sVrfyTo2 ,
220 sData ,
221 sProcessing ,
222 sAuth ,
223 sStartingTls ,
224 sDiscarding ,
225 s_Any ,
226 s_Same
227 } ;
228 struct EventData /// Contains GNet::LineBuffer callback parameters or a complete input line, passed through the G::StateMachine.
229 {
230 const char * ptr ;
231 std::size_t size ;
232 std::size_t eolsize ;
233 std::size_t linesize ;
234 char c0 ;
235
236 EventData( const char * ptr , std::size_t size ) ;
237 EventData( const char * ptr , std::size_t size , std::size_t eolsize , std::size_t linesize , char c0 ) ;
238 } ;
240
241public:
242 ServerProtocol( const ServerProtocol & ) = delete ;
243 ServerProtocol( ServerProtocol && ) = delete ;
244 void operator=( const ServerProtocol & ) = delete ;
245 void operator=( ServerProtocol && ) = delete ;
246
247private:
248 void send( const char * ) ;
249 void send( std::string , bool = false ) ;
250 Event commandEvent( const std::string & ) const ;
251 std::string commandWord( const std::string & line ) const ;
252 std::string commandLine( const std::string & line ) const ;
253 static const std::string & crlf() ;
254 bool authenticationRequiresEncryption() const ;
255 void reset() ;
256 void badClientEvent() ;
257 void processDone( bool , const MessageId & , const std::string & , const std::string & ) ; // ProtocolMessage::doneSignal()
258 void prepareDone( bool , bool , std::string ) ;
259 bool isEndOfText( const EventData & ) const ;
260 bool isEscaped( const EventData & ) const ;
261 void doNoop( EventData , bool & ) ;
262 void doIgnore( EventData , bool & ) ;
263 void doNothing( EventData , bool & ) ;
264 void doDiscarded( EventData , bool & ) ;
265 void doDiscard( EventData , bool & ) ;
266 void doHelp( EventData , bool & ) ;
267 void doExpn( EventData , bool & ) ;
268 void doEagerQuit( EventData , bool & ) ;
269 void doQuit( EventData , bool & ) ;
270 void doEhlo( EventData , bool & ) ;
271 void doHelo( EventData , bool & ) ;
272 void sendReadyForTls() ;
273 void sendBadMechanism() ;
274 void doAuthInvalid( EventData , bool & ) ;
275 void doAuth( EventData , bool & ) ;
276 void doAuthData( EventData , bool & ) ;
277 void doMail( EventData , bool & ) ;
278 void doRcpt( EventData , bool & ) ;
279 void doUnknown( EventData , bool & ) ;
280 void doRset( EventData , bool & ) ;
281 void doData( EventData , bool & ) ;
282 void doContent( EventData , bool & ) ;
283 void doComplete( EventData , bool & ) ;
284 void doEot( EventData , bool & ) ;
285 void doVrfy( EventData , bool & ) ;
286 void doVrfyReply( EventData , bool & ) ;
287 void doVrfyToReply( EventData , bool & ) ;
288 void doNoRecipients( EventData , bool & ) ;
289 void doStartTls( EventData , bool & ) ;
290 void doSecure( EventData , bool & ) ;
291 void doSecureGreeting( EventData , bool & ) ;
292 void verifyDone( const VerifierStatus & ) ;
293 void sendBadFrom( const std::string & ) ;
294 void sendTooBig( bool disconnecting = false ) ;
295 void sendChallenge( const std::string & ) ;
296 void sendBadTo( const std::string & , bool ) ;
297 void sendOutOfSequence() ;
298 void sendGreeting( const std::string & ) ;
299 void sendQuitOk() ;
300 void sendClosing() ;
301 void sendUnrecognised( const std::string & ) ;
302 void sendNotImplemented() ;
303 void sendHeloReply() ;
304 void sendEhloReply() ;
305 void sendRsetReply() ;
306 void sendMailReply() ;
307 void sendRcptReply() ;
308 void sendDataReply() ;
309 void sendCompletionReply( bool ok , const std::string & ) ;
310 void sendInvalidArgument() ;
311 void sendAuthenticationCancelled() ;
312 void sendAuthRequired() ;
313 void sendEncryptionRequired() ;
314 void sendNoRecipients() ;
315 void sendMissingParameter() ;
316 void sendVerified( const std::string & ) ;
317 void sendNotVerified( const std::string & , bool ) ;
318 void sendWillAccept( const std::string & ) ;
319 void sendAuthDone( bool ok ) ;
320 void sendOk() ;
321 std::pair<std::string,std::string> parseAddress( const std::string & ) const ;
322 std::pair<std::string,std::string> parseMailFrom( const std::string & ) const ;
323 std::string parseMailParameter( const std::string & , const std::string & ) const ;
324 std::size_t parseMailSize( const std::string & ) const ;
325 std::string parseMailAuth( const std::string & ) const ;
326 std::pair<std::string,std::string> parseRcptTo( const std::string & ) const ;
327 std::string parseRcptParameter( const std::string & ) const ;
328 std::string parsePeerName( const std::string & ) const ;
329 void verify( const std::string & , const std::string & ) ;
330
331private:
332 Sender & m_sender ;
333 Verifier & m_verifier ;
334 Text & m_text ;
335 ProtocolMessage & m_message ;
336 std::unique_ptr<GAuth::SaslServer> m_sasl ;
337 Config m_config ;
338 Fsm m_fsm ;
339 bool m_with_starttls ;
340 GNet::Address m_peer_address ;
341 bool m_secure ;
342 std::string m_certificate ;
343 std::string m_protocol ;
344 std::string m_cipher ;
345 unsigned int m_bad_client_count ;
346 unsigned int m_bad_client_limit ;
347 std::string m_session_peer_name ;
348 bool m_session_authenticated ;
349 std::string m_buffer ;
350} ;
351
352//| \class GSmtp::ServerProtocolText
353/// A default implementation for the
354/// ServerProtocol::Text interface.
355///
357{
358public:
359 ServerProtocolText( const std::string & code_ident , const std::string & thishost ,
360 const GNet::Address & peer_address ) ;
361 ///< Constructor.
362
363 static std::string receivedLine( const std::string & smtp_peer_name_from_helo ,
364 const std::string & peer_address , const std::string & thishost ,
365 bool authenticated , bool secure , const std::string & secure_protocol ,
366 const std::string & secure_cipher ) ;
367 ///< Returns a standard "Received:" line.
368
369public:
370 ~ServerProtocolText() override = default ;
371 ServerProtocolText( const ServerProtocolText & ) = delete ;
373 void operator=( const ServerProtocolText & ) = delete ;
374 void operator=( ServerProtocolText && ) = delete ;
375
376private: // overrides
377 std::string greeting() const override ; // Override from GSmtp::ServerProtocol::Text.
378 std::string hello( const std::string & smtp_peer_name_from_helo ) const override ; // Override from GSmtp::ServerProtocol::Text.
379 std::string received( const std::string & , bool , bool , const std::string & , const std::string & ) const override ; // Override from GSmtp::ServerProtocol::Text.
380
381private:
382 std::string m_code_ident ;
383 std::string m_thishost ;
384 GNet::Address m_peer_address ;
385} ;
386
387inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_with_vrfy( bool b ) { with_vrfy = b ; return *this ; }
388inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_filter_timeout( unsigned int t ) { filter_timeout = t ; return *this ; }
389inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_max_size( std::size_t n ) { max_size = n ; return *this ; }
390inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_authentication_requires_encryption( bool b ) { authentication_requires_encryption = b ; return *this ; }
391inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_mail_requires_encryption( bool b ) { mail_requires_encryption = b ; return *this ; }
392inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_disconnect_on_max_size( bool b ) { disconnect_on_max_size = b ; return *this ; }
393inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_tls_starttls( bool b ) { tls_starttls = b ; return *this ; }
394inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_tls_connection( bool b ) { tls_connection = b ; return *this ; }
395inline GSmtp::ServerProtocol::Config & GSmtp::ServerProtocol::Config::set_allow_pipelining( bool b ) { allow_pipelining = b ; return *this ; }
396
397#endif
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
An interface used by the ServerProtocol class to assemble and process an incoming message.
A default implementation for the ServerProtocol::Text interface.
ServerProtocolText(const std::string &code_ident, const std::string &thishost, const GNet::Address &peer_address)
Constructor.
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, const std::string &secure_protocol, const std::string &secure_cipher)
Returns a standard "Received:" line.
An interface used by ServerProtocol to send protocol replies.
virtual ~Sender()=default
Destructor.
virtual void protocolShutdown()=0
Called on receipt of a quit command after the quit response has been sent allowing the socket to be s...
virtual void protocolSend(const std::string &s, bool go_secure)=0
Called when the protocol class wants to send data down the socket.
An interface used by ServerProtocol to provide response text strings.
virtual std::string greeting() const =0
Returns a system identifier for the initial greeting.
virtual std::string received(const std::string &smtp_peer_name, bool auth, bool secure, const std::string &protocol, const std::string &cipher) const =0
Returns a complete 'Received' line.
virtual ~Text()=default
Destructor.
virtual std::string hello(const std::string &smtp_peer_name) const =0
Returns a hello response.
Implements the SMTP server-side protocol.
bool apply(const char *line_data, std::size_t line_size, std::size_t eolsize, std::size_t linesize, char c0)
Called on receipt of a line of text from the remote client.
void init()
Starts the protocol.
void secure(const std::string &certificate, const std::string &protocol, const std::string &cipher)
To be called when the transport protocol goes into secure mode.
bool inDataState() const
Returns true if currently in the data-transfer state.
ServerProtocol(Sender &, Verifier &, ProtocolMessage &, const GAuth::SaslServerSecrets &secrets, const std::string &sasl_server_config, Text &text, const GNet::Address &peer_address, const Config &config)
Constructor.
bool halfDuplexBusy() const
Returns true if the protocol has received a command but not yet sent a response.
virtual ~ServerProtocol()
Destructor.
An asynchronous interface that verifies recipient 'to' addresses.
Definition: gverifier.h:43
SMTP and message-store classes.
Definition: gadminserver.h:39
A structure containing configuration parameters for ServerProtocol.