dnsruby 1.74.0 → 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 +302 -1
- data/Rakefile +0 -3
- data/demo/axfr.rb +24 -6
- data/demo/rubydig.rb +0 -2
- data/dnsruby.gemspec +5 -4
- data/lib/dnsruby/cache.rb +279 -47
- data/lib/dnsruby/code_mapper.rb +35 -8
- data/lib/dnsruby/code_mappers.rb +63 -49
- data/lib/dnsruby/config.rb +75 -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 +645 -203
- data/lib/dnsruby/presentation.rb +147 -0
- data/lib/dnsruby/query_control.rb +132 -0
- data/lib/dnsruby/recursor.rb +795 -339
- data/lib/dnsruby/resolver.rb +468 -186
- 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 +19 -0
- 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/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 +979 -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 +42 -20
- data/test/tc_res_opt.rb +21 -1
- data/test/tc_resolver.rb +21 -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 +105 -10
- data/.coveralls.yml +0 -2
data/lib/dnsruby/dnssec.rb
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# ++
|
|
16
16
|
require 'digest/sha2'
|
|
17
17
|
require 'dnsruby/key_cache'
|
|
18
|
+
require 'dnsruby/validation_budget'
|
|
18
19
|
require 'dnsruby/single_verifier'
|
|
19
20
|
module Dnsruby
|
|
20
21
|
|
|
@@ -27,13 +28,10 @@ module Dnsruby
|
|
|
27
28
|
# able to treat the DNSSEC signatures as trust relationships between
|
|
28
29
|
# the zone administrators and the stub resolver itself. "
|
|
29
30
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# given keys
|
|
34
|
-
# directly, or by loading the IANA TAR or the DLV ISC ZSK). Validation will then
|
|
35
|
-
# be performed from these keys (or the DLV registry, if configured). Negative
|
|
36
|
-
# and positive responses are validation.
|
|
31
|
+
# Resolver queries do not validate responses by default; set
|
|
32
|
+
# Resolver#do_validation to true to enable validation. Root DS trust anchors
|
|
33
|
+
# are configured below. Applications may also use verify() to verify RRSets
|
|
34
|
+
# or Messages with given keys, or add their own trust anchors to this class.
|
|
37
35
|
#
|
|
38
36
|
# Messages are tagged with the current security_level (Message::SecurityLevel).
|
|
39
37
|
# UNCHECKED means Dnsruby has not attempted to validate the response.
|
|
@@ -44,14 +42,32 @@ module Dnsruby
|
|
|
44
42
|
# Several validators are provided, with each maintaining its own cache of trusted keys.
|
|
45
43
|
# If validators are added or removed, the caches of the other validators are not affected.
|
|
46
44
|
class Dnssec
|
|
45
|
+
# Limits apply to one complete validation, including trust-chain discovery.
|
|
46
|
+
# A limit is a VerifyError; it never downgrades a signed answer to insecure.
|
|
47
|
+
@validation_limits = ValidationBudget::DEFAULT_LIMITS
|
|
48
|
+
def self.validation_limits
|
|
49
|
+
@validation_limits
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.validation_limits=(limits)
|
|
53
|
+
unknown = limits.keys - ValidationBudget::DEFAULT_LIMITS.keys
|
|
54
|
+
raise ArgumentError, "Unknown DNSSEC validation limits: #{unknown.join(', ')}" unless unknown.empty?
|
|
55
|
+
merged = ValidationBudget::DEFAULT_LIMITS.merge(limits)
|
|
56
|
+
merged.each do |key, value|
|
|
57
|
+
valid = key == :timeout ? value.is_a?(Numeric) && value.finite? && value > 0 : value.is_a?(Integer) && value > 0
|
|
58
|
+
raise ArgumentError, "Invalid DNSSEC validation limit #{key}" unless valid
|
|
59
|
+
end
|
|
60
|
+
@validation_limits = merged.freeze
|
|
61
|
+
end
|
|
47
62
|
# A class to cache trusted keys
|
|
48
63
|
|
|
49
64
|
|
|
50
65
|
class ValidationPolicy
|
|
51
66
|
# @TODO@ Could do this by getting client to add verifiers in the order they
|
|
52
67
|
# want them to be used. Could then dispense with all this logic
|
|
53
|
-
#
|
|
54
|
-
#
|
|
68
|
+
# Configured DLV registries are a final fallback for the two ordered
|
|
69
|
+
# policies, after root/local anchors, when those paths are not bogus.
|
|
70
|
+
# The two ONLY policies never consult another source of trust.
|
|
55
71
|
|
|
56
72
|
# * Always use the root and ignore local trust anchors.
|
|
57
73
|
ALWAYS_ROOT_ONLY = 1
|
|
@@ -74,15 +90,16 @@ module Dnsruby
|
|
|
74
90
|
@@validation_policy
|
|
75
91
|
end
|
|
76
92
|
|
|
77
|
-
@@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
|
|
78
|
-
|
|
79
93
|
# #NOTE# You may wish to import these via a secure channel yourself, if
|
|
80
94
|
# using Dnsruby for validation.
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
# KSK-2017 and KSK-2024, published at https://data.iana.org/root-anchors/root-anchors.xml
|
|
96
|
+
ROOT_TRUST_ANCHORS = [
|
|
97
|
+
". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D".freeze,
|
|
98
|
+
". IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16".freeze
|
|
99
|
+
].freeze
|
|
83
100
|
|
|
84
|
-
@@
|
|
85
|
-
@@root_verifier.add_root_ds(
|
|
101
|
+
@@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
|
|
102
|
+
ROOT_TRUST_ANCHORS.each {|ds| @@root_verifier.add_root_ds(RR.create(ds))}
|
|
86
103
|
|
|
87
104
|
@@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV)
|
|
88
105
|
|
|
@@ -104,11 +121,12 @@ module Dnsruby
|
|
|
104
121
|
# Create a new verifier?
|
|
105
122
|
@@anchor_verifier.add_trust_anchor_with_expiration(k, expiration)
|
|
106
123
|
end
|
|
107
|
-
# Remove
|
|
124
|
+
# Remove a configured trust anchor and clear keys learned from local anchors.
|
|
108
125
|
def Dnssec.remove_trust_anchor(t)
|
|
109
126
|
@@anchor_verifier.remove_trust_anchor(t)
|
|
110
127
|
end
|
|
111
|
-
#
|
|
128
|
+
# Remove all local trust anchors and keys learned from them.
|
|
129
|
+
# Built-in root anchors are unaffected.
|
|
112
130
|
def self.clear_trust_anchors
|
|
113
131
|
@@anchor_verifier.clear_trust_anchors
|
|
114
132
|
end
|
|
@@ -117,6 +135,8 @@ module Dnsruby
|
|
|
117
135
|
return @@anchor_verifier.trust_anchors
|
|
118
136
|
end
|
|
119
137
|
|
|
138
|
+
# Clear learned keys and all DS/DLV anchors, including the built-in root DS
|
|
139
|
+
# anchors. Configured DNSKEY anchors are retained.
|
|
120
140
|
def self.clear_trusted_keys
|
|
121
141
|
[@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v|
|
|
122
142
|
v.clear_trusted_keys
|
|
@@ -124,10 +144,10 @@ module Dnsruby
|
|
|
124
144
|
end
|
|
125
145
|
|
|
126
146
|
def self.reset
|
|
147
|
+
SingleVerifier.set_recursor_hints(nil)
|
|
127
148
|
@@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT
|
|
128
149
|
@@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
|
|
129
|
-
@@root_verifier.add_root_ds(
|
|
130
|
-
@@root_verifier.add_root_ds(@@root_key_new)
|
|
150
|
+
ROOT_TRUST_ANCHORS.each {|ds| @@root_verifier.add_root_ds(RR.create(ds))}
|
|
131
151
|
|
|
132
152
|
@@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV)
|
|
133
153
|
|
|
@@ -138,8 +158,7 @@ module Dnsruby
|
|
|
138
158
|
end
|
|
139
159
|
|
|
140
160
|
def self.set_hints(hints)
|
|
141
|
-
|
|
142
|
-
@@anchor_verifier.set_hints(hints)
|
|
161
|
+
SingleVerifier.set_recursor_hints(hints)
|
|
143
162
|
end
|
|
144
163
|
|
|
145
164
|
def self.no_keys?
|
|
@@ -184,10 +203,14 @@ module Dnsruby
|
|
|
184
203
|
def self.validate(msg)
|
|
185
204
|
query = Message.new()
|
|
186
205
|
query.header.cd=true
|
|
187
|
-
|
|
206
|
+
validate_with_query(query, msg)
|
|
188
207
|
end
|
|
189
208
|
|
|
190
209
|
def self.validate_with_query(query, msg)
|
|
210
|
+
ValidationBudget.with { validate_with_query_internal(query, msg) }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
private_class_method def self.validate_with_query_internal(query, msg)
|
|
191
214
|
if (!msg)
|
|
192
215
|
return false
|
|
193
216
|
end
|
|
@@ -200,13 +223,19 @@ module Dnsruby
|
|
|
200
223
|
}
|
|
201
224
|
if (found_sigs)
|
|
202
225
|
begin
|
|
203
|
-
|
|
226
|
+
result = verify_result(msg, nil, operational: true)
|
|
227
|
+
if result == :secure
|
|
204
228
|
msg.security_level = Message::SecurityLevel.SECURE
|
|
229
|
+
msg.security_error = ''
|
|
205
230
|
return true
|
|
206
231
|
end
|
|
207
|
-
rescue
|
|
208
|
-
|
|
209
|
-
|
|
232
|
+
rescue ValidationLimitError
|
|
233
|
+
raise
|
|
234
|
+
rescue VerifyError
|
|
235
|
+
# Missing cached keys and a failed trusted path both arrive here.
|
|
236
|
+
# Full validation discovers the applicable anchors and distinguishes
|
|
237
|
+
# those cases. A cached INSECURE result must go through that policy
|
|
238
|
+
# too: another applicable anchor may establish SECURE or BOGUS.
|
|
210
239
|
end
|
|
211
240
|
end
|
|
212
241
|
|
|
@@ -244,17 +273,21 @@ module Dnsruby
|
|
|
244
273
|
Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query)
|
|
245
274
|
end
|
|
246
275
|
end
|
|
247
|
-
if (last_level != Message::SecurityLevel.SECURE && last_level != Message::SecurityLevel.BOGUS)
|
|
276
|
+
if (last_level != Message::SecurityLevel.SECURE && last_level != Message::SecurityLevel.BOGUS) &&
|
|
277
|
+
[ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT, ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS].include?(@@validation_policy)
|
|
248
278
|
last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
|
|
249
279
|
Proc.new{|m, q| validate_with_dlv(m, q)}, msg, query)
|
|
250
280
|
end
|
|
251
281
|
# Set the message security level!
|
|
282
|
+
if last_level == Message::SecurityLevel.SECURE
|
|
283
|
+
last_error = ''
|
|
284
|
+
elsif last_error && !last_error.empty?
|
|
285
|
+
last_level = Message::SecurityLevel.BOGUS
|
|
286
|
+
end
|
|
252
287
|
msg.security_level = last_level
|
|
253
288
|
msg.security_error = last_error
|
|
254
|
-
if
|
|
255
|
-
|
|
256
|
-
end
|
|
257
|
-
raise VerifyError.new(last_error) if (last_level < 0)
|
|
289
|
+
msg.header.ad = false if msg.security_level == Message::SecurityLevel.INSECURE
|
|
290
|
+
raise VerifyError.new(last_error) if (msg.security_level < 0)
|
|
258
291
|
return (msg.security_level.code > Message::SecurityLevel::UNCHECKED)
|
|
259
292
|
end
|
|
260
293
|
msg.security_level = Message::SecurityLevel.UNCHECKED
|
|
@@ -262,11 +295,33 @@ module Dnsruby
|
|
|
262
295
|
end
|
|
263
296
|
|
|
264
297
|
def self.try_validation(last_level, last_error, last_error_level, proc, msg, query) # :nodoc:
|
|
298
|
+
ValidationBudget.with do
|
|
299
|
+
try_validation_internal(last_level, last_error, last_error_level, proc, msg, query)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
private_class_method def self.try_validation_internal(last_level, last_error, last_error_level, proc, msg, query)
|
|
304
|
+
# Keep the lifetime with the successful result. A later unanchored
|
|
305
|
+
# verifier can return INSECURE without establishing a cacheable proof.
|
|
306
|
+
previous_expiration = if last_level == Message::SecurityLevel.INSECURE
|
|
307
|
+
msg.instance_variable_get(:@dnsruby_authenticated_expiration)
|
|
308
|
+
end
|
|
265
309
|
begin
|
|
266
|
-
proc.call(msg, query)
|
|
310
|
+
result = proc.call(msg, query)
|
|
311
|
+
if !result || msg.security_level == Message::SecurityLevel.BOGUS
|
|
312
|
+
detail = msg.security_error.to_s
|
|
313
|
+
raise VerifyError, detail.empty? ? 'DNSSEC validation failed' : detail
|
|
314
|
+
end
|
|
315
|
+
if previous_expiration && msg.security_level <= Message::SecurityLevel.INSECURE
|
|
316
|
+
current_expiration = msg.instance_variable_get(:@dnsruby_authenticated_expiration)
|
|
317
|
+
msg.instance_variable_set(:@dnsruby_authenticated_expiration,
|
|
318
|
+
[previous_expiration, current_expiration].compact.min)
|
|
319
|
+
end
|
|
267
320
|
last_level = Message::SecurityLevel.new([msg.security_level.code, last_level.code].max)
|
|
268
321
|
rescue VerifyError => e
|
|
269
|
-
|
|
322
|
+
# A failed trusted path cannot be erased by a later unanchored or
|
|
323
|
+
# insecure result. A separately authenticated SECURE path may win.
|
|
324
|
+
if last_error.to_s.empty? || last_error_level < last_level
|
|
270
325
|
last_error = e.to_s
|
|
271
326
|
last_error_level = last_level
|
|
272
327
|
end
|
|
@@ -275,27 +330,62 @@ module Dnsruby
|
|
|
275
330
|
end
|
|
276
331
|
|
|
277
332
|
def self.validate_with_anchors(msg, query)
|
|
278
|
-
|
|
333
|
+
@@anchor_verifier.validate(msg, query)
|
|
279
334
|
end
|
|
280
335
|
|
|
281
336
|
def self.validate_with_root(msg, query)
|
|
282
|
-
|
|
337
|
+
@@root_verifier.validate(msg, query)
|
|
283
338
|
end
|
|
284
339
|
|
|
285
340
|
def self.validate_with_dlv(msg, query)
|
|
286
|
-
|
|
341
|
+
@@dlv_verifier.validate(msg, query)
|
|
287
342
|
end
|
|
288
343
|
|
|
289
344
|
def self.verify(msg, keys=nil)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
345
|
+
verify_result(msg, keys) == :secure
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Keep the result separate from mutable Message state: a previous validation
|
|
349
|
+
# must not turn a newly verified Opt-Out response into a secure response.
|
|
350
|
+
private_class_method def self.verify_result(msg, keys=nil, operational: false)
|
|
351
|
+
ValidationBudget.with { verify_internal(msg, keys, operational: operational) }
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
private_class_method def self.verify_internal(msg, keys, operational: false)
|
|
355
|
+
insecure = false
|
|
356
|
+
insecure_expiration = nil
|
|
357
|
+
last_error = nil
|
|
358
|
+
verifiers = if operational
|
|
359
|
+
case @@validation_policy
|
|
360
|
+
when ValidationPolicy::ALWAYS_ROOT_ONLY then [@@root_verifier]
|
|
361
|
+
when ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY then [@@anchor_verifier]
|
|
362
|
+
when ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS then [@@root_verifier, @@anchor_verifier]
|
|
363
|
+
else [@@anchor_verifier, @@root_verifier]
|
|
364
|
+
end
|
|
365
|
+
else
|
|
366
|
+
[@@anchor_verifier, @@root_verifier, @@dlv_verifier]
|
|
367
|
+
end
|
|
368
|
+
verifiers.each do |verifier|
|
|
293
369
|
begin
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
370
|
+
result = operational ? verifier.verify_for_validation(msg) : verifier.verify_result(msg, keys)
|
|
371
|
+
return :secure if result == :secure
|
|
372
|
+
if result == :insecure
|
|
373
|
+
insecure = true
|
|
374
|
+
expiration = msg.instance_variable_get(:@dnsruby_authenticated_expiration)
|
|
375
|
+
insecure_expiration = [insecure_expiration, expiration].compact.min
|
|
376
|
+
end
|
|
377
|
+
rescue ValidationLimitError
|
|
378
|
+
raise
|
|
379
|
+
rescue VerifyError => error
|
|
380
|
+
last_error = error
|
|
297
381
|
end
|
|
298
382
|
end
|
|
383
|
+
if insecure
|
|
384
|
+
msg.instance_variable_set(:@dnsruby_authenticated_expiration, insecure_expiration) if insecure_expiration
|
|
385
|
+
return :insecure
|
|
386
|
+
end
|
|
387
|
+
raise last_error if last_error
|
|
388
|
+
false
|
|
299
389
|
end
|
|
300
390
|
|
|
301
391
|
def self.anchor_verifier
|
|
@@ -312,9 +402,22 @@ module Dnsruby
|
|
|
312
402
|
|
|
313
403
|
|
|
314
404
|
def self.verify_rrset(rrset, keys = nil)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
405
|
+
ValidationBudget.with do
|
|
406
|
+
last_error = nil
|
|
407
|
+
verified = [@@anchor_verifier, @@root_verifier, @@dlv_verifier].any? do |verifier|
|
|
408
|
+
ValidationBudget.current.check!
|
|
409
|
+
begin
|
|
410
|
+
verifier.verify_rrset(rrset, keys)
|
|
411
|
+
rescue ValidationLimitError
|
|
412
|
+
raise
|
|
413
|
+
rescue VerifyError => error
|
|
414
|
+
last_error = error
|
|
415
|
+
false
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
raise last_error if !verified && last_error
|
|
419
|
+
verified
|
|
420
|
+
end
|
|
318
421
|
end
|
|
319
422
|
end
|
|
320
423
|
end
|
data/lib/dnsruby/ipv6.rb
CHANGED
|
@@ -68,6 +68,7 @@ module Dnsruby
|
|
|
68
68
|
prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
|
|
69
69
|
suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
|
|
70
70
|
omitlen = 16 - a1.length - a2.length
|
|
71
|
+
raise ArgumentError, "not numeric IPv6 address: #{arg}" unless omitlen > 0
|
|
71
72
|
address << a1 << "\0" * omitlen << a2
|
|
72
73
|
elsif Regex_6Hex4Dec =~ arg
|
|
73
74
|
prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
|
|
@@ -85,6 +86,7 @@ module Dnsruby
|
|
|
85
86
|
prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
|
|
86
87
|
suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
|
|
87
88
|
omitlen = 12 - a1.length - a2.length
|
|
89
|
+
raise ArgumentError, "not numeric IPv6 address: #{arg}" unless omitlen > 0
|
|
88
90
|
address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
|
|
89
91
|
else
|
|
90
92
|
raise ArgumentError.new("not numeric IPv6 address: " + arg)
|
|
@@ -108,12 +110,50 @@ module Dnsruby
|
|
|
108
110
|
# The raw IPv6 address as a String
|
|
109
111
|
attr_reader :address
|
|
110
112
|
|
|
113
|
+
# The historical Dnsruby representation, retained for callers that compare
|
|
114
|
+
# address strings. Use #to_s_canonical for RFC 5952 formatting.
|
|
111
115
|
def to_s
|
|
112
|
-
|
|
113
|
-
unless
|
|
114
|
-
|
|
116
|
+
text = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("n8"))
|
|
117
|
+
unless text.sub!(/(^|:)0(:0)+(:|$)/, '::')
|
|
118
|
+
text.sub!(/(^|:)0(:|$)/, '::')
|
|
115
119
|
end
|
|
116
|
-
|
|
120
|
+
text
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The RFC 5952 canonical text representation of the address, opt-in.
|
|
124
|
+
def to_s_canonical
|
|
125
|
+
fields = @address.unpack("n8")
|
|
126
|
+
|
|
127
|
+
# RFC 5952 section 5: an address carrying an embedded IPv4 address
|
|
128
|
+
# under the well-known ::ffff:0:0/96 prefix is written in the mixed
|
|
129
|
+
# hexadecimal / dotted decimal notation.
|
|
130
|
+
if fields[0, 5].all?(&:zero?) && fields[5] == 0xffff
|
|
131
|
+
return "::ffff:" + fields[6, 2].pack("n2").unpack("C4").join(".")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# RFC 5952 section 4.2.3: "::" replaces the longest run of consecutive
|
|
135
|
+
# all-zero fields, and the first such run when several are equally long.
|
|
136
|
+
# Section 4.2.2: a run of a single all-zero field is never replaced.
|
|
137
|
+
run_start = nil
|
|
138
|
+
best_start, best_length = nil, 1
|
|
139
|
+
fields.each_with_index do |field, i|
|
|
140
|
+
if field == 0
|
|
141
|
+
run_start ||= i
|
|
142
|
+
length = i - run_start + 1
|
|
143
|
+
best_start, best_length = run_start, length if length > best_length
|
|
144
|
+
else
|
|
145
|
+
run_start = nil
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# RFC 5952 sections 4.1 and 4.3: no leading zeros, and lowercase hex
|
|
150
|
+
strings = fields.map {|field| field.to_s(16)}
|
|
151
|
+
if best_start
|
|
152
|
+
strings[best_start, best_length] = ['']
|
|
153
|
+
strings.unshift('') if best_start == 0
|
|
154
|
+
strings.push('') if best_start + best_length == fields.length
|
|
155
|
+
end
|
|
156
|
+
return strings.join(':')
|
|
117
157
|
end
|
|
118
158
|
|
|
119
159
|
def inspect #:nodoc:
|
|
@@ -141,4 +181,4 @@ module Dnsruby
|
|
|
141
181
|
return @address.hash
|
|
142
182
|
end
|
|
143
183
|
end
|
|
144
|
-
end
|
|
184
|
+
end
|