ip 0.1.1 → 0.2.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/examples/check-included.rb +27 -0
- data/examples/check-overlap.rb +26 -0
- data/examples/generate-a-records.rb +26 -0
- data/lib/ip.rb +9 -339
- data/lib/ip/address.rb +393 -0
- data/lib/ip/cidr.rb +192 -0
- data/lib/ip/range.rb +54 -0
- data/lib/ip/util.rb +144 -0
- data/test/ip.rb +394 -84
- metadata +10 -2
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Checks if a given IP address exists in a subnet.
|
4
|
+
#
|
5
|
+
# Takes either IPv6 or IPv4.
|
6
|
+
#
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rubygems'
|
10
|
+
rescue LoadError => e
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'ip'
|
14
|
+
|
15
|
+
if !ARGV[0] or !ARGV[1]
|
16
|
+
$stderr.puts "usage: #{File.basename($0)} <cidr> <ip>"
|
17
|
+
exit -1
|
18
|
+
end
|
19
|
+
|
20
|
+
if IP::CIDR.new(ARGV[0]).includes? IP::Address::Util.string_to_ip(ARGV[1])
|
21
|
+
$stderr.puts "IP #{ARGV[1]} exists in the #{ARGV[0]} subnet"
|
22
|
+
exit 0
|
23
|
+
else
|
24
|
+
$stderr.puts "IP #{ARGV[1]} does not exist in the #{ARGV[0]} subnet"
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# given two CIDR formatted addresses, check for overlaps.
|
4
|
+
#
|
5
|
+
# Takes either IPv6 or IPv4.
|
6
|
+
#
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rubygems'
|
10
|
+
rescue LoadError => e
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'ip'
|
14
|
+
|
15
|
+
if !ARGV[0] or !ARGV[1]
|
16
|
+
$stderr.puts "usage: #{File.basename($0)} <cidr> <cidr>"
|
17
|
+
exit -1
|
18
|
+
end
|
19
|
+
|
20
|
+
if IP::CIDR.new(ARGV[0]).overlaps? IP::CIDR.new(ARGV[1])
|
21
|
+
$stderr.puts "These address ranges overlap."
|
22
|
+
exit 1
|
23
|
+
else
|
24
|
+
$stderr.puts "No overlaps"
|
25
|
+
exit 0
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Generate a set of A records suitable for inclusion in a
|
4
|
+
# zone.
|
5
|
+
#
|
6
|
+
# Takes two IP addresses as a range to generate.
|
7
|
+
#
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'rubygems'
|
11
|
+
rescue LoadError => e
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'ip'
|
15
|
+
|
16
|
+
if !ARGV[0] or !ARGV[1]
|
17
|
+
$stderr.puts "usage: #{File.basename($0)} <start ip> <end ip>"
|
18
|
+
exit -1
|
19
|
+
end
|
20
|
+
|
21
|
+
$fmt = "%15.15s IN A %15.15s"
|
22
|
+
|
23
|
+
IP::Range[ARGV[0], ARGV[1]].each do |ip|
|
24
|
+
hostname = ip.ip_address.gsub(/\./, "-")
|
25
|
+
puts ($fmt % [hostname, ip.ip_address])
|
26
|
+
end
|
data/lib/ip.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
# Author:: Erik Hollensbe
|
6
6
|
# License:: BSD
|
7
7
|
# Contact:: erik@hollensbe.org
|
8
|
+
# Copyright:: Copyright (c) 2005-2006 Erik Hollensbe
|
8
9
|
#
|
9
10
|
# IP is, as mentioned above, a collection of tools to work
|
10
11
|
# with IP addresses. There are three major classes included
|
@@ -30,7 +31,7 @@
|
|
30
31
|
# methods that made code easier to understand and/or clearer, and
|
31
32
|
# making plenty of suggestions made creating this module much easier.
|
32
33
|
#
|
33
|
-
|
34
|
+
#--
|
34
35
|
#
|
35
36
|
# The compilation of software known as ip.rb is distributed under the
|
36
37
|
# following terms:
|
@@ -54,6 +55,7 @@
|
|
54
55
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
55
56
|
# SUCH DAMAGE.
|
56
57
|
#
|
58
|
+
#++
|
57
59
|
|
58
60
|
class IP
|
59
61
|
|
@@ -74,344 +76,12 @@ class IP
|
|
74
76
|
class BoundaryException < Exception
|
75
77
|
end
|
76
78
|
|
77
|
-
#
|
78
|
-
# IP::Range - Calculates a range of IP addresses, and returns an
|
79
|
-
# Array of IP::Address objects.
|
80
|
-
#
|
81
|
-
# Usage::
|
82
|
-
#
|
83
|
-
# IP::Range['10.0.0.1', '10.0.0.2'] => IP::Address objects between
|
84
|
-
# 10.0.0.1 and 10.0.0.2 (inclusive)
|
85
|
-
#
|
86
|
-
# IP::Range can also take two IP::Address objects.
|
87
|
-
#
|
88
|
-
# Will throw a IP::AddressException if for some reason addresses
|
89
|
-
# cannot be parsed.
|
90
|
-
#
|
91
|
-
|
92
|
-
class Range
|
93
|
-
|
94
|
-
#
|
95
|
-
# See the documentation for IP::Range for more information on this
|
96
|
-
# method.
|
97
|
-
#
|
98
|
-
|
99
|
-
def Range.[](addr1, addr2)
|
100
|
-
raw1, raw2 = [nil, nil]
|
101
|
-
|
102
|
-
if addr1.kind_of? String
|
103
|
-
raw1 = IP::Address::Util.pack(IP::Address.new(addr1))
|
104
|
-
elsif addr1.kind_of? IP::Address
|
105
|
-
raw1 = IP::Address::Util.pack(addr1)
|
106
|
-
else
|
107
|
-
raise IP::AddressException("IP Address is not type String or IP::Address")
|
108
|
-
end
|
109
|
-
|
110
|
-
if addr2.kind_of? String
|
111
|
-
raw2 = IP::Address::Util.pack(IP::Address.new(addr2))
|
112
|
-
elsif addr2.kind_of? IP::Address
|
113
|
-
raw2 = IP::Address::Util.pack(addr2)
|
114
|
-
else
|
115
|
-
raise IP::AddressException("IP Address is not type String or IP::Address")
|
116
|
-
end
|
117
|
-
|
118
|
-
range = []
|
119
|
-
|
120
|
-
(raw1..raw2).each { |x| range.push(IP::Address::Util.unpack(x)) }
|
121
|
-
|
122
|
-
return range
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# IP::CIDR - Works with Classless Inter-Domain Routing formats, such
|
128
|
-
# as 10.0.0.1/32 or 10.0.0.1/255.255.255.255
|
129
|
-
#
|
130
|
-
|
131
|
-
class CIDR
|
132
|
-
#
|
133
|
-
# Contains the original CIDR you fed it, returned as a string.
|
134
|
-
#
|
135
|
-
attr_reader :cidr
|
136
|
-
#
|
137
|
-
# Contains the IP address (LHS) only. Returned as an IP::Address
|
138
|
-
# object.
|
139
|
-
#
|
140
|
-
attr_reader :ip
|
141
|
-
#
|
142
|
-
# Contains the integer-based (short) netmask (RHS) only. Returned as an
|
143
|
-
# integer.
|
144
|
-
#
|
145
|
-
attr_reader :mask
|
146
|
-
|
147
|
-
#
|
148
|
-
# Given a string of format X.X.X.X/X, in standard CIDR notation,
|
149
|
-
# this will construct a IP::CIDR object.
|
150
|
-
#
|
151
|
-
def initialize(cidr)
|
152
|
-
if !cidr.kind_of? String
|
153
|
-
raise IP::AddressException.new("CIDR value is not of type String")
|
154
|
-
end
|
155
|
-
|
156
|
-
@cidr = cidr
|
157
|
-
@ip, @mask = cidr.split(/\//, 2)
|
158
|
-
|
159
|
-
if @ip.nil? or @mask.nil?
|
160
|
-
raise IP::AddressException.new("CIDR is not valid - invalid format")
|
161
|
-
end
|
162
|
-
|
163
|
-
if @mask.length == 0 or /[^0-9.]/.match @mask
|
164
|
-
raise IP::AddressException.new("CIDR RHS is not valid - #{@mask}")
|
165
|
-
end
|
166
|
-
|
167
|
-
if @mask.length > 2
|
168
|
-
# this will throw an exception if the netmask is malformed.
|
169
|
-
@mask = IP::Address::Util.short_netmask(IP::Address.new(@mask))
|
170
|
-
end
|
171
|
-
|
172
|
-
@ip = IP::Address.new(@ip)
|
173
|
-
@mask = @mask.to_i
|
174
|
-
end
|
175
|
-
|
176
|
-
def netmask
|
177
|
-
warn "IP::CIDR#netmask is deprecated. Please use IP::CIDR#long_netmask instead."
|
178
|
-
return self.long_netmask
|
179
|
-
end
|
180
|
-
|
181
|
-
#
|
182
|
-
# This produces the long netmask (eg. 255.255.255.255) of the CIDR in an
|
183
|
-
# IP::Address object.
|
184
|
-
#
|
185
|
-
def long_netmask
|
186
|
-
return IP::Address::Util.long_netmask(@mask)
|
187
|
-
end
|
188
|
-
|
189
|
-
#
|
190
|
-
# This produces the short netmask (eg. 32) of the CIDR in an IP::Address
|
191
|
-
# object.
|
192
|
-
#
|
193
|
-
|
194
|
-
def short_netmask
|
195
|
-
return @mask
|
196
|
-
end
|
197
|
-
|
198
|
-
#
|
199
|
-
# This produces a range ala IP::Range, but only for the subnet
|
200
|
-
# defined by the CIDR object.
|
201
|
-
#
|
202
|
-
def range
|
203
|
-
return IP::Range[self.first_ip, self.last_ip]
|
204
|
-
end
|
205
|
-
|
206
|
-
#
|
207
|
-
# This returns the first ip address of the cidr as an IP::Address object.
|
208
|
-
#
|
209
|
-
def first_ip
|
210
|
-
rawip = IP::Address::Util.pack(@ip)
|
211
|
-
rawnm = 0xFFFFFFFF << (32 - @mask)
|
212
|
-
lower = rawip & rawnm
|
213
|
-
return IP::Address::Util.unpack(lower)
|
214
|
-
end
|
215
|
-
|
216
|
-
#
|
217
|
-
# This returns the last ip address of the cidr as an IP::Address object.
|
218
|
-
#
|
219
|
-
def last_ip
|
220
|
-
rawip = IP::Address::Util.pack(@ip)
|
221
|
-
rawnm = 0xFFFFFFFF << (32 - @mask)
|
222
|
-
upper = rawip | ~rawnm
|
223
|
-
return IP::Address::Util.unpack(upper)
|
224
|
-
end
|
225
|
-
|
226
|
-
#
|
227
|
-
# This will take another IP::CIDR object as an argument and check to see
|
228
|
-
# if it overlaps with this cidr object. Returns true/false on overlap.
|
229
|
-
#
|
230
|
-
# This also throws a TypeError if passed invalid data.
|
231
|
-
#
|
232
|
-
def overlaps?(other_cidr)
|
233
|
-
raise TypeError.new("Expected object of type IP::CIDR") unless(other_cidr.kind_of?(IP::CIDR))
|
234
|
-
|
235
|
-
myfirst = IP::Address::Util.pack(self.first_ip)
|
236
|
-
mylast = IP::Address::Util.pack(self.last_ip)
|
237
|
-
|
238
|
-
otherfirst = IP::Address::Util.pack(other_cidr.first_ip)
|
239
|
-
otherlast = IP::Address::Util.pack(other_cidr.last_ip)
|
240
|
-
|
241
|
-
return ((myfirst >= otherfirst && myfirst <= otherlast) ||
|
242
|
-
(mylast <= otherlast && mylast >= otherfirst) ||
|
243
|
-
(otherfirst >= myfirst && otherfirst <= mylast)) ? true : false;
|
244
|
-
end
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
|
-
#
|
249
|
-
# IP::Address - utility class to work with dotted-quad IP addresses.
|
250
|
-
#
|
251
|
-
class Address
|
252
|
-
#
|
253
|
-
# This original IP Address you passed it, returned as a string.
|
254
|
-
#
|
255
|
-
attr_reader :ip_address
|
256
|
-
#
|
257
|
-
# This returns an Array of Integer which contains the octets of
|
258
|
-
# the IP, in descending order.
|
259
|
-
#
|
260
|
-
attr_reader :octets
|
261
|
-
|
262
|
-
#
|
263
|
-
# When given a string, constructs an IP::Address object.
|
264
|
-
#
|
265
|
-
def initialize(ip_address)
|
266
|
-
if ! ip_address.kind_of? String
|
267
|
-
raise IP::AddressException.new("Fed IP address is not String")
|
268
|
-
end
|
269
|
-
@ip_address = ip_address
|
270
|
-
|
271
|
-
#
|
272
|
-
# Unbeknowest by me, to_i will not throw an exception if the string
|
273
|
-
# can't be converted cleanly - it just truncates, similar to atoi() and perl's int().
|
274
|
-
#
|
275
|
-
# Code below does a final sanity check.
|
276
|
-
#
|
277
|
-
|
278
|
-
octets = ip_address.split(/\./)
|
279
|
-
octets_i = octets.collect { |x| x.to_i }
|
280
|
-
|
281
|
-
0.upto(octets.length - 1) do |octet|
|
282
|
-
if octets[octet] != octets_i[octet].to_s
|
283
|
-
raise IP::AddressException.new("Integer conversion failed")
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
@octets = octets_i
|
288
|
-
|
289
|
-
# I made a design decision to allow 0.0.0.0 here.
|
290
|
-
if @octets.length != 4 or @octets.find_all { |x| x > 255 }.length > 0
|
291
|
-
raise IP::AddressException.new("IP address is improperly formed")
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
#
|
296
|
-
# Returns an octet given the proper index. The octets returned are
|
297
|
-
# Integer types.
|
298
|
-
#
|
299
|
-
def [](num)
|
300
|
-
if num > 3
|
301
|
-
raise IP::BoundaryException.new("Max octet number is 3")
|
302
|
-
end
|
303
|
-
return @octets[num]
|
304
|
-
end
|
305
|
-
|
306
|
-
#
|
307
|
-
# See [].
|
308
|
-
#
|
309
|
-
alias_method :octet, :[]
|
310
|
-
|
311
|
-
#
|
312
|
-
# Class method to pack an IP::Address object into a long integer
|
313
|
-
# used for calculation. Returns a 'FixNum' type.
|
314
|
-
#
|
315
|
-
# This method is deprecated. Please use IP::Address::Util#pack instead.
|
316
|
-
#
|
317
|
-
def Address.pack(ip)
|
318
|
-
warn "IP::Address#pack is deprecated. Please use IP::Address::Util#pack instead."
|
319
|
-
return IP::Address::Util.pack(ip)
|
320
|
-
end
|
321
|
-
|
322
|
-
#
|
323
|
-
# Class method to take a 'FixNum' type and return an IP::Address
|
324
|
-
# object.
|
325
|
-
#
|
326
|
-
# This method is deprecated. Please use IP::Address::Util#unpack instead.
|
327
|
-
#
|
328
|
-
def Address.unpack(ip)
|
329
|
-
warn "IP::Address#unpack is deprecated. Please use IP::Address::Util#unpack instead."
|
330
|
-
return IP::Address::Util.unpack(ip)
|
331
|
-
end
|
332
|
-
|
333
|
-
end
|
334
|
-
|
335
79
|
end
|
336
80
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
ret = 0
|
344
|
-
myip = ip.octets.reverse
|
345
|
-
4.times { |x| ret = ret | (myip[x] & 0xFF) << 8*x }
|
346
|
-
return ret
|
347
|
-
end
|
348
|
-
|
349
|
-
module_function :pack
|
350
|
-
|
351
|
-
#
|
352
|
-
# Take a 'FixNum' type and return an IP::Address object.
|
353
|
-
#
|
354
|
-
def unpack(ip)
|
355
|
-
ret = []
|
356
|
-
4.times { |x| ret.push((ip >> 8*x) & 0xFF) }
|
357
|
-
return IP::Address.new(ret.reverse.join("."))
|
358
|
-
end
|
359
|
-
|
360
|
-
module_function :unpack
|
361
|
-
|
362
|
-
#
|
363
|
-
# Given an IP::Address object suitable for a netmask, returns the CIDR-notation
|
364
|
-
# "short" netmask.
|
365
|
-
#
|
366
|
-
# ex:
|
367
|
-
# short_netmask(IP::Address.new("255.255.255.255")) => 32
|
368
|
-
# short_netmask(IP::Address.new("255.255.255.240")) => 28
|
369
|
-
#
|
370
|
-
|
371
|
-
def short_netmask(ip)
|
372
|
-
a = []
|
373
|
-
(0..3).each do |x|
|
374
|
-
if x < 3 && ip[x] < 255 && ip[x+1] > 0
|
375
|
-
raise IP::BoundaryException.new("Invalid Netmask")
|
376
|
-
end
|
377
|
-
a += binary_vector(ip[x])
|
378
|
-
end
|
379
|
-
retval = 0
|
380
|
-
a.each { |x| retval += x }
|
381
|
-
return retval
|
382
|
-
end
|
383
|
-
|
384
|
-
module_function :short_netmask
|
385
|
-
|
386
|
-
#
|
387
|
-
# Given a CIDR-notation "short" netmask, returns a IP::Address object containing
|
388
|
-
# the equivalent "long" netmask.
|
389
|
-
#
|
390
|
-
# ex:
|
391
|
-
# long_netmask(32) => IP::Address object of "255.255.255.255"
|
392
|
-
# long_netmask(28) => IP::Address object of "255.255.255.240"
|
393
|
-
#
|
394
|
-
|
395
|
-
def long_netmask(short)
|
396
|
-
raw = 0xFFFFFFFF << (32 - short)
|
397
|
-
return IP::Address::Util.unpack(raw)
|
398
|
-
end
|
399
|
-
|
400
|
-
module_function :long_netmask
|
401
|
-
|
402
|
-
#
|
403
|
-
# Given a number (presumably an octet), will produce a binary vector indicating the
|
404
|
-
# on/off positions as 1 or 0.
|
405
|
-
#
|
406
|
-
# ex:
|
407
|
-
# binary_vector(255) => [1, 1, 1, 1, 1, 1, 1, 1]
|
408
|
-
# binary_vector(240) => [1, 1, 1, 1, 0, 0, 0, 0]
|
409
|
-
#
|
81
|
+
$:.unshift(File.dirname(__FILE__))
|
82
|
+
require 'ip/address'
|
83
|
+
require 'ip/cidr'
|
84
|
+
require 'ip/range'
|
85
|
+
require 'ip/util'
|
86
|
+
$:.shift
|
410
87
|
|
411
|
-
def binary_vector(octet)
|
412
|
-
return octet.to_i.to_s(2).split(//).collect { |x| x.to_i }
|
413
|
-
end
|
414
|
-
|
415
|
-
module_function :binary_vector
|
416
|
-
|
417
|
-
end
|