ip 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/ip/address.rb
ADDED
@@ -0,0 +1,393 @@
|
|
1
|
+
#
|
2
|
+
# IP::Address - base class for IP::Address::IPv4 and IP::Address::IPv6
|
3
|
+
#
|
4
|
+
|
5
|
+
class IP::Address
|
6
|
+
|
7
|
+
#
|
8
|
+
# This original IP Address you passed it, returned as a string.
|
9
|
+
#
|
10
|
+
attr_reader :ip_address
|
11
|
+
#
|
12
|
+
# This returns an Array of Integer which contains the octets of
|
13
|
+
# the IP, in descending order.
|
14
|
+
#
|
15
|
+
attr_reader :octets
|
16
|
+
|
17
|
+
#
|
18
|
+
# Returns an octet given the proper index. The octets returned are
|
19
|
+
# Integer types.
|
20
|
+
#
|
21
|
+
def [](num)
|
22
|
+
if @octets[num].nil?
|
23
|
+
raise IP::BoundaryException.new("Invalid octet")
|
24
|
+
end
|
25
|
+
return @octets[num]
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# See [].
|
30
|
+
#
|
31
|
+
alias_method :octet, :[]
|
32
|
+
|
33
|
+
#
|
34
|
+
# Returns a 128-bit integer representing the address.
|
35
|
+
#
|
36
|
+
def pack
|
37
|
+
fail "This method is abstract."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Support for IPv4
|
43
|
+
#
|
44
|
+
|
45
|
+
class IP::Address::IPv4 < IP::Address
|
46
|
+
#
|
47
|
+
# Constructs an IP::Address::IPv4 object.
|
48
|
+
#
|
49
|
+
# This can take two types of input. Either a string that contains
|
50
|
+
# a dotted-quad formatted address, or an integer that contains the
|
51
|
+
# data. This integer is expected to be constructed in the way that
|
52
|
+
# IP::Address::Util.pack_ipv4 would generate such an integer.
|
53
|
+
#
|
54
|
+
# This constructor will throw IP::AddressException on any parse
|
55
|
+
# errors.
|
56
|
+
#
|
57
|
+
def initialize(ip_address)
|
58
|
+
if ip_address.kind_of? Integer
|
59
|
+
# unpack to generate a string, and parse that.
|
60
|
+
# overwrites 'ip_address'
|
61
|
+
# horribly inefficient, but general.
|
62
|
+
|
63
|
+
raw = IP::Address::Util.raw_unpack(ip_address)[0..1]
|
64
|
+
octets = []
|
65
|
+
|
66
|
+
2.times do |x|
|
67
|
+
octets.push(raw[x] & 0x00FF)
|
68
|
+
octets.push((raw[x] & 0xFF00) >> 8)
|
69
|
+
end
|
70
|
+
|
71
|
+
ip_address = octets.reverse.join(".")
|
72
|
+
end
|
73
|
+
|
74
|
+
if ! ip_address.kind_of? String
|
75
|
+
raise IP::AddressException.new("Fed IP address '#{ip_address}' is not String or Fixnum")
|
76
|
+
end
|
77
|
+
|
78
|
+
@ip_address = ip_address
|
79
|
+
|
80
|
+
#
|
81
|
+
# Unbeknowest by me, to_i will not throw an exception if the string
|
82
|
+
# can't be converted cleanly - it just truncates, similar to atoi() and perl's int().
|
83
|
+
#
|
84
|
+
# Code below does a final sanity check.
|
85
|
+
#
|
86
|
+
|
87
|
+
octets = ip_address.split(/\./)
|
88
|
+
octets_i = octets.collect { |x| x.to_i }
|
89
|
+
|
90
|
+
0.upto(octets.length - 1) do |octet|
|
91
|
+
if octets[octet] != octets_i[octet].to_s
|
92
|
+
raise IP::AddressException.new("Integer conversion failed")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
@octets = octets_i
|
97
|
+
|
98
|
+
# I made a design decision to allow 0.0.0.0 here.
|
99
|
+
if @octets.length != 4 or @octets.find_all { |x| x > 255 }.length > 0
|
100
|
+
raise IP::AddressException.new("IP address is improperly formed")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Returns a 128-bit integer representing the address.
|
106
|
+
#
|
107
|
+
def pack
|
108
|
+
# this routine does relatively little. all it does is ensure
|
109
|
+
# that the IP address is of a certain size and has certain numeric limits.
|
110
|
+
myip = self.octets
|
111
|
+
packval = [0] * 6
|
112
|
+
|
113
|
+
#
|
114
|
+
# this ensures that the octets are 8 bit, and combines the octets in order to
|
115
|
+
# form two 16-bit integers suitable for pushing into the last places in 'packval'
|
116
|
+
#
|
117
|
+
|
118
|
+
(0..3).step(2) { |x| packval.push(((myip[x] & 0xFF) << 8) | (myip[x+1] & 0xFF)) }
|
119
|
+
|
120
|
+
return IP::Address::Util.raw_pack(packval)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class IP::Address::IPv6 < IP::Address
|
125
|
+
|
126
|
+
#
|
127
|
+
# Construct a new IP::Address::IPv6 object.
|
128
|
+
#
|
129
|
+
# It can be passed two different types for construction:
|
130
|
+
#
|
131
|
+
# * A string which contains a valid, RFC4291-compliant IPv6 address
|
132
|
+
# (all forms are supported, including the
|
133
|
+
# backwards-compatibility IPv4 methods)
|
134
|
+
#
|
135
|
+
# * A 128-bit integer which is a sum of all the octets, left-most
|
136
|
+
# octet being the highest 32-bit portion (see IP::Address::Util
|
137
|
+
# for help generating this value)
|
138
|
+
#
|
139
|
+
|
140
|
+
def initialize(ip_address)
|
141
|
+
if ip_address.kind_of? Integer
|
142
|
+
# unpack to generate a string, and parse that.
|
143
|
+
# overwrites 'ip_address'
|
144
|
+
# horribly inefficient, but general.
|
145
|
+
|
146
|
+
raw = IP::Address::Util.raw_unpack(ip_address)
|
147
|
+
|
148
|
+
ip_address = format_address(raw.reverse)
|
149
|
+
end
|
150
|
+
|
151
|
+
if ! ip_address.kind_of? String
|
152
|
+
raise IP::AddressException.new("Fed IP address '#{ip_address}' is not String or Fixnum")
|
153
|
+
end
|
154
|
+
|
155
|
+
@ip_address = ip_address
|
156
|
+
|
157
|
+
octets = parse_address(ip_address)
|
158
|
+
|
159
|
+
if octets.length != 8
|
160
|
+
puts octets
|
161
|
+
raise IP::AddressException.new("IPv6 address '#{ip_address}' does not have 8 octets or a floating range specifier")
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# Now we check the contents of the address, to be sure we have
|
166
|
+
# proper hexidecimal values
|
167
|
+
#
|
168
|
+
|
169
|
+
@octets = octets_atoi(octets)
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# returns an octet in its hexidecimal representation.
|
174
|
+
#
|
175
|
+
|
176
|
+
def octet_as_hex(index)
|
177
|
+
return format_octet(self[index])
|
178
|
+
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# Returns an address with no floating range specifier.
|
182
|
+
#
|
183
|
+
# Ex:
|
184
|
+
#
|
185
|
+
# IP::Address::IPv6.new("DEAD::BEEF").long_address => "DEAD:0:0:0:0:0:0:BEEF"
|
186
|
+
#
|
187
|
+
|
188
|
+
def long_address
|
189
|
+
return format_address
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# Returns a shortened address using the :: range specifier.
|
194
|
+
#
|
195
|
+
# This will replace any sequential octets that are equal to '0' with '::'.
|
196
|
+
# It does this searching from right to left, looking for a sequence
|
197
|
+
# of them. Per specification, only one sequence can be replaced in
|
198
|
+
# this fashion. It will return a long address if it can't find
|
199
|
+
# something suitable.
|
200
|
+
#
|
201
|
+
# Ex:
|
202
|
+
#
|
203
|
+
# "DEAD:0:0:0:BEEF:0:0:0" => "DEAD:0:0:0:BEEF::"
|
204
|
+
|
205
|
+
def short_address
|
206
|
+
octets = @octets.dup
|
207
|
+
|
208
|
+
# short circuit: if less than 2 octets are equal to 0, don't
|
209
|
+
# bother - return a long address.
|
210
|
+
|
211
|
+
if octets.find_all { |x| x == 0 }.length < 2
|
212
|
+
return format_address(octets)
|
213
|
+
end
|
214
|
+
|
215
|
+
filling = false
|
216
|
+
|
217
|
+
left = []
|
218
|
+
right = []
|
219
|
+
|
220
|
+
7.downto(0) do |x|
|
221
|
+
if !filling and left.length == 0 and octets[x] == 0
|
222
|
+
filling = true
|
223
|
+
elsif filling
|
224
|
+
if octets[x] != 0
|
225
|
+
left.push(octets[x])
|
226
|
+
filling = false
|
227
|
+
end
|
228
|
+
elsif left.length > 0
|
229
|
+
left.push(octets[x])
|
230
|
+
else
|
231
|
+
right.push(octets[x])
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
return format_address(left.reverse) + "::" + format_address(right.reverse)
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
#
|
240
|
+
# Returns a 128-bit integer representing the address.
|
241
|
+
#
|
242
|
+
def pack
|
243
|
+
return IP::Address::Util.raw_pack(self.octets.dup)
|
244
|
+
end
|
245
|
+
|
246
|
+
protected
|
247
|
+
|
248
|
+
#
|
249
|
+
# will parse a string address and return an 8 index array containing
|
250
|
+
# all the octets. Should handle IPv4 to IPv6 and wildcards properly.
|
251
|
+
#
|
252
|
+
|
253
|
+
def parse_address(address)
|
254
|
+
|
255
|
+
#
|
256
|
+
# since IPv6 can have missing parts, we have to scan sequentially.
|
257
|
+
# fill the LHS until we encounter '::', at which point we fill the
|
258
|
+
# RHS.
|
259
|
+
#
|
260
|
+
# If the LHS is 8 octets, we're done. Otherwise, we take a
|
261
|
+
# difference of 8 and the sum of the number of octets from the LHS
|
262
|
+
# from the number of octets from the RHS. We then fill abs(num) at
|
263
|
+
# the end of the LHS array with 0, and combine them to form the
|
264
|
+
# address.
|
265
|
+
#
|
266
|
+
# There can only be one '::' in an address, so if we are filling
|
267
|
+
# the RHS and encounter another '::', we throw AddressException.
|
268
|
+
#
|
269
|
+
|
270
|
+
# catches ::XXXX::
|
271
|
+
if address.scan(/::/).length > 1
|
272
|
+
raise IP::AddressException.new("IPv6 address '#{ip_address}' has more than one floating range ('::') specifier")
|
273
|
+
end
|
274
|
+
|
275
|
+
octets = address.split(":")
|
276
|
+
|
277
|
+
if octets.length < 8
|
278
|
+
|
279
|
+
if octets[-1].index(".").nil? and address.match(/::/)
|
280
|
+
octets = handle_wildcard_in_address(octets)
|
281
|
+
elsif octets[-1].index(".")
|
282
|
+
# we have a dotted quad IPv4 compatibility address.
|
283
|
+
# create an IPv4 object, get the raw value and stuff it into
|
284
|
+
# the lower two octets.
|
285
|
+
|
286
|
+
raw = IP::Address::IPv4.new(octets.pop).pack
|
287
|
+
raw = raw & 0xFFFFFFFF
|
288
|
+
low = raw & 0xFFFF
|
289
|
+
high = (raw >> 16) & 0xFFFF
|
290
|
+
octets = handle_wildcard_in_address(octets)[0..5] + ([high, low].collect { |x| format_octet(x) })
|
291
|
+
else
|
292
|
+
raise IP::AddressException.new("IPv6 address '#{address}' has less than 8 octets")
|
293
|
+
end
|
294
|
+
|
295
|
+
elsif octets.length > 8
|
296
|
+
raise IP::AddressException.new("IPv6 address '#{address}' has more than 8 octets")
|
297
|
+
end
|
298
|
+
|
299
|
+
return octets
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
#
|
304
|
+
# This handles :: addressing in IPv6 and generates a full set of
|
305
|
+
# octets in response.
|
306
|
+
#
|
307
|
+
# The series of octets handed to this routine are expected to be the
|
308
|
+
# result of splitting the address by ':'.
|
309
|
+
#
|
310
|
+
|
311
|
+
def handle_wildcard_in_address(octets)
|
312
|
+
lhs = []
|
313
|
+
rhs = []
|
314
|
+
|
315
|
+
i = octets.index("") # find ::
|
316
|
+
|
317
|
+
# easy out for xxxx:xxxx:: and so on
|
318
|
+
if i.nil?
|
319
|
+
lhs = octets.dup
|
320
|
+
elsif i == 0
|
321
|
+
# for some reason "::123:123".split(":") returns two empty
|
322
|
+
# strings in the array, yet a trailing "::" doesn't.
|
323
|
+
rhs = octets[2..-1]
|
324
|
+
else
|
325
|
+
lhs = octets[0..(i-1)]
|
326
|
+
rhs = octets[(i+1)..-1]
|
327
|
+
end
|
328
|
+
|
329
|
+
unless rhs.index("").nil?
|
330
|
+
raise IP::AddressException.new("IPv6 address '#{ip_address}' has more than one floating range ('::') specifier")
|
331
|
+
end
|
332
|
+
|
333
|
+
missing = (8 - (lhs.length + rhs.length))
|
334
|
+
missing.times { lhs.push("0") }
|
335
|
+
|
336
|
+
octets = lhs + rhs
|
337
|
+
|
338
|
+
return octets
|
339
|
+
end
|
340
|
+
|
341
|
+
#
|
342
|
+
# Converts (and checks) a series of octets from ascii to integer.
|
343
|
+
#
|
344
|
+
|
345
|
+
def octets_atoi(octets)
|
346
|
+
new_octets = []
|
347
|
+
|
348
|
+
octets.each do |x|
|
349
|
+
if x.length > 4
|
350
|
+
raise IP::AddressException.new("IPv6 address '#{ip_address}' has an octet that is larger than 32 bits")
|
351
|
+
end
|
352
|
+
|
353
|
+
octet = x.hex
|
354
|
+
|
355
|
+
# normalize the octet to 4 places with leading zeroes, uppercase.
|
356
|
+
x = ("0" * (4 - x.length)) + x.upcase
|
357
|
+
|
358
|
+
unless ("%0.4X" % octet) == x
|
359
|
+
raise IP::AddressException.new("IPv6 address '#{ip_address}' has octets that contain non-hexidecimal data")
|
360
|
+
end
|
361
|
+
|
362
|
+
new_octets.push(octet)
|
363
|
+
end
|
364
|
+
|
365
|
+
return new_octets
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
#
|
371
|
+
# Formats the integer octets in ruby into their respective hex values.
|
372
|
+
#
|
373
|
+
|
374
|
+
def format_octet(octet)
|
375
|
+
# this isn't required by the spec, but makes the output quite a bit
|
376
|
+
# prettier.
|
377
|
+
if octet < 10
|
378
|
+
return octet.to_s
|
379
|
+
end
|
380
|
+
|
381
|
+
return "%0.4X" % octet
|
382
|
+
end
|
383
|
+
|
384
|
+
#
|
385
|
+
# The same as +format_octet+, but processes an array (intended to be
|
386
|
+
# a whole address).
|
387
|
+
#
|
388
|
+
|
389
|
+
def format_address(octets=@octets)
|
390
|
+
return octets.collect { |x| format_octet(x) }.join(":")
|
391
|
+
end
|
392
|
+
|
393
|
+
end
|
data/lib/ip/cidr.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
#
|
2
|
+
# IP::CIDR - Works with Classless Inter-Domain Routing formats, such
|
3
|
+
# as 10.0.0.1/32 or 10.0.0.1/255.255.255.255
|
4
|
+
#
|
5
|
+
|
6
|
+
class IP::CIDR
|
7
|
+
#
|
8
|
+
# Contains the original CIDR you fed it, returned as a string.
|
9
|
+
#
|
10
|
+
attr_reader :cidr
|
11
|
+
#
|
12
|
+
# Contains the IP address (LHS) only. Returned as an IP::Address
|
13
|
+
# object.
|
14
|
+
#
|
15
|
+
attr_reader :ip
|
16
|
+
#
|
17
|
+
# Contains the integer-based (short) netmask (RHS) only. Returned as an
|
18
|
+
# integer.
|
19
|
+
#
|
20
|
+
attr_reader :mask
|
21
|
+
|
22
|
+
#
|
23
|
+
# Given a string of format X.X.X.X/X, in standard CIDR notation,
|
24
|
+
# this will construct a IP::CIDR object.
|
25
|
+
#
|
26
|
+
def initialize(cidr)
|
27
|
+
if !cidr.kind_of? String
|
28
|
+
raise IP::AddressException.new("CIDR value is not of type String")
|
29
|
+
end
|
30
|
+
|
31
|
+
@cidr = cidr
|
32
|
+
@ip, @mask = cidr.split(/\//, 2)
|
33
|
+
|
34
|
+
@ip = IP::Address::Util.string_to_ip(@ip)
|
35
|
+
|
36
|
+
if @ip.nil? or @mask.nil?
|
37
|
+
raise IP::AddressException.new("CIDR is not valid - invalid format")
|
38
|
+
end
|
39
|
+
|
40
|
+
if @mask.length == 0 or /[^0-9.]/.match @mask or
|
41
|
+
(@ip.kind_of? IP::Address::IPv6 and @mask.to_i.to_s != @mask)
|
42
|
+
raise IP::AddressException.new("CIDR RHS is not valid - #{@mask}")
|
43
|
+
end
|
44
|
+
|
45
|
+
if @ip.kind_of? IP::Address::IPv4 and @mask.length > 2
|
46
|
+
# get the short netmask for IPv4 - this will throw an exception if the netmask is malformed.
|
47
|
+
@mask = IP::Address::Util.short_netmask(IP::Address::IPv4.new(@mask))
|
48
|
+
end
|
49
|
+
|
50
|
+
@mask = @mask.to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def netmask
|
54
|
+
warn "IP::CIDR#netmask is deprecated. Please use IP::CIDR#long_netmask instead."
|
55
|
+
return self.long_netmask
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# This produces the long netmask (eg. 255.255.255.255) of the CIDR in an
|
60
|
+
# IP::Address object.
|
61
|
+
#
|
62
|
+
# This will throw an exception for IPv6 addresses.
|
63
|
+
#
|
64
|
+
def long_netmask
|
65
|
+
if @ip.kind_of? IP::Address::IPv6
|
66
|
+
raise IP::AddressException.new("IPv6 does not support a long netmask.")
|
67
|
+
end
|
68
|
+
|
69
|
+
return IP::Address::Util.long_netmask_ipv4(@mask)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# This produces the short netmask (eg. 32) of the CIDR in an IP::Address
|
74
|
+
# object.
|
75
|
+
#
|
76
|
+
|
77
|
+
def short_netmask
|
78
|
+
return @mask
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# This produces a range ala IP::Range, but only for the subnet
|
83
|
+
# defined by the CIDR object.
|
84
|
+
#
|
85
|
+
def range
|
86
|
+
return IP::Range[self.first_ip, self.last_ip]
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# This returns the first ip address of the cidr as an IP::Address object.
|
91
|
+
#
|
92
|
+
def first_ip
|
93
|
+
rawip = @ip.pack
|
94
|
+
|
95
|
+
#
|
96
|
+
# since our actual mask calculation is done with the full 128 bits,
|
97
|
+
# we have to shift calculations that we want in IPv4 to the left to
|
98
|
+
# get proper return values.
|
99
|
+
#
|
100
|
+
|
101
|
+
if @ip.kind_of? IP::Address::IPv4
|
102
|
+
rawip = rawip << 96
|
103
|
+
end
|
104
|
+
|
105
|
+
rawnm = (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) << (128 - @mask)
|
106
|
+
lower = rawip & rawnm
|
107
|
+
|
108
|
+
if @ip.kind_of? IP::Address::IPv4
|
109
|
+
lower = lower & (0xFFFFFFFF000000000000000000000000)
|
110
|
+
lower = lower >> 96
|
111
|
+
end
|
112
|
+
|
113
|
+
case @ip.class.object_id
|
114
|
+
when IP::Address::IPv4.object_id
|
115
|
+
return IP::Address::IPv4.new(lower)
|
116
|
+
when IP::Address::IPv6.object_id
|
117
|
+
return IP::Address::IPv6.new(lower)
|
118
|
+
else
|
119
|
+
raise IP::AddressException.new("Cannot determine type of IP address")
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# This returns the last ip address of the cidr as an IP::Address object.
|
126
|
+
#
|
127
|
+
def last_ip
|
128
|
+
rawip = @ip.pack
|
129
|
+
|
130
|
+
# see #first_ip for the reason that we shift this way for IPv4.
|
131
|
+
if @ip.kind_of? IP::Address::IPv4
|
132
|
+
rawip = rawip << 96
|
133
|
+
end
|
134
|
+
|
135
|
+
rawnm = (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) << (128 - @mask)
|
136
|
+
upper = rawip | ~rawnm
|
137
|
+
|
138
|
+
if @ip.kind_of? IP::Address::IPv4
|
139
|
+
upper = upper & (0xFFFFFFFF000000000000000000000000)
|
140
|
+
upper = upper >> 96
|
141
|
+
end
|
142
|
+
|
143
|
+
case @ip.class.object_id
|
144
|
+
when IP::Address::IPv4.object_id
|
145
|
+
return IP::Address::IPv4.new(upper)
|
146
|
+
when IP::Address::IPv6.object_id
|
147
|
+
return IP::Address::IPv6.new(upper)
|
148
|
+
else
|
149
|
+
raise IP::AddressException.new("Cannot determine type of IP address")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# This will take another IP::CIDR object as an argument and check to see
|
155
|
+
# if it overlaps with this cidr object. Returns true/false on overlap.
|
156
|
+
#
|
157
|
+
# This also throws a TypeError if passed invalid data.
|
158
|
+
#
|
159
|
+
def overlaps?(other_cidr)
|
160
|
+
raise TypeError.new("Expected object of type IP::CIDR") unless(other_cidr.kind_of?(IP::CIDR))
|
161
|
+
|
162
|
+
myfirst = self.first_ip.pack
|
163
|
+
mylast = self.last_ip.pack
|
164
|
+
|
165
|
+
otherfirst = other_cidr.first_ip.pack
|
166
|
+
otherlast = other_cidr.last_ip.pack
|
167
|
+
|
168
|
+
return ((myfirst >= otherfirst && myfirst <= otherlast) ||
|
169
|
+
(mylast <= otherlast && mylast >= otherfirst) ||
|
170
|
+
(otherfirst >= myfirst && otherfirst <= mylast)) ? true : false;
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# Given an IP::Address object, will determine if it is included in
|
175
|
+
# the current subnet. This does not generate a range and is
|
176
|
+
# comparatively much faster.
|
177
|
+
#
|
178
|
+
# This will *not* generate an exception when IPv4 objects are
|
179
|
+
# compared to IPv6, and vice-versa. This is intentional.
|
180
|
+
#
|
181
|
+
|
182
|
+
def includes?(address)
|
183
|
+
raise TypeError.new("Expected type of IP::Address or derived") unless (address.kind_of? IP::Address)
|
184
|
+
|
185
|
+
raw = address.pack
|
186
|
+
first = first_ip.pack
|
187
|
+
last = last_ip.pack
|
188
|
+
|
189
|
+
return (raw >= first) && (raw <= last)
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|