Class | IPAddr |
In: |
lib/ipaddr.rb
|
Parent: | Object |
IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and IPv6 are supported.
require 'ipaddr' ipaddr1 = IPAddr.new "3ffe:505:2::1" p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff> p ipaddr1.to_s #=> "3ffe:505:2::1" ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000> p ipaddr2.to_s #=> "3ffe:505:2::" ipaddr3 = IPAddr.new "192.168.2.0/24" p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
IN4MASK | = | 0xffffffff |
IN6MASK | = | 0xffffffffffffffffffffffffffffffff |
IN6FORMAT | = | (["%.4x"] * 8).join(':') |
family | [R] | Returns the address family of this IP address. |
Creates a new ipaddr object either from a human readable IP address representation in string, or from a packed in_addr value followed by an address family.
In the former case, the following are the valid formats that will be recognized: "address", "address/prefixlen" and "address/mask", where IPv6 address may be enclosed in square brackets (`[’ and `]’). If a prefixlen or a mask is specified, it returns a masked IP address. Although the address family is determined automatically from a specified string, you can specify one explicitly by the optional second argument.
Otherwise an IP addess is generated from a packed in_addr value and an address family.
The IPAddr class defines many methods and operators, and some of those, such as &, |, include? and ==, accept a string, or a packed in_addr value instead of an IPAddr object.
# File lib/ipaddr.rb, line 443 443: def initialize(addr = '::', family = Socket::AF_UNSPEC) 444: if !addr.kind_of?(String) 445: case family 446: when Socket::AF_INET, Socket::AF_INET6 447: set(addr.to_i, family) 448: @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK 449: return 450: when Socket::AF_UNSPEC 451: raise ArgumentError, "address family must be specified" 452: else 453: raise ArgumentError, "unsupported address family: #{family}" 454: end 455: end 456: prefix, prefixlen = addr.split('/') 457: if prefix =~ /^\[(.*)\]$/i 458: prefix = $1 459: family = Socket::AF_INET6 460: end 461: # It seems AI_NUMERICHOST doesn't do the job. 462: #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil, 463: # Socket::AI_NUMERICHOST) 464: begin 465: IPSocket.getaddress(prefix) # test if address is vaild 466: rescue 467: raise ArgumentError, "invalid address" 468: end 469: @addr = @family = nil 470: if family == Socket::AF_UNSPEC || family == Socket::AF_INET 471: @addr = in_addr(prefix) 472: if @addr 473: @family = Socket::AF_INET 474: end 475: end 476: if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6) 477: @addr = in6_addr(prefix) 478: @family = Socket::AF_INET6 479: end 480: if family != Socket::AF_UNSPEC && @family != family 481: raise ArgumentError, "address family mismatch" 482: end 483: if prefixlen 484: mask!(prefixlen) 485: else 486: @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK 487: end 488: end
Convert a network byte ordered string form of an IP address into human readable form.
# File lib/ipaddr.rb, line 101 101: def IPAddr::ntop(addr) 102: case addr.size 103: when 4 104: s = addr.unpack('C4').join('.') 105: when 16 106: s = IN6FORMAT % addr.unpack('n8') 107: else 108: raise ArgumentError, "unsupported address family" 109: end 110: return s 111: end
Compares the ipaddr with another.
# File lib/ipaddr.rb, line 325 325: def <=>(other) 326: other = coerce_other(other) 327: 328: return nil if other.family != @family 329: 330: return @addr <=> other.to_i 331: end
Returns true if two ipaddrs are equal.
# File lib/ipaddr.rb, line 139 139: def ==(other) 140: other = coerce_other(other) 141: return @family == other.family && @addr == other.to_i 142: end
Returns a network byte ordered string form of the IP address.
# File lib/ipaddr.rb, line 225 225: def hton 226: case @family 227: when Socket::AF_INET 228: return [@addr].pack('N') 229: when Socket::AF_INET6 230: return (0..7).map { |i| 231: (@addr >> (112 - 16 * i)) & 0xffff 232: }.pack('n8') 233: else 234: raise "unsupported address family" 235: end 236: end
Returns true if the given ipaddr is in the range.
e.g.:
require 'ipaddr' net1 = IPAddr.new("192.168.2.0/24") net2 = IPAddr.new("192.168.2.100") net3 = IPAddr.new("192.168.3.0") p net1.include?(net2) #=> true p net1.include?(net3) #=> false
# File lib/ipaddr.rb, line 159 159: def include?(other) 160: other = coerce_other(other) 161: if ipv4_mapped? 162: if (@mask_addr >> 32) != 0xffffffffffffffffffffffff 163: return false 164: end 165: mask_addr = (@mask_addr & IN4MASK) 166: addr = (@addr & IN4MASK) 167: family = Socket::AF_INET 168: else 169: mask_addr = @mask_addr 170: addr = @addr 171: family = @family 172: end 173: if other.ipv4_mapped? 174: other_addr = (other.to_i & IN4MASK) 175: other_family = Socket::AF_INET 176: else 177: other_addr = other.to_i 178: other_family = other.family 179: end 180: 181: if family != other_family 182: return false 183: end 184: return ((addr & mask_addr) == (other_addr & mask_addr)) 185: end
Returns a string containing a human-readable representation of the ipaddr. ("#<IPAddr: family:address/mask>")
# File lib/ipaddr.rb, line 352 352: def inspect 353: case @family 354: when Socket::AF_INET 355: af = "IPv4" 356: when Socket::AF_INET6 357: af = "IPv6" 358: else 359: raise "unsupported address family" 360: end 361: return sprintf("#<%s: %s:%s/%s>", self.class.name, 362: af, _to_string(@addr), _to_string(@mask_addr)) 363: end
Returns true if the ipaddr is an IPv4 address.
# File lib/ipaddr.rb, line 239 239: def ipv4? 240: return @family == Socket::AF_INET 241: end
Returns true if the ipaddr is an IPv4-compatible IPv6 address.
# File lib/ipaddr.rb, line 254 254: def ipv4_compat? 255: if !ipv6? || (@addr >> 32) != 0 256: return false 257: end 258: a = (@addr & IN4MASK) 259: return a != 0 && a != 1 260: end
Returns a new ipaddr built by converting the native IPv4 address into an IPv4-mapped IPv6 address.
# File lib/ipaddr.rb, line 264 264: def ipv4_mapped 265: if !ipv4? 266: raise ArgumentError, "not an IPv4 address" 267: end 268: return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) 269: end
Returns true if the ipaddr is an IPv4-mapped IPv6 address.
# File lib/ipaddr.rb, line 249 249: def ipv4_mapped? 250: return ipv6? && (@addr >> 32) == 0xffff 251: end
Returns true if the ipaddr is an IPv6 address.
# File lib/ipaddr.rb, line 244 244: def ipv6? 245: return @family == Socket::AF_INET6 246: end
Returns a new ipaddr built by converting the IPv6 address into a native IPv4 address. If the IP address is not an IPv4-mapped or IPv4-compatible IPv6 address, returns self.
# File lib/ipaddr.rb, line 283 283: def native 284: if !ipv4_mapped? && !ipv4_compat? 285: return self 286: end 287: return self.clone.set(@addr & IN4MASK, Socket::AF_INET) 288: end
Returns a string for DNS reverse lookup. It returns a string in RFC3172 form for an IPv6 address.
# File lib/ipaddr.rb, line 292 292: def reverse 293: case @family 294: when Socket::AF_INET 295: return _reverse + ".in-addr.arpa" 296: when Socket::AF_INET6 297: return ip6_arpa 298: else 299: raise "unsupported address family" 300: end 301: end
Returns the successor to the ipaddr.
# File lib/ipaddr.rb, line 320 320: def succ 321: return self.clone.set(@addr + 1, @family) 322: end
Returns the integer representation of the ipaddr.
# File lib/ipaddr.rb, line 189 189: def to_i 190: return @addr 191: end
Creates a Range object for the network address.
# File lib/ipaddr.rb, line 335 335: def to_range 336: begin_addr = (@addr & @mask_addr) 337: 338: case @family 339: when Socket::AF_INET 340: end_addr = (@addr | (IN4MASK ^ @mask_addr)) 341: when Socket::AF_INET6 342: end_addr = (@addr | (IN6MASK ^ @mask_addr)) 343: else 344: raise "unsupported address family" 345: end 346: 347: return clone.set(begin_addr, @family)..clone.set(end_addr, @family) 348: end
Returns a string containing the IP address representation.
# File lib/ipaddr.rb, line 194 194: def to_s 195: str = to_string 196: return str if ipv4? 197: 198: str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1') 199: loop do 200: break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::') 201: break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':') 202: break if str.sub!(/\b0:0:0:0:0:0\b/, ':') 203: break if str.sub!(/\b0:0:0:0:0\b/, ':') 204: break if str.sub!(/\b0:0:0:0\b/, ':') 205: break if str.sub!(/\b0:0:0\b/, ':') 206: break if str.sub!(/\b0:0\b/, ':') 207: break 208: end 209: str.sub!(/:{3,}/, '::') 210: 211: if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\Z/i =~ str 212: str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256) 213: end 214: 215: str 216: end
Returns a string containing the IP address representation in canonical form.
# File lib/ipaddr.rb, line 220 220: def to_string 221: return _to_string(@addr) 222: end
# File lib/ipaddr.rb, line 387 387: def mask!(mask) 388: if mask.kind_of?(String) 389: if mask =~ /^\d+$/ 390: prefixlen = mask.to_i 391: else 392: m = IPAddr.new(mask) 393: if m.family != @family 394: raise ArgumentError, "address family is not same" 395: end 396: @mask_addr = m.to_i 397: @addr &= @mask_addr 398: return self 399: end 400: else 401: prefixlen = mask 402: end 403: case @family 404: when Socket::AF_INET 405: if prefixlen < 0 || prefixlen > 32 406: raise ArgumentError, "invalid length" 407: end 408: masklen = 32 - prefixlen 409: @mask_addr = ((IN4MASK >> masklen) << masklen) 410: when Socket::AF_INET6 411: if prefixlen < 0 || prefixlen > 128 412: raise ArgumentError, "invalid length" 413: end 414: masklen = 128 - prefixlen 415: @mask_addr = ((IN6MASK >> masklen) << masklen) 416: else 417: raise "unsupported address family" 418: end 419: @addr = ((@addr >> masklen) << masklen) 420: return self 421: end
# File lib/ipaddr.rb, line 367 367: def set(addr, *family) 368: case family[0] ? family[0] : @family 369: when Socket::AF_INET 370: if addr < 0 || addr > IN4MASK 371: raise ArgumentError, "invalid address" 372: end 373: when Socket::AF_INET6 374: if addr < 0 || addr > IN6MASK 375: raise ArgumentError, "invalid address" 376: end 377: else 378: raise ArgumentError, "unsupported address family" 379: end 380: @addr = addr 381: if family[0] 382: @family = family[0] 383: end 384: return self 385: end
# File lib/ipaddr.rb, line 545 545: def _reverse 546: case @family 547: when Socket::AF_INET 548: return (0..3).map { |i| 549: (@addr >> (8 * i)) & 0xff 550: }.join('.') 551: when Socket::AF_INET6 552: return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.') 553: else 554: raise "unsupported address family" 555: end 556: end
# File lib/ipaddr.rb, line 558 558: def _to_string(addr) 559: case @family 560: when Socket::AF_INET 561: return (0..3).map { |i| 562: (addr >> (24 - 8 * i)) & 0xff 563: }.join('.') 564: when Socket::AF_INET6 565: return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:')) 566: else 567: raise "unsupported address family" 568: end 569: end
# File lib/ipaddr.rb, line 534 534: def addr_mask(addr) 535: case @family 536: when Socket::AF_INET 537: return addr & IN4MASK 538: when Socket::AF_INET6 539: return addr & IN6MASK 540: else 541: raise "unsupported address family" 542: end 543: end
# File lib/ipaddr.rb, line 490 490: def coerce_other(other) 491: case other 492: when IPAddr 493: other 494: when String 495: self.class.new(other) 496: else 497: self.class.new(other, @family) 498: end 499: end
# File lib/ipaddr.rb, line 510 510: def in6_addr(left) 511: case left 512: when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i 513: return in_addr($1) + 0xffff00000000 514: when /^::(\d+\.\d+\.\d+\.\d+)$/i 515: return in_addr($1) 516: when /[^0-9a-f:]/i 517: raise ArgumentError, "invalid address" 518: when /^(.*)::(.*)$/ 519: left, right = $1, $2 520: else 521: right = '' 522: end 523: l = left.split(':') 524: r = right.split(':') 525: rest = 8 - l.size - r.size 526: if rest < 0 527: return nil 528: end 529: return (l + Array.new(rest, '0') + r).inject(0) { |i, s| 530: i << 16 | s.hex 531: } 532: end