Sovite.SPF.Record (sovite v0.6.0)

Copy Markdown View Source

Parses SPF records (RFC 7208 §4.5, §12).

iex> Sovite.SPF.Record.parse("v=spf1 ip4:192.0.2.0/24 -all")
{:ok, [{:pass, {:ip4, {192, 0, 2, 0}, 24}, "ip4:192.0.2.0/24"}, {:fail, :all, "-all"}]}

The whole record is parsed before it is evaluated, so a syntax error anywhere makes it unusable, as the RFC requires. Mechanism and modifier names are matched against fixed tables, so a record never creates atoms.

A record parses to a list of terms in record order:

  • directives, {qualifier, mechanism, text}, where text is the term as written;
  • {:redirect, domain_spec} and {:exp, domain_spec} modifiers.

Unknown modifiers are checked for syntax and then left out.

Summary

Types

IPv4 and IPv6 prefix lengths for a and mx.

What a matching directive returns.

Functions

Parses an SPF record into its terms.

Returns true if the TXT record text is an SPF record: it starts with v=spf1, followed by a space or nothing (RFC 7208 §4.5).

Types

cidr()

@type cidr() :: {0..32, 0..128}

IPv4 and IPv6 prefix lengths for a and mx.

directive()

@type directive() :: {qualifier(), mechanism(), String.t()}

mechanism()

@type mechanism() ::
  :all
  | {:include, Sovite.SPF.Macro.t()}
  | {:a, Sovite.SPF.Macro.t() | nil, cidr()}
  | {:mx, Sovite.SPF.Macro.t() | nil, cidr()}
  | {:ptr, Sovite.SPF.Macro.t() | nil}
  | {:ip4, :inet.ip4_address(), 0..32}
  | {:ip6, :inet.ip6_address(), 0..128}
  | {:exists, Sovite.SPF.Macro.t()}

modifier()

@type modifier() :: {:redirect, Sovite.SPF.Macro.t()} | {:exp, Sovite.SPF.Macro.t()}

qualifier()

@type qualifier() :: :pass | :fail | :softfail | :neutral

What a matching directive returns.

spf_term()

@type spf_term() :: directive() | modifier()

Functions

parse(record)

@spec parse(String.t()) :: {:ok, [spf_term()]} | {:error, String.t()}

Parses an SPF record into its terms.

iex> Sovite.SPF.Record.parse("v=spf1 a:%{d}/24//64 redirect=_spf.example.com")
{:ok,
 [
   {:pass, {:a, [{:macro, :d, nil, false, [], false}], {24, 64}}, "a:%{d}/24//64"},
   {:redirect, ["_spf.example.com"]}
 ]}
iex> Sovite.SPF.Record.parse("v=spf1 ip4:192.0.2.1/33")
{:error, ~s(invalid term "ip4:192.0.2.1/33")}

spf?(arg1)

@spec spf?(String.t()) :: boolean()

Returns true if the TXT record text is an SPF record: it starts with v=spf1, followed by a space or nothing (RFC 7208 §4.5).

iex> Sovite.SPF.Record.spf?("v=spf1 -all")
true
iex> Sovite.SPF.Record.spf?("v=spf10")
false