dmarcator 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f09058293dac6f0e31294f06182d69502ee06bc152aa3eb2ef06c07515f4363b
4
- data.tar.gz: 6e3aee91c8c8d098c0f066a6b301ef9bc36f42018078769f15e65d695d35ba8a
3
+ metadata.gz: f4c6e25ca7a0fc2d77204c72ebb01906fce1ac4b537b1e011e505ed2938dda2b
4
+ data.tar.gz: 36f390d4c0e42c5e938df0345e9d412de2b09aed29da9ca83a424414090b88f1
5
5
  SHA512:
6
- metadata.gz: dd47e82f78219d828832c95912a84581ad589efaec940bc25749a0a38b49692805f9afd8030e0f0bd01b2b611dd05a36fec474a8beb9866be340ba9a2e327f39
7
- data.tar.gz: 973b12e73708b1540e912063db142ca91e07c5e29cca86e32481222e25334448b8efe754113bfeec6fae591c6411f8a82d796429c7ac36c26e4c22c8c3b714fa
6
+ metadata.gz: 8f3db5da3231a84613e3a938d3e294f01da16df7aee2e633ba321e0002a8278ec28755accb08a1db2e6c72f2d0424cb34ea019b3168b32872503f59dddb6eaa4
7
+ data.tar.gz: 28d72632daef6199571fd1cd461c112610ebd14bd45a03b48c6b9127f571e82f8431ad35331e4c05d52482b2b5d085bb973a51aa37a888ede498b8b7a85f4eac
data/ChangeLog.md CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  ### 0.8.0 / 2026-10-29
2
6
 
3
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.
data/README.md CHANGED
@@ -104,6 +104,7 @@ The lookup methods are named after what they return:
104
104
 
105
105
  | Method | Returns | Cost |
106
106
  |---|---|---|
107
+ | `DMARCator.txt_records(name)` | `Array<Resolv::DNS::Resource::IN::TXT>` — the records themselves | one lookup |
107
108
  | `DMARCator.txt(name)` | `Array<String>` — every TXT record at `name` | one lookup |
108
109
  | `DMARCator.dmarc(domain)` | `String` — the DMARC record text, or `nil` | one lookup |
109
110
  | `DMARCator::Record.lookup(domain)` | `Record` — parsed | one lookup + parse (~0.4 ms) |
@@ -136,13 +137,24 @@ else :duplicate # more than one DMARC record breaks enforcement
136
137
  end
137
138
  ```
138
139
 
139
- All three methods accept an injected resolver, and an injected resolver keeps
140
- its own configuration (timeouts are only applied to resolvers the library
141
- builds):
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):
142
153
 
143
154
  ```ruby
144
155
  resolver = DMARCator::Resolver::DoH.new(url: "https://dns.example/dns-query")
145
156
  DMARCator.txt("_dmarc.example.com", resolver: resolver)
157
+ DMARCator.txt_records("_dmarc.example.com", resolver: resolver)
146
158
  DMARCator.dmarc("example.com", resolver: resolver)
147
159
  DMARCator::Record.lookup("example.com", resolver: resolver)
148
160
  ```
@@ -171,6 +171,39 @@ module DMARCator
171
171
  # @since 0.8.0
172
172
  #
173
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)
174
207
  lookup_txt(validate_txt_name(name), resolver: resolver)
175
208
  end
176
209
 
@@ -204,7 +237,9 @@ module DMARCator
204
237
 
205
238
  # Per RFC 7489 §6.6.3, use the TXT record beginning with "v=DMARC1".
206
239
  # If multiple DMARC records exist, treat the domain as having none.
207
- dmarc_records = lookup_txt(name, resolver: resolver).select do |text|
240
+ dmarc_records = lookup_txt(name, resolver: resolver).map do |record|
241
+ record.strings.join
242
+ end.select do |text|
208
243
  text.start_with?('v=DMARC1')
209
244
  end
210
245
 
@@ -224,13 +259,13 @@ module DMARCator
224
259
  #
225
260
  # Looks up the TXT records at an already-validated name.
226
261
  #
227
- # Shared by {txt} and {dmarc} so both use one transport path.
262
+ # Shared by {txt}, {txt_records} and {dmarc} so they use one transport path.
228
263
  #
229
264
  # @param [String] name
230
265
  #
231
266
  # @param [Resolv::DNS, Resolver::DoH, Resolver::DoT] resolver
232
267
  #
233
- # @return [Array<String>]
268
+ # @return [Array<Resolv::DNS::Resource::IN::TXT>]
234
269
  #
235
270
  # @raise [ResolutionError]
236
271
  #
@@ -246,7 +281,7 @@ module DMARCator
246
281
  raise ResolutionError, "DNS TXT lookup failed for #{name}: #{error.message}"
247
282
  end
248
283
 
249
- records.map { |record| record.strings.join }
284
+ records
250
285
  end
251
286
  private_class_method :lookup_txt
252
287
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module DMARCator
4
4
  # dmarcator version
5
- VERSION = '0.8.0'
5
+ VERSION = '0.9.0'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dmarcator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Berman