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, 2009, 2012 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 39423 2013-02-23 04:03:59Z knu $
11#
12# Contact:
13#   - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
14#
15# TODO:
16#   - scope_id support
17#
18require 'socket'
19
20# IPAddr provides a set of methods to manipulate an IP address.  Both IPv4 and
21# IPv6 are supported.
22#
23# == Example
24#
25#   require 'ipaddr'
26#
27#   ipaddr1 = IPAddr.new "3ffe:505:2::1"
28#
29#   p ipaddr1                   #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
30#
31#   p ipaddr1.to_s              #=> "3ffe:505:2::1"
32#
33#   ipaddr2 = ipaddr1.mask(48)  #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
34#
35#   p ipaddr2.to_s              #=> "3ffe:505:2::"
36#
37#   ipaddr3 = IPAddr.new "192.168.2.0/24"
38#
39#   p ipaddr3                   #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
40
41class IPAddr
42
43  # 32 bit mask for IPv4
44  IN4MASK = 0xffffffff
45  # 128 bit mask for IPv4
46  IN6MASK = 0xffffffffffffffffffffffffffffffff
47  # Format string for IPv6
48  IN6FORMAT = (["%.4x"] * 8).join(':')
49
50  # Regexp _internally_ used for parsing IPv4 address.
51  RE_IPV4ADDRLIKE = %r{
52    \A
53    (\d+) \. (\d+) \. (\d+) \. (\d+)
54    \z
55  }x
56
57  # Regexp _internally_ used for parsing IPv6 address.
58  RE_IPV6ADDRLIKE_FULL = %r{
59    \A
60    (?:
61      (?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
62    |
63      ( (?: [\da-f]{1,4} : ){6} )
64      (\d+) \. (\d+) \. (\d+) \. (\d+)
65    )
66    \z
67  }xi
68
69  # Regexp _internally_ used for parsing IPv6 address.
70  RE_IPV6ADDRLIKE_COMPRESSED = %r{
71    \A
72    ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
73    ::
74    ( (?:
75      ( (?: [\da-f]{1,4} : )* )
76      (?:
77        [\da-f]{1,4}
78      |
79        (\d+) \. (\d+) \. (\d+) \. (\d+)
80      )
81    )? )
82    \z
83  }xi
84
85  # Generic IPAddr related error. Exceptions raised in this class should
86  # inherit from Error.
87  class Error < ArgumentError; end
88
89  # Raised when the provided IP address is an invalid address.
90  class InvalidAddressError < Error; end
91
92  # Raised when the address family is invalid such as an address with an
93  # unsupported family, an address with an inconsistent family, or an address
94  # who's family cannot be determined.
95  class AddressFamilyError < Error; end
96
97  # Raised when the address is an invalid length.
98  class InvalidPrefixError < InvalidAddressError; end
99
100  # Returns the address family of this IP address.
101  attr_reader :family
102
103  # Creates a new ipaddr containing the given network byte ordered
104  # string form of an IP address.
105  def IPAddr::new_ntoh(addr)
106    return IPAddr.new(IPAddr::ntop(addr))
107  end
108
109  # Convert a network byte ordered string form of an IP address into
110  # human readable form.
111  def IPAddr::ntop(addr)
112    case addr.size
113    when 4
114      s = addr.unpack('C4').join('.')
115    when 16
116      s = IN6FORMAT % addr.unpack('n8')
117    else
118      raise AddressFamilyError, "unsupported address family"
119    end
120    return s
121  end
122
123  # Returns a new ipaddr built by bitwise AND.
124  def &(other)
125    return self.clone.set(@addr & coerce_other(other).to_i)
126  end
127
128  # Returns a new ipaddr built by bitwise OR.
129  def |(other)
130    return self.clone.set(@addr | coerce_other(other).to_i)
131  end
132
133  # Returns a new ipaddr built by bitwise right-shift.
134  def >>(num)
135    return self.clone.set(@addr >> num)
136  end
137
138  # Returns a new ipaddr built by bitwise left shift.
139  def <<(num)
140    return self.clone.set(addr_mask(@addr << num))
141  end
142
143  # Returns a new ipaddr built by bitwise negation.
144  def ~
145    return self.clone.set(addr_mask(~@addr))
146  end
147
148  # Returns true if two ipaddrs are equal.
149  def ==(other)
150    other = coerce_other(other)
151    return @family == other.family && @addr == other.to_i
152  end
153
154  # Returns a new ipaddr built by masking IP address with the given
155  # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
156  def mask(prefixlen)
157    return self.clone.mask!(prefixlen)
158  end
159
160  # Returns true if the given ipaddr is in the range.
161  #
162  # e.g.:
163  #   require 'ipaddr'
164  #   net1 = IPAddr.new("192.168.2.0/24")
165  #   net2 = IPAddr.new("192.168.2.100")
166  #   net3 = IPAddr.new("192.168.3.0")
167  #   p net1.include?(net2)     #=> true
168  #   p net1.include?(net3)     #=> false
169  def include?(other)
170    other = coerce_other(other)
171    if ipv4_mapped?
172      if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
173        return false
174      end
175      mask_addr = (@mask_addr & IN4MASK)
176      addr = (@addr & IN4MASK)
177      family = Socket::AF_INET
178    else
179      mask_addr = @mask_addr
180      addr = @addr
181      family = @family
182    end
183    if other.ipv4_mapped?
184      other_addr = (other.to_i & IN4MASK)
185      other_family = Socket::AF_INET
186    else
187      other_addr = other.to_i
188      other_family = other.family
189    end
190
191    if family != other_family
192      return false
193    end
194    return ((addr & mask_addr) == (other_addr & mask_addr))
195  end
196  alias === include?
197
198  # Returns the integer representation of the ipaddr.
199  def to_i
200    return @addr
201  end
202
203  # Returns a string containing the IP address representation.
204  def to_s
205    str = to_string
206    return str if ipv4?
207
208    str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
209    loop do
210      break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
211      break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
212      break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
213      break if str.sub!(/\b0:0:0:0:0\b/, ':')
214      break if str.sub!(/\b0:0:0:0\b/, ':')
215      break if str.sub!(/\b0:0:0\b/, ':')
216      break if str.sub!(/\b0:0\b/, ':')
217      break
218    end
219    str.sub!(/:{3,}/, '::')
220
221    if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
222      str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
223    end
224
225    str
226  end
227
228  # Returns a string containing the IP address representation in
229  # canonical form.
230  def to_string
231    return _to_string(@addr)
232  end
233
234  # Returns a network byte ordered string form of the IP address.
235  def hton
236    case @family
237    when Socket::AF_INET
238      return [@addr].pack('N')
239    when Socket::AF_INET6
240      return (0..7).map { |i|
241        (@addr >> (112 - 16 * i)) & 0xffff
242      }.pack('n8')
243    else
244      raise AddressFamilyError, "unsupported address family"
245    end
246  end
247
248  # Returns true if the ipaddr is an IPv4 address.
249  def ipv4?
250    return @family == Socket::AF_INET
251  end
252
253  # Returns true if the ipaddr is an IPv6 address.
254  def ipv6?
255    return @family == Socket::AF_INET6
256  end
257
258  # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
259  def ipv4_mapped?
260    return ipv6? && (@addr >> 32) == 0xffff
261  end
262
263  # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
264  def ipv4_compat?
265    if !ipv6? || (@addr >> 32) != 0
266      return false
267    end
268    a = (@addr & IN4MASK)
269    return a != 0 && a != 1
270  end
271
272  # Returns a new ipaddr built by converting the native IPv4 address
273  # into an IPv4-mapped IPv6 address.
274  def ipv4_mapped
275    if !ipv4?
276      raise InvalidAddressError, "not an IPv4 address"
277    end
278    return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
279  end
280
281  # Returns a new ipaddr built by converting the native IPv4 address
282  # into an IPv4-compatible IPv6 address.
283  def ipv4_compat
284    if !ipv4?
285      raise InvalidAddressError, "not an IPv4 address"
286    end
287    return self.clone.set(@addr, Socket::AF_INET6)
288  end
289
290  # Returns a new ipaddr built by converting the IPv6 address into a
291  # native IPv4 address.  If the IP address is not an IPv4-mapped or
292  # IPv4-compatible IPv6 address, returns self.
293  def native
294    if !ipv4_mapped? && !ipv4_compat?
295      return self
296    end
297    return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
298  end
299
300  # Returns a string for DNS reverse lookup.  It returns a string in
301  # RFC3172 form for an IPv6 address.
302  def reverse
303    case @family
304    when Socket::AF_INET
305      return _reverse + ".in-addr.arpa"
306    when Socket::AF_INET6
307      return ip6_arpa
308    else
309      raise AddressFamilyError, "unsupported address family"
310    end
311  end
312
313  # Returns a string for DNS reverse lookup compatible with RFC3172.
314  def ip6_arpa
315    if !ipv6?
316      raise InvalidAddressError, "not an IPv6 address"
317    end
318    return _reverse + ".ip6.arpa"
319  end
320
321  # Returns a string for DNS reverse lookup compatible with RFC1886.
322  def ip6_int
323    if !ipv6?
324      raise InvalidAddressError, "not an IPv6 address"
325    end
326    return _reverse + ".ip6.int"
327  end
328
329  # Returns the successor to the ipaddr.
330  def succ
331    return self.clone.set(@addr + 1, @family)
332  end
333
334  # Compares the ipaddr with another.
335  def <=>(other)
336    other = coerce_other(other)
337
338    return nil if other.family != @family
339
340    return @addr <=> other.to_i
341  end
342  include Comparable
343
344  # Checks equality used by Hash.
345  def eql?(other)
346    return self.class == other.class && self.hash == other.hash && self == other
347  end
348
349  # Returns a hash value used by Hash, Set, and Array classes
350  def hash
351    return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
352  end
353
354  # Creates a Range object for the network address.
355  def to_range
356    begin_addr = (@addr & @mask_addr)
357
358    case @family
359    when Socket::AF_INET
360      end_addr = (@addr | (IN4MASK ^ @mask_addr))
361    when Socket::AF_INET6
362      end_addr = (@addr | (IN6MASK ^ @mask_addr))
363    else
364      raise AddressFamilyError, "unsupported address family"
365    end
366
367    return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
368  end
369
370  # Returns a string containing a human-readable representation of the
371  # ipaddr. ("#<IPAddr: family:address/mask>")
372  def inspect
373    case @family
374    when Socket::AF_INET
375      af = "IPv4"
376    when Socket::AF_INET6
377      af = "IPv6"
378    else
379      raise AddressFamilyError, "unsupported address family"
380    end
381    return sprintf("#<%s: %s:%s/%s>", self.class.name,
382                   af, _to_string(@addr), _to_string(@mask_addr))
383  end
384
385  protected
386
387  # Set +@addr+, the internal stored ip address, to given +addr+. The
388  # parameter +addr+ is validated using the first +family+ member,
389  # which is +Socket::AF_INET+ or +Socket::AF_INET6+.
390  def set(addr, *family)
391    case family[0] ? family[0] : @family
392    when Socket::AF_INET
393      if addr < 0 || addr > IN4MASK
394        raise InvalidAddressError, "invalid address"
395      end
396    when Socket::AF_INET6
397      if addr < 0 || addr > IN6MASK
398        raise InvalidAddressError, "invalid address"
399      end
400    else
401      raise AddressFamilyError, "unsupported address family"
402    end
403    @addr = addr
404    if family[0]
405      @family = family[0]
406    end
407    return self
408  end
409
410  # Set current netmask to given mask.
411  def mask!(mask)
412    if mask.kind_of?(String)
413      if mask =~ /^\d+$/
414        prefixlen = mask.to_i
415      else
416        m = IPAddr.new(mask)
417        if m.family != @family
418          raise InvalidPrefixError, "address family is not same"
419        end
420        @mask_addr = m.to_i
421        @addr &= @mask_addr
422        return self
423      end
424    else
425      prefixlen = mask
426    end
427    case @family
428    when Socket::AF_INET
429      if prefixlen < 0 || prefixlen > 32
430        raise InvalidPrefixError, "invalid length"
431      end
432      masklen = 32 - prefixlen
433      @mask_addr = ((IN4MASK >> masklen) << masklen)
434    when Socket::AF_INET6
435      if prefixlen < 0 || prefixlen > 128
436        raise InvalidPrefixError, "invalid length"
437      end
438      masklen = 128 - prefixlen
439      @mask_addr = ((IN6MASK >> masklen) << masklen)
440    else
441      raise AddressFamilyError, "unsupported address family"
442    end
443    @addr = ((@addr >> masklen) << masklen)
444    return self
445  end
446
447  private
448
449  # Creates a new ipaddr object either from a human readable IP
450  # address representation in string, or from a packed in_addr value
451  # followed by an address family.
452  #
453  # In the former case, the following are the valid formats that will
454  # be recognized: "address", "address/prefixlen" and "address/mask",
455  # where IPv6 address may be enclosed in square brackets (`[' and
456  # `]').  If a prefixlen or a mask is specified, it returns a masked
457  # IP address.  Although the address family is determined
458  # automatically from a specified string, you can specify one
459  # explicitly by the optional second argument.
460  #
461  # Otherwise an IP address is generated from a packed in_addr value
462  # and an address family.
463  #
464  # The IPAddr class defines many methods and operators, and some of
465  # those, such as &, |, include? and ==, accept a string, or a packed
466  # in_addr value instead of an IPAddr object.
467  def initialize(addr = '::', family = Socket::AF_UNSPEC)
468    if !addr.kind_of?(String)
469      case family
470      when Socket::AF_INET, Socket::AF_INET6
471        set(addr.to_i, family)
472        @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
473        return
474      when Socket::AF_UNSPEC
475        raise AddressFamilyError, "address family must be specified"
476      else
477        raise AddressFamilyError, "unsupported address family: #{family}"
478      end
479    end
480    prefix, prefixlen = addr.split('/')
481    if prefix =~ /^\[(.*)\]$/i
482      prefix = $1
483      family = Socket::AF_INET6
484    end
485    # It seems AI_NUMERICHOST doesn't do the job.
486    #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
487    #                  Socket::AI_NUMERICHOST)
488    @addr = @family = nil
489    if family == Socket::AF_UNSPEC || family == Socket::AF_INET
490      @addr = in_addr(prefix)
491      if @addr
492        @family = Socket::AF_INET
493      end
494    end
495    if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
496      @addr = in6_addr(prefix)
497      @family = Socket::AF_INET6
498    end
499    if family != Socket::AF_UNSPEC && @family != family
500      raise AddressFamilyError, "address family mismatch"
501    end
502    if prefixlen
503      mask!(prefixlen)
504    else
505      @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
506    end
507  end
508
509  def coerce_other(other)
510    case other
511    when IPAddr
512      other
513    when String
514      self.class.new(other)
515    else
516      self.class.new(other, @family)
517    end
518  end
519
520  def in_addr(addr)
521    case addr
522    when Array
523      octets = addr
524    else
525      m = RE_IPV4ADDRLIKE.match(addr) or return nil
526      octets = m.captures
527    end
528    octets.inject(0) { |i, s|
529      (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address"
530      s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous"
531      i << 8 | n
532    }
533  end
534
535  def in6_addr(left)
536    case left
537    when RE_IPV6ADDRLIKE_FULL
538      if $2
539        addr = in_addr($~[2,4])
540        left = $1 + ':'
541      else
542        addr = 0
543      end
544      right = ''
545    when RE_IPV6ADDRLIKE_COMPRESSED
546      if $4
547        left.count(':') <= 6 or raise InvalidAddressError, "invalid address"
548        addr = in_addr($~[4,4])
549        left = $1
550        right = $3 + '0:0'
551      else
552        left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
553          raise InvalidAddressError, "invalid address"
554        left = $1
555        right = $2
556        addr = 0
557      end
558    else
559      raise InvalidAddressError, "invalid address"
560    end
561    l = left.split(':')
562    r = right.split(':')
563    rest = 8 - l.size - r.size
564    if rest < 0
565      return nil
566    end
567    (l + Array.new(rest, '0') + r).inject(0) { |i, s|
568      i << 16 | s.hex
569    } | addr
570  end
571
572  def addr_mask(addr)
573    case @family
574    when Socket::AF_INET
575      return addr & IN4MASK
576    when Socket::AF_INET6
577      return addr & IN6MASK
578    else
579      raise AddressFamilyError, "unsupported address family"
580    end
581  end
582
583  def _reverse
584    case @family
585    when Socket::AF_INET
586      return (0..3).map { |i|
587        (@addr >> (8 * i)) & 0xff
588      }.join('.')
589    when Socket::AF_INET6
590      return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
591    else
592      raise AddressFamilyError, "unsupported address family"
593    end
594  end
595
596  def _to_string(addr)
597    case @family
598    when Socket::AF_INET
599      return (0..3).map { |i|
600        (addr >> (24 - 8 * i)) & 0xff
601      }.join('.')
602    when Socket::AF_INET6
603      return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
604    else
605      raise AddressFamilyError, "unsupported address family"
606    end
607  end
608
609end
610
611unless Socket.const_defined? :AF_INET6
612  class Socket < BasicSocket
613    # IPv6 protocol family
614    AF_INET6 = Object.new
615  end
616
617  class << IPSocket
618    private
619
620    def valid_v6?(addr)
621      case addr
622      when IPAddr::RE_IPV6ADDRLIKE_FULL
623        if $2
624          $~[2,4].all? {|i| i.to_i < 256 }
625        else
626          true
627        end
628      when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
629        if $4
630          addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
631        else
632          addr.count(':') <= 7
633        end
634      else
635        false
636      end
637    end
638
639    alias getaddress_orig getaddress
640
641    public
642
643    # Returns a +String+ based representation of a valid DNS hostname,
644    # IPv4 or IPv6 address.
645    #
646    #   IPSocket.getaddress 'localhost'         #=> "::1"
647    #   IPSocket.getaddress 'broadcasthost'     #=> "255.255.255.255"
648    #   IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
649    #   IPSocket.getaddress 'www.ccc.de'        #=> "2a00:1328:e102:ccc0::122"
650    def getaddress(s)
651      if valid_v6?(s)
652        s
653      else
654        getaddress_orig(s)
655      end
656    end
657  end
658end
659
660if $0 == __FILE__
661  eval DATA.read, nil, $0, __LINE__+4
662end
663
664__END__
665
666require 'test/unit'
667
668class TC_IPAddr < Test::Unit::TestCase
669  def test_s_new
670    [
671      ["3FFE:505:ffff::/48"],
672      ["0:0:0:1::"],
673      ["2001:200:300::/48"],
674      ["2001:200:300::192.168.1.2/48"],
675      ["1:2:3:4:5:6:7::"],
676      ["::2:3:4:5:6:7:8"],
677    ].each { |args|
678      assert_nothing_raised {
679        IPAddr.new(*args)
680      }
681    }
682
683    a = IPAddr.new
684    assert_equal("::", a.to_s)
685    assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
686    assert_equal(Socket::AF_INET6, a.family)
687
688    a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
689    assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
690    assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
691    assert_equal(Socket::AF_INET6, a.family)
692
693    a = IPAddr.new("3ffe:505:2::/48")
694    assert_equal("3ffe:505:2::", a.to_s)
695    assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
696    assert_equal(Socket::AF_INET6, a.family)
697    assert_equal(false, a.ipv4?)
698    assert_equal(true, a.ipv6?)
699    assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
700
701    a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
702    assert_equal("3ffe:505:2::", a.to_s)
703    assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
704    assert_equal(Socket::AF_INET6, a.family)
705
706    a = IPAddr.new("0.0.0.0")
707    assert_equal("0.0.0.0", a.to_s)
708    assert_equal("0.0.0.0", a.to_string)
709    assert_equal(Socket::AF_INET, a.family)
710
711    a = IPAddr.new("192.168.1.2")
712    assert_equal("192.168.1.2", a.to_s)
713    assert_equal("192.168.1.2", a.to_string)
714    assert_equal(Socket::AF_INET, a.family)
715    assert_equal(true, a.ipv4?)
716    assert_equal(false, a.ipv6?)
717
718    a = IPAddr.new("192.168.1.2/24")
719    assert_equal("192.168.1.0", a.to_s)
720    assert_equal("192.168.1.0", a.to_string)
721    assert_equal(Socket::AF_INET, a.family)
722    assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
723
724    a = IPAddr.new("192.168.1.2/255.255.255.0")
725    assert_equal("192.168.1.0", a.to_s)
726    assert_equal("192.168.1.0", a.to_string)
727    assert_equal(Socket::AF_INET, a.family)
728
729    assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
730    assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
731
732    assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
733    assert_equal("1:2:3:4:5:6:7:0", IPAddr.new("1:2:3:4:5:6:7::").to_s)
734    assert_equal("0:2:3:4:5:6:7:8", IPAddr.new("::2:3:4:5:6:7:8").to_s)
735
736    assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") }
737    assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.011") }
738    assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%fxp0") }
739    assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("[192.168.1.2]/120") }
740    assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/255.255.255.0") }
741    assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/129") }
742    assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/33") }
743    assert_raises(IPAddr::AddressFamilyError) { IPAddr.new(1) }
744    assert_raises(IPAddr::AddressFamilyError) { IPAddr.new("::ffff:192.168.1.2/120", Socket::AF_INET) }
745  end
746
747  def test_s_new_ntoh
748    addr = ''
749    IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
750      addr += sprintf("%02x", c)
751    }
752    assert_equal("123456789abcdef0123456789abcdef0", addr)
753    addr = ''
754    IPAddr.new("123.45.67.89").hton.each_byte { |c|
755      addr += sprintf("%02x", c)
756    }
757    assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
758    a = IPAddr.new("3ffe:505:2::")
759    assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
760    a = IPAddr.new("192.168.2.1")
761    assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
762  end
763
764  def test_ipv4_compat
765    a = IPAddr.new("::192.168.1.2")
766    assert_equal("::192.168.1.2", a.to_s)
767    assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
768    assert_equal(Socket::AF_INET6, a.family)
769    assert_equal(true, a.ipv4_compat?)
770    b = a.native
771    assert_equal("192.168.1.2", b.to_s)
772    assert_equal(Socket::AF_INET, b.family)
773    assert_equal(false, b.ipv4_compat?)
774
775    a = IPAddr.new("192.168.1.2")
776    b = a.ipv4_compat
777    assert_equal("::192.168.1.2", b.to_s)
778    assert_equal(Socket::AF_INET6, b.family)
779  end
780
781  def test_ipv4_mapped
782    a = IPAddr.new("::ffff:192.168.1.2")
783    assert_equal("::ffff:192.168.1.2", a.to_s)
784    assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
785    assert_equal(Socket::AF_INET6, a.family)
786    assert_equal(true, a.ipv4_mapped?)
787    b = a.native
788    assert_equal("192.168.1.2", b.to_s)
789    assert_equal(Socket::AF_INET, b.family)
790    assert_equal(false, b.ipv4_mapped?)
791
792    a = IPAddr.new("192.168.1.2")
793    b = a.ipv4_mapped
794    assert_equal("::ffff:192.168.1.2", b.to_s)
795    assert_equal(Socket::AF_INET6, b.family)
796  end
797
798  def test_reverse
799    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)
800    assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
801  end
802
803  def test_ip6_arpa
804    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)
805    assert_raises(IPAddr::InvalidAddressError) {
806      IPAddr.new("192.168.2.1").ip6_arpa
807    }
808  end
809
810  def test_ip6_int
811    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)
812    assert_raises(IPAddr::InvalidAddressError) {
813      IPAddr.new("192.168.2.1").ip6_int
814    }
815  end
816
817  def test_to_s
818    assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
819    assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
820  end
821end
822
823class TC_Operator < Test::Unit::TestCase
824
825  IN6MASK32  = "ffff:ffff::"
826  IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
827
828  def setup
829    @in6_addr_any = IPAddr.new()
830    @a = IPAddr.new("3ffe:505:2::/48")
831    @b = IPAddr.new("0:0:0:1::")
832    @c = IPAddr.new(IN6MASK32)
833  end
834  alias set_up setup
835
836  def test_or
837    assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
838    a = @a
839    a |= @b
840    assert_equal("3ffe:505:2:1::", a.to_s)
841    assert_equal("3ffe:505:2::", @a.to_s)
842    assert_equal("3ffe:505:2:1::",
843                 (@a | 0x00000000000000010000000000000000).to_s)
844  end
845
846  def test_and
847    assert_equal("3ffe:505::", (@a & @c).to_s)
848    a = @a
849    a &= @c
850    assert_equal("3ffe:505::", a.to_s)
851    assert_equal("3ffe:505:2::", @a.to_s)
852    assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
853  end
854
855  def test_shift_right
856    assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
857    a = @a
858    a >>= 16
859    assert_equal("0:3ffe:505:2::", a.to_s)
860    assert_equal("3ffe:505:2::", @a.to_s)
861  end
862
863  def test_shift_left
864    assert_equal("505:2::", (@a << 16).to_s)
865    a = @a
866    a <<= 16
867    assert_equal("505:2::", a.to_s)
868    assert_equal("3ffe:505:2::", @a.to_s)
869  end
870
871  def test_carrot
872    a = ~@in6_addr_any
873    assert_equal(IN6MASK128, a.to_s)
874    assert_equal("::", @in6_addr_any.to_s)
875  end
876
877  def test_equal
878    assert_equal(true, @a == IPAddr.new("3FFE:505:2::"))
879    assert_equal(true, @a == IPAddr.new("3ffe:0505:0002::"))
880    assert_equal(true, @a == IPAddr.new("3ffe:0505:0002:0:0:0:0:0"))
881    assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
882    assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
883    assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
884  end
885
886  def test_mask
887    a = @a.mask(32)
888    assert_equal("3ffe:505::", a.to_s)
889    assert_equal("3ffe:505:2::", @a.to_s)
890  end
891
892  def test_include?
893    assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
894    assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
895    assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
896    net1 = IPAddr.new("192.168.2.0/24")
897    assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
898    assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
899    assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
900    # test with integer parameter
901    int = (192 << 24) + (168 << 16) + (2 << 8) + 13
902
903    assert_equal(true, net1.include?(int))
904    assert_equal(false, net1.include?(int+255))
905
906  end
907
908  def test_hash
909    a1 = IPAddr.new('192.168.2.0')
910    a2 = IPAddr.new('192.168.2.0')
911    a3 = IPAddr.new('3ffe:505:2::1')
912    a4 = IPAddr.new('3ffe:505:2::1')
913    a5 = IPAddr.new('127.0.0.1')
914    a6 = IPAddr.new('::1')
915    a7 = IPAddr.new('192.168.2.0/25')
916    a8 = IPAddr.new('192.168.2.0/25')
917
918    h = { a1 => 'ipv4', a2 => 'ipv4', a3 => 'ipv6', a4 => 'ipv6', a5 => 'ipv4', a6 => 'ipv6', a7 => 'ipv4', a8 => 'ipv4'}
919    assert_equal(5, h.size)
920    assert_equal('ipv4', h[a1])
921    assert_equal('ipv4', h[a2])
922    assert_equal('ipv6', h[a3])
923    assert_equal('ipv6', h[a4])
924
925    require 'set'
926    s = Set[a1, a2, a3, a4, a5, a6, a7, a8]
927    assert_equal(5, s.size)
928    assert_equal(true, s.include?(a1))
929    assert_equal(true, s.include?(a2))
930    assert_equal(true, s.include?(a3))
931    assert_equal(true, s.include?(a4))
932    assert_equal(true, s.include?(a5))
933    assert_equal(true, s.include?(a6))
934  end
935end
936