| 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