[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / lib / ipaddr.rb
blobdbb213c90adfd37320f0412bf192d285ced69aa2
1 # frozen_string_literal: true
3 # ipaddr.rb - A class to manipulate an IP address
5 # Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
6 # Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
7 # All rights reserved.
9 # You can redistribute and/or modify it under the same terms as Ruby.
11 # $Id$
13 # Contact:
14 #   - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
16 # TODO:
17 #   - scope_id support
19 require 'socket'
21 # IPAddr provides a set of methods to manipulate an IP address.  Both IPv4 and
22 # IPv6 are supported.
24 # == Example
26 #   require 'ipaddr'
28 #   ipaddr1 = IPAddr.new "3ffe:505:2::1"
30 #   p ipaddr1                   #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
32 #   p ipaddr1.to_s              #=> "3ffe:505:2::1"
34 #   ipaddr2 = ipaddr1.mask(48)  #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
36 #   p ipaddr2.to_s              #=> "3ffe:505:2::"
38 #   ipaddr3 = IPAddr.new "192.168.2.0/24"
40 #   p ipaddr3                   #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
42 class IPAddr
43   VERSION = "1.2.6"
45   # 32 bit mask for IPv4
46   IN4MASK = 0xffffffff
47   # 128 bit mask for IPv6
48   IN6MASK = 0xffffffffffffffffffffffffffffffff
49   # Format string for IPv6
50   IN6FORMAT = (["%.4x"] * 8).join(':').freeze
52   # Regexp _internally_ used for parsing IPv4 address.
53   RE_IPV4ADDRLIKE = %r{
54     \A
55     \d+ \. \d+ \. \d+ \. \d+
56     \z
57   }x
59   # Regexp _internally_ used for parsing IPv6 address.
60   RE_IPV6ADDRLIKE_FULL = %r{
61     \A
62     (?:
63       (?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
64     |
65       ( (?: [\da-f]{1,4} : ){6} )
66       (\d+) \. (\d+) \. (\d+) \. (\d+)
67     )
68     \z
69   }xi
71   # Regexp _internally_ used for parsing IPv6 address.
72   RE_IPV6ADDRLIKE_COMPRESSED = %r{
73     \A
74     ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
75     ::
76     ( (?:
77       ( (?: [\da-f]{1,4} : )* )
78       (?:
79         [\da-f]{1,4}
80       |
81         (\d+) \. (\d+) \. (\d+) \. (\d+)
82       )
83     )? )
84     \z
85   }xi
87   # Generic IPAddr related error. Exceptions raised in this class should
88   # inherit from Error.
89   class Error < ArgumentError; end
91   # Raised when the provided IP address is an invalid address.
92   class InvalidAddressError < Error; end
94   # Raised when the address family is invalid such as an address with an
95   # unsupported family, an address with an inconsistent family, or an address
96   # who's family cannot be determined.
97   class AddressFamilyError < Error; end
99   # Raised when the address is an invalid length.
100   class InvalidPrefixError < InvalidAddressError; end
102   # Returns the address family of this IP address.
103   attr_reader :family
105   # Creates a new ipaddr containing the given network byte ordered
106   # string form of an IP address.
107   def self.new_ntoh(addr)
108     return new(ntop(addr))
109   end
111   # Convert a network byte ordered string form of an IP address into
112   # human readable form.
113   # It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY).
114   def self.ntop(addr)
115     if addr.is_a?(String) && addr.encoding != Encoding::BINARY
116       raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)"
117     end
119     case addr.bytesize
120     when 4
121       addr.unpack('C4').join('.')
122     when 16
123       IN6FORMAT % addr.unpack('n8')
124     else
125       raise AddressFamilyError, "unsupported address family"
126     end
127   end
129   # Returns a new ipaddr built by bitwise AND.
130   def &(other)
131     return self.clone.set(@addr & coerce_other(other).to_i)
132   end
134   # Returns a new ipaddr built by bitwise OR.
135   def |(other)
136     return self.clone.set(@addr | coerce_other(other).to_i)
137   end
139   # Returns a new ipaddr built by bitwise right-shift.
140   def >>(num)
141     return self.clone.set(@addr >> num)
142   end
144   # Returns a new ipaddr built by bitwise left shift.
145   def <<(num)
146     return self.clone.set(addr_mask(@addr << num))
147   end
149   # Returns a new ipaddr built by bitwise negation.
150   def ~
151     return self.clone.set(addr_mask(~@addr))
152   end
154   # Returns true if two ipaddrs are equal.
155   def ==(other)
156     other = coerce_other(other)
157   rescue
158     false
159   else
160     @family == other.family && @addr == other.to_i
161   end
163   # Returns a new ipaddr built by masking IP address with the given
164   # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
165   def mask(prefixlen)
166     return self.clone.mask!(prefixlen)
167   end
169   # Returns true if the given ipaddr is in the range.
170   #
171   # e.g.:
172   #   require 'ipaddr'
173   #   net1 = IPAddr.new("192.168.2.0/24")
174   #   net2 = IPAddr.new("192.168.2.100")
175   #   net3 = IPAddr.new("192.168.3.0")
176   #   net4 = IPAddr.new("192.168.2.0/16")
177   #   p net1.include?(net2)     #=> true
178   #   p net1.include?(net3)     #=> false
179   #   p net1.include?(net4)     #=> false
180   #   p net4.include?(net1)     #=> true
181   def include?(other)
182     other = coerce_other(other)
183     return false unless other.family == family
184     begin_addr <= other.begin_addr && end_addr >= other.end_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     str = _to_string(@addr)
223     if @family == Socket::AF_INET6
224       str << zone_id.to_s
225     end
227     return str
228   end
230   # Returns a string containing the IP address representation in
231   # cidr notation
232   def cidr
233     format("%s/%s", to_s, prefix)
234   end
236   # Returns a network byte ordered string form of the IP address.
237   def hton
238     case @family
239     when Socket::AF_INET
240       return [@addr].pack('N')
241     when Socket::AF_INET6
242       return (0..7).map { |i|
243         (@addr >> (112 - 16 * i)) & 0xffff
244       }.pack('n8')
245     else
246       raise AddressFamilyError, "unsupported address family"
247     end
248   end
250   # Returns true if the ipaddr is an IPv4 address.
251   def ipv4?
252     return @family == Socket::AF_INET
253   end
255   # Returns true if the ipaddr is an IPv6 address.
256   def ipv6?
257     return @family == Socket::AF_INET6
258   end
260   # Returns true if the ipaddr is a loopback address.
261   # Loopback IPv4 addresses in the IPv4-mapped IPv6
262   # address range are also considered as loopback addresses.
263   def loopback?
264     case @family
265     when Socket::AF_INET
266       @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8
267     when Socket::AF_INET6
268       @addr == 1 || # ::1
269         (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
270           @addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8
271         ))
272     else
273       raise AddressFamilyError, "unsupported address family"
274     end
275   end
277   # Returns true if the ipaddr is a private address.  IPv4 addresses
278   # in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC
279   # 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC
280   # 4193 are considered private. Private IPv4 addresses in the
281   # IPv4-mapped IPv6 address range are also considered private.
282   def private?
283     case @family
284     when Socket::AF_INET
285       @addr & 0xff000000 == 0x0a000000 ||    # 10.0.0.0/8
286         @addr & 0xfff00000 == 0xac100000 ||  # 172.16.0.0/12
287         @addr & 0xffff0000 == 0xc0a80000     # 192.168.0.0/16
288     when Socket::AF_INET6
289       @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000 ||
290         (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
291           @addr & 0xff000000 == 0x0a000000 ||  # ::ffff:10.0.0.0/8
292           @addr & 0xfff00000 == 0xac100000 ||  # ::ffff::172.16.0.0/12
293           @addr & 0xffff0000 == 0xc0a80000     # ::ffff::192.168.0.0/16
294         ))
295     else
296       raise AddressFamilyError, "unsupported address family"
297     end
298   end
300   # Returns true if the ipaddr is a link-local address.  IPv4
301   # addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local
302   # IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
303   # considered link-local. Link-local IPv4 addresses in the
304   # IPv4-mapped IPv6 address range are also considered link-local.
305   def link_local?
306     case @family
307     when Socket::AF_INET
308       @addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
309     when Socket::AF_INET6
310       @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10
311         (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
312           @addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16
313         ))
314     else
315       raise AddressFamilyError, "unsupported address family"
316     end
317   end
319   # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
320   def ipv4_mapped?
321     return ipv6? && (@addr >> 32) == 0xffff
322   end
324   # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
325   def ipv4_compat?
326     warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
327     _ipv4_compat?
328   end
330   def _ipv4_compat?
331     if !ipv6? || (@addr >> 32) != 0
332       return false
333     end
334     a = (@addr & IN4MASK)
335     return a != 0 && a != 1
336   end
338   private :_ipv4_compat?
340   # Returns a new ipaddr built by converting the native IPv4 address
341   # into an IPv4-mapped IPv6 address.
342   def ipv4_mapped
343     if !ipv4?
344       raise InvalidAddressError, "not an IPv4 address: #{@addr}"
345     end
346     clone = self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
347     clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000)
348     clone
349   end
351   # Returns a new ipaddr built by converting the native IPv4 address
352   # into an IPv4-compatible IPv6 address.
353   def ipv4_compat
354     warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
355     if !ipv4?
356       raise InvalidAddressError, "not an IPv4 address: #{@addr}"
357     end
358     return self.clone.set(@addr, Socket::AF_INET6)
359   end
361   # Returns a new ipaddr built by converting the IPv6 address into a
362   # native IPv4 address.  If the IP address is not an IPv4-mapped or
363   # IPv4-compatible IPv6 address, returns self.
364   def native
365     if !ipv4_mapped? && !_ipv4_compat?
366       return self
367     end
368     return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
369   end
371   # Returns a string for DNS reverse lookup.  It returns a string in
372   # RFC3172 form for an IPv6 address.
373   def reverse
374     case @family
375     when Socket::AF_INET
376       return _reverse + ".in-addr.arpa"
377     when Socket::AF_INET6
378       return ip6_arpa
379     else
380       raise AddressFamilyError, "unsupported address family"
381     end
382   end
384   # Returns a string for DNS reverse lookup compatible with RFC3172.
385   def ip6_arpa
386     if !ipv6?
387       raise InvalidAddressError, "not an IPv6 address: #{@addr}"
388     end
389     return _reverse + ".ip6.arpa"
390   end
392   # Returns a string for DNS reverse lookup compatible with RFC1886.
393   def ip6_int
394     if !ipv6?
395       raise InvalidAddressError, "not an IPv6 address: #{@addr}"
396     end
397     return _reverse + ".ip6.int"
398   end
400   # Returns the successor to the ipaddr.
401   def succ
402     return self.clone.set(@addr + 1, @family)
403   end
405   # Compares the ipaddr with another.
406   def <=>(other)
407     other = coerce_other(other)
408   rescue
409     nil
410   else
411     @addr <=> other.to_i if other.family == @family
412   end
413   include Comparable
415   # Checks equality used by Hash.
416   def eql?(other)
417     return self.class == other.class && self.hash == other.hash && self == other
418   end
420   # Returns a hash value used by Hash, Set, and Array classes
421   def hash
422     return ([@addr, @mask_addr, @zone_id].hash << 1) | (ipv4? ? 0 : 1)
423   end
425   # Creates a Range object for the network address.
426   def to_range
427     self.class.new(begin_addr, @family)..self.class.new(end_addr, @family)
428   end
430   # Returns the prefix length in bits for the ipaddr.
431   def prefix
432     case @family
433     when Socket::AF_INET
434       n = IN4MASK ^ @mask_addr
435       i = 32
436     when Socket::AF_INET6
437       n = IN6MASK ^ @mask_addr
438       i = 128
439     else
440       raise AddressFamilyError, "unsupported address family"
441     end
442     while n.positive?
443       n >>= 1
444       i -= 1
445     end
446     i
447   end
449   # Sets the prefix length in bits
450   def prefix=(prefix)
451     case prefix
452     when Integer
453       mask!(prefix)
454     else
455       raise InvalidPrefixError, "prefix must be an integer"
456     end
457   end
459   # Returns a string containing a human-readable representation of the
460   # ipaddr. ("#<IPAddr: family:address/mask>")
461   def inspect
462     case @family
463     when Socket::AF_INET
464       af = "IPv4"
465     when Socket::AF_INET6
466       af = "IPv6"
467       zone_id = @zone_id.to_s
468     else
469       raise AddressFamilyError, "unsupported address family"
470     end
471     return sprintf("#<%s: %s:%s%s/%s>", self.class.name,
472                    af, _to_string(@addr), zone_id, _to_string(@mask_addr))
473   end
475   # Returns the netmask in string format e.g. 255.255.0.0
476   def netmask
477     _to_string(@mask_addr)
478   end
480   # Returns the wildcard mask in string format e.g. 0.0.255.255
481   def wildcard_mask
482     case @family
483     when Socket::AF_INET
484       mask = IN4MASK ^ @mask_addr
485     when Socket::AF_INET6
486       mask = IN6MASK ^ @mask_addr
487     else
488       raise AddressFamilyError, "unsupported address family"
489     end
491     _to_string(mask)
492   end
494   # Returns the IPv6 zone identifier, if present.
495   # Raises InvalidAddressError if not an IPv6 address.
496   def zone_id
497     if @family == Socket::AF_INET6
498       @zone_id
499     else
500       raise InvalidAddressError, "not an IPv6 address"
501     end
502   end
504   # Returns the IPv6 zone identifier, if present.
505   # Raises InvalidAddressError if not an IPv6 address.
506   def zone_id=(zid)
507     if @family == Socket::AF_INET6
508       case zid
509       when nil, /\A%(\w+)\z/
510         @zone_id = zid
511       else
512         raise InvalidAddressError, "invalid zone identifier for address"
513       end
514     else
515       raise InvalidAddressError, "not an IPv6 address"
516     end
517   end
519   protected
521   def begin_addr
522     @addr & @mask_addr
523   end
525   def end_addr
526     case @family
527     when Socket::AF_INET
528       @addr | (IN4MASK ^ @mask_addr)
529     when Socket::AF_INET6
530       @addr | (IN6MASK ^ @mask_addr)
531     else
532       raise AddressFamilyError, "unsupported address family"
533     end
534   end
536   # Set +@addr+, the internal stored ip address, to given +addr+. The
537   # parameter +addr+ is validated using the first +family+ member,
538   # which is +Socket::AF_INET+ or +Socket::AF_INET6+.
539   def set(addr, *family)
540     case family[0] ? family[0] : @family
541     when Socket::AF_INET
542       if addr < 0 || addr > IN4MASK
543         raise InvalidAddressError, "invalid address: #{addr}"
544       end
545     when Socket::AF_INET6
546       if addr < 0 || addr > IN6MASK
547         raise InvalidAddressError, "invalid address: #{addr}"
548       end
549     else
550       raise AddressFamilyError, "unsupported address family"
551     end
552     @addr = addr
553     if family[0]
554       @family = family[0]
555       if @family == Socket::AF_INET
556         @mask_addr &= IN4MASK
557       end
558     end
559     return self
560   end
562   # Set current netmask to given mask.
563   def mask!(mask)
564     case mask
565     when String
566       case mask
567       when /\A(0|[1-9]+\d*)\z/
568         prefixlen = mask.to_i
569       when /\A\d+\z/
570         raise InvalidPrefixError, "leading zeros in prefix"
571       else
572         m = IPAddr.new(mask)
573         if m.family != @family
574           raise InvalidPrefixError, "address family is not same"
575         end
576         @mask_addr = m.to_i
577         n = @mask_addr ^ m.instance_variable_get(:@mask_addr)
578         unless ((n + 1) & n).zero?
579           raise InvalidPrefixError, "invalid mask #{mask}"
580         end
581         @addr &= @mask_addr
582         return self
583       end
584     else
585       prefixlen = mask
586     end
587     case @family
588     when Socket::AF_INET
589       if prefixlen < 0 || prefixlen > 32
590         raise InvalidPrefixError, "invalid length"
591       end
592       masklen = 32 - prefixlen
593       @mask_addr = ((IN4MASK >> masklen) << masklen)
594     when Socket::AF_INET6
595       if prefixlen < 0 || prefixlen > 128
596         raise InvalidPrefixError, "invalid length"
597       end
598       masklen = 128 - prefixlen
599       @mask_addr = ((IN6MASK >> masklen) << masklen)
600     else
601       raise AddressFamilyError, "unsupported address family"
602     end
603     @addr = ((@addr >> masklen) << masklen)
604     return self
605   end
607   private
609   # Creates a new ipaddr object either from a human readable IP
610   # address representation in string, or from a packed in_addr value
611   # followed by an address family.
612   #
613   # In the former case, the following are the valid formats that will
614   # be recognized: "address", "address/prefixlen" and "address/mask",
615   # where IPv6 address may be enclosed in square brackets (`[' and
616   # `]').  If a prefixlen or a mask is specified, it returns a masked
617   # IP address.  Although the address family is determined
618   # automatically from a specified string, you can specify one
619   # explicitly by the optional second argument.
620   #
621   # Otherwise an IP address is generated from a packed in_addr value
622   # and an address family.
623   #
624   # The IPAddr class defines many methods and operators, and some of
625   # those, such as &, |, include? and ==, accept a string, or a packed
626   # in_addr value instead of an IPAddr object.
627   def initialize(addr = '::', family = Socket::AF_UNSPEC)
628     @mask_addr = nil
629     if !addr.kind_of?(String)
630       case family
631       when Socket::AF_INET, Socket::AF_INET6
632         set(addr.to_i, family)
633         @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
634         return
635       when Socket::AF_UNSPEC
636         raise AddressFamilyError, "address family must be specified"
637       else
638         raise AddressFamilyError, "unsupported address family: #{family}"
639       end
640     end
641     prefix, prefixlen = addr.split('/', 2)
642     if prefix =~ /\A\[(.*)\]\z/i
643       prefix = $1
644       family = Socket::AF_INET6
645     end
646     if prefix =~ /\A(.*)(%\w+)\z/
647       prefix = $1
648       zone_id = $2
649       family = Socket::AF_INET6
650     end
651     # It seems AI_NUMERICHOST doesn't do the job.
652     #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
653     #                  Socket::AI_NUMERICHOST)
654     @addr = @family = nil
655     if family == Socket::AF_UNSPEC || family == Socket::AF_INET
656       @addr = in_addr(prefix)
657       if @addr
658         @family = Socket::AF_INET
659       end
660     end
661     if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
662       @addr = in6_addr(prefix)
663       @family = Socket::AF_INET6
664     end
665     @zone_id = zone_id
666     if family != Socket::AF_UNSPEC && @family != family
667       raise AddressFamilyError, "address family mismatch"
668     end
669     if prefixlen
670       mask!(prefixlen)
671     else
672       @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
673     end
674   end
676   def coerce_other(other)
677     case other
678     when IPAddr
679       other
680     when String
681       self.class.new(other)
682     else
683       self.class.new(other, @family)
684     end
685   end
687   def in_addr(addr)
688     case addr
689     when Array
690       octets = addr
691     else
692       RE_IPV4ADDRLIKE.match?(addr) or return nil
693       octets = addr.split('.')
694     end
695     octets.inject(0) { |i, s|
696       (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}"
697       (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
698       i << 8 | n
699     }
700   end
702   def in6_addr(left)
703     case left
704     when RE_IPV6ADDRLIKE_FULL
705       if $2
706         addr = in_addr($~[2,4])
707         left = $1 + ':'
708       else
709         addr = 0
710       end
711       right = ''
712     when RE_IPV6ADDRLIKE_COMPRESSED
713       if $4
714         left.count(':') <= 6 or raise InvalidAddressError, "invalid address: #{@addr}"
715         addr = in_addr($~[4,4])
716         left = $1
717         right = $3 + '0:0'
718       else
719         left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
720           raise InvalidAddressError, "invalid address: #{@addr}"
721         left = $1
722         right = $2
723         addr = 0
724       end
725     else
726       raise InvalidAddressError, "invalid address: #{@addr}"
727     end
728     l = left.split(':')
729     r = right.split(':')
730     rest = 8 - l.size - r.size
731     if rest < 0
732       return nil
733     end
734     (l + Array.new(rest, '0') + r).inject(0) { |i, s|
735       i << 16 | s.hex
736     } | addr
737   end
739   def addr_mask(addr)
740     case @family
741     when Socket::AF_INET
742       return addr & IN4MASK
743     when Socket::AF_INET6
744       return addr & IN6MASK
745     else
746       raise AddressFamilyError, "unsupported address family"
747     end
748   end
750   def _reverse
751     case @family
752     when Socket::AF_INET
753       return (0..3).map { |i|
754         (@addr >> (8 * i)) & 0xff
755       }.join('.')
756     when Socket::AF_INET6
757       return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
758     else
759       raise AddressFamilyError, "unsupported address family"
760     end
761   end
763   def _to_string(addr)
764     case @family
765     when Socket::AF_INET
766       return (0..3).map { |i|
767         (addr >> (24 - 8 * i)) & 0xff
768       }.join('.')
769     when Socket::AF_INET6
770       return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
771     else
772       raise AddressFamilyError, "unsupported address family"
773     end
774   end
778 unless Socket.const_defined? :AF_INET6
779   class Socket < BasicSocket
780     # IPv6 protocol family
781     AF_INET6 = Object.new.freeze
782   end
784   class << IPSocket
785     private
787     def valid_v6?(addr)
788       case addr
789       when IPAddr::RE_IPV6ADDRLIKE_FULL
790         if $2
791           $~[2,4].all? {|i| i.to_i < 256 }
792         else
793           true
794         end
795       when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
796         if $4
797           addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
798         else
799           addr.count(':') <= 7
800         end
801       else
802         false
803       end
804     end
806     alias getaddress_orig getaddress
808     public
810     # Returns a +String+ based representation of a valid DNS hostname,
811     # IPv4 or IPv6 address.
812     #
813     #   IPSocket.getaddress 'localhost'         #=> "::1"
814     #   IPSocket.getaddress 'broadcasthost'     #=> "255.255.255.255"
815     #   IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
816     #   IPSocket.getaddress 'www.ccc.de'        #=> "2a00:1328:e102:ccc0::122"
817     def getaddress(s)
818       if valid_v6?(s)
819         s
820       else
821         getaddress_orig(s)
822       end
823     end
824   end