From 3ec8cceb320fa573a29c44203c1e937a8e3dea27 Mon Sep 17 00:00:00 2001 From: Shizuo Fujita Date: Sun, 22 Mar 2026 15:44:53 +0900 Subject: [PATCH] Ignore IPv6 nameservers to prevent Socket::ResolutionError The DNSResolver currently does not support IPv6 and initializes an IPv4 (AF_INET) UDP socket. However, modern OS environments often include IPv6 addresses in `/etc/resolv.conf` by default. When the resolver attempts to send a query to an IPv6 nameserver using the IPv4 socket, it crashes with: `Socket::ResolutionError: getaddrinfo: Address family for hostname not supported` --- lib/cool.io/dns_resolver.rb | 9 ++++++++- spec/dns_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/cool.io/dns_resolver.rb b/lib/cool.io/dns_resolver.rb index 0935121..af7f299 100644 --- a/lib/cool.io/dns_resolver.rb +++ b/lib/cool.io/dns_resolver.rb @@ -67,8 +67,9 @@ def self.hosts(host, hostfile = Resolv::Hosts::DefaultFileName) # list of nameservers to query. By default the resolver will # use nameservers listed in /etc/resolv.conf def initialize(hostname, *nameservers) + nameservers = reject_ipv6_nameservers(nameservers) if nameservers.empty? - nameservers = Resolv::DNS::Config.default_config_hash[:nameserver] + nameservers = reject_ipv6_nameservers(Resolv::DNS::Config.default_config_hash[:nameserver]) raise RuntimeError, "no nameservers found" if nameservers.empty? # TODO just call resolve_failed, not raise [also handle Errno::ENOENT)] end @@ -203,6 +204,12 @@ def response_address(message) nil end + private + + def reject_ipv6_nameservers(nameservers) + nameservers.reject { |ns| ns.include?(':') } + end + class Timeout < TimerWatcher def initialize(resolver) @resolver = resolver diff --git a/spec/dns_spec.rb b/spec/dns_spec.rb index 038f83a..50d3e31 100644 --- a/spec/dns_spec.rb +++ b/spec/dns_spec.rb @@ -56,4 +56,24 @@ def on_resolve_failed expect( Coolio::DNSResolver.hosts("localhost", file.path)).to eq @preferred_localhost_address end end + + describe "IPv6 nameserver filtering" do + it "ignores IPv6 nameservers provided in arguments" do + resolver = Coolio::DNSResolver.new("example.com", "8.8.8.8", "2001:4860:4860::8888", "1.1.1.1") + + nameservers = resolver.instance_variable_get(:@nameservers) + expect(nameservers).to eq(["8.8.8.8", "1.1.1.1"]) + end + + it "falls back to default IPv4 config if only IPv6 addresses are provided" do + allow(Resolv::DNS::Config).to receive(:default_config_hash).and_return({ + nameserver: ["8.8.4.4", "2001:4860:4860::8844"] + }) + + resolver = Coolio::DNSResolver.new("example.com", "2001:4860:4860::8888") + + nameservers = resolver.instance_variable_get(:@nameservers) + expect(nameservers).to eq(["8.8.4.4"]) + end + end end