Bug #17112
closedResolv.getaddress fails with IPv6 link-local addresses
Description
I noticed that I cannot resolve any link-local IPv6 address using Resolv.getaddress
. For example calling Resolv.getaddress('fe80::eca4:7b00:ecc5:206c%8')
fails with Resolv::ResolvError
Resolving any IPv4 address succeedes, as well as resolving the loopback address ::1
.
After some code digging I noticed that Resolv::DNS
doesn't even attempt to resolve that IP address. It checks if there is any local IPv6 interface with an IP address which is not a loopback or a link-local address.
def each_address(name)
each_resource(name, Resource::IN::A) {|resource| yield resource.address}
if use_ipv6? # <===== false on my system
each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
end
end
use_ipv6?
ultimately boils down to this:
Socket.ip_address_list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
In other words if the system doesn't have a "real" IPv6 address, Resolv.getaddress
cannot resolve link-local addresses. (::1
is resolved using Resolv::Hosts
which doesn't perform the same check)
One could argue that IP addresses don't have to be resolved, and I could just perform a regex check and skip Resolv.getaddress
if I already have an IP address.
Unfortunately that is not possible when using resolv-replace
. In that case most (all?) of Ruby's address resolving is piped through Resolv.getaddress
.
(That is how I noticed this bug: The RubyMine test runner uses drb which connects to a process on the same machine using a link-local IPv6 address. Since I'm using resolv-replace
and I don't have a network interface with a non-loopback non-local-link address I encountered this bug.)
Platform:
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]
Microsoft Windows [Version 10.0.18363.959]
Updated by daniel-rikowski (Daniel Rikowski) over 4 years ago
After some experiments I noticed that changing use_ipv6?
isn't the whole story.
In Resolv#each_address
there seems to be a check for IP addresses. In case an IP address is given no actual resolving is performed.
if AddressRegex =~ name
yield name
return
end
with
AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
Unfortunately the regex check doesn't catch link-local addresses. It looks like the IPv6 regexes cannot process the zone ID suffix. (i.e. the interface number/name after the percentage sign, e.g. 'fe80::...%8' or 'fe80::...%eth0')
As a result a regular resolving is attempted which fails (at least on my system)
As a workaround I modified AddressRegex
to detect link-local addresses and everything worked as expected:
require 'resolv'
Regex_CompressedHex_LinkLocal = /\A
((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) %[[:alnum:]]+
\z/x
Resolv::AddressRegex = /(?:#{Resolv::IPv4::Regex})|(?:#{Resolv::IPv6::Regex})|(?:#{Regex_CompressedHex_LinkLocal})/
(Notice that the regexp is just a quick hack and not a proper link-local pattern. For example the fe80::
prefix isn't taken into account)
Updated by jeremyevans0 (Jeremy Evans) about 4 years ago
- Assignee set to akr (Akira Tanaka)
I've added a pull request with a fix for this: https://github.com/ruby/ruby/pull/3452
Updated by jeremyevans (Jeremy Evans) about 4 years ago
- Status changed from Open to Closed
Applied in changeset git|2f12af42f7f26d570219b87a89294532a86a8ae2.
Add support for IPv6 link local addresses to resolv
Now that it should work correctly, test that every address returned
by Socket.ip_address_list is resolvable.
Socket works with IPv6 link local addresses, and ipaddr now does
as well, so I think resolv should support them.
Fixes [Bug #17112]