ipaccess 0.0.2

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.
@@ -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
+