| 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 containing the given human readable form of an IP address. It also accepts `address/prefixlen’ and `address/mask’. When prefixlen or mask is specified, it returns a masked ipaddr. IPv6 address may beenclosed with `[’ and `]’.
Although an address family is determined automatically from a specified address, you can specify an address family explicitly by the optional second argument.
# File lib/ipaddr.rb, line 402
402: def initialize(addr = '::', family = Socket::AF_UNSPEC)
403: if !addr.kind_of?(String)
404: if family != Socket::AF_INET6 && family != Socket::AF_INET
405: raise ArgumentError, "unsupported address family"
406: end
407: set(addr, family)
408: @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
409: return
410: end
411: prefix, prefixlen = addr.split('/')
412: if prefix =~ /^\[(.*)\]$/i
413: prefix = $1
414: family = Socket::AF_INET6
415: end
416: # It seems AI_NUMERICHOST doesn't do the job.
417: #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
418: # Socket::AI_NUMERICHOST)
419: begin
420: IPSocket.getaddress(prefix) # test if address is vaild
421: rescue
422: raise ArgumentError, "invalid address"
423: end
424: @addr = @family = nil
425: if family == Socket::AF_UNSPEC || family == Socket::AF_INET
426: @addr = in_addr(prefix)
427: if @addr
428: @family = Socket::AF_INET
429: end
430: end
431: if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
432: @addr = in6_addr(prefix)
433: @family = Socket::AF_INET6
434: end
435: if family != Socket::AF_UNSPEC && @family != family
436: raise ArgumentError, "address family unmatch"
437: end
438: if prefixlen
439: mask!(prefixlen)
440: else
441: @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
442: end
443: end
Convert a network byte ordered string form of an IP address into human readable form.
# File lib/ipaddr.rb, line 96
96: def IPAddr::ntop(addr)
97: case addr.size
98: when 4
99: s = addr.unpack('C4').join('.')
100: when 16
101: s = IN6FORMAT % addr.unpack('n8')
102: else
103: raise ArgumentError, "unsupported address family"
104: end
105: return s
106: end
Returns true if two ipaddr are equal.
# File lib/ipaddr.rb, line 134
134: def ==(other)
135: if other.kind_of?(IPAddr) && @family != other.family
136: return false
137: end
138: return (@addr == other.to_i)
139: 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")
p net1.include?(IPAddr.new("192.168.2.0")) #=> true
p net1.include?(IPAddr.new("192.168.2.255")) #=> true
p net1.include?(IPAddr.new("192.168.3.0")) #=> false
# File lib/ipaddr.rb, line 155
155: def include?(other)
156: if ipv4_mapped?
157: if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
158: return false
159: end
160: mask_addr = (@mask_addr & IN4MASK)
161: addr = (@addr & IN4MASK)
162: family = Socket::AF_INET
163: else
164: mask_addr = @mask_addr
165: addr = @addr
166: family = @family
167: end
168: if other.kind_of?(IPAddr)
169: if other.ipv4_mapped?
170: other_addr = (other.to_i & IN4MASK)
171: other_family = Socket::AF_INET
172: else
173: other_addr = other.to_i
174: other_family = other.family
175: end
176: else # Not IPAddr - assume integer in same family as us
177: other_addr = other.to_i
178: other_family = 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 321
321: def inspect
322: case @family
323: when Socket::AF_INET
324: af = "IPv4"
325: when Socket::AF_INET6
326: af = "IPv6"
327: else
328: raise "unsupported address family"
329: end
330: return sprintf("#<%s: %s:%s/%s>", self.class.name,
331: af, _to_string(@addr), _to_string(@mask_addr))
332: 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 integer representation of the ipaddr.
# File lib/ipaddr.rb, line 189
189: def to_i
190: return @addr
191: 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 356
356: def mask!(mask)
357: if mask.kind_of?(String)
358: if mask =~ /^\d+$/
359: prefixlen = mask.to_i
360: else
361: m = IPAddr.new(mask)
362: if m.family != @family
363: raise ArgumentError, "address family is not same"
364: end
365: @mask_addr = m.to_i
366: @addr &= @mask_addr
367: return self
368: end
369: else
370: prefixlen = mask
371: end
372: case @family
373: when Socket::AF_INET
374: if prefixlen < 0 || prefixlen > 32
375: raise ArgumentError, "invalid length"
376: end
377: masklen = 32 - prefixlen
378: @mask_addr = ((IN4MASK >> masklen) << masklen)
379: when Socket::AF_INET6
380: if prefixlen < 0 || prefixlen > 128
381: raise ArgumentError, "invalid length"
382: end
383: masklen = 128 - prefixlen
384: @mask_addr = ((IN6MASK >> masklen) << masklen)
385: else
386: raise "unsupported address family"
387: end
388: @addr = ((@addr >> masklen) << masklen)
389: return self
390: end
# File lib/ipaddr.rb, line 336
336: def set(addr, *family)
337: case family[0] ? family[0] : @family
338: when Socket::AF_INET
339: if addr < 0 || addr > IN4MASK
340: raise ArgumentError, "invalid address"
341: end
342: when Socket::AF_INET6
343: if addr < 0 || addr > IN6MASK
344: raise ArgumentError, "invalid address"
345: end
346: else
347: raise ArgumentError, "unsupported address family"
348: end
349: @addr = addr
350: if family[0]
351: @family = family[0]
352: end
353: return self
354: end
# File lib/ipaddr.rb, line 497
497: def _reverse
498: case @family
499: when Socket::AF_INET
500: return (0..3).map { |i|
501: (@addr >> (8 * i)) & 0xff
502: }.join('.')
503: when Socket::AF_INET6
504: return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
505: else
506: raise "unsupported address family"
507: end
508: end
# File lib/ipaddr.rb, line 510
510: def _to_string(addr)
511: case @family
512: when Socket::AF_INET
513: return (0..3).map { |i|
514: (addr >> (24 - 8 * i)) & 0xff
515: }.join('.')
516: when Socket::AF_INET6
517: return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
518: else
519: raise "unsupported address family"
520: end
521: end
# File lib/ipaddr.rb, line 485
485: def addr_mask(addr)
486: case @family
487: when Socket::AF_INET
488: addr &= IN4MASK
489: when Socket::AF_INET6
490: addr &= IN6MASK
491: else
492: raise "unsupported address family"
493: end
494: return addr
495: end
# File lib/ipaddr.rb, line 457
457: def in6_addr(left)
458: case left
459: when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
460: return in_addr($1) + 0xffff00000000
461: when /^::(\d+\.\d+\.\d+\.\d+)$/i
462: return in_addr($1)
463: when /[^0-9a-f:]/i
464: raise ArgumentError, "invalid address"
465: when /^(.*)::(.*)$/
466: left, right = $1, $2
467: else
468: right = ''
469: end
470: l = left.split(':')
471: r = right.split(':')
472: rest = 8 - l.size - r.size
473: if rest < 0
474: return nil
475: end
476: a = [l, Array.new(rest, '0'), r].flatten!
477: n = 0
478: a.each { |i|
479: n <<= 16
480: n += i.hex
481: }
482: return n
483: end