Project

General

Profile

Actions

Bug #22538

closed

require 'socket' raises NameError for builds with --disable-ipv6, and IPv6 addresses leak from the resolver

Bug #22538: require 'socket' raises NameError for builds with --disable-ipv6, and IPv6 addresses leak from the resolver

Added by datibbaw (Tjerk Meesters) 1 day ago. Updated about 1 hour ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:127419]

Description

On a Ruby built with ./configure --disable-ipv6, require 'socket' fails:

$ ruby -rsocket -e0
.../socket.rb:622:in '<class:Socket>': uninitialized constant Socket::AF_INET6 (NameError)
	from .../socket.rb:478:in '<top (required)>'

Reproduced with:

  • ruby 3.4.10 (2026-06-30 revision 2b0b7728dc) +PRISM [arm64-darwin25]
  • ruby 4.1.0dev (2026-11-12T01:39:13Z master 0738c9dfaa) +PRISM [arm64-darwin25]

Cause

With --disable-ipv6, INET6 is not defined, so mkconstants.rb omits
AF_INET6, PF_INET6 and IPV6_*. Since Happy Eyeballs v2 was added to
Socket.tcp (Ruby 3.4), socket.rb defines a class-level constant that
references Socket::AF_INET6 without a defined? check:

ADDRESS_FAMILIES = {
  ipv6: Socket::AF_INET6,
  ipv4: Socket::AF_INET
}.freeze

Further problems once the require is fixed

--disable-ipv6 only removes the Ruby-level IPv6 constants. The system
resolver still returns IPv6 addresses (with AF_UNSPEC hints), and
getifaddrs still reports IPv6 interface addresses. Code that skips IPv6
handling with defined?(Socket::AF_INET6) then gets IPv6 sockets anyway:

  • Socket.tcp_server_sockets(0) never returns. getaddrinfo(nil, 0, ..., AI_PASSIVE)
    returns :: and 0.0.0.0; ipv6only! is a no-op because
    Socket::IPV6_V6ONLY is not defined, so the [::]:P socket is
    dual-stack, binding 0.0.0.0:P fails with EADDRINUSE, and
    ip_sockets_port0 retries forever. This also hangs
    TestSocket#test_accept_loop_with_unix.
  • Socket.tcp(host, port, nil, 0) (local port only) resolves ::1 and
    127.0.0.1 locally; ADDRESS_FAMILIES.key(AF_INET6) is nil, so the
    resolver thread runs with a nil family.
  • Addrinfo#connect_address does not turn [::] into [::1] (that branch
    is guarded by defined?(Socket::AF_INET6)), and connecting to it fails
    with ENOBUFS on macOS.
  • Socket.ip_address_list and Socket.udp_server_sockets return IPv6
    addresses.
  • TCPSocket.new (C Happy Eyeballs) starts an IPv6 resolver thread and then
    discards its results.

Proposed fix

Make --disable-ipv6 consistently IPv4-only:

  • socket.rb: only add :ipv6 to ADDRESS_FAMILIES when Socket::AF_INET6
    is defined.
  • raddrinfo.c (rsock_getaddrinfo, which every Ruby-level lookup goes
    through): when INET6 is not defined, change PF_UNSPEC hints to PF_INET
    and reject PF_INET6 with EAI_FAMILY.
  • ipsocket.c: resolve only IPv4 in the TCPSocket.new fast fallback path.
  • socket.c: skip AF_INET6 in the getifaddrs and ioctl implementations of
    Socket.ip_address_list. The Windows implementation already does this.
  • Tests: skip tests that need IPv6 (::1 servers, IPv6 addresses); make
    stubbed Socket.tcp and TCPSocket.new tests work when Socket::AF_INET6
    is undefined.

User-visible change, only in --disable-ipv6 builds: IPv6 literals such as
Addrinfo.ip("::1") or TCPSocket.new("::1", port) raise
Socket::ResolutionError instead of partly working.

I will open a pull request.

Note on backporting to 3.4

On 3.4, fixing ADDRESS_FAMILIES alone is not enough. With only one family
to resolve, Socket.tcp_with_fast_fallback passes the Symbol :ipv4 to
Addrinfo.getaddrinfo, which raises unknown socket domain: ipv4
(#21512). A 3.4 backport also needs both fixes for that issue:
b896f80598 and its follow-up f33766720c. The first commit alone passes
ADDRESS_FAMILIES[:family_name], i.e. nil, so it silently resolves all
families.

Updated by nobu (Nobuyoshi Nakada) about 5 hours ago Actions #1

  • Status changed from Open to Closed

Applied in changeset git|c99813a8d8484058987c968923cd814e99aa6dd3.


[Bug #22538] Fix failures when IPv6 is disabled

Updated by datibbaw (Tjerk Meesters) about 3 hours ago Actions #2 [ruby-core:127460]

nobu (Nobuyoshi Nakada) wrote in #note-1:

Applied in changeset git|c99813a8d8484058987c968923cd814e99aa6dd3.


[Bug #22538] Fix failures when IPv6 is disabled

Thanks for that, but that only fixes a small part of the issue; the rest involves the Happy Eyeballs implementation that causes hangs and otherwise undesirable behaviour ... in fact, the test suite doesn't pass with --disable-ipv6 on a system that does support it.

I've drafted my proposed change here: https://github.com/ruby/ruby/compare/master...datibbaw:ruby:disable-ipv6-consistency

Updated by nobu (Nobuyoshi Nakada) about 1 hour ago Actions #3 [ruby-core:127465]

Could you make another ticket for Happy Eyeball related issues?

Actions

Also available in: PDF Atom