Greylisting decisions, in the style of postgrey.
Mail from an unknown triplet of client network, sender, and recipient is deferred the first time. Real servers retry after a while and are let through; much spam software never retries. Once a triplet has passed, it passes at once for as long as it keeps being used.
triplet = Greylist.triplet(ip, sender, recipient)
entry = MyApp.lookup(Greylist.key(triplet))
case Greylist.check(entry, DateTime.utc_now()) do
{:pass, entry} -> MyApp.store(entry) && accept()
{{:defer, seconds}, entry} -> MyApp.store(entry) && defer(seconds)
endFunctions are pure: the caller stores entries under key/1 wherever
it likes, and can delete those past their :expires_at.
The triplet is loosened so that retries still match: the client address is reduced to its /24 (IPv4) or /64 (IPv6) network, since big senders retry from other hosts, and the sender loses BATV tags and has the digits of its local part replaced, since bounce and VERP addresses change with every message.
Options
:delay- milliseconds a new triplet is deferred for. Defaults to five minutes.:retry_window- milliseconds a deferred triplet is remembered for; one that has not passed by then starts over. Defaults to two days.:max_age- milliseconds a passed triplet is remembered for after it was last seen. Defaults to 35 days.
Summary
Functions
Decides on mail for a triplet whose stored entry is entry, or nil
if there is none. Returns the decision and the entry to store.
Returns a fixed-size key for storing a triplet: 64 hex characters.
Returns the triplet for a client address, envelope sender, and recipient.
Types
@type decision() :: :pass | {:defer, pos_integer()}
@type entry() :: %{ first_seen: DateTime.t(), last_seen: DateTime.t(), passed_at: DateTime.t() | nil, expires_at: DateTime.t() }
Functions
@spec check(entry() | nil, DateTime.t(), keyword()) :: {decision(), entry()}
Decides on mail for a triplet whose stored entry is entry, or nil
if there is none. Returns the decision and the entry to store.
Returns a fixed-size key for storing a triplet: 64 hex characters.
@spec triplet(:inet.ip_address(), String.t(), String.t()) :: triplet()
Returns the triplet for a client address, envelope sender, and recipient.
iex> Sovite.Abuse.Greylist.triplet({192, 0, 2, 7}, "prvs=1234abcdef=Bounce-42@List.Example", "Bob@Example.com")
{"192.0.2.0/24", "bounce-#@list.example", "bob@example.com"}