* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / ipaddr.rb
blob7c246326ca9e0cbad93c608d327cf5cc04039624
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>.
6 # All rights reserved.
8 # You can redistribute and/or modify it under the same terms as Ruby.
10 # $Id$
12 # Contact:
13 #   - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
15 # TODO:
16 #   - scope_id support
18 require 'socket'
20 unless Socket.const_defined? "AF_INET6"
21   class Socket
22     AF_INET6 = Object.new
23   end
25   class << IPSocket
26     def valid_v4?(addr)
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}
29       end
30       return false
31     end
33     def valid_v6?(addr)
34       # IPv6 (normal)
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
38       # IPv6 (IPv4 compat)
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?($')
43       false
44     end
46     def valid?(addr)
47       valid_v4?(addr) || valid_v6?(addr)
48     end
50     alias getaddress_orig getaddress
51     def getaddress(s)
52       if valid?(s)
53         s
54       elsif /\A[-A-Za-z\d.]+\Z/ =~ s
55         getaddress_orig(s)
56       else
57         raise ArgumentError, "invalid address"
58       end
59     end
60   end
61 end
63 # IPAddr provides a set of methods to manipulate an IP address.  Both IPv4 and
64 # IPv6 are supported.
66 # == Example
68 #   require 'ipaddr'
69 #   
70 #   ipaddr1 = IPAddr.new "3ffe:505:2::1"
71 #   
72 #   p ipaddr1                   #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
73 #   
74 #   p ipaddr1.to_s              #=> "3ffe:505:2::1"
75 #   
76 #   ipaddr2 = ipaddr1.mask(48)  #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
77 #   
78 #   p ipaddr2.to_s              #=> "3ffe:505:2::"
79 #   
80 #   ipaddr3 = IPAddr.new "192.168.2.0/24"
81 #   
82 #   p ipaddr3                   #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
84 class IPAddr
86   IN4MASK = 0xffffffff
87   IN6MASK = 0xffffffffffffffffffffffffffffffff
88   IN6FORMAT = (["%.4x"] * 8).join(':')
90   # Returns the address family of this IP address.
91   attr_reader :family
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))
97   end
99   # Convert a network byte ordered string form of an IP address into
100   # human readable form.
101   def IPAddr::ntop(addr)
102     case addr.size
103     when 4
104       s = addr.unpack('C4').join('.')
105     when 16
106       s = IN6FORMAT % addr.unpack('n8')
107     else
108       raise ArgumentError, "unsupported address family"
109     end
110     return s
111   end
113   # Returns a new ipaddr built by bitwise AND.
114   def &(other)
115     return self.clone.set(@addr & coerce_other(other).to_i)
116   end
118   # Returns a new ipaddr built by bitwise OR.
119   def |(other)
120     return self.clone.set(@addr | coerce_other(other).to_i)
121   end
123   # Returns a new ipaddr built by bitwise right-shift.
124   def >>(num)
125     return self.clone.set(@addr >> num)
126   end
128   # Returns a new ipaddr built by bitwise left shift.
129   def <<(num)
130     return self.clone.set(addr_mask(@addr << num))
131   end
133   # Returns a new ipaddr built by bitwise negation.
134   def ~
135     return self.clone.set(addr_mask(~@addr))
136   end
138   # Returns true if two ipaddrs are equal.
139   def ==(other)
140     other = coerce_other(other)
141     return @family == other.family && @addr == other.to_i
142   end
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.)
146   def mask(prefixlen)
147     return self.clone.mask!(prefixlen)
148   end
150   # Returns true if the given ipaddr is in the range.
151   #
152   # e.g.:
153   #   require 'ipaddr'
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
159   def include?(other)
160     other = coerce_other(other)
161     if ipv4_mapped?
162       if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
163         return false
164       end
165       mask_addr = (@mask_addr & IN4MASK)
166       addr = (@addr & IN4MASK)
167       family = Socket::AF_INET
168     else
169       mask_addr = @mask_addr
170       addr = @addr
171       family = @family
172     end
173     if other.ipv4_mapped?
174       other_addr = (other.to_i & IN4MASK)
175       other_family = Socket::AF_INET
176     else
177       other_addr = other.to_i
178       other_family = other.family
179     end
181     if family != other_family
182       return false
183     end
184     return ((addr & mask_addr) == (other_addr & mask_addr))
185   end
186   alias === include?
188   # Returns the integer representation of the ipaddr.
189   def to_i
190     return @addr
191   end
193   # Returns a string containing the IP address representation.
194   def to_s
195     str = to_string
196     return str if ipv4?
198     str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
199     loop do
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/, ':')
207       break
208     end
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)
213     end
215     str
216   end
218   # Returns a string containing the IP address representation in
219   # canonical form.
220   def to_string
221     return _to_string(@addr)
222   end
224   # Returns a network byte ordered string form of the IP address.
225   def hton
226     case @family
227     when Socket::AF_INET
228       return [@addr].pack('N')
229     when Socket::AF_INET6
230       return (0..7).map { |i|
231         (@addr >> (112 - 16 * i)) & 0xffff
232       }.pack('n8')
233     else
234       raise "unsupported address family"
235     end
236   end
238   # Returns true if the ipaddr is an IPv4 address.
239   def ipv4?
240     return @family == Socket::AF_INET
241   end
243   # Returns true if the ipaddr is an IPv6 address.
244   def ipv6?
245     return @family == Socket::AF_INET6
246   end
248   # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
249   def ipv4_mapped?
250     return ipv6? && (@addr >> 32) == 0xffff
251   end
253   # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
254   def ipv4_compat?
255     if !ipv6? || (@addr >> 32) != 0
256       return false
257     end
258     a = (@addr & IN4MASK)
259     return a != 0 && a != 1
260   end
262   # Returns a new ipaddr built by converting the native IPv4 address
263   # into an IPv4-mapped IPv6 address.
264   def ipv4_mapped
265     if !ipv4?
266       raise ArgumentError, "not an IPv4 address"
267     end
268     return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
269   end
271   # Returns a new ipaddr built by converting the native IPv4 address
272   # into an IPv4-compatible IPv6 address.
273   def ipv4_compat
274     if !ipv4?
275       raise ArgumentError, "not an IPv4 address"
276     end
277     return self.clone.set(@addr, Socket::AF_INET6)
278   end
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.
283   def native
284     if !ipv4_mapped? && !ipv4_compat?
285       return self
286     end
287     return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
288   end
290   # Returns a string for DNS reverse lookup.  It returns a string in
291   # RFC3172 form for an IPv6 address.
292   def reverse
293     case @family
294     when Socket::AF_INET
295       return _reverse + ".in-addr.arpa"
296     when Socket::AF_INET6
297       return ip6_arpa
298     else
299       raise "unsupported address family"
300     end
301   end
303   # Returns a string for DNS reverse lookup compatible with RFC3172.
304   def ip6_arpa
305     if !ipv6?
306       raise ArgumentError, "not an IPv6 address"
307     end
308     return _reverse + ".ip6.arpa"
309   end
311   # Returns a string for DNS reverse lookup compatible with RFC1886.
312   def ip6_int
313     if !ipv6?
314       raise ArgumentError, "not an IPv6 address"
315     end
316     return _reverse + ".ip6.int"
317   end
319   # Returns the successor to the ipaddr.
320   def succ
321     return self.clone.set(@addr + 1, @family)
322   end
324   # Compares the ipaddr with another.
325   def <=>(other)
326     other = coerce_other(other)
328     return nil if other.family != @family
330     return @addr <=> other.to_i
331   end
332   include Comparable
334   # Creates a Range object for the network address.
335   def to_range
336     begin_addr = (@addr & @mask_addr)
338     case @family
339     when Socket::AF_INET
340       end_addr = (@addr | (IN4MASK ^ @mask_addr))
341     when Socket::AF_INET6
342       end_addr = (@addr | (IN6MASK ^ @mask_addr))
343     else
344       raise "unsupported address family"
345     end
347     return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
348   end
350   # Returns a string containing a human-readable representation of the
351   # ipaddr. ("#<IPAddr: family:address/mask>")
352   def inspect
353     case @family
354     when Socket::AF_INET
355       af = "IPv4"
356     when Socket::AF_INET6
357       af = "IPv6"
358     else
359       raise "unsupported address family"
360     end
361     return sprintf("#<%s: %s:%s/%s>", self.class.name,
362                    af, _to_string(@addr), _to_string(@mask_addr))
363   end
365   protected
367   def set(addr, *family)
368     case family[0] ? family[0] : @family
369     when Socket::AF_INET
370       if addr < 0 || addr > IN4MASK
371         raise ArgumentError, "invalid address"
372       end
373     when Socket::AF_INET6
374       if addr < 0 || addr > IN6MASK
375         raise ArgumentError, "invalid address"
376       end
377     else
378       raise ArgumentError, "unsupported address family"
379     end
380     @addr = addr
381     if family[0]
382       @family = family[0]
383     end
384     return self
385   end
387   def mask!(mask)
388     if mask.kind_of?(String)
389       if mask =~ /^\d+$/
390         prefixlen = mask.to_i
391       else
392         m = IPAddr.new(mask)
393         if m.family != @family
394           raise ArgumentError, "address family is not same"
395         end
396         @mask_addr = m.to_i
397         @addr &= @mask_addr
398         return self
399       end
400     else
401       prefixlen = mask
402     end
403     case @family
404     when Socket::AF_INET
405       if prefixlen < 0 || prefixlen > 32
406         raise ArgumentError, "invalid length"
407       end
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"
413       end
414       masklen = 128 - prefixlen
415       @mask_addr = ((IN6MASK >> masklen) << masklen)
416     else
417       raise "unsupported address family"
418     end
419     @addr = ((@addr >> masklen) << masklen)
420     return self
421   end
423   private
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.
428   # 
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.
436   # 
437   # Otherwise an IP addess is generated from a packed in_addr value
438   # and an address family.
439   #
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)
445       case family
446       when Socket::AF_INET, Socket::AF_INET6
447         set(addr.to_i, family)
448         @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
449         return
450       when Socket::AF_UNSPEC
451         raise ArgumentError, "address family must be specified"
452       else
453         raise ArgumentError, "unsupported address family: #{family}"
454       end
455     end
456     prefix, prefixlen = addr.split('/')
457     if prefix =~ /^\[(.*)\]$/i
458       prefix = $1
459       family = Socket::AF_INET6
460     end
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)
464     begin
465       IPSocket.getaddress(prefix)               # test if address is vaild
466     rescue
467       raise ArgumentError, "invalid address"
468     end
469     @addr = @family = nil
470     if family == Socket::AF_UNSPEC || family == Socket::AF_INET
471       @addr = in_addr(prefix)
472       if @addr
473         @family = Socket::AF_INET
474       end
475     end
476     if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
477       @addr = in6_addr(prefix)
478       @family = Socket::AF_INET6
479     end
480     if family != Socket::AF_UNSPEC && @family != family
481       raise ArgumentError, "address family mismatch"
482     end
483     if prefixlen
484       mask!(prefixlen)
485     else
486       @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
487     end
488   end
490   def coerce_other(other)
491     case other
492     when IPAddr
493       other
494     when String
495       self.class.new(other)
496     else
497       self.class.new(other, @family)
498     end
499   end
501   def in_addr(addr)
502     if addr =~ /^\d+\.\d+\.\d+\.\d+$/
503       return addr.split('.').inject(0) { |i, s|
504         i << 8 | s.to_i
505       }
506     end
507     return nil
508   end
510   def in6_addr(left)
511     case left
512     when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
513       return in_addr($1) + 0xffff00000000
514     when /^::(\d+\.\d+\.\d+\.\d+)$/i
515       return in_addr($1)
516     when /[^0-9a-f:]/i
517       raise ArgumentError, "invalid address"
518     when /^(.*)::(.*)$/
519       left, right = $1, $2
520     else
521       right = ''
522     end
523     l = left.split(':')
524     r = right.split(':')
525     rest = 8 - l.size - r.size
526     if rest < 0
527       return nil
528     end
529     return (l + Array.new(rest, '0') + r).inject(0) { |i, s|
530       i << 16 | s.hex
531     }
532   end
534   def addr_mask(addr)
535     case @family
536     when Socket::AF_INET
537       return addr & IN4MASK
538     when Socket::AF_INET6
539       return addr & IN6MASK
540     else
541       raise "unsupported address family"
542     end
543   end
545   def _reverse
546     case @family
547     when Socket::AF_INET
548       return (0..3).map { |i|
549         (@addr >> (8 * i)) & 0xff
550       }.join('.')
551     when Socket::AF_INET6
552       return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
553     else
554       raise "unsupported address family"
555     end
556   end
558   def _to_string(addr)
559     case @family
560     when Socket::AF_INET
561       return (0..3).map { |i|
562         (addr >> (24 - 8 * i)) & 0xff
563       }.join('.')
564     when Socket::AF_INET6
565       return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
566     else
567       raise "unsupported address family"
568     end
569   end
573 if $0 == __FILE__
574   eval DATA.read, nil, $0, __LINE__+4
577 __END__
579 require 'test/unit'
580 require 'test/unit/ui/console/testrunner'
582 class TC_IPAddr < Test::Unit::TestCase
583   def test_s_new
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")
588     }
590     a = IPAddr.new
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)
641     [
642       ["fe80::1%fxp0"],
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"],
648     ].each { |args|
649       assert_raises(ArgumentError) {
650         IPAddr.new(*args)
651       }
652     }
653   end
655   def test_s_new_ntoh
656     addr = ''
657     IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
658       addr += sprintf("%02x", c)
659     }
660     assert_equal("123456789abcdef0123456789abcdef0", addr)
661     addr = ''
662     IPAddr.new("123.45.67.89").hton.each_byte { |c|
663       addr += sprintf("%02x", c)
664     }
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)
670   end
672   def test_ipv4_compat
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?)
678     b = a.native
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")
684     b = a.ipv4_compat
685     assert_equal("::192.168.1.2", b.to_s)
686     assert_equal(Socket::AF_INET6, b.family)
687   end
689   def test_ipv4_mapped
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?)
695     b = a.native
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")
701     b = a.ipv4_mapped
702     assert_equal("::ffff:192.168.1.2", b.to_s)
703     assert_equal(Socket::AF_INET6, b.family)
704   end
706   def test_reverse
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)
709   end
711   def test_ip6_arpa
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
715     }
716   end
718   def test_ip6_int
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
722     }
723   end
725   def test_to_s
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)
728   end
731 class TC_Operator < Test::Unit::TestCase
733   IN6MASK32  = "ffff:ffff::"
734   IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
736   def setup
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)
741   end
742   alias set_up setup
744   def test_or
745     assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
746     a = @a
747     a |= @b
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)
752   end
754   def test_and
755     assert_equal("3ffe:505::", (@a & @c).to_s)
756     a = @a
757     a &= @c
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)
761   end
763   def test_shift_right
764     assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
765     a = @a
766     a >>= 16
767     assert_equal("0:3ffe:505:2::", a.to_s)
768     assert_equal("3ffe:505:2::", @a.to_s)
769   end
771   def test_shift_left
772     assert_equal("505:2::", (@a << 16).to_s)
773     a = @a
774     a <<= 16
775     assert_equal("505:2::", a.to_s)
776     assert_equal("3ffe:505:2::", @a.to_s)
777   end
779   def test_carrot
780     a = ~@in6_addr_any
781     assert_equal(IN6MASK128, a.to_s)
782     assert_equal("::", @in6_addr_any.to_s)
783   end
785   def test_equal
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::"))
790   end
792   def test_mask
793     a = @a.mask(32)
794     assert_equal("3ffe:505::", a.to_s)
795     assert_equal("3ffe:505:2::", @a.to_s)
796   end
798   def test_include?
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))
812   end