Sovite.Core.SMTPHandler (sovite v0.2.0)

Copy Markdown View Source

The MTA's Sovite.SMTP.Server.Handler: relay control, restrictions, authentication, recipient checks and expansion, and durable spooling.

Recipients are checked at RCPT time:

  1. <Postmaster>, and postmaster@ and abuse@ any hosted domain, are always accepted (RFC 5321 §4.5.1, RFC 2142). A bare <Postmaster> is queued as postmaster@<server.hostname>.
  2. Domains this server handles (local, aliased, hosted, and relay; see Sovite.Core.Routing): the recipient is expanded (Sovite.Core.Recipients.expand/2); one that no alias matched must be a known user (Sovite.Core.Recipients.check/2), otherwise 550 5.1.1.
  3. Any other destination, including address literals: accepted only from smtp.trusted_networks or after AUTH, otherwise 554 5.7.1 Relay access denied. The default config trusts no one, so it is never an open relay.

Domains are compared case-insensitively. Address tricks such as user%remote@local or source routes do not relay: only the domain of the parsed mailbox counts.

The restriction chains (Sovite.Core.Restrictions) run at connect, EHLO, MAIL, RCPT, DATA, and at the end of the data, after the built-in checks of each stage; they can reject, but never permit what relay control or recipient validation refuses.

Rewriting

The sender is rewritten at MAIL (Sovite.Core.Rewrite.sender/2) and each recipient at RCPT, before expansion. routing.always_bcc and the BCC maps add recipients at DATA. Mail from trusted networks and authenticated clients also gets its header addresses rewritten.

Authentication

AUTH runs against the configured backend (auth.backend), see Sovite.SASL.Server and Sovite.SASL.Dovecot. Failed logins are counted per client address (per /64 for IPv6) in a Sovite.Abuse.Penalty; a banned address gets 454 4.7.0 to AUTH, and is turned away at connect on listeners that require authentication. Each failure is answered after auth.failure_delay, to slow down guessing.

An authenticated client may only use the sender addresses its login maps to, see Sovite.Core.SenderCheck.

Submission fixes

Messages from authenticated clients are fixed up as RFC 6409 §8 allows: a missing Date: or Message-ID: is added, and the header fields in submission.strip_headers are removed.

Loops

A message with more than smtp.max_hops Received: fields is refused at the end of the data with 554 5.4.6 (RFC 5321 §6.3): it is most likely going round in circles.

Queueing

The message is accepted with 250 only after Sovite.Queue.Spool has made it durable. A Received: header is added at the top. The queue manager given as :queue_manager is then told about it. A message a restriction put on hold goes to the hold queue instead; one it discarded is accepted and dropped.

Telemetry

  • [:sovite, :smtp, :message, :discarded] and [:sovite, :smtp, :message, :held] - %{}, %{session_id, queue_id, reason}
  • [:sovite, :routing, :expansion_error] - %{}, %{session_id, recipient, reason}

Summary

Functions

Handler options from the running configuration.

Functions

opts(config, queue_manager \\ nil, runtime \\ [])

@spec opts(Sovite.Core.Config.t(), GenServer.server() | nil, keyword()) :: map()

Handler options from the running configuration.

  • queue_manager - the Sovite.Core.QueueManager to notify about new messages, if any.
  • runtime - :repo (a Sovite.Core.Repo reference), :penalty (the name of the Sovite.Abuse.Penalty for failed logins), :require_auth (the listener requires authentication), and :resolver (for restrictions that look up domains).