SPF macro strings (RFC 7208 §7).
A macro string is parsed once with parse/2 and expanded with
expand/2 or expand_domain/2 against a context/0:
iex> {:ok, macro} = Sovite.SPF.Macro.parse("%{ir}.%{v}._spf.%{d2}")
iex> Sovite.SPF.Macro.expand(macro, %{
...> sender: "strong-bad@email.example.com",
...> domain: "email.example.com",
...> ip: {192, 0, 2, 3}
...> })
"3.2.0.192.in-addr._spf.example.com"Expansion is pure. The p macro needs a validated host name for the
client, which takes DNS lookups, so the caller looks it up when
uses_ptr?/1 says so and passes it as :ptr.
Summary
Types
The values macros expand to.
Where a macro string appears, which decides what it may contain.
A letter, digits (labels to keep), reverse, delimiters, URL-escape.
A parsed macro string.
Functions
Formats an address for the i macro: dotted decimal for IPv4, and
dot-separated nibbles for IPv6 (RFC 7208 §7.3).
Expands macro with the values in context.
Expands macro for use as a domain name: if the result is longer than
253 characters, labels are removed from the left until it fits
(RFC 7208 §7.3).
Parses a macro string of the given kind.
Returns true if macro uses the p macro, which needs a PTR lookup.
Types
@type context() :: %{ optional(:sender) => String.t(), optional(:domain) => String.t(), optional(:ip) => :inet.ip_address(), optional(:helo) => String.t() | nil, optional(:ptr) => String.t() | nil, optional(:receiver) => String.t() | nil, optional(:now) => integer() | nil }
The values macros expand to.
:sender-s, andlandofrom its local part and domain.:domain-d, the domain being checked.:ip-i,v, andc, the client address.:helo-h. Defaults to"unknown".:ptr-p, the client's validated host name. Defaults to"unknown".:receiver-r. Defaults to"unknown".:now-t, in Unix seconds. Defaults to the current time.
@type kind() :: :domain_spec | :explanation | :macro_string
Where a macro string appears, which decides what it may contain.
:domain_spec- a mechanism or modifier target. Must end like a domain name, and may not usec,r, ort.:explanation- the text of anexp=record. May contain spaces.:macro_string- the value of an unknown modifier.
@type macro() :: {:macro, atom(), pos_integer() | nil, boolean(), [String.t()], boolean()}
A letter, digits (labels to keep), reverse, delimiters, URL-escape.
A parsed macro string.
Functions
@spec dotted(:inet.ip_address()) :: String.t()
Formats an address for the i macro: dotted decimal for IPv4, and
dot-separated nibbles for IPv6 (RFC 7208 §7.3).
iex> Sovite.SPF.Macro.dotted({0x2001, 0xDB8, 0, 0, 0, 0, 0, 0xCB01})
"2.0.0.1.0.d.b.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.b.0.1"
Expands macro with the values in context.
iex> {:ok, macro} = Sovite.SPF.Macro.parse("%{l1r-}.%{O}", :macro_string)
iex> Sovite.SPF.Macro.expand(macro, %{sender: "strong-bad@email.example.com"})
"strong.email.example.com"
Expands macro for use as a domain name: if the result is longer than
253 characters, labels are removed from the left until it fits
(RFC 7208 §7.3).
Parses a macro string of the given kind.
iex> Sovite.SPF.Macro.parse("%{d}")
{:ok, [{:macro, :d, nil, false, [], false}]}
iex> Sovite.SPF.Macro.parse("%{c}.example.com")
{:error, "macro letter not allowed here"}
iex> Sovite.SPF.Macro.parse("%{c}", :explanation)
{:ok, [{:macro, :c, nil, false, [], false}]}
Returns true if macro uses the p macro, which needs a PTR lookup.