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.
@@ -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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ - ruby-head
6
+ before_install: gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ipaddr.gemspec
4
+ gemspec
@@ -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.
@@ -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).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -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__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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
@@ -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: []