ip 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/ip.rb +141 -43
  2. data/test/ip.rb +274 -0
  3. metadata +5 -4
data/lib/ip.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # IP - Collection of Tools to work with IP addresses
3
3
  #
4
- # Version:: 0.0.2
4
+ # Version:: 0.1.0
5
5
  # Author:: Erik Hollensbe
6
6
  # License:: BSD
7
7
  # Contact:: erik@hollensbe.org
@@ -95,24 +95,24 @@ class IP
95
95
  raw1, raw2 = [nil, nil]
96
96
 
97
97
  if addr1.kind_of? String
98
- raw1 = IP::Address.pack(IP::Address.new(addr1))
98
+ raw1 = IP::Address::Util.pack(IP::Address.new(addr1))
99
99
  elsif addr1.kind_of? IP::Address
100
- raw1 = IP::Address.pack(addr1)
100
+ raw1 = IP::Address::Util.pack(addr1)
101
101
  else
102
102
  raise IP::AddressException("IP Address is not type String or IP::Address")
103
103
  end
104
104
 
105
105
  if addr2.kind_of? String
106
- raw2 = IP::Address.pack(IP::Address.new(addr2))
106
+ raw2 = IP::Address::Util.pack(IP::Address.new(addr2))
107
107
  elsif addr2.kind_of? IP::Address
108
- raw2 = IP::Address.pack(addr2)
108
+ raw2 = IP::Address::Util.pack(addr2)
109
109
  else
110
110
  raise IP::AddressException("IP Address is not type String or IP::Address")
111
111
  end
112
112
 
113
113
  range = []
114
114
 
115
- (raw1..raw2).each { |x| range.push(IP::Address.unpack(x)) }
115
+ (raw1..raw2).each { |x| range.push(IP::Address::Util.unpack(x)) }
116
116
 
117
117
  return range
118
118
  end
@@ -120,13 +120,9 @@ class IP
120
120
 
121
121
  #
122
122
  # IP::CIDR - Works with Classless Inter-Domain Routing formats, such
123
- # as 10.0.0.1/32.
123
+ # as 10.0.0.1/32 or 10.0.0.1/255.255.255.255
124
124
  #
125
- # Note: this class does not work with expanded RHS
126
- # netmasks, such as 10.0.0.1/255.255.255.255. This is to be
127
- # corrected in a later release.
128
- #
129
-
125
+
130
126
  class CIDR
131
127
  #
132
128
  # Contains the original CIDR you fed it, returned as a string.
@@ -138,7 +134,7 @@ class IP
138
134
  #
139
135
  attr_reader :ip
140
136
  #
141
- # Contains the integer-based netmask (RHS) only. Returned as an
137
+ # Contains the integer-based (short) netmask (RHS) only. Returned as an
142
138
  # integer.
143
139
  #
144
140
  attr_reader :mask
@@ -149,26 +145,49 @@ class IP
149
145
  #
150
146
  def initialize(cidr)
151
147
  if !cidr.kind_of? String
152
- raise IP::AddressException("CIDR value is not of type String")
148
+ raise IP::AddressException.new("CIDR value is not of type String")
153
149
  end
154
150
 
155
151
  @cidr = cidr
