ipaddress 0.8.0 → 0.8.2

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