dmarcator 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog.md +11 -0
- data/README.md +91 -4
- data/lib/dmarcator/query.rb +168 -22
- data/lib/dmarcator/record.rb +24 -9
- data/lib/dmarcator/resolver.rb +3 -1
- data/lib/dmarcator/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f4c6e25ca7a0fc2d77204c72ebb01906fce1ac4b537b1e011e505ed2938dda2b
|
|
4
|
+
data.tar.gz: 36f390d4c0e42c5e938df0345e9d412de2b09aed29da9ca83a424414090b88f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f3db5da3231a84613e3a938d3e294f01da16df7aee2e633ba321e0002a8278ec28755accb08a1db2e6c72f2d0424cb34ea019b3168b32872503f59dddb6eaa4
|
|
7
|
+
data.tar.gz: 28d72632daef6199571fd1cd461c112610ebd14bd45a03b48c6b9127f571e82f8431ad35331e4c05d52482b2b5d085bb973a51aa37a888ede498b8b7a85f4eac
|
data/ChangeLog.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
### 0.9.0 / 2026-10-29
|
|
2
|
+
|
|
3
|
+
* Add `DMARCator.txt_records`, which returns the TXT resource records instead of strings, so callers that cache lookups can read each record's `ttl`. `DMARCator.txt` is now defined in terms of it and behaves the same.
|
|
4
|
+
|
|
5
|
+
### 0.8.0 / 2026-10-29
|
|
6
|
+
|
|
7
|
+
* Add `DMARCator.txt`, which returns every TXT record at a name — unfiltered, in response order, with multi-chunk records joined. It accepts service labels such as `_dmarc` and `_domainkey` through a new relaxed `validate_txt_name`, and lets callers detect duplicate DMARC records that `dmarc` deliberately hides.
|
|
8
|
+
* Rename the lookup methods after what they return: `DMARCator.dmarc` (was `DMARCator.query`) and `DMARCator::Record.lookup` (was `DMARCator::Record.query`). The old names remain as deprecated aliases and emit no runtime warnings.
|
|
9
|
+
* Apply `DMARCATOR_DNS_TIMEOUTS` when the library builds a plaintext resolver, instead of on every lookup. An injected resolver now keeps its own configuration.
|
|
10
|
+
* Document the lookup APIs, bulk-scan guidance and deprecations in the README.
|
|
11
|
+
|
|
1
12
|
### 0.7.0 / 2026-10-29
|
|
2
13
|
|
|
3
14
|
* Plaintext DNS lookups now fail closed: a lookup failure raises `DMARCator::ResolutionError` instead of being reported as a domain without a DMARC record. Previously a timeout or resolver failure looked like no policy, which failed open. This is a behavior change and applies to every transport.
|
data/README.md
CHANGED
|
@@ -89,7 +89,7 @@ record.sp
|
|
|
89
89
|
Query the DMARC record for a domain:
|
|
90
90
|
|
|
91
91
|
```ruby
|
|
92
|
-
record = DMARCator::Record.
|
|
92
|
+
record = DMARCator::Record.lookup("twitter.com")
|
|
93
93
|
# => #<DMARCator::Record ... @p=:reject, @rua=[...], ...>
|
|
94
94
|
```
|
|
95
95
|
|
|
@@ -98,6 +98,85 @@ Domains must be ASCII (A-labels): pass punycode such as
|
|
|
98
98
|
`DMARCator::ResolutionError`; `nil` only means the domain genuinely has no
|
|
99
99
|
DMARC record.
|
|
100
100
|
|
|
101
|
+
## Lookup APIs
|
|
102
|
+
|
|
103
|
+
The lookup methods are named after what they return:
|
|
104
|
+
|
|
105
|
+
| Method | Returns | Cost |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `DMARCator.txt_records(name)` | `Array<Resolv::DNS::Resource::IN::TXT>` — the records themselves | one lookup |
|
|
108
|
+
| `DMARCator.txt(name)` | `Array<String>` — every TXT record at `name` | one lookup |
|
|
109
|
+
| `DMARCator.dmarc(domain)` | `String` — the DMARC record text, or `nil` | one lookup |
|
|
110
|
+
| `DMARCator::Record.lookup(domain)` | `Record` — parsed | one lookup + parse (~0.4 ms) |
|
|
111
|
+
| `DMARCator::Record.parse(text)` | `Record` — parsed from a string | parse only |
|
|
112
|
+
|
|
113
|
+
`DMARCator.txt` is the primitive: unfiltered, in response order, multi-chunk
|
|
114
|
+
records joined. It works for any TXT name, including `_dmarc.` and
|
|
115
|
+
`_domainkey.` labels, and leaves interpretation to the caller:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
DMARCator.txt("_dmarc.example.com")
|
|
119
|
+
# => ["v=DMARC1; p=reject; rua=mailto:dmarc@example.com"]
|
|
120
|
+
|
|
121
|
+
DMARCator.txt("example.com")
|
|
122
|
+
# => ["v=spf1 include:_spf.example.com -all", "google-site-verification=abc"]
|
|
123
|
+
|
|
124
|
+
DMARCator.txt("s1._domainkey.example.com")
|
|
125
|
+
# => []
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Because it returns every record, it is also how you detect misconfiguration
|
|
129
|
+
that `DMARCator.dmarc` deliberately hides per RFC 7489 §6.6.3:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
records = DMARCator.txt("_dmarc.#{domain}").select { |t| t.start_with?("v=DMARC1") }
|
|
133
|
+
case records.length
|
|
134
|
+
when 0 then :none
|
|
135
|
+
when 1 then :ok
|
|
136
|
+
else :duplicate # more than one DMARC record breaks enforcement
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use `DMARCator.txt_records` when you need the DNS TTL, for example to cache
|
|
141
|
+
lookups in a database:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
records = DMARCator.txt_records("_dmarc.#{domain}")
|
|
145
|
+
records.each do |record|
|
|
146
|
+
record.strings.join # the TXT value
|
|
147
|
+
record.ttl # seconds the answer may be cached
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
All of these accept an injected resolver, and an injected resolver keeps its
|
|
152
|
+
own configuration (timeouts are only applied to resolvers the library builds):
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
resolver = DMARCator::Resolver::DoH.new(url: "https://dns.example/dns-query")
|
|
156
|
+
DMARCator.txt("_dmarc.example.com", resolver: resolver)
|
|
157
|
+
DMARCator.txt_records("_dmarc.example.com", resolver: resolver)
|
|
158
|
+
DMARCator.dmarc("example.com", resolver: resolver)
|
|
159
|
+
DMARCator::Record.lookup("example.com", resolver: resolver)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Bulk lookups
|
|
163
|
+
|
|
164
|
+
`DMARCator.dmarc` is the cheapest DMARC path — one lookup, no parsing. For a
|
|
165
|
+
scan over many domains:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
resolver = DMARCator.default_resolver # build once, not per domain
|
|
169
|
+
domains.each do |domain|
|
|
170
|
+
record = DMARCator.dmarc(domain, resolver: resolver)
|
|
171
|
+
# parse only when you need the fields
|
|
172
|
+
end
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Notes for large scans: `Record.lookup` adds a parse per domain (~0.4 ms, far
|
|
176
|
+
more than the lookup on a fast resolver); `Resolv::DNS` does not lock per query,
|
|
177
|
+
so use one resolver per thread when parallelising; and DoT/DoH open a new TLS
|
|
178
|
+
connection per lookup, so plaintext is the better choice for bulk work.
|
|
179
|
+
|
|
101
180
|
## Strict parsing
|
|
102
181
|
|
|
103
182
|
By default, unknown tags and tags with invalid values are ignored. Pass
|
|
@@ -164,7 +243,7 @@ A resolver can also be built explicitly:
|
|
|
164
243
|
|
|
165
244
|
```ruby
|
|
166
245
|
resolver = DMARCator::Resolver::DoH.new(url: "https://dns.example/dns-query")
|
|
167
|
-
record = DMARCator::Record.
|
|
246
|
+
record = DMARCator::Record.lookup("example.com", resolver: resolver)
|
|
168
247
|
```
|
|
169
248
|
|
|
170
249
|
## Security
|
|
@@ -216,8 +295,12 @@ was built, so a gem can always be traced back to its source:
|
|
|
216
295
|
# 2. Commit the release.
|
|
217
296
|
git commit -am "Bump version to X.Y.Z"
|
|
218
297
|
|
|
219
|
-
# 3. Tag that commit before building anything
|
|
220
|
-
|
|
298
|
+
# 3. Tag that commit before building anything, with release notes in the
|
|
299
|
+
# tag message. Repeating -m adds blank-line-separated paragraphs.
|
|
300
|
+
git tag -a vX.Y.Z \
|
|
301
|
+
-m "dmarcator X.Y.Z" \
|
|
302
|
+
-m "Summary of the release, including behaviour changes and upgrade notes." \
|
|
303
|
+
-m "See ChangeLog.md for the itemized list."
|
|
221
304
|
|
|
222
305
|
# 4. Push the commit and its tags.
|
|
223
306
|
git push origin main --follow-tags
|
|
@@ -227,6 +310,10 @@ gem build dmarc.gemspec
|
|
|
227
310
|
gem push dmarcator-X.Y.Z.gem
|
|
228
311
|
```
|
|
229
312
|
|
|
313
|
+
The tag message is the release note: it is what `git show vX.Y.Z` prints, so
|
|
314
|
+
put the highlight summary and any upgrade guidance there. Write it before step
|
|
315
|
+
4, because a pushed tag message cannot be changed without force-pushing the tag.
|
|
316
|
+
|
|
230
317
|
Tag before publishing, never after: a tag without a published gem, or a gem
|
|
231
318
|
without a tag, makes the two hard to reconcile later.
|
|
232
319
|
|
data/lib/dmarcator/query.rb
CHANGED
|
@@ -17,8 +17,18 @@ module DMARCator
|
|
|
17
17
|
#
|
|
18
18
|
# DNS query timeouts in seconds, tried in order.
|
|
19
19
|
#
|
|
20
|
+
# Applied when the library builds a plaintext resolver. Timeouts on an
|
|
21
|
+
# injected resolver are left alone.
|
|
22
|
+
#
|
|
20
23
|
DNS_TIMEOUTS = [2, 4, 8].freeze
|
|
21
24
|
|
|
25
|
+
#
|
|
26
|
+
# The maximum length of a textual DNS name, in octets.
|
|
27
|
+
#
|
|
28
|
+
# @since 0.8.0
|
|
29
|
+
#
|
|
30
|
+
MAX_TXT_NAME_LENGTH = 253
|
|
31
|
+
|
|
22
32
|
#
|
|
23
33
|
# Determines the version of the loaded `resolv` gem, if available.
|
|
24
34
|
#
|
|
@@ -57,6 +67,10 @@ module DMARCator
|
|
|
57
67
|
#
|
|
58
68
|
# Validates a domain before using it in a DNS query.
|
|
59
69
|
#
|
|
70
|
+
# Rejects underscores and reserves room for the `_dmarc.` prefix, so this is
|
|
71
|
+
# the validator for registrable domains rather than arbitrary DNS names.
|
|
72
|
+
# Use {validate_txt_name} for service labels.
|
|
73
|
+
#
|
|
60
74
|
# @param [String] domain
|
|
61
75
|
# The domain to validate.
|
|
62
76
|
#
|
|
@@ -79,6 +93,37 @@ module DMARCator
|
|
|
79
93
|
domain
|
|
80
94
|
end
|
|
81
95
|
|
|
96
|
+
#
|
|
97
|
+
# Validates an arbitrary DNS name used for a TXT lookup.
|
|
98
|
+
#
|
|
99
|
+
# Unlike {validate_domain}, service labels such as `_dmarc` and `_domainkey`
|
|
100
|
+
# are allowed, so names may contain underscores. Length and label limits are
|
|
101
|
+
# still enforced so hostile input cannot reach the resolver unbounded.
|
|
102
|
+
#
|
|
103
|
+
# @param [String] name
|
|
104
|
+
# The DNS name to validate.
|
|
105
|
+
#
|
|
106
|
+
# @return [String]
|
|
107
|
+
# The validated name.
|
|
108
|
+
#
|
|
109
|
+
# @raise [ArgumentError]
|
|
110
|
+
# The name is not a syntactically valid DNS name.
|
|
111
|
+
#
|
|
112
|
+
# @since 0.8.0
|
|
113
|
+
#
|
|
114
|
+
def self.validate_txt_name(name)
|
|
115
|
+
unless name.is_a?(String) && !name.empty? &&
|
|
116
|
+
name.length <= MAX_TXT_NAME_LENGTH &&
|
|
117
|
+
!name.start_with?('.') &&
|
|
118
|
+
!name.include?('..') &&
|
|
119
|
+
name.match?(/\A[A-Za-z0-9._-]+\z/) &&
|
|
120
|
+
name.split('.').all? { |label| label.length <= 63 && !label.start_with?('-') && !label.end_with?('-') }
|
|
121
|
+
raise ArgumentError, "invalid TXT name: #{name.inspect}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
name
|
|
125
|
+
end
|
|
126
|
+
|
|
82
127
|
#
|
|
83
128
|
# Builds the resolver configured by the environment.
|
|
84
129
|
#
|
|
@@ -94,50 +139,151 @@ module DMARCator
|
|
|
94
139
|
end
|
|
95
140
|
|
|
96
141
|
#
|
|
97
|
-
#
|
|
142
|
+
# Looks up every TXT record published at a DNS name.
|
|
143
|
+
#
|
|
144
|
+
# Records are returned exactly as published: unfiltered, in response order,
|
|
145
|
+
# and with multi-chunk records joined. Interpreting them (SPF, DKIM, DMARC,
|
|
146
|
+
# domain verification) is left to the caller. Use this when the individual
|
|
147
|
+
# records matter, for example to detect that a domain publishes more than
|
|
148
|
+
# one DMARC record.
|
|
149
|
+
#
|
|
150
|
+
# @param [String] name
|
|
151
|
+
# The DNS name to look up, such as `example.com`, `_dmarc.example.com`, or
|
|
152
|
+
# `s1._domainkey.example.com`.
|
|
153
|
+
#
|
|
154
|
+
# @param [Resolv::DNS, Resolver::DoH, Resolver::DoT] resolver
|
|
155
|
+
# The resolver to use. When omitted, one is built from `DMARCATOR_DNS`.
|
|
156
|
+
# An injected resolver keeps its own configuration.
|
|
157
|
+
#
|
|
158
|
+
# @return [Array<String>]
|
|
159
|
+
# The TXT records, or an empty array when the name has none. NXDOMAIN and
|
|
160
|
+
# NODATA are indistinguishable here.
|
|
161
|
+
#
|
|
162
|
+
# @raise [ResolutionError]
|
|
163
|
+
# The lookup failed. A failure is never reported as an empty result, so a
|
|
164
|
+
# blocked or degraded resolver cannot be mistaken for an absent record.
|
|
165
|
+
#
|
|
166
|
+
# @raise [ArgumentError]
|
|
167
|
+
# The name is not a syntactically valid DNS name.
|
|
168
|
+
#
|
|
169
|
+
# @note Names must be ASCII: pass punycode (A-labels) rather than Unicode.
|
|
170
|
+
#
|
|
171
|
+
# @since 0.8.0
|
|
172
|
+
#
|
|
173
|
+
def self.txt(name, resolver: default_resolver)
|
|
174
|
+
txt_records(name, resolver: resolver).map { |record| record.strings.join }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
#
|
|
178
|
+
# Looks up every TXT record at a DNS name, returning the resource records
|
|
179
|
+
# themselves.
|
|
180
|
+
#
|
|
181
|
+
# Like {txt}, but without flattening to strings. The records expose
|
|
182
|
+
# `strings` and `ttl`, which matters when caching results: use
|
|
183
|
+
# `record.ttl` to decide how long an answer may be reused.
|
|
184
|
+
#
|
|
185
|
+
# @param [String] name
|
|
186
|
+
# The DNS name to look up.
|
|
187
|
+
#
|
|
188
|
+
# @param [Resolv::DNS, Resolver::DoH, Resolver::DoT] resolver
|
|
189
|
+
# The resolver to use. When omitted, one is built from `DMARCATOR_DNS`.
|
|
190
|
+
# An injected resolver keeps its own configuration.
|
|
191
|
+
#
|
|
192
|
+
# @return [Array<Resolv::DNS::Resource::IN::TXT>]
|
|
193
|
+
# The records, or an empty array when the name has none. NXDOMAIN and
|
|
194
|
+
# NODATA are indistinguishable here.
|
|
195
|
+
#
|
|
196
|
+
# @raise [ResolutionError]
|
|
197
|
+
# The lookup failed. A failure is never reported as an empty result.
|
|
198
|
+
#
|
|
199
|
+
# @raise [ArgumentError]
|
|
200
|
+
# The name is not a syntactically valid DNS name.
|
|
201
|
+
#
|
|
202
|
+
# @note Names must be ASCII: pass punycode (A-labels) rather than Unicode.
|
|
203
|
+
#
|
|
204
|
+
# @since 0.9.0
|
|
205
|
+
#
|
|
206
|
+
def self.txt_records(name, resolver: default_resolver)
|
|
207
|
+
lookup_txt(validate_txt_name(name), resolver: resolver)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
#
|
|
211
|
+
# Looks up the DMARC record text published for a domain.
|
|
212
|
+
#
|
|
213
|
+
# This is the lowest-cost DMARC path: one TXT lookup and no parsing. Use it
|
|
214
|
+
# for bulk scans, and call {Record.parse} only on records you care about.
|
|
98
215
|
#
|
|
99
216
|
# @param [String] domain
|
|
100
|
-
# The domain to
|
|
217
|
+
# The domain to look up DMARC for.
|
|
101
218
|
#
|
|
102
219
|
# @param [Resolv::DNS, Resolver::DoH, Resolver::DoT] resolver
|
|
103
|
-
# The resolver to use. When omitted, one is built from `DMARCATOR_DNS
|
|
104
|
-
# plaintext resolvers receive {DNS_TIMEOUTS}.
|
|
220
|
+
# The resolver to use. When omitted, one is built from `DMARCATOR_DNS`.
|
|
105
221
|
#
|
|
106
222
|
# @return [String, nil]
|
|
107
|
-
# The
|
|
223
|
+
# The DMARC record text, or `nil` when the domain publishes none or
|
|
224
|
+
# publishes more than one (per RFC 7489 §6.6.3). Use {txt} to see every
|
|
225
|
+
# record, including duplicates.
|
|
108
226
|
#
|
|
109
227
|
# @raise [ResolutionError]
|
|
110
|
-
# The lookup failed.
|
|
111
|
-
# blocked or degraded resolver cannot silently enforce no policy.
|
|
228
|
+
# The lookup failed.
|
|
112
229
|
#
|
|
113
|
-
# @
|
|
230
|
+
# @raise [ArgumentError]
|
|
231
|
+
# The domain is not a syntactically valid DNS name.
|
|
114
232
|
#
|
|
115
|
-
# @since 0.
|
|
233
|
+
# @since 0.8.0
|
|
116
234
|
#
|
|
117
|
-
def self.
|
|
118
|
-
validate_domain(domain)
|
|
235
|
+
def self.dmarc(domain, resolver: default_resolver)
|
|
236
|
+
name = "_dmarc.#{validate_domain(domain)}"
|
|
237
|
+
|
|
238
|
+
# Per RFC 7489 §6.6.3, use the TXT record beginning with "v=DMARC1".
|
|
239
|
+
# If multiple DMARC records exist, treat the domain as having none.
|
|
240
|
+
dmarc_records = lookup_txt(name, resolver: resolver).map do |record|
|
|
241
|
+
record.strings.join
|
|
242
|
+
end.select do |text|
|
|
243
|
+
text.start_with?('v=DMARC1')
|
|
244
|
+
end
|
|
119
245
|
|
|
120
|
-
|
|
246
|
+
dmarc_records.first if dmarc_records.one?
|
|
247
|
+
end
|
|
121
248
|
|
|
122
|
-
|
|
249
|
+
#
|
|
250
|
+
# @deprecated Use {dmarc}, which names what it returns. Kept for
|
|
251
|
+
# compatibility; it will be removed in a future release.
|
|
252
|
+
#
|
|
253
|
+
# @see dmarc
|
|
254
|
+
#
|
|
255
|
+
def self.query(domain,resolver=default_resolver)
|
|
256
|
+
dmarc(domain, resolver: resolver)
|
|
257
|
+
end
|
|
123
258
|
|
|
259
|
+
#
|
|
260
|
+
# Looks up the TXT records at an already-validated name.
|
|
261
|
+
#
|
|
262
|
+
# Shared by {txt}, {txt_records} and {dmarc} so they use one transport path.
|
|
263
|
+
#
|
|
264
|
+
# @param [String] name
|
|
265
|
+
#
|
|
266
|
+
# @param [Resolv::DNS, Resolver::DoH, Resolver::DoT] resolver
|
|
267
|
+
#
|
|
268
|
+
# @return [Array<Resolv::DNS::Resource::IN::TXT>]
|
|
269
|
+
#
|
|
270
|
+
# @raise [ResolutionError]
|
|
271
|
+
#
|
|
272
|
+
# @api private
|
|
273
|
+
#
|
|
274
|
+
def self.lookup_txt(name, resolver:)
|
|
124
275
|
# Resolv returns an empty result for NXDOMAIN and raises for timeouts and
|
|
125
276
|
# other failures, so any exception here is a transport failure, not an
|
|
126
277
|
# absent record.
|
|
127
278
|
begin
|
|
128
|
-
records = resolver.getresources(
|
|
279
|
+
records = resolver.getresources(name, Resolv::DNS::Resource::IN::TXT)
|
|
129
280
|
rescue Resolv::ResolvError => error
|
|
130
|
-
raise ResolutionError, "DNS lookup failed for #{
|
|
281
|
+
raise ResolutionError, "DNS TXT lookup failed for #{name}: #{error.message}"
|
|
131
282
|
end
|
|
132
283
|
|
|
133
|
-
|
|
134
|
-
# If multiple DMARC records exist, treat the domain as having none.
|
|
135
|
-
dmarc_records = records.map { |record| record.strings.join }
|
|
136
|
-
.select { |text| text.start_with?('v=DMARC1') }
|
|
137
|
-
|
|
138
|
-
return dmarc_records.first if dmarc_records.one?
|
|
139
|
-
nil
|
|
284
|
+
records
|
|
140
285
|
end
|
|
286
|
+
private_class_method :lookup_txt
|
|
141
287
|
end
|
|
142
288
|
|
|
143
289
|
if DMARCator.vulnerable_resolv?
|
data/lib/dmarcator/record.rb
CHANGED
|
@@ -312,31 +312,46 @@ module DMARCator
|
|
|
312
312
|
end
|
|
313
313
|
|
|
314
314
|
#
|
|
315
|
-
#
|
|
315
|
+
# Looks up and parses the DMARC record for a domain.
|
|
316
|
+
#
|
|
317
|
+
# Prefer {DMARCator.dmarc} for bulk work: this method adds a parse
|
|
318
|
+
# (roughly 0.4 ms) on top of the lookup.
|
|
316
319
|
#
|
|
317
320
|
# @param [String] domain
|
|
318
|
-
# The domain to
|
|
321
|
+
# The domain to look up DMARC for.
|
|
319
322
|
#
|
|
320
|
-
# @param [Resolv::DNS] resolver
|
|
321
|
-
# The resolver to use.
|
|
323
|
+
# @param [Resolv::DNS, DMARCator::Resolver::DoH, DMARCator::Resolver::DoT] resolver
|
|
324
|
+
# The resolver to use. When omitted, one is built from `DMARCATOR_DNS`.
|
|
322
325
|
#
|
|
323
326
|
# @return [Record, nil]
|
|
324
|
-
# The parsed DMARC record
|
|
325
|
-
# returned.
|
|
327
|
+
# The parsed DMARC record, or `nil` when the domain publishes none.
|
|
326
328
|
#
|
|
327
329
|
# @raise [InvalidRecord]
|
|
328
330
|
# The DMARC record could not be parsed.
|
|
329
331
|
#
|
|
330
|
-
# @
|
|
332
|
+
# @raise [DMARCator::ResolutionError]
|
|
333
|
+
# The lookup failed.
|
|
334
|
+
#
|
|
335
|
+
# @since 0.8.0
|
|
331
336
|
#
|
|
332
337
|
# @api public
|
|
333
338
|
#
|
|
334
|
-
def self.
|
|
335
|
-
if (dmarc = DMARCator.
|
|
339
|
+
def self.lookup(domain, resolver: DMARCator.default_resolver)
|
|
340
|
+
if (dmarc = DMARCator.dmarc(domain, resolver: resolver))
|
|
336
341
|
parse(dmarc)
|
|
337
342
|
end
|
|
338
343
|
end
|
|
339
344
|
|
|
345
|
+
#
|
|
346
|
+
# @deprecated Use {lookup}, which names what it returns. Kept for
|
|
347
|
+
# compatibility; it will be removed in a future release.
|
|
348
|
+
#
|
|
349
|
+
# @see lookup
|
|
350
|
+
#
|
|
351
|
+
def self.query(domain,resolver=DMARCator.default_resolver)
|
|
352
|
+
lookup(domain, resolver: resolver)
|
|
353
|
+
end
|
|
354
|
+
|
|
340
355
|
#
|
|
341
356
|
# Converts the record to a Hash.
|
|
342
357
|
#
|
data/lib/dmarcator/resolver.rb
CHANGED
data/lib/dmarcator/version.rb
CHANGED