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/code_mappers.rb
CHANGED
|
@@ -2,6 +2,31 @@ module Dnsruby
|
|
|
2
2
|
|
|
3
3
|
require 'dnsruby/code_mapper'
|
|
4
4
|
|
|
5
|
+
# DNS types and classes are unsigned 16-bit values. Other CodeMapper
|
|
6
|
+
# subclasses may use different value types or ranges.
|
|
7
|
+
module RRCodeRange #:nodoc: all
|
|
8
|
+
def self.check(code)
|
|
9
|
+
unless code.is_a?(Integer) && code.between?(0, 0xffff)
|
|
10
|
+
raise ArgumentError, "DNS type/class code must be an integer in 0..65535, got #{code.inspect}"
|
|
11
|
+
end
|
|
12
|
+
code
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(arg)
|
|
16
|
+
super
|
|
17
|
+
# Registered names and copies of mutable mapper objects need checking too.
|
|
18
|
+
RRCodeRange.check(@code)
|
|
19
|
+
if arg.is_a?(self.class) && arg.string.is_a?(String) &&
|
|
20
|
+
self.class.to_code(arg.string).nil? &&
|
|
21
|
+
(is_a?(Classes) ? /\ACLASS\d+\z/i : /\ATYPE\d+\z/i).match?(arg.string)
|
|
22
|
+
# Keep unregistered numeric spellings on the copy. Registered names
|
|
23
|
+
# use the current preferred alias from CodeMapper.
|
|
24
|
+
@string = arg.string
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
private_constant :RRCodeRange
|
|
29
|
+
|
|
5
30
|
class OpCode < CodeMapper
|
|
6
31
|
Query = 0 # RFC 1035
|
|
7
32
|
IQuery = 1 # RFC 1035
|
|
@@ -42,6 +67,8 @@ module Dnsruby
|
|
|
42
67
|
end
|
|
43
68
|
|
|
44
69
|
class Classes < CodeMapper
|
|
70
|
+
include RRCodeRange
|
|
71
|
+
|
|
45
72
|
IN = 1 # RFC 1035
|
|
46
73
|
CH = 3 # RFC 1035
|
|
47
74
|
# CHAOS = 3 # RFC 1035
|
|
@@ -52,16 +79,20 @@ module Dnsruby
|
|
|
52
79
|
update()
|
|
53
80
|
|
|
54
81
|
def unknown_string(arg)
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
if (match = /\ACLASS(\d+)\z/i.match(arg))
|
|
83
|
+
# Store one name per code so case and leading-zero variants cannot
|
|
84
|
+
# fill the global mapping table.
|
|
85
|
+
unknown_code(match[1].to_i)
|
|
86
|
+
# Keep the supplied spelling on this object without registering it.
|
|
87
|
+
@string = arg.dup.freeze
|
|
58
88
|
else
|
|
59
89
|
raise ArgumentError.new("String #{arg} not a member of #{self.class}")
|
|
60
90
|
end
|
|
61
91
|
end
|
|
62
92
|
|
|
63
93
|
def unknown_code(arg)
|
|
64
|
-
|
|
94
|
+
RRCodeRange.check(arg)
|
|
95
|
+
Classes.add_pair('CLASS' + arg.to_s, arg) unless Classes.to_string(arg)
|
|
65
96
|
set_code(arg)
|
|
66
97
|
end
|
|
67
98
|
|
|
@@ -73,37 +104,29 @@ module Dnsruby
|
|
|
73
104
|
name = name.upcase
|
|
74
105
|
|
|
75
106
|
if to_code(name)
|
|
76
|
-
return to_code(name)
|
|
107
|
+
return RRCodeRange.check(to_code(name))
|
|
77
108
|
end
|
|
78
109
|
|
|
79
|
-
if ((name
|
|
110
|
+
if ((name =~/\A\s*CLASS(\d+)\s*\z/o) == nil)
|
|
80
111
|
raise ArgumentError, "classesbyval() argument is not CLASS### (#{name})"
|
|
81
112
|
end
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
if val > 0xffff
|
|
85
|
-
raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
return val;
|
|
114
|
+
RRCodeRange.check($1.to_i)
|
|
89
115
|
end
|
|
90
116
|
|
|
91
117
|
|
|
92
118
|
|
|
93
119
|
def Classes.classesbyval(val) #:nodoc: all
|
|
94
120
|
if (val.class == String)
|
|
95
|
-
if ((val =~
|
|
121
|
+
if ((val =~ /\A\s*([0-9]+)\s*\z/) == nil)
|
|
96
122
|
raise ArgumentError, "classesbybal() argument is not numeric (#{val})" # unless val.gsub!("^\s*0*([0-9]+)\s*$", "$1")
|
|
97
123
|
# val =~ s/^\s*0*([0-9]+)\s*$/$1/o;#
|
|
98
124
|
end
|
|
99
125
|
val = $1.to_i
|
|
100
126
|
end
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff if val > 0xffff;
|
|
105
|
-
|
|
106
|
-
return "CLASS#{val}";
|
|
128
|
+
RRCodeRange.check(val)
|
|
129
|
+
to_string(val) || "CLASS#{val}"
|
|
107
130
|
end
|
|
108
131
|
end
|
|
109
132
|
|
|
@@ -111,6 +134,8 @@ module Dnsruby
|
|
|
111
134
|
#
|
|
112
135
|
# New RR types should be added to this set
|
|
113
136
|
class Types < CodeMapper
|
|
137
|
+
include RRCodeRange
|
|
138
|
+
|
|
114
139
|
SIGZERO = 0 # RFC2931 consider this a pseudo type
|
|
115
140
|
A = 1 # RFC 1035, Section 3.4.1
|
|
116
141
|
NS = 2 # RFC 1035, Section 3.3.11
|
|
@@ -150,7 +175,7 @@ module Dnsruby
|
|
|
150
175
|
KX = 36 # RFC 2230
|
|
151
176
|
CERT = 37 # RFC 2538
|
|
152
177
|
DNAME = 39 # RFC 2672
|
|
153
|
-
OPT = 41 # RFC
|
|
178
|
+
OPT = 41 # RFC 6891
|
|
154
179
|
APL = 42 # RFC 3123
|
|
155
180
|
DS = 43 # RFC 4034
|
|
156
181
|
SSHFP = 44 # RFC 4255
|
|
@@ -165,13 +190,15 @@ module Dnsruby
|
|
|
165
190
|
HIP = 55 # RFC 5205
|
|
166
191
|
CDS = 59 # RFC 7344
|
|
167
192
|
CDNSKEY = 60 # RFC 7344
|
|
193
|
+
SVCB = 64 # RFC 9460
|
|
194
|
+
HTTPS = 65 # RFC 9460
|
|
168
195
|
SPF = 99 # RFC 4408
|
|
169
196
|
UINFO = 100 # non-standard
|
|
170
197
|
UID = 101 # non-standard
|
|
171
198
|
GID = 102 # non-standard
|
|
172
199
|
UNSPEC = 103 # non-standard
|
|
173
200
|
TKEY = 249 # RFC 2930
|
|
174
|
-
TSIG = 250 # RFC
|
|
201
|
+
TSIG = 250 # RFC 8945, Section 4.1
|
|
175
202
|
IXFR = 251 # RFC 1995
|
|
176
203
|
AXFR = 252 # RFC 1035
|
|
177
204
|
MAILB = 253 # RFC 1035 (MB, MG, MR)
|
|
@@ -183,16 +210,18 @@ module Dnsruby
|
|
|
183
210
|
update()
|
|
184
211
|
|
|
185
212
|
def unknown_string(arg) #:nodoc: all
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
213
|
+
if (match = /\ATYPE(\d+)\z/i.match(arg))
|
|
214
|
+
# Store one name per code; keep alternate spellings on this object.
|
|
215
|
+
unknown_code(match[1].to_i)
|
|
216
|
+
@string = arg.dup.freeze
|
|
189
217
|
else
|
|
190
218
|
raise ArgumentError.new("String #{arg} not a member of #{self.class}")
|
|
191
219
|
end
|
|
192
220
|
end
|
|
193
221
|
|
|
194
222
|
def unknown_code(arg) #:nodoc: all
|
|
195
|
-
|
|
223
|
+
RRCodeRange.check(arg)
|
|
224
|
+
Types.add_pair('TYPE' + arg.to_s, arg) unless Types.to_string(arg)
|
|
196
225
|
set_code(arg)
|
|
197
226
|
end
|
|
198
227
|
|
|
@@ -206,20 +235,15 @@ module Dnsruby
|
|
|
206
235
|
name = name.upcase
|
|
207
236
|
|
|
208
237
|
if to_code(name)
|
|
209
|
-
return to_code(name)
|
|
238
|
+
return RRCodeRange.check(to_code(name))
|
|
210
239
|
end
|
|
211
240
|
|
|
212
241
|
|
|
213
|
-
if ((name
|
|
242
|
+
if ((name =~/\A\s*TYPE(\d+)\s*\z/o)==nil)
|
|
214
243
|
raise ArgumentError, "Net::DNS::typesbyname() argument (#{name}) is not TYPE###"
|
|
215
244
|
end
|
|
216
245
|
|
|
217
|
-
|
|
218
|
-
if val > 0xffff
|
|
219
|
-
raise ArgumentError, 'Net::DNS::typesbyname() argument larger than ' + 0xffff
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
return val;
|
|
246
|
+
RRCodeRange.check($1.to_i)
|
|
223
247
|
end
|
|
224
248
|
|
|
225
249
|
|
|
@@ -227,29 +251,17 @@ module Dnsruby
|
|
|
227
251
|
# code. If the TYPE mapping is not specified the generic mnemonic
|
|
228
252
|
# TYPE### is returned.
|
|
229
253
|
def Types.typesbyval(val) #:nodoc: all
|
|
230
|
-
if (!defined?val)
|
|
231
|
-
raise ArgumentError, "Net::DNS::typesbyval() argument is not defined"
|
|
232
|
-
end
|
|
233
|
-
|
|
234
254
|
if val.class == String
|
|
235
|
-
|
|
236
|
-
if ((val =~ /^\s*0*(\d+)\s*$", "$1/o) == nil)
|
|
255
|
+
if ((val =~ /\A\s*(\d+)\s*\z/) == nil)
|
|
237
256
|
raise ArgumentError, "Net::DNS::typesbyval() argument (#{val}) is not numeric"
|
|
238
|
-
# val =~s/^\s*0*(\d+)\s*$/$1/o;
|
|
239
257
|
end
|
|
240
258
|
|
|
241
259
|
val = $1.to_i
|
|
242
260
|
end
|
|
243
261
|
|
|
244
262
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
raise ArgumentError, 'Net::DNS::typesbyval() argument larger than ' + 0xffff if
|
|
250
|
-
val > 0xffff;
|
|
251
|
-
|
|
252
|
-
return "TYPE#{val}";
|
|
263
|
+
RRCodeRange.check(val)
|
|
264
|
+
to_string(val) || "TYPE#{val}"
|
|
253
265
|
end
|
|
254
266
|
end
|
|
255
267
|
|
|
@@ -264,8 +276,8 @@ module Dnsruby
|
|
|
264
276
|
|
|
265
277
|
class MetaTypes < CodeMapper
|
|
266
278
|
TKEY = 249 # Transaction Key [RFC2930]
|
|
267
|
-
TSIG = 250 # Transaction Signature [
|
|
268
|
-
OPT = 41 # RFC
|
|
279
|
+
TSIG = 250 # Transaction Signature [RFC8945]
|
|
280
|
+
OPT = 41 # RFC 6891
|
|
269
281
|
end
|
|
270
282
|
|
|
271
283
|
# http://www.iana.org/assignments/dns-sec-alg-numbers/
|
|
@@ -279,6 +291,8 @@ module Dnsruby
|
|
|
279
291
|
RSASHA512 = 10
|
|
280
292
|
ECDSAP256SHA256 = 13
|
|
281
293
|
ECDSAP384SHA384 = 14
|
|
294
|
+
ED25519 = 15
|
|
295
|
+
ED448 = 16
|
|
282
296
|
INDIRECT = 252
|
|
283
297
|
PRIVATEDNS = 253
|
|
284
298
|
PRIVATEOID = 254
|
data/lib/dnsruby/config.rb
CHANGED
|
@@ -127,6 +127,19 @@ module Dnsruby
|
|
|
127
127
|
dom = config_hash[:domain] if config_hash.include? :domain
|
|
128
128
|
|
|
129
129
|
if (!@configured)
|
|
130
|
+
# A bad file entry must not hide the other servers or prevent an
|
|
131
|
+
# explicit resolver override. Keep programmatic configuration strict.
|
|
132
|
+
unless config_info.kind_of?(Hash) && config_info.key?(:nameserver)
|
|
133
|
+
ns = ns.select do |server|
|
|
134
|
+
begin
|
|
135
|
+
check_ns([server])
|
|
136
|
+
true
|
|
137
|
+
rescue ArgumentError, ResolvError => error
|
|
138
|
+
Dnsruby.log.warn { "Ignoring invalid nameserver #{server.inspect}: #{error.message}" }
|
|
139
|
+
false
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
130
143
|
send("nameserver=",ns)
|
|
131
144
|
end
|
|
132
145
|
@configured = true
|
|
@@ -202,6 +215,9 @@ module Dnsruby
|
|
|
202
215
|
end
|
|
203
216
|
ns.each {|n|
|
|
204
217
|
if (String ===n)
|
|
218
|
+
if Config.scoped_ipv6_server?(n)
|
|
219
|
+
next
|
|
220
|
+
end
|
|
205
221
|
# Make sure we can make a Name or an address from it
|
|
206
222
|
begin
|
|
207
223
|
IPv4.create(n)
|
|
@@ -247,6 +263,52 @@ module Dnsruby
|
|
|
247
263
|
Dnsruby.log.debug{"Nameservers = #{@nameserver.join(", ")}"}
|
|
248
264
|
end
|
|
249
265
|
|
|
266
|
+
# Validate an IPv6 socket endpoint without putting its local interface zone
|
|
267
|
+
# into a DNS address. Callers must retain the original string for socket I/O.
|
|
268
|
+
def Config.ipv6_server_address(server) #:nodoc: all
|
|
269
|
+
if String === server && server.include?('%')
|
|
270
|
+
address, zone = server.split('%', 2)
|
|
271
|
+
unless /\A[0-9A-Za-z_.-]+\z/.match?(zone)
|
|
272
|
+
raise ArgumentError, "invalid IPv6 zone identifier: #{server.inspect}"
|
|
273
|
+
end
|
|
274
|
+
return IPv6.create(address)
|
|
275
|
+
end
|
|
276
|
+
IPv6.create(server)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# DNS labels can contain both ':' and '%'. Recognize an endpoint only
|
|
280
|
+
# when the complete address and zone are valid; otherwise keep DNS lookup.
|
|
281
|
+
def Config.scoped_ipv6_server?(server) #:nodoc: all
|
|
282
|
+
return false unless String === server && server.include?('%')
|
|
283
|
+
ipv6_server_address(server)
|
|
284
|
+
true
|
|
285
|
+
rescue ArgumentError
|
|
286
|
+
false
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def Config.nameserver_resolver #:nodoc: all
|
|
290
|
+
config = Config.default_config_hash
|
|
291
|
+
# Bootstrap hostname lookups only through servers that need no DNS
|
|
292
|
+
# lookup themselves. Keep hostnames in the caller's configuration.
|
|
293
|
+
servers = Array(config[:nameserver]).select do |server|
|
|
294
|
+
next true if server == 'localhost'
|
|
295
|
+
begin
|
|
296
|
+
IPv4.create(server)
|
|
297
|
+
rescue ArgumentError
|
|
298
|
+
begin
|
|
299
|
+
Config.ipv6_server_address(server)
|
|
300
|
+
rescue ArgumentError
|
|
301
|
+
next false
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
true
|
|
305
|
+
end
|
|
306
|
+
if servers.empty?
|
|
307
|
+
raise ArgumentError, 'No nameserver addresses available for hostname lookup'
|
|
308
|
+
end
|
|
309
|
+
Resolver.new(config_info: config.merge(nameserver: servers))
|
|
310
|
+
end
|
|
311
|
+
|
|
250
312
|
def Config.resolve_server(ns) #:nodoc: all
|
|
251
313
|
# Sanity check server
|
|
252
314
|
# If it's an IP address, then use that for server
|
|
@@ -255,6 +317,10 @@ module Dnsruby
|
|
|
255
317
|
if (Name === ns)
|
|
256
318
|
ns = ns.to_s
|
|
257
319
|
end
|
|
320
|
+
# Keep the complete endpoint so the OS can choose the interface.
|
|
321
|
+
if scoped_ipv6_server?(ns)
|
|
322
|
+
return ns
|
|
323
|
+
end
|
|
258
324
|
begin
|
|
259
325
|
addr = IPv4.create(ns)
|
|
260
326
|
server = ns
|
|
@@ -269,8 +335,8 @@ module Dnsruby
|
|
|
269
335
|
server = "127.0.0.1"
|
|
270
336
|
else
|
|
271
337
|
# Use Dnsruby to resolve the servers
|
|
272
|
-
# First, try the default resolvers
|
|
273
|
-
resolver =
|
|
338
|
+
# First, try the default resolvers with known addresses
|
|
339
|
+
resolver = Config.nameserver_resolver
|
|
274
340
|
found = false
|
|
275
341
|
begin
|
|
276
342
|
ret = resolver.query(ns)
|
|
@@ -281,16 +347,18 @@ module Dnsruby
|
|
|
281
347
|
found = true
|
|
282
348
|
end
|
|
283
349
|
}
|
|
284
|
-
rescue Exception
|
|
350
|
+
rescue Exception => error
|
|
351
|
+
raise if ValidationBudget.abort?(error)
|
|
285
352
|
end
|
|
286
353
|
if (!found)
|
|
287
354
|
# That didn't work - try recursing from the root
|
|
288
|
-
recursor = Recursor.new
|
|
355
|
+
recursor = Recursor.new(resolver)
|
|
289
356
|
ret = recursor.query(ns)
|
|
290
357
|
ret.answer.each {|rr|
|
|
291
358
|
if ([Types::A, Types::AAAA].include?rr.type)
|
|
292
359
|
addr = rr.address.to_s
|
|
293
360
|
server = addr
|
|
361
|
+
found = true
|
|
294
362
|
end
|
|
295
363
|
}
|
|
296
364
|
if (!found)
|
|
@@ -301,9 +369,12 @@ module Dnsruby
|
|
|
301
369
|
end
|
|
302
370
|
end
|
|
303
371
|
rescue Exception => e
|
|
372
|
+
raise if ValidationBudget.abort?(e)
|
|
304
373
|
Dnsruby.log.error{"Can't make sense of nameserver : #{server}, exception : #{e}"}
|
|
305
374
|
raise ArgumentError.new("Can't make sense of nameserver : #{server}, exception : #{e}")
|
|
306
375
|
return nil
|
|
376
|
+
ensure
|
|
377
|
+
resolver.close if resolver
|
|
307
378
|
end
|
|
308
379
|
end
|
|
309
380
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Dnsruby
|
|
2
|
+
# Indexed minimum heap. Updating or cancelling a deadline removes its old
|
|
3
|
+
# entry immediately, so storage never exceeds the number of live owners.
|
|
4
|
+
# Callers provide synchronization alongside their corresponding query tables.
|
|
5
|
+
class DeadlineQueue # :nodoc: all
|
|
6
|
+
def initialize
|
|
7
|
+
@heap = []
|
|
8
|
+
@positions = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def first
|
|
12
|
+
@heap.first
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first_deadline
|
|
16
|
+
@heap.first&.last
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def []=(key, deadline)
|
|
20
|
+
index = @positions[key]
|
|
21
|
+
if index
|
|
22
|
+
@heap[index][1] = deadline
|
|
23
|
+
move_down(move_up(index))
|
|
24
|
+
else
|
|
25
|
+
@positions[key] = @heap.length
|
|
26
|
+
@heap << [key, deadline]
|
|
27
|
+
move_up(@heap.length - 1)
|
|
28
|
+
end
|
|
29
|
+
deadline
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def delete(key)
|
|
33
|
+
index = @positions.delete(key)
|
|
34
|
+
return unless index
|
|
35
|
+
removed = @heap[index]
|
|
36
|
+
last = @heap.pop
|
|
37
|
+
if index < @heap.length
|
|
38
|
+
@heap[index] = last
|
|
39
|
+
@positions[last[0]] = index
|
|
40
|
+
move_down(move_up(index))
|
|
41
|
+
end
|
|
42
|
+
removed[1]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def swap(left, right)
|
|
48
|
+
@heap[left], @heap[right] = @heap[right], @heap[left]
|
|
49
|
+
@positions[@heap[left][0]] = left
|
|
50
|
+
@positions[@heap[right][0]] = right
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def move_up(index)
|
|
54
|
+
while index > 0
|
|
55
|
+
parent = (index - 1) / 2
|
|
56
|
+
break if @heap[parent][1] <= @heap[index][1]
|
|
57
|
+
swap(parent, index)
|
|
58
|
+
index = parent
|
|
59
|
+
end
|
|
60
|
+
index
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def move_down(index)
|
|
64
|
+
loop do
|
|
65
|
+
child = index * 2 + 1
|
|
66
|
+
break if child >= @heap.length
|
|
67
|
+
right = child + 1
|
|
68
|
+
child = right if right < @heap.length && @heap[right][1] < @heap[child][1]
|
|
69
|
+
break if @heap[index][1] <= @heap[child][1]
|
|
70
|
+
swap(index, child)
|
|
71
|
+
index = child
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|