rubysl-ipaddr 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -6
- data/lib/rubysl/ipaddr/ipaddr.rb +264 -143
- data/lib/rubysl/ipaddr/version.rb +2 -2
- data/rubysl-ipaddr.gemspec +2 -3
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da2d6bd4f039da4e0148fa2df7c361a7631be02c
|
4
|
+
data.tar.gz: 6c6bf37f901a3cd7f06471e5f700bae06a613349
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 771036121c91fa439f3c934740af4719471dce44df2d45ad5b9f0a310b3814427b91e560f7bb40bae2be283aeb6e0893391a630873ce129d67eb844db9dc1ac2
|
7
|
+
data.tar.gz: 20608b6099a074ce62809a84e4a01e7ed6d4360730b5b24dd2732a363160ede846d5889305a92a2f0d31d7046e3a351f04f0ff20eb5e09af61e44d798bd28318
|
data/.travis.yml
CHANGED
data/lib/rubysl/ipaddr/ipaddr.rb
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
# ipaddr.rb - A class to manipulate an IP address
|
3
3
|
#
|
4
4
|
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
|
5
|
-
# Copyright (c) 2007 Akinori MUSHA <knu@iDaemons.org>.
|
5
|
+
# Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
|
6
6
|
# All rights reserved.
|
7
7
|
#
|
8
8
|
# You can redistribute and/or modify it under the same terms as Ruby.
|
9
9
|
#
|
10
|
-
# $Id
|
10
|
+
# $Id$
|
11
11
|
#
|
12
12
|
# Contact:
|
13
13
|
# - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
|
@@ -17,78 +17,88 @@
|
|
17
17
|
#
|
18
18
|
require 'socket'
|
19
19
|
|
20
|
-
unless Socket.const_defined? "AF_INET6"
|
21
|
-
class Socket
|
22
|
-
AF_INET6 = Object.new
|
23
|
-
end
|
24
|
-
|
25
|
-
class << IPSocket
|
26
|
-
def valid_v4?(addr)
|
27
|
-
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
|
28
|
-
return $~.captures.all? {|i| i.to_i < 256}
|
29
|
-
end
|
30
|
-
return false
|
31
|
-
end
|
32
|
-
|
33
|
-
def valid_v6?(addr)
|
34
|
-
# IPv6 (normal)
|
35
|
-
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
|
36
|
-
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
|
37
|
-
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
|
38
|
-
# IPv6 (IPv4 compat)
|
39
|
-
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
|
40
|
-
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?($')
|
41
|
-
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
|
42
|
-
|
43
|
-
false
|
44
|
-
end
|
45
|
-
|
46
|
-
def valid?(addr)
|
47
|
-
valid_v4?(addr) || valid_v6?(addr)
|
48
|
-
end
|
49
|
-
|
50
|
-
alias getaddress_orig getaddress
|
51
|
-
def getaddress(s)
|
52
|
-
if valid?(s)
|
53
|
-
s
|
54
|
-
elsif /\A[-A-Za-z\d.]+\Z/ =~ s
|
55
|
-
getaddress_orig(s)
|
56
|
-
else
|
57
|
-
raise ArgumentError, "invalid address"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
20
|
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
|
64
21
|
# IPv6 are supported.
|
65
22
|
#
|
66
23
|
# == Example
|
67
24
|
#
|
68
25
|
# require 'ipaddr'
|
69
|
-
#
|
26
|
+
#
|
70
27
|
# ipaddr1 = IPAddr.new "3ffe:505:2::1"
|
71
|
-
#
|
72
|
-
# p ipaddr1
|
73
|
-
#
|
74
|
-
# p ipaddr1.to_s
|
75
|
-
#
|
76
|
-
# ipaddr2 = ipaddr1.mask(48)
|
77
|
-
#
|
78
|
-
# p ipaddr2.to_s
|
79
|
-
#
|
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
|
+
#
|
80
37
|
# ipaddr3 = IPAddr.new "192.168.2.0/24"
|
81
|
-
#
|
82
|
-
# p ipaddr3
|
38
|
+
#
|
39
|
+
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
83
40
|
|
84
41
|
class IPAddr
|
85
42
|
|
43
|
+
# 32 bit mask for IPv4
|
86
44
|
IN4MASK = 0xffffffff
|
45
|
+
# 128 bit mask for IPv4
|
87
46
|
IN6MASK = 0xffffffffffffffffffffffffffffffff
|
47
|
+
# Format string for IPv6
|
88
48
|
IN6FORMAT = (["%.4x"] * 8).join(':')
|
89
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
|
+
|
90
100
|
# Returns the address family of this IP address.
|
91
|
-
|
101
|
+
attr_reader :family
|
92
102
|
|
93
103
|
# Creates a new ipaddr containing the given network byte ordered
|
94
104
|
# string form of an IP address.
|
@@ -105,7 +115,7 @@ class IPAddr
|
|
105
115
|
when 16
|
106
116
|
s = IN6FORMAT % addr.unpack('n8')
|
107
117
|
else
|
108
|
-
raise
|
118
|
+
raise AddressFamilyError, "unsupported address family"
|
109
119
|
end
|
110
120
|
return s
|
111
121
|
end
|
@@ -154,13 +164,13 @@ class IPAddr
|
|
154
164
|
# net1 = IPAddr.new("192.168.2.0/24")
|
155
165
|
# net2 = IPAddr.new("192.168.2.100")
|
156
166
|
# net3 = IPAddr.new("192.168.3.0")
|
157
|
-
# p net1.include?(net2)
|
158
|
-
# p net1.include?(net3)
|
167
|
+
# p net1.include?(net2) #=> true
|
168
|
+
# p net1.include?(net3) #=> false
|
159
169
|
def include?(other)
|
160
170
|
other = coerce_other(other)
|
161
171
|
if ipv4_mapped?
|
162
172
|
if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
|
163
|
-
|
173
|
+
return false
|
164
174
|
end
|
165
175
|
mask_addr = (@mask_addr & IN4MASK)
|
166
176
|
addr = (@addr & IN4MASK)
|
@@ -197,7 +207,7 @@ class IPAddr
|
|
197
207
|
|
198
208
|
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
|
199
209
|
loop do
|
200
|
-
break if str.sub!(/\A0:0:0:0:0:0:0:0\
|
210
|
+
break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
|
201
211
|
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
|
202
212
|
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
|
203
213
|
break if str.sub!(/\b0:0:0:0:0\b/, ':')
|
@@ -208,7 +218,7 @@ class IPAddr
|
|
208
218
|
end
|
209
219
|
str.sub!(/:{3,}/, '::')
|
210
220
|
|
211
|
-
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\
|
221
|
+
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
|
212
222
|
str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
|
213
223
|
end
|
214
224
|
|
@@ -228,10 +238,10 @@ class IPAddr
|
|
228
238
|
return [@addr].pack('N')
|
229
239
|
when Socket::AF_INET6
|
230
240
|
return (0..7).map { |i|
|
231
|
-
|
241
|
+
(@addr >> (112 - 16 * i)) & 0xffff
|
232
242
|
}.pack('n8')
|
233
243
|
else
|
234
|
-
raise "unsupported address family"
|
244
|
+
raise AddressFamilyError, "unsupported address family"
|
235
245
|
end
|
236
246
|
end
|
237
247
|
|
@@ -263,7 +273,7 @@ class IPAddr
|
|
263
273
|
# into an IPv4-mapped IPv6 address.
|
264
274
|
def ipv4_mapped
|
265
275
|
if !ipv4?
|
266
|
-
raise
|
276
|
+
raise InvalidAddressError, "not an IPv4 address"
|
267
277
|
end
|
268
278
|
return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
|
269
279
|
end
|
@@ -272,7 +282,7 @@ class IPAddr
|
|
272
282
|
# into an IPv4-compatible IPv6 address.
|
273
283
|
def ipv4_compat
|
274
284
|
if !ipv4?
|
275
|
-
raise
|
285
|
+
raise InvalidAddressError, "not an IPv4 address"
|
276
286
|
end
|
277
287
|
return self.clone.set(@addr, Socket::AF_INET6)
|
278
288
|
end
|
@@ -296,14 +306,14 @@ class IPAddr
|
|
296
306
|
when Socket::AF_INET6
|
297
307
|
return ip6_arpa
|
298
308
|
else
|
299
|
-
raise "unsupported address family"
|
309
|
+
raise AddressFamilyError, "unsupported address family"
|
300
310
|
end
|
301
311
|
end
|
302
312
|
|
303
313
|
# Returns a string for DNS reverse lookup compatible with RFC3172.
|
304
314
|
def ip6_arpa
|
305
315
|
if !ipv6?
|
306
|
-
raise
|
316
|
+
raise InvalidAddressError, "not an IPv6 address"
|
307
317
|
end
|
308
318
|
return _reverse + ".ip6.arpa"
|
309
319
|
end
|
@@ -311,7 +321,7 @@ class IPAddr
|
|
311
321
|
# Returns a string for DNS reverse lookup compatible with RFC1886.
|
312
322
|
def ip6_int
|
313
323
|
if !ipv6?
|
314
|
-
raise
|
324
|
+
raise InvalidAddressError, "not an IPv6 address"
|
315
325
|
end
|
316
326
|
return _reverse + ".ip6.int"
|
317
327
|
end
|
@@ -331,6 +341,16 @@ class IPAddr
|
|
331
341
|
end
|
332
342
|
include Comparable
|
333
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
|
+
|
334
354
|
# Creates a Range object for the network address.
|
335
355
|
def to_range
|
336
356
|
begin_addr = (@addr & @mask_addr)
|
@@ -341,7 +361,7 @@ class IPAddr
|
|
341
361
|
when Socket::AF_INET6
|
342
362
|
end_addr = (@addr | (IN6MASK ^ @mask_addr))
|
343
363
|
else
|
344
|
-
raise "unsupported address family"
|
364
|
+
raise AddressFamilyError, "unsupported address family"
|
345
365
|
end
|
346
366
|
|
347
367
|
return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
|
@@ -356,26 +376,29 @@ class IPAddr
|
|
356
376
|
when Socket::AF_INET6
|
357
377
|
af = "IPv6"
|
358
378
|
else
|
359
|
-
raise "unsupported address family"
|
379
|
+
raise AddressFamilyError, "unsupported address family"
|
360
380
|
end
|
361
381
|
return sprintf("#<%s: %s:%s/%s>", self.class.name,
|
362
|
-
|
382
|
+
af, _to_string(@addr), _to_string(@mask_addr))
|
363
383
|
end
|
364
384
|
|
365
385
|
protected
|
366
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+.
|
367
390
|
def set(addr, *family)
|
368
391
|
case family[0] ? family[0] : @family
|
369
392
|
when Socket::AF_INET
|
370
393
|
if addr < 0 || addr > IN4MASK
|
371
|
-
|
394
|
+
raise InvalidAddressError, "invalid address"
|
372
395
|
end
|
373
396
|
when Socket::AF_INET6
|
374
397
|
if addr < 0 || addr > IN6MASK
|
375
|
-
|
398
|
+
raise InvalidAddressError, "invalid address"
|
376
399
|
end
|
377
400
|
else
|
378
|
-
raise
|
401
|
+
raise AddressFamilyError, "unsupported address family"
|
379
402
|
end
|
380
403
|
@addr = addr
|
381
404
|
if family[0]
|
@@ -384,18 +407,19 @@ class IPAddr
|
|
384
407
|
return self
|
385
408
|
end
|
386
409
|
|
410
|
+
# Set current netmask to given mask.
|
387
411
|
def mask!(mask)
|
388
412
|
if mask.kind_of?(String)
|
389
413
|
if mask =~ /^\d+$/
|
390
|
-
|
414
|
+
prefixlen = mask.to_i
|
391
415
|
else
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
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
|
399
423
|
end
|
400
424
|
else
|
401
425
|
prefixlen = mask
|
@@ -403,18 +427,18 @@ class IPAddr
|
|
403
427
|
case @family
|
404
428
|
when Socket::AF_INET
|
405
429
|
if prefixlen < 0 || prefixlen > 32
|
406
|
-
|
430
|
+
raise InvalidPrefixError, "invalid length"
|
407
431
|
end
|
408
432
|
masklen = 32 - prefixlen
|
409
433
|
@mask_addr = ((IN4MASK >> masklen) << masklen)
|
410
434
|
when Socket::AF_INET6
|
411
435
|
if prefixlen < 0 || prefixlen > 128
|
412
|
-
|
436
|
+
raise InvalidPrefixError, "invalid length"
|
413
437
|
end
|
414
438
|
masklen = 128 - prefixlen
|
415
439
|
@mask_addr = ((IN6MASK >> masklen) << masklen)
|
416
440
|
else
|
417
|
-
raise "unsupported address family"
|
441
|
+
raise AddressFamilyError, "unsupported address family"
|
418
442
|
end
|
419
443
|
@addr = ((@addr >> masklen) << masklen)
|
420
444
|
return self
|
@@ -425,7 +449,7 @@ class IPAddr
|
|
425
449
|
# Creates a new ipaddr object either from a human readable IP
|
426
450
|
# address representation in string, or from a packed in_addr value
|
427
451
|
# followed by an address family.
|
428
|
-
#
|
452
|
+
#
|
429
453
|
# In the former case, the following are the valid formats that will
|
430
454
|
# be recognized: "address", "address/prefixlen" and "address/mask",
|
431
455
|
# where IPv6 address may be enclosed in square brackets (`[' and
|
@@ -433,8 +457,8 @@ class IPAddr
|
|
433
457
|
# IP address. Although the address family is determined
|
434
458
|
# automatically from a specified string, you can specify one
|
435
459
|
# explicitly by the optional second argument.
|
436
|
-
#
|
437
|
-
# Otherwise an IP
|
460
|
+
#
|
461
|
+
# Otherwise an IP address is generated from a packed in_addr value
|
438
462
|
# and an address family.
|
439
463
|
#
|
440
464
|
# The IPAddr class defines many methods and operators, and some of
|
@@ -448,9 +472,9 @@ class IPAddr
|
|
448
472
|
@mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
|
449
473
|
return
|
450
474
|
when Socket::AF_UNSPEC
|
451
|
-
|
475
|
+
raise AddressFamilyError, "address family must be specified"
|
452
476
|
else
|
453
|
-
|
477
|
+
raise AddressFamilyError, "unsupported address family: #{family}"
|
454
478
|
end
|
455
479
|
end
|
456
480
|
prefix, prefixlen = addr.split('/')
|
@@ -460,17 +484,12 @@ class IPAddr
|
|
460
484
|
end
|
461
485
|
# It seems AI_NUMERICHOST doesn't do the job.
|
462
486
|
#Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
|
463
|
-
#
|
464
|
-
begin
|
465
|
-
IPSocket.getaddress(prefix) # test if address is vaild
|
466
|
-
rescue
|
467
|
-
raise ArgumentError, "invalid address"
|
468
|
-
end
|
487
|
+
# Socket::AI_NUMERICHOST)
|
469
488
|
@addr = @family = nil
|
470
489
|
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
|
471
490
|
@addr = in_addr(prefix)
|
472
491
|
if @addr
|
473
|
-
|
492
|
+
@family = Socket::AF_INET
|
474
493
|
end
|
475
494
|
end
|
476
495
|
if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
|
@@ -478,7 +497,7 @@ class IPAddr
|
|
478
497
|
@family = Socket::AF_INET6
|
479
498
|
end
|
480
499
|
if family != Socket::AF_UNSPEC && @family != family
|
481
|
-
raise
|
500
|
+
raise AddressFamilyError, "address family mismatch"
|
482
501
|
end
|
483
502
|
if prefixlen
|
484
503
|
mask!(prefixlen)
|
@@ -499,26 +518,45 @@ class IPAddr
|
|
499
518
|
end
|
500
519
|
|
501
520
|
def in_addr(addr)
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
521
|
+
case addr
|
522
|
+
when Array
|
523
|
+
octets = addr
|
524
|
+
else
|
525
|
+
m = RE_IPV4ADDRLIKE.match(addr) or return nil
|
526
|
+
octets = m.captures
|
506
527
|
end
|
507
|
-
|
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
|
+
}
|
508
533
|
end
|
509
534
|
|
510
535
|
def in6_addr(left)
|
511
536
|
case left
|
512
|
-
when
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
left, right = $1, $2
|
520
|
-
else
|
537
|
+
when RE_IPV6ADDRLIKE_FULL
|
538
|
+
if $2
|
539
|
+
addr = in_addr($~[2,4])
|
540
|
+
left = $1 + ':'
|
541
|
+
else
|
542
|
+
addr = 0
|
543
|
+
end
|
521
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"
|
522
560
|
end
|
523
561
|
l = left.split(':')
|
524
562
|
r = right.split(':')
|
@@ -526,9 +564,9 @@ class IPAddr
|
|
526
564
|
if rest < 0
|
527
565
|
return nil
|
528
566
|
end
|
529
|
-
|
567
|
+
(l + Array.new(rest, '0') + r).inject(0) { |i, s|
|
530
568
|
i << 16 | s.hex
|
531
|
-
}
|
569
|
+
} | addr
|
532
570
|
end
|
533
571
|
|
534
572
|
def addr_mask(addr)
|
@@ -538,7 +576,7 @@ class IPAddr
|
|
538
576
|
when Socket::AF_INET6
|
539
577
|
return addr & IN6MASK
|
540
578
|
else
|
541
|
-
raise "unsupported address family"
|
579
|
+
raise AddressFamilyError, "unsupported address family"
|
542
580
|
end
|
543
581
|
end
|
544
582
|
|
@@ -546,12 +584,12 @@ class IPAddr
|
|
546
584
|
case @family
|
547
585
|
when Socket::AF_INET
|
548
586
|
return (0..3).map { |i|
|
549
|
-
|
587
|
+
(@addr >> (8 * i)) & 0xff
|
550
588
|
}.join('.')
|
551
589
|
when Socket::AF_INET6
|
552
590
|
return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
|
553
591
|
else
|
554
|
-
raise "unsupported address family"
|
592
|
+
raise AddressFamilyError, "unsupported address family"
|
555
593
|
end
|
556
594
|
end
|
557
595
|
|
@@ -559,17 +597,66 @@ class IPAddr
|
|
559
597
|
case @family
|
560
598
|
when Socket::AF_INET
|
561
599
|
return (0..3).map { |i|
|
562
|
-
|
600
|
+
(addr >> (24 - 8 * i)) & 0xff
|
563
601
|
}.join('.')
|
564
602
|
when Socket::AF_INET6
|
565
603
|
return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
|
566
604
|
else
|
567
|
-
raise "unsupported address family"
|
605
|
+
raise AddressFamilyError, "unsupported address family"
|
568
606
|
end
|
569
607
|
end
|
570
608
|
|
571
609
|
end
|
572
610
|
|
611
|
+
unless 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
|
658
|
+
end
|
659
|
+
|
573
660
|
if $0 == __FILE__
|
574
661
|
eval DATA.read, nil, $0, __LINE__+4
|
575
662
|
end
|
@@ -577,14 +664,20 @@ end
|
|
577
664
|
__END__
|
578
665
|
|
579
666
|
require 'test/unit'
|
580
|
-
require 'test/unit/ui/console/testrunner'
|
581
667
|
|
582
668
|
class TC_IPAddr < Test::Unit::TestCase
|
583
669
|
def test_s_new
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
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
|
+
}
|
588
681
|
}
|
589
682
|
|
590
683
|
a = IPAddr.new
|
@@ -637,19 +730,18 @@ class TC_IPAddr < Test::Unit::TestCase
|
|
637
730
|
assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
|
638
731
|
|
639
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)
|
640
735
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
IPAddr.new(*args)
|
651
|
-
}
|
652
|
-
}
|
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) }
|
653
745
|
end
|
654
746
|
|
655
747
|
def test_s_new_ntoh
|
@@ -710,14 +802,14 @@ class TC_IPAddr < Test::Unit::TestCase
|
|
710
802
|
|
711
803
|
def test_ip6_arpa
|
712
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)
|
713
|
-
assert_raises(
|
805
|
+
assert_raises(IPAddr::InvalidAddressError) {
|
714
806
|
IPAddr.new("192.168.2.1").ip6_arpa
|
715
807
|
}
|
716
808
|
end
|
717
809
|
|
718
810
|
def test_ip6_int
|
719
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)
|
720
|
-
assert_raises(
|
812
|
+
assert_raises(IPAddr::InvalidAddressError) {
|
721
813
|
IPAddr.new("192.168.2.1").ip6_int
|
722
814
|
}
|
723
815
|
end
|
@@ -748,7 +840,7 @@ class TC_Operator < Test::Unit::TestCase
|
|
748
840
|
assert_equal("3ffe:505:2:1::", a.to_s)
|
749
841
|
assert_equal("3ffe:505:2::", @a.to_s)
|
750
842
|
assert_equal("3ffe:505:2:1::",
|
751
|
-
|
843
|
+
(@a | 0x00000000000000010000000000000000).to_s)
|
752
844
|
end
|
753
845
|
|
754
846
|
def test_and
|
@@ -783,7 +875,9 @@ class TC_Operator < Test::Unit::TestCase
|
|
783
875
|
end
|
784
876
|
|
785
877
|
def test_equal
|
786
|
-
assert_equal(true, @a == IPAddr.new("
|
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"))
|
787
881
|
assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
|
788
882
|
assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
|
789
883
|
assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
|
@@ -811,4 +905,31 @@ class TC_Operator < Test::Unit::TestCase
|
|
811
905
|
|
812
906
|
end
|
813
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
|
814
935
|
end
|
data/rubysl-ipaddr.gemspec
CHANGED
@@ -3,7 +3,7 @@ require './lib/rubysl/ipaddr/version'
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |spec|
|
5
5
|
spec.name = "rubysl-ipaddr"
|
6
|
-
spec.version = RubySL::
|
6
|
+
spec.version = RubySL::Ipaddr::VERSION
|
7
7
|
spec.authors = ["Brian Shirai"]
|
8
8
|
spec.email = ["brixen@gmail.com"]
|
9
9
|
spec.description = %q{Ruby standard library ipaddr.}
|
@@ -19,5 +19,4 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.add_development_dependency "bundler", "~> 1.3"
|
20
20
|
spec.add_development_dependency "rake", "~> 10.0"
|
21
21
|
spec.add_development_dependency "mspec", "~> 1.5"
|
22
|
-
|
23
|
-
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubysl-ipaddr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Shirai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.5'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubysl-prettyprint
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ~>
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '1.0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ~>
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '1.0'
|
69
55
|
description: Ruby standard library ipaddr.
|
70
56
|
email:
|
71
57
|
- brixen@gmail.com
|