From 011b126f9334761f546e39a6b8afdff9606a65e9 Mon Sep 17 00:00:00 2001 From: Stephan Kaag <209887080+stephanmoneybird@users.noreply.github.com> Date: Mon, 28 Sep 2026 12:29:24 +0200 Subject: [PATCH] Match resources of unregistered types in getresources Since fa5e689c4 a class from Generic.create is not registered, so decoding a reply gives every resource of that type a fresh class. extract_resources compared by class identity and dropped all of them, so getresources and each_resource returned nothing for types without a named class, such as NAPTR. Compare by type and class value as Message#== already does. Fixes #151 --- lib/resolv.rb | 18 ++++++++++-------- test/resolv/test_resource.rb | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/resolv.rb b/lib/resolv.rb index 72df381..62385de 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -626,26 +626,28 @@ def extract_resources(msg, name, typeclass) # :nodoc: n0 = Name.create(name) msg.each_resource {|n, ttl, data| if n0 == n - case data - when typeclass + if resource_of_type?(data, typeclass) yield data yielded = true - when Resource::CNAME + elsif Resource::CNAME === data n0 = data.name end end } return if yielded msg.each_resource {|n, ttl, data| - if n0 == n - case data - when typeclass - yield data - end + if n0 == n && resource_of_type?(data, typeclass) + yield data end } end + # Decoding makes a fresh class for each resource of an unregistered type, + # so a class from Generic.create never matches such a resource by identity. + private def resource_of_type?(data, typeclass) # :nodoc: + typeclass === data || Resource::Generic.type_class_equal?(data.class, typeclass) + end + def self.random(arg) # :nodoc: begin SecureRandom.random_number(arg) diff --git a/test/resolv/test_resource.rb b/test/resolv/test_resource.rb index c5d22ec..8e7f214 100644 --- a/test/resolv/test_resource.rb +++ b/test/resolv/test_resource.rb @@ -64,6 +64,24 @@ def test_generic_question_equality Resolv::DNS::Message.decode(generic_question(40001)) end + # Looking up a type that has no named class means passing a class from + # Generic.create, which is never the class a decoded answer carries. + def test_getresources_with_generic_type + generic = Resolv::DNS::Resource::Generic + message = Resolv::DNS::Message.new + message.add_answer("example.com.", 60, generic.create(40000, 60000).new("\x01\x02\x03")) + reply = Resolv::DNS::Message.decode(message.encode) + dns = Resolv::DNS.new + dns.define_singleton_method(:fetch_resource) do |_name, _typeclass, &block| + block.call(reply, Resolv::DNS::Name.create("example.com.")) + end + + resources = dns.getresources("example.com", generic.create(40000, 60000)) + assert_equal ["\x01\x02\x03".b], resources.map(&:data) + assert_empty dns.getresources("example.com", generic.create(40001, 60000)) + assert_empty dns.getresources("example.com", Resolv::DNS::Resource::IN::A) + end + private def header(qdcount, ancount) "\x00\x00\x00\x00".b + [qdcount, ancount, 0, 0].pack('nnnn') end