rubysl-ipaddr 1.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 +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/ipaddr.rb +1 -0
- data/lib/rubysl/ipaddr.rb +2 -0
- data/lib/rubysl/ipaddr/ipaddr.rb +814 -0
- data/lib/rubysl/ipaddr/version.rb +5 -0
- data/rubysl-ipaddr.gemspec +23 -0
- data/spec/hton_spec.rb +29 -0
- data/spec/ipv4_conversion_spec.rb +45 -0
- data/spec/new_spec.rb +94 -0
- data/spec/operator_spec.rb +79 -0
- data/spec/reverse_spec.rb +26 -0
- data/spec/to_s_spec.rb +19 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 61a9688ec7dfabdf5ce11445ebc70a24a953a1a2
|
4
|
+
data.tar.gz: 6b81ae1ea4c64b3ebb45277958b80c21fcf4aa61
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 65e6dcee1a31bf2c6144874bf1c8b597027bdd38a63122f1f4f873fc394dfe4d7ab77afbc39c7d94489cddd0b8a82bdfcd22fc00d8490126218f5c2e05fb412f
|
7
|
+
data.tar.gz: 3b119ec1727908dc3e5a87d57fd0b5b64c837a2d3fa4f816342f3aa763bb6ddee989dc495c5f3843496a3d2c20f295bfac0d79795952568a1a4651c8c44cf152
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2013, Brian Shirai
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
3. Neither the name of the library nor the names of its contributors may be
|
13
|
+
used to endorse or promote products derived from this software without
|
14
|
+
specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
|
20
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
22
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
23
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
24
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
25
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Rubysl::Ipaddr
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rubysl-ipaddr'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rubysl-ipaddr
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/ipaddr.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "rubysl/ipaddr"
|
@@ -0,0 +1,814 @@
|
|
1
|
+
#
|
2
|
+
# ipaddr.rb - A class to manipulate an IP address
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
|
5
|
+
# Copyright (c) 2007 Akinori MUSHA <knu@iDaemons.org>.
|
6
|
+
# All rights reserved.
|
7
|
+
#
|
8
|
+
# You can redistribute and/or modify it under the same terms as Ruby.
|
9
|
+
#
|
10
|
+
# $Id: ipaddr.rb 18049 2008-07-12 15:08:29Z shyouhei $
|
11
|
+
#
|
12
|
+
# Contact:
|
13
|
+
# - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
|
14
|
+
#
|
15
|
+
# TODO:
|
16
|
+
# - scope_id support
|
17
|
+
#
|
18
|
+
require 'socket'
|
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
|
+
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
|
64
|
+
# IPv6 are supported.
|
65
|
+
#
|
66
|
+
# == Example
|
67
|
+
#
|
68
|
+
# require 'ipaddr'
|
69
|
+
#
|
70
|
+
# ipaddr1 = IPAddr.new "3ffe:505:2::1"
|
71
|
+
#
|
72
|
+
# p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
|
73
|
+
#
|
74
|
+
# p ipaddr1.to_s #=> "3ffe:505:2::1"
|
75
|
+
#
|
76
|
+
# ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
|
77
|
+
#
|
78
|
+
# p ipaddr2.to_s #=> "3ffe:505:2::"
|
79
|
+
#
|
80
|
+
# ipaddr3 = IPAddr.new "192.168.2.0/24"
|
81
|
+
#
|
82
|
+
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
83
|
+
|
84
|
+
class IPAddr
|
85
|
+
|
86
|
+
IN4MASK = 0xffffffff
|
87
|
+
IN6MASK = 0xffffffffffffffffffffffffffffffff
|
88
|
+
IN6FORMAT = (["%.4x"] * 8).join(':')
|
89
|
+
|
90
|
+
# Returns the address family of this IP address.
|
91
|
+
attr :family
|
92
|
+
|
93
|
+
# Creates a new ipaddr containing the given network byte ordered
|
94
|
+
# string form of an IP address.
|
95
|
+
def IPAddr::new_ntoh(addr)
|
96
|
+
return IPAddr.new(IPAddr::ntop(addr))
|
97
|
+
end
|
98
|
+
|
99
|
+
# Convert a network byte ordered string form of an IP address into
|
100
|
+
# human readable form.
|
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
|
112
|
+
|
113
|
+
# Returns a new ipaddr built by bitwise AND.
|
114
|
+
def &(other)
|
115
|
+
return self.clone.set(@addr & coerce_other(other).to_i)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a new ipaddr built by bitwise OR.
|
119
|
+
def |(other)
|
120
|
+
return self.clone.set(@addr | coerce_other(other).to_i)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns a new ipaddr built by bitwise right-shift.
|
124
|
+
def >>(num)
|
125
|
+
return self.clone.set(@addr >> num)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns a new ipaddr built by bitwise left shift.
|
129
|
+
def <<(num)
|
130
|
+
return self.clone.set(addr_mask(@addr << num))
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns a new ipaddr built by bitwise negation.
|
134
|
+
def ~
|
135
|
+
return self.clone.set(addr_mask(~@addr))
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns true if two ipaddrs are equal.
|
139
|
+
def ==(other)
|
140
|
+
other = coerce_other(other)
|
141
|
+
return @family == other.family && @addr == other.to_i
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a new ipaddr built by masking IP address with the given
|
145
|
+
# prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
|
146
|
+
def mask(prefixlen)
|
147
|
+
return self.clone.mask!(prefixlen)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns true if the given ipaddr is in the range.
|
151
|
+
#
|
152
|
+
# e.g.:
|
153
|
+
# require 'ipaddr'
|
154
|
+
# net1 = IPAddr.new("192.168.2.0/24")
|
155
|
+
# net2 = IPAddr.new("192.168.2.100")
|
156
|
+
# net3 = IPAddr.new("192.168.3.0")
|
157
|
+
# p net1.include?(net2) #=> true
|
158
|
+
# p net1.include?(net3) #=> false
|
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
|
186
|
+
alias === include?
|
187
|
+
|
188
|
+
# Returns the integer representation of the ipaddr.
|
189
|
+
def to_i
|
190
|
+
return @addr
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns a string containing the IP address representation.
|
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
|
217
|
+
|
218
|
+
# Returns a string containing the IP address representation in
|
219
|
+
# canonical form.
|
220
|
+
def to_string
|
221
|
+
return _to_string(@addr)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Returns a network byte ordered string form of the IP address.
|
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
|
237
|
+
|
238
|
+
# Returns true if the ipaddr is an IPv4 address.
|
239
|
+
def ipv4?
|
240
|
+
return @family == Socket::AF_INET
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns true if the ipaddr is an IPv6 address.
|
244
|
+
def ipv6?
|
245
|
+
return @family == Socket::AF_INET6
|
246
|
+
end
|
247
|
+
|
248
|
+
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
|
249
|
+
def ipv4_mapped?
|
250
|
+
return ipv6? && (@addr >> 32) == 0xffff
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns true if the ipaddr is an IPv4-compatible IPv6 address.
|
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
|
261
|
+
|
262
|
+
# Returns a new ipaddr built by converting the native IPv4 address
|
263
|
+
# into an IPv4-mapped IPv6 address.
|
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
|
270
|
+
|
271
|
+
# Returns a new ipaddr built by converting the native IPv4 address
|
272
|
+
# into an IPv4-compatible IPv6 address.
|
273
|
+
def ipv4_compat
|
274
|
+
if !ipv4?
|
275
|
+
raise ArgumentError, "not an IPv4 address"
|
276
|
+
end
|
277
|
+
return self.clone.set(@addr, Socket::AF_INET6)
|
278
|
+
end
|
279
|
+
|
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.
|
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
|
289
|
+
|
290
|
+
# Returns a string for DNS reverse lookup. It returns a string in
|
291
|
+
# RFC3172 form for an IPv6 address.
|
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
|
302
|
+
|
303
|
+
# Returns a string for DNS reverse lookup compatible with RFC3172.
|
304
|
+
def ip6_arpa
|
305
|
+
if !ipv6?
|
306
|
+
raise ArgumentError, "not an IPv6 address"
|
307
|
+
end
|
308
|
+
return _reverse + ".ip6.arpa"
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns a string for DNS reverse lookup compatible with RFC1886.
|
312
|
+
def ip6_int
|
313
|
+
if !ipv6?
|
314
|
+
raise ArgumentError, "not an IPv6 address"
|
315
|
+
end
|
316
|
+
return _reverse + ".ip6.int"
|
317
|
+
end
|
318
|
+
|
319
|
+
# Returns the successor to the ipaddr.
|
320
|
+
def succ
|
321
|
+
return self.clone.set(@addr + 1, @family)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Compares the ipaddr with another.
|
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
|
332
|
+
include Comparable
|
333
|
+
|
334
|
+
# Creates a Range object for the network address.
|
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
|
349
|
+
|
350
|
+
# Returns a string containing a human-readable representation of the
|
351
|
+
# ipaddr. ("#<IPAddr: family:address/mask>")
|
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
|
364
|
+
|
365
|
+
protected
|
366
|
+
|
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
|
386
|
+
|
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
|
422
|
+
|
423
|
+
private
|
424
|
+
|
425
|
+
# Creates a new ipaddr object either from a human readable IP
|
426
|
+
# address representation in string, or from a packed in_addr value
|
427
|
+
# followed by an address family.
|
428
|
+
#
|
429
|
+
# In the former case, the following are the valid formats that will
|
430
|
+
# be recognized: "address", "address/prefixlen" and "address/mask",
|
431
|
+
# where IPv6 address may be enclosed in square brackets (`[' and
|
432
|
+
# `]'). If a prefixlen or a mask is specified, it returns a masked
|
433
|
+
# IP address. Although the address family is determined
|
434
|
+
# automatically from a specified string, you can specify one
|
435
|
+
# explicitly by the optional second argument.
|
436
|
+
#
|
437
|
+
# Otherwise an IP addess is generated from a packed in_addr value
|
438
|
+
# and an address family.
|
439
|
+
#
|
440
|
+
# The IPAddr class defines many methods and operators, and some of
|
441
|
+
# those, such as &, |, include? and ==, accept a string, or a packed
|
442
|
+
# in_addr value instead of an IPAddr object.
|
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
|
489
|
+
|
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
|
500
|
+
|
501
|
+
def in_addr(addr)
|
502
|
+
if addr =~ /^\d+\.\d+\.\d+\.\d+$/
|
503
|
+
return addr.split('.').inject(0) { |i, s|
|
504
|
+
i << 8 | s.to_i
|
505
|
+
}
|
506
|
+
end
|
507
|
+
return nil
|
508
|
+
end
|
509
|
+
|
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
|
533
|
+
|
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
|
544
|
+
|
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
|
557
|
+
|
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
|
570
|
+
|
571
|
+
end
|
572
|
+
|
573
|
+
if $0 == __FILE__
|
574
|
+
eval DATA.read, nil, $0, __LINE__+4
|
575
|
+
end
|
576
|
+
|
577
|
+
__END__
|
578
|
+
|
579
|
+
require 'test/unit'
|
580
|
+
require 'test/unit/ui/console/testrunner'
|
581
|
+
|
582
|
+
class TC_IPAddr < Test::Unit::TestCase
|
583
|
+
def test_s_new
|
584
|
+
assert_nothing_raised {
|
585
|
+
IPAddr.new("3FFE:505:ffff::/48")
|
586
|
+
IPAddr.new("0:0:0:1::")
|
587
|
+
IPAddr.new("2001:200:300::/48")
|
588
|
+
}
|
589
|
+
|
590
|
+
a = IPAddr.new
|
591
|
+
assert_equal("::", a.to_s)
|
592
|
+
assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
|
593
|
+
assert_equal(Socket::AF_INET6, a.family)
|
594
|
+
|
595
|
+
a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
|
596
|
+
assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
|
597
|
+
assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
|
598
|
+
assert_equal(Socket::AF_INET6, a.family)
|
599
|
+
|
600
|
+
a = IPAddr.new("3ffe:505:2::/48")
|
601
|
+
assert_equal("3ffe:505:2::", a.to_s)
|
602
|
+
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
|
603
|
+
assert_equal(Socket::AF_INET6, a.family)
|
604
|
+
assert_equal(false, a.ipv4?)
|
605
|
+
assert_equal(true, a.ipv6?)
|
606
|
+
assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
|
607
|
+
|
608
|
+
a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
|
609
|
+
assert_equal("3ffe:505:2::", a.to_s)
|
610
|
+
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
|
611
|
+
assert_equal(Socket::AF_INET6, a.family)
|
612
|
+
|
613
|
+
a = IPAddr.new("0.0.0.0")
|
614
|
+
assert_equal("0.0.0.0", a.to_s)
|
615
|
+
assert_equal("0.0.0.0", a.to_string)
|
616
|
+
assert_equal(Socket::AF_INET, a.family)
|
617
|
+
|
618
|
+
a = IPAddr.new("192.168.1.2")
|
619
|
+
assert_equal("192.168.1.2", a.to_s)
|
620
|
+
assert_equal("192.168.1.2", a.to_string)
|
621
|
+
assert_equal(Socket::AF_INET, a.family)
|
622
|
+
assert_equal(true, a.ipv4?)
|
623
|
+
assert_equal(false, a.ipv6?)
|
624
|
+
|
625
|
+
a = IPAddr.new("192.168.1.2/24")
|
626
|
+
assert_equal("192.168.1.0", a.to_s)
|
627
|
+
assert_equal("192.168.1.0", a.to_string)
|
628
|
+
assert_equal(Socket::AF_INET, a.family)
|
629
|
+
assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
|
630
|
+
|
631
|
+
a = IPAddr.new("192.168.1.2/255.255.255.0")
|
632
|
+
assert_equal("192.168.1.0", a.to_s)
|
633
|
+
assert_equal("192.168.1.0", a.to_string)
|
634
|
+
assert_equal(Socket::AF_INET, a.family)
|
635
|
+
|
636
|
+
assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
|
637
|
+
assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
|
638
|
+
|
639
|
+
assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
|
640
|
+
|
641
|
+
[
|
642
|
+
["fe80::1%fxp0"],
|
643
|
+
["::1/255.255.255.0"],
|
644
|
+
["::1:192.168.1.2/120"],
|
645
|
+
[IPAddr.new("::1").to_i],
|
646
|
+
["::ffff:192.168.1.2/120", Socket::AF_INET],
|
647
|
+
["[192.168.1.2]/120"],
|
648
|
+
].each { |args|
|
649
|
+
assert_raises(ArgumentError) {
|
650
|
+
IPAddr.new(*args)
|
651
|
+
}
|
652
|
+
}
|
653
|
+
end
|
654
|
+
|
655
|
+
def test_s_new_ntoh
|
656
|
+
addr = ''
|
657
|
+
IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
|
658
|
+
addr += sprintf("%02x", c)
|
659
|
+
}
|
660
|
+
assert_equal("123456789abcdef0123456789abcdef0", addr)
|
661
|
+
addr = ''
|
662
|
+
IPAddr.new("123.45.67.89").hton.each_byte { |c|
|
663
|
+
addr += sprintf("%02x", c)
|
664
|
+
}
|
665
|
+
assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
|
666
|
+
a = IPAddr.new("3ffe:505:2::")
|
667
|
+
assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
|
668
|
+
a = IPAddr.new("192.168.2.1")
|
669
|
+
assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
|
670
|
+
end
|
671
|
+
|
672
|
+
def test_ipv4_compat
|
673
|
+
a = IPAddr.new("::192.168.1.2")
|
674
|
+
assert_equal("::192.168.1.2", a.to_s)
|
675
|
+
assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
|
676
|
+
assert_equal(Socket::AF_INET6, a.family)
|
677
|
+
assert_equal(true, a.ipv4_compat?)
|
678
|
+
b = a.native
|
679
|
+
assert_equal("192.168.1.2", b.to_s)
|
680
|
+
assert_equal(Socket::AF_INET, b.family)
|
681
|
+
assert_equal(false, b.ipv4_compat?)
|
682
|
+
|
683
|
+
a = IPAddr.new("192.168.1.2")
|
684
|
+
b = a.ipv4_compat
|
685
|
+
assert_equal("::192.168.1.2", b.to_s)
|
686
|
+
assert_equal(Socket::AF_INET6, b.family)
|
687
|
+
end
|
688
|
+
|
689
|
+
def test_ipv4_mapped
|
690
|
+
a = IPAddr.new("::ffff:192.168.1.2")
|
691
|
+
assert_equal("::ffff:192.168.1.2", a.to_s)
|
692
|
+
assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
|
693
|
+
assert_equal(Socket::AF_INET6, a.family)
|
694
|
+
assert_equal(true, a.ipv4_mapped?)
|
695
|
+
b = a.native
|
696
|
+
assert_equal("192.168.1.2", b.to_s)
|
697
|
+
assert_equal(Socket::AF_INET, b.family)
|
698
|
+
assert_equal(false, b.ipv4_mapped?)
|
699
|
+
|
700
|
+
a = IPAddr.new("192.168.1.2")
|
701
|
+
b = a.ipv4_mapped
|
702
|
+
assert_equal("::ffff:192.168.1.2", b.to_s)
|
703
|
+
assert_equal(Socket::AF_INET6, b.family)
|
704
|
+
end
|
705
|
+
|
706
|
+
def test_reverse
|
707
|
+
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)
|
708
|
+
assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
|
709
|
+
end
|
710
|
+
|
711
|
+
def test_ip6_arpa
|
712
|
+
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(ArgumentError) {
|
714
|
+
IPAddr.new("192.168.2.1").ip6_arpa
|
715
|
+
}
|
716
|
+
end
|
717
|
+
|
718
|
+
def test_ip6_int
|
719
|
+
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(ArgumentError) {
|
721
|
+
IPAddr.new("192.168.2.1").ip6_int
|
722
|
+
}
|
723
|
+
end
|
724
|
+
|
725
|
+
def test_to_s
|
726
|
+
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
|
727
|
+
assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
class TC_Operator < Test::Unit::TestCase
|
732
|
+
|
733
|
+
IN6MASK32 = "ffff:ffff::"
|
734
|
+
IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
|
735
|
+
|
736
|
+
def setup
|
737
|
+
@in6_addr_any = IPAddr.new()
|
738
|
+
@a = IPAddr.new("3ffe:505:2::/48")
|
739
|
+
@b = IPAddr.new("0:0:0:1::")
|
740
|
+
@c = IPAddr.new(IN6MASK32)
|
741
|
+
end
|
742
|
+
alias set_up setup
|
743
|
+
|
744
|
+
def test_or
|
745
|
+
assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
|
746
|
+
a = @a
|
747
|
+
a |= @b
|
748
|
+
assert_equal("3ffe:505:2:1::", a.to_s)
|
749
|
+
assert_equal("3ffe:505:2::", @a.to_s)
|
750
|
+
assert_equal("3ffe:505:2:1::",
|
751
|
+
(@a | 0x00000000000000010000000000000000).to_s)
|
752
|
+
end
|
753
|
+
|
754
|
+
def test_and
|
755
|
+
assert_equal("3ffe:505::", (@a & @c).to_s)
|
756
|
+
a = @a
|
757
|
+
a &= @c
|
758
|
+
assert_equal("3ffe:505::", a.to_s)
|
759
|
+
assert_equal("3ffe:505:2::", @a.to_s)
|
760
|
+
assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
|
761
|
+
end
|
762
|
+
|
763
|
+
def test_shift_right
|
764
|
+
assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
|
765
|
+
a = @a
|
766
|
+
a >>= 16
|
767
|
+
assert_equal("0:3ffe:505:2::", a.to_s)
|
768
|
+
assert_equal("3ffe:505:2::", @a.to_s)
|
769
|
+
end
|
770
|
+
|
771
|
+
def test_shift_left
|
772
|
+
assert_equal("505:2::", (@a << 16).to_s)
|
773
|
+
a = @a
|
774
|
+
a <<= 16
|
775
|
+
assert_equal("505:2::", a.to_s)
|
776
|
+
assert_equal("3ffe:505:2::", @a.to_s)
|
777
|
+
end
|
778
|
+
|
779
|
+
def test_carrot
|
780
|
+
a = ~@in6_addr_any
|
781
|
+
assert_equal(IN6MASK128, a.to_s)
|
782
|
+
assert_equal("::", @in6_addr_any.to_s)
|
783
|
+
end
|
784
|
+
|
785
|
+
def test_equal
|
786
|
+
assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
|
787
|
+
assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
|
788
|
+
assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
|
789
|
+
assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
|
790
|
+
end
|
791
|
+
|
792
|
+
def test_mask
|
793
|
+
a = @a.mask(32)
|
794
|
+
assert_equal("3ffe:505::", a.to_s)
|
795
|
+
assert_equal("3ffe:505:2::", @a.to_s)
|
796
|
+
end
|
797
|
+
|
798
|
+
def test_include?
|
799
|
+
assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
|
800
|
+
assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
|
801
|
+
assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
|
802
|
+
net1 = IPAddr.new("192.168.2.0/24")
|
803
|
+
assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
|
804
|
+
assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
|
805
|
+
assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
|
806
|
+
# test with integer parameter
|
807
|
+
int = (192 << 24) + (168 << 16) + (2 << 8) + 13
|
808
|
+
|
809
|
+
assert_equal(true, net1.include?(int))
|
810
|
+
assert_equal(false, net1.include?(int+255))
|
811
|
+
|
812
|
+
end
|
813
|
+
|
814
|
+
end
|