156
- @ip, @mask = cidr.split(/\//)
152
+ @ip, @mask = cidr.split(/\//, 2)
153
+
154
+ if @ip.nil? or @mask.nil?
155
+ raise IP::AddressException.new("CIDR is not valid - invalid format")
156
+ end
157
157
 
158
- if @mask.length == 0 or @mask.length > 2 or /\D/.match @mask
159
- raise IP::AddressException("CIDR RHS is not valid - #{@mask}")
158
+ if @mask.length == 0 or /[^0-9.]/.match @mask
159
+ raise IP::AddressException.new("CIDR RHS is not valid - #{@mask}")
160
+ end
161
+
162
+ if @mask.length > 2
163
+ # this will throw an exception if the netmask is malformed.
164
+ @mask = IP::Address::Util.short_netmask(IP::Address.new(@mask))
160
165
  end
161
166
 
162
167
  @ip = IP::Address.new(@ip)
163
168
  @mask = @mask.to_i
164
169
  end
165
170
 
171
+ def netmask
172
+ warn "IP::CIDR#netmask is deprecated. Please use IP::CIDR#long_netmask instead."
173
+ return self.long_netmask
174
+ end
175
+
166
176
  #
167
- # This produces the netmask of the CIDR in an IP::Address object.
177
+ # This produces the long netmask (eg. 255.255.255.255) of the CIDR in an
178
+ # IP::Address object.
168
179
  #
169
- def netmask
170
- raw = 0xFFFFFFFF << (32 - @mask)
171
- return IP::Address.unpack(raw)
180
+ def long_netmask
181
+ return IP::Address::Util.long_netmask(@mask)
182
+ end
183
+
184
+ #
185
+ # This produces the short netmask (eg. 32) of the CIDR in an IP::Address
186
+ # object.
187
+ #
188
+
189
+ def short_netmask
190
+ return @mask
172
191
  end
173
192
 
174
193
  #
@@ -176,31 +195,27 @@ class IP
176
195
  # defined by the CIDR object.
177
196
  #
178
197
  def range
179
- rawip = IP::Address.pack(@ip)
180
- rawnm = 0xFFFFFFFF << (32 - @mask)
181
- upper = rawip | ~rawnm
182
- lower = rawip & rawnm
183
- return IP::Range[IP::Address.unpack(lower), IP::Address.unpack(upper)]
198
+ return IP::Range[self.first_ip, self.last_ip]
184
199
  end
185
200
 
186
201
  #
187
202
  # This returns the first ip address of the cidr as an IP::Address object.
188
203
  #
189
204
  def first_ip
190
- rawip = IP::Address.pack(@ip)
205
+ rawip = IP::Address::Util.pack(@ip)
191
206
  rawnm = 0xFFFFFFFF << (32 - @mask)
192
207
  lower = rawip & rawnm
193
- return IP::Address.unpack(lower)
208
+ return IP::Address::Util.unpack(lower)
194
209
  end
195
210
 
196
211
  #
197
212
  # This returns the last ip address of the cidr as an IP::Address object.
198
213
  #
199
214
  def last_ip
200
- rawip = IP::Address.pack(@ip)
215
+ rawip = IP::Address::Util.pack(@ip)
201
216
  rawnm = 0xFFFFFFFF << (32 - @mask)
202
217
  upper = rawip | ~rawnm
203
- return IP::Address.unpack(upper)
218
+ return IP::Address::Util.unpack(upper)
204
219
  end
205
220
 
206
221
  #
@@ -210,13 +225,13 @@ class IP
210
225
  # This also throws a TypeError if passed invalid data.
211
226
  #
212
227
  def overlaps?(other_cidr)
213
- fail TypeError.new("Expected object of type IP::Address") unless(other_cidr.kind_of?(IP::CIDR))
228
+ raise TypeError.new("Expected object of type IP::CIDR") unless(other_cidr.kind_of?(IP::CIDR))
214
229
 
215
- myfirst = IP::Address.pack(self.first_ip)
216
- mylast = IP::Address.pack(self.last_ip)
230
+ myfirst = IP::Address::Util.pack(self.first_ip)
231
+ mylast = IP::Address::Util.pack(self.last_ip)
217
232
 
218
- otherfirst = IP::Address.pack(other_cidr.first_ip)
219
- otherlast = IP::Address.pack(other_cidr.last_ip)
233
+ otherfirst = IP::Address::Util.pack(other_cidr.first_ip)
234
+ otherlast = IP::Address::Util.pack(other_cidr.last_ip)
220
235
 
221
236
  return ((myfirst >= otherfirst && myfirst <= otherlast) ||
222
237
  (mylast <= otherlast && mylast >= otherfirst) ||
@@ -244,7 +259,7 @@ class IP
244
259
  #
245
260
  def initialize(ip_address)
246
261
  if ! ip_address.kind_of? String
247
- raise IP::AddressException("Fed IP address is not String")
262
+ raise IP::AddressException.new("Fed IP address is not String")
248
263
  end
249
264
  @ip_address = ip_address
250
265
  @octets = ip_address.split(/\./).collect { |x| x.to_i }
@@ -275,23 +290,106 @@ class IP
275
290
  # Class method to pack an IP::Address object into a long integer
276
291
  # used for calculation. Returns a 'FixNum' type.
277
292
  #
293
+ # This method is deprecated. Please use IP::Address::Util#pack instead.
294
+ #
278
295
  def Address.pack(ip)
279
- ret = 0
280
- myip = ip.octets.reverse
281
- 4.times { |x| ret = ret | (myip[x] & 0xFF) << 8*x }
282
- return ret
296
+ warn "IP::Address#pack is deprecated. Please use IP::Address::Util#pack instead."
297
+ return IP::Address::Util.pack(ip)
283
298
  end
284
299
 
285
300
  #
286
301
  # Class method to take a 'FixNum' type and return an IP::Address
287
302
  # object.
288
303
  #
304
+ # This method is deprecated. Please use IP::Address::Util#unpack instead.
305
+ #
289
306
  def Address.unpack(ip)
290
- ret = []
291
- 4.times { |x| ret.push((ip >> 8*x) & 0xFF) }
292
- return IP::Address.new(ret.reverse.join("."))
307
+ warn "IP::Address#unpack is deprecated. Please use IP::Address::Util#unpack instead."
308
+ return IP::Address::Util.unpack(ip)
293
309
  end
294
310
 
295
311
  end
296
312
 
297
313
  end
314
+
315
+ module IP::Address::Util
316
+ #
317
+ # Pack an IP::Address object into a long integer
318
+ # used for calculation. Returns a 'FixNum' type.
319
+ #
320
+ def pack(ip)
321
+ ret = 0
322
+ myip = ip.octets.reverse
323
+ 4.times { |x| ret = ret | (myip[x] & 0xFF) << 8*x }
324
+ return ret
325
+ end
326
+
327
+ module_function :pack
328
+
329
+ #
330
+ # Take a 'FixNum' type and return an IP::Address object.
331
+ #
332
+ def unpack(ip)
333
+ ret = []
334
+ 4.times { |x| ret.push((ip >> 8*x) & 0xFF) }
335
+ return IP::Address.new(ret.reverse.join("."))
336
+ end
337
+
338
+ module_function :unpack
339
+
340
+ #
341
+ # Given an IP::Address object suitable for a netmask, returns the CIDR-notation
342
+ # "short" netmask.
343
+ #
344
+ # ex:
345
+ # short_netmask(IP::Address.new("255.255.255.255")) => 32
346
+ # short_netmask(IP::Address.new("255.255.255.240")) => 28
347
+ #
348
+
349
+ def short_netmask(ip)
350
+ a = []
351
+ (0..3).each do |x|
352
+ if x < 3 && ip[x] < 255 && ip[x+1] > 0
353
+ raise IP::BoundaryException.new("Invalid Netmask")
354
+ end
355
+ a += binary_vector(ip[x])
356
+ end
357
+ retval = 0
358
+ a.each { |x| retval += x }
359
+ return retval
360
+ end
361
+
362
+ module_function :short_netmask
363
+
364
+ #
365
+ # Given a CIDR-notation "short" netmask, returns a IP::Address object containing
366
+ # the equivalent "long" netmask.
367
+ #
368
+ # ex:
369
+ # long_netmask(32) => IP::Address object of "255.255.255.255"
370
+ # long_netmask(28) => IP::Address object of "255.255.255.240"
371
+ #
372
+
373
+ def long_netmask(short)
374
+ raw = 0xFFFFFFFF << (32 - short)
375
+ return IP::Address::Util.unpack(raw)
376
+ end
377
+
378
+ module_function :long_netmask
379
+
380
+ #
381
+ # Given a number (presumably an octet), will produce a binary vector indicating the
382
+ # on/off positions as 1 or 0.
383
+ #
384
+ # ex:
385
+ # binary_vector(255) => [1, 1, 1, 1, 1, 1, 1, 1]
386
+ # binary_vector(240) => [1, 1, 1, 1, 0, 0, 0, 0]
387
+ #
388
+
389
+ def binary_vector(octet)
390
+ return octet.to_i.to_s(2).split(//).collect { |x| x.to_i }
391
+ end
392
+
393
+ module_function :binary_vector
394
+
395
+ end
@@ -0,0 +1,274 @@
1
+ require 'test/unit'
2
+ load 'lib/ip.rb'
3
+
4
+ class CIDRTest < Test::Unit::TestCase
5
+
6
+ def name
7
+ return "IP::CIDR tests"
8
+ end
9
+
10
+ def test_init
11
+ a = nil
12
+ begin
13
+ IP::CIDR.new(Hash.new)
14
+ a = false
15
+ rescue IP::AddressException => e
16
+ a = true
17
+ end
18
+
19
+ assert(a, "data types test #1")
20
+
21
+ begin
22
+ IP::CIDR.new("10.0.0.1")
23
+ a = false
24
+ rescue IP::AddressException => e
25
+ a = true
26
+ end
27
+
28
+ assert(a, "data types test #2")
29
+
30
+ begin
31
+ IP::CIDR.new("10.0.0.1/")
32
+ a = false
33
+ rescue IP::AddressException => e
34
+ a = true
35
+ end
36
+
37
+ assert(a, "data types test #3")
38
+
39
+ begin
40
+ IP::CIDR.new("10.0.0.1/asdf/32")
41
+ a = false
42
+ rescue IP::AddressException => e
43
+ a = true
44
+ end
45
+
46
+ assert(a, "data type test #4")
47
+
48
+ begin
49
+ IP::CIDR.new("10.0.0.1/foomatic_wootmaster")
50
+ a = false
51
+ rescue IP::AddressException => e
52
+ a = true
53
+ end
54
+
55
+ assert(a, "data types test #5")
56
+
57
+ begin
58
+ IP::CIDR.new("foomatic_wootmaster/32")
59
+ a = false
60
+ rescue IP::AddressException => e
61
+ a = true
62
+ end
63
+
64
+ assert(a, "data types test #6")
65
+
66
+ begin
67
+ IP::CIDR.new("10.0.0.1/255")
68
+ a = false
69
+ rescue IP::AddressException => e
70
+ a = true
71
+ end
72
+
73
+ assert(a, "data types test #7")
74
+
75
+ cidr = IP::CIDR.new("10.0.0.1/32")
76
+
77
+ assert(cidr.ip.ip_address == "10.0.0.1", "data integrity test #1")
78
+ assert(cidr.mask == 32, "data integrity test #2")
79
+ assert(cidr.cidr == "10.0.0.1/32", "data integrity test #3")
80
+
81
+ cidr = IP::CIDR.new("10.0.0.1/255.255.255.255")
82
+
83
+ assert(cidr.ip.ip_address == "10.0.0.1", "data integrity test #4")
84
+ assert(cidr.mask == 32, "data integrity test #5")
85
+ assert(cidr.cidr == "10.0.0.1/255.255.255.255", "data integrity test #6")
86
+ end
87
+
88
+ def test_netmasks
89
+ cidr = IP::CIDR.new("10.0.0.1/32")
90
+ assert(cidr.short_netmask == 32, "netmask test #1")
91
+ assert(cidr.long_netmask.ip_address == "255.255.255.255", "netmask test #2")
92
+
93
+ cidr = IP::CIDR.new("10.0.0.1/255.255.255.241")
94
+ assert(cidr.short_netmask == 29, "netmask test #3")
95
+ assert(cidr.long_netmask.ip_address == "255.255.255.248", "netmask test #4")
96
+ end
97
+
98
+ def test_first_last
99
+ cidr = IP::CIDR.new("10.0.0.2/24")
100
+ assert(cidr.first_ip.ip_address == "10.0.0.0", "first/last test #1")
101
+ assert(cidr.last_ip.ip_address == "10.0.0.255", "first/last test #2")
102
+ end
103
+
104
+ def test_range
105
+ cidr = IP::CIDR.new("10.0.0.2/24")
106
+ assert(cidr.range.find_all { |x| x.ip_address == "10.0.0.1" }.length == 1, "range test #1")
107
+ assert(cidr.range.find_all { |x| x.ip_address == "10.0.1.0" }.length == 0, "range test #2")
108
+ end
109
+
110
+ def test_overlaps
111
+ cidr = IP::CIDR.new("10.0.0.2/24")
112
+ cidr2 = IP::CIDR.new("10.0.0.1/29")
113
+
114
+ assert(cidr.overlaps?(cidr2), "overlaps test #1")
115
+
116
+ cidr2 = IP::CIDR.new("10.0.0.1/16")
117
+
118
+ assert(cidr2.overlaps?(cidr), "overlaps test #2")
119
+ assert(cidr.overlaps?(cidr2), "overlaps test #3")
120
+ end
121
+ end
122
+
123
+ class RangeTest < Test::Unit::TestCase
124
+ def name
125
+ return "IP::Range tests"
126
+ end
127
+
128
+ def test_range
129
+ a = nil
130
+ begin
131
+ IP::Range["10.0.0.1", "10.0.0.2"]
132
+ a = true
133
+ rescue Exception => e
134
+ a = false
135
+ end
136
+
137
+ assert(a, "data types test #1")
138
+
139
+ begin
140
+ IP::Range[IP::Address.new("10.0.0.1"), IP::Address.new("10.0.0.2")]
141
+ a = true
142
+ rescue Exception => e
143
+ a = false
144
+ end
145
+
146
+ assert(a, "data types test #2")
147
+
148
+ range = IP::Range["10.0.0.1", "10.0.0.10"]
149
+
150
+ assert(range.find_all { |x| x.ip_address == "10.0.0.1" }.length == 1, "range check #1")
151
+ assert(range.find_all { |x| x.ip_address == "10.0.0.10" }.length == 1, "range check #2")
152
+ assert(range.find_all { |x| x.ip_address == "10.0.0.7" }.length == 1, "range check #3")
153
+ assert(range.find_all { |x| x.ip_address == "10.0.0.11" }.length == 0, "range check #4")
154
+
155
+ end
156
+
157
+ end
158
+
159
+ class AddressTest < Test::Unit::TestCase
160
+ def name
161
+ return "IP::Address tests"
162
+ end
163
+
164
+ def test_init
165
+ ip = nil
166
+ begin
167
+ ip = IP::Address.new(Hash.new)
168
+ rescue IP::AddressException => e
169
+ assert(true, "init test #1")
170
+ end
171
+
172
+ assert(false, "init test #2") if ip
173
+
174
+ ip = nil
175
+
176
+ begin
177
+ ip = IP::Address.new("asdf")
178
+ rescue IP::AddressException => e
179
+ assert(true, "init test #2")
180
+ end
181
+
182
+ assert(false, "init test #2") if ip
183
+ ip = nil
184
+
185
+ begin
186
+ ip = IP::Address.new("0.0.0")
187
+ rescue IP::AddressException => e
188
+ assert(true, "init test #3")
189
+ end
190
+
191
+ assert(false, "init test #3") if ip
192
+ ip = nil
193
+
194
+ begin
195
+ ip = IP::Address.new("256.255.255.255")
196
+ rescue IP::AddressException => e
197
+ assert(true, "init test #4")
198
+ end
199
+
200
+ assert(false, "init test #4") if ip
201
+ end
202
+
203
+ def test_accessor
204
+ ip = IP::Address.new("10.1.2.3")
205
+ assert(ip.ip_address == "10.1.2.3", "accessor test #1")
206
+ assert(ip.octets[0] == 10, "accessor test #2")
207
+ assert(ip.octets[3] == 3, "accessor test #3")
208
+ assert(ip.octets[4] == nil, "accessor test #4")
209
+
210
+ assert(ip.octet(1) == ip[1] && ip[1] == 1, "accessor test #5")
211
+
212
+ oct = nil
213
+
214
+ begin
215
+ oct = ip[4]
216
+ rescue IP::BoundaryException => e
217
+ assert(true, "accessor test #6")
218
+ end
219
+
220
+ assert(false, "accessor test #6") if oct
221
+ end
222
+
223
+ end
224
+
225
+ class UtilTest < Test::Unit::TestCase
226
+ def name
227
+ return "IP::Address::Util tests"
228
+ end
229
+
230
+ def test_pack_unpack
231
+ address = "10.0.0.1"
232
+ assert(IP::Address::Util.unpack(IP::Address::Util.pack(IP::Address.new(address))).ip_address == address, "pack/unpack test")
233
+ end
234
+
235
+ def test_short_netmask
236
+ ip = IP::Address.new("255.255.255.255")
237
+ assert(IP::Address::Util.short_netmask(ip) == 32, "Short Netmask Test #1")
238
+ ip = IP::Address.new("255.255.255.241")
239
+ assert(IP::Address::Util.short_netmask(ip) == 29, "Short Netmask Test #2")
240
+
241
+ nm = nil
242
+
243
+ begin
244
+ nm = IP::Address::Util.short_netmask(IP::Address.new("255.255.0.255"))
245
+ rescue IP::BoundaryException => e
246
+ assert(true, "Short Netmask BoundaryException Check #1")
247
+ end
248
+
249
+ assert(false, "Short Netmask BoundaryException Check #1") if nm
250
+
251
+ nm = nil
252
+
253
+ begin
254
+ nm = IP::Address::Util.short_netmask(IP::Address.new("255.255.240.255"))
255
+ rescue IP::BoundaryException => e
256
+ assert(true, "Short Netmask BoundaryException check #2")
257
+ end
258
+
259
+ assert(false, "Short Netmask BoundaryException check #2") if nm
260
+ end
261
+
262
+ def test_long_netmask
263
+ assert(IP::Address::Util.long_netmask(32).ip_address == "255.255.255.255", "Long Netmask Test #1")
264
+ assert(IP::Address::Util.long_netmask(29).ip_address == "255.255.255.248", "Long Netmask Test #2")
265
+ end
266
+
267
+ def test_binary_vector
268
+ assert(IP::Address::Util.binary_vector(255).length == 8, "Binary Vector Test #1")
269
+ assert(IP::Address::Util.binary_vector(240).find_all { |x| x == 1 }.length == 4, "Binary Vector Test #2")
270
+ assert(IP::Address::Util.binary_vector(241).find_all { |x| x == 1 }.length == 5, "Binary Vector Test #3")
271
+ assert(IP::Address::Util.binary_vector(240)[0] == 1, "Binary Vector Test #4")
272
+ end
273
+
274
+ end
metadata CHANGED
@@ -3,14 +3,14 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: ip
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.3
7
- date: 2006-01-03 00:00:00 -08:00
6
+ version: 0.1.0
7
+ date: 2006-01-20 00:00:00 -08:00
8
8
  summary: "Ruby classes to work with IP address, ranges, and netmasks"
9
9
  require_paths:
10
10
  - lib
11
11
  email: erik@hollensbe.org
12
12
  homepage:
13
- rubyforge_project:
13
+ rubyforge_project: ip-address
14
14
  description:
15
15
  autorequire: ip
16
16
  default_executable:
@@ -30,7 +30,8 @@ authors:
30
30
  - Erik Hollensbe
31
31
  files:
32
32
  - lib/ip.rb
33
- test_files: []
33
+ test_files:
34
+ - test/ip.rb
34
35
  rdoc_options: []
35
36
  extra_rdoc_files: []
36
37
  executables: []