ip-ranges 0.1.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.
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/README.markdown +57 -0
- data/Rakefile +82 -0
- data/lib/ip-ranges.rb +51 -0
- data/lib/ip-ranges/ip.rb +140 -0
- data/lib/ip-ranges/range.rb +119 -0
- data/spec/ip-ranges/ip_spec.rb +94 -0
- data/spec/ip-ranges/range_spec.rb +119 -0
- data/spec/ip_ranges_spec.rb +43 -0
- data/spec/spec_helper.rb +1 -0
- metadata +119 -0
data/Gemfile
ADDED
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')
|
data/lib/ip-ranges/ip.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|