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 +9 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ipaddr.gemspec +28 -0
- data/lib/ipaddr.rb +662 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e0852ad74f0bf3001f4c3bc91c8f4709066469b3
|
4
|
+
data.tar.gz: e7207467627207e243b6764fd80cb9d27c4690e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6819559510a3f7e073dc08ecba98679efd9bed002cb780b4efdb925477af41d1c36bfeb8c2057826bab94b3196ea4769886299cecd5a661273651c88d54a1c0f
|
7
|
+
data.tar.gz: 8fa05f41fe8712d9641dd03515341611dd46ee2fdd9a0deb886942a8e17fed97c6e01f9d0d007d40cc554dea7bf0870e9896ad8cd0ffc857c1cc802807187377
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>
|
2
|
+
Copyright (c) 2007-2017 Akinori MUSHA <knu@iDaemons.org>
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
+
SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# IPAddr
|
2
|
+
|
3
|
+
IPAddr provides a set of methods to manipulate an IP address. Both
|
4
|
+
IPv4 and IPv6 are supported.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'ipaddr'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install ipaddr
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'ipaddr'
|
26
|
+
|
27
|
+
ipaddr1 = IPAddr.new "3ffe:505:2::1"
|
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
|
+
|
37
|
+
ipaddr3 = IPAddr.new "192.168.2.0/24"
|
38
|
+
|
39
|
+
p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
40
|
+
```
|
41
|
+
|
42
|
+
## Development
|
43
|
+
|
44
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
45
|
+
|
46
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/ipaddr.
|
51
|
+
|
52
|
+
## License
|
53
|
+
|
54
|
+
The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ipaddr"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/ipaddr.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ipaddr"
|
7
|
+
spec.version = "1.0.0"
|
8
|
+
spec.authors = ["Akinori MUSHA", "Hajimu UMEMOTO"]
|
9
|
+
spec.email = ["knu@idaemons.org", "ume@mahoroba.org"]
|
10
|
+
|
11
|
+
spec.summary = %q{A class to manipulate an IP address in ruby}
|
12
|
+
spec.description = <<-'DESCRIPTION'
|
13
|
+
IPAddr provides a set of methods to manipulate an IP address.
|
14
|
+
Both IPv4 and IPv6 are supported.
|
15
|
+
DESCRIPTION
|
16
|
+
spec.homepage = "https://github.com/ruby/ipaddr"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "test-unit"
|
28
|
+
end
|
data/lib/ipaddr.rb
ADDED
@@ -0,0 +1,662 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# ipaddr.rb - A class to manipulate an IP address
|
4
|
+
#
|
5
|
+
# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
|
6
|
+
# Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
# You can redistribute and/or modify it under the same terms as Ruby.
|
10
|
+
#
|
11
|
+
# $Id$
|
12
|
+
#
|
13
|
+
# Contact:
|
14
|
+
# - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
|
15
|
+
#
|
16
|
+
# TODO:
|
17
|
+
# - scope_id support
|
18
|
+
#
|
19
|
+
require 'socket'
|
20
|
+
|
21
|
+
# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
|
22
|
+
# IPv6 are supported.
|
23
|
+
#
|
24
|
+
# == Example
|
25
|
+
#
|
26
|
+
# require 'ipaddr'
|
27
|
+
#
|
28
|
+
# ipaddr1 = IPAddr.new "3ffe:505:2::1"
|
29
|
+
#
|
30
|
+
# p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
|
31
|
+
#
|
32
|
+
# p ipaddr1.to_s #=> "3ffe:505:2::1"
|
33
|
+
#
|
34
|
+
# ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
|
35
|
+
#
|
36
|
+
# p ipaddr2.to_s #=> "3ffe:505:2::"
|
37
|
+
#
|
38
|
+
# ipaddr3 = IPAddr.new "192.168.2.0/24"
|
39
|
+
#
|
40
|
+
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
41
|
+
|
42
|
+
class IPAddr
|
43
|
+
|
44
|
+
# 32 bit mask for IPv4
|
45
|
+
IN4MASK = 0xffffffff
|
46
|
+
# 128 bit mask for IPv6
|
47
|
+
IN6MASK = 0xffffffffffffffffffffffffffffffff
|
48
|
+
# Format string for IPv6
|
49
|
+
IN6FORMAT = (["%.4x"] * 8).join(':')
|
50
|
+
|
51
|
+
# Regexp _internally_ used for parsing IPv4 address.
|
52
|
+
RE_IPV4ADDRLIKE = %r{
|
53
|
+
\A
|
54
|
+
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
55
|
+
\z
|
56
|
+
}x
|
57
|
+
|
58
|
+
# Regexp _internally_ used for parsing IPv6 address.
|
59
|
+
RE_IPV6ADDRLIKE_FULL = %r{
|
60
|
+
\A
|
61
|
+
(?:
|
62
|
+
(?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
|
63
|
+
|
|
64
|
+
( (?: [\da-f]{1,4} : ){6} )
|
65
|
+
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
66
|
+
)
|
67
|
+
\z
|
68
|
+
}xi
|
69
|
+
|
70
|
+
# Regexp _internally_ used for parsing IPv6 address.
|
71
|
+
RE_IPV6ADDRLIKE_COMPRESSED = %r{
|
72
|
+
\A
|
73
|
+
( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
|
74
|
+
::
|
75
|
+
( (?:
|
76
|
+
( (?: [\da-f]{1,4} : )* )
|
77
|
+
(?:
|
78
|
+
[\da-f]{1,4}
|
79
|
+
|
|
80
|
+
(\d+) \. (\d+) \. (\d+) \. (\d+)
|
81
|
+
)
|
82
|
+
)? )
|
83
|
+
\z
|
84
|
+
}xi
|
85
|
+
|
86
|
+
# Generic IPAddr related error. Exceptions raised in this class should
|
87
|
+
# inherit from Error.
|
88
|
+
class Error < ArgumentError; end
|
89
|
+
|
90
|
+
# Raised when the provided IP address is an invalid address.
|
91
|
+
class InvalidAddressError < Error; end
|
92
|
+
|
93
|
+
# Raised when the address family is invalid such as an address with an
|
94
|
+
# unsupported family, an address with an inconsistent family, or an address
|
95
|
+
# who's family cannot be determined.
|
96
|
+
class AddressFamilyError < Error; end
|
97
|
+
|
98
|
+
# Raised when the address is an invalid length.
|
99
|
+
class InvalidPrefixError < InvalidAddressError; end
|
100
|
+
|
101
|
+
# Returns the address family of this IP address.
|
102
|
+
attr_reader :family
|
103
|
+
|
104
|
+
# Creates a new ipaddr containing the given network byte ordered
|
105
|
+
# string form of an IP address.
|
106
|
+
def IPAddr::new_ntoh(addr)
|
107
|
+
return IPAddr.new(IPAddr::ntop(addr))
|
108
|
+
end
|
109
|
+
|
110
|
+
# Convert a network byte ordered string form of an IP address into
|
111
|
+
# human readable form.
|
112
|
+
def IPAddr::ntop(addr)
|
113
|
+
case addr.size
|
114
|
+
when 4
|
115
|
+
s = addr.unpack('C4').join('.')
|
116
|
+
when 16
|
117
|
+
s = IN6FORMAT % addr.unpack('n8')
|
118
|
+
else
|
119
|
+
raise AddressFamilyError, "unsupported address family"
|
120
|
+
end
|
121
|
+
return s
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns a new ipaddr built by bitwise AND.
|
125
|
+
def &(other)
|
126
|
+
return self.clone.set(@addr & coerce_other(other).to_i)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns a new ipaddr built by bitwise OR.
|
130
|
+
def |(other)
|
131
|
+
return self.clone.set(@addr | coerce_other(other).to_i)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a new ipaddr built by bitwise right-shift.
|
135
|
+
def >>(num)
|
136
|
+
return self.clone.set(@addr >> num)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a new ipaddr built by bitwise left shift.
|
140
|
+
def <<(num)
|
141
|
+
return self.clone.set(addr_mask(@addr << num))
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a new ipaddr built by bitwise negation.
|
145
|
+
def ~
|
146
|
+
return self.clone.set(addr_mask(~@addr))
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns true if two ipaddrs are equal.
|
150
|
+
def ==(other)
|
151
|
+
other = coerce_other(other)
|
152
|
+
rescue
|
153
|
+
false
|
154
|
+
else
|
155
|
+
@family == other.family && @addr == other.to_i
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a new ipaddr built by masking IP address with the given
|
159
|
+
# prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
|
160
|
+
def mask(prefixlen)
|
161
|
+
return self.clone.mask!(prefixlen)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns true if the given ipaddr is in the range.
|
165
|
+
#
|
166
|
+
# e.g.:
|
167
|
+
# require 'ipaddr'
|
168
|
+
# net1 = IPAddr.new("192.168.2.0/24")
|
169
|
+
# net2 = IPAddr.new("192.168.2.100")
|
170
|
+
# net3 = IPAddr.new("192.168.3.0")
|
171
|
+
# p net1.include?(net2) #=> true
|
172
|
+
# p net1.include?(net3) #=> false
|
173
|
+
def include?(other)
|
174
|
+
other = coerce_other(other)
|
175
|
+
if ipv4_mapped?
|
176
|
+
if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
mask_addr = (@mask_addr & IN4MASK)
|
180
|
+
addr = (@addr & IN4MASK)
|
181
|
+
family = Socket::AF_INET
|
182
|
+
else
|
183
|
+
mask_addr = @mask_addr
|
184
|
+
addr = @addr
|
185
|
+
family = @family
|
186
|
+
end
|
187
|
+
if other.ipv4_mapped?
|
188
|
+
other_addr = (other.to_i & IN4MASK)
|
189
|
+
other_family = Socket::AF_INET
|
190
|
+
else
|
191
|
+
other_addr = other.to_i
|
192
|
+
other_family = other.family
|
193
|
+
end
|
194
|
+
|
195
|
+
if family != other_family
|
196
|
+
return false
|
197
|
+
end
|
198
|
+
return ((addr & mask_addr) == (other_addr & mask_addr))
|
199
|
+
end
|
200
|
+
alias === include?
|
201
|
+
|
202
|
+
# Returns the integer representation of the ipaddr.
|
203
|
+
def to_i
|
204
|
+
return @addr
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns a string containing the IP address representation.
|
208
|
+
def to_s
|
209
|
+
str = to_string
|
210
|
+
return str if ipv4?
|
211
|
+
|
212
|
+
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
|
213
|
+
loop do
|
214
|
+
break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
|
215
|
+
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
|
216
|
+
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
|
217
|
+
break if str.sub!(/\b0:0:0:0:0\b/, ':')
|
218
|
+
break if str.sub!(/\b0:0:0:0\b/, ':')
|
219
|
+
break if str.sub!(/\b0:0:0\b/, ':')
|
220
|
+
break if str.sub!(/\b0:0\b/, ':')
|
221
|
+
break
|
222
|
+
end
|
223
|
+
str.sub!(/:{3,}/, '::')
|
224
|
+
|
225
|
+
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
|
226
|
+
str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
|
227
|
+
end
|
228
|
+
|
229
|
+
str
|
230
|
+
end
|
231
|
+
|
232
|
+
# Returns a string containing the IP address representation in
|
233
|
+
# canonical form.
|
234
|
+
def to_string
|
235
|
+
return _to_string(@addr)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns a network byte ordered string form of the IP address.
|
239
|
+
def hton
|
240
|
+
case @family
|
241
|
+
when Socket::AF_INET
|
242
|
+
return [@addr].pack('N')
|
243
|
+
when Socket::AF_INET6
|
244
|
+
return (0..7).map { |i|
|
245
|
+
(@addr >> (112 - 16 * i)) & 0xffff
|
246
|
+
}.pack('n8')
|
247
|
+
else
|
248
|
+
raise AddressFamilyError, "unsupported address family"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns true if the ipaddr is an IPv4 address.
|
253
|
+
def ipv4?
|
254
|
+
return @family == Socket::AF_INET
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns true if the ipaddr is an IPv6 address.
|
258
|
+
def ipv6?
|
259
|
+
return @family == Socket::AF_INET6
|
260
|
+
end
|
261
|
+
|
262
|
+
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
|
263
|
+
def ipv4_mapped?
|
264
|
+
return ipv6? && (@addr >> 32) == 0xffff
|
265
|
+
end
|
266
|
+
|
267
|
+
# Returns true if the ipaddr is an IPv4-compatible IPv6 address.
|
268
|
+
def ipv4_compat?
|
269
|
+
if !ipv6? || (@addr >> 32) != 0
|
270
|
+
return false
|
271
|
+
end
|
272
|
+
a = (@addr & IN4MASK)
|
273
|
+
return a != 0 && a != 1
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns a new ipaddr built by converting the native IPv4 address
|
277
|
+
# into an IPv4-mapped IPv6 address.
|
278
|
+
def ipv4_mapped
|
279
|
+
if !ipv4?
|
280
|
+
raise InvalidAddressError, "not an IPv4 address"
|
281
|
+
end
|
282
|
+
return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns a new ipaddr built by converting the native IPv4 address
|
286
|
+
# into an IPv4-compatible IPv6 address.
|
287
|
+
def ipv4_compat
|
288
|
+
if !ipv4?
|
289
|
+
raise InvalidAddressError, "not an IPv4 address"
|
290
|
+
end
|
291
|
+
return self.clone.set(@addr, Socket::AF_INET6)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Returns a new ipaddr built by converting the IPv6 address into a
|
295
|
+
# native IPv4 address. If the IP address is not an IPv4-mapped or
|
296
|
+
# IPv4-compatible IPv6 address, returns self.
|
297
|
+
def native
|
298
|
+
if !ipv4_mapped? && !ipv4_compat?
|
299
|
+
return self
|
300
|
+
end
|
301
|
+
return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Returns a string for DNS reverse lookup. It returns a string in
|
305
|
+
# RFC3172 form for an IPv6 address.
|
306
|
+
def reverse
|
307
|
+
case @family
|
308
|
+
when Socket::AF_INET
|
309
|
+
return _reverse + ".in-addr.arpa"
|
310
|
+
when Socket::AF_INET6
|
311
|
+
return ip6_arpa
|
312
|
+
else
|
313
|
+
raise AddressFamilyError, "unsupported address family"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Returns a string for DNS reverse lookup compatible with RFC3172.
|
318
|
+
def ip6_arpa
|
319
|
+
if !ipv6?
|
320
|
+
raise InvalidAddressError, "not an IPv6 address"
|
321
|
+
end
|
322
|
+
return _reverse + ".ip6.arpa"
|
323
|
+
end
|
324
|
+
|
325
|
+
# Returns a string for DNS reverse lookup compatible with RFC1886.
|
326
|
+
def ip6_int
|
327
|
+
if !ipv6?
|
328
|
+
raise InvalidAddressError, "not an IPv6 address"
|
329
|
+
end
|
330
|
+
return _reverse + ".ip6.int"
|
331
|
+
end
|
332
|
+
|
333
|
+
# Returns the successor to the ipaddr.
|
334
|
+
def succ
|
335
|
+
return self.clone.set(@addr + 1, @family)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Compares the ipaddr with another.
|
339
|
+
def <=>(other)
|
340
|
+
other = coerce_other(other)
|
341
|
+
rescue
|
342
|
+
nil
|
343
|
+
else
|
344
|
+
@addr <=> other.to_i if other.family == @family
|
345
|
+
end
|
346
|
+
include Comparable
|
347
|
+
|
348
|
+
# Checks equality used by Hash.
|
349
|
+
def eql?(other)
|
350
|
+
return self.class == other.class && self.hash == other.hash && self == other
|
351
|
+
end
|
352
|
+
|
353
|
+
# Returns a hash value used by Hash, Set, and Array classes
|
354
|
+
def hash
|
355
|
+
return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
|
356
|
+
end
|
357
|
+
|
358
|
+
# Creates a Range object for the network address.
|
359
|
+
def to_range
|
360
|
+
begin_addr = (@addr & @mask_addr)
|
361
|
+
|
362
|
+
case @family
|
363
|
+
when Socket::AF_INET
|
364
|
+
end_addr = (@addr | (IN4MASK ^ @mask_addr))
|
365
|
+
when Socket::AF_INET6
|
366
|
+
end_addr = (@addr | (IN6MASK ^ @mask_addr))
|
367
|
+
else
|
368
|
+
raise AddressFamilyError, "unsupported address family"
|
369
|
+
end
|
370
|
+
|
371
|
+
return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
|
372
|
+
end
|
373
|
+
|
374
|
+
# Returns a string containing a human-readable representation of the
|
375
|
+
# ipaddr. ("#<IPAddr: family:address/mask>")
|
376
|
+
def inspect
|
377
|
+
case @family
|
378
|
+
when Socket::AF_INET
|
379
|
+
af = "IPv4"
|
380
|
+
when Socket::AF_INET6
|
381
|
+
af = "IPv6"
|
382
|
+
else
|
383
|
+
raise AddressFamilyError, "unsupported address family"
|
384
|
+
end
|
385
|
+
return sprintf("#<%s: %s:%s/%s>", self.class.name,
|
386
|
+
af, _to_string(@addr), _to_string(@mask_addr))
|
387
|
+
end
|
388
|
+
|
389
|
+
protected
|
390
|
+
|
391
|
+
# Set +@addr+, the internal stored ip address, to given +addr+. The
|
392
|
+
# parameter +addr+ is validated using the first +family+ member,
|
393
|
+
# which is +Socket::AF_INET+ or +Socket::AF_INET6+.
|
394
|
+
def set(addr, *family)
|
395
|
+
case family[0] ? family[0] : @family
|
396
|
+
when Socket::AF_INET
|
397
|
+
if addr < 0 || addr > IN4MASK
|
398
|
+
raise InvalidAddressError, "invalid address"
|
399
|
+
end
|
400
|
+
when Socket::AF_INET6
|
401
|
+
if addr < 0 || addr > IN6MASK
|
402
|
+
raise InvalidAddressError, "invalid address"
|
403
|
+
end
|
404
|
+
else
|
405
|
+
raise AddressFamilyError, "unsupported address family"
|
406
|
+
end
|
407
|
+
@addr = addr
|
408
|
+
if family[0]
|
409
|
+
@family = family[0]
|
410
|
+
end
|
411
|
+
return self
|
412
|
+
end
|
413
|
+
|
414
|
+
# Set current netmask to given mask.
|
415
|
+
def mask!(mask)
|
416
|
+
if mask.kind_of?(String)
|
417
|
+
if mask =~ /\A\d+\z/
|
418
|
+
prefixlen = mask.to_i
|
419
|
+
else
|
420
|
+
m = IPAddr.new(mask)
|
421
|
+
if m.family != @family
|
422
|
+
raise InvalidPrefixError, "address family is not same"
|
423
|
+
end
|
424
|
+
@mask_addr = m.to_i
|
425
|
+
@addr &= @mask_addr
|
426
|
+
return self
|
427
|
+
end
|
428
|
+
else
|
429
|
+
prefixlen = mask
|
430
|
+
end
|
431
|
+
case @family
|
432
|
+
when Socket::AF_INET
|
433
|
+
if prefixlen < 0 || prefixlen > 32
|
434
|
+
raise InvalidPrefixError, "invalid length"
|
435
|
+
end
|
436
|
+
masklen = 32 - prefixlen
|
437
|
+
@mask_addr = ((IN4MASK >> masklen) << masklen)
|
438
|
+
when Socket::AF_INET6
|
439
|
+
if prefixlen < 0 || prefixlen > 128
|
440
|
+
raise InvalidPrefixError, "invalid length"
|
441
|
+
end
|
442
|
+
masklen = 128 - prefixlen
|
443
|
+
@mask_addr = ((IN6MASK >> masklen) << masklen)
|
444
|
+
else
|
445
|
+
raise AddressFamilyError, "unsupported address family"
|
446
|
+
end
|
447
|
+
@addr = ((@addr >> masklen) << masklen)
|
448
|
+
return self
|
449
|
+
end
|
450
|
+
|
451
|
+
private
|
452
|
+
|
453
|
+
# Creates a new ipaddr object either from a human readable IP
|
454
|
+
# address representation in string, or from a packed in_addr value
|
455
|
+
# followed by an address family.
|
456
|
+
#
|
457
|
+
# In the former case, the following are the valid formats that will
|
458
|
+
# be recognized: "address", "address/prefixlen" and "address/mask",
|
459
|
+
# where IPv6 address may be enclosed in square brackets (`[' and
|
460
|
+
# `]'). If a prefixlen or a mask is specified, it returns a masked
|
461
|
+
# IP address. Although the address family is determined
|
462
|
+
# automatically from a specified string, you can specify one
|
463
|
+
# explicitly by the optional second argument.
|
464
|
+
#
|
465
|
+
# Otherwise an IP address is generated from a packed in_addr value
|
466
|
+
# and an address family.
|
467
|
+
#
|
468
|
+
# The IPAddr class defines many methods and operators, and some of
|
469
|
+
# those, such as &, |, include? and ==, accept a string, or a packed
|
470
|
+
# in_addr value instead of an IPAddr object.
|
471
|
+
def initialize(addr = '::', family = Socket::AF_UNSPEC)
|
472
|
+
if !addr.kind_of?(String)
|
473
|
+
case family
|
474
|
+
when Socket::AF_INET, Socket::AF_INET6
|
475
|
+
set(addr.to_i, family)
|
476
|
+
@mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
|
477
|
+
return
|
478
|
+
when Socket::AF_UNSPEC
|
479
|
+
raise AddressFamilyError, "address family must be specified"
|
480
|
+
else
|
481
|
+
raise AddressFamilyError, "unsupported address family: #{family}"
|
482
|
+
end
|
483
|
+
end
|
484
|
+
prefix, prefixlen = addr.split('/')
|
485
|
+
if prefix =~ /\A\[(.*)\]\z/i
|
486
|
+
prefix = $1
|
487
|
+
family = Socket::AF_INET6
|
488
|
+
end
|
489
|
+
# It seems AI_NUMERICHOST doesn't do the job.
|
490
|
+
#Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
|
491
|
+
# Socket::AI_NUMERICHOST)
|
492
|
+
@addr = @family = nil
|
493
|
+
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
|
494
|
+
@addr = in_addr(prefix)
|
495
|
+
if @addr
|
496
|
+
@family = Socket::AF_INET
|
497
|
+
end
|
498
|
+
end
|
499
|
+
if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
|
500
|
+
@addr = in6_addr(prefix)
|
501
|
+
@family = Socket::AF_INET6
|
502
|
+
end
|
503
|
+
if family != Socket::AF_UNSPEC && @family != family
|
504
|
+
raise AddressFamilyError, "address family mismatch"
|
505
|
+
end
|
506
|
+
if prefixlen
|
507
|
+
mask!(prefixlen)
|
508
|
+
else
|
509
|
+
@mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def coerce_other(other)
|
514
|
+
case other
|
515
|
+
when IPAddr
|
516
|
+
other
|
517
|
+
when String
|
518
|
+
self.class.new(other)
|
519
|
+
else
|
520
|
+
self.class.new(other, @family)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
def in_addr(addr)
|
525
|
+
case addr
|
526
|
+
when Array
|
527
|
+
octets = addr
|
528
|
+
else
|
529
|
+
m = RE_IPV4ADDRLIKE.match(addr) or return nil
|
530
|
+
octets = m.captures
|
531
|
+
end
|
532
|
+
octets.inject(0) { |i, s|
|
533
|
+
(n = s.to_i) < 256 or raise InvalidAddressError, "invalid address"
|
534
|
+
s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous"
|
535
|
+
i << 8 | n
|
536
|
+
}
|
537
|
+
end
|
538
|
+
|
539
|
+
def in6_addr(left)
|
540
|
+
case left
|
541
|
+
when RE_IPV6ADDRLIKE_FULL
|
542
|
+
if $2
|
543
|
+
addr = in_addr($~[2,4])
|
544
|
+
left = $1 + ':'
|
545
|
+
else
|
546
|
+
addr = 0
|
547
|
+
end
|
548
|
+
right = ''
|
549
|
+
when RE_IPV6ADDRLIKE_COMPRESSED
|
550
|
+
if $4
|
551
|
+
left.count(':') <= 6 or raise InvalidAddressError, "invalid address"
|
552
|
+
addr = in_addr($~[4,4])
|
553
|
+
left = $1
|
554
|
+
right = $3 + '0:0'
|
555
|
+
else
|
556
|
+
left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
|
557
|
+
raise InvalidAddressError, "invalid address"
|
558
|
+
left = $1
|
559
|
+
right = $2
|
560
|
+
addr = 0
|
561
|
+
end
|
562
|
+
else
|
563
|
+
raise InvalidAddressError, "invalid address"
|
564
|
+
end
|
565
|
+
l = left.split(':')
|
566
|
+
r = right.split(':')
|
567
|
+
rest = 8 - l.size - r.size
|
568
|
+
if rest < 0
|
569
|
+
return nil
|
570
|
+
end
|
571
|
+
(l + Array.new(rest, '0') + r).inject(0) { |i, s|
|
572
|
+
i << 16 | s.hex
|
573
|
+
} | addr
|
574
|
+
end
|
575
|
+
|
576
|
+
def addr_mask(addr)
|
577
|
+
case @family
|
578
|
+
when Socket::AF_INET
|
579
|
+
return addr & IN4MASK
|
580
|
+
when Socket::AF_INET6
|
581
|
+
return addr & IN6MASK
|
582
|
+
else
|
583
|
+
raise AddressFamilyError, "unsupported address family"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
def _reverse
|
588
|
+
case @family
|
589
|
+
when Socket::AF_INET
|
590
|
+
return (0..3).map { |i|
|
591
|
+
(@addr >> (8 * i)) & 0xff
|
592
|
+
}.join('.')
|
593
|
+
when Socket::AF_INET6
|
594
|
+
return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
|
595
|
+
else
|
596
|
+
raise AddressFamilyError, "unsupported address family"
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
def _to_string(addr)
|
601
|
+
case @family
|
602
|
+
when Socket::AF_INET
|
603
|
+
return (0..3).map { |i|
|
604
|
+
(addr >> (24 - 8 * i)) & 0xff
|
605
|
+
}.join('.')
|
606
|
+
when Socket::AF_INET6
|
607
|
+
return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
|
608
|
+
else
|
609
|
+
raise AddressFamilyError, "unsupported address family"
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
end
|
614
|
+
|
615
|
+
unless Socket.const_defined? :AF_INET6
|
616
|
+
class Socket < BasicSocket
|
617
|
+
# IPv6 protocol family
|
618
|
+
AF_INET6 = Object.new
|
619
|
+
end
|
620
|
+
|
621
|
+
class << IPSocket
|
622
|
+
private
|
623
|
+
|
624
|
+
def valid_v6?(addr)
|
625
|
+
case addr
|
626
|
+
when IPAddr::RE_IPV6ADDRLIKE_FULL
|
627
|
+
if $2
|
628
|
+
$~[2,4].all? {|i| i.to_i < 256 }
|
629
|
+
else
|
630
|
+
true
|
631
|
+
end
|
632
|
+
when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
|
633
|
+
if $4
|
634
|
+
addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
|
635
|
+
else
|
636
|
+
addr.count(':') <= 7
|
637
|
+
end
|
638
|
+
else
|
639
|
+
false
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
alias getaddress_orig getaddress
|
644
|
+
|
645
|
+
public
|
646
|
+
|
647
|
+
# Returns a +String+ based representation of a valid DNS hostname,
|
648
|
+
# IPv4 or IPv6 address.
|
649
|
+
#
|
650
|
+
# IPSocket.getaddress 'localhost' #=> "::1"
|
651
|
+
# IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255"
|
652
|
+
# IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
|
653
|
+
# IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122"
|
654
|
+
def getaddress(s)
|
655
|
+
if valid_v6?(s)
|
656
|
+
s
|
657
|
+
else
|
658
|
+
getaddress_orig(s)
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ipaddr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Akinori MUSHA
|
8
|
+
- Hajimu UMEMOTO
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-08-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.15'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.15'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: test-unit
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: |
|
57
|
+
IPAddr provides a set of methods to manipulate an IP address.
|
58
|
+
Both IPv4 and IPv6 are supported.
|
59
|
+
email:
|
60
|
+
- knu@idaemons.org
|
61
|
+
- ume@mahoroba.org
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- ".gitignore"
|
67
|
+
- ".travis.yml"
|
68
|
+
- Gemfile
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- bin/console
|
73
|
+
- bin/setup
|
74
|
+
- ipaddr.gemspec
|
75
|
+
- lib/ipaddr.rb
|
76
|
+
homepage: https://github.com/ruby/ipaddr
|
77
|
+
licenses: []
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.6.11
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: A class to manipulate an IP address in ruby
|
99
|
+
test_files: []
|