ipaddress 0.8.0 → 0.8.3

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.
data/Rakefile CHANGED
@@ -1,25 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
  require 'rake/clean'
4
+ require "bundler/gem_tasks"
4
5
 
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gem|
8
- gem.name = "ipaddress"
9
- gem.summary = %Q{IPv4/IPv6 addresses manipulation library}
10
- gem.email = "ceresa@gmail.com"
11
- gem.homepage = "http://github.com/bluemonk/ipaddress"
12
- gem.authors = ["Marco Ceresa"]
13
- gem.description = <<-EOD
14
- IPAddress is a Ruby library designed to make manipulation
15
- of IPv4 and IPv6 addresses both powerful and simple. It mantains
16
- a layer of compatibility with Ruby's own IPAddr, while
17
- addressing many of its issues.
18
- EOD
19
- end
20
- rescue LoadError
21
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
- end
23
6
 
24
7
  require 'rake/testtask'
25
8
  Rake::TestTask.new(:test) do |test|
@@ -44,7 +27,7 @@ end
44
27
 
45
28
  task :default => :test
46
29
 
47
- require 'rake/rdoctask'
30
+ require 'rdoc/task'
48
31
  Rake::RDocTask.new do |rdoc|
49
32
  if File.exist?('VERSION.yml')
50
33
  config = YAML.load(File.read('VERSION.yml'))
