* lib/ipaddr.rb: Inhibit zero-filled octets in an IPv4 address in
all platforms. [ruby-dev:45671] * lib/ipaddr.rb: Allow the x❌x❌x❌d.d.d.d form not limited to IPv4 mapped/compatible addresses. This change also makes it possible for the parser to understand IPv4 mapped and compatible IPv6 addresses in non-compressed form. * lib/ipaddr.rb: Stop exposing IPSocket.valid*? methods which were only usable on non-IPv6-ready platforms. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35865 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
70be643c5b
commit
866761d2e8
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
Sat Jun 2 18:09:02 2012 Akinori MUSHA <knu@iDaemons.org>
|
||||||
|
|
||||||
|
* lib/ipaddr.rb: Inhibit zero-filled octets in an IPv4 address in
|
||||||
|
all platforms. [ruby-dev:45671]
|
||||||
|
|
||||||
|
* lib/ipaddr.rb: Allow the x:x:x:x:x:x:d.d.d.d form not limited to
|
||||||
|
IPv4 mapped/compatible addresses. This change also makes it
|
||||||
|
possible for the parser to understand IPv4 mapped and compatible
|
||||||
|
IPv6 addresses in non-compressed form.
|
||||||
|
|
||||||
|
* lib/ipaddr.rb: Stop exposing IPSocket.valid*? methods which were
|
||||||
|
only usable on non-IPv6-ready platforms.
|
||||||
|
|
||||||
Sat Jun 2 16:59:00 2012 NARUSE, Yui <naruse@ruby-lang.org>
|
Sat Jun 2 16:59:00 2012 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
* string.c (rb_enc_cr_str_buf_cat): don't reset coderange as unknown.
|
* string.c (rb_enc_cr_str_buf_cat): don't reset coderange as unknown.
|
||||||
|
216
lib/ipaddr.rb
216
lib/ipaddr.rb
@ -2,7 +2,7 @@
|
|||||||
# ipaddr.rb - A class to manipulate an IP address
|
# ipaddr.rb - A class to manipulate an IP address
|
||||||
#
|
#
|
||||||
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
|
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
|
||||||
# Copyright (c) 2007 Akinori MUSHA <knu@iDaemons.org>.
|
# Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# You can redistribute and/or modify it under the same terms as Ruby.
|
# You can redistribute and/or modify it under the same terms as Ruby.
|
||||||
@ -17,61 +17,6 @@
|
|||||||
#
|
#
|
||||||
require 'socket'
|
require 'socket'
|
||||||
|
|
||||||
unless Socket.const_defined? "AF_INET6"
|
|
||||||
class Socket < BasicSocket
|
|
||||||
# IPv6 protocol family
|
|
||||||
AF_INET6 = Object.new
|
|
||||||
end
|
|
||||||
|
|
||||||
class << IPSocket
|
|
||||||
# Returns +true+ if +addr+ is a valid IPv4 address.
|
|
||||||
def valid_v4?(addr)
|
|
||||||
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
|
|
||||||
return $~.captures.all? {|i| i.to_i < 256}
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns +true+ if +addr+ is a valid IPv6 address.
|
|
||||||
def valid_v6?(addr)
|
|
||||||
# IPv6 (normal)
|
|
||||||
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
|
|
||||||
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
|
|
||||||
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
|
|
||||||
# IPv6 (IPv4 compat)
|
|
||||||
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
|
|
||||||
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
|
|
||||||
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns +true+ if +addr+ is either a valid IPv4 or IPv6 address.
|
|
||||||
def valid?(addr)
|
|
||||||
valid_v4?(addr) || valid_v6?(addr)
|
|
||||||
end
|
|
||||||
|
|
||||||
alias getaddress_orig getaddress
|
|
||||||
|
|
||||||
# Returns a +String+ based representation of a valid DNS hostname,
|
|
||||||
# IPv4 or IPv6 address.
|
|
||||||
#
|
|
||||||
# IPSocket.getaddress 'localhost' #=> "::1"
|
|
||||||
# IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255"
|
|
||||||
# IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
|
|
||||||
# IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122"
|
|
||||||
def getaddress(s)
|
|
||||||
if valid?(s)
|
|
||||||
s
|
|
||||||
elsif /\A[-A-Za-z\d.]+\Z/ =~ s
|
|
||||||
getaddress_orig(s)
|
|
||||||
else
|
|
||||||
raise ArgumentError, "invalid address"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
|
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
|
||||||
# IPv6 are supported.
|
# IPv6 are supported.
|
||||||
#
|
#
|
||||||
@ -102,6 +47,41 @@ class IPAddr
|
|||||||
# Format string for IPv6
|
# Format string for IPv6
|
||||||
IN6FORMAT = (["%.4x"] * 8).join(':')
|
IN6FORMAT = (["%.4x"] * 8).join(':')
|
||||||
|
|
||||||
|
# Regexp _internally_ used for parsing IPv4 address.
|
||||||
|
RE_IPV4ADDRLIKE = %r{
|
||||||
|
\A
|
||||||
|
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
||||||
|
\z
|
||||||
|
}x
|
||||||
|
|
||||||
|
# Regexp _internally_ used for parsing IPv6 address.
|
||||||
|
RE_IPV6ADDRLIKE_FULL = %r{
|
||||||
|
\A
|
||||||
|
(?:
|
||||||
|
(?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
|
||||||
|
|
|
||||||
|
( (?: [\da-f]{1,4} : ){6} )
|
||||||
|
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
||||||
|
)
|
||||||
|
\z
|
||||||
|
}xi
|
||||||
|
|
||||||
|
# Regexp _internally_ used for parsing IPv6 address.
|
||||||
|
RE_IPV6ADDRLIKE_COMPRESSED = %r{
|
||||||
|
\A
|
||||||
|
( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
|
||||||
|
::
|
||||||
|
( (?:
|
||||||
|
( (?: [\da-f]{1,4} : )* )
|
||||||
|
(?:
|
||||||
|
[\da-f]{1,4}
|
||||||
|
|
|
||||||
|
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
||||||
|
)
|
||||||
|
)? )
|
||||||
|
\z
|
||||||
|
}xi
|
||||||
|
|
||||||
# Returns the address family of this IP address.
|
# Returns the address family of this IP address.
|
||||||
attr_reader :family
|
attr_reader :family
|
||||||
|
|
||||||
@ -212,7 +192,7 @@ class IPAddr
|
|||||||
|
|
||||||
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
|
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
|
||||||
loop do
|
loop do
|
||||||
break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
|
break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
|
||||||
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
|
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
|
||||||
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
|
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
|
||||||
break if str.sub!(/\b0:0:0:0:0\b/, ':')
|
break if str.sub!(/\b0:0:0:0:0\b/, ':')
|
||||||
@ -223,7 +203,7 @@ class IPAddr
|
|||||||
end
|
end
|
||||||
str.sub!(/:{3,}/, '::')
|
str.sub!(/:{3,}/, '::')
|
||||||
|
|
||||||
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\Z/i =~ str
|
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
|
||||||
str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
|
str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -490,11 +470,6 @@ class IPAddr
|
|||||||
# 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)
|
||||||
begin
|
|
||||||
IPSocket.getaddress(prefix) # test if address is valid
|
|
||||||
rescue
|
|
||||||
raise ArgumentError, "invalid address"
|
|
||||||
end
|
|
||||||
@addr = @family = nil
|
@addr = @family = nil
|
||||||
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
|
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
|
||||||
@addr = in_addr(prefix)
|
@addr = in_addr(prefix)
|
||||||
@ -528,26 +503,44 @@ class IPAddr
|
|||||||
end
|
end
|
||||||
|
|
||||||
def in_addr(addr)
|
def in_addr(addr)
|
||||||
if addr =~ /^\d+\.\d+\.\d+\.\d+$/
|
case addr
|
||||||
return addr.split('.').inject(0) { |i, s|
|
when Array
|
||||||
i << 8 | s.to_i
|
octets = addr
|
||||||
}
|
else
|
||||||
|
m = RE_IPV4ADDRLIKE.match(addr) or return nil
|
||||||
|
octets = m.captures
|
||||||
end
|
end
|
||||||
return nil
|
octets.inject(0) { |i, s|
|
||||||
|
(n = s.to_i) < 256 or raise ArgumentError, "invalid address"
|
||||||
|
s.match(/\A0./) and raise ArgumentError, "zero-filled number is ambiguous"
|
||||||
|
i << 8 | n
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def in6_addr(left)
|
def in6_addr(left)
|
||||||
case left
|
case left
|
||||||
when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
|
when RE_IPV6ADDRLIKE_FULL
|
||||||
return in_addr($1) + 0xffff00000000
|
if $2
|
||||||
when /^::(\d+\.\d+\.\d+\.\d+)$/i
|
addr = in_addr($~[2,4])
|
||||||
return in_addr($1)
|
left = $1 + ':'
|
||||||
when /[^0-9a-f:]/i
|
|
||||||
raise ArgumentError, "invalid address"
|
|
||||||
when /^(.*)::(.*)$/
|
|
||||||
left, right = $1, $2
|
|
||||||
else
|
else
|
||||||
|
addr = 0
|
||||||
|
end
|
||||||
right = ''
|
right = ''
|
||||||
|
when RE_IPV6ADDRLIKE_COMPRESSED
|
||||||
|
if $4
|
||||||
|
left.count(':') <= 6 or raise ArgumentError, "invalid address"
|
||||||
|
addr = in_addr($~[4,4])
|
||||||
|
left = $1
|
||||||
|
right = $3 + '0:0'
|
||||||
|
else
|
||||||
|
left.count(':') <= 7 or raise ArgumentError, "invalid address"
|
||||||
|
left = $1
|
||||||
|
right = $2
|
||||||
|
addr = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise ArgumentError, "invalid address"
|
||||||
end
|
end
|
||||||
l = left.split(':')
|
l = left.split(':')
|
||||||
r = right.split(':')
|
r = right.split(':')
|
||||||
@ -555,9 +548,9 @@ class IPAddr
|
|||||||
if rest < 0
|
if rest < 0
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return (l + Array.new(rest, '0') + r).inject(0) { |i, s|
|
(l + Array.new(rest, '0') + r).inject(0) { |i, s|
|
||||||
i << 16 | s.hex
|
i << 16 | s.hex
|
||||||
}
|
} | addr
|
||||||
end
|
end
|
||||||
|
|
||||||
def addr_mask(addr)
|
def addr_mask(addr)
|
||||||
@ -599,6 +592,55 @@ class IPAddr
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless Socket.const_defined? "AF_INET6"
|
||||||
|
class Socket < BasicSocket
|
||||||
|
# IPv6 protocol family
|
||||||
|
AF_INET6 = Object.new
|
||||||
|
end
|
||||||
|
|
||||||
|
class << IPSocket
|
||||||
|
private
|
||||||
|
|
||||||
|
def valid_v6?(addr)
|
||||||
|
case addr
|
||||||
|
when IPAddr::RE_IPV6ADDRLIKE_FULL
|
||||||
|
if $2
|
||||||
|
$~[2,4].all? {|i| i.to_i < 256 }
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
|
||||||
|
if $4
|
||||||
|
addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
|
||||||
|
else
|
||||||
|
addr.count(':') <= 7
|
||||||
|
end
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias getaddress_orig getaddress
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
# Returns a +String+ based representation of a valid DNS hostname,
|
||||||
|
# IPv4 or IPv6 address.
|
||||||
|
#
|
||||||
|
# IPSocket.getaddress 'localhost' #=> "::1"
|
||||||
|
# IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255"
|
||||||
|
# IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
|
||||||
|
# IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122"
|
||||||
|
def getaddress(s)
|
||||||
|
if valid_v6?(s)
|
||||||
|
s
|
||||||
|
else
|
||||||
|
getaddress_orig(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if $0 == __FILE__
|
if $0 == __FILE__
|
||||||
eval DATA.read, nil, $0, __LINE__+4
|
eval DATA.read, nil, $0, __LINE__+4
|
||||||
end
|
end
|
||||||
@ -609,10 +651,15 @@ require 'test/unit'
|
|||||||
|
|
||||||
class TC_IPAddr < Test::Unit::TestCase
|
class TC_IPAddr < Test::Unit::TestCase
|
||||||
def test_s_new
|
def test_s_new
|
||||||
|
[
|
||||||
|
["3FFE:505:ffff::/48"],
|
||||||
|
["0:0:0:1::"],
|
||||||
|
["2001:200:300::/48"],
|
||||||
|
["2001:200:300::192.168.1.2/48"],
|
||||||
|
].each { |args|
|
||||||
assert_nothing_raised {
|
assert_nothing_raised {
|
||||||
IPAddr.new("3FFE:505:ffff::/48")
|
IPAddr.new(*args)
|
||||||
IPAddr.new("0:0:0:1::")
|
}
|
||||||
IPAddr.new("2001:200:300::/48")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a = IPAddr.new
|
a = IPAddr.new
|
||||||
@ -667,9 +714,10 @@ class TC_IPAddr < Test::Unit::TestCase
|
|||||||
assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
|
assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
|
||||||
|
|
||||||
[
|
[
|
||||||
|
["192.168.0.256"],
|
||||||
|
["192.168.0.011"],
|
||||||
["fe80::1%fxp0"],
|
["fe80::1%fxp0"],
|
||||||
["::1/255.255.255.0"],
|
["::1/255.255.255.0"],
|
||||||
["::1:192.168.1.2/120"],
|
|
||||||
[IPAddr.new("::1").to_i],
|
[IPAddr.new("::1").to_i],
|
||||||
["::ffff:192.168.1.2/120", Socket::AF_INET],
|
["::ffff:192.168.1.2/120", Socket::AF_INET],
|
||||||
["[192.168.1.2]/120"],
|
["[192.168.1.2]/120"],
|
||||||
@ -811,7 +859,9 @@ class TC_Operator < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_equal
|
def test_equal
|
||||||
assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
|
assert_equal(true, @a == IPAddr.new("3FFE:505:2::"))
|
||||||
|
assert_equal(true, @a == IPAddr.new("3ffe:0505:0002::"))
|
||||||
|
assert_equal(true, @a == IPAddr.new("3ffe:0505:0002:0:0:0:0:0"))
|
||||||
assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
|
assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
|
||||||
assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
|
assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
|
||||||
assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
|
assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user