2
# ipaddr.rb - A class to manipulate an IP address
4
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
7
# You can redistribute and/or modify it under the same terms as Ruby.
9
# $Id: ipaddr.rb 11708 2007-02-12 23:01:19Z shyouhei $
15
unless Socket.const_defined? "AF_INET6"
22
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
23
return $~.captures.all? {|i| i.to_i < 256}
30
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
31
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
32
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
34
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
35
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
36
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
42
valid_v4?(addr) || valid_v6?(addr)
45
alias getaddress_orig getaddress
49
elsif /\A[-A-Za-z\d.]+\Z/ =~ s
52
raise ArgumentError, "invalid address"
58
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
65
# ipaddr1 = IPAddr.new "3ffe:505:2::1"
67
# p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
69
# p ipaddr1.to_s #=> "3ffe:505:2::1"
71
# ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
73
# p ipaddr2.to_s #=> "3ffe:505:2::"
75
# ipaddr3 = IPAddr.new "192.168.2.0/24"
77
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
82
IN6MASK = 0xffffffffffffffffffffffffffffffff
83
IN6FORMAT = (["%.4x"] * 8).join(':')
85
# Returns the address family of this IP address.
88
# Creates a new ipaddr containing the given network byte ordered
89
# string form of an IP address.
90
def IPAddr::new_ntoh(addr)
91
return IPAddr.new(IPAddr::ntop(addr))
94
# Convert a network byte ordered string form of an IP address into
95
# human readable form.
96
def IPAddr::ntop(addr)
99
s = addr.unpack('C4').join('.')
101
s = IN6FORMAT % addr.unpack('n8')
103
raise ArgumentError, "unsupported address family"
108
# Returns a new ipaddr built by bitwise AND.
110
return self.clone.set(@addr & other.to_i)
113
# Returns a new ipaddr built by bitwise OR.
115
return self.clone.set(@addr | other.to_i)
118
# Returns a new ipaddr built by bitwise right-shift.
120
return self.clone.set(@addr >> num)
123
# Returns a new ipaddr built by bitwise left shift.
125
return self.clone.set(addr_mask(@addr << num))
128
# Returns a new ipaddr built by bitwise negation.
130
return self.clone.set(addr_mask(~@addr))
133
# Returns true if two ipaddr are equal.
135
if other.kind_of?(IPAddr) && @family != other.family
138
return (@addr == other.to_i)
141
# Returns a new ipaddr built by masking IP address with the given
142
# prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
144
return self.clone.mask!(prefixlen)
147
# Returns true if the given ipaddr is in the range.
151
# net1 = IPAddr.new("192.168.2.0/24")
152
# p net1.include?(IPAddr.new("192.168.2.0")) #=> true
153
# p net1.include?(IPAddr.new("192.168.2.255")) #=> true
154
# p net1.include?(IPAddr.new("192.168.3.0")) #=> false
157
if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
160
mask_addr = (@mask_addr & IN4MASK)
161
addr = (@addr & IN4MASK)
162
family = Socket::AF_INET
164
mask_addr = @mask_addr
168
if other.kind_of?(IPAddr)
169
if other.ipv4_mapped?
170
other_addr = (other.to_i & IN4MASK)
171
other_family = Socket::AF_INET
173
other_addr = other.to_i
174
other_family = other.family
176
else # Not IPAddr - assume integer in same family as us
177
other_addr = other.to_i
178
other_family = family
181
if family != other_family
184
return ((addr & mask_addr) == (other_addr & mask_addr))
188
# Returns the integer representation of the ipaddr.
193
# Returns a string containing the IP address representation.
198
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
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/, ':')
209
str.sub!(/:{3,}/, '::')
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)
218
# Returns a string containing the IP address representation in
221
return _to_string(@addr)
224
# Returns a network byte ordered string form of the IP address.
228
return [@addr].pack('N')
229
when Socket::AF_INET6
230
return (0..7).map { |i|
231
(@addr >> (112 - 16 * i)) & 0xffff
234
raise "unsupported address family"
238
# Returns true if the ipaddr is an IPv4 address.
240
return @family == Socket::AF_INET
243
# Returns true if the ipaddr is an IPv6 address.
245
return @family == Socket::AF_INET6
248
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
250
return ipv6? && (@addr >> 32) == 0xffff
253
# Returns true if the ipaddr is an IPv4-compatible IPv6 address.
255
if !ipv6? || (@addr >> 32) != 0
258
a = (@addr & IN4MASK)
259
return a != 0 && a != 1
262
# Returns a new ipaddr built by converting the native IPv4 address
263
# into an IPv4-mapped IPv6 address.
266
raise ArgumentError, "not an IPv4 address"
268
return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
271
# Returns a new ipaddr built by converting the native IPv4 address
272
# into an IPv4-compatible IPv6 address.
275
raise ArgumentError, "not an IPv4 address"
277
return self.clone.set(@addr, Socket::AF_INET6)
280
# Returns a new ipaddr built by converting the IPv6 address into a
281
# native IPv4 address. If the IP address is not an IPv4-mapped or
282
# IPv4-compatible IPv6 address, returns self.
284
if !ipv4_mapped? && !ipv4_compat?
287
return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
290
# Returns a string for DNS reverse lookup. It returns a string in
291
# RFC3172 form for an IPv6 address.
295
return _reverse + ".in-addr.arpa"
296
when Socket::AF_INET6
299
raise "unsupported address family"
303
# Returns a string for DNS reverse lookup compatible with RFC3172.
306
raise ArgumentError, "not an IPv6 address"
308
return _reverse + ".ip6.arpa"
311
# Returns a string for DNS reverse lookup compatible with RFC1886.
314
raise ArgumentError, "not an IPv6 address"
316
return _reverse + ".ip6.int"
319
# Returns a string containing a human-readable representation of the
320
# ipaddr. ("#<IPAddr: family:address/mask>")
325
when Socket::AF_INET6
328
raise "unsupported address family"
330
return sprintf("#<%s: %s:%s/%s>", self.class.name,
331
af, _to_string(@addr), _to_string(@mask_addr))
336
def set(addr, *family)
337
case family[0] ? family[0] : @family
339
if addr < 0 || addr > IN4MASK
340
raise ArgumentError, "invalid address"
342
when Socket::AF_INET6
343
if addr < 0 || addr > IN6MASK
344
raise ArgumentError, "invalid address"
347
raise ArgumentError, "unsupported address family"
357
if mask.kind_of?(String)
359
prefixlen = mask.to_i
362
if m.family != @family
363
raise ArgumentError, "address family is not same"
374
if prefixlen < 0 || prefixlen > 32
375
raise ArgumentError, "invalid length"
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"
383
masklen = 128 - prefixlen
384
@mask_addr = ((IN6MASK >> masklen) << masklen)
386
raise "unsupported address family"
388
@addr = ((@addr >> masklen) << masklen)
394
# Creates a new ipaddr containing the given human readable form of
395
# an IP address. It also accepts `address/prefixlen' and
396
# `address/mask'. When prefixlen or mask is specified, it returns a
397
# masked ipaddr. IPv6 address may beenclosed with `[' and `]'.
399
# Although an address family is determined automatically from a
400
# specified address, you can specify an address family explicitly by
401
# the optional second argument.
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"
408
@mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
411
prefix, prefixlen = addr.split('/')
412
if prefix =~ /^\[(.*)\]$/i
414
family = Socket::AF_INET6
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)
420
IPSocket.getaddress(prefix) # test if address is vaild
422
raise ArgumentError, "invalid address"
424
@addr = @family = nil
425
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
426
@addr = in_addr(prefix)
428
@family = Socket::AF_INET
431
if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
432
@addr = in6_addr(prefix)
433
@family = Socket::AF_INET6
435
if family != Socket::AF_UNSPEC && @family != family
436
raise ArgumentError, "address family unmatch"
441
@mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
446
if addr =~ /^\d+\.\d+\.\d+\.\d+$/
448
addr.split('.').each { |i|
459
when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
460
return in_addr($1) + 0xffff00000000
461
when /^::(\d+\.\d+\.\d+\.\d+)$/i
464
raise ArgumentError, "invalid address"
472
rest = 8 - l.size - r.size
476
a = [l, Array.new(rest, '0'), r].flatten!
489
when Socket::AF_INET6
492
raise "unsupported address family"
500
return (0..3).map { |i|
501
(@addr >> (8 * i)) & 0xff
503
when Socket::AF_INET6
504
return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
506
raise "unsupported address family"
513
return (0..3).map { |i|
514
(addr >> (24 - 8 * i)) & 0xff
516
when Socket::AF_INET6
517
return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
519
raise "unsupported address family"
526
eval DATA.read, nil, $0, __LINE__+4
532
require 'test/unit/ui/console/testrunner'
534
class TC_IPAddr < Test::Unit::TestCase
536
assert_nothing_raised {
537
IPAddr.new("3FFE:505:ffff::/48")
538
IPAddr.new("0:0:0:1::")
539
IPAddr.new("2001:200:300::/48")
543
assert_equal("::", a.to_s)
544
assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
545
assert_equal(Socket::AF_INET6, a.family)
547
a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
548
assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
549
assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
550
assert_equal(Socket::AF_INET6, a.family)
552
a = IPAddr.new("3ffe:505:2::/48")
553
assert_equal("3ffe:505:2::", a.to_s)
554
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
555
assert_equal(Socket::AF_INET6, a.family)
556
assert_equal(false, a.ipv4?)
557
assert_equal(true, a.ipv6?)
558
assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
560
a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
561
assert_equal("3ffe:505:2::", a.to_s)
562
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
563
assert_equal(Socket::AF_INET6, a.family)
565
a = IPAddr.new("0.0.0.0")
566
assert_equal("0.0.0.0", a.to_s)
567
assert_equal("0.0.0.0", a.to_string)
568
assert_equal(Socket::AF_INET, a.family)
570
a = IPAddr.new("192.168.1.2")
571
assert_equal("192.168.1.2", a.to_s)
572
assert_equal("192.168.1.2", a.to_string)
573
assert_equal(Socket::AF_INET, a.family)
574
assert_equal(true, a.ipv4?)
575
assert_equal(false, a.ipv6?)
577
a = IPAddr.new("192.168.1.2/24")
578
assert_equal("192.168.1.0", a.to_s)
579
assert_equal("192.168.1.0", a.to_string)
580
assert_equal(Socket::AF_INET, a.family)
581
assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
583
a = IPAddr.new("192.168.1.2/255.255.255.0")
584
assert_equal("192.168.1.0", a.to_s)
585
assert_equal("192.168.1.0", a.to_string)
586
assert_equal(Socket::AF_INET, a.family)
588
assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
589
assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
591
assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
595
["::1/255.255.255.0"],
596
["::1:192.168.1.2/120"],
597
[IPAddr.new("::1").to_i],
598
["::ffff:192.168.1.2/120", Socket::AF_INET],
599
["[192.168.1.2]/120"],
601
assert_raises(ArgumentError) {
609
IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
610
addr += sprintf("%02x", c)
612
assert_equal("123456789abcdef0123456789abcdef0", addr)
614
IPAddr.new("123.45.67.89").hton.each_byte { |c|
615
addr += sprintf("%02x", c)
617
assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
618
a = IPAddr.new("3ffe:505:2::")
619
assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
620
a = IPAddr.new("192.168.2.1")
621
assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
625
a = IPAddr.new("::192.168.1.2")
626
assert_equal("::192.168.1.2", a.to_s)
627
assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
628
assert_equal(Socket::AF_INET6, a.family)
629
assert_equal(true, a.ipv4_compat?)
631
assert_equal("192.168.1.2", b.to_s)
632
assert_equal(Socket::AF_INET, b.family)
633
assert_equal(false, b.ipv4_compat?)
635
a = IPAddr.new("192.168.1.2")
637
assert_equal("::192.168.1.2", b.to_s)
638
assert_equal(Socket::AF_INET6, b.family)
642
a = IPAddr.new("::ffff:192.168.1.2")
643
assert_equal("::ffff:192.168.1.2", a.to_s)
644
assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
645
assert_equal(Socket::AF_INET6, a.family)
646
assert_equal(true, a.ipv4_mapped?)
648
assert_equal("192.168.1.2", b.to_s)
649
assert_equal(Socket::AF_INET, b.family)
650
assert_equal(false, b.ipv4_mapped?)
652
a = IPAddr.new("192.168.1.2")
654
assert_equal("::ffff:192.168.1.2", b.to_s)
655
assert_equal(Socket::AF_INET6, b.family)
659
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)
660
assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
664
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)
665
assert_raises(ArgumentError) {
666
IPAddr.new("192.168.2.1").ip6_arpa
671
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)
672
assert_raises(ArgumentError) {
673
IPAddr.new("192.168.2.1").ip6_int
678
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
679
assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
683
class TC_Operator < Test::Unit::TestCase
685
IN6MASK32 = "ffff:ffff::"
686
IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
689
@in6_addr_any = IPAddr.new()
690
@a = IPAddr.new("3ffe:505:2::/48")
691
@b = IPAddr.new("0:0:0:1::")
692
@c = IPAddr.new(IN6MASK32)
697
assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
700
assert_equal("3ffe:505:2:1::", a.to_s)
701
assert_equal("3ffe:505:2::", @a.to_s)
702
assert_equal("3ffe:505:2:1::",
703
(@a | 0x00000000000000010000000000000000).to_s)
707
assert_equal("3ffe:505::", (@a & @c).to_s)
710
assert_equal("3ffe:505::", a.to_s)
711
assert_equal("3ffe:505:2::", @a.to_s)
712
assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
716
assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
719
assert_equal("0:3ffe:505:2::", a.to_s)
720
assert_equal("3ffe:505:2::", @a.to_s)
724
assert_equal("505:2::", (@a << 16).to_s)
727
assert_equal("505:2::", a.to_s)
728
assert_equal("3ffe:505:2::", @a.to_s)
733
assert_equal(IN6MASK128, a.to_s)
734
assert_equal("::", @in6_addr_any.to_s)
738
assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
739
assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
740
assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
741
assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
746
assert_equal("3ffe:505::", a.to_s)
747
assert_equal("3ffe:505:2::", @a.to_s)
751
assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
752
assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
753
assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
754
net1 = IPAddr.new("192.168.2.0/24")
755
assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
756
assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
757
assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
758
# test with integer parameter
759
int = (192 << 24) + (168 << 16) + (2 << 8) + 13
761
assert_equal(true, net1.include?(int))
762
assert_equal(false, net1.include?(int+255))