From 7276d4b4e87bfdc9b609f481a734e39c499de253 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 17 Sep 2021 17:17:20 -0700 Subject: [PATCH] [ruby/resolv] Support a :raise_timeout_errors option to raise timeouts as Resolv::ResolvError This allows to differentiate a timeout from an NXDOMAIN response. Fixes [Bug #18151] https://github.com/ruby/resolv/commit/c0e5abab76 --- lib/resolv.rb | 6 ++++++ test/resolv/test_dns.rb | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/resolv.rb b/lib/resolv.rb index 8203e5110a..0943edc400 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -312,6 +312,8 @@ class Resolv # String:: Path to a file using /etc/resolv.conf's format. # Hash:: Must contain :nameserver, :search and :ndots keys. # :nameserver_port can be used to specify port number of nameserver address. + # :raise_timeout_errors can be used to raise timeout errors + # as exceptions instead of treating the same as an NXDOMAIN response. # # The value of :nameserver should be an address string or # an array of address strings. @@ -1032,6 +1034,7 @@ class Resolv end @search = config_hash[:search] if config_hash.include? :search @ndots = config_hash[:ndots] if config_hash.include? :ndots + @raise_timeout_errors = config_hash[:raise_timeout_errors] if @nameserver_port.empty? @nameserver_port << ['0.0.0.0', Port] @@ -1118,6 +1121,7 @@ class Resolv def resolv(name) candidates = generate_candidates(name) timeouts = @timeouts || generate_timeouts + timeout_error = false begin candidates.each {|candidate| begin @@ -1129,11 +1133,13 @@ class Resolv end } } + timeout_error = true raise ResolvError.new("DNS resolv timeout: #{name}") rescue NXDomain end } rescue ResolvError + raise if @raise_timeout_errors && timeout_error end end diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb index c0400b4bb8..63ab66657c 100644 --- a/test/resolv/test_dns.rb +++ b/test/resolv/test_dns.rb @@ -630,4 +630,28 @@ class TestResolvDNS < Test::Unit::TestCase end assert_raise(Resolv::ResolvError) { dns.each_name('example.com') } end + + def test_unreachable_server + unreachable_ip = '127.0.0.1' + sock = UDPSocket.new + sock.connect(unreachable_ip, 53) + begin + sock.send('1', 0) + rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH + else + omit('cannot test unreachable server, as IP used is reachable') + end + + config = { + :nameserver => [unreachable_ip], + :search => ['lan'], + :ndots => 1 + } + r = Resolv.new([Resolv::DNS.new(config)]) + assert_equal([], r.getaddresses('www.google.com')) + + config[:raise_timeout_errors] = true + r = Resolv.new([Resolv::DNS.new(config)]) + assert_raise(Resolv::ResolvError) { r.getaddresses('www.google.com') } + end end