ipaccess 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: Copyright (c) 2009 Paweł Wilk
5
+ # License:: This program is licensed under the terms of {GNU Lesser General Public License}[link:docs/LGPL-LICENSE.html] or {Ruby License}[link:docs/COPYING.html].
6
+ #
7
+ # This file contains IPAccessDenied class
8
+ # used to report access denials by
9
+ # IPAccess objects.
10
+ #
11
+ #--
12
+ #
13
+ # Copyright (C) 2009 by Paweł Wilk. All Rights Reserved.
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU Lesser General Public License
17
+ # as published by the Free Software Foundation; either version 3 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #++
23
+
24
+
25
+ # This class handles IP access denied exceptions.
26
+
27
+ class IPAccessDenied < Errno::EACCES
28
+
29
+ # Creates new object. First argument should be
30
+ # a NetAddr::CIDR object containing address
31
+ # of denied connection. Second argument should
32
+ # be a CIDR rule that matched. Last argument
33
+ # should be an IPAccess object.
34
+
35
+ def initialize(addr, rule=nil, access_list=nil)
36
+ @peer_ip = addr
37
+ @rule = rule
38
+ @access_list = access_list
39
+ end
40
+
41
+ # Returns string representation of access list name rule.
42
+
43
+ def list_desc
44
+ if (@access_list.is_a?(IPAccess) && !@access_list.name.to_s.empty?)
45
+ "#{@access_list.name} "
46
+ elsif @access_list.is_a?(String)
47
+ "#{@access_list} "
48
+ else
49
+ ""
50
+ end
51
+ end
52
+
53
+ # Returns string representation of rule.
54
+
55
+ def rule_desc
56
+ if @rule.is_a?(NetAddr::CIDR)
57
+ if @rule.version == 6
58
+ rule = @rule.to_s(:Short => true)
59
+ rule = ":#{rule}" if rule =~ /^\//
60
+ else
61
+ rule = @rule.to_s
62
+ end
63
+ return " rule: #{rule}"
64
+ elsif @rule.is_a?(String)
65
+ return " rule: #{@rule}"
66
+ else
67
+ return ""
68
+ end
69
+ end
70
+
71
+ # Returns string representation of address.
72
+
73
+ def addr_desc
74
+ if @peer_ip.is_a?(NetAddr::CIDR)
75
+ if @peer_ip.version == 6
76
+ if @peer_ip.to_i(:netmask) == ((2**128)-1)
77
+ return @peer_ip.ip(:Short => true)
78
+ else
79
+ pip = @peer_ip.to_s(:Short => true)
80
+ pip = ":#{pip}" if pip =~ /^\//
81
+ return pip
82
+ end
83
+ else
84
+ if @peer_ip.to_i(:netmask) == 4294967295
85
+ return @peer_ip.ip
86
+ else
87
+ return @peer_ip.to_s
88
+ end
89
+ end
90
+ elsif @peer_ip.is_a?(String)
91
+ return @peer_ip
92
+ else
93
+ return @peer_ip.to_s
94
+ end
95
+ end
96
+
97
+ # Shows an error message.
98
+
99
+ def message
100
+ return "connection with #{addr_desc} " +
101
+ "denied by #{list_desc}#{rule_desc}"
102
+ end
103
+
104
+ end
105
+
106
+ # This class handles IP access denied exceptions
107
+ # for incoming connections/datagrams.
108
+
109
+ class IPAccessDenied::Input < IPAccessDenied
110
+
111
+ def message
112
+ return "incoming connection from " +
113
+ "#{addr_desc} denied by " +
114
+ "#{list_desc}#{rule_desc}"
115
+ end
116
+
117
+ end
118
+
119
+ # This class handles IP access denied exceptions
120
+ # for outgoing connections/datagrams.
121
+
122
+ class IPAccessDenied::Output < IPAccessDenied
123
+
124
+ def message
125
+ return "outgoing connection to " +
126
+ "#{addr_desc} denied by " +
127
+ "#{list_desc}#{rule_desc}"
128
+ end
129
+
130
+ end
131
+
@@ -0,0 +1,1209 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: Copyright (c) 2009 Paweł Wilk
5
+ # License:: This program is licensed under the terms of {GNU Lesser General Public License}[link:docs/LGPL-LICENSE.html] or {Ruby License}[link:docs/COPYING.html].
6
+ #
7
+ # This file contains IPAccessList class, which uses
8
+ # NetAddr::Tree to implement IP access list.
9
+ #
10
+ #--
11
+ #
12
+ # Copyright (C) 2009 by Paweł Wilk. All Rights Reserved.
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU Lesser General Public License
16
+ # as published by the Free Software Foundation; either version 3 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #
21
+ #++
22
+
23
+ require 'ipaddr'
24
+ require 'socket'
25
+ require 'resolv'
26
+ require 'netaddr'
27
+ require 'ipaccess/netaddr_patch'
28
+
29
+ # This class implements easy to
30
+ # manage IP access list based on NetAddr::Tree
31
+ # which uses binary search to speed up
32
+ # matching process. It stores data in a tree
33
+ # of NetAddr::CIDR objects and allows to add,
34
+ # remove and search them.
35
+ #
36
+ # ==== Access lists
37
+ #
38
+ # To control access IPAccessList maintains
39
+ # two abstract lists: white list and black
40
+ # list. Each list contains rules (CIDR objects
41
+ # with information about IP address and
42
+ # network mask). Access is evaluated as
43
+ # blocked when tested IP address matches
44
+ # rule from black list and not matches any rule
45
+ # from white list. Basically, white list rules
46
+ # override black list rules.
47
+ #
48
+ # To be precise: in order to increase
49
+ # lookups performance internally there
50
+ # are no real lists but one tree containing
51
+ # specially marked objects.
52
+ #
53
+ # ==== Basic Operations
54
+ #
55
+ # This class has no methods that actualy
56
+ # do network operations, it just allows
57
+ # you to check IP against black and
58
+ # white list. There are 2 major types
59
+ # of operations you can perform: rules
60
+ # management and access checks.
61
+ #
62
+ # Rules management methods allow you to
63
+ # add, remove and find IP access rules.
64
+ # Access checks let you test if given
65
+ # address or addresses are allowed or
66
+ # denied to perform network operations
67
+ # according to rules.
68
+ #
69
+ # ==== IPv4 and IPv6
70
+ #
71
+ # IPv6 addresses that are IPv4 compatible
72
+ # or IPv4 masked are automatically
73
+ # translated into IPv4 addresses while
74
+ # adding or searching.
75
+ #
76
+ # ==== Examples
77
+ #
78
+ # Examples of usage:
79
+ #
80
+ # access = IPAccessList.new # create new access list
81
+ # access.blacklist :ipv4_private # blacklist private IPv4 addresses
82
+ # access.whitelist '172.16.0.7' # whitelist 172.16.0.7
83
+ # access.granted? '172.16.0.7' # check access
84
+ # access.granted? '172.16.0.1' # check access
85
+ #
86
+ # Examples of deny-all & allow-selected strategy:
87
+ #
88
+ # access = IPAccessList.new # create new access list
89
+ # access.deny :all # blacklist all
90
+ # access.allow '192.168.1.0/24' # allow my private network
91
+ # access.allow :local # allow localhost
92
+ #
93
+ # puts access.show # display internal structure
94
+ # puts access.blacklist # display blacklisted IP addresses
95
+ # puts access.whitelist # display whitelisted IP addresses
96
+
97
+ class IPAccessList < NetAddr::Tree
98
+
99
+ # Creates new IPAccessList object. You may pass objects
100
+ # containing IP information to it. These objects will
101
+ # create black list rules. See obj_to_cidr description
102
+ # for more info on how to pass arguments.
103
+ #
104
+ # IPAccessList object and/or NetAddr::CIDR object(s) may
105
+ # carry black or white list assignments inside. If such
106
+ # object(s) will be used to create initial ruleset then
107
+ # assignment found there would be used instead of default.
108
+ #
109
+ # You should avoid passing hostnames as arguments since
110
+ # DNS is not reliable and responses may change with time.
111
+ # That may cause security flaws.
112
+ #
113
+ # ==== Examples
114
+ #
115
+ # IPAccessList.new '192.168.0.0/16', '127.0.0.1/255.0.0.0'
116
+ # IPAccessList.new :private, :local
117
+ # IPAccessList.new "randomseed.pl", :nonpublic
118
+
119
+ def initialize(*args)
120
+ args = [] if args == [nil]
121
+ super()
122
+ add!(*args) unless args.empty?
123
+ return self
124
+ end
125
+
126
+ # This method converts names to NetAddr::CIDR objects. It returns an array of CIDR objects.
127
+ #
128
+ # Allowed input: strings (DNS names or IP addresses optionally with masks), numbers (IP addresses representation),
129
+ # IPSocket objects, URI objects, IPAddr objects, Net::HTTP objects, IPAddrList objects, NetAddr::CIDR objects,
130
+ # NetAddr::Tree objects, IPAccessList objects, symbols, objects that contain file descriptors bound to sockets,
131
+ # and arrays of these.
132
+ #
133
+ # In case of resolving the IPv6 link-local addresses zone index is removed.
134
+ #
135
+ # ==== Examples
136
+ #
137
+ # obj_to_cidr("127.0.0.1") # uses IP address
138
+ # obj_to_cidr(2130706433) # uses numeric representation of 127.0.0.1
139
+ # obj_to_cidr(:private, "localhost") # uses special symbol and DNS hostname
140
+ # obj_to_cidr(:private, :localhost) # uses special symbols
141
+ # obj_to_cidr [:private, :auto] # other way to write the above
142
+ # obj_to_cidr "10.0.0.0/8" # uses masked IP address
143
+ # obj_to_cidr "10.0.0.0/255.0.0.0" # uses masked IP address
144
+ # obj_to_cidr IPSocket.new("www.pl", 80) # uses socket
145
+ # obj_to_cidr IPAddr("10.0.0.1") # uses IPAddr object
146
+ # obj_to_cidr NetAddr::CIDR.create("10.0.0.1") # uses NetAddr object
147
+ # obj_to_cidr URI('http://www.pl/') # uses URI
148
+ # obj_to_cidr 'http://www.pl/' # uses extracted host string
149
+ #
150
+ # ==== Special symbols
151
+ #
152
+ # When symbol is passed to this method it tries to find out if it has special meaning.
153
+ # That allows you to create access rules in an easy way. For most of them you may
154
+ # also specify IP protocol version using +ipv4_+ or +ipv6_+ prefix.
155
+ #
156
+ # Known symbols are:
157
+ #
158
+ # <b>+:all+</b> (+:any+, +:anyone+, +:world+, +:internet+, +:net+, +:everything+, +:everyone+, +:everybody+, +:anybody+)
159
+ #
160
+ # variants: +:ipv4_+ and +:ipv6_+
161
+ #
162
+ # Creates masked IP address that matches all networks:
163
+ # – 0.0.0.0/0
164
+ # – ::/0
165
+ #
166
+ # <b>+:broadcast+</b> (+:brd+)
167
+ #
168
+ # variants: +:ipv4_+ and +:ipv6_+
169
+ #
170
+ # Creates masked IP address that matches generic broadcast address:
171
+ # – 255.255.255.255/32
172
+ # – ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
173
+ #
174
+ # <b>+:local+</b> (+:localhost+, +:localdomain+, +:loopback+, +:lo+)
175
+ #
176
+ # variants: +:ipv4_+ and +:ipv6_+
177
+ #
178
+ # Creates masked IP addresses that match localhost:
179
+ # – 127.0.0.1/8
180
+ # – ::1/128
181
+ #
182
+ # <b>+:auto+</b> (+:automatic+, +:linklocal+)
183
+ #
184
+ # variants: +:ipv4_+ and +:ipv6_+
185
+ #
186
+ # Creates masked IP addresses that match automatically assigned address ranges:
187
+ # – 169.254.0.0/16
188
+ # – fe80::/10
189
+ #
190
+ # <b>+:private+</b> (+:intra+, +:intranet+, +:internal+)
191
+ #
192
+ # variants: +:ipv4_+ and +:ipv6_+
193
+ #
194
+ # Creates masked IP addresses that match private ranges:
195
+ # – 10.0.0.0/8
196
+ # – 172.16.0.0/12
197
+ # – 192.168.0.0/16
198
+ # – 2001:10::/28
199
+ # – 2001:db8::/32
200
+ # – fc00::/7
201
+ # – fdde:9e1a:dc85:7374::/64
202
+ #
203
+ # <b>+:multicast+</b> (+:multi+, +:multiemission+)
204
+ #
205
+ # variants: +:ipv4_+ and +:ipv6_+
206
+ #
207
+ # Creates masked IP addresses that match multicast addresses ranges:
208
+ # – 224.0.0.0/4
209
+ # – ff00::/8
210
+ # – ff02::1:ff00:0/104
211
+ #
212
+ # <b>+:reserved+</b> (+:example+)
213
+ #
214
+ # variants: +:ipv4_+
215
+ #
216
+ # Creates masked IP addresses that match reserved addresses ranges:
217
+ # – 192.0.2.0/24
218
+ # – 128.0.0.0/16
219
+ # – 191.255.0.0/16
220
+ # – 192.0.0.0/24
221
+ # – 198.18.0.0/15
222
+ # – 223.255.255.0/24
223
+ # – 240.0.0.0/4
224
+ #
225
+ # <b>+:strange+</b> (+:unusual+, +:nonpublic+, +:unpublic+)
226
+ #
227
+ # Creates masked IP addressess that match the following sets (both IPv4 and IPv6):
228
+ # – :local
229
+ # – :auto
230
+ # – :private
231
+ # – :reserved
232
+ # – :multicast
233
+
234
+ def self.obj_to_cidr(*obj)
235
+ obj = obj.flatten
236
+
237
+ if obj.size == 1
238
+ obj = obj.first
239
+ else
240
+ ary = []
241
+ obj.each { |o| ary += obj_to_cidr(o) }
242
+ ary.flatten!
243
+ return ary
244
+ end
245
+
246
+ # NetAddr::CIDR - immediate generation
247
+ return [obj.dup] if obj.is_a?(NetAddr::CIDR)
248
+
249
+ # IPAccessList - immediate generation
250
+ return obj.to_a if obj.is_a?(self.class)
251
+
252
+ # NetAddr::Tree - immediate generation
253
+ return obj.dump.map { |addr| addr[:CIDR] } if obj.is_a?(NetAddr::Tree)
254
+
255
+ # number - immediate generation
256
+ return [NetAddr::CIDR.create(obj)] if obj.is_a?(Numeric)
257
+
258
+ # IPAddr - fetch IP/mask string
259
+ obj = obj.native.inspect.split[1].chomp('>')[5..-1] if obj.is_a?(IPAddr)
260
+
261
+ # object containing socket member (e.g. Net::HTTP) - fetch socket
262
+ if obj.respond_to?(:socket)
263
+ obj = obj.socket
264
+ elsif obj.respond_to?(:client_socket)
265
+ obj = obj.client_socket
266
+ elsif obj.instance_variable_defined?(:@socket)
267
+ obj = obj.instance_variable_get(:@socket)
268
+ end
269
+ obj = obj.io if (obj.respond_to?(:io) && obj.io.respond_to?(:getpeername))
270
+
271
+ # some file descriptor but not socket - fetch socket
272
+ obj = Socket.for_fd(obj.fileno) if (!obj.respond_to?(:getpeername) && obj.respond_to?(:fileno))
273
+
274
+ # Socket - immediate generation
275
+ if obj.respond_to?(:getpeername)
276
+ peeraddr = Socket.unpack_sockaddr_in(obj.getpeername).last.split('%').first
277
+ return [NetAddr::CIDR.create(peeraddr)]
278
+ end
279
+
280
+ # symbol - immediate generation
281
+ if obj.is_a?(Symbol)
282
+ case obj
283
+ when :ipv4_all, :ipv4_any, :ipv4_anyone, :ipv4_world, :ipv4_internet, :ipv4_net, :ipv4_everything, :ipv4_everyone, :ipv4_everybody, :ipv4_anybody
284
+ obj = [ "0.0.0.0/0" ]
285
+ when :ipv6_all, :ipv6_any, :ipv6_anyone, :ipv6_world, :ipv6_internet, :ipv6_net, :ipv6_everything, :ipv6_everyone, :ipv6_everybody, :ipv6_anybody
286
+ obj = [ "0.0.0.0/0", "::/0" ]
287
+ when :ipv4_broadcast, :ipv4_brd
288
+ obj = [ "255.255.255.255/32" ]
289
+ when :ipv6_broadcast, :ipv6_brd
290
+ obj = [ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" ]
291
+ when :ipv4_local, :ipv4_localhost, :ipv4_loopback, :ipv4_lo
292
+ obj = [ "127.0.0.1/8" ]
293
+ when :ipv6_local, :ipv6_localhost, :ipv6_loopback, :ipv6_lo
294
+ obj = [ "::1/128" ]
295
+ when :ipv4_auto, :ipv4_automatic, :ipv4_linklocal
296
+ obj = [ "169.254.0.0/16" ]
297
+ when :ipv6_auto, :ipv6_automatic, :ipv6_linklocal
298
+ obj = [ "fe80::/10" ]
299
+ when :ipv4_private, :ipv4_intra, :ipv4_intranet, :ipv4_internal
300
+ obj = [ "10.0.0.0/8",
301
+ "172.16.0.0/12",
302
+ "192.168.0.0/16" ]
303
+ when :ipv6_private, :ipv6_intra, :ipv6_intranet, :ipv6_internal, :ipv6_ula, :ipv6_unique
304
+ obj = [ "2001:10::/28",
305
+ "2001:db8::/32",
306
+ "fc00::/7",
307
+ "fdde:9e1a:dc85:7374::/64" ]
308
+ when :ipv4_multicast, :ipv4_multi, :ipv4_multiemission
309
+ obj = [ "224.0.0.0/4" ]
310
+ when :ipv6_multicast, :ipv6_multi, :ipv6_multiemission
311
+ obj = [ "ff00::/8",
312
+ "ff02::1:ff00:0/104" ]
313
+ when :ipv4_example, :ipv4_reserved
314
+ obj = [ "192.0.2.0/24",
315
+ "128.0.0.0/16",
316
+ "191.255.0.0/16",
317
+ "192.0.0.0/24",
318
+ "198.18.0.0/15",
319
+ "223.255.255.0/24",
320
+ "240.0.0.0/4" ]
321
+ when :all, :any, :anyone, :world, :internet, :net, :everything, :everyone, :everybody, :anybody
322
+ return obj_to_cidr(:ipv4_all,
323
+ :ipv6_all)
324
+ when :broadcast, :brd
325
+ return obj_to_cidr(:ipv4_broadcast,
326
+ :ipv6_broadcast)
327
+ when :local, :localhost, :localdomain, :loopback, :lo
328
+ return obj_to_cidr(:ipv4_local,
329
+ :ipv6_local)
330
+ when :auto, :automatic, :linklocal
331
+ return obj_to_cidr(:ipv4_auto,
332
+ :ipv6_auto)
333
+ when :private, :intra, :intranet, :internal
334
+ return obj_to_cidr(:ipv4_private,
335
+ :ipv6_private)
336
+ when :multicast, :multi, :multiemission
337
+ return obj_to_cidr(:ipv4_multicast,
338
+ :ipv6_multicast)
339
+ when :reserved, :example
340
+ return obj_to_cidr(:ipv4_example)
341
+ when :strange, :unusual, :nonpublic, :unpublic
342
+ return obj_to_cidr(:local,
343
+ :auto,
344
+ :private,
345
+ :reserved,
346
+ :multicast)
347
+ else
348
+ raise ArgumentError, "Provided symbol is unknown: #{obj.to_s}"
349
+ end
350
+ return obj.map { |addr| NetAddr::CIDR.create(addr) } if obj.is_a?(Array)
351
+ end
352
+
353
+ # URI or something that responds to host method - fetch string
354
+ obj = obj.host if obj.respond_to?(:host)
355
+
356
+ # IPAddrList - immediate generation
357
+ return obj.to_a if obj.class.name.to_sym == :IPAddrList
358
+
359
+ # string or similar - immediate generation
360
+ if obj.respond_to?(:to_s)
361
+ obj = obj.to_s
362
+ # URI
363
+ if obj =~ /^[^:]+:\/\/(.*)/
364
+ obj = $1.split('/').first
365
+ # IP in URI
366
+ if obj =~ /^\[([^\]]+)\]/
367
+ obj = $1
368
+ else
369
+ obj = obj.split(':').first
370
+ end
371
+ end
372
+ begin
373
+ obj = NetAddr::CIDR.create(obj.split('%').first)
374
+ rescue NetAddr::ValidationError
375
+ addresses = Resolv::getaddresses(obj)
376
+ addresses.map! do |addr|
377
+ begin
378
+ NetAddr::CIDR.create(addr.split('%').first)
379
+ rescue ArgumentError
380
+ nil
381
+ end
382
+ end
383
+ addresses.flatten!
384
+ addresses.compact!
385
+ return addresses
386
+ end
387
+ end
388
+
389
+ # should never happend
390
+ return obj.is_a?(NetAddr::CIDR) ? [obj.dup] : [NetAddr::CIDR.create(obj.to_s)]
391
+ end
392
+
393
+ # This method calls IPAccessList.obj_to_cidr
394
+
395
+ def obj_to_cidr(*args)
396
+ self.class.obj_to_cidr(*args)
397
+ end
398
+
399
+ # This method finds all matching addresses in the list
400
+ # and returns an array containing these addresses.
401
+ # If the optional block is supplied, each matching element
402
+ # is passed to it, and the block‘s result is stored
403
+ # in the output array.
404
+ #
405
+ # Ba aware that it may call the block for same object twice
406
+ # if you'll pass two matching addresses.
407
+ #
408
+ # See obj_to_cidr description for more info about arguments
409
+ # you may pass to it.
410
+
411
+ def grep(*args)
412
+ return [] if empty?
413
+ out_ary = []
414
+ addrs = obj_to_cidr(*args)
415
+ addrs.each do |addr|
416
+ m = included_cidr(addr)
417
+ out_ary.push( block_given? ? yield(m) : m) unless m.nil?
418
+ end
419
+ return out_ary
420
+ end
421
+
422
+ alias_method :search, :grep
423
+
424
+ # This method finds all addresses in the list that are
425
+ # equal to given addresses/netmasks and returns an array containing
426
+ # these addresses. It is intended to be used to operate on
427
+ # lists rather than to match IPs to them.
428
+ #
429
+ # If the optional block is supplied,
430
+ # each matching element is passed to it, and the block‘s
431
+ # result is stored in the output array.
432
+ #
433
+ # See obj_to_cidr description for more info about arguments
434
+ # you may pass to it.
435
+
436
+ def grep_exact(*args)
437
+ return [] if empty?
438
+ out_ary = []
439
+ addrs = obj_to_cidr(*args)
440
+ addrs.each do |addr|
441
+ m = included_cidr(addr)
442
+ if (m == addr)
443
+ out_ary.push( block_given? ? yield(m) : m)
444
+ end
445
+ end
446
+ return out_ary
447
+ end
448
+
449
+ # This method adds new rule(s) to access list. By default
450
+ # elements are added to black list. If first or last argument
451
+ # is +:white+ or +:black+ then element is added to the specified
452
+ # list.
453
+ #
454
+ # If the given rule is exact (IP and mask) as pre-existent
455
+ # rule in the same access list then it is not added.
456
+ #
457
+ # You should avoid passing hostnames as arguments since
458
+ # DNS is not reliable and responses may change with time
459
+ # which may cause security flaws.
460
+ #
461
+ # Special case: some CIDR objects may carry information about
462
+ # access list they should belong to. If the last argument
463
+ # of this method does not specify access list and added rule
464
+ # is the kind of special CIDR containing information about
465
+ # assignment to some list then this extra sugar will be used
466
+ # in assignment instead of default +:black+. These special
467
+ # CIDR object are usualy result of passing IPAccessList
468
+ # as an argument. To be sure which access
469
+ # list will be altered always give its name when passing
470
+ # IPAccessList.
471
+ #
472
+ # See obj_to_cidr description for more info about arguments
473
+ # you may pass to it.
474
+
475
+ def add!(*args)
476
+ acl_list = nil
477
+ acl_list = args.shift if (args.first.is_a?(Symbol) && (args.first == :white || args.first == :black))
478
+ acl_list = args.pop if (args.last.is_a?(Symbol) && (args.last == :white || args.last == :black))
479
+ return nil if args.empty?
480
+ addrs = obj_to_cidr(*args)
481
+ addrs.each do |addr|
482
+ addr = addr.ipv4 if addr.ipv4_compliant?
483
+ add_list = acl_list.nil? ? addr.tag[:ACL] : acl_list # object with extra sugar
484
+ add_list = :black if add_list.nil?
485
+ exists = find_me(addr)
486
+ if exists.nil?
487
+ addr.tag[:Subnets] = []
488
+ addr.tag[:ACL] = add_list
489
+ add_to_tree(addr)
490
+ elsif exists.tag[:ACL] != add_list
491
+ exists.tag[:ACL] = :ashen
492
+ end
493
+ end
494
+ return nil
495
+ end
496
+
497
+ alias_method :add, :add!
498
+
499
+ # This method removes CIDR rules specified by the given
500
+ # objects containing IP information. It returns an array
501
+ # of removed CIDR rules.
502
+ #
503
+ # Make sure you will specify correct and exact netmasks
504
+ # in order to delete proper rules. This method will NOT
505
+ # remove rules that imprecisely match given address or rules
506
+ # that logically depends on specified rules, e.g.
507
+ # removing +192.168.0.0/16+ will leave +192.168.0.0/24+
508
+ # untouched. To create access exceptions for some
509
+ # ranges and/or addresses whitelist them using permit
510
+ # method. This method removes rules matching exact addresses/masks.
511
+ #
512
+ # If the first or last argument is a symbol and it's +:white+
513
+ # or +:black+ then the specified rule will be removed
514
+ # only from the given (white or black) list. If the list is
515
+ # not specified the rule will be removed from both lists.
516
+ #
517
+ # Special case: some CIDR objects may carry information about
518
+ # access list they should belong to. If the last argument
519
+ # of this method does not specify access list and added rule
520
+ # is the kind of special CIDR containing information about
521
+ # assignment to some list then this extra sugar will be used
522
+ # while removing. These special CIDR objects are usualy result
523
+ # of passing IPAccessList as an argument. To be sure which access
524
+ # list will be altered always give its name when passing
525
+ # IPAccessList.
526
+ #
527
+ # You should avoid passing hostnames as arguments since
528
+ # DNS is not reliable and responses may change with time
529
+ # which may cause security flaws.
530
+ #
531
+ # See obj_to_cidr description for more info about arguments
532
+ # you may pass to it.
533
+
534
+ def delete!(*args)
535
+ acl_list = nil
536
+ acl_list = args.shift if (args.first.is_a?(Symbol) && (args.first == :white || args.first == :black))
537
+ acl_list = args.pop if (args.last.is_a?(Symbol) && (args.last == :white || args.last == :black))
538
+ removed = []
539
+ return removed if (args.empty? || empty?)
540
+ addrs = obj_to_cidr(*args)
541
+ addrs.each do |addr|
542
+ addr = addr.ipv4 if addr.ipv4_compliant?
543
+ exists = find_me(addr)
544
+ unless exists.nil?
545
+ src_list = acl_list.nil? ? addr.tag[:ACL] : acl_list
546
+ src_list = nil if src_list == :ashen
547
+ ex_list = exists.tag[:ACL]
548
+ parent = exists.tag[:Parent]
549
+ children = exists.tag[:Subnets]
550
+ if (!src_list.nil? && ex_list == :ashen)
551
+ removed.push exists.safe_dup(:Subnets, :Parent)
552
+ exists.tag[:ACL] = (src_list == :black) ? :white : :black
553
+ elsif (src_list.nil? || ex_list == src_list)
554
+ removed.push exists.safe_dup(:Subnets, :Parent)
555
+ parent.tag[:Subnets].delete(exists)
556
+ children.each { |childaddr| add_to_parent(childaddr, parent) }
557
+ end
558
+ end # if found
559
+ end # args.each
560
+
561
+ return removed
562
+ end
563
+
564
+ alias_method :del!, :delete!
565
+ alias_method :delete, :delete!
566
+
567
+ # Adds IP addresses in given object(s) to white list if called
568
+ # with at least one argument. Returns white list if called
569
+ # without arguments (array of CIDR objects).
570
+ #
571
+ # You should avoid passing hostnames as arguments since
572
+ # DNS is not reliable and responses may change with time
573
+ # which may cause security flaws.
574
+
575
+ def whitelist(*args)
576
+ args.empty? ? to_a(:white) : add!(:white, *args)
577
+ end
578
+
579
+ alias_method :add_white, :whitelist
580
+ alias_method :allow, :whitelist
581
+ alias_method :permit, :whitelist
582
+
583
+ # Adds IP addresses in given object(s) to black list if called
584
+ # with at least one argument. Returns black list if called
585
+ # without arguments (array of CIDR objects).
586
+ #
587
+ # You should avoid passing hostnames as arguments since
588
+ # DNS is not reliable and responses may change with time
589
+ # which may cause security flaws.
590
+
591
+ def blacklist(*args)
592
+ args.empty? ? to_a(:black) : add!(:black, *args)
593
+ end
594
+
595
+ alias_method :add_black, :blacklist
596
+ alias_method :deny, :blacklist
597
+ alias_method :block, :blacklist
598
+
599
+ # This method returns an array of matching CIDR objects
600
+ # for the given objects containing IP information.
601
+ #
602
+ # It is designed to browse rules, NOT to check access. To do access
603
+ # check use IPAccessList#granted and IPAccessList#denied methods.
604
+ #
605
+ # See obj_to_cidr description for more info about arguments
606
+ # you may pass to it.
607
+ #
608
+ # Examples:
609
+ # access = IPAccessList.new '127.0.0.1/8' # blacklisted local IP
610
+ # access.included '127.0.0.1' # returns [127.0.0.0/8]
611
+ # access.included '127.0.0.1/24' # returns [127.0.0.0/8]
612
+ # access.included '127.0.0.1'/8 # returns [127.0.0.0/8]
613
+ # access.included '127.0.1.2'/8 # returns [127.0.0.0/8]
614
+
615
+ def included(*args)
616
+ found = []
617
+ return found if empty?
618
+ addrs = obj_to_cidr(*args)
619
+ return found if addrs.empty?
620
+ addrs.each do |addr|
621
+ rule = included_cidr(addr)
622
+ found.push(rule) unless rule.nil?
623
+ end
624
+ return found
625
+ end
626
+
627
+ # This method returns +true+ if ALL
628
+ # of the given objects containing IP information
629
+ # match some rules. Otherwise it returns +false+.
630
+ #
631
+ # It is designed to browse rules, NOT to check access. To do access
632
+ # check use IPAccessList#granted and IPAccessList#denied methods.
633
+ #
634
+ # See obj_to_cidr description for more info about arguments
635
+ # you may pass to it.
636
+
637
+ def include?(*args)
638
+ return false if empty?
639
+ addrs = obj_to_cidr(*args)
640
+ return false if addrs.empty?
641
+ addrs.each do |addr|
642
+ rule = included_cidr(addr)
643
+ return false if rule.nil?
644
+ end
645
+ return true
646
+ end
647
+
648
+ alias_method :include_all?, :include?
649
+
650
+ # This method returns first matching CIDR rule from
651
+ # the given objects containing IP information.
652
+ # Otherwise it returns +nil+.
653
+ #
654
+ # It is designed to browse rules, NOT to check access. To do access
655
+ # check use IPAccessList#granted and IPAccessList#denied methods.
656
+ #
657
+ # See obj_to_cidr description for more info about arguments
658
+ # you may pass to it.
659
+
660
+ def included_first(*args)
661
+ return nil if empty?
662
+ addrs = obj_to_cidr(*args)
663
+ return nil if addrs.empty?
664
+ addrs.each do |addr|
665
+ rule = included_cidr(addr)
666
+ return rule unless rule.nil?
667
+ end
668
+ return nil
669
+ end
670
+
671
+ # This method returns +true+ if at least one of
672
+ # the given objects containing IP information
673
+ # matches rule from the list. Otherwise it returns +false+.
674
+ #
675
+ # It is designed to browse rules, NOT to check access. To do access
676
+ # check use IPAccessList#granted and IPAccessList#denied methods.
677
+ #
678
+ # See obj_to_cidr description for more info about arguments
679
+ # you may pass to it.
680
+
681
+ def include_one?(*args)
682
+ not included_first.nil?
683
+ end
684
+
685
+ # This method returns matching CIDR rule if the given IP address
686
+ # (expressed as CIDR object) is on the list. Otherwise it returns +nil+.
687
+
688
+ def included_cidr(addr)
689
+ addr = addr.ipv4 if addr.ipv4_compliant?
690
+ root = addr.version == 4 ? @v4_root : @v6_root
691
+ return nil if root.tag[:Subnets].empty?
692
+ found = nil
693
+ found = find_me(addr)
694
+ found = find_parent(addr) if found.nil?
695
+ return nil if (found.nil? || found.hash == root.hash || !found.matches?(addr))
696
+ return found.safe_dup(:Subnets, :Parent)
697
+ end
698
+
699
+ # This method returns +true+ if the given IP address
700
+ # (expressed as CIDR object) matches some rule.
701
+ # Otherwise it returns +false+.
702
+ #
703
+ # It is designed to browse rules, NOT to check access. To do access
704
+ # check use granted_cidr and denied_cidr methods.
705
+
706
+ def include_cidr?(addr)
707
+ not included_cidr(addr).nil?
708
+ end
709
+
710
+ # This method returns an array containing CDIR objects that
711
+ # are result of finding IP rules given in the array.
712
+ #
713
+ # It is designed to browse rules, NOT to check access. To do access
714
+ # check use granted_cidr and denied_cidr methods.
715
+ #
716
+ # You should avoid passing hostnames as arguments since
717
+ # DNS is not reliable and responses may change with time
718
+ # which may cause security flaws.
719
+
720
+ def rule_exists(list, *args)
721
+ found = []
722
+ return found if empty?
723
+ addrs = obj_to_cidr(*args)
724
+ return found if addrs.empty?
725
+ addrs.each do |addr|
726
+ rule = rule_exists_cidr(list, addr)
727
+ found.push(rule) unless rule.nil?
728
+ end
729
+ return found
730
+ end
731
+ private :rule_exists
732
+
733
+ # This method returns CDIR object that
734
+ # equals given IP rule in the given list.
735
+ # It returns +nil+ if such rule doesn't
736
+ # exists.
737
+ #
738
+ # It is designed to check rules, NOT access. To do access
739
+ # check use granted_cidr and denied_cidr methods.
740
+
741
+ def rule_exists_cidr(list, addr)
742
+ addr = addr.ipv4 if addr.ipv4_compliant?
743
+ root = addr.version == 4 ? @v4_root : @v6_root
744
+ return nil if root.tag[:Subnets].empty?
745
+ found = find_me(addr)
746
+ if (found.nil? || found.hash == root.hash ||
747
+ (found.tag[:ACL] != list && found.tag[:ACL] != :ashen))
748
+ return nil
749
+ else
750
+ return found.safe_dup(:Subnets, :Parent)
751
+ end
752
+ end
753
+ private :rule_exists_cidr
754
+
755
+ # This method returns an array containing CDIR objects that
756
+ # are result of finding given IP rules in the black list.
757
+ #
758
+ # It is designed to browse rules, NOT to check access. To do access
759
+ # check use IPAccessList#granted and IPAccessList#denied methods.
760
+ #
761
+ # See obj_to_cidr description for more info about arguments
762
+ # you may pass to it.
763
+
764
+ def find_blacklist_rules(*args)
765
+ rule_exists(:black, *args)
766
+ end
767
+
768
+ alias_method :find_blacklist_rule, :find_blacklist_rules
769
+
770
+ # This method returns CDIR object that
771
+ # equals given IP rule in the black list.
772
+ # Otherwise it returns +nil+.
773
+ #
774
+ # It is designed to browse rules, NOT to check access. To do access
775
+ # check use granted_cidr and denied_cidr methods.
776
+
777
+ def find_blacklist_rule_cidr(addr)
778
+ rule_exists_cidr(:black, addr)
779
+ end
780
+
781
+ # This method returns +true+ if ALL of the given
782
+ # IP addresses/masks are present in the black list.
783
+ #
784
+ # It is designed to browse rules, NOT to check access. To do access
785
+ # check use IPAccessList#granted and IPAccessList#denied methods.
786
+ #
787
+ # See obj_to_cidr description for more info about arguments
788
+ # you may pass to it.
789
+
790
+ def blacklist_rules_exist?(*args)
791
+ addrs = obj_to_cidr(*args)
792
+ return found if addrs.empty?
793
+ addrs.each do |addr|
794
+ rule = rule_exists_cidr(:black, addr)
795
+ return false if rule.nil?
796
+ end
797
+ return true
798
+ end
799
+
800
+ alias_method :blacklist_rule_exists?, :blacklist_rules_exist?
801
+
802
+ # This method returns +true+ if the given
803
+ # IP address is on the IP rules black list.
804
+ #
805
+ # It is designed to browse rules, NOT to check access. To do access
806
+ # check use IPAccessList#granted and IPAccessList#denied methods.
807
+
808
+ def blacklist_rule_exists_cidr?(addr)
809
+ not rule_exists_cidr(:black, addr).nil?
810
+ end
811
+
812
+ # This method returns an array containing CDIR objects that
813
+ # is result of finding given IP rules in the white list.
814
+ #
815
+ # It is designed to browse rules, NOT to check access. To do access
816
+ # check use IPAccessList#granted and IPAccessList#denied methods.
817
+ #
818
+ # See obj_to_cidr description for more info about arguments
819
+ # you may pass to it.
820
+
821
+ def find_whitelist_rules(*args)
822
+ rule_exists(:white, *args)
823
+ end
824
+
825
+ alias_method :find_blacklist_rule, :find_blacklist_rules
826
+
827
+ # This method returns CDIR object that
828
+ # equals given IP rule in the white list.
829
+ #
830
+ # It is designed to check rules, NOT access. To do access
831
+ # check use allowed_cidr and denied_cidr methods.
832
+
833
+ def find_whitelist_rule_cidr(addr)
834
+ rule_exists_cidr(:white, addr)
835
+ end
836
+
837
+ # This method returns +true+ if ALL of the given
838
+ # IP addresses are on the white list.
839
+ #
840
+ # It is designed to check rules, NOT access. To do access
841
+ # check use allowed and denied methods.
842
+ #
843
+ # See obj_to_cidr description for more info about arguments
844
+ # you may pass to it.
845
+
846
+ def whitelist_rules_exist?(*args)
847
+ addrs = obj_to_cidr(*args)
848
+ return found if addrs.empty?
849
+ addrs.each do |addr|
850
+ rule = rule_exists_cidr(:white, addr)
851
+ return false if rule.nil?
852
+ end
853
+ return true
854
+ end
855
+
856
+ # This method returns +true+ if the given
857
+ # IP address is on the IP rules white list.
858
+ #
859
+ # It is designed to check rules, NOT access. To do access
860
+ # check use allowed and denied methods.
861
+
862
+ def whitelist_rule_exists_cidr?(addr)
863
+ not rule_exists_cidr(:white, addr).nil?
864
+ end
865
+
866
+ # This method returns CIDR rule that is the same
867
+ # as given IP and mask. It returns copy
868
+ # of a rule that query matches (CIDR object) or +nil+
869
+ # if no match was found.
870
+ #
871
+ # Examples:
872
+ # access = IPAccessList.new '127.0.0.1/8' # blacklisted local IP
873
+ # access.find '127.0.0.1' # returns nil
874
+ # access.find '127.0.0.1/24' # returns nil
875
+ # access.find '127.0.0.1'/8 # returns CIDR: 127.0.0.0/8
876
+ # access.find '127.0.1.2'/8 # returns CIDR: 127.0.0.0/8
877
+ #
878
+ # If you want simpler or more fancy search in rules
879
+ # (e.g. without need to specify mask or with ability to
880
+ # check many rules at one time) use methods like
881
+ # find_blacklist_rules or find_whitelist_rules.
882
+ #
883
+ # It is designed to browse rules, NOT to check access. To do access
884
+ # check use IPAccessList#granted and IPAccessList#denied methods.
885
+ #
886
+ # See obj_to_cidr description for more info about argument
887
+ # you may pass to it. Be aware that in case of name or special
888
+ # symbol given as an address only first result will be used and
889
+ # it will probably do not match because lack of proper netmask.
890
+
891
+ def find(addr)
892
+ return nil if empty?
893
+ addr = obj_to_cidr(addr)
894
+ return nil if addr.empty?
895
+ addr = addr.first
896
+ addr = addr.ipv4 if addr.ipv4_compliant?
897
+ root = addr.version == 4 ? @v4_root : @v6_root
898
+ return nil if root.tag[:Subnets].empty?
899
+ return super(addr)
900
+ end
901
+
902
+ # This method returns a hash containing CIDR object of
903
+ # given address named +:IP+ and CIDR object of the matching rule
904
+ # named +:Rule+ if the given CIDR contains blacklisted
905
+ # and not whitelisted address. Otherwise it returns an empty
906
+ # hash.
907
+ #
908
+ # It should be used to check access for one IP. It is
909
+ # recommended to use it in low-level routines.
910
+ #
911
+ # To not create copy of object when reporting rule
912
+ # but to use reference to original entry you may set
913
+ # second argument +true+. Use this with caution since
914
+ # modifying returned object may affect internal
915
+ # structure of access list.
916
+
917
+ def denied_cidr(addr, nodup=false)
918
+ addr = addr.ipv4 if addr.ipv4_compliant?
919
+ root = addr.version == 4 ? @v4_root : @v6_root
920
+ list = root
921
+ return nil if list.tag[:Subnets].length.zero?
922
+
923
+ until (li = NetAddr.cidr_find_in_list(addr, list.tag[:Subnets])).nil?
924
+ if li.is_a?(Integer)
925
+ li = list.tag[:Subnets][li]
926
+ break
927
+ else
928
+ if li.tag[:ACL] == :black
929
+ list = li
930
+ else
931
+ break
932
+ end
933
+ end
934
+ end
935
+
936
+ ret = {}
937
+ li = list if li.nil?
938
+ if (!li.nil? && li.tag[:ACL] == :black && li.matches?(addr))
939
+ if nodup
940
+ rule = li
941
+ addr = addr
942
+ else
943
+ rule = li.safe_dup(:Subnets, :Parent)
944
+ addr = addr.safe_dup
945
+ end
946
+ ret[:IP] = addr
947
+ ret[:Rule] = rule
948
+ end
949
+ return ret
950
+ end
951
+
952
+ # This method returns +true+ if the given CIDR contains
953
+ # blacklisted and not whitelisted address. Otherwise
954
+ # it returns +false+.
955
+ #
956
+ # It should be used to check access for one IP. It is recommended
957
+ # to use it in low-level routines.
958
+
959
+ def denied_cidr?(addr)
960
+ not denied_cidr(addr, true).empty?
961
+ end
962
+
963
+ # This method checks if access for IP or IPs is denied.
964
+ # It returns an array of hashes containing tested CIDR
965
+ # objects (named +:IP+) and rules objects (named +:Rule+).
966
+ # Pair is present in output if given IP address matches
967
+ # black list rules and noesn't match white list rules.
968
+ #
969
+ # See obj_to_cidr description for more info about arguments
970
+ # you may pass to it.
971
+ #
972
+ # It should be used to check access for many IP addresses
973
+ # and/or address(-es) that are not necessarily represented
974
+ # by CIDR objects.
975
+ #
976
+ # You should avoid passing hostnames as arguments since
977
+ # DNS is not reliable and responses may change with time
978
+ # which may cause security flaws.
979
+ #
980
+ # To not create copy of objects when reporting rules
981
+ # but to use reference to original entries you may set
982
+ # last argument +true+. Use this with caution since
983
+ # modifying returned object may affect internal
984
+ # structure of access list.
985
+
986
+ def denied(*args)
987
+ found = []
988
+ return found if empty?
989
+ nodup = args.last.is_a?(TrueClass) ? args.pop : false
990
+ addrs = obj_to_cidr(*args)
991
+ addrs.each do |addr|
992
+ pair = denied_cidr(addr, nodup)
993
+ found.push(pair) unless pair.empty?
994
+ end
995
+ return found
996
+ end
997
+
998
+ # This method returns +true+ if at least one of given CIDR
999
+ # objects matches black list rules and doesn't match white
1000
+ # list rules. Otherwise it returns +false+.
1001
+ #
1002
+ # See obj_to_cidr description for more info about arguments
1003
+ # you may pass to it.
1004
+
1005
+ def denied?(*args)
1006
+ args.push true
1007
+ not denied(*args).empty?
1008
+ end
1009
+
1010
+ alias_method :denied_one?, :denied?
1011
+ alias_method :denied_one_of?, :denied?
1012
+
1013
+ # This method returns given CIDR object
1014
+ # if the given CIDR is not blacklisted or whitelisted.
1015
+ # Otherwise it returns +nil+.
1016
+ #
1017
+ # It should be used to check access for one IP. It is recommended
1018
+ # to use it in low-level routines.
1019
+
1020
+ def granted_cidr(addr)
1021
+ denied_cidr(addr, true).empty? ? addr : nil
1022
+ end
1023
+
1024
+ # This method returns +true+ if the given CIDR is not
1025
+ # blacklisted or whitelisted. Otherwise it returns +false+.
1026
+ #
1027
+ # It should be used to check access for one IP. It is
1028
+ # recommended to use it in low-level routines.
1029
+
1030
+ def granted_cidr?(addr)
1031
+ denied_cidr(addr, true).empty?
1032
+ end
1033
+
1034
+ # This method returns an array of the given CIDR objects that
1035
+ # don't match black list rules or match white list rules.
1036
+ #
1037
+ # See obj_to_cidr description for more info about arguments
1038
+ # you may pass to it.
1039
+ #
1040
+ # It should be used to check access for many IP addresses
1041
+ # and/or address(-es) that are not necessarily represented
1042
+ # by CIDR objects.
1043
+ #
1044
+ # You should avoid passing hostnames as arguments since
1045
+ # DNS is not reliable and responses may change with time
1046
+ # which may cause security flaws.
1047
+
1048
+ def granted(*args)
1049
+ found = []
1050
+ return found if empty?
1051
+ args = obj_to_cidr(*args)
1052
+ args.each do |addr|
1053
+ rule = denied_cidr(addr, true)
1054
+ found.push(addr) if rule.empty?
1055
+ end
1056
+ return found
1057
+ end
1058
+
1059
+ # This method returns +true+ if all of given CIDR
1060
+ # objects are not blacklisted or are whitelisted.
1061
+ # Otherwise it returns +false+.
1062
+ #
1063
+ # See obj_to_cidr description for more info about arguments
1064
+ # you may pass to it.
1065
+ #
1066
+ # You should avoid passing hostnames as arguments since
1067
+ # DNS is not reliable and responses may change with time
1068
+ # which may cause security flaws.
1069
+
1070
+ def granted?(*args)
1071
+ args.push true
1072
+ denied(*args).empty?
1073
+ end
1074
+
1075
+ alias_method :granted_one?, :granted?
1076
+ alias_method :granted_one_of?, :granted?
1077
+
1078
+ # Returns new instance containing elements from this object
1079
+ # and objects passed as an argument. If objects contain IP
1080
+ # information but it's impossible to obtain whether they
1081
+ # relate to black or white list, then blacklisting is assumed.
1082
+ #
1083
+ # See obj_to_cidr description for more info about arguments
1084
+ # you may pass to it.
1085
+
1086
+ def +(*args)
1087
+ obj = self.class.new(self)
1088
+ obj.add!(*args)
1089
+ return obj
1090
+ end
1091
+
1092
+ # Returns new list with removed CIDR objects which are exactly the same as objects passed as an argument.
1093
+ #
1094
+ # See obj_to_cidr description for more info about arguments
1095
+ # you may pass to it.
1096
+
1097
+ def -(*args)
1098
+ self_copy = self.class.new(self)
1099
+ self_copy.delete(*args)
1100
+ return self_copy
1101
+ end
1102
+
1103
+ # Returns list of addresses and masks as a string with elements joined using space or given string.
1104
+
1105
+ def join(sep=' ')
1106
+ dump.map do |obj|
1107
+ obj[:CIDR].to_s
1108
+ end.join(sep)
1109
+ end
1110
+
1111
+ # Remove all elements.
1112
+
1113
+ def clear!
1114
+ remove!('0.0.0.0/0')
1115
+ remove!('::/0')
1116
+ end
1117
+
1118
+ # This method returns +true+ if the list is empty.
1119
+
1120
+ def empty?
1121
+ @v4_root.tag[:Subnets].empty? &&
1122
+ @v6_root.tag[:Subnets].empty?
1123
+ end
1124
+
1125
+ # This operator calls add! method.
1126
+
1127
+ def <<(*args)
1128
+ add!(*args)
1129
+ return self
1130
+ end
1131
+
1132
+ # This method returns an array of CIDR objects belonging
1133
+ # to given access list. If no list is specified it returns
1134
+ # an array containing all lists. It preserves access list
1135
+ # information in copied objects.
1136
+
1137
+ def dump_flat_list(parent, type=nil)
1138
+ list = []
1139
+ parent.tag[:Subnets].each do |entry|
1140
+ if (type.nil? || entry.tag[:ACL] == type || entry.tag[:ACL] == :ashen)
1141
+ list.push(entry)
1142
+ end
1143
+ if (entry.tag[:Subnets].length > 0)
1144
+ list.concat dump_flat_list(entry, type)
1145
+ end
1146
+ end
1147
+ list.map { |cidr| cidr.safe_dup(:Subnets, :Parent) }
1148
+ return list
1149
+ end
1150
+ private :dump_flat_list
1151
+
1152
+ # This method produces array of CIDR objects that
1153
+ # belong to an access list specified by type (:white or :black).
1154
+ # If no type is given it returns all entries. It preserves
1155
+ # access list assignment information in CIDR copies.
1156
+
1157
+ def to_a(type=nil)
1158
+ dump_flat_list(@v4_root, type) +
1159
+ dump_flat_list(@v6_root, type)
1160
+ end
1161
+
1162
+ # This method shows internal tree of CIDR objects marked
1163
+ # with access list they belong to. While interpreting it
1164
+ # you should be aware that access for tested IP will not
1165
+ # be denied if black list rule has at least one whitelisted,
1166
+ # preceding rule in the path that leads to it. You may
1167
+ # also notice doubled entries sometimes. That happens
1168
+ # in case when the same rule is belongs to both:
1169
+ # black list and white list.
1170
+
1171
+ def show()
1172
+ printed = "IPv4 Tree\n---------\n"
1173
+ list4 = dump_children(@v4_root)
1174
+ list6 = dump_children(@v6_root)
1175
+
1176
+ list4.each do |entry|
1177
+ cidr = entry[:CIDR]
1178
+ depth = entry[:Depth]
1179
+ alist = cidr.tag[:ACL]
1180
+ indent = depth.zero? ? "" : " " * (depth*3)
1181
+ if alist == :ashen
1182
+ printed << "[black] #{indent}#{cidr.desc}\n"
1183
+ printed << "[white] #{indent}#{cidr.desc}\n"
1184
+ else
1185
+ alist = cidr.tag[:ACL].nil? ? "[undef]" : "[#{cidr.tag[:ACL]}]"
1186
+ printed << "#{alist} #{indent}#{cidr.desc}\n"
1187
+ end
1188
+ end
1189
+
1190
+ printed << "\nIPv6 Tree\n---------\n" if list6.length.nonzero?
1191
+ list6.each do |entry|
1192
+ cidr = entry[:CIDR]
1193
+ depth = entry[:Depth]
1194
+ alist = cidr.tag[:ACL]
1195
+ indent = depth.zero? ? "" : " " * (depth*3)
1196
+ if alist == :ashen
1197
+ printed << "[black] #{indent}#{cidr.desc(:Short => true)}\n"
1198
+ printed << "[white] #{indent}#{cidr.desc(:Short => true)}\n"
1199
+ else
1200
+ alist = cidr.tag[:ACL].nil? ? "[undef]" : "[#{cidr.tag[:ACL]}]"
1201
+ printed << "#{alist} #{indent}#{cidr.desc(:Short => true)}\n"
1202
+ end
1203
+ end
1204
+ return printed
1205
+ end
1206
+
1207
+ end # class IPAccessList
1208
+
1209
+