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.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +16 -4
  3. data/DNSSEC +3 -2
  4. data/EXAMPLES +1 -1
  5. data/README.md +370 -64
  6. data/RELEASE_NOTES.md +302 -1
  7. data/Rakefile +0 -3
  8. data/demo/axfr.rb +24 -6
  9. data/demo/rubydig.rb +0 -2
  10. data/dnsruby.gemspec +5 -4
  11. data/lib/dnsruby/cache.rb +279 -47
  12. data/lib/dnsruby/code_mapper.rb +35 -8
  13. data/lib/dnsruby/code_mappers.rb +63 -49
  14. data/lib/dnsruby/config.rb +75 -4
  15. data/lib/dnsruby/deadline_queue.rb +75 -0
  16. data/lib/dnsruby/dnssec.rb +148 -45
  17. data/lib/dnsruby/ipv6.rb +45 -5
  18. data/lib/dnsruby/key_cache.rb +202 -35
  19. data/lib/dnsruby/message/decoder.rb +85 -19
  20. data/lib/dnsruby/message/encoder.rb +27 -16
  21. data/lib/dnsruby/message/header.rb +8 -12
  22. data/lib/dnsruby/message/message.rb +53 -22
  23. data/lib/dnsruby/message/section.rb +12 -34
  24. data/lib/dnsruby/name.rb +5 -2
  25. data/lib/dnsruby/packet_sender.rb +645 -203
  26. data/lib/dnsruby/presentation.rb +147 -0
  27. data/lib/dnsruby/query_control.rb +132 -0
  28. data/lib/dnsruby/recursor.rb +795 -339
  29. data/lib/dnsruby/resolver.rb +468 -186
  30. data/lib/dnsruby/resource/CAA.rb +67 -16
  31. data/lib/dnsruby/resource/DLV.rb +6 -0
  32. data/lib/dnsruby/resource/DNSKEY.rb +108 -16
  33. data/lib/dnsruby/resource/DS.rb +27 -28
  34. data/lib/dnsruby/resource/HTTPS.rb +16 -0
  35. data/lib/dnsruby/resource/IN.rb +19 -0
  36. data/lib/dnsruby/resource/MX.rb +2 -2
  37. data/lib/dnsruby/resource/NSEC.rb +6 -10
  38. data/lib/dnsruby/resource/NSEC3.rb +55 -19
  39. data/lib/dnsruby/resource/NSEC3PARAM.rb +4 -6
  40. data/lib/dnsruby/resource/RR.rb +108 -65
  41. data/lib/dnsruby/resource/RRSIG.rb +5 -5
  42. data/lib/dnsruby/resource/RRSet.rb +38 -22
  43. data/lib/dnsruby/resource/SRV.rb +2 -2
  44. data/lib/dnsruby/resource/SVCB.rb +775 -0
  45. data/lib/dnsruby/resource/TSIG.rb +266 -231
  46. data/lib/dnsruby/resource/TXT.rb +34 -42
  47. data/lib/dnsruby/resource/domain_name.rb +1 -1
  48. data/lib/dnsruby/resource/generic.rb +31 -0
  49. data/lib/dnsruby/resource/generic_cache.rb +384 -0
  50. data/lib/dnsruby/resource/generic_constants.rb +79 -0
  51. data/lib/dnsruby/select_thread.rb +979 -291
  52. data/lib/dnsruby/serial_time.rb +25 -0
  53. data/lib/dnsruby/single_resolver.rb +25 -6
  54. data/lib/dnsruby/single_verifier.rb +1620 -530
  55. data/lib/dnsruby/transport_clock.rb +9 -0
  56. data/lib/dnsruby/update.rb +8 -6
  57. data/lib/dnsruby/validation_budget.rb +143 -0
  58. data/lib/dnsruby/validator_thread.rb +139 -58
  59. data/lib/dnsruby/version.rb +1 -1
  60. data/lib/dnsruby/zone_reader.rb +196 -129
  61. data/lib/dnsruby/zone_transfer.rb +157 -25
  62. data/lib/dnsruby.rb +7 -2
  63. data/test/ci_watchdog.rb +33 -0
  64. data/test/fixtures/org-ds-2026-11-05.zone +5 -0
  65. data/test/fixtures/root-dnskey-2026-11-05.zone +7 -0
  66. data/test/fork_test_helper.rb +37 -0
  67. data/test/local_dns_server.rb +57 -0
  68. data/test/soak_server.rb +152 -0
  69. data/test/socket_helpers.rb +26 -0
  70. data/test/spec_helper.rb +8 -13
  71. data/test/tc_attempt_configuration.rb +137 -0
  72. data/test/tc_caa.rb +2 -2
  73. data/test/tc_cache_keys.rb +36 -0
  74. data/test/tc_cancelled_send_capacity.rb +158 -0
  75. data/test/tc_dnssec_alias_validation.rb +289 -0
  76. data/test/tc_dnssec_budget.rb +489 -0
  77. data/test/tc_dnssec_cache_bounds.rb +197 -0
  78. data/test/tc_dnssec_cache_lifetime.rb +615 -0
  79. data/test/tc_dnssec_denial_proofs.rb +285 -0
  80. data/test/tc_dnssec_recursion_limits.rb +186 -0
  81. data/test/tc_dnssec_recursor_review_fixes.rb +351 -0
  82. data/test/tc_dnssec_trust_anchors.rb +286 -0
  83. data/test/tc_dnssec_trust_races.rb +147 -0
  84. data/test/tc_dnssec_wildcard_proofs.rb +249 -0
  85. data/test/tc_ds.rb +5 -5
  86. data/test/tc_edns_retry_regressions.rb +255 -0
  87. data/test/tc_escapedchars.rb +8 -0
  88. data/test/tc_final_review_fixes.rb +167 -0
  89. data/test/tc_final_transport_contracts.rb +104 -0
  90. data/test/tc_generic_cache.rb +852 -0
  91. data/test/tc_header.rb +26 -0
  92. data/test/tc_ipv6.rb +120 -0
  93. data/test/tc_key_cache_identity.rb +382 -0
  94. data/test/tc_malformed_tcp.rb +712 -0
  95. data/test/tc_name.rb +99 -5
  96. data/test/tc_nameserver_bootstrap.rb +322 -0
  97. data/test/tc_nsec.rb +4 -45
  98. data/test/tc_nsec3.rb +17 -1
  99. data/test/tc_pipeline_retirement.rb +358 -0
  100. data/test/tc_pipeline_retry.rb +462 -0
  101. data/test/tc_pipeline_send.rb +663 -0
  102. data/test/tc_protocol_review_fixes.rb +95 -0
  103. data/test/tc_query_control.rb +90 -0
  104. data/test/tc_record_codec_contracts.rb +107 -0
  105. data/test/tc_record_regressions.rb +225 -0
  106. data/test/tc_recur.rb +1 -0
  107. data/test/tc_recursor_address_fallback.rb +169 -0
  108. data/test/tc_recursor_limits.rb +139 -0
  109. data/test/tc_release_auxiliary_fixes.rb +288 -0
  110. data/test/tc_release_configuration_fixes.rb +612 -0
  111. data/test/tc_release_dnssec_completion.rb +405 -0
  112. data/test/tc_release_dnssec_fixes.rb +305 -0
  113. data/test/tc_release_negative_cache.rb +327 -0
  114. data/test/tc_release_recursor_cache.rb +212 -0
  115. data/test/tc_release_review_transport.rb +372 -0
  116. data/test/tc_release_review_transport_native.rb +56 -0
  117. data/test/tc_release_transport.rb +906 -0
  118. data/test/tc_release_transport_fixes.rb +196 -0
  119. data/test/tc_release_zone_defaults.rb +157 -0
  120. data/test/tc_release_zone_ttls.rb +39 -0
  121. data/test/tc_res_config.rb +42 -20
  122. data/test/tc_res_opt.rb +21 -1
  123. data/test/tc_resolver.rb +21 -19
  124. data/test/tc_resolver_concurrency.rb +383 -0
  125. data/test/tc_resolver_configuration_contracts.rb +31 -0
  126. data/test/tc_resolver_validation.rb +211 -0
  127. data/test/tc_review_cache.rb +163 -0
  128. data/test/tc_review_code_mapper.rb +279 -0
  129. data/test/tc_review_control_deadlines.rb +169 -0
  130. data/test/tc_review_followup_transport.rb +583 -0
  131. data/test/tc_review_packet_contracts.rb +369 -0
  132. data/test/tc_review_presentation.rb +213 -0
  133. data/test/tc_review_record_compatibility.rb +101 -0
  134. data/test/tc_review_transport_fixes.rb +195 -0
  135. data/test/tc_review_transport_native.rb +415 -0
  136. data/test/tc_review_validation_priming.rb +178 -0
  137. data/test/tc_rr-opt.rb +5 -2
  138. data/test/tc_rr-unknown.rb +19 -0
  139. data/test/tc_rr.rb +14 -0
  140. data/test/tc_rrset_grouping.rb +50 -0
  141. data/test/tc_rrset_review_fixes.rb +58 -0
  142. data/test/tc_rrsig.rb +21 -1
  143. data/test/tc_scoped_ipv6.rb +153 -0
  144. data/test/tc_scoped_ipv6_select.rb +157 -0
  145. data/test/tc_scoped_ipv6_transport.rb +329 -0
  146. data/test/tc_selector_wakeup.rb +42 -0
  147. data/test/tc_soak.rb +118 -196
  148. data/test/tc_socket_helpers.rb +90 -0
  149. data/test/tc_supporting_lookup_limits.rb +252 -0
  150. data/test/tc_svcb.rb +1043 -0
  151. data/test/tc_svcb_signed_target.rb +145 -0
  152. data/test/tc_svcb_transport.rb +120 -0
  153. data/test/tc_tcp.rb +69 -43
  154. data/test/tc_tcp_pipelining.rb +95 -204
  155. data/test/tc_transport_fork.rb +179 -0
  156. data/test/tc_tsig.rb +2 -2
  157. data/test/tc_tsig_api.rb +251 -0
  158. data/test/tc_tsig_rfc8945.rb +357 -0
  159. data/test/tc_v2_release_dnssec.rb +442 -0
  160. data/test/tc_v2_release_transport.rb +304 -0
  161. data/test/tc_v2_zone_transfer_native.rb +58 -0
  162. data/test/tc_zone_reader.rb +217 -1
  163. data/test/tcp_pipelining_server.rb +150 -0
  164. data/test/ts_offline.rb +77 -6
  165. data/test/ts_online.rb +2 -1
  166. metadata +105 -10
  167. data/.coveralls.yml +0 -2
@@ -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 (arg=~/^CLASS/i)
56
- Classes.add_pair(arg, arg.gsub('CLASS', '').to_i)
57
- set_string(arg)
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
- Classes.add_pair('CLASS' + arg.to_s, arg)
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 =~/^\s*CLASS(\d+)\s*$/o) == nil)
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
- val = $1.to_i
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 =~ /^\s*0*([0-9]+)\s*$/) == nil)
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
- return to_string(val) if to_string(val)
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 2671
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 2931
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 (arg=~/^TYPE/i)
187
- Types.add_pair(arg, arg.gsub('TYPE', '').to_i)
188
- set_string(arg)
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
- Types.add_pair('TYPE' + arg.to_s, arg)
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 =~/^\s*TYPE(\d+)\s*$/o)==nil)
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
- val = $1.to_i
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
- # if val.gsub!("^\s*0*(\d+)\s*$", "$1")
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
- if to_string(val)
246
- return to_string(val)
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 [RFC2845]
268
- OPT = 41 # RFC 2671
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
@@ -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 = Resolver.new
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