[前][次][番号順一覧][スレッド一覧]

ruby-changes:68293

From: Jeremy <ko1@a...>
Date: Thu, 7 Oct 2021 18:35:50 +0900 (JST)
Subject: [ruby-changes:68293] bd6e1a0f08 (master): [ruby/ipaddr] Support zone identifiers in IPv6 addresses

https://git.ruby-lang.org/ruby.git/commit/?id=bd6e1a0f08

From bd6e1a0f0883dba7b02f30cefe5ebec96d02cb90 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Mon, 13 Jul 2020 09:36:06 -0700
Subject: [ruby/ipaddr] Support zone identifiers in IPv6 addresses

These are supported by Ruby's socket library if the operating system
supports zone indentifiers, so they should be supported by ipaddr.
See RFCs 4007 and 6874 for additional information.

Implements Ruby Feature #10911

https://github.com/ruby/ipaddr/commit/09a6408fb2
---
 lib/ipaddr.rb       | 46 ++++++++++++++++++++++++++++++++++++++++++----
 test/test_ipaddr.rb | 26 +++++++++++++++++++++++++-
 2 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb
index c21c0cbf12..48141198ef 100644
--- a/lib/ipaddr.rb
+++ b/lib/ipaddr.rb
@@ -231,7 +231,13 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L231
   # Returns a string containing the IP address representation in
   # canonical form.
   def to_string
-    return _to_string(@addr)
+    str = _to_string(@addr)
+
+    if @family == Socket::AF_INET6
+      str << zone_id.to_s
+    end
+
+    return str
   end
 
   # Returns a network byte ordered string form of the IP address.
@@ -403,7 +409,7 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L409
 
   # Returns a hash value used by Hash, Set, and Array classes
   def hash
-    return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
+    return ([@addr, @mask_addr, @zone_id].hash << 1) | (ipv4? ? 0 : 1)
   end
 
   # Creates a Range object for the network address.
@@ -459,11 +465,12 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L465
       af = "IPv4"
     when Socket::AF_INET6
       af = "IPv6"
+      zone_id = @zone_id.to_s
     else
       raise AddressFamilyError, "unsupported address family"
     end
-    return sprintf("#<%s: %s:%s/%s>", self.class.name,
-                   af, _to_string(@addr), _to_string(@mask_addr))
+    return sprintf("#<%s: %s:%s%s/%s>", self.class.name,
+                   af, _to_string(@addr), zone_id, _to_string(@mask_addr))
   end
 
   # Returns the netmask in string format e.g. 255.255.0.0
@@ -471,6 +478,31 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L478
     _to_string(@mask_addr)
   end
 
+  # Returns the IPv6 zone identifier, if present.
+  # Raises InvalidAddressError if not an IPv6 address.
+  def zone_id
+    if @family == Socket::AF_INET6
+      @zone_id
+    else
+      raise InvalidAddressError, "not an IPv6 address"
+    end
+  end
+
+  # Returns the IPv6 zone identifier, if present.
+  # Raises InvalidAddressError if not an IPv6 address.
+  def zone_id=(zid)
+    if @family == Socket::AF_INET6
+      case zid
+      when nil, /\A%(\w+)\z/
+        @zone_id = zid
+      else
+        raise InvalidAddressError, "invalid zone identifier for address"
+      end
+    else
+      raise InvalidAddressError, "not an IPv6 address"
+    end
+  end
+
   protected
 
   # Set +@addr+, the internal stored ip address, to given +addr+. The
@@ -579,6 +611,11 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L611
       prefix = $1
       family = Socket::AF_INET6
     end
+    if prefix =~ /\A(.*)(%\w+)\z/
+      prefix = $1
+      zone_id = $2
+      family = Socket::AF_INET6
+    end
     # It seems AI_NUMERICHOST doesn't do the job.
     #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
     #                  Socket::AI_NUMERICHOST)
@@ -593,6 +630,7 @@ class IPAddr https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L630
       @addr = in6_addr(prefix)
       @family = Socket::AF_INET6
     end
+    @zone_id = zone_id
     if family != Socket::AF_UNSPEC && @family != family
       raise AddressFamilyError, "address family mismatch"
     end
diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb
index c055f4b2c4..029ad06642 100644
--- a/test/test_ipaddr.rb
+++ b/test/test_ipaddr.rb
@@ -43,6 +43,17 @@ class TC_IPAddr < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_ipaddr.rb#L43
     assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
     assert_equal(Socket::AF_INET6, a.family)
     assert_equal(48, a.prefix)
+    assert_nil(a.zone_id)
+
+    a = IPAddr.new("fe80::1%ab0")
+    assert_equal("fe80::1%ab0", a.to_s)
+    assert_equal("fe80:0000:0000:0000:0000:0000:0000:0001%ab0", a.to_string)
+    assert_equal(Socket::AF_INET6, a.family)
+    assert_equal(false, a.ipv4?)
+    assert_equal(true, a.ipv6?)
+    assert_equal("#<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0001%ab0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>", a.inspect)
+    assert_equal(128, a.prefix)
+    assert_equal('%ab0', a.zone_id)
 
     a = IPAddr.new("0.0.0.0")
     assert_equal("0.0.0.0", a.to_s)
@@ -87,7 +98,8 @@ class TC_IPAddr < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_ipaddr.rb#L98
 
     assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") }
     assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.011") }
-    assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%fxp0") }
+    assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%") }
+    assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%]") }
     assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("[192.168.1.2]/120") }
     assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("[2001:200:300::]\nINVALID") }
     assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.1/32\nINVALID") }
@@ -231,6 +243,18 @@ class TC_IPAddr < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_ipaddr.rb#L243
     a = IPAddr.new("192.168.1.2/24")
     assert_equal(a.netmask, "255.255.255.0")
   end
+
+  def test_zone_id
+    a = IPAddr.new("192.168.1.2")
+    assert_raise(IPAddr::InvalidAddressError) { a.zone_id = '%ab0' }
+    assert_raise(IPAddr::InvalidAddressError) { a.zone_id }
+
+    a = IPAddr.new("1:2:3:4:5:6:7:8")
+    a.zone_id = '%ab0'
+    assert_equal('%ab0', a.zone_id)
+    assert_equal("1:2:3:4:5:6:7:8%ab0", a.to_s)
+    assert_raise(IPAddr::InvalidAddressError) { a.zone_id = '%' }
+  end
 end
 
 class TC_Operator < Test::Unit::TestCase
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]