@@ -1,55 +1,26 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ipaddress/version'
5
5
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{ipaddress}
8
- s.version = "0.8.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Marco Ceresa"]
12
- s.date = %q{2011-05-17}
13
- s.description = %q{ IPAddress is a Ruby library designed to make manipulation
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ipaddress"
8
+ spec.version = Ipaddress::VERSION
9
+ spec.authors = ["bluemonk", "mikemackintosh"]
10
+ spec.email = ["ceresa@gmail.com"]
11
+ spec.summary = %q{IPv4/IPv6 address manipulation library}
12
+ spec.description = %q{IPAddress is a Ruby library designed to make manipulation
14
13
  of IPv4 and IPv6 addresses both powerful and simple. It mantains
15
14
  a layer of compatibility with Ruby's own IPAddr, while
16
- addressing many of its issues.
17
- }
18
- s.email = %q{ceresa@gmail.com}
19
- s.extra_rdoc_files = [
20
- "LICENSE",
21
- "README.rdoc"
22
- ]
23
- s.files = [
24
- ".document",
25
- "CHANGELOG.rdoc",
26
- "LICENSE",
27
- "README.rdoc",
28
- "Rakefile",
29
- "VERSION",
30
- "ipaddress.gemspec",
31
- "lib/ipaddress.rb",
32
- "lib/ipaddress/ipv4.rb",
33
- "lib/ipaddress/ipv6.rb",
34
- "lib/ipaddress/prefix.rb",
35
- "test/ipaddress/ipv4_test.rb",
36
- "test/ipaddress/ipv6_test.rb",
37
- "test/ipaddress/prefix_test.rb",
38
- "test/ipaddress_test.rb",
39
- "test/test_helper.rb"
40
- ]
41
- s.homepage = %q{http://github.com/bluemonk/ipaddress}
42
- s.require_paths = ["lib"]
43
- s.rubygems_version = %q{1.6.2}
44
- s.summary = %q{IPv4/IPv6 addresses manipulation library}
15
+ addressing many of its issues.}
16
+ spec.homepage = "https://github.com/bluemonk/ipaddress"
17
+ spec.license = "MIT"
45
18
 
46
- if s.respond_to? :specification_version then
47
- s.specification_version = 3
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
48
23
 
49
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
- else
51
- end
52
- else
53
- end
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "rake"
54
26
  end
55
-
@@ -14,6 +14,7 @@
14
14
 
15
15
  require 'ipaddress/ipv4'
16
16
  require 'ipaddress/ipv6'
17
+ require 'ipaddress/mongoid' if defined?(Mongoid)
17
18
 
18
19
  module IPAddress
19
20
 
@@ -25,6 +26,7 @@ module IPAddress
25
26
  # Parse the argument string to create a new
26
27
  # IPv4, IPv6 or Mapped IP object
27
28
  #
29
+ # ip = IPAddress.parse 167837953 # 10.1.1.1
28
30
  # ip = IPAddress.parse "172.16.10.1/24"
29
31
  # ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64"
30
32
  # ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128"
@@ -40,14 +42,42 @@ module IPAddress
40
42
  # #=> IPAddress::IPv6::Mapped
41
43
  #
42
44
  def IPAddress::parse(str)
45
+
46
+ # Check if an int was passed
47
+ if str.kind_of? Integer
48
+ return IPAddress::IPv4.new(ntoa(str))
49
+ end
50
+
43
51
  case str
44
52
  when /:.+\./
45
53
  IPAddress::IPv6::Mapped.new(str)
54
+ when /\./
55
+ IPAddress::IPv4.new(str)
56
+ when /:/
57
+ IPAddress::IPv6.new(str)
46
58
  else
47
- IPAddress::IPv4.new(str) rescue IPAddress::IPv6.new(str)
59
+ raise ArgumentError, "Unknown IP Address #{str}"
48
60
  end
49
61
  end
50
62
 
63
+ #
64
+ # Converts a unit32 to IPv4
65
+ #
66
+ # IPAddress::ntoa(167837953)
67
+ # #-> "10.1.1.1"
68
+ #
69
+ def self.ntoa(uint)
70
+ unless(uint.is_a? Numeric and uint <= 0xffffffff and uint >= 0)
71
+ raise(::ArgumentError, "not a long integer: #{uint.inspect}")
72
+ end
73
+ ret = []
74
+ 4.times do
75
+ ret.unshift(uint & 0xff)
76
+ uint >>= 8
77
+ end
78
+ ret.join('.')
79
+ end
80
+
51
81
  #
52
82
  # True if the object is an IPv4 address
53
83
  #
@@ -131,15 +161,10 @@ module IPAddress
131
161
  # IPAddress::valid_ipv6? "2002::DEAD::BEEF"
132
162
  # #=> false
133
163
  #
134
- def self.valid_ipv6?(addr)
135
- # IPv6 (normal)
136
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
137
- 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
138
- return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
139
- # IPv6 (IPv4 compat)
140
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
141
- 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_ipv4?($')
142
- return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
164
+ def self.valid_ipv6?(addr)
165
+ # https://gist.github.com/cpetschnig/294476
166
+ # http://forums.intermapper.com/viewtopic.php?t=452
167
+ return true if /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ =~ addr
143
168
  false
144
169
  end
145
170
 
@@ -189,7 +214,7 @@ end
189
214
  #
190
215
  # Compatibility with Ruby 1.8
191
216
  #
192
- if RUBY_VERSION =~ /1\.8/
217
+ if RUBY_VERSION =~ /^1\.8/
193
218
  class Hash # :nodoc:
194
219
  alias :key :index
195
220
  end
@@ -230,6 +230,21 @@ module IPAddress;
230
230
  alias_method :to_i, :u32
231
231
  alias_method :to_u32, :u32
232
232
 
233
+ #
234
+ # Returns the address portion in
235
+ # hex
236
+ #
237
+ # ip = IPAddress("10.0.0.0")
238
+ #
239
+ # ip.to_h
240
+ # #=> 0a000000
241
+ #
242
+ def hex(space=true)
243
+ "%.4x%.4x" % [to_u32].pack("N").unpack("nn")
244
+ end
245
+ alias_method :to_h, :hex
246
+ alias_method :to_hex, :hex
247
+
233
248
  #
234
249
  # Returns the address portion of an IPv4 object
235
250
  # in a network byte order format.
@@ -271,6 +286,21 @@ module IPAddress;
271
286
  @octets[index]
272
287
  end
273
288
  alias_method :octet, :[]
289
+
290
+ #
291
+ # Updated the octet specified at index
292
+ #
293
+ # ip = IPAddress("172.16.100.50/24")
294
+ # ip[2] = 200
295
+ #
296
+ # #=> #<IPAddress::IPv4:0x00000000000000 @address="172.16.200.1",
297
+ # #=> @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>
298
+ #
299
+ def []=(index, value)
300
+ @octets[index] = value.to_i
301
+ initialize("#{@octets.join('.')}/#{prefix}")
302
+ end
303
+ alias_method :octet=, :[]=
274
304
 
275
305
  #
276
306
  # Returns the address portion of an IP in binary format,
@@ -294,7 +324,14 @@ module IPAddress;
294
324
  # #=> "172.16.10.255"
295
325
  #
296
326
  def broadcast
297
- self.class.parse_u32(broadcast_u32, @prefix)
327
+ case
328
+ when prefix <= 30
329
+ self.class.parse_u32(broadcast_u32, @prefix)
330
+ when prefix == 31
331
+ self.class.parse_u32(-1, @prefix)
332
+ when prefix == 32
333
+ return self
334
+ end
298
335
  end
299
336
 
300
337
  #
@@ -311,7 +348,7 @@ module IPAddress;
311
348
  # #=> true
312
349
  #
313
350
  def network?
314
- @u32 | @prefix.to_u32 == @prefix.to_u32
351
+ (@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
315
352
  end
316
353
 
317
354
  #
@@ -348,7 +385,14 @@ module IPAddress;
348
385
  # #=> "192.168.100.1"
349
386
  #
350
387
  def first
351
- self.class.parse_u32(network_u32+1, @prefix)
388
+ case
389
+ when prefix <= 30
390
+ self.class.parse_u32(network_u32+1, @prefix)
391
+ when prefix == 31
392
+ self.class.parse_u32(network_u32, @prefix)
393
+ when prefix == 32
394
+ return self
395
+ end
352
396
  end
353
397
 
354
398
  #
@@ -373,7 +417,14 @@ module IPAddress;
373
417
  # #=> "192.168.100.254"
374
418
  #
375
419
  def last
376
- self.class.parse_u32(broadcast_u32-1, @prefix)
420
+ case
421
+ when prefix <= 30
422
+ self.class.parse_u32(broadcast_u32-1, @prefix)
423
+ when prefix == 31
424
+ self.class.parse_u32(broadcast_u32, @prefix)
425
+ when prefix == 32
426
+ return self
427
+ end
377
428
  end
378
429
 
379
430
  #
@@ -569,6 +620,48 @@ module IPAddress;
569
620
  self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
570
621
  end
571
622
 
623
+ #
624
+ # Checks if an IPv4 address objects belongs
625
+ # to a multicast network RFC3171
626
+ #
627
+ # Example:
628
+ #
629
+ # ip = IPAddress "224.0.0.0/4"
630
+ # ip.multicast?
631
+ # #=> true
632
+ #
633
+ def multicast?
634
+ [self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
635
+ end
636
+
637
+ #
638
+ # Checks if an IPv4 address objects belongs
639
+ # to a multicast network RFC3171
640
+ #
641
+ # Example:
642
+ #
643
+ # ip = IPAddress "224.0.0.0/4"
644
+ # ip.multicast?
645
+ # #=> true
646
+ #
647
+ def multicast?
648
+ [self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
649
+ end
650
+
651
+ #
652
+ # Checks if an IPv4 address objects belongs
653
+ # to a loopback network RFC1122
654
+ #
655
+ # Example:
656
+ #
657
+ # ip = IPAddress "127.0.0.1"
658
+ # ip.loopback?
659
+ # #=> true
660
+ #
661
+ def loopback?
662
+ [self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
663
+ end
664
+
572
665
  #
573
666
  # Returns the IP address in in-addr.arpa format
574
667
  # for DNS lookups
@@ -583,6 +676,26 @@ module IPAddress;
583
676
  end
584
677
  alias_method :arpa, :reverse
585
678
 
679
+ #
680
+ # Return a list of IP's between @address
681
+ # and the supplied IP
682
+ #
683
+ # ip = IPAddress("172.16.100.51/32")
684
+ #
685
+ # ip.to("172.16.100.100")
686
+ # #=> ["172.16.100.51",
687
+ # #=> "172.16.100.52",
688
+ # #=> ...
689
+ # #=> "172.16.100.99",
690
+ # #=> "172.16.100.100"]
691
+ #
692
+ def to(e)
693
+ unless e.is_a? IPAddress::IPv4
694
+ e = IPv4.new(e)
695
+ end
696
+
697
+ Range.new(@u32, e.to_u32).map{|i| IPAddress.ntoa(i) }
698
+ end
586
699
  #
587
700
  # Splits a network into different subnets
588
701
  #
@@ -598,9 +711,9 @@ module IPAddress;
598
711
  #
599
712
  # network / 4 # implies map{|i| i.to_string}
600
713
  # #=> ["172.16.10.0/26",
601
- # "172.16.10.64/26",
602
- # "172.16.10.128/26",
603
- # "172.16.10.192/26"]
714
+ # #=> "172.16.10.64/26",
715
+ # #=> "172.16.10.128/26",
716
+ # #=> "172.16.10.192/26"]
604
717
  #
605
718
  # If +num+ is any other number, the supernet will be
606
719
  # divided into some networks with a even number of hosts and
@@ -610,8 +723,8 @@ module IPAddress;
610
723
  #
611
724
  # network / 3 # implies map{|i| i.to_string}
612
725
  # #=> ["172.16.10.0/26",
613
- # "172.16.10.64/26",
614
- # "172.16.10.128/25"]
726
+ # #=> "172.16.10.64/26",
727
+ # #=> "172.16.10.128/25"]
615
728
  #
616
729
  # Returns an array of IPv4 objects
617
730
  #
@@ -969,12 +1082,9 @@ module IPAddress;
969
1082
  #
970
1083
  private
971
1084
 
1085
+ # Tweaked to remove the #upto(32)
972
1086
  def newprefix(num)
973
- num.upto(32) do |i|
974
- if (a = Math::log2(i).to_i) == Math::log2(i)
975
- return @prefix + a
976
- end
977
- end
1087
+ return @prefix + (Math::log2(num).ceil )
978
1088
  end
979
1089
 
980
1090
  def sum_first_found(arr)
@@ -252,6 +252,15 @@ module IPAddress;
252
252
  end
253
253
  alias_method :group, :[]
254
254
 
255
+ #
256
+ # Updated the octet specified at index
257
+ #
258
+ def []=(index, value)
259
+ @groups[index] = value
260
+ initialize("#{IN6FORMAT % @groups}/#{prefix}")
261
+ end
262
+ alias_method :group=, :[]=
263
+
255
264
  #
256
265
  # Returns a Base16 number representing the IPv6
257
266
  # address
@@ -0,0 +1,75 @@
1
+ module IPAddress
2
+
3
+ #
4
+ # Mongoid field serialization
5
+ #
6
+ # IPAddress objects are converted to String
7
+ #
8
+ # IPAddress.mongoize IPAddress.parse("172.16.10.1")
9
+ # #=> "172.16.10.1"
10
+ #
11
+ # Prefix will be removed from host adresses
12
+ #
13
+ # IPAddress.mongoize "172.16.10.1/32"
14
+ # #=> "172.16.10.1"
15
+ #
16
+ # Prefix will be kept for network addresses
17
+ #
18
+ # IPAddress.mongoize "172.16.10.1/24"
19
+ # #=> "172.16.10.1/24"
20
+ #
21
+ # IPv6 addresses will be stored uncompressed to ease DB search and sorting
22
+ #
23
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a"
24
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
25
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a/64"
26
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
27
+ #
28
+ # Invalid addresses will be serialized as nil
29
+ #
30
+ # IPAddress.mongoize "invalid"
31
+ # #=> nil
32
+ # IPAddress.mongoize ""
33
+ # #=> nil
34
+ # IPAddress.mongoize 1
35
+ # #=> nil
36
+ # IPAddress.mongoize nil
37
+ # #=> nil
38
+ #
39
+ def self.mongoize(ipaddress)
40
+ ipaddress = self.parse(ipaddress) unless ipaddress.is_a?(IPAddress)
41
+ if ipaddress.bits.length == ipaddress.prefix
42
+ ipaddress.address
43
+ elsif ipaddress.is_a?(IPAddress::IPv6)
44
+ ipaddress.to_string_uncompressed
45
+ else
46
+ ipaddress.to_string
47
+ end
48
+ rescue ArgumentError
49
+ nil
50
+ end
51
+
52
+ #
53
+ # Mongoid field deserialization
54
+ #
55
+ def self.demongoize(string)
56
+ parse(string)
57
+ rescue ArgumentError
58
+ nil
59
+ end
60
+
61
+ #
62
+ # Delegates to IPAddress.mongoize
63
+ #
64
+ def self.evolve(ipaddress)
65
+ mongoize(ipaddress)
66
+ end
67
+
68
+ #
69
+ # Sends self object to IPAddress#mongoize
70
+ #
71
+ def mongoize
72
+ IPAddress.mongoize(self)
73
+ end
74
+
75
+ end