From dfaec9cc10a16b42804fd8185f2f74d5a07009cf Mon Sep 17 00:00:00 2001 From: Tjerk Meesters Date: Tue, 13 Oct 2026 07:46:14 +0800 Subject: [PATCH 1/2] Add IPAddr#to_hex Returns the hexadecimal representation of the address value, right adjusted to the address size (8 characters for IPv4, 32 for IPv6). This is used for e.g. SPF macro expansions. Co-Authored-By: Claude Code --- lib/ipaddr.rb | 30 +++++++++++++++++++++++------- test/test_ipaddr.rb | 10 ++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 10027f3..7b4f420 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -205,6 +205,25 @@ def to_i return @addr end + # Returns a string containing the hexadecimal representation of the + # IP address value, right adjusted to the address size. + # + # e.g.: + # require 'ipaddr' + # p IPAddr.new("192.168.1.1").to_hex #=> "c0a80101" + # p IPAddr.new("3ffe:505:2::1").to_hex #=> "3ffe0505000200000000000000000001" + def to_hex + case @family + when Socket::AF_INET + @addr.to_s(16).rjust(8, '0') + when Socket::AF_INET6 + @addr.to_s(16).rjust(32, '0') + else + # In case we ever add support for AF_INET8, this should raise an error. + raise AddressFamilyError, "unsupported address family" + end + end + # Returns a string containing the IP address representation. def to_s str = _to_string(@addr) @@ -268,12 +287,9 @@ def cidr # Returns a network byte ordered string form of the IP address. def hton case @family - when Socket::AF_INET - return [@addr].pack('N') - when Socket::AF_INET6 - return (0..7).map { |i| - (@addr >> (112 - 16 * i)) & 0xffff - }.pack('n8') + when Socket::AF_INET, Socket::AF_INET6 + value = to_hex + return [value].pack("H#{value.length}") else raise AddressFamilyError, "unsupported address family" end @@ -829,7 +845,7 @@ def _reverse (@addr >> (8 * i)) & 0xff }.join('.') when Socket::AF_INET6 - return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.') + return to_hex.reverse.chars.join('.') else raise AddressFamilyError, "unsupported address family" end diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb index 6a4b0cb..617e4c8 100644 --- a/test/test_ipaddr.rb +++ b/test/test_ipaddr.rb @@ -295,6 +295,16 @@ def test_to_s_with_zone_id assert_equal("fe80::1%0012", IPAddr.new("fe80::1%0012").to_s) end + def test_to_hex + assert_equal("c0a80101", IPAddr.new("192.168.1.1").to_hex) + assert_equal("00000000", IPAddr.new("0.0.0.0").to_hex) + assert_equal("ffffffff", IPAddr.new("255.255.255.255").to_hex) + assert_equal("3ffe0505000200000000000000000001", IPAddr.new("3ffe:505:2::1").to_hex) + assert_equal("0123456789abcdef0abcdef012345678", IPAddr.new("123:4567:89ab:cdef:0abc:def0:1234:5678").to_hex) + assert_equal("00000000000000000000000000000000", IPAddr.new("::").to_hex) + assert_equal("ffffffffffffffffffffffffffffffff", IPAddr.new("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").to_hex) + end + def test_as_json assert_equal("192.168.1.2", IPAddr.new("192.168.1.2").as_json) assert_equal("192.168.1.0/24", IPAddr.new("192.168.1.2/24").as_json) From fd59950fc7d8b21cffea4e0226f17dc989b9c49f Mon Sep 17 00:00:00 2001 From: Tjerk Meesters Date: Fri, 9 Oct 2026 12:16:34 +0800 Subject: [PATCH 2/2] Fix assert_warning availability in test suite The ipv4_compat tests use assert_warning, which is provided by test-unit-ruby-core's CoreAssertions module rather than the test-unit gem itself. Require core_assertions and include the module into the test case so those tests actually run instead of erroring. Co-Authored-By: Claude Code --- test/test_ipaddr.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb index 617e4c8..5555994 100644 --- a/test/test_ipaddr.rb +++ b/test/test_ipaddr.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true require 'test/unit' +require 'core_assertions' require 'ipaddr' class TC_IPAddr < Test::Unit::TestCase + include Test::Unit::CoreAssertions + def test_s_new [ ["3FFE:505:ffff::/48"],