1 # frozen_string_literal: true
3 # ipaddr.rb - A class to manipulate an IP address
5 # Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
6 # Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
9 # You can redistribute and/or modify it under the same terms as Ruby.
14 # - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
21 # IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
28 # ipaddr1 = IPAddr.new "3ffe:505:2::1"
30 # p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
32 # p ipaddr1.to_s #=> "3ffe:505:2::1"
34 # ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
36 # p ipaddr2.to_s #=> "3ffe:505:2::"
38 # ipaddr3 = IPAddr.new "192.168.2.0/24"
40 # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
45 # 32 bit mask for IPv4
47 # 128 bit mask for IPv6
48 IN6MASK = 0xffffffffffffffffffffffffffffffff
49 # Format string for IPv6
50 IN6FORMAT = (["%.4x"] * 8).join(':').freeze
52 # Regexp _internally_ used for parsing IPv4 address.
55 \d+ \. \d+ \. \d+ \. \d+
59 # Regexp _internally_ used for parsing IPv6 address.
60 RE_IPV6ADDRLIKE_FULL = %r{
63 (?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
65 ( (?: [\da-f]{1,4} : ){6} )
66 (\d+) \. (\d+) \. (\d+) \. (\d+)
71 # Regexp _internally_ used for parsing IPv6 address.
72 RE_IPV6ADDRLIKE_COMPRESSED = %r{
74 ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
77 ( (?: [\da-f]{1,4} : )* )
81 (\d+) \. (\d+) \. (\d+) \. (\d+)
87 # Generic IPAddr related error. Exceptions raised in this class should
89 class Error < ArgumentError; end
91 # Raised when the provided IP address is an invalid address.
92 class InvalidAddressError < Error; end
94 # Raised when the address family is invalid such as an address with an
95 # unsupported family, an address with an inconsistent family, or an address
96 # who's family cannot be determined.
97 class AddressFamilyError < Error; end
99 # Raised when the address is an invalid length.
100 class InvalidPrefixError < InvalidAddressError; end
102 # Returns the address family of this IP address.
105 # Creates a new ipaddr containing the given network byte ordered
106 # string form of an IP address.
107 def self.new_ntoh(addr)
108 return new(ntop(addr))
111 # Convert a network byte ordered string form of an IP address into
112 # human readable form.
113 # It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY).
115 if addr.is_a?(String) && addr.encoding != Encoding::BINARY
116 raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)"
121 addr.unpack('C4').join('.')
123 IN6FORMAT % addr.unpack('n8')
125 raise AddressFamilyError, "unsupported address family"
129 # Returns a new ipaddr built by bitwise AND.
131 return self.clone.set(@addr & coerce_other(other).to_i)
134 # Returns a new ipaddr built by bitwise OR.
136 return self.clone.set(@addr | coerce_other(other).to_i)
139 # Returns a new ipaddr built by bitwise right-shift.
141 return self.clone.set(@addr >> num)
144 # Returns a new ipaddr built by bitwise left shift.
146 return self.clone.set(addr_mask(@addr << num))
149 # Returns a new ipaddr built by bitwise negation.
151 return self.clone.set(addr_mask(~@addr))
154 # Returns true if two ipaddrs are equal.
156 other = coerce_other(other)
160 @family == other.family && @addr == other.to_i
163 # Returns a new ipaddr built by masking IP address with the given
164 # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
166 return self.clone.mask!(prefixlen)
169 # Returns true if the given ipaddr is in the range.
173 # net1 = IPAddr.new("192.168.2.0/24")
174 # net2 = IPAddr.new("192.168.2.100")
175 # net3 = IPAddr.new("192.168.3.0")
176 # net4 = IPAddr.new("192.168.2.0/16")
177 # p net1.include?(net2) #=> true
178 # p net1.include?(net3) #=> false
179 # p net1.include?(net4) #=> false
180 # p net4.include?(net1) #=> true
182 other = coerce_other(other)
183 return false unless other.family == family
184 begin_addr <= other.begin_addr && end_addr >= other.end_addr
188 # Returns the integer representation of the ipaddr.
193 # Returns a string containing the IP address representation.
198 str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
200 break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
201 break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
202 break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
203 break if str.sub!(/\b0:0:0:0:0\b/, ':')
204 break if str.sub!(/\b0:0:0:0\b/, ':')
205 break if str.sub!(/\b0:0:0\b/, ':')
206 break if str.sub!(/\b0:0\b/, ':')
209 str.sub!(/:{3,}/, '::')
211 if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
212 str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
218 # Returns a string containing the IP address representation in
221 str = _to_string(@addr)
223 if @family == Socket::AF_INET6
230 # Returns a string containing the IP address representation in
233 format("%s/%s", to_s, prefix)
236 # Returns a network byte ordered string form of the IP address.
240 return [@addr].pack('N')
241 when Socket::AF_INET6
242 return (0..7).map { |i|
243 (@addr >> (112 - 16 * i)) & 0xffff
246 raise AddressFamilyError, "unsupported address family"
250 # Returns true if the ipaddr is an IPv4 address.
252 return @family == Socket::AF_INET
255 # Returns true if the ipaddr is an IPv6 address.
257 return @family == Socket::AF_INET6
260 # Returns true if the ipaddr is a loopback address.
261 # Loopback IPv4 addresses in the IPv4-mapped IPv6
262 # address range are also considered as loopback addresses.
266 @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8
267 when Socket::AF_INET6
269 (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
270 @addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8
273 raise AddressFamilyError, "unsupported address family"
277 # Returns true if the ipaddr is a private address. IPv4 addresses
278 # in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC
279 # 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC
280 # 4193 are considered private. Private IPv4 addresses in the
281 # IPv4-mapped IPv6 address range are also considered private.
285 @addr & 0xff000000 == 0x0a000000 || # 10.0.0.0/8
286 @addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12
287 @addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16
288 when Socket::AF_INET6
289 @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000 ||
290 (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
291 @addr & 0xff000000 == 0x0a000000 || # ::ffff:10.0.0.0/8
292 @addr & 0xfff00000 == 0xac100000 || # ::ffff::172.16.0.0/12
293 @addr & 0xffff0000 == 0xc0a80000 # ::ffff::192.168.0.0/16
296 raise AddressFamilyError, "unsupported address family"
300 # Returns true if the ipaddr is a link-local address. IPv4
301 # addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local
302 # IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
303 # considered link-local. Link-local IPv4 addresses in the
304 # IPv4-mapped IPv6 address range are also considered link-local.
308 @addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
309 when Socket::AF_INET6
310 @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10
311 (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
312 @addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16
315 raise AddressFamilyError, "unsupported address family"
319 # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
321 return ipv6? && (@addr >> 32) == 0xffff
324 # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
326 warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
331 if !ipv6? || (@addr >> 32) != 0
334 a = (@addr & IN4MASK)
335 return a != 0 && a != 1
338 private :_ipv4_compat?
340 # Returns a new ipaddr built by converting the native IPv4 address
341 # into an IPv4-mapped IPv6 address.
344 raise InvalidAddressError, "not an IPv4 address: #{@addr}"
346 clone = self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
347 clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000)
351 # Returns a new ipaddr built by converting the native IPv4 address
352 # into an IPv4-compatible IPv6 address.
354 warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
356 raise InvalidAddressError, "not an IPv4 address: #{@addr}"
358 return self.clone.set(@addr, Socket::AF_INET6)
361 # Returns a new ipaddr built by converting the IPv6 address into a
362 # native IPv4 address. If the IP address is not an IPv4-mapped or
363 # IPv4-compatible IPv6 address, returns self.
365 if !ipv4_mapped? && !_ipv4_compat?
368 return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
371 # Returns a string for DNS reverse lookup. It returns a string in
372 # RFC3172 form for an IPv6 address.
376 return _reverse + ".in-addr.arpa"
377 when Socket::AF_INET6
380 raise AddressFamilyError, "unsupported address family"
384 # Returns a string for DNS reverse lookup compatible with RFC3172.
387 raise InvalidAddressError, "not an IPv6 address: #{@addr}"
389 return _reverse + ".ip6.arpa"
392 # Returns a string for DNS reverse lookup compatible with RFC1886.
395 raise InvalidAddressError, "not an IPv6 address: #{@addr}"
397 return _reverse + ".ip6.int"
400 # Returns the successor to the ipaddr.
402 return self.clone.set(@addr + 1, @family)
405 # Compares the ipaddr with another.
407 other = coerce_other(other)
411 @addr <=> other.to_i if other.family == @family
415 # Checks equality used by Hash.
417 return self.class == other.class && self.hash == other.hash && self == other
420 # Returns a hash value used by Hash, Set, and Array classes
422 return ([@addr, @mask_addr, @zone_id].hash << 1) | (ipv4? ? 0 : 1)
425 # Creates a Range object for the network address.
427 self.class.new(begin_addr, @family)..self.class.new(end_addr, @family)
430 # Returns the prefix length in bits for the ipaddr.
434 n = IN4MASK ^ @mask_addr
436 when Socket::AF_INET6
437 n = IN6MASK ^ @mask_addr
440 raise AddressFamilyError, "unsupported address family"
449 # Sets the prefix length in bits
455 raise InvalidPrefixError, "prefix must be an integer"
459 # Returns a string containing a human-readable representation of the
460 # ipaddr. ("#<IPAddr: family:address/mask>")
465 when Socket::AF_INET6
467 zone_id = @zone_id.to_s
469 raise AddressFamilyError, "unsupported address family"
471 return sprintf("#<%s: %s:%s%s/%s>", self.class.name,
472 af, _to_string(@addr), zone_id, _to_string(@mask_addr))
475 # Returns the netmask in string format e.g. 255.255.0.0
477 _to_string(@mask_addr)
480 # Returns the wildcard mask in string format e.g. 0.0.255.255
484 mask = IN4MASK ^ @mask_addr
485 when Socket::AF_INET6
486 mask = IN6MASK ^ @mask_addr
488 raise AddressFamilyError, "unsupported address family"
494 # Returns the IPv6 zone identifier, if present.
495 # Raises InvalidAddressError if not an IPv6 address.
497 if @family == Socket::AF_INET6
500 raise InvalidAddressError, "not an IPv6 address"
504 # Returns the IPv6 zone identifier, if present.
505 # Raises InvalidAddressError if not an IPv6 address.
507 if @family == Socket::AF_INET6
509 when nil, /\A%(\w+)\z/
512 raise InvalidAddressError, "invalid zone identifier for address"
515 raise InvalidAddressError, "not an IPv6 address"
528 @addr | (IN4MASK ^ @mask_addr)
529 when Socket::AF_INET6
530 @addr | (IN6MASK ^ @mask_addr)
532 raise AddressFamilyError, "unsupported address family"
536 # Set +@addr+, the internal stored ip address, to given +addr+. The
537 # parameter +addr+ is validated using the first +family+ member,
538 # which is +Socket::AF_INET+ or +Socket::AF_INET6+.
539 def set(addr, *family)
540 case family[0] ? family[0] : @family
542 if addr < 0 || addr > IN4MASK
543 raise InvalidAddressError, "invalid address: #{addr}"
545 when Socket::AF_INET6
546 if addr < 0 || addr > IN6MASK
547 raise InvalidAddressError, "invalid address: #{addr}"
550 raise AddressFamilyError, "unsupported address family"
555 if @family == Socket::AF_INET
556 @mask_addr &= IN4MASK
562 # Set current netmask to given mask.
567 when /\A(0|[1-9]+\d*)\z/
568 prefixlen = mask.to_i
570 raise InvalidPrefixError, "leading zeros in prefix"
573 if m.family != @family
574 raise InvalidPrefixError, "address family is not same"
577 n = @mask_addr ^ m.instance_variable_get(:@mask_addr)
578 unless ((n + 1) & n).zero?
579 raise InvalidPrefixError, "invalid mask #{mask}"
589 if prefixlen < 0 || prefixlen > 32
590 raise InvalidPrefixError, "invalid length"
592 masklen = 32 - prefixlen
593 @mask_addr = ((IN4MASK >> masklen) << masklen)
594 when Socket::AF_INET6
595 if prefixlen < 0 || prefixlen > 128
596 raise InvalidPrefixError, "invalid length"
598 masklen = 128 - prefixlen
599 @mask_addr = ((IN6MASK >> masklen) << masklen)
601 raise AddressFamilyError, "unsupported address family"
603 @addr = ((@addr >> masklen) << masklen)
609 # Creates a new ipaddr object either from a human readable IP
610 # address representation in string, or from a packed in_addr value
611 # followed by an address family.
613 # In the former case, the following are the valid formats that will
614 # be recognized: "address", "address/prefixlen" and "address/mask",
615 # where IPv6 address may be enclosed in square brackets (`[' and
616 # `]'). If a prefixlen or a mask is specified, it returns a masked
617 # IP address. Although the address family is determined
618 # automatically from a specified string, you can specify one
619 # explicitly by the optional second argument.
621 # Otherwise an IP address is generated from a packed in_addr value
622 # and an address family.
624 # The IPAddr class defines many methods and operators, and some of
625 # those, such as &, |, include? and ==, accept a string, or a packed
626 # in_addr value instead of an IPAddr object.
627 def initialize(addr = '::', family = Socket::AF_UNSPEC)
629 if !addr.kind_of?(String)
631 when Socket::AF_INET, Socket::AF_INET6
632 set(addr.to_i, family)
633 @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
635 when Socket::AF_UNSPEC
636 raise AddressFamilyError, "address family must be specified"
638 raise AddressFamilyError, "unsupported address family: #{family}"
641 prefix, prefixlen = addr.split('/', 2)
642 if prefix =~ /\A\[(.*)\]\z/i
644 family = Socket::AF_INET6
646 if prefix =~ /\A(.*)(%\w+)\z/
649 family = Socket::AF_INET6
651 # It seems AI_NUMERICHOST doesn't do the job.
652 #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
653 # Socket::AI_NUMERICHOST)
654 @addr = @family = nil
655 if family == Socket::AF_UNSPEC || family == Socket::AF_INET
656 @addr = in_addr(prefix)
658 @family = Socket::AF_INET
661 if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
662 @addr = in6_addr(prefix)
663 @family = Socket::AF_INET6
666 if family != Socket::AF_UNSPEC && @family != family
667 raise AddressFamilyError, "address family mismatch"
672 @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
676 def coerce_other(other)
681 self.class.new(other)
683 self.class.new(other, @family)
692 RE_IPV4ADDRLIKE.match?(addr) or return nil
693 octets = addr.split('.')
695 octets.inject(0) { |i, s|
696 (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}"
697 (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
704 when RE_IPV6ADDRLIKE_FULL
706 addr = in_addr($~[2,4])
712 when RE_IPV6ADDRLIKE_COMPRESSED
714 left.count(':') <= 6 or raise InvalidAddressError, "invalid address: #{@addr}"
715 addr = in_addr($~[4,4])
719 left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
720 raise InvalidAddressError, "invalid address: #{@addr}"
726 raise InvalidAddressError, "invalid address: #{@addr}"
730 rest = 8 - l.size - r.size
734 (l + Array.new(rest, '0') + r).inject(0) { |i, s|
742 return addr & IN4MASK
743 when Socket::AF_INET6
744 return addr & IN6MASK
746 raise AddressFamilyError, "unsupported address family"
753 return (0..3).map { |i|
754 (@addr >> (8 * i)) & 0xff
756 when Socket::AF_INET6
757 return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
759 raise AddressFamilyError, "unsupported address family"
766 return (0..3).map { |i|
767 (addr >> (24 - 8 * i)) & 0xff
769 when Socket::AF_INET6
770 return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
772 raise AddressFamilyError, "unsupported address family"
778 unless Socket.const_defined? :AF_INET6
779 class Socket < BasicSocket
780 # IPv6 protocol family
781 AF_INET6 = Object.new.freeze
789 when IPAddr::RE_IPV6ADDRLIKE_FULL
791 $~[2,4].all? {|i| i.to_i < 256 }
795 when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
797 addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
806 alias getaddress_orig getaddress
810 # Returns a +String+ based representation of a valid DNS hostname,
811 # IPv4 or IPv6 address.
813 # IPSocket.getaddress 'localhost' #=> "::1"
814 # IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255"
815 # IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
816 # IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122"