Skip to content

Commit

Permalink
Support a :raise_timeout_errors option to raise timeouts as Resolv::R…
Browse files Browse the repository at this point in the history
…esolvError

This allows to differentiate a timeout from an NXDOMAIN response.

Fixes [Bug #18151]
  • Loading branch information
jeremyevans committed Nov 25, 2023
1 parent 0de996d commit c0e5aba
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lib/resolv.rb
Expand Up @@ -312,6 +312,8 @@ def self.open(*args)
# 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.
Expand Down Expand Up @@ -1041,6 +1043,7 @@ def lazy_initialize
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]
Expand Down Expand Up @@ -1131,6 +1134,7 @@ def generate_timeouts
def resolv(name)
candidates = generate_candidates(name)
timeouts = @timeouts || generate_timeouts
timeout_error = false
begin
candidates.each {|candidate|
begin
Expand All @@ -1142,11 +1146,13 @@ def resolv(name)
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

Expand Down
24 changes: 24 additions & 0 deletions test/resolv/test_dns.rb
Expand Up @@ -630,4 +630,28 @@ def dns.each_resource(name, typeclass)
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

0 comments on commit c0e5aba

Please sign in to comment.