Module: MtaSts

Defined in:
lib/mta_sts.rb,
lib/mta_sts/policy.rb,
lib/mta_sts/report.rb,
lib/mta_sts/result.rb,
lib/mta_sts/fetcher.rb,
lib/mta_sts/version.rb,
lib/mta_sts/report/result.rb

Defined Under Namespace

Classes: Fetcher, Policy, Report, Result

Constant Summary collapse

DISCOVERY_PREFIX =
"_mta-sts."
POLICY_HOST_PREFIX =
"mta-sts."
REPORTING_PREFIX =

RFC 8460 §3

"_smtp._tls."
STS_RECORD =

RFC 8461 §3.1 ABNF: sts-version = %s"v=STSv1", then sts-field-delim. The ABNF spells that delimiter *WSP ";" *WSP, so v=STSv1 ; id=... is grammatical — but §3.1's prose says to discard records that do not begin with "v=STSv1;", and the prose is what everyone else implements. Following the grammar here would admit records the rest of the internet drops, which is the wrong way to disagree with the RFC about itself.

/\Av=STSv1;/
FIELD_DELIM =
";"
ID_FIELD =
/\Aid=(.*)\z/
STS_ID =

RFC 8461 §3.1 ABNF: sts-id = 1*32(ALPHA / DIGIT)

/\A[a-zA-Z0-9]{1,32}\z/
VERSION =
"0.3.0"

Class Method Summary collapse

Class Method Details

.lookup(domain, known:, resolver: MailResolver::Resolver.new, fetcher: Fetcher.new, now: Time.now) ⇒ Object

known: is required so omitting it is a decision, not an accident (§5.1).



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/mta_sts.rb', line 28

def lookup(domain, known:, resolver: MailResolver::Resolver.new, fetcher: Fetcher.new, now: Time.now)
  domain = Policy.normalize(recipient_domain(domain))
  held = usable(known, domain, now)
  id = discover(domain, resolver)

  case
  when id == :unreachable    then unreachable(domain, held)
  when id == :absent         then absent(domain, held)
  when held && held.id == id then renewed(domain, held, now)
  else                            fetched(domain, id, held, fetcher, now)
  end
end

.reporting(domain, resolver: MailResolver::Resolver.new) ⇒ Object

RFC 8460 §3



42
43
44
45
46
47
48
49
50
# File 'lib/mta_sts.rb', line 42

def reporting(domain, resolver: MailResolver::Resolver.new)
  domain = Policy.normalize(recipient_domain(domain))

  case record = sole_record("#{REPORTING_PREFIX}#{domain}", matching: Report::REPORT_RECORD, resolver: resolver)
  when :unreachable then unanswered(domain)
  when :absent      then unpublished(domain)
  else                   reported(domain, record)
  end
end