Class: MailerToGo::SPF::Resolver
- Inherits:
-
Object
- Object
- MailerToGo::SPF::Resolver
- Defined in:
- lib/mailertogo/spf/resolver.rb
Overview
The DNS seam.
A resolver is anything that responds to #call(name) and returns:
[String, …] — the TXT strings published at that name
[] — the name exists but has no TXT (or does not exist at all):
a definitive "nothing here"
nil — DNS did not answer (timeout, SERVFAIL, refused):
inconclusive, verdict withheld
That three-way return is the whole contract, and the nil is the important part: a resolver hiccup must never be reported as "this domain does not authorise you". Everything in this gem funnels a nil into an :unknown result rather than a :fail.
A plain lambda satisfies the contract, which is how the test suite runs with no network at all:
zone = { "example.com" => ["v=spf1 include:_spf.mailertogo.net ~all"] }
MailerToGo::SPF.("example.com", resolver: ->(n) { zone.fetch(n, []) })
The default below uses Ruby's stdlib Resolv::DNS, so the gem has no runtime dependencies. If you already speak DNS-over-HTTPS (or hold a resolver pool, or want per-request caching), pass your own — see the README.
Constant Summary collapse
- DEFAULT_TIMEOUT =
3
Instance Method Summary collapse
- #call(name) ⇒ Object
-
#initialize(timeout: DEFAULT_TIMEOUT, nameservers: nil) ⇒ Resolver
constructor
timeout — seconds per nameserver attempt.
Constructor Details
#initialize(timeout: DEFAULT_TIMEOUT, nameservers: nil) ⇒ Resolver
timeout — seconds per nameserver attempt. nameservers — override the system resolvers, e.g. %w[1.1.1.1 8.8.8.8].
37 38 39 40 |
# File 'lib/mailertogo/spf/resolver.rb', line 37 def initialize(timeout: DEFAULT_TIMEOUT, nameservers: nil) @timeout = timeout @nameservers = nameservers end |
Instance Method Details
#call(name) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/mailertogo/spf/resolver.rb', line 42 def call(name) query = Record.normalize_name(name) return [] if query.empty? dns = @nameservers ? ::Resolv::DNS.new(nameserver: Array(@nameservers)) : ::Resolv::DNS.new dns.timeouts = @timeout begin dns.getresources(query, ::Resolv::DNS::Resource::IN::TXT).map { |r| r.strings.join } ensure dns.close end rescue ::Resolv::ResolvError # Resolv collapses NXDOMAIN and "no information" into one error, so this # is the conservative reading: the name published nothing. A resolver # that can see the rcode (DoH, for instance) should return nil for # SERVFAIL and [] only for NXDOMAIN/NODATA — see the README. [] rescue StandardError # Timeouts (Resolv::ResolvTimeout) and everything else unexpected: # inconclusive, never a failure. nil end |