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