PageRenderTime 107ms CodeModel.GetById 64ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/ipaddr.rb

http://github.com/agross/netopenspace
Ruby | 814 lines | 732 code | 48 blank | 34 comment | 35 complexity | fca9ba6ea6024fdee88a368060e2e4cf MD5 | raw file
  1#
  2# ipaddr.rb - A class to manipulate an IP address
  3#
  4# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
  5# Copyright (c) 2007 Akinori MUSHA <knu@iDaemons.org>.
  6# All rights reserved.
  7#
  8# You can redistribute and/or modify it under the same terms as Ruby.
  9#
 10# $Id: ipaddr.rb 18049 2008-07-12 15:08:29Z shyouhei $
 11#
 12# Contact:
 13#   - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
 14#
 15# TODO:
 16#   - scope_id support
 17#
 18require 'socket'
 19
 20unless Socket.const_defined? "AF_INET6"
 21  class Socket
 22    AF_INET6 = Object.new
 23  end
 24
 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
 32
 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?($')
 42
 43      false
 44    end
 45
 46    def valid?(addr)
 47      valid_v4?(addr) || valid_v6?(addr)
 48    end
 49
 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
 61end
 62
 63# IPAddr provides a set of methods to manipulate an IP address.  Both IPv4 and
 64# IPv6 are supported.
 65#
 66# == Example
 67#
 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>
 83
 84class IPAddr
 85
 86  IN4MASK = 0xffffffff
 87  IN6MASK = 0xffffffffffffffffffffffffffffffff
 88  IN6FORMAT = (["%.4x"] * 8).join(':')
 89
 90  # Returns the address family of this IP address.
 91  attr :family
 92
 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
 98
 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
112
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
117
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
122
123  # Returns a new ipaddr built by bitwise right-shift.
124  def >>(num)
125    return self.clone.set(@addr >> num)
126  end
127
128  # Returns a new ipaddr built by bitwise left shift.
129  def <<(num)
130    return self.clone.set(addr_mask(@addr << num))
131  end
132
133  # Returns a new ipaddr built by bitwise negation.
134  def ~
135    return self.clone.set(addr_mask(~@addr))
136  end
137
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
143
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
149
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
180
181    if family != other_family
182      return false
183    end
184    return ((addr & mask_addr) == (other_addr & mask_addr))
185  end
186  alias === include?
187
188  # Returns the integer representation of the ipaddr.
189  def to_i
190    return @addr
191  end
192
193  # Returns a string containing the IP address representation.
194  def to_s
195    str = to_string
196    return str if ipv4?
197
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,}/, '::')
210
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
214
215    str
216  end
217
218  # Returns a string containing the IP address representation in
219  # canonical form.
220  def to_string
221    return _to_string(@addr)
222  end
223
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
237
238  # Returns true if the ipaddr is an IPv4 address.
239  def ipv4?
240    return @family == Socket::AF_INET
241  end
242
243  # Returns true if the ipaddr is an IPv6 address.
244  def ipv6?
245    return @family == Socket::AF_INET6
246  end
247
248  # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
249  def ipv4_mapped?
250    return ipv6? && (@addr >> 32) == 0xffff
251  end
252
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
261
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
270
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
279
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
289
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
302
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
310
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
318
319  # Returns the successor to the ipaddr.
320  def succ
321    return self.clone.set(@addr + 1, @family)
322  end
323
324  # Compares the ipaddr with another.
325  def <=>(other)
326    other = coerce_other(other)
327
328    return nil if other.family != @family
329
330    return @addr <=> other.to_i
331  end
332  include Comparable
333
334  # Creates a Range object for the network address.
335  def to_range
336    begin_addr = (@addr & @mask_addr)
337
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
346
347    return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
348  end
349
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
364
365  protected
366
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
386
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
422
423  private
424
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
489
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
500
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
509
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
533
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
544
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
557
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
570
571end
572
573if $0 == __FILE__
574  eval DATA.read, nil, $0, __LINE__+4
575end
576
577__END__
578
579require 'test/unit'
580require 'test/unit/ui/console/testrunner'
581
582class 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    }
589
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)
594
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)
599
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)
607
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)
612
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)
617
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?)
624
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)
630
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)
635
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)
638
639    assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
640
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
654
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
671
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?)
682
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
688
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?)
699
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
705
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
710
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
717
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
724
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
729end
730
731class TC_Operator < Test::Unit::TestCase
732
733  IN6MASK32  = "ffff:ffff::"
734  IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
735
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
743
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
753
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
762
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
770
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
778
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
784
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
791
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
797
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
808
809    assert_equal(true, net1.include?(int))
810    assert_equal(false, net1.include?(int+255))
811
812  end
813
814end