dnsruby 1.73.1 → 2.0.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/.github/workflows/ci.yml +16 -4
- data/DNSSEC +3 -2
- data/EXAMPLES +1 -1
- data/README.md +370 -64
- data/RELEASE_NOTES.md +314 -1
- data/Rakefile +0 -3
- data/demo/axfr.rb +24 -6
- data/dnsruby.gemspec +6 -4
- data/lib/dnsruby/cache.rb +279 -49
- data/lib/dnsruby/code_mapper.rb +35 -8
- data/lib/dnsruby/code_mappers.rb +63 -49
- data/lib/dnsruby/config.rb +77 -4
- data/lib/dnsruby/deadline_queue.rb +75 -0
- data/lib/dnsruby/dnssec.rb +148 -45
- data/lib/dnsruby/ipv6.rb +45 -5
- data/lib/dnsruby/key_cache.rb +202 -35
- data/lib/dnsruby/message/decoder.rb +85 -19
- data/lib/dnsruby/message/encoder.rb +27 -16
- data/lib/dnsruby/message/header.rb +8 -12
- data/lib/dnsruby/message/message.rb +53 -22
- data/lib/dnsruby/message/section.rb +12 -34
- data/lib/dnsruby/name.rb +5 -2
- data/lib/dnsruby/packet_sender.rb +664 -207
- data/lib/dnsruby/presentation.rb +147 -0
- data/lib/dnsruby/query_control.rb +132 -0
- data/lib/dnsruby/recursor.rb +798 -341
- data/lib/dnsruby/resolver.rb +472 -176
- data/lib/dnsruby/resource/CAA.rb +67 -16
- data/lib/dnsruby/resource/DLV.rb +6 -0
- data/lib/dnsruby/resource/DNSKEY.rb +108 -16
- data/lib/dnsruby/resource/DS.rb +27 -28
- data/lib/dnsruby/resource/HTTPS.rb +16 -0
- data/lib/dnsruby/resource/IN.rb +21 -2
- data/lib/dnsruby/resource/MX.rb +2 -2
- data/lib/dnsruby/resource/NSEC.rb +6 -10
- data/lib/dnsruby/resource/NSEC3.rb +55 -19
- data/lib/dnsruby/resource/NSEC3PARAM.rb +4 -6
- data/lib/dnsruby/resource/OPT.rb +2 -3
- data/lib/dnsruby/resource/RR.rb +108 -65
- data/lib/dnsruby/resource/RRSIG.rb +5 -5
- data/lib/dnsruby/resource/RRSet.rb +38 -22
- data/lib/dnsruby/resource/SRV.rb +2 -2
- data/lib/dnsruby/resource/SVCB.rb +775 -0
- data/lib/dnsruby/resource/TSIG.rb +266 -231
- data/lib/dnsruby/resource/TXT.rb +34 -42
- data/lib/dnsruby/resource/domain_name.rb +1 -1
- data/lib/dnsruby/resource/generic.rb +31 -0
- data/lib/dnsruby/resource/generic_cache.rb +384 -0
- data/lib/dnsruby/resource/generic_constants.rb +79 -0
- data/lib/dnsruby/select_thread.rb +986 -291
- data/lib/dnsruby/serial_time.rb +25 -0
- data/lib/dnsruby/single_resolver.rb +25 -6
- data/lib/dnsruby/single_verifier.rb +1620 -530
- data/lib/dnsruby/transport_clock.rb +9 -0
- data/lib/dnsruby/update.rb +8 -6
- data/lib/dnsruby/validation_budget.rb +143 -0
- data/lib/dnsruby/validator_thread.rb +139 -58
- data/lib/dnsruby/version.rb +1 -1
- data/lib/dnsruby/zone_reader.rb +196 -129
- data/lib/dnsruby/zone_transfer.rb +157 -25
- data/lib/dnsruby.rb +7 -2
- data/test/ci_watchdog.rb +33 -0
- data/test/fixtures/org-ds-2026-11-05.zone +5 -0
- data/test/fixtures/root-dnskey-2026-11-05.zone +7 -0
- data/test/fork_test_helper.rb +37 -0
- data/test/local_dns_server.rb +57 -0
- data/test/soak_server.rb +152 -0
- data/test/socket_helpers.rb +26 -0
- data/test/spec_helper.rb +8 -13
- data/test/tc_attempt_configuration.rb +137 -0
- data/test/tc_caa.rb +2 -2
- data/test/tc_cache_keys.rb +36 -0
- data/test/tc_cancelled_send_capacity.rb +158 -0
- data/test/tc_dnssec_alias_validation.rb +289 -0
- data/test/tc_dnssec_budget.rb +489 -0
- data/test/tc_dnssec_cache_bounds.rb +197 -0
- data/test/tc_dnssec_cache_lifetime.rb +615 -0
- data/test/tc_dnssec_denial_proofs.rb +285 -0
- data/test/tc_dnssec_recursion_limits.rb +186 -0
- data/test/tc_dnssec_recursor_review_fixes.rb +351 -0
- data/test/tc_dnssec_trust_anchors.rb +286 -0
- data/test/tc_dnssec_trust_races.rb +147 -0
- data/test/tc_dnssec_wildcard_proofs.rb +249 -0
- data/test/tc_ds.rb +5 -5
- data/test/tc_edns_retry_regressions.rb +255 -0
- data/test/tc_escapedchars.rb +8 -0
- data/test/tc_final_review_fixes.rb +167 -0
- data/test/tc_final_transport_contracts.rb +104 -0
- data/test/tc_generic_cache.rb +852 -0
- data/test/tc_header.rb +26 -0
- data/test/tc_ipv6.rb +120 -0
- data/test/tc_key_cache_identity.rb +382 -0
- data/test/tc_malformed_tcp.rb +712 -0
- data/test/tc_name.rb +99 -5
- data/test/tc_nameserver_bootstrap.rb +322 -0
- data/test/tc_nsec.rb +4 -45
- data/test/tc_nsec3.rb +17 -1
- data/test/tc_pipeline_retirement.rb +358 -0
- data/test/tc_pipeline_retry.rb +462 -0
- data/test/tc_pipeline_send.rb +663 -0
- data/test/tc_protocol_review_fixes.rb +95 -0
- data/test/tc_query_control.rb +90 -0
- data/test/tc_record_codec_contracts.rb +107 -0
- data/test/tc_record_regressions.rb +225 -0
- data/test/tc_recur.rb +1 -0
- data/test/tc_recursor_address_fallback.rb +169 -0
- data/test/tc_recursor_limits.rb +139 -0
- data/test/tc_release_auxiliary_fixes.rb +288 -0
- data/test/tc_release_configuration_fixes.rb +612 -0
- data/test/tc_release_dnssec_completion.rb +405 -0
- data/test/tc_release_dnssec_fixes.rb +305 -0
- data/test/tc_release_negative_cache.rb +327 -0
- data/test/tc_release_recursor_cache.rb +212 -0
- data/test/tc_release_review_transport.rb +372 -0
- data/test/tc_release_review_transport_native.rb +56 -0
- data/test/tc_release_transport.rb +906 -0
- data/test/tc_release_transport_fixes.rb +196 -0
- data/test/tc_release_zone_defaults.rb +157 -0
- data/test/tc_release_zone_ttls.rb +39 -0
- data/test/tc_res_config.rb +53 -2
- data/test/tc_res_opt.rb +21 -1
- data/test/tc_resolver.rb +34 -19
- data/test/tc_resolver_concurrency.rb +383 -0
- data/test/tc_resolver_configuration_contracts.rb +31 -0
- data/test/tc_resolver_validation.rb +211 -0
- data/test/tc_review_cache.rb +163 -0
- data/test/tc_review_code_mapper.rb +279 -0
- data/test/tc_review_control_deadlines.rb +169 -0
- data/test/tc_review_followup_transport.rb +583 -0
- data/test/tc_review_packet_contracts.rb +369 -0
- data/test/tc_review_presentation.rb +213 -0
- data/test/tc_review_record_compatibility.rb +101 -0
- data/test/tc_review_transport_fixes.rb +195 -0
- data/test/tc_review_transport_native.rb +415 -0
- data/test/tc_review_validation_priming.rb +178 -0
- data/test/tc_rr-opt.rb +5 -2
- data/test/tc_rr-unknown.rb +19 -0
- data/test/tc_rr.rb +14 -0
- data/test/tc_rrset_grouping.rb +50 -0
- data/test/tc_rrset_review_fixes.rb +58 -0
- data/test/tc_rrsig.rb +21 -1
- data/test/tc_scoped_ipv6.rb +153 -0
- data/test/tc_scoped_ipv6_select.rb +157 -0
- data/test/tc_scoped_ipv6_transport.rb +329 -0
- data/test/tc_selector_wakeup.rb +42 -0
- data/test/tc_soak.rb +118 -196
- data/test/tc_socket_helpers.rb +90 -0
- data/test/tc_supporting_lookup_limits.rb +252 -0
- data/test/tc_svcb.rb +1043 -0
- data/test/tc_svcb_signed_target.rb +145 -0
- data/test/tc_svcb_transport.rb +120 -0
- data/test/tc_tcp.rb +69 -43
- data/test/tc_tcp_pipelining.rb +95 -204
- data/test/tc_transport_fork.rb +179 -0
- data/test/tc_tsig.rb +2 -2
- data/test/tc_tsig_api.rb +251 -0
- data/test/tc_tsig_rfc8945.rb +357 -0
- data/test/tc_v2_release_dnssec.rb +442 -0
- data/test/tc_v2_release_transport.rb +304 -0
- data/test/tc_v2_zone_transfer_native.rb +58 -0
- data/test/tc_zone_reader.rb +217 -1
- data/test/tcp_pipelining_server.rb +150 -0
- data/test/ts_offline.rb +77 -6
- data/test/ts_online.rb +2 -1
- metadata +127 -14
- data/.coveralls.yml +0 -2
|
@@ -92,6 +92,7 @@ module Dnsruby
|
|
|
92
92
|
# self.salt_length=(salt_length)
|
|
93
93
|
# self.salt=(salt)
|
|
94
94
|
@salt=salt
|
|
95
|
+
@salt_length = salt.bytesize
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
def from_string(input)
|
|
@@ -114,10 +115,7 @@ module Dnsruby
|
|
|
114
115
|
# s = salt()
|
|
115
116
|
s = @salt
|
|
116
117
|
sl = s.length()
|
|
117
|
-
|
|
118
|
-
sl == 0
|
|
119
|
-
end
|
|
120
|
-
msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl)
|
|
118
|
+
msg.put_pack("CCnC", @hash_alg.code, @flags, @iterations, sl)
|
|
121
119
|
|
|
122
120
|
if (sl > 0)
|
|
123
121
|
msg.put_bytes(s)
|
|
@@ -125,11 +123,11 @@ module Dnsruby
|
|
|
125
123
|
end
|
|
126
124
|
|
|
127
125
|
def self.decode_rdata(msg) #:nodoc: all
|
|
128
|
-
hash_alg, flags, iterations, salt_length = msg.get_unpack("
|
|
126
|
+
hash_alg, flags, iterations, salt_length = msg.get_unpack("CCnC")
|
|
129
127
|
salt = msg.get_bytes(salt_length)
|
|
130
128
|
return self.new(
|
|
131
129
|
[hash_alg, flags, iterations, salt_length, salt])
|
|
132
130
|
end
|
|
133
131
|
end
|
|
134
132
|
end
|
|
135
|
-
end
|
|
133
|
+
end
|
data/lib/dnsruby/resource/OPT.rb
CHANGED
|
@@ -227,13 +227,12 @@ module Dnsruby
|
|
|
227
227
|
if @options
|
|
228
228
|
@options.each do |opt|
|
|
229
229
|
if opt.code == EDNS_SUBNET_OPTION
|
|
230
|
-
ret = ret + "CLIENT-SUBNET: #{get_client_subnet(opt)}"
|
|
230
|
+
ret = ret + "CLIENT-SUBNET: #{get_client_subnet(opt)}\n"
|
|
231
231
|
else
|
|
232
|
-
ret = ret + "
|
|
232
|
+
ret = ret + "OPT=#{opt.code}: #{opt.data}\n"
|
|
233
233
|
end
|
|
234
234
|
end
|
|
235
235
|
end
|
|
236
|
-
ret = ret + "\n"
|
|
237
236
|
return ret
|
|
238
237
|
end
|
|
239
238
|
|
data/lib/dnsruby/resource/RR.rb
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
|
|
16
16
|
require 'dnsruby/code_mappers'
|
|
17
|
+
require 'dnsruby/presentation'
|
|
17
18
|
|
|
18
19
|
module Dnsruby
|
|
19
20
|
class RR
|
|
@@ -31,7 +32,7 @@ class RR
|
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
# A regular expression which catches any valid resource record.
|
|
34
|
-
@@RR_REGEX = Regexp.new("^\\s*(
|
|
35
|
+
@@RR_REGEX = Regexp.new("^\\s*((?:\\\\.|\\S)+)\\s*(\\d+)?\\s*(#{Classes.regexp +
|
|
35
36
|
"|CLASS\\d+"})?\\s*(#{Types.regexp + '|TYPE\\d+'})?\\s*([\\s\\S]*)\$") #:nodoc: all
|
|
36
37
|
|
|
37
38
|
@@implemented_rr_map = nil
|
|
@@ -74,7 +75,11 @@ class RR
|
|
|
74
75
|
|
|
75
76
|
def clone
|
|
76
77
|
encoded = MessageEncoder.new { |encoder| encoder.put_rr(self, true) }.to_s
|
|
77
|
-
MessageDecoder.new(encoded).get_rr
|
|
78
|
+
copy = MessageDecoder.new(encoded).get_rr
|
|
79
|
+
[:@dnsruby_received_at, :@dnsruby_authenticated_expiration, :@dnsruby_learned_trust].each do |field|
|
|
80
|
+
copy.instance_variable_set(field, instance_variable_get(field)) if instance_variable_defined?(field)
|
|
81
|
+
end
|
|
82
|
+
copy
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
|
|
@@ -178,11 +183,20 @@ class RR
|
|
|
178
183
|
#
|
|
179
184
|
#
|
|
180
185
|
def RR.new_from_string(rrstring)
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
|
|
186
|
+
# Standalone records allow unmatched parentheses, as in 1.74.0; ZoneReader
|
|
187
|
+
# requires balanced continuations. Skip the scanner for records without
|
|
188
|
+
# quotes, escapes, comments, parentheses or line breaks.
|
|
189
|
+
if /[\\'"();\r\n]/.match?(rrstring)
|
|
190
|
+
rrstring, = Presentation.scan(rrstring, allow_unbalanced: true,
|
|
191
|
+
single_quoted_from: rrstring.include?("'") ? Presentation.single_quoted_start(rrstring) : nil)
|
|
192
|
+
end
|
|
193
|
+
new_from_normalised_string(rrstring)
|
|
194
|
+
end
|
|
185
195
|
|
|
196
|
+
# ZoneReader already handles comments, grouping and quoted continuations.
|
|
197
|
+
# Keep this internal path separate from the public string constructors so
|
|
198
|
+
# normalized records need not run through the lexical scanner again.
|
|
199
|
+
def RR.new_from_normalised_string(rrstring) #:nodoc: all
|
|
186
200
|
matches = (/#{@@RR_REGEX}/xo).match(rrstring)
|
|
187
201
|
unless matches
|
|
188
202
|
raise "#{rrstring} did not match RR pattern. Please report this to the author!"
|
|
@@ -194,7 +208,9 @@ class RR
|
|
|
194
208
|
rrtype = matches[4] || ''
|
|
195
209
|
rdata = matches[5] || ''
|
|
196
210
|
|
|
197
|
-
|
|
211
|
+
# Only remove trailing record layout. `$` also matches before an embedded
|
|
212
|
+
# newline and would delete spaces that belong to a quoted TXT value.
|
|
213
|
+
rdata.sub!(/\s+\z/o, '') if rdata
|
|
198
214
|
|
|
199
215
|
# RFC3597 tweaks
|
|
200
216
|
# This converts to known class and type if specified as TYPE###
|
|
@@ -216,60 +232,93 @@ class RR
|
|
|
216
232
|
rrtype = 'ANY'
|
|
217
233
|
end
|
|
218
234
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
rdata.gsub!('(', '')
|
|
222
|
-
rdata.gsub!(')', '')
|
|
223
|
-
end
|
|
235
|
+
if ttl == 0 && rdata.empty? && empty_update_type?(rrtype, rrclass)
|
|
236
|
+
return empty_update_record(name, rrtype, rrclass)
|
|
224
237
|
end
|
|
225
238
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
239
|
+
if /\A\s*\\#/.match?(rdata)
|
|
240
|
+
# RFC 3597 section 5 permits zero octets with no hexadecimal field.
|
|
241
|
+
# Validate before packing: pack('H*') alone also accepts non-hex input.
|
|
242
|
+
generic = /\A\s*\\#\s+(\d+)(?:\s+([0-9a-fA-F\s]*))?\z/.match(rdata)
|
|
243
|
+
raise DecodeError, 'Expected RFC3597 representation of RDATA' unless generic
|
|
244
|
+
rdlength = generic[1].to_i
|
|
245
|
+
hexdump = (generic[2] || '').gsub(/\s+/, '')
|
|
246
|
+
if rdlength > 0xffff || hexdump.length != rdlength * 2
|
|
247
|
+
raise DecodeError, "#{rdata} is inconsistent with its RDATA length #{rdlength}."
|
|
229
248
|
end
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
pack_rdata = ->(regex) do
|
|
233
|
-
rdata =~ regex
|
|
234
|
-
matches = regex.match(rdata)
|
|
235
|
-
rdlength = matches[1].to_i
|
|
236
|
-
hexdump = matches[2].gsub(/\s*/, '')
|
|
237
|
-
|
|
238
|
-
test_length.(hexdump, rdlength)
|
|
239
|
-
packed_rdata = [hexdump].pack('H*')
|
|
240
|
-
|
|
241
|
-
[packed_rdata, rdlength]
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
if implemented_rrs.include?(rrtype) && rdata !~/^\s*\\#/o
|
|
249
|
+
return new_from_data(name, rrtype, rrclass, ttl, rdlength, [hexdump].pack('H*'), 0)
|
|
250
|
+
elsif implemented_rrs.include?(rrtype)
|
|
245
251
|
return _get_subclass(name, rrtype, rrclass, ttl, rdata)
|
|
246
|
-
elsif implemented_rrs.include?(rrtype) # A known RR type starting with \#
|
|
247
|
-
packed_rdata, rdlength = pack_rdata.(/\\#\s+(\d+)\s+(.*)$/o)
|
|
248
|
-
return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength);
|
|
249
|
-
elsif rdata =~ /\s*\\#\s+\d+\s+/o
|
|
250
|
-
regex = /\\#\s+(\d+)\s+(.*)$/o
|
|
251
|
-
# We are now dealing with the truly unknown.
|
|
252
|
-
raise 'Expected RFC3597 representation of RDATA' unless rdata =~ regex
|
|
253
|
-
packed_rdata, rdlength = pack_rdata.(regex)
|
|
254
|
-
return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength);
|
|
255
252
|
else
|
|
256
253
|
# God knows how to handle these...
|
|
257
254
|
return _get_subclass(name, rrtype, rrclass, ttl, '')
|
|
258
255
|
end
|
|
259
256
|
end
|
|
257
|
+
private_class_method :new_from_normalised_string
|
|
260
258
|
|
|
261
259
|
def RR.new_from_data(*args) #:nodoc: all
|
|
262
260
|
name, rrtype, rrclass, ttl, rdlength, data, offset = args
|
|
263
261
|
rdata = data ? data[offset, rdlength] : []
|
|
264
262
|
decoder = MessageDecoder.new(rdata)
|
|
265
|
-
|
|
263
|
+
record_class = get_class(rrtype, rrclass)
|
|
264
|
+
record = if rdlength == 0 && ttl == 0 && empty_update_type?(rrtype, rrclass)
|
|
265
|
+
empty_update_record(name, rrtype, rrclass)
|
|
266
|
+
else
|
|
267
|
+
record_class.decode_rdata(decoder)
|
|
268
|
+
end
|
|
266
269
|
record.name = Name.create(name)
|
|
267
270
|
record.ttl = ttl
|
|
268
271
|
record.type = rrtype
|
|
269
272
|
record.klass = Classes.new(rrclass)
|
|
273
|
+
record.validate_decoded_rdata
|
|
270
274
|
record
|
|
271
275
|
end
|
|
272
276
|
|
|
277
|
+
# Some formats depend on the actual RR class, which is assigned after the
|
|
278
|
+
# format-specific decoder runs (notably ANY/NONE UPDATE markers).
|
|
279
|
+
def validate_decoded_rdata #:nodoc: all
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# RFC 2136 prerequisites and whole-RRset deletes carry no RDATA, including
|
|
283
|
+
# for types whose ordinary records require fields. Keep the familiar typed
|
|
284
|
+
# record while representing this operation separately from an empty value.
|
|
285
|
+
module EmptyUpdateData #:nodoc: all
|
|
286
|
+
def rdata_to_string
|
|
287
|
+
''
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def data
|
|
291
|
+
''
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def rdlength
|
|
295
|
+
0
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def encode_rdata(_encoder, _canonical = false)
|
|
299
|
+
unless ttl == 0 && RR.empty_update_type?(type, klass)
|
|
300
|
+
raise EncodeError, 'Empty UPDATE records require TTL zero and class ANY or NONE'
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def validate_decoded_rdata
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def self.empty_update_type?(type, klass) #:nodoc: all
|
|
309
|
+
code = Types.new(type).code
|
|
310
|
+
[Classes::ANY, Classes::NONE].include?(Classes.new(klass)) &&
|
|
311
|
+
code > 0 && code != Types::OPT && !(128..254).cover?(code)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def self.empty_update_record(name, type, klass) #:nodoc: all
|
|
315
|
+
raise ArgumentError, 'Invalid empty UPDATE record type or class' unless empty_update_type?(type, klass)
|
|
316
|
+
record_class = get_class(type, klass)
|
|
317
|
+
record = record_class <= RR::DomainName ? record_class.new(Name.new([])) : record_class.new
|
|
318
|
+
record.name, record.type, record.klass, record.ttl, record.rdata = name, type, klass, 0, ''
|
|
319
|
+
record.extend(EmptyUpdateData)
|
|
320
|
+
end
|
|
321
|
+
|
|
273
322
|
# Return an array of all the currently implemented RR types
|
|
274
323
|
def RR.implemented_rrs
|
|
275
324
|
@@implemented_rr_map ||= ClassHash.keys.map { |key| Dnsruby::Types.to_string(key[0]) }
|
|
@@ -319,30 +368,21 @@ class RR
|
|
|
319
368
|
raise DecodeError.new("#{self.class} is RR.")
|
|
320
369
|
end
|
|
321
370
|
|
|
371
|
+
# Metadata and alternate presentations are not part of record identity.
|
|
372
|
+
def equality_fields #:nodoc:
|
|
373
|
+
fields = instance_variables - [:@ttl, :@rdata, :@dnsruby_learned_trust,
|
|
374
|
+
:@dnsruby_authenticated_expiration, :@dnsruby_received_at]
|
|
375
|
+
fields.delete(:@digest) if is_a?(RR::DS)
|
|
376
|
+
fields.sort
|
|
377
|
+
end
|
|
378
|
+
protected :equality_fields
|
|
379
|
+
|
|
322
380
|
def ==(other)
|
|
323
381
|
return false unless self.class == other.class
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
ivars.delete '@ttl' # RFC 2136 section 1.1
|
|
328
|
-
ivars.delete '@rdata'
|
|
329
|
-
if self.type == Types.DS
|
|
330
|
-
ivars.delete '@digest'
|
|
331
|
-
end
|
|
332
|
-
ivars.sort
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
get_instance_var_values = ->(object, ivar_names) do
|
|
336
|
-
ivar_names.map { |ivar_name| object.instance_variable_get(ivar_name) }
|
|
382
|
+
fields = equality_fields
|
|
383
|
+
fields == other.equality_fields && fields.all? do |field|
|
|
384
|
+
instance_variable_get(field) == other.instance_variable_get(field)
|
|
337
385
|
end
|
|
338
|
-
|
|
339
|
-
self_ivars = ivars_to_compare.(self)
|
|
340
|
-
other_ivars = ivars_to_compare.(other)
|
|
341
|
-
return false unless self_ivars == other_ivars
|
|
342
|
-
|
|
343
|
-
self_values = get_instance_var_values.(self, self_ivars)
|
|
344
|
-
other_values = get_instance_var_values.(other, other_ivars)
|
|
345
|
-
self_values == other_values
|
|
346
386
|
end
|
|
347
387
|
|
|
348
388
|
def eql?(other) #:nodoc:
|
|
@@ -350,22 +390,25 @@ class RR
|
|
|
350
390
|
end
|
|
351
391
|
|
|
352
392
|
def hash # :nodoc:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
hash_value ^ self.instance_variable_get(var_name).hash
|
|
393
|
+
equality_fields.inject(0) do |hash_value, field|
|
|
394
|
+
hash_value ^ instance_variable_get(field).hash
|
|
356
395
|
end
|
|
357
396
|
end
|
|
358
397
|
|
|
359
398
|
def self.find_class(type_value, class_value) # :nodoc: all
|
|
360
399
|
if !! (ret = ClassHash[[type_value, class_value]])
|
|
361
400
|
return ret
|
|
401
|
+
elsif !! (ret = GenericCache.find(type_value, class_value))
|
|
402
|
+
return ret
|
|
362
403
|
elsif !! (val = ClassInsensitiveTypes[type_value])
|
|
363
404
|
klass = Class.new(val)
|
|
364
405
|
klass.const_set(:TypeValue, type_value)
|
|
365
406
|
klass.const_set(:ClassValue, class_value)
|
|
366
407
|
return klass
|
|
367
408
|
else
|
|
368
|
-
return
|
|
409
|
+
return GenericCache.factory(type_value, class_value) do
|
|
410
|
+
Generic.create(type_value, class_value)
|
|
411
|
+
end
|
|
369
412
|
end
|
|
370
413
|
end
|
|
371
414
|
|
|
@@ -240,15 +240,15 @@ module Dnsruby
|
|
|
240
240
|
|
|
241
241
|
def encode_rdata(msg, canonical=false) #:nodoc: all
|
|
242
242
|
# 2 octets, then 2 sets of 1 octet
|
|
243
|
-
msg.put_pack('
|
|
243
|
+
msg.put_pack('nCC', @type_covered.to_i, @algorithm.to_i, @labels)
|
|
244
244
|
msg.put_pack("NNN", @original_ttl, @expiration, @inception)
|
|
245
245
|
msg.put_pack("n", @key_tag)
|
|
246
|
-
msg.put_name(@signers_name,
|
|
246
|
+
msg.put_name(@signers_name, true, false)
|
|
247
247
|
msg.put_bytes(@signature)
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
def self.decode_rdata(msg) #:nodoc: all
|
|
251
|
-
type_covered, algorithm, labels = msg.get_unpack('
|
|
251
|
+
type_covered, algorithm, labels = msg.get_unpack('nCC')
|
|
252
252
|
original_ttl, expiration, inception = msg.get_unpack('NNN')
|
|
253
253
|
key_tag, = msg.get_unpack('n')
|
|
254
254
|
signers_name = msg.get_name
|
|
@@ -263,7 +263,7 @@ module Dnsruby
|
|
|
263
263
|
# with the Signer's Name field in canonical form and
|
|
264
264
|
# the Signature field excluded;
|
|
265
265
|
data = MessageEncoder.new { |msg|
|
|
266
|
-
msg.put_pack('
|
|
266
|
+
msg.put_pack('nCC', @type_covered.to_i, @algorithm.to_i, @labels)
|
|
267
267
|
msg.put_pack("NNN", @original_ttl, @expiration, @inception)
|
|
268
268
|
msg.put_pack("n", @key_tag)
|
|
269
269
|
msg.put_name(@signers_name, true)
|
|
@@ -272,4 +272,4 @@ module Dnsruby
|
|
|
272
272
|
end
|
|
273
273
|
end
|
|
274
274
|
end
|
|
275
|
-
end
|
|
275
|
+
end
|
|
@@ -16,7 +16,12 @@ class RRSet
|
|
|
16
16
|
end
|
|
17
17
|
@rrs = []
|
|
18
18
|
@num_sigs = 0
|
|
19
|
+
# During construction all entries are private wire copies. Discard this
|
|
20
|
+
# temporary index before exposing the set: public RR objects are mutable.
|
|
21
|
+
@membership_index = {}
|
|
19
22
|
rrs.each {|rr| add(rr)}
|
|
23
|
+
ensure
|
|
24
|
+
@membership_index = nil
|
|
20
25
|
end
|
|
21
26
|
def self.new_from_string(string)
|
|
22
27
|
rr_strings = string.split("\n")
|
|
@@ -32,7 +37,14 @@ class RRSet
|
|
|
32
37
|
return @rrs[0, @rrs.length-@num_sigs]
|
|
33
38
|
end
|
|
34
39
|
def privateAdd(r) #:nodoc:
|
|
35
|
-
if
|
|
40
|
+
budget = ValidationBudget.current if defined?(ValidationBudget)
|
|
41
|
+
duplicate = if @membership_index
|
|
42
|
+
budget&.check!(1)
|
|
43
|
+
@membership_index.key?(r)
|
|
44
|
+
else
|
|
45
|
+
@rrs.any? { |existing| budget&.check!(1); existing == r }
|
|
46
|
+
end
|
|
47
|
+
if duplicate
|
|
36
48
|
return true
|
|
37
49
|
end
|
|
38
50
|
new_pos = @rrs.length - @num_sigs
|
|
@@ -47,6 +59,7 @@ class RRSet
|
|
|
47
59
|
@num_sigs += 1
|
|
48
60
|
end
|
|
49
61
|
@rrs.insert(new_pos, r)
|
|
62
|
+
@membership_index[r] = true if @membership_index
|
|
50
63
|
return true
|
|
51
64
|
end
|
|
52
65
|
|
|
@@ -56,25 +69,19 @@ class RRSet
|
|
|
56
69
|
def add(rin, do_clone = true)
|
|
57
70
|
if (rin.instance_of?RRSet)
|
|
58
71
|
ret = false
|
|
59
|
-
|
|
72
|
+
(rin.rrs + rin.sigs).each {|rr| ret = add(rr, do_clone)}
|
|
60
73
|
return ret
|
|
61
74
|
end
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# Check the type, klass and ttl are correct
|
|
73
|
-
first = @rrs[0]
|
|
74
|
-
if (!r.sameRRset(first))
|
|
75
|
-
return false
|
|
76
|
-
# raise ArgumentError.new("record does not match rrset")
|
|
77
|
-
end
|
|
75
|
+
@membership_index = nil unless do_clone
|
|
76
|
+
add_record(rin, do_clone)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private def add_record(rin, do_clone)
|
|
80
|
+
# Reject unrelated records before copying their wire data.
|
|
81
|
+
first = @rrs.first
|
|
82
|
+
return false if first && !rin.sameRRset(first)
|
|
83
|
+
r = do_clone ? rin.clone : rin
|
|
84
|
+
return privateAdd(r) unless first
|
|
78
85
|
|
|
79
86
|
if (!(r.type == Types::RRSIG) && (!(first.type == Types::RRSIG)))
|
|
80
87
|
if (r.ttl != first.ttl) # RFC2181, section 5.2
|
|
@@ -114,12 +121,19 @@ class RRSet
|
|
|
114
121
|
end
|
|
115
122
|
|
|
116
123
|
return_rrs = RRSet.new
|
|
117
|
-
canonical_rrs.keys.sort.
|
|
118
|
-
return_rrs.add(canonical_rrs[rdata], false)
|
|
119
|
-
}
|
|
124
|
+
return_rrs.send(:add_canonical_records, canonical_rrs.keys.sort.map { |rdata| canonical_rrs[rdata] })
|
|
120
125
|
return return_rrs
|
|
121
126
|
end
|
|
122
127
|
|
|
128
|
+
private def add_canonical_records(records)
|
|
129
|
+
# Preserve the original references and TTL normalization of add(rr, false).
|
|
130
|
+
# The index exists only while building; callers can mutate records later.
|
|
131
|
+
@membership_index = {}
|
|
132
|
+
records.each { |record| add_record(record, false) }
|
|
133
|
+
ensure
|
|
134
|
+
@membership_index = nil
|
|
135
|
+
end
|
|
136
|
+
|
|
123
137
|
def ==(other)
|
|
124
138
|
return false unless other.instance_of?RRSet
|
|
125
139
|
return false if (other.sigs.length != self.sigs.length)
|
|
@@ -137,7 +151,9 @@ class RRSet
|
|
|
137
151
|
end
|
|
138
152
|
# Delete the RR from this RRSet
|
|
139
153
|
def delete(rr)
|
|
140
|
-
@rrs.delete(rr)
|
|
154
|
+
removed = @rrs.delete(rr)
|
|
155
|
+
@num_sigs = @rrs.count { |record| record.type == Types::RRSIG } if removed
|
|
156
|
+
removed
|
|
141
157
|
end
|
|
142
158
|
def each
|
|
143
159
|
@rrs.each do |rr|
|
data/lib/dnsruby/resource/SRV.rb
CHANGED
|
@@ -96,7 +96,7 @@ module Dnsruby
|
|
|
96
96
|
msg.put_pack("n", @priority)
|
|
97
97
|
msg.put_pack("n", @weight)
|
|
98
98
|
msg.put_pack("n", @port)
|
|
99
|
-
msg.put_name(@target,canonical)
|
|
99
|
+
msg.put_name(@target, true, canonical)
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def self.decode_rdata(msg) #:nodoc: all
|
|
@@ -109,4 +109,4 @@ module Dnsruby
|
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
|
-
end
|
|
112
|
+
end
|