mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[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
09a6408fb2
This commit is contained in:
parent
74ed881e10
commit
bd6e1a0f08
2 changed files with 67 additions and 5 deletions
|
@ -231,7 +231,13 @@ class IPAddr
|
||||||
# Returns a string containing the IP address representation in
|
# Returns a string containing the IP address representation in
|
||||||
# canonical form.
|
# canonical form.
|
||||||
def to_string
|
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
|
end
|
||||||
|
|
||||||
# Returns a network byte ordered string form of the IP address.
|
# Returns a network byte ordered string form of the IP address.
|
||||||
|
@ -403,7 +409,7 @@ class IPAddr
|
||||||
|
|
||||||
# Returns a hash value used by Hash, Set, and Array classes
|
# Returns a hash value used by Hash, Set, and Array classes
|
||||||
def hash
|
def hash
|
||||||
return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
|
return ([@addr, @mask_addr, @zone_id].hash << 1) | (ipv4? ? 0 : 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a Range object for the network address.
|
# Creates a Range object for the network address.
|
||||||
|
@ -459,11 +465,12 @@ class IPAddr
|
||||||
af = "IPv4"
|
af = "IPv4"
|
||||||
when Socket::AF_INET6
|
when Socket::AF_INET6
|
||||||
af = "IPv6"
|
af = "IPv6"
|
||||||
|
zone_id = @zone_id.to_s
|
||||||
else
|
else
|
||||||
raise AddressFamilyError, "unsupported address family"
|
raise AddressFamilyError, "unsupported address family"
|
||||||
end
|
end
|
||||||
return sprintf("#<%s: %s:%s/%s>", self.class.name,
|
return sprintf("#<%s: %s:%s%s/%s>", self.class.name,
|
||||||
af, _to_string(@addr), _to_string(@mask_addr))
|
af, _to_string(@addr), zone_id, _to_string(@mask_addr))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the netmask in string format e.g. 255.255.0.0
|
# Returns the netmask in string format e.g. 255.255.0.0
|
||||||
|
@ -471,6 +478,31 @@ class IPAddr
|
||||||
_to_string(@mask_addr)
|
_to_string(@mask_addr)
|
||||||
end
|
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
|
protected
|
||||||
|
|
||||||
# Set +@addr+, the internal stored ip address, to given +addr+. The
|
# Set +@addr+, the internal stored ip address, to given +addr+. The
|
||||||
|
@ -579,6 +611,11 @@ class IPAddr
|
||||||
prefix = $1
|
prefix = $1
|
||||||
family = Socket::AF_INET6
|
family = Socket::AF_INET6
|
||||||
end
|
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.
|
# It seems AI_NUMERICHOST doesn't do the job.
|
||||||
#Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
|
#Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
|
||||||
# Socket::AI_NUMERICHOST)
|
# Socket::AI_NUMERICHOST)
|
||||||
|
@ -593,6 +630,7 @@ class IPAddr
|
||||||
@addr = in6_addr(prefix)
|
@addr = in6_addr(prefix)
|
||||||
@family = Socket::AF_INET6
|
@family = Socket::AF_INET6
|
||||||
end
|
end
|
||||||
|
@zone_id = zone_id
|
||||||
if family != Socket::AF_UNSPEC && @family != family
|
if family != Socket::AF_UNSPEC && @family != family
|
||||||
raise AddressFamilyError, "address family mismatch"
|
raise AddressFamilyError, "address family mismatch"
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,6 +43,17 @@ class TC_IPAddr < Test::Unit::TestCase
|
||||||
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
|
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
|
||||||
assert_equal(Socket::AF_INET6, a.family)
|
assert_equal(Socket::AF_INET6, a.family)
|
||||||
assert_equal(48, a.prefix)
|
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")
|
a = IPAddr.new("0.0.0.0")
|
||||||
assert_equal("0.0.0.0", a.to_s)
|
assert_equal("0.0.0.0", a.to_s)
|
||||||
|
@ -87,7 +98,8 @@ class TC_IPAddr < Test::Unit::TestCase
|
||||||
|
|
||||||
assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") }
|
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("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("[192.168.1.2]/120") }
|
||||||
assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("[2001:200:300::]\nINVALID") }
|
assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("[2001:200:300::]\nINVALID") }
|
||||||
assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.1/32\nINVALID") }
|
assert_raise(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.1/32\nINVALID") }
|
||||||
|
@ -231,6 +243,18 @@ class TC_IPAddr < Test::Unit::TestCase
|
||||||
a = IPAddr.new("192.168.1.2/24")
|
a = IPAddr.new("192.168.1.2/24")
|
||||||
assert_equal(a.netmask, "255.255.255.0")
|
assert_equal(a.netmask, "255.255.255.0")
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
class TC_Operator < Test::Unit::TestCase
|
class TC_Operator < Test::Unit::TestCase
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue