2 # ipaddr.rb - A class to manipulate an IP address
4 # Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
5 # Copyright (c) 2007 Akinori MUSHA <knu@iDaemons.org>.
8 # You can redistribute and/or modify it under the same terms as Ruby.
13 # - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
20 unless Socket.const_defined? "AF_INET6"
27 if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
28 return $~.captures.all? {|i| i.to_i < 256}
35 return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
36 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
37 return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
39 return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
40 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?($')
41 return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
47 valid_v4?(addr) || valid_v6?(addr)
50 alias getaddress_orig getaddress
54 elsif /\A[-A-Za-z\d.]+\Z/ =~ s
57 raise ArgumentError, "invalid address"
63 # IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
70 # ipaddr1 = IPAddr.new "3ffe:505:2::1"
72 # p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
74 # p ipaddr1.to_s #=> "3ffe:505:2::1"
76 # ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
78 # p ipaddr2.to_s #=> "3ffe:505:2::"
80 # ipaddr3 = IPAddr.new "192.168.2.0/24"
82 # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
87 IN6MASK = 0xffffffffffffffffffffffffffffffff
88 IN6FORMAT = (["%.4x"] * 8).join(':')
90 # Returns the address family of this IP address.
93 # Creates a new ipaddr containing the given network byte ordered
94 # string form of an IP address.
95 def IPAddr::new_ntoh(addr)
96 return IPAddr.new(IPAddr::ntop(addr))
99 # Convert a network byte ordered string form of an IP address into
100 # human readable form.
101 def IPAddr::ntop(addr)
104 s = addr.unpack('C4').join('.')
106 s = IN6FORMAT % addr.unpack('n8')
108 raise ArgumentError, "unsupported address family"
113 # Returns a new ipaddr built by bitwise AND.
115 return self.clone.set(@addr & coerce_other(other).to_i)
118 # Returns a new ipaddr built by bitwise OR.
120 return self.clone.set(@addr | coerce_other(other).to_i)
123 # Returns a new ipaddr built by bitwise right-shift.
125 return self.clone.set(@addr >> num)
128 # Returns a new ipaddr built by bitwise left shift.
130 return self.clone.set(addr_mask(@addr << num))
133 # Returns a new ipaddr built by bitwise negation.
135 return self.clone.set(addr_mask(~@addr))
138 # Returns true if two ipaddrs are equal.
140 other = coerce_other(other)
141 return @family == other.family && @addr == other.to_i
144 # Returns a new ipaddr built by masking IP address with the given
145 # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
147 return self.clone.mask!(prefixlen)
150 # Returns true if the given ipaddr is in the range.
154 # net1 = IPAddr.new("192.168.2.0/24")
155 # net2 = IPAddr.new("192.168.2.100")
156 # net3 = IPAddr.new("192.168.3.0")
157 # p net1.include?(net2) #=> true
158 # p net1.include?(net3) #=> false
160 other = coerce_other(other)
162 if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
165 mask_addr = (@mask_addr & IN4MASK)
166 addr = (@addr & IN4MASK)
167 family = Socket::AF_INET
169 mask_addr = @mask_addr
173 if other.ipv4_mapped?
174 other_addr = (other.to_i & IN4MASK)
175 other_family = Socket::AF_INET
177 other_addr = other.to_i
178 other_family = other.family
181 if family != other_family
184 return ((addr & mask_addr) == (other_addr & mask_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 return _to_string(@addr)
224 # Returns a network byte ordered string form of the IP address.
228 return [@addr].pack('N')
229 when Socket::AF_INET6
230 return (0..7).map { |i|
231 (@addr >> (112 - 16 * i)) & 0xffff
234 raise "unsupported address family"
238 # Returns true if the ipaddr is an IPv4 address.
240 return @family == Socket::AF_INET
243 # Returns true if the ipaddr is an IPv6 address.
245 return @family == Socket::AF_INET6
248 # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
250 return ipv6? && (@addr >> 32) == 0xffff
253 # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
255 if !ipv6? || (@addr >> 32) != 0
258 a = (@addr & IN4MASK)
259 return a != 0 && a != 1
262 # Returns a new ipaddr built by converting the native IPv4 address
263 # into an IPv4-mapped IPv6 address.
266 raise ArgumentError, "not an IPv4 address"
268 return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
271 # Returns a new ipaddr built by converting the native IPv4 address
272 # into an IPv4-compatible IPv6 address.
275 raise ArgumentError, "not an IPv4 address"
277 return self.clone.set(@addr, Socket::AF_INET6)
280 # Returns a new ipaddr built by converting the IPv6 address into a
281 # native IPv4 address. If the IP address is not an IPv4-mapped or
282 # IPv4-compatible IPv6 address, returns self.
284 if !ipv4_mapped? && !ipv4_compat?
287 return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
290 # Returns a string for DNS reverse lookup. It returns a string in
291 # RFC3172 form for an IPv6 address.
295 return _reverse + ".in-addr.arpa"
296 when Socket::AF_INET6
299 raise "unsupported address family"
303 # Returns a string for DNS reverse lookup compatible with RFC3172.
306 raise ArgumentError, "not an IPv6 address"
308 return _reverse + ".ip6.arpa"
311 # Returns a string for DNS reverse lookup compatible with RFC1886.
314 raise ArgumentError, "not an IPv6 address"
316 return _reverse + ".ip6.int"
319 # Returns the successor to the ipaddr.
321 return self.clone.set(@addr + 1, @family)
324 # Compares the ipaddr with another.
326 other = coerce_other(other)
328 return nil if other.family != @family
330 return @addr <=> other.to_i
334 # Creates a Range object for the network address.
336 begin_addr = (@addr & @mask_addr)
340 end_addr = (@addr | (IN4MASK ^ @mask_addr))
341 when Socket::AF_INET6
342 end_addr = (@addr | (IN6MASK ^ @mask_addr))
344 raise "unsupported address family"
347 return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
350 # Returns a string containing a human-readable representation of the
351 # ipaddr. ("#<IPAddr: family:address/mask>")
356 when Socket::AF_INET6
359 raise "unsupported address family"
361 return sprintf("#<%s: %s:%s/%s>", self.class.name,
362 af, _to_string(@addr), _to_string(@mask_addr))
367 def set(addr, *family)
368 case family[0] ? family[0] : @family
370 if addr < 0 || addr > IN4MASK
371 raise ArgumentError, "invalid address"
373 when Socket::AF_INET6
374 if addr < 0 || addr > IN6MASK
375 raise ArgumentError, "invalid address"
378 raise ArgumentError, "unsupported address family"
388 if mask.kind_of?(String)
390 prefixlen = mask.to_i
393 if m.family != @family
394 raise ArgumentError, "address family is not same"
405 if prefixlen < 0 || prefixlen > 32
406 raise ArgumentError, "invalid length"
408 masklen = 32 - prefixlen
409 @mask_addr = ((IN4MASK >> masklen) << masklen)
410 when Socket::AF_INET6
411 if prefixlen < 0 || prefixlen > 128
412 raise ArgumentError, "invalid length"
414 masklen = 128 - prefixlen
415 @mask_addr = ((IN6MASK >> masklen) << masklen)
417 raise "unsupported address family"
419 @addr = ((@addr >> masklen) << masklen)
425 # Creates a new ipaddr object either from a human readable IP
426 # address representation in string, or from a packed in_addr value
427 # followed by an address family.
429 # In the former case, the following are the valid formats that will
430 # be recognized: "address", "address/prefixlen" and "address/mask",
431 # where IPv6 address may be enclosed in square brackets (`[' and
432 # `]'). If a prefixlen or a mask is specified, it returns a masked
433 # IP address. Although the address family is determined
434 # automatically from a specified string, you can specify one
435 # explicitly by the optional second argument.
437 # Otherwise an IP addess is generated from a packed in_addr value
438 # and an address family.
440 # The IPAddr class defines many methods and operators, and some of
441 # those, such as &, |, include? and ==, accept a string, or a packed
442 # in_addr value instead of an IPAddr object.
443 def initialize(addr = '::', family = Socket::AF_UNSPEC)
444 if !addr.kind_of?(String)
446 when Socket::AF_INET, Socket::AF_INET6
447 set(addr.to_i, family)
448 @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
450 when Socket::AF_UNSPEC
451 raise ArgumentError, "address family must be specified"
453 raise ArgumentError, "unsupported address family: #{family}"
456 prefix, prefixlen = addr.split('/')
457 if prefix =~ /^\[(.*)\]$/i
459 family = Socket::AF_INET6
461 # It seems AI_NUMERICHOST doesn't do the job.
462 #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
463 # Socket::AI_NUMERICHOST)
465 IPSocket.getaddress(prefix) # test if address is vaild
467 raise ArgumentError, "invalid address"
469 @addr = @family = nil
470 if family == Socket::AF_UNSPEC || family == Socket::AF_INET
471 @addr = in_addr(prefix)
473 @family = Socket::AF_INET
476 if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
477 @addr = in6_addr(prefix)
478 @family = Socket::AF_INET6
480 if family != Socket::AF_UNSPEC && @family != family
481 raise ArgumentError, "address family mismatch"
486 @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
490 def coerce_other(other)
495 self.class.new(other)
497 self.class.new(other, @family)
502 if addr =~ /^\d+\.\d+\.\d+\.\d+$/
503 return addr.split('.').inject(0) { |i, s|
512 when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
513 return in_addr($1) + 0xffff00000000
514 when /^::(\d+\.\d+\.\d+\.\d+)$/i
517 raise ArgumentError, "invalid address"
525 rest = 8 - l.size - r.size
529 return (l + Array.new(rest, '0') + r).inject(0) { |i, s|
537 return addr & IN4MASK
538 when Socket::AF_INET6
539 return addr & IN6MASK
541 raise "unsupported address family"
548 return (0..3).map { |i|
549 (@addr >> (8 * i)) & 0xff
551 when Socket::AF_INET6
552 return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
554 raise "unsupported address family"
561 return (0..3).map { |i|
562 (addr >> (24 - 8 * i)) & 0xff
564 when Socket::AF_INET6
565 return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
567 raise "unsupported address family"
574 eval DATA.read, nil, $0, __LINE__+4
580 require 'test/unit/ui/console/testrunner'
582 class TC_IPAddr < Test::Unit::TestCase
584 assert_nothing_raised {
585 IPAddr.new("3FFE:505:ffff::/48")
586 IPAddr.new("0:0:0:1::")
587 IPAddr.new("2001:200:300::/48")
591 assert_equal("::", a.to_s)
592 assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
593 assert_equal(Socket::AF_INET6, a.family)
595 a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
596 assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
597 assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
598 assert_equal(Socket::AF_INET6, a.family)
600 a = IPAddr.new("3ffe:505:2::/48")
601 assert_equal("3ffe:505:2::", a.to_s)
602 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
603 assert_equal(Socket::AF_INET6, a.family)
604 assert_equal(false, a.ipv4?)
605 assert_equal(true, a.ipv6?)
606 assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
608 a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
609 assert_equal("3ffe:505:2::", a.to_s)
610 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
611 assert_equal(Socket::AF_INET6, a.family)
613 a = IPAddr.new("0.0.0.0")
614 assert_equal("0.0.0.0", a.to_s)
615 assert_equal("0.0.0.0", a.to_string)
616 assert_equal(Socket::AF_INET, a.family)
618 a = IPAddr.new("192.168.1.2")
619 assert_equal("192.168.1.2", a.to_s)
620 assert_equal("192.168.1.2", a.to_string)
621 assert_equal(Socket::AF_INET, a.family)
622 assert_equal(true, a.ipv4?)
623 assert_equal(false, a.ipv6?)
625 a = IPAddr.new("192.168.1.2/24")
626 assert_equal("192.168.1.0", a.to_s)
627 assert_equal("192.168.1.0", a.to_string)
628 assert_equal(Socket::AF_INET, a.family)
629 assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
631 a = IPAddr.new("192.168.1.2/255.255.255.0")
632 assert_equal("192.168.1.0", a.to_s)
633 assert_equal("192.168.1.0", a.to_string)
634 assert_equal(Socket::AF_INET, a.family)
636 assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
637 assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
639 assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
643 ["::1/255.255.255.0"],
644 ["::1:192.168.1.2/120"],
645 [IPAddr.new("::1").to_i],
646 ["::ffff:192.168.1.2/120", Socket::AF_INET],
647 ["[192.168.1.2]/120"],
649 assert_raises(ArgumentError) {
657 IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
658 addr += sprintf("%02x", c)
660 assert_equal("123456789abcdef0123456789abcdef0", addr)
662 IPAddr.new("123.45.67.89").hton.each_byte { |c|
663 addr += sprintf("%02x", c)
665 assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
666 a = IPAddr.new("3ffe:505:2::")
667 assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
668 a = IPAddr.new("192.168.2.1")
669 assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
673 a = IPAddr.new("::192.168.1.2")
674 assert_equal("::192.168.1.2", a.to_s)
675 assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
676 assert_equal(Socket::AF_INET6, a.family)
677 assert_equal(true, a.ipv4_compat?)
679 assert_equal("192.168.1.2", b.to_s)
680 assert_equal(Socket::AF_INET, b.family)
681 assert_equal(false, b.ipv4_compat?)
683 a = IPAddr.new("192.168.1.2")
685 assert_equal("::192.168.1.2", b.to_s)
686 assert_equal(Socket::AF_INET6, b.family)
690 a = IPAddr.new("::ffff:192.168.1.2")
691 assert_equal("::ffff:192.168.1.2", a.to_s)
692 assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
693 assert_equal(Socket::AF_INET6, a.family)
694 assert_equal(true, a.ipv4_mapped?)
696 assert_equal("192.168.1.2", b.to_s)
697 assert_equal(Socket::AF_INET, b.family)
698 assert_equal(false, b.ipv4_mapped?)
700 a = IPAddr.new("192.168.1.2")
702 assert_equal("::ffff:192.168.1.2", b.to_s)
703 assert_equal(Socket::AF_INET6, b.family)
707 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)
708 assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
712 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)
713 assert_raises(ArgumentError) {
714 IPAddr.new("192.168.2.1").ip6_arpa
719 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)
720 assert_raises(ArgumentError) {
721 IPAddr.new("192.168.2.1").ip6_int
726 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
727 assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
731 class TC_Operator < Test::Unit::TestCase
733 IN6MASK32 = "ffff:ffff::"
734 IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
737 @in6_addr_any = IPAddr.new()
738 @a = IPAddr.new("3ffe:505:2::/48")
739 @b = IPAddr.new("0:0:0:1::")
740 @c = IPAddr.new(IN6MASK32)
745 assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
748 assert_equal("3ffe:505:2:1::", a.to_s)
749 assert_equal("3ffe:505:2::", @a.to_s)
750 assert_equal("3ffe:505:2:1::",
751 (@a | 0x00000000000000010000000000000000).to_s)
755 assert_equal("3ffe:505::", (@a & @c).to_s)
758 assert_equal("3ffe:505::", a.to_s)
759 assert_equal("3ffe:505:2::", @a.to_s)
760 assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
764 assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
767 assert_equal("0:3ffe:505:2::", a.to_s)
768 assert_equal("3ffe:505:2::", @a.to_s)
772 assert_equal("505:2::", (@a << 16).to_s)
775 assert_equal("505:2::", a.to_s)
776 assert_equal("3ffe:505:2::", @a.to_s)
781 assert_equal(IN6MASK128, a.to_s)
782 assert_equal("::", @in6_addr_any.to_s)
786 assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
787 assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
788 assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
789 assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
794 assert_equal("3ffe:505::", a.to_s)
795 assert_equal("3ffe:505:2::", @a.to_s)
799 assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
800 assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
801 assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
802 net1 = IPAddr.new("192.168.2.0/24")
803 assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
804 assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
805 assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
806 # test with integer parameter
807 int = (192 << 24) + (168 << 16) + (2 << 8) + 13
809 assert_equal(true, net1.include?(int))
810 assert_equal(false, net1.include?(int+255))