pub struct SmtpConnection { /* private fields */ }Expand description
An SMTP/LMTP client connection.
Manages a single TCP (or TLS) connection to an SMTP or LMTP server. The caller provides a timeout for each operation — there are no hardcoded defaults.
All methods take &self rather than &mut self. Mutable state (the
TCP stream, read buffer, and capabilities) is protected by an internal
tokio::sync::Mutex, so the connection can be shared across async
tasks via Arc<SmtpConnection> without an external lock. SMTP is a
serial protocol (RFC 5321 Section 3.1), so the mutex serialises
operations correctly.
Message construction is NOT this library’s responsibility. The caller provides fully-formed RFC 5322 message bytes.
Implementations§
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn auth_plain(
&self,
user: &str,
pass: &str,
timeout: Duration,
) -> Result<(), Error>
pub async fn auth_plain( &self, user: &str, pass: &str, timeout: Duration, ) -> Result<(), Error>
Authenticate with PLAIN mechanism (RFC 4616), omitting authzid
so the server derives the authorization identity from authcid.
On failure, returns Error::Auth with the full SmtpResponse
so callers can distinguish transient (454) from permanent (535)
failures (RFC 4954 Section 4).
If the server responds with a 334 challenge, the exchange is cancelled per RFC 4954 Section 6.
RFC 4954 Section 4 defines AUTH mechanism [initial-response], so
PLAIN may include its initial response directly on the AUTH command
line whenever it fits within SMTP’s command line limit. The client
falls back to the two-step exchange only when the command would
exceed the SMTP line-length limit (RFC 5321 Section 4.5.3.1.4).
Sourcepub async fn auth_plain_with_authzid(
&self,
authzid: &str,
user: &str,
pass: &str,
timeout: Duration,
) -> Result<(), Error>
pub async fn auth_plain_with_authzid( &self, authzid: &str, user: &str, pass: &str, timeout: Duration, ) -> Result<(), Error>
Authenticate with PLAIN mechanism (RFC 4616) using an explicit
authorization identity (authzid).
RFC 4616 Section 2: message = [authzid] UTF8NUL authcid UTF8NUL passwd.
Use this when the client needs to authenticate as one identity
(authcid) while requesting authorization as another (authzid).
The provided authzid must be non-empty and must not contain NUL,
because RFC 4616 defines authzid = 1*SAFE and uses NUL as the
field delimiter.
Sourcepub async fn auth_xoauth2(
&self,
user: &str,
token: &str,
timeout: Duration,
) -> Result<(), Error>
pub async fn auth_xoauth2( &self, user: &str, token: &str, timeout: Duration, ) -> Result<(), Error>
Authenticate with XOAUTH2 mechanism.
On failure, returns Error::Auth with the full SmtpResponse
so callers can distinguish transient (454) from permanent (535)
failures (RFC 4954 Section 4).
If the server responds with a 334 challenge, the exchange is cancelled per RFC 4954 Section 6.
RFC 4954 Section 4 defines AUTH mechanism [initial-response], so
XOAUTH2 may include its initial response directly on the AUTH command
line whenever it fits within SMTP’s command line limit. The client
falls back to the two-step exchange only when the command would
exceed the SMTP line-length limit (RFC 5321 Section 4.5.3.1.4).
Sourcepub async fn auth_login(
&self,
user: &str,
pass: &str,
timeout: Duration,
) -> Result<(), Error>
pub async fn auth_login( &self, user: &str, pass: &str, timeout: Duration, ) -> Result<(), Error>
Authenticate with LOGIN mechanism (draft-murchison-sasl-login).
AUTH LOGIN is a de-facto standard two-step challenge-response mechanism widely deployed by corporate and legacy servers. The SASL exchange follows the pattern in RFC 4954 Section 4:
- Client sends
AUTH LOGIN\r\n - Server sends
334 VXNlcm5hbWU6\r\n(base64 “Username:”) - Client sends
<base64(user)>\r\n - Server sends
334 UGFzc3dvcmQ6\r\n(base64 “Password:”) - Client sends
<base64(pass)>\r\n - Server sends
235(success) or535(failure)
On failure, returns Error::Auth with the full SmtpResponse
so callers can distinguish transient (454) from permanent (535)
failures (RFC 4954 Section 4).
Sourcepub async fn auth_oauthbearer(
&self,
token: &str,
timeout: Duration,
) -> Result<(), Error>
pub async fn auth_oauthbearer( &self, token: &str, timeout: Duration, ) -> Result<(), Error>
Authenticate with OAUTHBEARER mechanism (RFC 7628 Section 3.1).
OAUTHBEARER is the modern standard OAuth 2.0 bearer token SASL mechanism, used by Microsoft 365 and other providers.
SASL payload: n,,\x01auth=Bearer <token>\x01\x01
On failure, returns Error::Auth with the full SmtpResponse
so callers can distinguish transient (454) from permanent (535)
failures (RFC 4954 Section 4).
RFC 4954 Section 4 defines AUTH mechanism [initial-response], so
OAUTHBEARER may include its initial response directly on the AUTH
command line whenever it fits within SMTP’s command line limit. The
client falls back to the two-step exchange only when the command
would exceed the SMTP line-length limit (RFC 5321 Section 4.5.3.1.4).
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn send_bdat(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
params: Option<&MailFromParams>,
timeout: Duration,
) -> Result<SendResult, Error>
pub async fn send_bdat( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], params: Option<&MailFromParams>, timeout: Duration, ) -> Result<SendResult, Error>
Send a message using BDAT chunking (RFC 3030 Section 3).
Uses the BDAT command instead of DATA, which avoids dot-stuffing and allows transmission of binary content. The server must advertise the CHUNKING extension (RFC 3030).
Performs the full MAIL FROM / RCPT TO / BDAT sequence. The message is sent as a single BDAT chunk with the LAST flag.
Accepts optional MailFromParams to include ESMTP parameters
such as BODY=BINARYMIME (RFC 3030 Section 2) and SMTPUTF8
(RFC 6531 Section 3.4). The SIZE parameter is always included
automatically when the server advertises the SIZE extension
(RFC 1870 Section 3).
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
Returns Error::Protocol if the server does not support CHUNKING.
Sourcepub async fn send_bdat_with_all_params(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
mail_params: Option<&MailFromParams>,
rcpt_params: &[RcptToParams],
timeout: Duration,
) -> Result<SendResult, Error>
pub async fn send_bdat_with_all_params( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], mail_params: Option<&MailFromParams>, rcpt_params: &[RcptToParams], timeout: Duration, ) -> Result<SendResult, Error>
Send a message using BDAT chunking with both MAIL FROM and per-recipient RCPT TO parameters (RFC 3030 Section 3).
Like send_bdat but also accepts per-recipient
RcptToParams to include DSN parameters (NOTIFY, ORCPT) on each
RCPT TO command (RFC 3461 Sections 4.1–4.2).
rcpt_params must have the same length as recipients — each
entry is paired by index. Returns Error::Protocol if the
lengths do not match.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
The server must advertise CHUNKING (RFC 3030). Returns
Error::Protocol if the server does not support it.
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn set_ehlo_domain(&self, domain: &str) -> Result<(), Error>
pub async fn set_ehlo_domain(&self, domain: &str) -> Result<(), Error>
Set the domain sent in EHLO/LHLO (RFC 5321 Section 4.1.1.1).
The argument should be the client’s FQDN or, when no FQDN is available, an address literal (RFC 5321 Section 4.1.4).
Returns Error::Protocol if domain is empty or contains
non-printable-ASCII bytes (RFC 5321 Section 4.1.1.1).
This method acquires the internal mutex.
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn connect(
host: &str,
port: u16,
tls_mode: TlsMode,
timeout: Duration,
) -> Result<Self, Error>
pub async fn connect( host: &str, port: u16, tls_mode: TlsMode, timeout: Duration, ) -> Result<Self, Error>
Connect to an SMTP server and perform the initial EHLO handshake (RFC 5321 Section 3.1).
For TlsMode::Implicit, connects over TLS immediately (RFC 8314 Section 3).
For TlsMode::StartTls, connects in cleartext, performs EHLO, upgrades
via STARTTLS (RFC 3207), then performs a second EHLO.
Sourcepub async fn connect_with_tls_config(
host: &str,
port: u16,
tls_mode: TlsMode,
timeout: Duration,
tls_config: Arc<ClientConfig>,
) -> Result<Self, Error>
pub async fn connect_with_tls_config( host: &str, port: u16, tls_mode: TlsMode, timeout: Duration, tls_config: Arc<ClientConfig>, ) -> Result<Self, Error>
Connect to an SMTP server with a custom TLS configuration (RFC 5321 Section 3.1).
Sourcepub async fn connect_lmtp(
host: &str,
port: u16,
tls_mode: TlsMode,
timeout: Duration,
) -> Result<Self, Error>
pub async fn connect_lmtp( host: &str, port: u16, tls_mode: TlsMode, timeout: Duration, ) -> Result<Self, Error>
Connect to an LMTP server and perform the initial LHLO handshake (RFC 2033 Section 4.1).
LMTP uses LHLO instead of EHLO and returns per-recipient responses after DATA (RFC 2033 Section 4.2).
Sourcepub async fn connect_lmtp_with_tls_config(
host: &str,
port: u16,
tls_mode: TlsMode,
timeout: Duration,
tls_config: Arc<ClientConfig>,
) -> Result<Self, Error>
pub async fn connect_lmtp_with_tls_config( host: &str, port: u16, tls_mode: TlsMode, timeout: Duration, tls_config: Arc<ClientConfig>, ) -> Result<Self, Error>
Connect to an LMTP server with a custom TLS configuration (RFC 2033 Section 4.1).
Sourcepub async fn capabilities(&self) -> ServerCapabilities
pub async fn capabilities(&self) -> ServerCapabilities
Return a snapshot of the server’s advertised capabilities from the last EHLO/LHLO.
Callers should inspect capabilities before choosing protocol paths:
e.g., check ServerCapabilities::supports_chunking before using
send_bdat (RFC 3030), or
ServerCapabilities::supports_8bitmime before declaring
BODY=8BITMIME (RFC 1652).
This method acquires the internal mutex; the returned value is an owned clone so no borrow is held after the call returns.
Sourcepub async fn is_shutting_down(&self) -> bool
pub async fn is_shutting_down(&self) -> bool
Returns true if the server has sent a 421 response, indicating it
is shutting down the transmission channel (RFC 5321 Section 3.8).
Once this returns true, no further commands should be sent on this
connection — subsequent send attempts will fail immediately.
Sourcepub async fn is_authenticated(&self) -> bool
pub async fn is_authenticated(&self) -> bool
Returns true if authentication has been completed on this session
(RFC 4954 Section 3).
Useful for checking session state before attempting to send mail or issue additional AUTH commands.
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn send_lmtp(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
params: Option<&MailFromParams>,
timeout: Duration,
) -> Result<LmtpSendResult, Error>
pub async fn send_lmtp( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], params: Option<&MailFromParams>, timeout: Duration, ) -> Result<LmtpSendResult, Error>
Send a message via LMTP and return per-recipient results.
LMTP (RFC 2033 Section 4.2) differs from SMTP in that the server returns one response per accepted recipient after the DATA terminator, rather than a single aggregate response.
Accepts optional MailFromParams to include ESMTP parameters
such as BODY=8BITMIME (RFC 1652 Section 3) and SMTPUTF8
(RFC 6531 Section 3.4). The SIZE parameter is always included
automatically when the server advertises the SIZE extension
(RFC 1870 Section 3).
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
Returns Error::Protocol if this connection is not LMTP.
Sourcepub async fn send_lmtp_with_all_params(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
mail_params: Option<&MailFromParams>,
rcpt_params: &[RcptToParams],
timeout: Duration,
) -> Result<LmtpSendResult, Error>
pub async fn send_lmtp_with_all_params( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], mail_params: Option<&MailFromParams>, rcpt_params: &[RcptToParams], timeout: Duration, ) -> Result<LmtpSendResult, Error>
Send a message via LMTP with both MAIL FROM and per-recipient RCPT TO parameters, returning per-recipient results.
Like send_lmtp but also accepts per-recipient
RcptToParams to include DSN parameters (NOTIFY, ORCPT) on each
RCPT TO command (RFC 3461 Sections 4.1–4.2).
rcpt_params must have the same length as recipients — each
entry is paired by index. Returns Error::Protocol if the
lengths do not match.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
Returns Error::Protocol if this connection is not LMTP.
Sourcepub async fn send_lmtp_bdat(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
params: Option<&MailFromParams>,
timeout: Duration,
) -> Result<LmtpSendResult, Error>
pub async fn send_lmtp_bdat( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], params: Option<&MailFromParams>, timeout: Duration, ) -> Result<LmtpSendResult, Error>
Send a message via LMTP using BDAT chunking and return per-recipient results.
Combines LMTP per-recipient response handling (RFC 2033 Section 4.2) with BDAT chunking (RFC 3030 Section 3). Unlike DATA, BDAT does not require dot-stuffing, making it suitable for binary content.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
The server must advertise CHUNKING (RFC 3030). Returns
Error::Protocol if this is not an LMTP connection or if the server
does not support CHUNKING.
Sourcepub async fn send_lmtp_bdat_with_all_params(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
mail_params: Option<&MailFromParams>,
rcpt_params: &[RcptToParams],
timeout: Duration,
) -> Result<LmtpSendResult, Error>
pub async fn send_lmtp_bdat_with_all_params( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], mail_params: Option<&MailFromParams>, rcpt_params: &[RcptToParams], timeout: Duration, ) -> Result<LmtpSendResult, Error>
Send a message via LMTP using BDAT chunking with both MAIL FROM and per-recipient RCPT TO parameters, returning per-recipient results.
Like send_lmtp_bdat but also accepts
per-recipient RcptToParams to include DSN parameters (NOTIFY,
ORCPT) on each RCPT TO command (RFC 3461 Sections 4.1–4.2).
rcpt_params must have the same length as recipients — each
entry is paired by index. Returns Error::Protocol if the
lengths do not match.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
The server must advertise CHUNKING (RFC 3030). Returns
Error::Protocol if this is not an LMTP connection or if the
server does not support CHUNKING.
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn send(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
timeout: Duration,
) -> Result<SendResult, Error>
pub async fn send( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], timeout: Duration, ) -> Result<SendResult, Error>
Send a message to one or more recipients.
Performs the full MAIL FROM / RCPT TO / DATA sequence (RFC 5321 Section 3.3). When the server advertises PIPELINING (RFC 1854), commands are batched for better throughput.
Returns a SendResult containing any rejected recipients
(RFC 5321 Section 3.3). When some RCPT TO commands are rejected
but at least one succeeds, the message is delivered to the accepted
recipients and the rejected ones are listed in
SendResult::rejected_recipients.
BCC recipients should be included in recipients but must NOT appear
in the message headers — that is the caller’s responsibility.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
timeout applies to the overall send operation. Per RFC 5321 Section
4.5.3.2, the recommended DATA timeout is 600 seconds.
Sourcepub async fn send_with_params(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
params: Option<&MailFromParams>,
timeout: Duration,
) -> Result<SendResult, Error>
pub async fn send_with_params( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], params: Option<&MailFromParams>, timeout: Duration, ) -> Result<SendResult, Error>
Send a message with extended MAIL FROM parameters.
Like send but accepts optional MailFromParams
to include ESMTP parameters such as BODY= (RFC 1652 Section 3,
RFC 3030 Section 2) and SMTPUTF8 (RFC 6531 Section 3.4).
Returns a SendResult containing any rejected recipients
(RFC 5321 Section 3.3). When some RCPT TO commands are rejected
but at least one succeeds, the message is delivered to the accepted
recipients and the rejected ones are listed in
SendResult::rejected_recipients.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
The SIZE parameter is always included automatically when the server advertises the SIZE extension (RFC 1870 Section 3).
Sourcepub async fn send_with_all_params(
&self,
from: &ReversePath,
recipients: &[ForwardPath],
message: &[u8],
mail_params: Option<&MailFromParams>,
rcpt_params: &[RcptToParams],
timeout: Duration,
) -> Result<SendResult, Error>
pub async fn send_with_all_params( &self, from: &ReversePath, recipients: &[ForwardPath], message: &[u8], mail_params: Option<&MailFromParams>, rcpt_params: &[RcptToParams], timeout: Duration, ) -> Result<SendResult, Error>
Send a message with both MAIL FROM and per-recipient RCPT TO parameters.
Like send_with_params but also accepts
per-recipient RcptToParams to include DSN parameters (NOTIFY,
ORCPT) on each RCPT TO command (RFC 3461 Sections 4.1–4.2).
Returns a SendResult containing any rejected recipients
(RFC 5321 Section 3.3).
rcpt_params must have the same length as recipients — each
entry is paired by index. Returns Error::Protocol if the
lengths do not match.
Addresses are pre-validated by their type constructors
(ReversePath::new, ForwardPath::new) per RFC 5321
Section 4.1.2.
The SIZE parameter is always included automatically when the server advertises the SIZE extension (RFC 1870 Section 3).
Source§impl SmtpConnection
impl SmtpConnection
Sourcepub async fn reset(&self, timeout: Duration) -> Result<(), Error>
pub async fn reset(&self, timeout: Duration) -> Result<(), Error>
Reset the session — RSET command (RFC 5321 Section 4.1.1.5).
Aborts the current mail transaction, if any.
Sourcepub async fn noop(&self, timeout: Duration) -> Result<(), Error>
pub async fn noop(&self, timeout: Duration) -> Result<(), Error>
Send a NOOP command (RFC 5321 Section 4.1.1.9).
The NOOP command does not affect any state; the server replies
with 250 OK. Useful as a keep-alive probe to prevent idle
timeouts on long-lived connections.
Sourcepub async fn vrfy(
&self,
address: &str,
timeout: Duration,
) -> Result<SmtpResponse, Error>
pub async fn vrfy( &self, address: &str, timeout: Duration, ) -> Result<SmtpResponse, Error>
Verify a user or mailbox — VRFY command (RFC 5321 Section 4.1.1.6).
Returns the server’s response, which may be:
- 250/251: the user is verified (the response text contains the mailbox name)
- 252: cannot verify the user, but will accept messages and attempt delivery
- 502: VRFY not implemented (RFC 5321 Section 3.5.3 allows servers to disable this command)
- 550: user not found
Valid for both SMTP and LMTP connections. RFC 2033 Section 4.3
only prohibits HELO, EHLO, and TURN in LMTP; VRFY is “not
required, but SHOULD be used if possible.” If address contains
non-ASCII characters and the server advertised SMTPUTF8, this
method sends VRFY <arg> SMTPUTF8 per RFC 6531 Section 3.7.4.2.
Sourcepub async fn expn(
&self,
list_name: &str,
timeout: Duration,
) -> Result<SmtpResponse, Error>
pub async fn expn( &self, list_name: &str, timeout: Duration, ) -> Result<SmtpResponse, Error>
Expand a mailing list — EXPN command (RFC 5321 Section 4.1.1.7).
Returns the server’s response, which may be:
- 250: the list is expanded (multi-line response with members)
- 502: EXPN not implemented (RFC 5321 Section 3.5.3 allows servers to disable this command)
- 550: list not found
Valid for both SMTP and LMTP connections. RFC 2033 Section 4.3
only prohibits HELO, EHLO, and TURN in LMTP; EXPN is “not
required, but SHOULD be used if possible.” If list_name contains
non-ASCII characters and the server advertised SMTPUTF8, this
method sends EXPN <arg> SMTPUTF8 per RFC 6531 Section 3.7.4.2.
Sourcepub async fn help(
&self,
topic: Option<&str>,
timeout: Duration,
) -> Result<SmtpResponse, Error>
pub async fn help( &self, topic: Option<&str>, timeout: Duration, ) -> Result<SmtpResponse, Error>
Request human-readable help text from the server (RFC 5321 Section 4.1.1.8).
Returns the server’s response, which is typically:
- 211: system status or topic-specific help
- 214: general help text
- 502: HELP not implemented
The optional topic argument is encoded as SMTP String
(HELP SP String CRLF) when present. If omitted, the command is sent
as bare HELP.
Sourcepub async fn quit(&self, timeout: Duration) -> Result<(), Error>
pub async fn quit(&self, timeout: Duration) -> Result<(), Error>
Gracefully close the connection — QUIT command (RFC 5321 Section 4.1.1.10).
Sourcepub async fn rehlo(&self, timeout: Duration) -> Result<(), Error>
pub async fn rehlo(&self, timeout: Duration) -> Result<(), Error>
Re-issue the EHLO/LHLO greeting to refresh server capabilities (RFC 5321 Section 4.1.1.1).
“An EHLO command MAY be issued by a client later in the session.”
This is useful after set_ehlo_domain to
send the new domain to the server, or to refresh the capability
snapshot (e.g., after the server has been reconfigured).
Updates the internal ServerCapabilities with the server’s
fresh EHLO response.
Trait Implementations§
Source§impl Debug for SmtpConnection
impl Debug for SmtpConnection
Source§fn fmt(&self, f: &mut Formatter<'_>) -> Result
fn fmt(&self, f: &mut Formatter<'_>) -> Result
Prints connection metadata useful for logging and diagnostics, without exposing internal stream state or buffers.
Uses try_lock() to inspect the inner state without blocking.
If the mutex is currently held by another task, the transport
and capabilities fields show "<locked>" as a fallback.