diff --git a/lib/resolv.rb b/lib/resolv.rb index bac338c..4a2be33 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -581,6 +581,11 @@ def fetch_resource(name, typeclass) # Retry via TCP: truncated[candidate] = true redo + elsif reply.answer.empty? + # NODATA: the name exists but has no records of the + # requested type. Continue with the next candidate in the + # search list, if any. + raise Config::NODATA.new(reply_name.to_s) else yield(reply, reply_name) end @@ -1293,7 +1298,7 @@ def resolv(name) } timeout_error = true raise ResolvError.new("DNS resolv timeout: #{name}") - rescue NXDomain + rescue NXDomain, NODATA end } rescue ResolvError @@ -1307,6 +1312,12 @@ def resolv(name) class NXDomain < ResolvError end + ## + # Indicates the name was found but has no data for the requested type. + + class NODATA < ResolvError + end + ## # Indicates some other unhandled resolver error was encountered. diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb index b8ecef2..5a36b08 100644 --- a/test/resolv/test_dns.rb +++ b/test/resolv/test_dns.rb @@ -542,6 +542,39 @@ def test_query_ipv4_address_timeout } end + def test_search_list_continues_on_nodata + bug18842 = '[Bug #18842]' + with_udp('127.0.0.1', 0) {|u| + _, server_port, _, server_address = u.addr + client_thread = Thread.new { + Resolv::DNS.open(:nameserver_port => [[server_address, server_port]], :search => ['bad1.com', 'bad2.com', 'good.com'], :ndots => 5) {|dns| + dns.getaddress("example") + } + } + server_thread = Thread.new { + 3.times do + msg, (_, client_port, _, client_address) = Timeout.timeout(5) {u.recvfrom(4096)} + query = Resolv::DNS::Message.decode(msg) + reply = Resolv::DNS::Message.new(query.id) + reply.qr = 1 + reply.rd = query.rd + reply.ra = 1 + query.each_question {|name, typeclass| + reply.add_question(name, typeclass) + if name.to_s == "example.good.com" + reply.add_answer(name, 3600, Resolv::DNS::Resource::IN::A.new("192.0.2.1")) + end + } + # No answers for the other candidates: a NODATA reply. + u.send(reply.encode, 0, client_address, client_port) + end + } + address = client_thread.value + server_thread.join + assert_equal(Resolv::IPv4.create("192.0.2.1"), address, bug18842) + } + end + def test_no_server omit if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning u = UDPSocket.new