rubysl-ipaddr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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