Skip to main content

SmtpConnection

Struct SmtpConnection 

Source
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

Source

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).

Source

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.

Source

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).

Source

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:

  1. Client sends AUTH LOGIN\r\n
  2. Server sends 334 VXNlcm5hbWU6\r\n (base64 “Username:”)
  3. Client sends <base64(user)>\r\n
  4. Server sends 334 UGFzc3dvcmQ6\r\n (base64 “Password:”)
  5. Client sends <base64(pass)>\r\n
  6. Server sends 235 (success) or 535 (failure)

On failure, returns Error::Auth with the full SmtpResponse so callers can distinguish transient (454) from permanent (535) failures (RFC 4954 Section 4).

Source

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

Source

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.

Source

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

Source

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

Source

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.

Source

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).

Source

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).

Source

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).

Source

pub fn protocol(&self) -> Protocol

Return the connection protocol (SMTP or LMTP).

Source

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.

Source

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.

Source

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

Source

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.

Source

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.

Source

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.

Source

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

Source

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.

Source

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).

Source

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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub async fn quit(&self, timeout: Duration) -> Result<(), Error>

Gracefully close the connection — QUIT command (RFC 5321 Section 4.1.1.10).

Source

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

Source§

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.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more