ip-ranges 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+ gem "netaddr"
3
+ gem "rdoc"
4
+ gem "rspec"
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ netaddr (1.5.0)
6
+ rdoc (3.9.4)
7
+ rspec (2.6.0)
8
+ rspec-core (~> 2.6.0)
9
+ rspec-expectations (~> 2.6.0)
10
+ rspec-mocks (~> 2.6.0)
11
+ rspec-core (2.6.4)
12
+ rspec-expectations (2.6.0)
13
+ diff-lcs (~> 1.1.2)
14
+ rspec-mocks (2.6.0)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ netaddr
21
+ rdoc
22
+ rspec
data/README.markdown ADDED
@@ -0,0 +1,57 @@
1
+ Compare IP ranges
2
+ =================
3
+
4
+ This gem allows you to take multiple IP ranges, which may be defined in several ways (as a single IP number, a dotted string or a CIDR range), and compare them against each other to check for uniqueness, overlaps, equivalence or one range containing another.
5
+
6
+
7
+ Example1 - Range class
8
+ ======================
9
+
10
+ r1 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
11
+ r2 = IpRanges::Range.new :range => '1.2.3.0/24'
12
+
13
+ r2.overlaps_range?(r1) => true
14
+ r2.contains_range?(r1) => true
15
+ r2 == r1 => false
16
+
17
+
18
+ Example2 - Check for overlaps
19
+ =============================
20
+
21
+ require 'rubygems'
22
+ require 'lib/ip-ranges'
23
+
24
+ include IpRanges
25
+
26
+ list = [
27
+ '1.2.3.4..1.2.3.5',
28
+ '1.2.3.0/24'
29
+ ]
30
+
31
+ puts check_for_overlaps(list)
32
+
33
+ # outputs: 1.2.3.4..1.2.3.5 is contained by range 1.2.3.0/24
34
+
35
+
36
+ MIT License
37
+ ===========
38
+
39
+ (c) David Salgado 2011, or whenever
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining a copy
42
+ of this software and associated documentation files (the "Software"), to deal
43
+ in the Software without restriction, including without limitation the rights
44
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
45
+ copies of the Software, and to permit persons to whom the Software is
46
+ furnished to do so, subject to the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be included in
49
+ all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,82 @@
1
+ require "rubygems/package_task"
2
+ require "rdoc/task"
3
+ require "rspec"
4
+ require "rspec/core/rake_task"
5
+ require 'rake/testtask'
6
+
7
+ $stdout.sync = true
8
+
9
+ task :default => :spec
10
+
11
+ task :spec do
12
+ system "bundle exec rspec spec/*_spec.rb spec/**/*_spec.rb"
13
+ end
14
+
15
+ # This builds the actual gem. For details of what all these options
16
+ # mean, and other ones you can add, check the documentation here:
17
+ #
18
+ # http://rubygems.org/read/chapter/20
19
+ #
20
+ spec = Gem::Specification.new do |s|
21
+
22
+ # Change these as appropriate
23
+ s.name = "ip-ranges"
24
+ s.version = "0.1.0"
25
+ s.summary = "Manage ranges of IP numbers determining equivalence, containment and overlaps"
26
+ s.author = "David Salgado"
27
+ s.email = "david@digitalronin.com"
28
+ s.homepage = "http://roninonrails.blogspot.com"
29
+
30
+ s.has_rdoc = true
31
+ s.description = "Compare multiple IP ranges for overlaps, equivalence and containment"
32
+ # s.extra_rdoc_files = %w(README)
33
+ s.rdoc_options = %w(-x pkg)
34
+
35
+ # Add any extra files to include in the gem (like your README)
36
+ s.files = %w(Gemfile Gemfile.lock Rakefile README.markdown) + Dir.glob("{spec,lib}/**/*")
37
+ s.require_paths = ["lib"]
38
+
39
+ # If you want to depend on other gems, add them here, along with any
40
+ # relevant versions
41
+ s.add_dependency("netaddr", "~> 1.5.0")
42
+
43
+ # If your tests use any gems, include them here
44
+ s.add_development_dependency("rspec")
45
+ s.add_development_dependency("rdoc")
46
+ end
47
+
48
+ # This task actually builds the gem. We also regenerate a static
49
+ # .gemspec file, which is useful if something (i.e. GitHub) will
50
+ # be automatically building a gem for this project. If you're not
51
+ # using GitHub, edit as appropriate.
52
+ #
53
+ # To publish your gem online, install the 'gemcutter' gem; Read more
54
+ # about that here: http://gemcutter.org/pages/gem_docs
55
+ Gem::PackageTask.new(spec) do |pkg|
56
+ pkg.gem_spec = spec
57
+ end
58
+
59
+ desc "Build the gemspec file #{spec.name}.gemspec"
60
+ task :gemspec do
61
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
62
+ File.open(file, "w") {|f| f << spec.to_ruby }
63
+ end
64
+
65
+ # If you don't want to generate the .gemspec file, just remove this line. Reasons
66
+ # why you might want to generate a gemspec:
67
+ # - using bundler with a git source
68
+ # - building the gem without rake (i.e. gem build blah.gemspec)
69
+ # - maybe others?
70
+ task :package => :gemspec
71
+
72
+ # Generate documentation
73
+ RDoc::Task.new do |rd|
74
+
75
+ rd.rdoc_files.include("lib/**/*.rb")
76
+ rd.rdoc_dir = "rdoc"
77
+ end
78
+
79
+ desc 'Clear out RDoc and generated packages'
80
+ task :clean => [:clobber_rdoc, :clobber_package] do
81
+ rm "#{spec.name}.gemspec"
82
+ end
data/lib/ip-ranges.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'netaddr'
2
+
3
+ # Manipulate and compare ranges of Ip numbers.
4
+ # In particular, this gem allows you to specify
5
+ # multiple Ip ranges, in a variety of formats,
6
+ # and compare them for overlaps, equivalence and
7
+ # containment (i.e. range1 contains all of range2)
8
+ #
9
+ # :title: IpRanges Gem
10
+ # Author:: David Salgado (mailto:david@digitalronin.com)
11
+ # Copyright:: Copyright (c) 2011 David Salgado
12
+ # License:: Distributes under the same terms as Ruby
13
+
14
+ module IpRanges
15
+
16
+ # Checks multiple ranges of IP numbers for uniqueness (in the sense that a given IP number
17
+ # should only appear once within the set of all supplied ranges).
18
+ #
19
+ # Arguments:
20
+ # * An array of strings that define single IP numbers or ranges (in dotted or CIDR notation)
21
+ #
22
+ # Returns:
23
+ # * An array of strings describing any overlaps, equivalences and containments which occur in the list (see the specs for details)
24
+ #
25
+ def check_for_overlaps(list)
26
+ rtn = []
27
+ while list.any?
28
+ test_range = list.shift
29
+ test = Range.new :range => test_range
30
+ list.each do |range|
31
+ i = Range.new :range => range
32
+ if test.overlaps_range?(i)
33
+ if test == i
34
+ rtn << "#{test_range} equals range #{range}"
35
+ elsif test.contains_range?(i)
36
+ rtn << "#{test_range} contains range #{range}"
37
+ elsif i.contains_range?(test)
38
+ rtn << "#{test_range} is contained by range #{range}"
39
+ else
40
+ rtn << "#{test_range} overlaps with #{range}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ rtn
46
+ end
47
+ end
48
+
49
+ libdir = File.join(File.dirname(__FILE__), 'ip-ranges')
50
+ require File.join(libdir, 'ip')
51
+ require File.join(libdir, 'range')
@@ -0,0 +1,140 @@
1
+ module IpRanges
2
+
3
+ # Class to represent a single IP number. Exposes methods for comparisons
4
+ # with other IP numbers, as well as a method to increment the IP number.
5
+ class Ip
6
+ # String representation of this Ip number
7
+ #
8
+ # Example:
9
+ #
10
+ # ip = Ip.new :number => '1.2.3.4'
11
+ # ip.number => '1.2.3.4'
12
+ #
13
+ attr_accessor :number
14
+
15
+ # Arguments:
16
+ #
17
+ # * A hash containing a single key whose value is the IP number as a string
18
+ #
19
+ # Example:
20
+ #
21
+ # Ip.new(:number => '1.2.3.4')
22
+ #
23
+ def initialize(params = {})
24
+ @number = params[:number]
25
+ end
26
+
27
+ # Example:
28
+ # Ip.new(:number => '1.2.3.4').to_s => '1.2.3.4'
29
+ def to_s
30
+ number
31
+ end
32
+
33
+ # Arguments:
34
+ #
35
+ # * another Ip object
36
+ #
37
+ # Example:
38
+ #
39
+ # ip1 = Ip.new(:number => '1.2.3.4')
40
+ # ip2 = Ip.new(:number => '1.2.3.5')
41
+ # ip3 = Ip.new(:number => '1.2.3.4')
42
+ #
43
+ # ip1 == ip2 => false
44
+ # ip1 == ip3 => true
45
+ def ==(ip)
46
+ number == ip.number
47
+ end
48
+
49
+ # Arguments:
50
+ #
51
+ # * another Ip object
52
+ #
53
+ # Example:
54
+ #
55
+ # ip1 = Ip.new(:number => '1.2.3.4')
56
+ # ip2 = Ip.new(:number => '1.2.3.5')
57
+ # ip3 = Ip.new(:number => '1.2.3.4')
58
+ #
59
+ # ip1 >= ip2 => false
60
+ # ip2 >= ip3 => true
61
+ # ip1 >= ip3 => true
62
+ def >=(ip)
63
+ self == ip || self > ip
64
+ end
65
+
66
+ # Arguments:
67
+ #
68
+ # * another Ip object
69
+ #
70
+ # Example:
71
+ #
72
+ # ip1 = Ip.new(:number => '1.2.3.4')
73
+ # ip2 = Ip.new(:number => '1.2.3.5')
74
+ #
75
+ # ip1 > ip2 => false
76
+ # ip2 > ip1 => true
77
+ # ip2 > ip2 => false
78
+ def >(ip)
79
+ a1, b1, c1, d1 = tuples number
80
+ a2, b2, c2, d2 = tuples ip.number
81
+ if a1 > a2
82
+ true
83
+ elsif a1 < a2
84
+ false
85
+ elsif b1 > b2
86
+ true
87
+ elsif b1 < b2
88
+ false
89
+ elsif c1 > c2
90
+ true
91
+ elsif c1 < c2
92
+ false
93
+ else
94
+ d1 > d2
95
+ end
96
+ end
97
+
98
+ # Changes the 'number' property of this Ip
99
+ # to the next number after this one.
100
+ #
101
+ # example:
102
+ #
103
+ # ip = Ip.new(:number => '1.2.3.4')
104
+ # ip.number => '1.2.3.4'
105
+ # ip.increment
106
+ # ip.number => '1.2.3.5'
107
+ #
108
+ def increment
109
+ a, b, c, d = tuples number
110
+ if d < 255
111
+ d += 1
112
+ else
113
+ d = 0
114
+ if c < 255
115
+ c += 1
116
+ else
117
+ c = 0
118
+ if b < 255
119
+ b += 1
120
+ else
121
+ b = 0
122
+ if a < 255
123
+ a += 1
124
+ else
125
+ raise "No more IPs"
126
+ end
127
+ end
128
+ end
129
+ end
130
+ @number = [a, b, c, d].join('.')
131
+ end
132
+
133
+ private
134
+
135
+ def tuples(string)
136
+ string.split(/\./).map {|i| i.to_i}
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,119 @@
1
+ module IpRanges
2
+
3
+ # Class to represent a range of IP numbers.
4
+ # Range objects are instantiated from a string which may be a single
5
+ # Exposes methods to compare this range with another range, and to
6
+ # iterate through all IP numbers in the range.
7
+ class Range
8
+ # First IP number in the range, as an Ip object
9
+ attr_accessor :first
10
+
11
+ # Last IP number in the range, as an Ip object
12
+ attr_accessor :last
13
+
14
+ SINGLE_IP = /^\d+\.\d+\.\d+\.\d+$/
15
+ DOTTED_RANGE = /^(\d+\.\d+\.\d+\.\d+)\.\.(\d+\.\d+\.\d+\.\d+)$/
16
+ CIDR_RANGE = %r[/]
17
+
18
+ BATCHSIZE = 250
19
+ PAUSE = 2
20
+
21
+ # Instatiate a range from a string representation which can be a single
22
+ # Ip number, a dotted range or a CIDR range
23
+ #
24
+ # Arguments:
25
+ # * A +Hash+ with a single key ':range' whose value is a string defining the range.
26
+ #
27
+ # Examples:
28
+ #
29
+ # IpRanges::Range.new :range => '1.2.3.4' => a range containing one Ip
30
+ # IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5' => a range containing two Ip numbers (spaces around the '..' are ignored)
31
+ # IpRanges::Range.new :range => '1.2.3.0/24' => a range containing 256 Ip numbers (1.2.3.0 .. 1.2.3.255)
32
+ #
33
+ def initialize(params)
34
+ string = params[:range].gsub(' ', '')
35
+ case string
36
+ when SINGLE_IP
37
+ @first = Ip.new :number => string
38
+ @last = Ip.new :number => string
39
+ when DOTTED_RANGE
40
+ @first = Ip.new :number => $1
41
+ @last = Ip.new :number => $2
42
+ when CIDR_RANGE
43
+ begin
44
+ cidr = NetAddr::CIDR.create(string)
45
+ @first = Ip.new :number => cidr.first
46
+ @last = Ip.new :number => cidr.last
47
+ rescue Exception => e
48
+ puts "Error creating CIDR range from: #{string}"
49
+ raise e
50
+ end
51
+ end
52
+ end
53
+
54
+ # Returns the last Ip object in this range.
55
+ def last
56
+ @last
57
+ end
58
+
59
+ # Returns the first Ip object in this range.
60
+ def first
61
+ @first
62
+ end
63
+
64
+ # Arguments
65
+ # * A +Range+ object
66
+ # True if this range is equivalent to the passed-in range.
67
+ def ==(range)
68
+ first == range.first && last == range.last
69
+ end
70
+
71
+ # Arguments
72
+ # * A +Range+ object
73
+ # True if this range intersects with the passed-in range.
74
+ def overlaps_range?(range)
75
+ contains_ip?(range.first) || contains_ip?(range.last) || range.contains_ip?(first) || range.contains_ip?(last)
76
+ end
77
+
78
+ # Arguments
79
+ # * A +Range+ object
80
+ # True if this range completely contains the passed-in range.
81
+ def contains_range?(range)
82
+ contains_ip?(range.first) && contains_ip?(range.last)
83
+ end
84
+
85
+ # Arguments
86
+ # * A +String+ or an +Ip+ object
87
+ # True if the range contains the IP
88
+ def contains_ip?(ip)
89
+ test = ip.kind_of?(String) ? Ip.new(:number => ip) : ip
90
+ test >= @first && @last >= test
91
+ end
92
+
93
+ # Yields each IP in the range, in turn, as an Ip object.
94
+ def each_ip
95
+ @counter = 0 # for throttling
96
+ ip = first
97
+ while last >= ip
98
+ yield ip.dup
99
+ ip.increment
100
+ throttle
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def throttle
107
+ @counter += 1
108
+ if @counter % BATCHSIZE == 0
109
+ take_a_nap
110
+ end
111
+ end
112
+
113
+ def take_a_nap
114
+ log "Counter: #{$counter}, sleeping for #{PAUSE} seconds" if $verbose
115
+ sleep PAUSE
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe IpRanges::Ip do
4
+ before do
5
+ @ip = IpRanges::Ip.new
6
+ end
7
+
8
+ it "duplicates with a different object" do
9
+ @ip.number = "1.200.3.4"
10
+ ip2 = @ip.dup
11
+ ip2.object_id.should_not == @ip.object_id
12
+ end
13
+
14
+ it "duplicates" do
15
+ @ip.number = "1.200.3.4"
16
+ ip2 = @ip.dup
17
+ ip2.should == @ip
18
+ end
19
+
20
+ it "is greater than or equal to" do
21
+ @ip.number = "1.200.3.4"
22
+ eq = IpRanges::Ip.new :number => "1.200.3.4"
23
+ lt = IpRanges::Ip.new :number => "1.200.3.3"
24
+ [eq, lt].each {|test| (@ip >= test).should be_true}
25
+ end
26
+
27
+ it "is equivalent" do
28
+ @ip.number = "1.200.3.4"
29
+ ip2 = IpRanges::Ip.new :number => "1.200.3.4"
30
+ @ip.should == ip2
31
+ end
32
+
33
+ it "renders to string" do
34
+ @ip.number = "1.200.3.4"
35
+ @ip.to_s.should == "1.200.3.4"
36
+ end
37
+
38
+ it "knows 1.2.3.255 < 1.2.4.0" do
39
+ @ip.number = '1.2.3.255'
40
+ ip2 = IpRanges::Ip.new :number => "1.2.4.0"
41
+ (@ip > ip2).should be_false
42
+ end
43
+
44
+ it "knows 1.2.4.0 >= 1.2.3.255" do
45
+ @ip.number = "1.2.4.0"
46
+ ip2 = IpRanges::Ip.new :number => '1.2.3.255'
47
+ (@ip >= ip2).should be_true
48
+ end
49
+
50
+ it "knows 1.200.3.4 > 1.199.3.4" do
51
+ @ip.number = "1.200.3.4"
52
+ ip2 = IpRanges::Ip.new :number => '1.199.3.4'
53
+ (@ip > ip2).should be_true
54
+ end
55
+
56
+ it "knows 1.2.3.4 > 1.2.2.4" do
57
+ @ip.number = "1.2.3.4"
58
+ ip2 = IpRanges::Ip.new :number => '1.2.2.4'
59
+ (@ip > ip2).should be_true
60
+ end
61
+
62
+ it "knows 1.2.3.4 > 1.2.3.3" do
63
+ @ip.number = "1.2.3.4"
64
+ ip2 = IpRanges::Ip.new :number => '1.2.3.3'
65
+ (@ip > ip2).should be_true
66
+ end
67
+
68
+ it "knows 1.2.3.3 <= 1.2.3.4" do
69
+ @ip.number = "1.2.3.3"
70
+ ip2 = IpRanges::Ip.new :number => '1.2.3.4'
71
+ (@ip > ip2).should be_false
72
+ end
73
+
74
+ it "increments when 2nd tuple hits 255" do
75
+ @ip.number = "1.255.255.255"
76
+ @ip.increment.should == "2.0.0.0"
77
+ end
78
+
79
+ it "increments when 3rd tuple hits 255" do
80
+ @ip.number = "1.2.255.255"
81
+ @ip.increment.should == "1.3.0.0"
82
+ end
83
+
84
+ it "increments when last tuple hits 255" do
85
+ @ip.number = "1.2.3.255"
86
+ @ip.increment.should == "1.2.4.0"
87
+ end
88
+
89
+ it "increments last tuple" do
90
+ @ip.number = '1.2.3.4'
91
+ @ip.increment.should == "1.2.3.5"
92
+ end
93
+
94
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe IpRanges::Range do
4
+ before do
5
+ end
6
+
7
+ context "checking for overlaps" do
8
+
9
+ it "knows when range a contains range b" do
10
+ ipr1 = IpRanges::Range.new :range => '1.2.3.3..1.2.3.5'
11
+ ipr2 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
12
+ ipr1.contains_range?(ipr2).should be_true
13
+ end
14
+
15
+ it "knows when range a does not contain range b" do
16
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
17
+ ipr2 = IpRanges::Range.new :range => '1.2.3.5..1.2.3.6'
18
+ ipr1.contains_range?(ipr2).should be_false
19
+ end
20
+
21
+ it "knows there is an overlap" do
22
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4'
23
+ ipr2 = IpRanges::Range.new :range => '1.2.3.4'
24
+ ipr1.overlaps_range?(ipr2).should be_true
25
+ end
26
+
27
+ it "knows there is no overlap" do
28
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4'
29
+ ipr2 = IpRanges::Range.new :range => '1.2.3.5'
30
+ ipr1.overlaps_range?(ipr2).should be_false
31
+ end
32
+
33
+ it "accepts Ip object for contains_ip" do
34
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
35
+ ip = IpRanges::Ip.new :number => '1.2.3.5'
36
+ ipr.contains_ip?(ip).should be_true
37
+ end
38
+
39
+ it "knows it doesn't contain ip numbers" do
40
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
41
+ %w(1.2.3.3 1.2.3.7).each {|num| ipr.contains_ip?(num).should be_false}
42
+ end
43
+
44
+ it "knows it contains an ip number" do
45
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
46
+ %w(1.2.3.4 1.2.3.5 1.2.3.6).each {|num| ipr.contains_ip?(num).should be_true}
47
+ end
48
+
49
+ end
50
+
51
+ it "knows it equals another range" do
52
+ dotted = IpRanges::Range.new :range => '1.2.3.0..1.2.3.255'
53
+ cidr = IpRanges::Range.new :range => '1.2.3.0/24'
54
+ (dotted == cidr).should be_true
55
+ (cidr == dotted).should be_true
56
+ end
57
+
58
+ it "iterates over all ips" do
59
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.6'
60
+ expected = [
61
+ IpRanges::Ip.new(:number => '1.2.3.4'),
62
+ IpRanges::Ip.new(:number => '1.2.3.5'),
63
+ IpRanges::Ip.new(:number => '1.2.3.6')
64
+ ]
65
+ arr = []
66
+ ipr.each_ip {|ip| arr << ip}
67
+ arr.should == expected
68
+ end
69
+
70
+ it "returns last ip from cidr range" do
71
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
72
+ ipr.last.to_s.should == '1.2.3.255'
73
+ end
74
+
75
+ it "returns first ip from cidr range" do
76
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
77
+ ipr.first.to_s.should == '1.2.3.0'
78
+ end
79
+
80
+ it "instantiates from a cidr range" do
81
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
82
+ ipr.should be_kind_of(IpRanges::Range)
83
+ end
84
+
85
+ it "returns last ip from dotted range" do
86
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
87
+ ipr.last.to_s.should == '1.2.3.5'
88
+ end
89
+
90
+ it "returns first ip from dotted range" do
91
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
92
+ ipr.first.to_s.should == '1.2.3.4'
93
+ end
94
+
95
+ it "instantiates from a dotted range" do
96
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
97
+ ipr.should be_kind_of(IpRanges::Range)
98
+ end
99
+
100
+ it "returns last ip" do
101
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
102
+ ipr.last.to_s.should == '1.2.3.4'
103
+ end
104
+
105
+ it "returns first ip" do
106
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
107
+ ipr.first.to_s.should == '1.2.3.4'
108
+ end
109
+
110
+ it "instantiates from a single IP" do
111
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
112
+ ipr.should be_kind_of(IpRanges::Range)
113
+ end
114
+
115
+ it "instantiates" do
116
+ ipr = IpRanges::Range.new :range => ''
117
+ ipr.should be_kind_of(IpRanges::Range)
118
+ end
119
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe IpRanges do
4
+ include IpRanges
5
+
6
+ context "checking for overlaps" do
7
+
8
+ it "explains range a equals range b" do
9
+ list = %w(1.2.3.0..1.2.3.255 1.2.3.0/24)
10
+ overlaps = check_for_overlaps(list)
11
+ overlaps[0].should == "1.2.3.0..1.2.3.255 equals range 1.2.3.0/24"
12
+ end
13
+
14
+ it "explains range a is contained by range b" do
15
+ list = %w(1.2.3.4..1.2.3.5 1.2.3.4..1.2.3.8)
16
+ overlaps = check_for_overlaps(list)
17
+ overlaps[0].should == "1.2.3.4..1.2.3.5 is contained by range 1.2.3.4..1.2.3.8"
18
+ end
19
+
20
+ it "explains range a contains range b" do
21
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.4..1.2.3.5)
22
+ overlaps = check_for_overlaps(list)
23
+ overlaps[0].should == "1.2.3.4..1.2.3.8 contains range 1.2.3.4..1.2.3.5"
24
+ end
25
+
26
+ it "explains overlap" do
27
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.5..1.2.3.9)
28
+ overlaps = check_for_overlaps(list)
29
+ overlaps[0].should == "1.2.3.4..1.2.3.8 overlaps with 1.2.3.5..1.2.3.9"
30
+ end
31
+
32
+ it "counts the overlaps" do
33
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.4..1.2.3.5 1.2.3.8)
34
+ check_for_overlaps(list).size.should == 2
35
+ end
36
+
37
+ it "takes a list" do
38
+ check_for_overlaps []
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1 @@
1
+ require 'lib/ip-ranges'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ip-ranges
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - David Salgado
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: netaddr
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 1
31
+ - 5
32
+ - 0
33
+ version: 1.5.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rdoc
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ description: Compare multiple IP ranges for overlaps, equivalence and containment
65
+ email: david@digitalronin.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - Gemfile
74
+ - Gemfile.lock
75
+ - Rakefile
76
+ - README.markdown
77
+ - spec/ip-ranges/ip_spec.rb
78
+ - spec/ip-ranges/range_spec.rb
79
+ - spec/ip_ranges_spec.rb
80
+ - spec/spec_helper.rb
81
+ - lib/ip-ranges/ip.rb
82
+ - lib/ip-ranges/range.rb
83
+ - lib/ip-ranges.rb
84
+ homepage: http://roninonrails.blogspot.com
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options:
89
+ - -x
90
+ - pkg
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.10
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Manage ranges of IP numbers determining equivalence, containment and overlaps
118
+ test_files: []
119
+