Class: MailAuth::Dmarc::Record

Inherits:
Object
  • Object
show all
Defined in:
lib/mailauth/dmarc/record.rb

Overview

The policy a domain publishes at _dmarc., plus the one thing the text itself doesn't say: whether we had to walk up to the organizational domain to find it, which is what decides between p and sp.

Constant Summary collapse

DMARC1 =
/\Av=DMARC1\b/i
TAGS =
%w[ v p sp adkim aspf pct rua ruf fo rf ri ].freeze
POLICIES =
%w[ none quarantine reject ].freeze
STRICT =
"s".freeze
DEFAULT_PCT =
100

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text, organizational: false) ⇒ Record

Returns a new instance of Record.



47
48
49
50
# File 'lib/mailauth/dmarc/record.rb', line 47

def initialize(text, organizational: false)
  @text, @organizational = text, organizational
  @tags = parse(text)
end

Instance Attribute Details

#tagsObject (readonly)

Returns the value of attribute tags.



45
46
47
# File 'lib/mailauth/dmarc/record.rb', line 45

def tags
  @tags
end

Class Method Details

.find(domain, resolver:) ⇒ Object

DNS failures are raised rather than swallowed: "we couldn't ask" is a different answer from "there is nothing there".



18
19
20
# File 'lib/mailauth/dmarc/record.rb', line 18

def find(domain, resolver:)
  at(domain, resolver: resolver) || at_organizational_domain(domain, resolver: resolver)
end

Instance Method Details

#organizational?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/mailauth/dmarc/record.rb', line 86

def organizational?
  @organizational
end

#pctObject

Surfaced, never applied: rolling the pct dice here would make the same message pass and fail at random, which an authentication result must never do.



73
74
75
# File 'lib/mailauth/dmarc/record.rb', line 73

def pct
  Integer(tags["pct"], exception: false)&.clamp(0, 100) || DEFAULT_PCT
end

#policyObject

RFC 7489 §6.6.3: mail from a subdomain answers to the parent's sp, deliberately not the parent's own p — an organization can reject forgeries of unused hostnames while still easing its own mail through under p=none.



62
63
64
65
66
67
68
# File 'lib/mailauth/dmarc/record.rb', line 62

def policy
  if organizational? && subdomain_policy
    subdomain_policy
  else
    requested_policy
  end
end

#strict_dkim?Boolean

Both alignment tags default to relaxed, so only an explicit "s" is strict.

Returns:

  • (Boolean)


78
79
80
# File 'lib/mailauth/dmarc/record.rb', line 78

def strict_dkim?
  tags["adkim"]&.downcase == STRICT
end

#strict_spf?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/mailauth/dmarc/record.rb', line 82

def strict_spf?
  tags["aspf"]&.downcase == STRICT
end

#to_sObject



90
91
92
# File 'lib/mailauth/dmarc/record.rb', line 90

def to_s
  @text
end

#usable?Boolean

p is the one required tag beyond the version; without it the record may as well not exist.

Returns:

  • (Boolean)


54
55
56
# File 'lib/mailauth/dmarc/record.rb', line 54

def usable?
  !requested_policy.nil?
end