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