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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-ipaddr.gemspec
4
+ gemspec
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,2 @@
1
+ require "rubysl/ipaddr/ipaddr"
2
+ require "rubysl/ipaddr/version"
@@ -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