Skip to content

Respect explicit families for bracketed and scoped IPv6 addresses - #128

Merged
taketo1113 merged 1 commit into
ruby:masterfrom
ac1982:fix/explicit-family-ipv6-literals
Oct 16, 2026
Merged

taketo1113 merged 1 commit into
ruby:masterfrom
ac1982:fix/explicit-family-ipv6-literals

Conversation

@ac1982

@ac1982 ac1982 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #62.

The constructor forces family to AF_INET6 when it finds brackets or a zone ID. This overwrites an explicitly supplied AF_INET, so IPAddr.new('[::]', Socket::AF_INET) succeeds even though the undecorated IPv6 spelling raises AddressFamilyError.

Keep the requested family and compare it with the parsed address at the existing post-parse family check. Parsing first preserves InvalidAddressError for invalid bracketed or scoped literals. Automatic family detection and explicitly requested IPv6 continue to work; bracketed IPv4 addresses remain invalid.

The regression covers bracketed, scoped, combined, and CIDR forms, including explicit IPv4 rejection and positive IPv6 cases. It fails on the original upstream source. Additional cases preserve the original InvalidAddressError for invalid bracketed/scoped IPv4 and malformed literals across unspecified, IPv4, and IPv6 family arguments; these fail on the previous PR head and pass after review revision. bundle exec rake test passes on Ruby 3.3.8 / Linux arm64: 40 tests, 591 assertions, no failures/errors/omissions. git diff --check passes. Other Ruby versions and macOS were not run locally.

AI assistance: OpenAI Codex investigated, implemented, and ran these checks.

@ac1982
ac1982 requested a review from knu as a code owner October 10, 2026 07:43
Comment thread lib/ipaddr.rb Outdated
Comment on lines +726 to +728
if requested_family != Socket::AF_UNSPEC && family != requested_family
raise AddressFamilyError, "address family mismatch"
end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception class for the following case changes from IPAddr::InvalidAddressError to IPAddr::AddressFamilyError with this change. Could you change this to preserve the existing behavior by raising IPAddr::InvalidAddressError instead?

# After fix
IPAddr.new("[192.168.1.2]", Socket::AF_INET)
/path/lib/ipaddr.rb:727:in 'IPAddr#initialize': address family mismatch (IPAddr::AddressFamilyError)
      raise AddressFamilyError, "address family mismatch"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

# Before fix
IPAddr.new("[192.168.1.2]", Socket::AF_INET)
/path/lib/ipaddr.rb:800:in 'IPAddr#in6_addr': invalid address: 192.168.1.2 (IPAddr::InvalidAddressError)
      raise InvalidAddressError, "invalid address: #{left}"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For example, how about making the following change?

-    if requested_family != Socket::AF_UNSPEC && family != requested_family
-      raise AddressFamilyError, "address family mismatch"
-    end
    # It seems AI_NUMERICHOST doesn't do the job.
    #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
...
      @family = Socket::AF_INET6
    end
    @zone_id = zone_id
+    if requested_family != Socket::AF_UNSPEC && @family != requested_family
-    if family != Socket::AF_UNSPEC && @family != family
      raise AddressFamilyError, "address family mismatch"
    end

Comment thread test/test_ipaddr.rb Outdated
@ac1982

ac1982 commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Thank you, you are right: checking the family before parsing changed the exception for malformed literals. In f48da8c I removed the early check and compared requested_family with @family at the existing post-parse check, as suggested.

Added regression coverage for bracketed/scoped IPv4 and malformed literals under unspecified, IPv4, and IPv6 family arguments. The new invalid-input test passes on the original upstream source, fails on the previous PR head, and passes after this change. The original valid-IPv6 family-mismatch regression remains covered. I also removed the AF_UNIX cases.

bundle exec rake test: 40 tests, 591 assertions, no failures/errors on Ruby 3.3.8 / Linux arm64. Other Ruby/platform combinations were not run locally. The PR description is updated.

@taketo1113
taketo1113 force-pushed the fix/explicit-family-ipv6-literals branch from f48da8c to 8696d8f Compare October 16, 2026 00:07
@taketo1113

Copy link
Copy Markdown
Collaborator

@ac1982 Thank you for the update.
I made some minor adjustments to the tests and squashed the commits.


This change affects the behavior as follows.

# After the fix
IPAddr.new('[::]', Socket::AF_INET)
/path/lib/ipaddr.rb:742:in 'IPAddr#initialize': address family mismatch (IPAddr::AddressFamilyError)
      raise AddressFamilyError, "address family mismatch"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

IPAddr.new('::%eth0', Socket::AF_INET)
/path/lib/ipaddr.rb:742:in 'IPAddr#initialize': address family mismatch (IPAddr::AddressFamilyError)
      raise AddressFamilyError, "address family mismatch"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

IPAddr.new('[::%eth0]', Socket::AF_INET)
/path/lib/ipaddr.rb:742:in 'IPAddr#initialize': address family mismatch (IPAddr::AddressFamilyError)
      raise AddressFamilyError, "address family mismatch"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

# Before the fix
IPAddr.new('[::]', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

IPAddr.new('::%eth0', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000%eth0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

IPAddr.new('[::%eth0]', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000%eth0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

Passing an IPv6 address string such as [::] or ::%eth0 with Socket::AF_INET as the address family is clearly incorrect.

In actual usage, I believe the impact will be minimal, as the family argument is primarily used when addr is an Integer rather than a String.
For example: IPAddr.new(1, Socket::AF_INET) or IPAddr.new(1, Socket::AF_INET6)

I also checked the usage of IPAddr.new with Socket::AF_INET in Ruby code using the following GitHub code search:
https://github.com/search?q=%22IPAddr.new%22+AND+%22Socket%3A%3AAF_INET%22+language%3ARuby&type=code&l=Ruby


Separately, I plan to address the following case in another change, as [fe80::1]/64 is not a valid IPv6 address notation:

IPAddr.new("[fe80::1%eth0]/64", Socket::AF_INET6)
# => #<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0000%eth0/ffff:ffff:ffff:ffff:0000:0000:0000:0000>

@taketo1113
taketo1113 merged commit fa1d050 into ruby:master Oct 16, 2026
29 checks passed
@taketo1113

Copy link
Copy Markdown
Collaborator

Separately, I plan to address the following case in another change, as [fe80::1]/64 is not a valid IPv6 address notation:

The existing tests accepted this invalid address, so I added a warning that it will raise IPAddr::InvalidAddressError in the future version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IPAddr.new accepts certain IPv6 literals when AF_INET is specified

2 participants