diff options
Diffstat (limited to 'lib/ipaddr.rb')
-rw-r--r-- | lib/ipaddr.rb | 110 |
1 files changed, 76 insertions, 34 deletions
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 7a5cf94830..dbb213c90a 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -40,7 +40,7 @@ require 'socket' # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0> class IPAddr - VERSION = "1.2.5" + VERSION = "1.2.6" # 32 bit mask for IPv4 IN4MASK = 0xffffffff @@ -52,7 +52,7 @@ class IPAddr # Regexp _internally_ used for parsing IPv4 address. RE_IPV4ADDRLIKE = %r{ \A - (\d+) \. (\d+) \. (\d+) \. (\d+) + \d+ \. \d+ \. \d+ \. \d+ \z }x @@ -110,8 +110,13 @@ class IPAddr # Convert a network byte ordered string form of an IP address into # human readable form. + # It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY). def self.ntop(addr) - case addr.size + if addr.is_a?(String) && addr.encoding != Encoding::BINARY + raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)" + end + + case addr.bytesize when 4 addr.unpack('C4').join('.') when 16 @@ -176,9 +181,7 @@ class IPAddr def include?(other) other = coerce_other(other) return false unless other.family == family - range = to_range - other = other.to_range - range.begin <= other.begin && range.end >= other.end + begin_addr <= other.begin_addr && end_addr >= other.end_addr end alias === include? @@ -224,6 +227,12 @@ class IPAddr return str end + # Returns a string containing the IP address representation in + # cidr notation + def cidr + format("%s/%s", to_s, prefix) + end + # Returns a network byte ordered string form of the IP address. def hton case @family @@ -249,12 +258,17 @@ class IPAddr 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 + @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8 when Socket::AF_INET6 - @addr == 1 + @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 @@ -263,7 +277,8 @@ class IPAddr # 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. + # 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 @@ -271,22 +286,31 @@ class IPAddr @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 & 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 + # 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. + # 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 + @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 @@ -400,17 +424,6 @@ class IPAddr # Creates a Range object for the network address. def to_range - begin_addr = (@addr & @mask_addr) - - case @family - when Socket::AF_INET - end_addr = (@addr | (IN4MASK ^ @mask_addr)) - when Socket::AF_INET6 - end_addr = (@addr | (IN6MASK ^ @mask_addr)) - else - raise AddressFamilyError, "unsupported address family" - end - self.class.new(begin_addr, @family)..self.class.new(end_addr, @family) end @@ -439,7 +452,7 @@ class IPAddr when Integer mask!(prefix) else - raise InvalidPrefixError, "prefix must be an integer: #{@addr}" + raise InvalidPrefixError, "prefix must be an integer" end end @@ -464,6 +477,20 @@ class IPAddr _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 @@ -491,6 +518,21 @@ class IPAddr protected + 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 + # Set +@addr+, the internal stored ip address, to given +addr+. The # parameter +addr+ is validated using the first +family+ member, # which is +Socket::AF_INET+ or +Socket::AF_INET6+. @@ -498,11 +540,11 @@ class IPAddr case family[0] ? family[0] : @family when Socket::AF_INET if addr < 0 || addr > IN4MASK - raise InvalidAddressError, "invalid address: #{@addr}" + raise InvalidAddressError, "invalid address: #{addr}" end when Socket::AF_INET6 if addr < 0 || addr > IN6MASK - raise InvalidAddressError, "invalid address: #{@addr}" + raise InvalidAddressError, "invalid address: #{addr}" end else raise AddressFamilyError, "unsupported address family" @@ -529,12 +571,12 @@ class IPAddr else m = IPAddr.new(mask) if m.family != @family - raise InvalidPrefixError, "address family is not same: #{@addr}" + 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}: #{@addr}" + raise InvalidPrefixError, "invalid mask #{mask}" end @addr &= @mask_addr return self @@ -545,13 +587,13 @@ class IPAddr case @family when Socket::AF_INET if prefixlen < 0 || prefixlen > 32 - raise InvalidPrefixError, "invalid length: #{@addr}" + raise InvalidPrefixError, "invalid length" end masklen = 32 - prefixlen @mask_addr = ((IN4MASK >> masklen) << masklen) when Socket::AF_INET6 if prefixlen < 0 || prefixlen > 128 - raise InvalidPrefixError, "invalid length: #{@addr}" + raise InvalidPrefixError, "invalid length" end masklen = 128 - prefixlen @mask_addr = ((IN6MASK >> masklen) << masklen) @@ -647,12 +689,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: #{@addr}" - s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}" + (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}" i << 8 | n } end |