2 # ipaddr.rb - A class to manipulate an IP address
4 # Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
7 # You can redistribute and/or modify it under the same terms as Ruby.
9 # $Id: ipaddr.rb 11708 2007-02-12 23:01:19Z shyouhei $
15 unless Socket.const_defined? "AF_INET6"
22 if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
23 return $~.captures.all? {|i| i.to_i < 256}
30 return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
31 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
32 return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
34 return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
35 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?($')
36 return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
42 valid_v4?(addr) || valid_v6?(addr)
45 alias getaddress_orig getaddress
49 elsif /\A[-A-Za-z\d.]+\Z/ =~ s
52 raise ArgumentError, "invalid address"
58 # IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
65 # ipaddr1 = IPAddr.new "3ffe:505:2::1"
67 # p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
69 # p ipaddr1.to_s #=> "3ffe:505:2::1"
71 # ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
73 # p ipaddr2.to_s #=> "3ffe:505:2::"
75 # ipaddr3 = IPAddr.new "192.168.2.0/24"
77 # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
82 IN6MASK = 0xffffffffffffffffffffffffffffffff
83 IN6FORMAT = (["%.4x"] * 8).join(':')
85 # Returns the address family of this IP address.
88 # Creates a new ipaddr containing the given network byte ordered
89 # string form of an IP address.
90 def IPAddr::new_ntoh(addr)
91 return IPAddr.new(IPAddr::ntop(addr))
94 # Convert a network byte ordered string form of an IP address into
95 # human readable form.
96 def IPAddr::ntop(addr)
99 s = addr.unpack('C4').join('.')
101 s = IN6FORMAT % addr.unpack('n8')
103 raise ArgumentError, "unsupported address family"
108 # Returns a new ipaddr built by bitwise AND.
110 return self.clone.set(@addr & other.to_i)
113 # Returns a new ipaddr built by bitwise OR.
115 return self.clone.set(@addr | other.to_i)
118 # Returns a new ipaddr built by bitwise right-shift.
120 return self.clone.set(@addr >> num)
123 # Returns a new ipaddr built by bitwise left shift.
125 return self.clone.set(addr_mask(@addr << num))
128 # Returns a new ipaddr built by bitwise negation.
130 return self.clone.set(addr_mask(~@addr))
133 # Returns true if two ipaddr are equal.
135 if other.kind_of?(IPAddr) && @family != other.family
138 return (@addr == other.to_i)
141 # Returns a new ipaddr built by masking IP address with the given
142 # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
144 return self.clone.mask!(prefixlen)
147 # Returns true if the given ipaddr is in the range.
151 # net1 = IPAddr.new("192.168.2.0/24")
152 # p net1.include?(IPAddr.new("192.168.2.0")) #=> true
153 # p net1.include?(IPAddr.new("192.168.2.255")) #=> true
154 # p net1.include?(IPAddr.new("192.168.3.0")) #=> false
157 if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
160 mask_addr = (@mask_addr & IN4MASK)
161 addr = (@addr & IN4MASK)
162 family = Socket::AF_INET
164 mask_addr = @mask_addr
168 if other.kind_of?(IPAddr)
169 if other.ipv4_mapped?
170 other_addr = (other.to_i & IN4MASK)
171 other_family = Socket::AF_INET
173 other_addr = other.to_i
174 other_family = other.family
176 else # Not IPAddr - assume integer in same family as us
177 other_addr = other.to_i
178 other_family = 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 a string containing a human-readable representation of the
320 # ipaddr. ("#<IPAddr: family:address/mask>")
325 when Socket::AF_INET6
328 raise "unsupported address family"
330 return sprintf("#<%s: %s:%s/%s>", self.class.name,
331 af, _to_string(@addr), _to_string(@mask_addr))
336 def set(addr, *family)
337 case family[0] ? family[0] : @family
339 if addr < 0 || addr > IN4MASK
340 raise ArgumentError, "invalid address"
342 when Socket::AF_INET6
343 if addr < 0 || addr > IN6MASK
344 raise ArgumentError, "invalid address"
347 raise ArgumentError, "unsupported address family"
357 if mask.kind_of?(String)
359 prefixlen = mask.to_i
362 if m.family != @family
363 raise ArgumentError, "address family is not same"
374 if prefixlen < 0 || prefixlen > 32
375 raise ArgumentError, "invalid length"
377 masklen = 32 - prefixlen
378 @mask_addr = ((IN4MASK >> masklen) << masklen)
379 when Socket::AF_INET6
380 if prefixlen < 0 || prefixlen > 128
381 raise ArgumentError, "invalid length"
383 masklen = 128 - prefixlen
384 @mask_addr = ((IN6MASK >> masklen) << masklen)
386 raise "unsupported address family"
388 @addr = ((@addr >> masklen) << masklen)
394 # Creates a new ipaddr containing the given human readable form of
395 # an IP address. It also accepts `address/prefixlen' and
396 # `address/mask'. When prefixlen or mask is specified, it returns a
397 # masked ipaddr. IPv6 address may beenclosed with `[' and `]'.
399 # Although an address family is determined automatically from a
400 # specified address, you can specify an address family explicitly by
401 # the optional second argument.
402 def initialize(addr = '::', family = Socket::AF_UNSPEC)
403 if !addr.kind_of?(String)
404 if family != Socket::AF_INET6 && family != Socket::AF_INET
405 raise ArgumentError, "unsupported address family"
408 @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
411 prefix, prefixlen = addr.split('/')
412 if prefix =~ /^\[(.*)\]$/i
414 family = Socket::AF_INET6
416 # It seems AI_NUMERICHOST doesn't do the job.
417 #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
418 # Socket::AI_NUMERICHOST)
420 IPSocket.getaddress(prefix) # test if address is vaild
422 raise ArgumentError, "invalid address"
424 @addr = @family = nil
425 if family == Socket::AF_UNSPEC || family == Socket::AF_INET
426 @addr = in_addr(prefix)
428 @family = Socket::AF_INET
431 if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
432 @addr = in6_addr(prefix)
433 @family = Socket::AF_INET6
435 if family != Socket::AF_UNSPEC && @family != family
436 raise ArgumentError, "address family unmatch"
441 @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
446 if addr =~ /^\d+\.\d+\.\d+\.\d+$/
448 addr.split('.').each { |i|
459 when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
460 return in_addr($1) + 0xffff00000000
461 when /^::(\d+\.\d+\.\d+\.\d+)$/i
464 raise ArgumentError, "invalid address"
472 rest = 8 - l.size - r.size
476 a = [l, Array.new(rest, '0'), r].flatten!
489 when Socket::AF_INET6
492 raise "unsupported address family"
500 return (0..3).map { |i|
501 (@addr >> (8 * i)) & 0xff
503 when Socket::AF_INET6
504 return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
506 raise "unsupported address family"
513 return (0..3).map { |i|
514 (addr >> (24 - 8 * i)) & 0xff
516 when Socket::AF_INET6
517 return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
519 raise "unsupported address family"
526 eval DATA.read, nil, $0, __LINE__+4
532 require 'test/unit/ui/console/testrunner'
534 class TC_IPAddr < Test::Unit::TestCase
536 assert_nothing_raised {
537 IPAddr.new("3FFE:505:ffff::/48")
538 IPAddr.new("0:0:0:1::")
539 IPAddr.new("2001:200:300::/48")
543 assert_equal("::", a.to_s)
544 assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
545 assert_equal(Socket::AF_INET6, a.family)
547 a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
548 assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
549 assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
550 assert_equal(Socket::AF_INET6, a.family)
552 a = IPAddr.new("3ffe:505:2::/48")
553 assert_equal("3ffe:505:2::", a.to_s)
554 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
555 assert_equal(Socket::AF_INET6, a.family)
556 assert_equal(false, a.ipv4?)
557 assert_equal(true, a.ipv6?)
558 assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
560 a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
561 assert_equal("3ffe:505:2::", a.to_s)
562 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
563 assert_equal(Socket::AF_INET6, a.family)
565 a = IPAddr.new("0.0.0.0")
566 assert_equal("0.0.0.0", a.to_s)
567 assert_equal("0.0.0.0", a.to_string)
568 assert_equal(Socket::AF_INET, a.family)
570 a = IPAddr.new("192.168.1.2")
571 assert_equal("192.168.1.2", a.to_s)
572 assert_equal("192.168.1.2", a.to_string)
573 assert_equal(Socket::AF_INET, a.family)
574 assert_equal(true, a.ipv4?)
575 assert_equal(false, a.ipv6?)
577 a = IPAddr.new("192.168.1.2/24")
578 assert_equal("192.168.1.0", a.to_s)
579 assert_equal("192.168.1.0", a.to_string)
580 assert_equal(Socket::AF_INET, a.family)
581 assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
583 a = IPAddr.new("192.168.1.2/255.255.255.0")
584 assert_equal("192.168.1.0", a.to_s)
585 assert_equal("192.168.1.0", a.to_string)
586 assert_equal(Socket::AF_INET, a.family)
588 assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
589 assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
591 assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
595 ["::1/255.255.255.0"],
596 ["::1:192.168.1.2/120"],
597 [IPAddr.new("::1").to_i],
598 ["::ffff:192.168.1.2/120", Socket::AF_INET],
599 ["[192.168.1.2]/120"],
601 assert_raises(ArgumentError) {
609 IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
610 addr += sprintf("%02x", c)
612 assert_equal("123456789abcdef0123456789abcdef0", addr)
614 IPAddr.new("123.45.67.89").hton.each_byte { |c|
615 addr += sprintf("%02x", c)
617 assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
618 a = IPAddr.new("3ffe:505:2::")
619 assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
620 a = IPAddr.new("192.168.2.1")
621 assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
625 a = IPAddr.new("::192.168.1.2")
626 assert_equal("::192.168.1.2", a.to_s)
627 assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
628 assert_equal(Socket::AF_INET6, a.family)
629 assert_equal(true, a.ipv4_compat?)
631 assert_equal("192.168.1.2", b.to_s)
632 assert_equal(Socket::AF_INET, b.family)
633 assert_equal(false, b.ipv4_compat?)
635 a = IPAddr.new("192.168.1.2")
637 assert_equal("::192.168.1.2", b.to_s)
638 assert_equal(Socket::AF_INET6, b.family)
642 a = IPAddr.new("::ffff:192.168.1.2")
643 assert_equal("::ffff:192.168.1.2", a.to_s)
644 assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
645 assert_equal(Socket::AF_INET6, a.family)
646 assert_equal(true, a.ipv4_mapped?)
648 assert_equal("192.168.1.2", b.to_s)
649 assert_equal(Socket::AF_INET, b.family)
650 assert_equal(false, b.ipv4_mapped?)
652 a = IPAddr.new("192.168.1.2")
654 assert_equal("::ffff:192.168.1.2", b.to_s)
655 assert_equal(Socket::AF_INET6, b.family)
659 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)
660 assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
664 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)
665 assert_raises(ArgumentError) {
666 IPAddr.new("192.168.2.1").ip6_arpa
671 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)
672 assert_raises(ArgumentError) {
673 IPAddr.new("192.168.2.1").ip6_int
678 assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
679 assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
683 class TC_Operator < Test::Unit::TestCase
685 IN6MASK32 = "ffff:ffff::"
686 IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
689 @in6_addr_any = IPAddr.new()
690 @a = IPAddr.new("3ffe:505:2::/48")
691 @b = IPAddr.new("0:0:0:1::")
692 @c = IPAddr.new(IN6MASK32)
697 assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
700 assert_equal("3ffe:505:2:1::", a.to_s)
701 assert_equal("3ffe:505:2::", @a.to_s)
702 assert_equal("3ffe:505:2:1::",
703 (@a | 0x00000000000000010000000000000000).to_s)
707 assert_equal("3ffe:505::", (@a & @c).to_s)
710 assert_equal("3ffe:505::", a.to_s)
711 assert_equal("3ffe:505:2::", @a.to_s)
712 assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
716 assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
719 assert_equal("0:3ffe:505:2::", a.to_s)
720 assert_equal("3ffe:505:2::", @a.to_s)
724 assert_equal("505:2::", (@a << 16).to_s)
727 assert_equal("505:2::", a.to_s)
728 assert_equal("3ffe:505:2::", @a.to_s)
733 assert_equal(IN6MASK128, a.to_s)
734 assert_equal("::", @in6_addr_any.to_s)
738 assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
739 assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
740 assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
741 assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
746 assert_equal("3ffe:505::", a.to_s)
747 assert_equal("3ffe:505:2::", @a.to_s)
751 assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
752 assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
753 assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
754 net1 = IPAddr.new("192.168.2.0/24")
755 assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
756 assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
757 assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
758 # test with integer parameter
759 int = (192 << 24) + (168 << 16) + (2 << 8) + 13
761 assert_equal(true, net1.include?(int))
762 assert_equal(false, net1.include?(int+255))