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
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
|