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