dnsruby 1.73.1 → 1.74.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2a1396a168f57eb47e7c1c1717234c6339758a1662282ee9d3b20621224080b
4
- data.tar.gz: f99e64b08c29d90930dd4fea1159d840f8d942e8670599352a281ce1173f7561
3
+ metadata.gz: 31fc09cb1cf4b1c2174c67a16e96601eb25c6509e6525c3cef9159da5fd656c2
4
+ data.tar.gz: e96495f35da0ff50636052f3f27623b4fc620572c0c4109f6c47a5a81eb77801
5
5
  SHA512:
6
- metadata.gz: b6a9810d20e6a0e159ac5960a290faaa60acb4d8035b67c29c04772cfa8147922708dbef6160cc99de7442e23ed71f177c9d51d2a340f1c24b7d81ad4f9c0071
7
- data.tar.gz: 2810fb4b2522d8d97db1803f7ee23d761ee982e71063f970c47ed1d00ea9cf25926d8ee84f2a8747b70a6d5b76cc5f18eaf5b8739307c17a7645cbd3a9e91462
6
+ metadata.gz: 84c58dc7dbf8d3c33d11ee7ea10d076d806cf1700c327b5e84fbebac08484fba8179734dbe9631c85daec38518b29ca3d8e3a29629d71a69a955f615bdf60346
7
+ data.tar.gz: 06b7c82e2b1e979f1c18aaa9567596f6724a7fb5eae8da688921958506ab9cc9ed1657f168699ec6a0112c3c25afcba419d333b7f3b4f618537fa6e6f1ee91c7
@@ -8,7 +8,7 @@ jobs:
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- ruby: [ '3.2', '3.3', '3.4' ] # , 'ruby-head' ]
11
+ ruby: [ '3.2.10', '3.3.11', '3.4.10', '4.0.5' ] # , 'ruby-head' ]
12
12
  rubyopt: ['']
13
13
  include:
14
14
  - ruby: '3.4'
data/RELEASE_NOTES.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Release Notes
2
2
 
