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
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Dnsruby
|
|
2
|
+
# Packet, query, retry, and I/O deadlines share one clock unaffected by wall
|
|
3
|
+
# clock corrections. DNS TTLs and signatures continue to use civil time.
|
|
4
|
+
module TransportClock #:nodoc:
|
|
5
|
+
def self.now
|
|
6
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
data/lib/dnsruby/update.rb
CHANGED
|
@@ -149,7 +149,11 @@ module Dnsruby
|
|
|
149
149
|
klass = Classes.new(zone()[0].zclass)
|
|
150
150
|
rdata=args[2]
|
|
151
151
|
end
|
|
152
|
-
rec =
|
|
152
|
+
rec = if args.length > 2
|
|
153
|
+
RR.create("#{name} #{ttl} #{klass} #{type} #{rdata}")
|
|
154
|
+
else
|
|
155
|
+
RR.empty_update_record(name, type, klass)
|
|
156
|
+
end
|
|
153
157
|
add_pre(rec)
|
|
154
158
|
return rec
|
|
155
159
|
else
|
|
@@ -172,8 +176,6 @@ module Dnsruby
|
|
|
172
176
|
# (in the zone and class denoted by the Zone Section) can exist.
|
|
173
177
|
#
|
|
174
178
|
def absent(*args)
|
|
175
|
-
ttl = 0
|
|
176
|
-
rdata = ""
|
|
177
179
|
klass = Classes.NONE
|
|
178
180
|
if (args.length>=1) # domain (RFC2136, Section 2.4.5)
|
|
179
181
|
name = args[0]
|
|
@@ -181,7 +183,7 @@ module Dnsruby
|
|
|
181
183
|
if (args.length==2) # RRSET (RFC2136, Section 2.4.3)
|
|
182
184
|
type = args[1]
|
|
183
185
|
end
|
|
184
|
-
rec = RR.
|
|
186
|
+
rec = RR.empty_update_record(name, type, klass)
|
|
185
187
|
add_pre(rec)
|
|
186
188
|
return rec
|
|
187
189
|
else
|
|
@@ -262,10 +264,10 @@ module Dnsruby
|
|
|
262
264
|
resource = nil
|
|
263
265
|
case args.length
|
|
264
266
|
when 1 # name
|
|
265
|
-
resource = RR.
|
|
267
|
+
resource = RR.empty_update_record(args[0], Types.ANY, klass)
|
|
266
268
|
add_update(resource)
|
|
267
269
|
when 2 # name, type
|
|
268
|
-
resource = RR.
|
|
270
|
+
resource = RR.empty_update_record(args[0], args[1], klass)
|
|
269
271
|
add_update(resource)
|
|
270
272
|
when 3 # name, type, rdata
|
|
271
273
|
name = args[0]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
require 'dnsruby/query_control'
|
|
2
|
+
|
|
3
|
+
module Dnsruby
|
|
4
|
+
# Resource limits are verification failures, never evidence that a zone is
|
|
5
|
+
# insecure. Keep this distinction through trust-anchor and algorithm fallbacks.
|
|
6
|
+
class ValidationLimitError < VerifyError; end
|
|
7
|
+
class ValidationCancelled < ValidationLimitError; end
|
|
8
|
+
|
|
9
|
+
# One budget counts DNSSEC work across nested validation calls. Supporting
|
|
10
|
+
# lookups share its deadline and cancellation signal through QueryControl.
|
|
11
|
+
class ValidationBudget # :nodoc: all
|
|
12
|
+
# Ordinary verification and lookup failures can select another key or
|
|
13
|
+
# anchor. Exhaustion must cross those fallbacks unchanged, then become the
|
|
14
|
+
# public verification error when the outermost validation returns.
|
|
15
|
+
class Abort < StandardError
|
|
16
|
+
attr_reader :public_error
|
|
17
|
+
|
|
18
|
+
def initialize(public_error)
|
|
19
|
+
@public_error = public_error
|
|
20
|
+
super(public_error.message)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
private_constant :Abort
|
|
24
|
+
|
|
25
|
+
DEFAULT_LIMITS = {
|
|
26
|
+
max_signature_verifications: 64,
|
|
27
|
+
max_work: 100_000,
|
|
28
|
+
timeout: 5.0
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
attr_reader :control, :transaction
|
|
32
|
+
|
|
33
|
+
def self.current
|
|
34
|
+
Thread.current[:dnsruby_validation_budget]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Supporting lookup configuration has legacy catch-all fallbacks. None
|
|
38
|
+
# may convert an enclosing operation's exhaustion into an ordinary failure.
|
|
39
|
+
def self.abort?(error)
|
|
40
|
+
error.is_a?(Abort) || error.is_a?(ValidationLimitError) ||
|
|
41
|
+
error.is_a?(QueryControl::Stopped) || error.is_a?(RecursionLimitError)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.with(budget = nil)
|
|
45
|
+
previous = current
|
|
46
|
+
budget = previous || budget || new
|
|
47
|
+
Thread.current[:dnsruby_validation_budget] = budget
|
|
48
|
+
QueryControl.with(budget.control) do
|
|
49
|
+
budget.check!
|
|
50
|
+
result = yield budget
|
|
51
|
+
budget.check!
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
rescue Abort, QueryControl::Stopped => error
|
|
55
|
+
raise if previous
|
|
56
|
+
raise public_error(error), cause: error
|
|
57
|
+
ensure
|
|
58
|
+
Thread.current[:dnsruby_validation_budget] = previous
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize(deadline: nil, limits: Dnssec.validation_limits, parent: nil)
|
|
62
|
+
# A plain token can be retained with learned records without retaining
|
|
63
|
+
# the budget, resolver control, or the rest of a validation operation.
|
|
64
|
+
if parent
|
|
65
|
+
@transaction = parent.transaction
|
|
66
|
+
@limits = parent.limits
|
|
67
|
+
@state = parent.state
|
|
68
|
+
@control = QueryControl.new(deadline: deadline, parent: parent.control)
|
|
69
|
+
else
|
|
70
|
+
@transaction = Object.new.freeze
|
|
71
|
+
@limits = limits
|
|
72
|
+
deadline = [deadline, TransportClock.now + limits[:timeout]].compact.min
|
|
73
|
+
@control = QueryControl.new(deadline: deadline, parent: QueryControl.current)
|
|
74
|
+
@state = { mutex: Mutex.new, signature_verifications: 0, work: 0 }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# A selector cancels its completed hop. Keep that signal local while all
|
|
79
|
+
# hops and parallel address workers count against the lookup's one budget.
|
|
80
|
+
def child(deadline: nil)
|
|
81
|
+
self.class.new(deadline: deadline, parent: self)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def deadline
|
|
85
|
+
@control.deadline
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def cancel
|
|
89
|
+
@control.cancel
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def check!(work = 0)
|
|
93
|
+
@control.check!
|
|
94
|
+
@state[:mutex].synchronize do
|
|
95
|
+
@state[:work] += work
|
|
96
|
+
check_counts!
|
|
97
|
+
end
|
|
98
|
+
true
|
|
99
|
+
rescue QueryControl::Stopped => error
|
|
100
|
+
raise if self.class.current
|
|
101
|
+
raise self.class.public_error(error), cause: error
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.public_error(error)
|
|
105
|
+
public_error = case error
|
|
106
|
+
when Abort then error.public_error
|
|
107
|
+
when QueryControl::Cancelled then ValidationCancelled.new('DNSSEC validation cancelled')
|
|
108
|
+
else ValidationLimitError.new('DNSSEC validation deadline exceeded')
|
|
109
|
+
end
|
|
110
|
+
public_error.set_backtrace(error.backtrace)
|
|
111
|
+
public_error
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def fail_with(error_class, message)
|
|
115
|
+
error = error_class.new(message)
|
|
116
|
+
raise(self.class.current ? Abort.new(error) : error)
|
|
117
|
+
end
|
|
118
|
+
private :fail_with
|
|
119
|
+
|
|
120
|
+
def check_counts!
|
|
121
|
+
if @state[:signature_verifications] > @limits[:max_signature_verifications]
|
|
122
|
+
fail_with(ValidationLimitError, 'DNSSEC signature verification limit exceeded')
|
|
123
|
+
end
|
|
124
|
+
if @state[:work] > @limits[:max_work]
|
|
125
|
+
fail_with(ValidationLimitError, 'DNSSEC validation work limit exceeded')
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
private :check_counts!
|
|
129
|
+
|
|
130
|
+
def signature!
|
|
131
|
+
check!
|
|
132
|
+
@state[:mutex].synchronize do
|
|
133
|
+
@state[:signature_verifications] += 1
|
|
134
|
+
check_counts!
|
|
135
|
+
end
|
|
136
|
+
true
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
protected
|
|
140
|
+
|
|
141
|
+
attr_reader :limits, :state
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -15,67 +15,149 @@
|
|
|
15
15
|
# ++
|
|
16
16
|
|
|
17
17
|
module Dnsruby
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# to do this.
|
|
18
|
+
# Runs local DNSSEC validation outside SelectThread, using a bounded pool.
|
|
19
|
+
# Nested trust discovery shares the original validation budget.
|
|
21
20
|
class ValidatorThread # :nodoc: all
|
|
22
|
-
|
|
21
|
+
WORKER_COUNT = 4
|
|
22
|
+
QUEUE_CAPACITY = 64
|
|
23
|
+
@worker_mutex = Mutex.new
|
|
24
|
+
|
|
25
|
+
# Chain discovery performs synchronous queries, so validation must run away
|
|
26
|
+
# from the selector. Bound both threads and queued responses under load.
|
|
27
|
+
def self.enqueue(validator)
|
|
28
|
+
rejected = []
|
|
29
|
+
accepted = @worker_mutex.synchronize do
|
|
30
|
+
# Threads do not survive fork. Parent work belongs to the parent, while
|
|
31
|
+
# the child's first admission must start its own pool.
|
|
32
|
+
if @worker_pid != Process.pid
|
|
33
|
+
@worker_pid = Process.pid
|
|
34
|
+
@work_queue = SizedQueue.new(QUEUE_CAPACITY)
|
|
35
|
+
@workers = []
|
|
36
|
+
end
|
|
37
|
+
@workers.select!(&:alive?)
|
|
38
|
+
begin
|
|
39
|
+
(WORKER_COUNT - @workers.length).times do
|
|
40
|
+
queue = @work_queue
|
|
41
|
+
@workers << Thread.new do
|
|
42
|
+
loop do
|
|
43
|
+
work = queue.pop
|
|
44
|
+
begin
|
|
45
|
+
work.do_validate
|
|
46
|
+
rescue StandardError => error
|
|
47
|
+
Dnsruby.log.error { "DNSSEC validation worker: #{error}" }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
rescue ThreadError
|
|
53
|
+
# A partial pool remains usable and is replenished on later admission.
|
|
54
|
+
# If no worker could start, fail queued work rather than strand it.
|
|
55
|
+
end
|
|
56
|
+
if @workers.empty?
|
|
57
|
+
begin
|
|
58
|
+
loop { rejected << @work_queue.pop(true) }
|
|
59
|
+
rescue ThreadError
|
|
60
|
+
end
|
|
61
|
+
false
|
|
62
|
+
else
|
|
63
|
+
begin
|
|
64
|
+
@work_queue.push(validator, true)
|
|
65
|
+
true
|
|
66
|
+
rescue ThreadError
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
rejected.each(&:reject_capacity)
|
|
72
|
+
accepted
|
|
73
|
+
end
|
|
74
|
+
|
|
23
75
|
def initialize(*args)
|
|
24
|
-
@client_id, @client_queue, @response, @error, @query, @st, @res = args
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# @@mutex = Mutex.new
|
|
28
|
-
# @@validation_queue = Queue.new
|
|
29
|
-
# @@validator_thread = Thread.new{
|
|
30
|
-
# do_validate
|
|
31
|
-
# }
|
|
76
|
+
@client_id, @client_queue, @response, @error, @query, @st, @res, @transmission_budget = args
|
|
77
|
+
@registered = false
|
|
78
|
+
prepare_budget if should_validate
|
|
32
79
|
end
|
|
80
|
+
|
|
81
|
+
def prepare_budget
|
|
82
|
+
return @budget if @budget
|
|
83
|
+
deadline = @st.validation_deadline(@client_queue, @client_id) if @st.respond_to?(:validation_deadline)
|
|
84
|
+
if !@transmission_budget && @st.respond_to?(:validation_transmission_budget)
|
|
85
|
+
@transmission_budget = @st.validation_transmission_budget(@client_queue, @client_id)
|
|
86
|
+
end
|
|
87
|
+
@transmission_budget ||= SelectThread::TransmissionBudget.current || SelectThread::TransmissionBudget.new
|
|
88
|
+
lookup_budget = @transmission_budget.context(:validation) { nil }
|
|
89
|
+
@budget = lookup_budget ? lookup_budget.child(deadline: deadline) : ValidationBudget.new(deadline: deadline)
|
|
90
|
+
end
|
|
91
|
+
private :prepare_budget
|
|
92
|
+
|
|
93
|
+
def register
|
|
94
|
+
return true if @registered
|
|
95
|
+
prepare_budget
|
|
96
|
+
return false unless @st.register_validation(@client_queue, @res, @budget.control)
|
|
97
|
+
@registered = true
|
|
98
|
+
end
|
|
99
|
+
private :register
|
|
100
|
+
|
|
33
101
|
def run
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
102
|
+
if should_validate
|
|
103
|
+
return unless register
|
|
104
|
+
unless self.class.enqueue(self)
|
|
105
|
+
reject_capacity
|
|
106
|
+
end
|
|
39
107
|
else
|
|
40
108
|
do_validate
|
|
41
109
|
end
|
|
42
110
|
end
|
|
43
111
|
|
|
44
|
-
|
|
45
|
-
# def add_to_queue(item)
|
|
46
|
-
# print "ADding to validator queue\n"
|
|
47
|
-
# # @@mutex.synchronize{
|
|
48
|
-
# @@validation_queue.push(item)
|
|
49
|
-
# # }
|
|
50
|
-
# end
|
|
51
112
|
def do_validate
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# print "Popped validation request\n"
|
|
59
|
-
# client_id, client_queue, response, err, query, st, res = item
|
|
60
|
-
validated_ok = validate(@query, @response, @res)
|
|
61
|
-
|
|
62
|
-
validated_ok = false if (@error && !(NXDomain === @error))
|
|
63
|
-
|
|
64
|
-
cache_if_valid(@query, @response)
|
|
65
|
-
|
|
66
|
-
# Now send the response back to the client...
|
|
67
|
-
# print "#{Time.now} : Got result for #{@query.question()[0].qname}, #{@query.question()[0].qtype}\n"
|
|
68
|
-
if (validated_ok)
|
|
69
|
-
@st.push_validation_response_to_select(@client_id, @client_queue, @response, nil, @query, @res)
|
|
113
|
+
needs_validation = should_validate
|
|
114
|
+
return if needs_validation && !register
|
|
115
|
+
validated_ok = if needs_validation
|
|
116
|
+
SelectThread::TransmissionBudget.with(@transmission_budget) do
|
|
117
|
+
ValidationBudget.with(@budget) { validate(@query, @response, @res) }
|
|
118
|
+
end
|
|
70
119
|
else
|
|
71
|
-
|
|
72
|
-
|
|
120
|
+
validate(@query, @response, @res)
|
|
121
|
+
end
|
|
122
|
+
validated_ok = false if @error && !@error.is_a?(NXDomain)
|
|
123
|
+
if @response.security_level == Message::SecurityLevel.BOGUS
|
|
124
|
+
detail = @response.security_error.to_s
|
|
125
|
+
raise VerifyError, detail.empty? ? 'DNSSEC validation failed' : detail
|
|
73
126
|
end
|
|
127
|
+
@budget.check! if needs_validation
|
|
128
|
+
cache_if_valid(@query, @response) if validated_ok
|
|
129
|
+
@st.push_validation_response_to_select(@client_id, @client_queue, @response,
|
|
130
|
+
validated_ok ? nil : @response.security_error, @query, @res)
|
|
131
|
+
rescue StandardError => error
|
|
132
|
+
# Resolver observers ignore late completions; direct PacketSender callers
|
|
133
|
+
# still need a terminal error after cancellation or their sender closing.
|
|
134
|
+
unless error.is_a?(VerifyError) || error.is_a?(RecursionLimitError)
|
|
135
|
+
error = VerifyError.new("DNSSEC validation failed: #{error.message}")
|
|
136
|
+
end
|
|
137
|
+
finish_error(error)
|
|
138
|
+
ensure
|
|
139
|
+
unregister
|
|
140
|
+
end
|
|
74
141
|
|
|
142
|
+
def finish_error(error)
|
|
143
|
+
@response.security_error = error
|
|
144
|
+
@response.security_level = Message::SecurityLevel.BOGUS
|
|
145
|
+
@st.push_validation_response_to_select(@client_id, @client_queue, @response,
|
|
146
|
+
error, @query, @res)
|
|
147
|
+
end
|
|
148
|
+
private :finish_error
|
|
75
149
|
|
|
76
|
-
|
|
150
|
+
def unregister
|
|
151
|
+
@st.unregister_validation(@client_queue, @res, @budget.control) if @registered
|
|
152
|
+
@registered = false
|
|
77
153
|
end
|
|
154
|
+
private :unregister
|
|
78
155
|
|
|
156
|
+
def reject_capacity
|
|
157
|
+
finish_error(ValidationLimitError.new('DNSSEC validation capacity exceeded'))
|
|
158
|
+
ensure
|
|
159
|
+
unregister
|
|
160
|
+
end
|
|
79
161
|
|
|
80
162
|
def should_validate
|
|
81
163
|
return ValidatorThread.requires_validation?(@query, @response, @error, @res)
|
|
@@ -95,26 +177,25 @@ module Dnsruby
|
|
|
95
177
|
end
|
|
96
178
|
|
|
97
179
|
def validate(query, response, res)
|
|
98
|
-
|
|
180
|
+
return true unless should_validate
|
|
181
|
+
ValidationBudget.with do
|
|
99
182
|
begin
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return true
|
|
183
|
+
validated = Dnssec.validate_with_query(query, response)
|
|
184
|
+
if !validated || response.security_level == Message::SecurityLevel.BOGUS
|
|
185
|
+
error = response.security_error
|
|
186
|
+
error = VerifyError.new(error.to_s.empty? ? 'DNSSEC validation failed' : error.to_s) unless error.is_a?(VerifyError)
|
|
187
|
+
response.security_error = error
|
|
188
|
+
response.security_level = Message::SecurityLevel.BOGUS
|
|
189
|
+
return false
|
|
190
|
+
end
|
|
191
|
+
true
|
|
110
192
|
rescue VerifyError => e
|
|
111
193
|
response.security_error = e
|
|
112
194
|
response.security_level = Message::SecurityLevel.BOGUS
|
|
113
195
|
# Response security_level should already be set
|
|
114
|
-
|
|
196
|
+
false
|
|
115
197
|
end
|
|
116
198
|
end
|
|
117
|
-
return true
|
|
118
199
|
end
|
|
119
200
|
|
|
120
201
|
def cache_if_valid(query, response)
|
data/lib/dnsruby/version.rb
CHANGED