diff options
Diffstat (limited to 'lib/ipaddr.rb')
| -rw-r--r-- | lib/ipaddr.rb | 614 |
1 files changed, 268 insertions, 346 deletions
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 13016fcbba..6b67d7eec6 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # # ipaddr.rb - A class to manipulate an IP address # @@ -39,18 +40,20 @@ require 'socket' # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0> class IPAddr + # The version string + VERSION = "1.2.8" # 32 bit mask for IPv4 IN4MASK = 0xffffffff - # 128 bit mask for IPv4 + # 128 bit mask for IPv6 IN6MASK = 0xffffffffffffffffffffffffffffffff # Format string for IPv6 - IN6FORMAT = (["%.4x"] * 8).join(':') + IN6FORMAT = (["%.4x"] * 8).join(':').freeze # Regexp _internally_ used for parsing IPv4 address. RE_IPV4ADDRLIKE = %r{ \A - (\d+) \. (\d+) \. (\d+) \. (\d+) + \d+ \. \d+ \. \d+ \. \d+ \z }x @@ -102,22 +105,26 @@ class IPAddr # Creates a new ipaddr containing the given network byte ordered # string form of an IP address. - def IPAddr::new_ntoh(addr) - return IPAddr.new(IPAddr::ntop(addr)) + def self.new_ntoh(addr) + return new(ntop(addr)) end # Convert a network byte ordered string form of an IP address into # human readable form. - def IPAddr::ntop(addr) - case addr.size + # It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY). + def self.ntop(addr) + if addr.is_a?(String) && addr.encoding != Encoding::BINARY + raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)" + end + + case addr.bytesize when 4 - s = addr.unpack('C4').join('.') + addr.unpack('C4').join('.') when 16 - s = IN6FORMAT % addr.unpack('n8') + IN6FORMAT % addr.unpack('n8') else raise AddressFamilyError, "unsupported address family" end - return s end # Returns a new ipaddr built by bitwise AND. @@ -145,10 +152,23 @@ class IPAddr return self.clone.set(addr_mask(~@addr)) end + # Returns a new ipaddr greater than the original address by offset + def +(offset) + self.clone.set(@addr + offset, @family) + end + + # Returns a new ipaddr less than the original address by offset + def -(offset) + self.clone.set(@addr - offset, @family) + end + # Returns true if two ipaddrs are equal. def ==(other) other = coerce_other(other) - return @family == other.family && @addr == other.to_i + rescue + false + else + @family == other.family && @addr == other.to_i end # Returns a new ipaddr built by masking IP address with the given @@ -164,34 +184,15 @@ class IPAddr # net1 = IPAddr.new("192.168.2.0/24") # net2 = IPAddr.new("192.168.2.100") # net3 = IPAddr.new("192.168.3.0") + # net4 = IPAddr.new("192.168.2.0/16") # p net1.include?(net2) #=> true # p net1.include?(net3) #=> false + # p net1.include?(net4) #=> false + # p net4.include?(net1) #=> true def include?(other) other = coerce_other(other) - if ipv4_mapped? - if (@mask_addr >> 32) != 0xffffffffffffffffffffffff - return false - end - mask_addr = (@mask_addr & IN4MASK) - addr = (@addr & IN4MASK) - family = Socket::AF_INET - else - mask_addr = @mask_addr - addr = @addr - family = @family - end - if other.ipv4_mapped? - other_addr = (other.to_i & IN4MASK) - other_family = Socket::AF_INET - else - other_addr = other.to_i - other_family = other.family - end - - if family != other_family - return false - end - return ((addr & mask_addr) == (other_addr & mask_addr)) + return false unless other.family == family + begin_addr <= other.begin_addr && end_addr >= other.end_addr end alias === include? @@ -228,7 +229,35 @@ class IPAddr # 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 string containing the IP address representation with prefix. + def as_json(*) + if ipv4? && prefix == 32 + to_s + elsif ipv6? && prefix == 128 + to_s + else + cidr + end + end + + # Returns a json string containing the IP address representation. + def to_json(*a) + %Q{"#{as_json(*a)}"} + end + + # Returns a string containing the IP address representation in + # cidr notation + def cidr + "#{to_s}/#{prefix}" end # Returns a network byte ordered string form of the IP address. @@ -255,6 +284,65 @@ class IPAddr return @family == Socket::AF_INET6 end + # Returns true if the ipaddr is a loopback address. + # Loopback IPv4 addresses in the IPv4-mapped IPv6 + # address range are also considered as loopback addresses. + def loopback? + case @family + when Socket::AF_INET + @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8 + when Socket::AF_INET6 + @addr == 1 || # ::1 + (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( + @addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8 + )) + else + raise AddressFamilyError, "unsupported address family" + end + end + + # Returns true if the ipaddr is a private address. IPv4 addresses + # in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC + # 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC + # 4193 are considered private. Private IPv4 addresses in the + # IPv4-mapped IPv6 address range are also considered private. + def private? + case @family + when Socket::AF_INET + @addr & 0xff000000 == 0x0a000000 || # 10.0.0.0/8 + @addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12 + @addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16 + when Socket::AF_INET6 + @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000 || + (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( + @addr & 0xff000000 == 0x0a000000 || # ::ffff:10.0.0.0/8 + @addr & 0xfff00000 == 0xac100000 || # ::ffff::172.16.0.0/12 + @addr & 0xffff0000 == 0xc0a80000 # ::ffff::192.168.0.0/16 + )) + else + raise AddressFamilyError, "unsupported address family" + end + end + + # Returns true if the ipaddr is a link-local address. IPv4 + # addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local + # IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are + # considered link-local. Link-local IPv4 addresses in the + # IPv4-mapped IPv6 address range are also considered link-local. + def link_local? + case @family + when Socket::AF_INET + @addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16 + when Socket::AF_INET6 + @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10 + (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( + @addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16 + )) + else + raise AddressFamilyError, "unsupported address family" + end + end + # Returns true if the ipaddr is an IPv4-mapped IPv6 address. def ipv4_mapped? return ipv6? && (@addr >> 32) == 0xffff @@ -262,6 +350,11 @@ class IPAddr # Returns true if the ipaddr is an IPv4-compatible IPv6 address. def ipv4_compat? + warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE + _ipv4_compat? + end + + def _ipv4_compat? # :nodoc: if !ipv6? || (@addr >> 32) != 0 return false end @@ -269,29 +362,36 @@ class IPAddr return a != 0 && a != 1 end + private :_ipv4_compat? + # Returns a new ipaddr built by converting the native IPv4 address # into an IPv4-mapped IPv6 address. def ipv4_mapped if !ipv4? - raise InvalidAddressError, "not an IPv4 address" + raise InvalidAddressError, "not an IPv4 address: #{to_s}" end - return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) + clone = self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) + clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000) + clone end # Returns a new ipaddr built by converting the native IPv4 address # into an IPv4-compatible IPv6 address. def ipv4_compat + warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE if !ipv4? - raise InvalidAddressError, "not an IPv4 address" + raise InvalidAddressError, "not an IPv4 address: #{to_s}" end - return self.clone.set(@addr, Socket::AF_INET6) + clone = self.clone.set(@addr, Socket::AF_INET6) + clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000) + clone end # Returns a new ipaddr built by converting the IPv6 address into a # native IPv4 address. If the IP address is not an IPv4-mapped or # IPv4-compatible IPv6 address, returns self. def native - if !ipv4_mapped? && !ipv4_compat? + if !ipv4_mapped? && !_ipv4_compat? return self end return self.clone.set(@addr & IN4MASK, Socket::AF_INET) @@ -313,7 +413,7 @@ class IPAddr # Returns a string for DNS reverse lookup compatible with RFC3172. def ip6_arpa if !ipv6? - raise InvalidAddressError, "not an IPv6 address" + raise InvalidAddressError, "not an IPv6 address: #{to_s}" end return _reverse + ".ip6.arpa" end @@ -321,7 +421,7 @@ class IPAddr # Returns a string for DNS reverse lookup compatible with RFC1886. def ip6_int if !ipv6? - raise InvalidAddressError, "not an IPv6 address" + raise InvalidAddressError, "not an IPv6 address: #{to_s}" end return _reverse + ".ip6.int" end @@ -334,10 +434,10 @@ class IPAddr # Compares the ipaddr with another. def <=>(other) other = coerce_other(other) - - return nil if other.family != @family - - return @addr <=> other.to_i + rescue + nil + else + @addr <=> other.to_i if other.family == @family end include Comparable @@ -348,23 +448,41 @@ class IPAddr # 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. def to_range - begin_addr = (@addr & @mask_addr) + self.class.new(begin_addr, @family)..self.class.new(end_addr, @family) + end + # Returns the prefix length in bits for the ipaddr. + def prefix case @family when Socket::AF_INET - end_addr = (@addr | (IN4MASK ^ @mask_addr)) + n = IN4MASK ^ @mask_addr + i = 32 when Socket::AF_INET6 - end_addr = (@addr | (IN6MASK ^ @mask_addr)) + n = IN6MASK ^ @mask_addr + i = 128 else raise AddressFamilyError, "unsupported address family" end + while n.positive? + n >>= 1 + i -= 1 + end + i + end - return clone.set(begin_addr, @family)..clone.set(end_addr, @family) + # Sets the prefix length in bits + def prefix=(prefix) + case prefix + when Integer + mask!(prefix) + else + raise InvalidPrefixError, "prefix must be an integer" + end end # Returns a string containing a human-readable representation of the @@ -375,14 +493,76 @@ class IPAddr 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 + def netmask + _to_string(@mask_addr) + end + + # Returns the wildcard mask in string format e.g. 0.0.255.255 + def wildcard_mask + case @family + when Socket::AF_INET + mask = IN4MASK ^ @mask_addr + when Socket::AF_INET6 + mask = IN6MASK ^ @mask_addr + else + raise AddressFamilyError, "unsupported address family" + end + + _to_string(mask) + 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 + # :stopdoc: + + def begin_addr + @addr & @mask_addr + end + + def end_addr + case @family + when Socket::AF_INET + @addr | (IN4MASK ^ @mask_addr) + when Socket::AF_INET6 + @addr | (IN6MASK ^ @mask_addr) + else + raise AddressFamilyError, "unsupported address family" + end + end + #:startdoc: # Set +@addr+, the internal stored ip address, to given +addr+. The # parameter +addr+ is validated using the first +family+ member, @@ -391,11 +571,11 @@ class IPAddr case family[0] ? family[0] : @family when Socket::AF_INET if addr < 0 || addr > IN4MASK - raise InvalidAddressError, "invalid address" + raise InvalidAddressError, "invalid address: #{addr}" end when Socket::AF_INET6 if addr < 0 || addr > IN6MASK - raise InvalidAddressError, "invalid address" + raise InvalidAddressError, "invalid address: #{addr}" end else raise AddressFamilyError, "unsupported address family" @@ -403,21 +583,32 @@ class IPAddr @addr = addr if family[0] @family = family[0] + if @family == Socket::AF_INET + @mask_addr &= IN4MASK + end end return self end # Set current netmask to given mask. def mask!(mask) - if mask.kind_of?(String) - if mask =~ /^\d+$/ + case mask + when String + case mask + when /\A(0|[1-9]+\d*)\z/ prefixlen = mask.to_i + when /\A\d+\z/ + raise InvalidPrefixError, "leading zeros in prefix" else m = IPAddr.new(mask) if m.family != @family raise InvalidPrefixError, "address family is not same" end @mask_addr = m.to_i + n = @mask_addr ^ m.instance_variable_get(:@mask_addr) + unless ((n + 1) & n).zero? + raise InvalidPrefixError, "invalid mask #{mask}" + end @addr &= @mask_addr return self end @@ -465,6 +656,7 @@ class IPAddr # those, such as &, |, include? and ==, accept a string, or a packed # in_addr value instead of an IPAddr object. def initialize(addr = '::', family = Socket::AF_UNSPEC) + @mask_addr = nil if !addr.kind_of?(String) case family when Socket::AF_INET, Socket::AF_INET6 @@ -477,11 +669,16 @@ class IPAddr raise AddressFamilyError, "unsupported address family: #{family}" end end - prefix, prefixlen = addr.split('/') - if prefix =~ /^\[(.*)\]$/i + prefix, prefixlen = addr.split('/', 2) + if prefix =~ /\A\[(.*)\]\z/i 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) @@ -496,6 +693,7 @@ class IPAddr @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 @@ -506,6 +704,7 @@ class IPAddr end end + # :stopdoc: def coerce_other(other) case other when IPAddr @@ -522,12 +721,12 @@ class IPAddr when Array octets = addr else - m = RE_IPV4ADDRLIKE.match(addr) or return nil - octets = m.captures + RE_IPV4ADDRLIKE.match?(addr) or return nil + octets = addr.split('.') end octets.inject(0) { |i, s| - (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address" - s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous" + (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{addr}" + (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{addr}" i << 8 | n } end @@ -544,19 +743,19 @@ class IPAddr right = '' when RE_IPV6ADDRLIKE_COMPRESSED if $4 - left.count(':') <= 6 or raise InvalidAddressError, "invalid address" + left.count(':') <= 6 or raise InvalidAddressError, "invalid address: #{left}" addr = in_addr($~[4,4]) left = $1 right = $3 + '0:0' else left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or - raise InvalidAddressError, "invalid address" + raise InvalidAddressError, "invalid address: #{left}" left = $1 right = $2 addr = 0 end else - raise InvalidAddressError, "invalid address" + raise InvalidAddressError, "invalid address: #{left}" end l = left.split(':') r = right.split(':') @@ -611,13 +810,13 @@ end unless Socket.const_defined? :AF_INET6 class Socket < BasicSocket # IPv6 protocol family - AF_INET6 = Object.new + AF_INET6 = Object.new.freeze end class << IPSocket private - def valid_v6?(addr) + def valid_v6?(addr) # :nodoc: case addr when IPAddr::RE_IPV6ADDRLIKE_FULL if $2 @@ -656,280 +855,3 @@ unless Socket.const_defined? :AF_INET6 end end end - -if $0 == __FILE__ - eval DATA.read, nil, $0, __LINE__+4 -end - -__END__ - -require 'test/unit' - -class TC_IPAddr < Test::Unit::TestCase - def test_s_new - [ - ["3FFE:505:ffff::/48"], - ["0:0:0:1::"], - ["2001:200:300::/48"], - ["2001:200:300::192.168.1.2/48"], - ["1:2:3:4:5:6:7::"], - ["::2:3:4:5:6:7:8"], - ].each { |args| - assert_nothing_raised { - IPAddr.new(*args) - } - } - - a = IPAddr.new - assert_equal("::", a.to_s) - assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - - a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") - assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s) - assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - - a = IPAddr.new("3ffe:505:2::/48") - assert_equal("3ffe:505:2::", a.to_s) - assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - assert_equal(false, a.ipv4?) - assert_equal(true, a.ipv6?) - assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect) - - a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") - assert_equal("3ffe:505:2::", a.to_s) - assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - - a = IPAddr.new("0.0.0.0") - assert_equal("0.0.0.0", a.to_s) - assert_equal("0.0.0.0", a.to_string) - assert_equal(Socket::AF_INET, a.family) - - a = IPAddr.new("192.168.1.2") - assert_equal("192.168.1.2", a.to_s) - assert_equal("192.168.1.2", a.to_string) - assert_equal(Socket::AF_INET, a.family) - assert_equal(true, a.ipv4?) - assert_equal(false, a.ipv6?) - - a = IPAddr.new("192.168.1.2/24") - assert_equal("192.168.1.0", a.to_s) - assert_equal("192.168.1.0", a.to_string) - assert_equal(Socket::AF_INET, a.family) - assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect) - - a = IPAddr.new("192.168.1.2/255.255.255.0") - assert_equal("192.168.1.0", a.to_s) - assert_equal("192.168.1.0", a.to_string) - assert_equal(Socket::AF_INET, a.family) - - assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s) - 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) - assert_equal("1:2:3:4:5:6:7:0", IPAddr.new("1:2:3:4:5:6:7::").to_s) - assert_equal("0:2:3:4:5:6:7:8", IPAddr.new("::2:3:4:5:6:7:8").to_s) - - assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") } - assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.011") } - assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%fxp0") } - assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("[192.168.1.2]/120") } - assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/255.255.255.0") } - assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/129") } - assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/33") } - assert_raises(IPAddr::AddressFamilyError) { IPAddr.new(1) } - assert_raises(IPAddr::AddressFamilyError) { IPAddr.new("::ffff:192.168.1.2/120", Socket::AF_INET) } - end - - def test_s_new_ntoh - addr = '' - IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c| - addr += sprintf("%02x", c) - } - assert_equal("123456789abcdef0123456789abcdef0", addr) - addr = '' - IPAddr.new("123.45.67.89").hton.each_byte { |c| - addr += sprintf("%02x", c) - } - assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr) - a = IPAddr.new("3ffe:505:2::") - assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s) - a = IPAddr.new("192.168.2.1") - assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s) - end - - def test_ipv4_compat - a = IPAddr.new("::192.168.1.2") - assert_equal("::192.168.1.2", a.to_s) - assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - assert_equal(true, a.ipv4_compat?) - b = a.native - assert_equal("192.168.1.2", b.to_s) - assert_equal(Socket::AF_INET, b.family) - assert_equal(false, b.ipv4_compat?) - - a = IPAddr.new("192.168.1.2") - b = a.ipv4_compat - assert_equal("::192.168.1.2", b.to_s) - assert_equal(Socket::AF_INET6, b.family) - end - - def test_ipv4_mapped - a = IPAddr.new("::ffff:192.168.1.2") - assert_equal("::ffff:192.168.1.2", a.to_s) - assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string) - assert_equal(Socket::AF_INET6, a.family) - assert_equal(true, a.ipv4_mapped?) - b = a.native - assert_equal("192.168.1.2", b.to_s) - assert_equal(Socket::AF_INET, b.family) - assert_equal(false, b.ipv4_mapped?) - - a = IPAddr.new("192.168.1.2") - b = a.ipv4_mapped - assert_equal("::ffff:192.168.1.2", b.to_s) - assert_equal(Socket::AF_INET6, b.family) - end - - def test_reverse - assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").reverse) - assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse) - end - - def test_ip6_arpa - assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").ip6_arpa) - assert_raises(IPAddr::InvalidAddressError) { - IPAddr.new("192.168.2.1").ip6_arpa - } - end - - def test_ip6_int - assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddr.new("3ffe:505:2::f").ip6_int) - assert_raises(IPAddr::InvalidAddressError) { - IPAddr.new("192.168.2.1").ip6_int - } - end - - def test_to_s - assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string) - assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s) - end -end - -class TC_Operator < Test::Unit::TestCase - - IN6MASK32 = "ffff:ffff::" - IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" - - def setup - @in6_addr_any = IPAddr.new() - @a = IPAddr.new("3ffe:505:2::/48") - @b = IPAddr.new("0:0:0:1::") - @c = IPAddr.new(IN6MASK32) - end - alias set_up setup - - def test_or - assert_equal("3ffe:505:2:1::", (@a | @b).to_s) - a = @a - a |= @b - assert_equal("3ffe:505:2:1::", a.to_s) - assert_equal("3ffe:505:2::", @a.to_s) - assert_equal("3ffe:505:2:1::", - (@a | 0x00000000000000010000000000000000).to_s) - end - - def test_and - assert_equal("3ffe:505::", (@a & @c).to_s) - a = @a - a &= @c - assert_equal("3ffe:505::", a.to_s) - assert_equal("3ffe:505:2::", @a.to_s) - assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s) - end - - def test_shift_right - assert_equal("0:3ffe:505:2::", (@a >> 16).to_s) - a = @a - a >>= 16 - assert_equal("0:3ffe:505:2::", a.to_s) - assert_equal("3ffe:505:2::", @a.to_s) - end - - def test_shift_left - assert_equal("505:2::", (@a << 16).to_s) - a = @a - a <<= 16 - assert_equal("505:2::", a.to_s) - assert_equal("3ffe:505:2::", @a.to_s) - end - - def test_carrot - a = ~@in6_addr_any - assert_equal(IN6MASK128, a.to_s) - assert_equal("::", @in6_addr_any.to_s) - end - - def test_equal - 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(true, @a != IPAddr.new("3ffe:505:3::")) - assert_equal(false, @a != IPAddr.new("3ffe:505:2::")) - end - - def test_mask - a = @a.mask(32) - assert_equal("3ffe:505::", a.to_s) - assert_equal("3ffe:505:2::", @a.to_s) - end - - def test_include? - assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::"))) - assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1"))) - assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::"))) - net1 = IPAddr.new("192.168.2.0/24") - assert_equal(true, net1.include?(IPAddr.new("192.168.2.0"))) - assert_equal(true, net1.include?(IPAddr.new("192.168.2.255"))) - assert_equal(false, net1.include?(IPAddr.new("192.168.3.0"))) - # test with integer parameter - int = (192 << 24) + (168 << 16) + (2 << 8) + 13 - - assert_equal(true, net1.include?(int)) - assert_equal(false, net1.include?(int+255)) - - end - - def test_hash - a1 = IPAddr.new('192.168.2.0') - a2 = IPAddr.new('192.168.2.0') - a3 = IPAddr.new('3ffe:505:2::1') - a4 = IPAddr.new('3ffe:505:2::1') - a5 = IPAddr.new('127.0.0.1') - a6 = IPAddr.new('::1') - a7 = IPAddr.new('192.168.2.0/25') - a8 = IPAddr.new('192.168.2.0/25') - - h = { a1 => 'ipv4', a2 => 'ipv4', a3 => 'ipv6', a4 => 'ipv6', a5 => 'ipv4', a6 => 'ipv6', a7 => 'ipv4', a8 => 'ipv4'} - assert_equal(5, h.size) - assert_equal('ipv4', h[a1]) - assert_equal('ipv4', h[a2]) - assert_equal('ipv6', h[a3]) - assert_equal('ipv6', h[a4]) - - require 'set' - s = Set[a1, a2, a3, a4, a5, a6, a7, a8] - assert_equal(5, s.size) - assert_equal(true, s.include?(a1)) - assert_equal(true, s.include?(a2)) - assert_equal(true, s.include?(a3)) - assert_equal(true, s.include?(a4)) - assert_equal(true, s.include?(a5)) - assert_equal(true, s.include?(a6)) - end -end |