3
+ ## v1.74.0
4
+
5
+ * Resolve all configured nameservers in parallel
6
+ * Only raise ArgumentError from Resolver if _all_ configured nameservers are invalid - otherwise, just log the bad one(s) and continue
7
+ * print out extra OPT data
8
+ * Fix wakeup socket deletion on stopping of select thread
9
+ * Make sure to add nameservers if using config_info in Resolver
10
+ * Fix for RRSET pruning and potential cache poisoning - thanks Alexander Tan
11
+ * Fix WKS record protocol field encoding and decoding size according to RFC 1035 Section 3.4.2 where PROTOCOL is an 8 bit (#220)
12
+ * Updating workflows to include Ruby 4, and get rdoc working for it
13
+
3
14
  ## v1.73.1
15
+
4
16
  * fix(caa): add support for issuevmc (#214) - thanks Mark McDonnell!
5
17
 
6
18
  ## v1.73.0
data/demo/rubydig.rb CHANGED
@@ -60,6 +60,8 @@ if ARGV[0] =~ /^@/
60
60
  else
61
61
  puts "Setting nameserver : #{nameserver}"
62
62
  resolver.nameserver = (nameserver.sub(/^@/, ''))
63
+ resolver.dnssec = true
64
+ resolver.do_validation = true
63
65
  puts "nameservers = #{resolver.config.nameserver}"
64
66
  zone_transfer.server = (nameserver.sub(/^@/, ''))
65
67
  end
data/dnsruby.gemspec CHANGED
@@ -43,6 +43,7 @@ DNSSEC NSEC3 support.'
43
43
  s.add_development_dependency 'rubydns', '>= 2.0.2'
44
44
  s.add_development_dependency 'nio4r', '>= 2.5.8'
45
45
  s.add_development_dependency 'minitest-display', '>= 0.3.1'
46
+ s.add_development_dependency 'rdoc', '>= 6.5', '< 8.0'
46
47
  s.add_development_dependency 'yard', '>= 0.9'
47
48
  s.add_development_dependency 'coveralls', '~> 0.8.23'
48
49
 
data/lib/dnsruby/cache.rb CHANGED
@@ -68,14 +68,12 @@ module Dnsruby
68
68
  # This method "fixes up" the response, so that the header and ttls are OK
69
69
  # The resolver will still need to copy the flags and ID across from the query
70
70
  def find(qname, qtype, qclass = Classes.IN)
71
- # print "CACHE find : #{qname}, #{qtype}\n"
72
71
  qn = Name.create(qname)
73
72
  qn.absolute = true
74
73
  key = CacheKey.new(qn, qtype, qclass).to_s
75
74
  @mutex.synchronize {
76
75
  data = @cache[key]
77
76
  if (!data)
78
- # print "CACHE lookup failed\n"
79
77
  return nil
80
78
  end
81
79
  if (data.expiration <= Time.now.to_i)
@@ -294,6 +294,8 @@ module Dnsruby
294
294
  end
295
295
  }
296
296
  if (!found)
297
+ # Log the error, in case Resolver is resolving a set of nameservers, and swallows the exception
298
+ TheLog.error{"Recursor can't locate #{server}"}
297
299
  raise ArgumentError.new("Recursor can't locate #{server}")
298
300
  end
299
301
  end
@@ -204,6 +204,7 @@ module Dnsruby
204
204
  @tcp_pipelining_max_queries = :infinite
205
205
  @use_counts = {}
206
206
 
207
+ ignore_config_resolv_errors = false;
207
208
  if arg.nil?
208
209
  elsif arg.kind_of? String
209
210
  @server = arg
@@ -212,10 +213,14 @@ module Dnsruby
212
213
  elsif arg.kind_of? Hash
213
214
  arg.keys.each do |attr|
214
215
  begin
215
- if ((attr.to_s == "src_address" || attr.to_s == "src_address6") &&
216
- (arg[attr] == nil || arg[attr] == ""))
216
+ if (attr.to_s == "ignore_config_resolv_errors")
217
+ ignore_config_resolv_errors = arg[attr]
217
218
  else
218
- send(attr.to_s + "=", arg[attr])
219
+ if ((attr.to_s == "src_address" || attr.to_s == "src_address6") &&
220
+ (arg[attr] == nil || arg[attr] == ""))
221
+ else
222
+ send(attr.to_s + "=", arg[attr])
223
+ end
219
224
  end
220
225
  rescue Exception => e
221
226
  Dnsruby.log.error { "PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n" }
@@ -223,7 +228,17 @@ module Dnsruby
223
228
  end
224
229
  end
225
230
  # Check server is IP
226
- @server=Config.resolve_server(@server)
231
+ # We should allow the call of PacketSender.new to flag just to log an error if this fails
232
+ # and then return null
233
+ if ignore_config_resolv_errors
234
+ begin
235
+ @server=Config.resolve_server(@server)
236
+ rescue ArgumentError
237
+ return
238
+ end
239
+ else
240
+ @server=Config.resolve_server(@server)
241
+ end
227
242
 
228
243
  check_ipv6
229
244
  # ResolverRegister::register_single_resolver(self)
@@ -678,7 +678,8 @@ module Dnsruby
678
678
  if (of.length <= known_zone.length)
679
679
  TheLog.debug(";; _dorecursion() Deadbeat name server did not provide new information.")
680
680
  next
681
- elsif (of =~ /#{known_zone}/)
681
+ # elsif (of =~ /#{known_zone}/)
682
+ elsif (of == known_zone || known_zone.length == 1 || of.end_with?(".#{known_zone}"))
682
683
  TheLog.debug(";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].")
683
684
  auth[server] ||= AddressCache.new #[] @TODO@ If there is no additional record for this, then we want to use the authority!
684
685
  if (rr.type == Types.NS)
@@ -688,7 +689,7 @@ module Dnsruby
688
689
  end
689
690
  end
690
691
  else
691
- TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?")
692
+ TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom.to_s + "] thinks " + of.to_s + " is closer than " + known_zone.to_s + "?")
692
693
  return nil
693
694
  end
694
695
  else
@@ -750,9 +751,9 @@ module Dnsruby
750
751
  # Now prune the response of any unrelated rrsets (RFC5452 section6)
751
752
  # "One very simple way to achieve this is to only accept data if it is
752
753
  # part of the domain for which the query was intended."
753
- if (!packet.header.aa)
754
- return
755
- end
754
+ # if (!packet.header.aa)
755
+ # return
756
+ # end
756
757
  if (!packet.question()[0])
757
758
  return
758
759
  end
@@ -401,7 +401,7 @@ module Dnsruby
401
401
  if @single_resolvers.length == 0
402
402
  Thread.start {
403
403
  sleep(@query_timeout == 0 ? 1 : @query_timeout)
404
- client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no nameservers configured')])
404
+ client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no valid nameservers configured')])
405
405
  }
406
406
  end
407
407
  client_query_id
@@ -436,7 +436,7 @@ module Dnsruby
436
436
  # * :tcp_pipelining
437
437
  # * :tcp_pipelining_max_queries - can be a number or :infinite symbol
438
438
  def initialize(*args)
439
- # @TODO@ Should we allow :namesver to be an RRSet of NS records? Would then need to randomly order them?
439
+ # @TODO@ Should we allow :nameserver to be an RRSet of NS records? Would then need to randomly order them?
440
440
  @resolver_ruby = nil
441
441
  @src_address = nil
442
442
  @src_address6 = nil
@@ -453,6 +453,7 @@ module Dnsruby
453
453
  begin
454
454
  if key == :config_info
455
455
  @config.set_config_info(args[0][:config_info])
456
+ add_config_nameservers
456
457
  elsif key == :nameserver
457
458
  set_config_nameserver(args[0][:nameserver])
458
459
  elsif key == :nameservers
@@ -481,10 +482,11 @@ module Dnsruby
481
482
  @config.get_ready
482
483
  end
483
484
  @configured = true
484
- @single_res_mutex.synchronize {
485
485
  # Add the Config nameservers
486
- @config.nameserver.each do |ns|
487
- res = PacketSender.new({
486
+ threads = []
487
+ @config.nameserver.each do |ns|
488
+ threads << Thread.new do
489
+ PacketSender.new({
488
490
  server: ns,
489
491
  port: @port,
490
492
  dnssec: @dnssec,
@@ -499,10 +501,22 @@ module Dnsruby
499
501
  src_address6: @src_address6,
500
502
  src_port: @src_port,
501
503
  recurse: @recurse,
502
- udp_size: @udp_size})
503
- @single_resolvers.push(res) if res
504
+ udp_size: @udp_size,
505
+ ignore_config_resolv_errors: true})
504
506
  end
505
- }
507
+ end
508
+
509
+ new_resolvers = threads.map(&:value).compact
510
+ new_resolvers = new_resolvers.select do |resolver|
511
+ !resolver.nil? && !resolver.server.nil?
512
+ end
513
+ # if new_resolvers.empty?
514
+ # raise ArgumentError.new("No valid nameservers found in config")
515
+ # end
516
+ @single_res_mutex.synchronize { @single_resolvers.concat(new_resolvers) }
517
+ if @single_resolvers.nil? || @single_resolvers.empty?
518
+ raise ArgumentError.new("No valid nameservers found")
519
+ end
506
520
  end
507
521
 
508
522
  def set_config_nameserver(n)
@@ -87,13 +87,13 @@ module Dnsruby
87
87
 
88
88
  def encode_rdata(msg, canonical=false) #:nodoc: all
89
89
  msg.put_bytes(@address.address)
90
- msg.put_pack("n", @protocol)
90
+ msg.put_pack("C", @protocol)
91
91
  msg.put_bytes(@bitmap)
92
92
  end
93
93
 
94
94
  def self.decode_rdata(msg) #:nodoc: all
95
95
  address = IPv4.new(msg.get_bytes(4))
96
- protocol, = msg.get_unpack("n")
96
+ protocol, = msg.get_unpack("C")
97
97
  bitmap = msg.get_bytes
98
98
  return self.new(address, protocol, bitmap)
99
99
  end
@@ -227,13 +227,12 @@ module Dnsruby
227
227
  if @options
228
228
  @options.each do |opt|
229
229
  if opt.code == EDNS_SUBNET_OPTION
230
- ret = ret + "CLIENT-SUBNET: #{get_client_subnet(opt)}"
230
+ ret = ret + "CLIENT-SUBNET: #{get_client_subnet(opt)}\n"
231
231
  else
232
- ret = ret + " " + opt.to_s
232
+ ret = ret + "OPT=#{opt.code}: #{opt.data}\n"
233
233
  end
234
234
  end
235
235
  end
236
- ret = ret + "\n"
237
236
  return ret
238
237
  end
239
238
 
@@ -228,17 +228,20 @@ module Dnsruby
228
228
  if (unused_loop_count > 10 && @@query_hash.empty? && @@observers.empty?)
229
229
  Dnsruby.log.debug("Try stop select loop")
230
230
 
231
+ # We leave the @@wakeup_sockets here, but remove everything else
231
232
  non_persistent_sockets = @@sockets.select { |s| ! @@socket_is_persistent[s] }
232
233
  non_persistent_sockets.each do |socket|
233
- socket.close rescue nil
234
- @@sockets.delete(socket)
234
+ if socket != @@wakeup_sockets[1]
235
+ socket.close rescue nil
236
+ @@sockets.delete(socket)
237
+ end
235
238
  end
236
239
 
237
- Dnsruby.log.debug("Deleted #{non_persistent_sockets.size} non-persistent sockets," +
238
- " #{@@sockets.count} persistent sockets remain.")
240
+ Dnsruby.log.debug("Deleted #{non_persistent_sockets.size - 1} non-persistent sockets," +
241
+ " #{@@sockets.count - 1} persistent sockets remain.")
239
242
  @@socket_hash.clear
240
243
 
241
- if @@sockets.empty?
244
+ if @@sockets.size == 1
242
245
  Dnsruby.log.debug("Stopping select loop")
243
246
  return
244
247
  end
@@ -251,10 +254,11 @@ module Dnsruby
251
254
  # Removes closed sockets from @@sockets, and returns an array containing 1
252
255
  # exception for each closed socket contained in @@socket_hash.
253
256
  def clean_up_closed_sockets
257
+ exceptions = []
254
258
  @@mutex.synchronize do
255
259
  closed_sockets_in_hash = @@sockets.select(&:closed?).select { |s| @@socket_hash[s] }
256
260
  @@sockets.delete_if { | socket | socket.closed? }
257
- closed_sockets_in_hash.each_with_object([]) do |socket, exceptions|
261
+ closed_sockets_in_hash.each_with_object([]) do |socket, exception|
258
262
  @@socket_hash[socket].each do | client_id |
259
263
  exceptions << [SocketEofResolvError.new("TCP socket closed before all answers received"), socket, client_id]
260
264
  end
@@ -389,6 +393,9 @@ module Dnsruby
389
393
  if !persistent?(socket) || max_attained?(socket)
390
394
  @@sockets.delete(socket)
391
395
  @@socket_hash.delete(socket)
396
+ @@socket_remaining_queries.delete(socket)
397
+ @@socket_is_persistent.delete(socket)
398
+ @@tcp_buffers.delete(socket)
392
399
  Dnsruby.log.debug("Closing socket #{socket}")
393
400
  socket.close rescue nil
394
401
  end
@@ -1,3 +1,3 @@
1
1
  module Dnsruby
2
- VERSION = '1.73.1'
2
+ VERSION = '1.74.0'
3
3
  end
@@ -92,4 +92,33 @@ class TestResolverConfig < Minitest::Test
92
92
  end;
93
93
  end
94
94
 
95
+ def test_bad_ns
96
+ begin
97
+ res = Dnsruby::Resolver.new(:nameserver => ["ns32.domiancontrol.com."])
98
+ res.query("example.com")
99
+ assert(false, "Should not query with bad nameserver")
100
+ rescue Exception => e
101
+ # OK
102
+ end
103
+ end
104
+
105
+ def test_bad_ns_post_init
106
+ begin
107
+ resolver = Dnsruby::Resolver.new(recurse: false, do_caching: false, retry_times: 2)
108
+ resolver.nameserver = ["ns32.domiancontrol.com."]
109
+ assert(false, "Should not allow bad nameserver")
110
+ resolver.query("example.com")
111
+ rescue ArgumentError
112
+ end
113
+ end
114
+
115
+ def test_one_good_ns_rest_bad
116
+ begin
117
+ resolver = Dnsruby::Resolver.new(recurse: false, do_caching: false, retry_times: 2)
118
+ resolver.nameserver = ["ns32.domiancontrol.com.", "1.1.1.1"]
119
+ resolver.query("example.com")
120
+ rescue ArgumentError
121
+ assert(false, "Should allow one good nameserver")
122
+ end
123
+ end
95
124
  end
data/test/tc_resolver.rb CHANGED
@@ -477,5 +477,18 @@ class TestRawQuery < Minitest::Test
477
477
  # Dnsruby::Cache.delete("googlöe.com", "MX")
478
478
 
479
479
  end
480
+
481
+ def test_config_info
482
+ res = Dnsruby::Resolver.new(
483
+ config_info: {
484
+ # contents here doesn't matter; even setting to {} is enough to cause ResolvTimeout
485
+ apply_domain: false,
486
+ apply_search_list: false,
487
+ },
488
+ query_timeout: 3, # make the timeout faster
489
+ )
490
+ res.do_caching = false
491
+ res.query("a.root-servers.net.", "A")
492
+ end
480
493
  end
481
494
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dnsruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.73.1
4
+ version: 1.74.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dalitz
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-11-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -80,6 +79,26 @@ dependencies:
80
79
  - - ">="
81
80
  - !ruby/object:Gem::Version
82
81
  version: 0.3.1
82
+ - !ruby/object:Gem::Dependency
83
+ name: rdoc
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '6.5'
89
+ - - "<"
90
+ - !ruby/object:Gem::Version
91
+ version: '8.0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '6.5'
99
+ - - "<"
100
+ - !ruby/object:Gem::Version
101
+ version: '8.0'
83
102
  - !ruby/object:Gem::Dependency
84
103
  name: yard
85
104
  requirement: !ruby/object:Gem::Requirement
@@ -159,9 +178,9 @@ executables: []
159
178
  extensions: []
160
179
  extra_rdoc_files:
161
180
  - DNSSEC
181
+ - EVENTMACHINE
162
182
  - EXAMPLES
163
183
  - README.md
164
- - EVENTMACHINE
165
184
  files:
166
185
  - ".coveralls.yml"
167
186
  - ".github/FUNDING.yml"
@@ -368,8 +387,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
368
387
  - !ruby/object:Gem::Version
369
388
  version: '0'
370
389
  requirements: []
371
- rubygems_version: 3.5.16
372
- signing_key:
390
+ rubygems_version: 3.7.2
373
391
  specification_version: 4
374
392
  summary: Ruby DNS(SEC) implementation
375
393
  test_files: