ipaccess 0.0.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +15 -0
  2. checksums.yaml.gz.sig +1 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gemtest +0 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +12 -0
  7. data/ChangeLog +1495 -0
  8. data/{docs/LGPL-LICENSE → LGPL-LICENSE} +0 -0
  9. data/Manifest.txt +76 -0
  10. data/README.md +96 -0
  11. data/Rakefile +65 -42
  12. data/docs/COPYING +41 -45
  13. data/docs/FAQ +12 -0
  14. data/docs/HISTORY +17 -0
  15. data/docs/LEGAL +1 -1
  16. data/docs/LGPL +166 -0
  17. data/docs/TODO +150 -7
  18. data/docs/images/ipaccess.png +0 -0
  19. data/docs/images/ipaccess_ac_for_args.png +0 -0
  20. data/docs/images/ipaccess_ac_for_socket.png +0 -0
  21. data/docs/images/ipaccess_logo.png +0 -0
  22. data/docs/images/ipaccess_relations.png +0 -0
  23. data/docs/images/ipaccess_setup_origin.png +0 -0
  24. data/docs/images/ipaccess_setup_origin_tab.png +0 -0
  25. data/docs/images/ipaccess_view.png +0 -0
  26. data/docs/rdoc.css +22 -0
  27. data/examples/ftp.rb +62 -0
  28. data/examples/http.rb +81 -0
  29. data/examples/imap.rb +37 -0
  30. data/examples/pop.rb +31 -0
  31. data/examples/smtp.rb +26 -0
  32. data/examples/tcp_server.rb +32 -0
  33. data/examples/tcp_socket.rb +7 -3
  34. data/examples/telnet.rb +32 -0
  35. data/examples/text_message.rb +45 -0
  36. data/lib/ipaccess.rb +557 -7
  37. data/lib/ipaccess/arm_sockets.rb +7 -4
  38. data/lib/ipaccess/ghost_doc/ghost_doc.rb +23 -0
  39. data/lib/ipaccess/ghost_doc/ghost_doc_acl.rb +54 -0
  40. data/lib/ipaccess/ghost_doc/ghost_doc_net_ftp.rb +213 -0
  41. data/lib/ipaccess/ghost_doc/ghost_doc_net_http.rb +272 -0
  42. data/lib/ipaccess/ghost_doc/ghost_doc_net_smtp.rb +186 -0
  43. data/lib/ipaccess/ghost_doc/ghost_doc_net_telnet.rb +227 -0
  44. data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist.rb +36 -0
  45. data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist_e.rb +7 -0
  46. data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist.rb +36 -0
  47. data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist_e.rb +7 -0
  48. data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist.rb +36 -0
  49. data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist_e.rb +7 -0
  50. data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist.rb +36 -0
  51. data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist_e.rb +7 -0
  52. data/lib/ipaccess/ghost_doc/ghost_doc_patched_usage.rb +64 -0
  53. data/lib/ipaccess/ghost_doc/ghost_doc_sockets.rb +571 -0
  54. data/lib/ipaccess/ip_access_check.rb +508 -0
  55. data/lib/ipaccess/ip_access_errors.rb +186 -40
  56. data/lib/ipaccess/ip_access_list.rb +955 -1122
  57. data/lib/ipaccess/ip_access_set.rb +212 -0
  58. data/lib/ipaccess/net/ftp.rb +39 -0
  59. data/lib/ipaccess/net/http.rb +39 -0
  60. data/lib/ipaccess/net/https.rb +30 -0
  61. data/lib/ipaccess/net/imap.rb +39 -0
  62. data/lib/ipaccess/net/pop.rb +46 -0
  63. data/lib/ipaccess/net/smtp.rb +39 -0
  64. data/lib/ipaccess/net/telnet.rb +38 -0
  65. data/lib/ipaccess/patches/generic.rb +807 -0
  66. data/lib/ipaccess/patches/net_ftp.rb +165 -0
  67. data/lib/ipaccess/patches/net_http.rb +175 -0
  68. data/lib/ipaccess/patches/net_https.rb +29 -0
  69. data/lib/ipaccess/patches/net_imap.rb +117 -0
  70. data/lib/ipaccess/patches/net_pop.rb +171 -0
  71. data/lib/ipaccess/patches/net_smtp.rb +130 -0
  72. data/lib/ipaccess/patches/net_telnet.rb +103 -0
  73. data/lib/ipaccess/{netaddr_patch.rb → patches/netaddr.rb} +20 -11
  74. data/lib/ipaccess/patches/sockets.rb +586 -0
  75. data/lib/ipaccess/socket.rb +52 -1
  76. data/lib/ipaccess/sockets.rb +4 -30
  77. data/spec/ip_access_list_spec.rb +33 -21
  78. data/spec/spec.opts +2 -2
  79. metadata +289 -63
  80. metadata.gz.sig +0 -0
  81. data/docs/DOWNLOAD +0 -17
  82. data/docs/README +0 -95
  83. data/docs/WELCOME +0 -8
  84. data/lib/ipaccess/ghost_doc.rb +0 -206
  85. data/lib/ipaccess/ghost_doc_acl.rb +0 -31
  86. data/lib/ipaccess/ip_access.rb +0 -456
  87. data/lib/ipaccess/ip_access_patches.rb +0 -431
  88. data/spec/core_spec.rb +0 -5
data/examples/imap.rb ADDED
@@ -0,0 +1,37 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/net/imap'
4
+
5
+ # Add host's IP by to black list of global output access set
6
+ IPAccess::Set::Global.output.blacklist 'randomseed.pl'
7
+
8
+ # Create custom access set with one blacklisted IP
9
+ acl = IPAccess::Set.new
10
+ acl.output.blacklist 'randomseed.pl'
11
+ acl.output.blacklist 'imap.heise.de'
12
+
13
+ #===== Example cases
14
+
15
+ # 1
16
+
17
+ imap = IPAccess::Net::IMAP.new('randomseed.pl')
18
+ imap.authenticate('LOGIN', 'joe_user', 'joes_password')
19
+ imap.examine('INBOX')
20
+ imap.search(["RECENT"]).each do |message_id|
21
+ envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
22
+ puts "#{envelope.from[0].name}: \t#{envelope.subject}"
23
+ end
24
+
25
+ # 2
26
+
27
+ imap = Net::IMAP.new('imap.heise.de', 'imaps', true, nil, false)
28
+
29
+ IPAccess.arm imap, acl
30
+
31
+ imap.authenticate('LOGIN', 'joe_user', 'joes_password')
32
+ imap.examine('INBOX')
33
+ imap.search(["RECENT"]).each do |message_id|
34
+ envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
35
+ puts "#{envelope.from[0].name}: \t#{envelope.subject}"
36
+ end
37
+
data/examples/pop.rb ADDED
@@ -0,0 +1,31 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/net/pop'
4
+
5
+ # Add host's IP by to black list of global output access set
6
+ IPAccess::Set::Global.output.blacklist 'randomseed.pl'
7
+
8
+ # Create custom access set with one blacklisted IP
9
+ acl = IPAccess::Set.new
10
+ acl.output.blacklist 'randomseed.pl'
11
+ acl.output.blacklist 'gmail.com'
12
+
13
+ #===== Example cases
14
+
15
+ # 1
16
+
17
+ i = 0
18
+ IPAccess::Net::APOP.delete_all('randomseed.pl', 110,
19
+ 'YourAccount', 'YourPassword') do |m|
20
+ File.open("inbox/#{i}", 'w') do |f|
21
+ f.write m.pop
22
+ end
23
+ i += 1
24
+ end
25
+
26
+ # 2
27
+
28
+ p = Net::POP3.new 'randomseed.pl'
29
+ IPAccess.arm p, acl
30
+ p.auth_only 'user', 'pass'
31
+
data/examples/smtp.rb ADDED
@@ -0,0 +1,26 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/net/smtp'
4
+
5
+ # Add host's IP by to black list of global output access set
6
+ IPAccess::Set::Global.output.blacklist 'randomseed.pl'
7
+
8
+ # Create custom access set with one blacklisted IP
9
+ acl = IPAccess::Set.new
10
+ acl.output.blacklist 'randomseed.pl'
11
+ acl.output.blacklist 'gmail.com'
12
+
13
+ #===== Example cases
14
+
15
+ # 1
16
+
17
+ IPAccess::Net::SMTP.start('randomseed.pl', 25) do |smtp|
18
+ ;
19
+ end
20
+
21
+ # 2
22
+
23
+ p = Net::SMTP.new 'randomseed.pl'
24
+ IPAccess.arm p, acl
25
+ p.start 'user', 'pass'
26
+
@@ -0,0 +1,32 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/socket'
4
+
5
+ acl = IPAccess::Set.new
6
+ acl.input.blacklist :strange
7
+
8
+ # 0
9
+
10
+ serv = IPAccess::TCPServer.new(31337) # create listening TCP socket
11
+ serv.acl = :private # create and use private access set
12
+ serv.blacklist :local, :private # block local and private IP addresses
13
+ serv.permit '127.0.0.5' # make an exception
14
+
15
+ puts serv.acl.show # show listed IP addresses
16
+ sock = serv.sysaccept # accept connection
17
+
18
+
19
+ # 1
20
+
21
+ # Create and use the TCP socket
22
+
23
+ s = IPAccess::TCPServer.new(31337, acl)
24
+ #s = TCPServer.new(31337)
25
+
26
+ # Arm object s
27
+ #IPAccess.arm s, acl
28
+
29
+ puts s.acl.show
30
+ puts "\nissue in terminal: telnet 127.0.0.1 31337\n\n"
31
+ g = s.accept
32
+
@@ -1,9 +1,13 @@
1
1
  $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
2
 
3
- require 'ipaccess'
3
+ require 'ipaccess/socket'
4
4
 
5
- IPAccess::Global.output.blacklist 'randomseed.pl'
6
- IPAccess.arm TCPSocket
5
+ # Add host's IP by to black list of global output access set
6
+ IPAccess::Set::Global.output.blacklist '78.0.0.0/8'
7
+
8
+ # Create and use the TCP socket
7
9
  s = TCPSocket.new('randomseed.pl', 80)
8
10
 
11
+ # Arm object s
12
+ IPAccess.arm s
9
13
 
@@ -0,0 +1,32 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/net/telnet' # load Net::Telnet version and IPAccess.arm method
4
+
5
+ opts = {}
6
+ opts["Host"] = 'randomseed.pl'
7
+ opts["Port"] = '80'
8
+
9
+ t = Net::Telnet.new(opts) # try to connect to remote host
10
+
11
+ begin
12
+
13
+ IPAccess.arm t # arm single Telnet object (will use global access set)
14
+ t.blacklist! 'randomseed.pl' # blacklist host while being connected
15
+
16
+ rescue IPAccessDenied => e
17
+
18
+ puts "Message:\t#{e.message}"
19
+ puts
20
+ puts "ACL:\t\t#{e.acl}"
21
+ puts "Exception:\t#{e.inspect}"
22
+ puts "Remote IP:\t#{e.peer_ip}"
23
+ puts "Rule:\t\t#{e.rule}"
24
+ puts "Originator:\t#{e.originator}"
25
+ puts "CIDR's Origin:\t#{e.peer_ip.tag[:Originator]}\n\n"
26
+ puts "Session closed:\t#{t.closed?}"
27
+
28
+ end
29
+
30
+ p t
31
+ p "end"
32
+
@@ -0,0 +1,45 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'ipaccess/socket'
4
+
5
+ begin
6
+
7
+ # add some rules
8
+ acl = IPAccess::Set.new
9
+ acl.input.blacklist_reasonable "Local and private addresses aren't welcome here", :local, :private
10
+ acl.input.whitelist_reasonable "I like that system, it makes toasts", '172.16.0.10', '1234:1234:1234:1234:1234:1234:1234:1234'
11
+
12
+ # show the lists with reasons for entries
13
+ puts acl.show(true)
14
+
15
+ # create a server
16
+ s = IPAccess::TCPServer.new(31337, acl)
17
+
18
+ # let it stays opened on access denied event
19
+ s.opened_on_deny = true
20
+
21
+ # listen for a connection
22
+ puts "\nnow use terminal and issue: telnet 127.0.0.1 31337\n\n"
23
+ n = s.accept
24
+
25
+ rescue IPAccessDenied => e
26
+
27
+ # show some stuff carried along with an exception
28
+ puts "Message: #{e.message}"
29
+ puts
30
+ puts "Exception: #{e.inspect}"
31
+ puts "Remote IP: #{e.peer_ip} (#{e.peer_ip_short})"
32
+ puts "Rule: #{e.rule} (#{e.rule_short})"
33
+ puts "Reason: #{e.reason}"
34
+ puts "Originator: #{e.originator}"
35
+ puts "Internal Originator in CIDR: #{e.peer_ip.tag[:Originator]}"
36
+ puts "ACL: #{e.acl}"
37
+
38
+ # send a reason to our peer before closing connection
39
+ unless e.originator.closed?
40
+ e.originator.write(e.reason + "\n\r")
41
+ e.originator.close
42
+ end
43
+
44
+ end
45
+
data/lib/ipaccess.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  # encoding: utf-8
2
2
  #
3
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].
4
+ # Copyright:: Copyright (c) 2009-2014 by Paweł Wilk
5
+ # License:: This program is licensed under the terms of {GNU Lesser General Public License}[link:docs/LGPL.html] or {Ruby License}[link:docs/COPYING.html].
6
6
  #
7
7
  # Classes contained in this library allow you to create
8
8
  # and manage IP access lists in an easy way. You may use
9
- # IPAccess class to maintain inpu/output traffic control.
10
- # You also may use IPAccessList class directly to build
9
+ # IPAccess::Set class to maintain inpu/output traffic control.
10
+ # You also may use IPAccess::List class directly to build
11
11
  # your own access sets based on black lists and white lists.
12
12
  #
13
13
  #--
@@ -28,8 +28,558 @@ require 'socket'
28
28
  require 'resolv'
29
29
  require 'netaddr'
30
30
 
31
- require 'ipaccess/netaddr_patch'
31
+ require 'ipaccess/patches/netaddr'
32
32
  require 'ipaccess/ip_access_list'
33
- require 'ipaccess/ip_access'
34
- require 'ipaccess/sockets'
33
+ require 'ipaccess/ip_access_set'
35
34
 
35
+ # This module contains classes that are
36
+ # used to control IP access. There are
37
+ # three major components you may need:
38
+ #
39
+ # === IPAccess::List class
40
+ #
41
+ # This class lets you create IP
42
+ # access list with blacklisted
43
+ # and whitelisted elements. It
44
+ # also has methods for checking
45
+ # whether given IP matches the
46
+ # list.
47
+ #
48
+ # === IPAccess::Set class
49
+ #
50
+ # This class contains two
51
+ # objects that are instances
52
+ # of IPAccess::List class.
53
+ # It allows you to create so
54
+ # called access set. The access
55
+ # set contains members named
56
+ # +input+ and +output+. All methods
57
+ # that validate IP access do it
58
+ # against one of the lists. Input
59
+ # access list is for incomming
60
+ # and output for outgoing IP traffic.
61
+ # In case of connection-oriented
62
+ # sockets and other network objects
63
+ # the convention is to use output access
64
+ # list to validate connections that
65
+ # we initiate. The incomming traffic
66
+ # in that model means the connections
67
+ # initiated by a remote peer.
68
+ #
69
+ # === Patching engine
70
+ #
71
+ # IPAccess was initialy considered as a
72
+ # set of classes that you may use
73
+ # in your own programs to control
74
+ # IP access. That means your own classes
75
+ # used for communication should use
76
+ # access lists or sets before making any
77
+ # real connections or sending any datagrams.
78
+ #
79
+ # Fortunately there are many network classes,
80
+ # including sockets, that Ruby ships with.
81
+ # It would be waste of resources to not modify
82
+ # them to support IP access control and automagically
83
+ # throw exceptions when access should be denied.
84
+ #
85
+ # And here the special module method called +IPAccess.arm+
86
+ # comes in. It lets you patch most of Ruby's
87
+ # networking classes and objects. Besides
88
+ # equipping them in IPAccess::Set instance
89
+ # it also adds some methods for doing quick
90
+ # checks and changes in access lists.
91
+ #
92
+ # The patching engine can arm network classes and
93
+ # single network objects. It is not loaded by default
94
+ # since you may not want extra code attached to a
95
+ # program that uses access lists or sets with
96
+ # own access checking code.
97
+ #
98
+ # === Variants of popular classes
99
+ #
100
+ # Sometimes you want to write a code that
101
+ # uses standard Ruby's network objects
102
+ # but you find it dirty to alter classes or objects.
103
+ # In that case you may want to use static variants
104
+ # of Ruby's network classes that are not patches
105
+ # but derived classes.
106
+ #
107
+ # === Exceptions
108
+ #
109
+ # When you are dealing with patched (armed) versions
110
+ # of classes and objects or when you are using
111
+ # special variants of popular network classes, you have
112
+ # to rely on exceptions as the only way for
113
+ # access checking methods to tell your program
114
+ # that an event (like access denied) happened.
115
+ #
116
+ # Note that when exception is thrown
117
+ # the communication session is closed in case
118
+ # of connection-oriented network objects.
119
+ # You may change it by switching +opened_on_deny+
120
+ # attribute to +true+.
121
+ #
122
+ # See IPAccess::Set#check_in to know more
123
+ # about tracking original network object
124
+ # that caused exception to happend. Note
125
+ # that in case of armed versions of network
126
+ # classes (or access-contolled variants)
127
+ # an information about original network
128
+ # object stored within an exception will be set to
129
+ # +nil+ if access had been denied before
130
+ # object was initialized. This shouldn't
131
+ # happend often, since access checks are lazy
132
+ # (they are performed only when connection
133
+ # is going to be made).
134
+ #
135
+ # See IPAccessDenied for more information
136
+ # about what you can do with exceptions.
137
+ #
138
+ # === Sockets in armed network objects
139
+ #
140
+ # Specialized Ruby's network classes,
141
+ # such as Net::HTTP or Net::Telnet
142
+ # and their variants created by this library,
143
+ # make use of socket objects. For example
144
+ # Net::HTTP class uses TCPSocket instance to
145
+ # create TCP connection. When versions
146
+ # of these <tt>Net::</tt> objects with
147
+ # enabled access control are used then
148
+ # the internal routines of IPAccess
149
+ # will also try to patch underlying sockets and assign
150
+ # to them the same access set that is used by main
151
+ # object. It is done to avoid access leaks.
152
+ # However, such armed internal sockets will have
153
+ # +opened_on_deny+ flag switched on since
154
+ # closing session (and an eventual connection)
155
+ # should be settled by main object.
156
+ #
157
+ # === Ordination of elements
158
+ #
159
+ # To properly understand what are the most important
160
+ # structures mentioned above it's worth
161
+ # to look at the diagram:
162
+ #
163
+ # link:images/ipaccess_view.png
164
+ #
165
+ # == Usage
166
+ #
167
+ # === Handling access sets and access lists
168
+ #
169
+ # If you need just IP access lists that you will handle in your own way
170
+ # you may want to use two classes:
171
+ #
172
+ # * IPAccess::Set to maintain access sets (containing input and output access lists),
173
+ # * IPAccess::List to maintain single access list.
174
+ #
175
+ # === Using socket classes
176
+ #
177
+ # If you want standard sockets to have access control enabled
178
+ # you may want to use:
179
+ #
180
+ # * IPAccess::Socket (or issue <tt>IPAccess.arm Socket</tt>)
181
+ # * IPAccess::TCPSocket (or issue <tt>IPAccess.arm TCPSocket</tt>)
182
+ # * IPAccess::UDPSocket (or issue <tt>IPAccess.arm UDPSocket</tt>)
183
+ # * IPAccess::SOCKSocket (or issue <tt>IPAccess.arm SOCKSocket</tt>)
184
+ # * IPAccess::TCPServer (or issue <tt>IPAccess.arm TCPServer</tt>)
185
+ #
186
+ # Before using any of them you must issue:
187
+ #
188
+ # * <tt>require 'ipaccess/socket'</tt>
189
+ #
190
+ # Using the IPAccess.arm causes standard socket class to be altered,
191
+ # while \IPAccess:: classes are just new variants of socket
192
+ # handling classes.
193
+ #
194
+ # ==== Using other supported network classes
195
+ #
196
+ # If you want some working objects to have access control enabled
197
+ # you may want to use:
198
+ #
199
+ # * IPAccess::Net::Telnet (or issue <tt>IPAccess.arm Net::Telnet</tt>)
200
+ # * IPAccess::Net::HTTP (or issue <tt>IPAccess.arm Net::HTTP</tt>)
201
+ # * IPAccess::Net::FTP (or issue <tt>IPAccess.arm Net::FTP</tt>)
202
+ # * IPAccess::Net::POP3 (or issue <tt>IPAccess.arm Net::POP3</tt>)
203
+ # * IPAccess::Net::IMAP (or issue <tt>IPAccess.arm Net::IMAP</tt>)
204
+ # * IPAccess::Net::SMTP (or issue <tt>IPAccess.arm Net::SMTP</tt>)
205
+ #
206
+ # ==== Using single network objects
207
+ #
208
+ # If you want to enable access control for single network
209
+ # object from the list shown above you may issue:
210
+ #
211
+ # require 'ipaccess/net/http'
212
+ # obj = Net::HTTP.new(host, port)
213
+ # IPAccess.arm obj
214
+ #
215
+ # or
216
+ #
217
+ # require 'ipaccess/socket'
218
+ # socket = IPAccess::TCPServer.new(31337)
219
+ # IPAccess.arm socket
220
+ #
221
+ # ..and so on.
222
+ #
223
+ # === Structures
224
+ #
225
+ # IP addresses used by the classes are internaly and interfacialy
226
+ # represented by NetAddr::CIDR[http://netaddr.rubyforge.org/classes/NetAddr/CIDR.html]
227
+ # objects (NetAddr::CIDRv4[http://netaddr.rubyforge.org/classes/NetAddr/CIDRv4.html] and
228
+ # NetAddr::CIDRv6[http://netaddr.rubyforge.org/classes/NetAddr/CIDRv6.html]). Due to
229
+ # performance reasons any access list internally is represented as a tree
230
+ # (slightly modified NetAddr::Tree[http://netaddr.rubyforge.org/classes/NetAddr/Tree.html])
231
+ # with special tags assigning rules to virtual lists.
232
+ #
233
+ # === Relations
234
+ #
235
+ # Here is a diagram which shows relations
236
+ # between the IPAccess::TCPSocket class
237
+ # and other classes from this module:
238
+ #
239
+ # link:images/ipaccess_relations.png
240
+
241
+ module IPAccess
242
+
243
+ # This method converts names to NetAddr::CIDR objects. It returns an array of CIDR objects.
244
+ #
245
+ # Allowed input are strings (DNS names or IP addresses optionally with masks), numbers (IP addresses representation),
246
+ # IPSocket objects, URI objects, IPAddr objects, Net::HTTP objects, IPAddrList objects, NetAddr::CIDR objects,
247
+ # NetAddr::Tree objects, IPAccess::List objects, symbols, objects that contain file descriptors bound to sockets
248
+ # (including OpenSSL sockets) and arrays of these.
249
+ #
250
+ # In case of resolving the IPv6 link-local addresses
251
+ # zone index is removed. In case of DNS names there may
252
+ # occur Resolv::ResolvError exception. If there is an
253
+ # object that cannot be converted the ArgumentError
254
+ # exception is raised.
255
+ #
256
+ # When an argument called +:include_origins+ is present then the method will attach
257
+ # original converted objects to results as the +:Origin+ tag of CIDR objects (<tt>tag[:Origin]</tt>).
258
+ # This rule applies only to single objects or objects inside of arrays or sets.
259
+ # Objects that are kind of NetAddr::CIDR, IPAccess::Set, NetAddr::Tree and arrays will
260
+ # never be set as originators.
261
+ #
262
+ # ==== Examples
263
+ #
264
+ # to_cidrs("127.0.0.1") # uses the IP address
265
+ # to_cidrs(2130706433) # uses numeric representation of 127.0.0.1
266
+ # to_cidrs(:private, "localhost") # uses special symbol and DNS hostname
267
+ # to_cidrs(:private, :localhost) # uses special symbols
268
+ # to_cidrs [:private, :auto] # other way to write the above
269
+ # to_cidrs "10.0.0.0/8" # uses masked IP address
270
+ # to_cidrs "10.0.0.0/255.0.0.0" # uses masked IP address
271
+ # to_cidrs IPSocket.new("www.pl", 80) # uses the socket
272
+ # to_cidrs IPAddr("10.0.0.1") # uses IPAddr object
273
+ # to_cidrs NetAddr::CIDR.create("10.0.0.1") # uses NetAddr object
274
+ # to_cidrs URI('http://www.pl/') # uses URI
275
+ # to_cidrs 'http://www.pl/' # uses the extracted host string
276
+ # to_cidrs 'somehost.xx' # uses the host string (fetches ALL addresses from DNS)
277
+ # to_cidrs 'somehost.xx/16' # uses the host string and a netmask
278
+ #
279
+ # ==== Special symbols
280
+ #
281
+ # When symbol is passed to this method it tries to find out if it has special meaning.
282
+ # That allows you to create access rules in an easy way. For most of them you may
283
+ # also specify IP protocol version using +ipv4_+ or +ipv6_+ prefix.
284
+ #
285
+ # Known symbols are:
286
+ #
287
+ # <b>+:all+</b> (+:any+, +:anyone+, +:world+, +:internet+, +:net+, +:everything+, +:everyone+, +:everybody+, +:anybody+)
288
+ #
289
+ # variants: +:ipv4_+ and +:ipv6_+
290
+ #
291
+ # Creates masked IP address that matches all networks:
292
+ # – 0.0.0.0/0
293
+ # – ::/0
294
+ #
295
+ # <b>+:broadcast+</b> (+:brd+)
296
+ #
297
+ # variants: +:ipv4_+ and +:ipv6_+
298
+ #
299
+ # Creates masked IP address that matches generic broadcast address:
300
+ # – 255.255.255.255/32
301
+ # – ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
302
+ #
303
+ # <b>+:local+</b> (+:localhost+, +:localdomain+, +:loopback+, +:lo+)
304
+ #
305
+ # variants: +:ipv4_+ and +:ipv6_+
306
+ #
307
+ # Creates masked IP addresses that match localhost:
308
+ # – 127.0.0.1/8
309
+ # – ::1/128
310
+ #
311
+ # <b>+:auto+</b> (+:automatic+, +:linklocal+)
312
+ #
313
+ # variants: +:ipv4_+ and +:ipv6_+
314
+ #
315
+ # Creates masked IP addresses that match automatically assigned address ranges:
316
+ # – 169.254.0.0/16
317
+ # – fe80::/10
318
+ #
319
+ # <b>+:private+</b> (+:intra+, +:intranet+, +:internal+)
320
+ #
321
+ # variants: +:ipv4_+ and +:ipv6_+
322
+ #
323
+ # Creates masked IP addresses that match private ranges:
324
+ # – 10.0.0.0/8
325
+ # – 172.16.0.0/12
326
+ # – 192.168.0.0/16
327
+ # – 2001:10::/28
328
+ # – 2001:db8::/32
329
+ # – fc00::/7
330
+ # – fdde:9e1a:dc85:7374::/64
331
+ #
332
+ # <b>+:multicast+</b> (+:multi+, +:multiemission+)
333
+ #
334
+ # variants: +:ipv4_+ and +:ipv6_+
335
+ #
336
+ # Creates masked IP addresses that match multicast addresses ranges:
337
+ # – 224.0.0.0/4
338
+ # – ff00::/8
339
+ # – ff02::1:ff00:0/104
340
+ #
341
+ # <b>+:reserved+</b> (+:example+)
342
+ #
343
+ # variants: +:ipv4_+
344
+ #
345
+ # Creates masked IP addresses that match reserved addresses ranges:
346
+ # – 192.0.2.0/24
347
+ # – 128.0.0.0/16
348
+ # – 191.255.0.0/16
349
+ # – 192.0.0.0/24
350
+ # – 198.18.0.0/15
351
+ # – 223.255.255.0/24
352
+ # – 240.0.0.0/4
353
+ #
354
+ # <b>+:strange+</b> (+:unusual+, +:nonpublic+, +:unpublic+)
355
+ #
356
+ # Creates masked IP addressess that match the following sets (both IPv4 and IPv6):
357
+ # – :local
358
+ # – :auto
359
+ # – :private
360
+ # – :reserved
361
+ # – :multicast
362
+
363
+ def self.to_cidrs(*addresses)
364
+ obj = addresses.flatten
365
+ include_origins = false
366
+ obj.delete_if { |x| include_origins = true if (x.is_a?(Symbol) && x == :include_origins) }
367
+
368
+ if obj.size == 1
369
+ obj = obj.first
370
+ else
371
+ ary = []
372
+ obj.each do |o|
373
+ ary += include_origins ? to_cidrs(o, :include_origins) : to_cidrs(o)
374
+ end
375
+ ary.flatten!
376
+ return ary
377
+ end
378
+
379
+ ori_obj = obj
380
+
381
+ # NetAddr::CIDR - immediate generation
382
+ if obj.is_a?(NetAddr::CIDR)
383
+ r = obj.dup
384
+ r.tag[:Originator] = ori_obj if include_origins
385
+ return [r]
386
+ end
387
+
388
+ # IPAccess::List - immediate generation
389
+ return obj.to_a if obj.is_a?(IPAccess::List)
390
+
391
+ # NetAddr::Tree - immediate generation
392
+ return obj.dump.map { |addr| addr[:CIDR] } if obj.is_a?(NetAddr::Tree)
393
+
394
+ # number or nil - immediate generation or exception
395
+ if (obj.is_a?(Numeric) || obj.nil?)
396
+ r = NetAddr::CIDR.create(obj)
397
+ r.tag[:Originator] = ori_obj if include_origins
398
+ return [r]
399
+ end
400
+
401
+ # object containing socket member (e.g. Net::HTTP) - fetch socket
402
+ if obj.respond_to?(:socket)
403
+ obj = obj.socket
404
+ elsif obj.respond_to?(:sock)
405
+ obj = obj.sock
406
+ elsif obj.respond_to?(:client_socket)
407
+ obj = obj.client_socket
408
+ elsif obj.instance_variable_defined?(:@socket)
409
+ obj = obj.instance_variable_get(:@socket)
410
+ elsif obj.instance_variable_defined?(:@client_socket)
411
+ obj = obj.instance_variable_get(:@client_socket)
412
+ elsif obj.instance_variable_defined?(:@sock)
413
+ obj = obj.instance_variable_get(:@sock)
414
+ end
415
+ obj = obj.io if (obj.respond_to?(:io) && obj.io.respond_to?(:getpeername))
416
+
417
+ # some file descriptor but not socket - fetch socket
418
+ obj = ::Socket.for_fd(obj.fileno) if (!obj.respond_to?(:getpeername) && obj.respond_to?(:fileno))
419
+
420
+ # Socket - immediate generation
421
+ if obj.respond_to?(:getpeername)
422
+ peeraddr = ::Socket.unpack_sockaddr_in(obj.getpeername).last.split('%').first
423
+ r = NetAddr::CIDR.create(peeraddr)
424
+ r.tag[:Originator] = ori_obj if include_origins
425
+ return [r]
426
+ end
427
+
428
+ # symbol - immediate generation
429
+ r_args = nil
430
+ if obj.is_a?(Symbol)
431
+ case obj
432
+ when :ipv4_all, :ipv4_any, :ipv4_anyone, :ipv4_world, :ipv4_internet, :ipv4_net, :ipv4_everything, :ipv4_everyone, :ipv4_everybody, :ipv4_anybody
433
+ obj = [ "0.0.0.0/0" ]
434
+ when :ipv6_all, :ipv6_any, :ipv6_anyone, :ipv6_world, :ipv6_internet, :ipv6_net, :ipv6_everything, :ipv6_everyone, :ipv6_everybody, :ipv6_anybody
435
+ obj = [ "0.0.0.0/0", "::/0" ]
436
+ when :ipv4_broadcast, :ipv4_brd
437
+ obj = [ "255.255.255.255/32" ]
438
+ when :ipv6_broadcast, :ipv6_brd
439
+ obj = [ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" ]
440
+ when :ipv4_local, :ipv4_localhost, :ipv4_loopback, :ipv4_lo
441
+ obj = [ "127.0.0.1/8" ]
442
+ when :ipv6_local, :ipv6_localhost, :ipv6_loopback, :ipv6_lo
443
+ obj = [ "::1/128" ]
444
+ when :ipv4_auto, :ipv4_automatic, :ipv4_linklocal
445
+ obj = [ "169.254.0.0/16" ]
446
+ when :ipv6_auto, :ipv6_automatic, :ipv6_linklocal
447
+ obj = [ "fe80::/10" ]
448
+ when :ipv4_private, :ipv4_intra, :ipv4_intranet, :ipv4_internal
449
+ obj = [ "10.0.0.0/8",
450
+ "172.16.0.0/12",
451
+ "192.168.0.0/16" ]
452
+ when :ipv6_private, :ipv6_intra, :ipv6_intranet, :ipv6_internal, :ipv6_ula, :ipv6_unique
453
+ obj = [ "2001:10::/28",
454
+ "2001:db8::/32",
455
+ "fc00::/7",
456
+ "fdde:9e1a:dc85:7374::/64" ]
457
+ when :ipv4_multicast, :ipv4_multi, :ipv4_multiemission
458
+ obj = [ "224.0.0.0/4" ]
459
+ when :ipv6_multicast, :ipv6_multi, :ipv6_multiemission
460
+ obj = [ "ff00::/8",
461
+ "ff02::1:ff00:0/104" ]
462
+ when :ipv4_example, :ipv4_reserved
463
+ obj = [ "192.0.2.0/24",
464
+ "128.0.0.0/16",
465
+ "191.255.0.0/16",
466
+ "192.0.0.0/24",
467
+ "198.18.0.0/15",
468
+ "223.255.255.0/24",
469
+ "240.0.0.0/4" ]
470
+ when :all, :any, :anyone, :world, :internet, :net, :everything, :everyone, :everybody, :anybody
471
+ r_args = [ :ipv4_all,
472
+ :ipv6_all ]
473
+ when :broadcast, :brd
474
+ r_args = [ :ipv4_broadcast,
475
+ :ipv6_broadcast ]
476
+ when :local, :localhost, :localdomain, :loopback, :lo
477
+ r_args = [ :ipv4_local,
478
+ :ipv6_local ]
479
+ when :auto, :automatic, :linklocal
480
+ r_args = [ :ipv4_auto,
481
+ :ipv6_auto ]
482
+ when :private, :intra, :intranet, :internal
483
+ r_args = [ :ipv4_private,
484
+ :ipv6_private ]
485
+ when :multicast, :multi, :multiemission
486
+ r_args = [ :ipv4_multicast,
487
+ :ipv6_multicast ]
488
+ when :reserved, :example
489
+ r_args = [ :ipv4_example ]
490
+ when :strange, :unusual, :nonpublic, :unpublic
491
+ r_args = [ :local,
492
+ :auto,
493
+ :private,
494
+ :reserved,
495
+ :multicast ]
496
+ else
497
+ raise ArgumentError, "provided symbol is unknown: #{obj.to_s}"
498
+ end
499
+
500
+ unless r_args.nil?
501
+ r_args.push :include_origins if include_origins
502
+ return to_cidrs(*r_args)
503
+ end
504
+
505
+ # strange types here
506
+ if obj.is_a?(Array)
507
+ return obj.map do |addr|
508
+ r = NetAddr::CIDR.create(addr)
509
+ r.tag[:Originator] = addr if include_origins
510
+ r
511
+ end
512
+ end
513
+ end
514
+
515
+ # URI or something that responds to host method - fetch string
516
+ obj = obj.host if obj.respond_to?(:host)
517
+
518
+ # objects of external classes
519
+ case obj.class.name.to_sym
520
+ when :IPAddr # IPAddr - fetch IP/mask string
521
+ obj = obj.native.inspect.split[1].chomp('>')[5..-1]
522
+ when :IPAddrList # IPAddrList - pass array to parse
523
+ return include_origins ? to_cidrs(obj.to_a, :include_origins) : to_cidrs(obj.to_a)
524
+ end
525
+
526
+ # string or similar - immediate generation
527
+ if obj.respond_to?(:to_s)
528
+ hostmask = ""
529
+ obj = obj.to_s
530
+ # URI
531
+ if obj =~ /^[^:]+:\/\/(.*)/
532
+ obj = $1.split('/').first
533
+ # IP in URI
534
+ if obj =~ /^\[([^\]]+)\]/
535
+ obj = $1
536
+ else
537
+ obj = obj.split(':').first
538
+ end
539
+ # host(s) and a mask
540
+ elsif obj =~ /^([^\/]+)(\/((\d{1,2}$)|(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$)))/
541
+ obj = $1
542
+ hostmask = $2
543
+ end
544
+ begin
545
+ ipa = obj.split('%').first.to_s
546
+ r = NetAddr::CIDR.create(ipa + hostmask)
547
+ rescue NetAddr::ValidationError
548
+ begin
549
+ addresses = Resolv::getaddresses(obj)
550
+ rescue NoMethodError # unhandled error
551
+ raise Resolv::ResolvError, "not connected or network error"
552
+ end
553
+ addresses.map! do |addr|
554
+ begin
555
+ r = NetAddr::CIDR.create(addr.split('%').first + hostmask)
556
+ r.tag[:Originator] = ori_obj
557
+ r
558
+ rescue ArgumentError
559
+ nil
560
+ end
561
+ end
562
+ addresses.flatten!
563
+ addresses.compact!
564
+ return addresses
565
+ end
566
+ r.tag[:Originator] = ori_obj
567
+ return [r]
568
+ end
569
+
570
+ # should never happend
571
+ r = obj.is_a?(NetAddr::CIDR) ? obj.dup : NetAddr::CIDR.create(obj.to_s)
572
+ r.tag[:Originator] = ori_obj
573
+ return [r]
574
+ end
575
+
576
+ # This method calls IPAccess.to_cidrs
577
+ # and returns first obtained entry containing
578
+ # single IP address with mask (NetAddr::CIDR).
579
+
580
+ def self.to_cidr(*addresses)
581
+ r = self.to_cidrs(*addresses)
582
+ return r.respond_to?(:first) ? first : r
583
+ end
584
+
585
+ end