ipaccess 0.0.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/.gemtest +0 -0
- data/.rspec +1 -0
- data/.yardopts +12 -0
- data/ChangeLog +1495 -0
- data/{docs/LGPL-LICENSE → LGPL-LICENSE} +0 -0
- data/Manifest.txt +76 -0
- data/README.md +96 -0
- data/Rakefile +65 -42
- data/docs/COPYING +41 -45
- data/docs/FAQ +12 -0
- data/docs/HISTORY +17 -0
- data/docs/LEGAL +1 -1
- data/docs/LGPL +166 -0
- data/docs/TODO +150 -7
- data/docs/images/ipaccess.png +0 -0
- data/docs/images/ipaccess_ac_for_args.png +0 -0
- data/docs/images/ipaccess_ac_for_socket.png +0 -0
- data/docs/images/ipaccess_logo.png +0 -0
- data/docs/images/ipaccess_relations.png +0 -0
- data/docs/images/ipaccess_setup_origin.png +0 -0
- data/docs/images/ipaccess_setup_origin_tab.png +0 -0
- data/docs/images/ipaccess_view.png +0 -0
- data/docs/rdoc.css +22 -0
- data/examples/ftp.rb +62 -0
- data/examples/http.rb +81 -0
- data/examples/imap.rb +37 -0
- data/examples/pop.rb +31 -0
- data/examples/smtp.rb +26 -0
- data/examples/tcp_server.rb +32 -0
- data/examples/tcp_socket.rb +7 -3
- data/examples/telnet.rb +32 -0
- data/examples/text_message.rb +45 -0
- data/lib/ipaccess.rb +557 -7
- data/lib/ipaccess/arm_sockets.rb +7 -4
- data/lib/ipaccess/ghost_doc/ghost_doc.rb +23 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_acl.rb +54 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_ftp.rb +213 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_http.rb +272 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_smtp.rb +186 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_telnet.rb +227 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_patched_usage.rb +64 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_sockets.rb +571 -0
- data/lib/ipaccess/ip_access_check.rb +508 -0
- data/lib/ipaccess/ip_access_errors.rb +186 -40
- data/lib/ipaccess/ip_access_list.rb +955 -1122
- data/lib/ipaccess/ip_access_set.rb +212 -0
- data/lib/ipaccess/net/ftp.rb +39 -0
- data/lib/ipaccess/net/http.rb +39 -0
- data/lib/ipaccess/net/https.rb +30 -0
- data/lib/ipaccess/net/imap.rb +39 -0
- data/lib/ipaccess/net/pop.rb +46 -0
- data/lib/ipaccess/net/smtp.rb +39 -0
- data/lib/ipaccess/net/telnet.rb +38 -0
- data/lib/ipaccess/patches/generic.rb +807 -0
- data/lib/ipaccess/patches/net_ftp.rb +165 -0
- data/lib/ipaccess/patches/net_http.rb +175 -0
- data/lib/ipaccess/patches/net_https.rb +29 -0
- data/lib/ipaccess/patches/net_imap.rb +117 -0
- data/lib/ipaccess/patches/net_pop.rb +171 -0
- data/lib/ipaccess/patches/net_smtp.rb +130 -0
- data/lib/ipaccess/patches/net_telnet.rb +103 -0
- data/lib/ipaccess/{netaddr_patch.rb → patches/netaddr.rb} +20 -11
- data/lib/ipaccess/patches/sockets.rb +586 -0
- data/lib/ipaccess/socket.rb +52 -1
- data/lib/ipaccess/sockets.rb +4 -30
- data/spec/ip_access_list_spec.rb +33 -21
- data/spec/spec.opts +2 -2
- metadata +289 -63
- metadata.gz.sig +0 -0
- data/docs/DOWNLOAD +0 -17
- data/docs/README +0 -95
- data/docs/WELCOME +0 -8
- data/lib/ipaccess/ghost_doc.rb +0 -206
- data/lib/ipaccess/ghost_doc_acl.rb +0 -31
- data/lib/ipaccess/ip_access.rb +0 -456
- data/lib/ipaccess/ip_access_patches.rb +0 -431
- data/spec/core_spec.rb +0 -5
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Author:: Paweł Wilk (mailto:pw@gnu.org)
|
|
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
|
+
#
|
|
7
|
+
# Modules contained in this file are meant for
|
|
8
|
+
# patching Ruby socket classes in order to add
|
|
9
|
+
# IP access control to them. It is also used
|
|
10
|
+
# to create variants of socket handling classes
|
|
11
|
+
# with IP access control.
|
|
12
|
+
#
|
|
13
|
+
#--
|
|
14
|
+
#
|
|
15
|
+
# Copyright (C) 2009 by Paweł Wilk. All Rights Reserved.
|
|
16
|
+
#
|
|
17
|
+
# This program is free software; you can redistribute it and/or modify
|
|
18
|
+
# it under the terms of either: 1) the GNU Lesser General Public License
|
|
19
|
+
# as published by the Free Software Foundation; either version 3 of the
|
|
20
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
|
21
|
+
#
|
|
22
|
+
# See the file COPYING for complete licensing information.
|
|
23
|
+
#
|
|
24
|
+
#++
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
require 'socket'
|
|
28
|
+
require 'ipaccess/ip_access_errors'
|
|
29
|
+
require 'ipaccess/patches/generic'
|
|
30
|
+
|
|
31
|
+
# :stopdoc:
|
|
32
|
+
|
|
33
|
+
# This modules contain patches for Ruby socket
|
|
34
|
+
# classes in order to enable IP access control
|
|
35
|
+
# for them.
|
|
36
|
+
#
|
|
37
|
+
# This module patches socket handling classes
|
|
38
|
+
# to use IP access control. Each patched socket
|
|
39
|
+
# class has acl member, which is an IPAccess::Set object.
|
|
40
|
+
|
|
41
|
+
module IPAccess::Patches
|
|
42
|
+
|
|
43
|
+
###################################################################
|
|
44
|
+
# Socket class with IP access control.
|
|
45
|
+
# It uses input and output access lists.
|
|
46
|
+
# Default access list for management operations is output.
|
|
47
|
+
|
|
48
|
+
module Socket
|
|
49
|
+
|
|
50
|
+
include IPAccess::Patches::ACL
|
|
51
|
+
|
|
52
|
+
def self.included(base)
|
|
53
|
+
|
|
54
|
+
marker = (base.name =~ /IPAccess/) ? base.superclass : base
|
|
55
|
+
return if marker.instance_variable_defined?(:@uses_ipaccess)
|
|
56
|
+
base.instance_variable_set(:@uses_ipaccess, true)
|
|
57
|
+
|
|
58
|
+
base.class_eval do
|
|
59
|
+
|
|
60
|
+
orig_initialize = self.instance_method :initialize
|
|
61
|
+
orig_accept = self.instance_method :accept
|
|
62
|
+
orig_accept_nonblock = self.instance_method :accept_nonblock
|
|
63
|
+
orig_connect = self.instance_method :connect
|
|
64
|
+
orig_recvfrom = self.instance_method :recvfrom
|
|
65
|
+
orig_recvfrom_nonblock = self.instance_method :recvfrom_nonblock
|
|
66
|
+
orig_sysaccept = self.instance_method :sysaccept
|
|
67
|
+
|
|
68
|
+
define_method :__ipacall__initialize do |block, *args|
|
|
69
|
+
@opened_on_deny = false
|
|
70
|
+
args.delete_if { |x| @opened_on_deny = true if (x.is_a?(Symbol) && x == :opened_on_deny) }
|
|
71
|
+
args.pop if args.last.nil?
|
|
72
|
+
self.acl = valid_acl?(args.last) ? args.pop : :global
|
|
73
|
+
@useables = IPAccess::ObjectsReferences
|
|
74
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
75
|
+
return self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# block passing wrapper for Ruby 1.8
|
|
79
|
+
def initialize(*args, &block)
|
|
80
|
+
__ipacall__initialize(block, *args)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# accept on steroids.
|
|
84
|
+
define_method :accept do |*args|
|
|
85
|
+
ret = orig_accept.bind(self).call(*args)
|
|
86
|
+
real_acl.input.check_socket(ret.first, ret.first) { try_terminate_subsocket(ret.first) }
|
|
87
|
+
return ret
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# accept_nonblock on steroids.
|
|
91
|
+
define_method :accept_nonblock do |*args|
|
|
92
|
+
ret = orig_accept_nonblock.bind(self).call(*args)
|
|
93
|
+
real_acl.input.check_socket(ret.first, ret.first) { try_terminate_subsocket(ret.first) }
|
|
94
|
+
return ret
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# sysaccept on steroids.
|
|
98
|
+
define_method :sysaccept do |*args|
|
|
99
|
+
ret = orig_accept.bind(self).call(*args)
|
|
100
|
+
real_acl.input.check_sockaddr(ret.last, ret.last) { try_terminate_subsocket(::Socket.for_fd(ret.first)) }
|
|
101
|
+
return ret
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# connect on steroids.
|
|
105
|
+
define_method :connect do |*args|
|
|
106
|
+
unless @opened_on_deny
|
|
107
|
+
real_acl.output.check_sockaddr(args.first)
|
|
108
|
+
return orig_connect.bind(self).call(*args)
|
|
109
|
+
else
|
|
110
|
+
ret = orig_connect.bind(self).call(*args)
|
|
111
|
+
real_acl.output.check_socket(ret, self)
|
|
112
|
+
end
|
|
113
|
+
return ret
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# this hook will be called each time @acl is reassigned
|
|
117
|
+
define_method :acl_recheck do
|
|
118
|
+
return nil if self.closed?
|
|
119
|
+
real_acl.output.check_socket(self, self) { try_terminate }
|
|
120
|
+
return nil
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# recvfrom on steroids.
|
|
124
|
+
define_method :recvfrom do |*args|
|
|
125
|
+
ret = orig_recvfrom.bind(self).call(*args)
|
|
126
|
+
peer_ip = ret[1][3]
|
|
127
|
+
family = ret[1][0]
|
|
128
|
+
if (family == "AF_INET" || family == "AF_INET6")
|
|
129
|
+
real_acl.input.check_ipstring(peer_ip, self) { try_terminate }
|
|
130
|
+
end
|
|
131
|
+
return ret
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# recvfrom_nonblock on steroids.
|
|
135
|
+
define_method :recvfrom_nonblock do |*args|
|
|
136
|
+
ret = orig_recvfrom_nonblock.bind(self).call(*args)
|
|
137
|
+
peer_ip = ret[1][3]
|
|
138
|
+
family = ret[1][0]
|
|
139
|
+
if (family == "AF_INET" || family == "AF_INET6")
|
|
140
|
+
real_acl.input.check_ipstring(peer_ip, self) { try_terminate }
|
|
141
|
+
end
|
|
142
|
+
return ret
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# This method returns default access list indicator
|
|
146
|
+
# used by protected object; in this case it's +:output+.
|
|
147
|
+
define_method :default_list do
|
|
148
|
+
:output
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
define_method :useables do
|
|
152
|
+
@useables
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end # base.class_eval
|
|
156
|
+
|
|
157
|
+
end # self.included
|
|
158
|
+
|
|
159
|
+
end # module Socket
|
|
160
|
+
|
|
161
|
+
###################################################################
|
|
162
|
+
# UDPSocket class with IP access control.
|
|
163
|
+
# It uses input and output access lists.
|
|
164
|
+
# Default access list for management operations is input.
|
|
165
|
+
|
|
166
|
+
module UDPSocket
|
|
167
|
+
|
|
168
|
+
include IPAccess::Patches::ACL
|
|
169
|
+
|
|
170
|
+
def self.included(base)
|
|
171
|
+
|
|
172
|
+
marker = (base.name =~ /IPAccess/) ? base.superclass : base
|
|
173
|
+
return if marker.instance_variable_defined?(:@uses_ipaccess)
|
|
174
|
+
base.instance_variable_set(:@uses_ipaccess, true)
|
|
175
|
+
|
|
176
|
+
base.class_eval do
|
|
177
|
+
|
|
178
|
+
orig_initialize = self.instance_method :initialize
|
|
179
|
+
orig_connect = self.instance_method :connect
|
|
180
|
+
orig_send = self.instance_method :send
|
|
181
|
+
orig_recvfrom = self.instance_method :recvfrom
|
|
182
|
+
orig_recvfrom_nonblock = self.instance_method :recvfrom_nonblock
|
|
183
|
+
|
|
184
|
+
define_method :__ipacall__initialize do |block, *args|
|
|
185
|
+
self.acl = valid_acl?(args.last) ? args.pop : :global
|
|
186
|
+
@opened_on_deny = true
|
|
187
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
188
|
+
return self
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# block passing wrapper for Ruby 1.8
|
|
192
|
+
def initialize(*args, &block)
|
|
193
|
+
__ipacall__initialize(block, *args)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# connect on steroids.
|
|
197
|
+
define_method :connect do |*args|
|
|
198
|
+
peer_ip = self.class.getaddress(args.shift)
|
|
199
|
+
real_acl.output.check_sockaddr(peer_ip, self)
|
|
200
|
+
return orig_connect.bind(self).call(peer_ip, *args)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# send on steroids.
|
|
204
|
+
define_method :send do |*args|
|
|
205
|
+
hostname = args[2]
|
|
206
|
+
return orig_send.bind(self).call(*args) if hostname.nil?
|
|
207
|
+
peer_ip = self.class.getaddress(hostname)
|
|
208
|
+
real_acl.output.check_sockaddr(peer_ip, self)
|
|
209
|
+
args[2] = peer_ip
|
|
210
|
+
return orig_send.bind(self).call(*args)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# recvfrom on steroids.
|
|
214
|
+
define_method :recvfrom do |*args|
|
|
215
|
+
ret = orig_recvfrom.bind(self).call(*args)
|
|
216
|
+
peer_ip = ret[1][3]
|
|
217
|
+
family = ret[1][0]
|
|
218
|
+
if (family == "AF_INET" || family == "AF_INET6")
|
|
219
|
+
real_acl.input.check_ipstring(peer_ip, self)
|
|
220
|
+
end
|
|
221
|
+
return ret
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# recvfrom_nonblock on steroids.
|
|
225
|
+
define_method :recvfrom_nonblock do |*args|
|
|
226
|
+
ret = orig_recvfrom_nonblock.bind(self).call(*args)
|
|
227
|
+
peer_ip = ret[1][3]
|
|
228
|
+
family = ret[1][0]
|
|
229
|
+
if (family == "AF_INET" || family == "AF_INET6")
|
|
230
|
+
real_acl.input.check_ipstring(peer_ip, self)
|
|
231
|
+
end
|
|
232
|
+
return ret
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# This method returns default access list indicator
|
|
236
|
+
# used by protected object; in this case it's +:input+.
|
|
237
|
+
define_method :default_list do
|
|
238
|
+
:intput
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# this kind of socket is not connection-oriented.
|
|
242
|
+
define_method :connection_close do
|
|
243
|
+
return nil
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# this hook will be called each time @acl is reassigned
|
|
247
|
+
define_method :acl_recheck do
|
|
248
|
+
return nil if self.closed?
|
|
249
|
+
real_acl.output.check_socket(self, self) { try_terminate }
|
|
250
|
+
return nil
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
end # base.class_eval
|
|
254
|
+
|
|
255
|
+
end # self.included
|
|
256
|
+
|
|
257
|
+
end # module UDPSocket
|
|
258
|
+
|
|
259
|
+
###################################################################
|
|
260
|
+
# SOCKSSocket class with IP access control.
|
|
261
|
+
# It uses output access lists.
|
|
262
|
+
|
|
263
|
+
module SOCKSocket
|
|
264
|
+
|
|
265
|
+
include IPAccess::Patches::ACL
|
|
266
|
+
|
|
267
|
+
def self.included(base)
|
|
268
|
+
|
|
269
|
+
marker = (base.name =~ /IPAccess/) ? base.superclass : base
|
|
270
|
+
return if marker.instance_variable_defined?(:@uses_ipaccess)
|
|
271
|
+
base.instance_variable_set(:@uses_ipaccess, true)
|
|
272
|
+
|
|
273
|
+
base.class_eval do
|
|
274
|
+
|
|
275
|
+
orig_initialize = self.instance_method :initialize
|
|
276
|
+
|
|
277
|
+
# initialize on steroids.
|
|
278
|
+
define_method :__pacall__initialize do |block, *args|
|
|
279
|
+
@opened_on_deny = false
|
|
280
|
+
args.delete_if { |x| @opened_on_deny = true if (x.is_a?(Symbol) && x == :opened_on_deny) }
|
|
281
|
+
args.pop if args.last.nil?
|
|
282
|
+
self.acl = valid_acl?(args.last) ? args.pop : :global
|
|
283
|
+
args[0] = self.class.getaddress(args[0])
|
|
284
|
+
unless @opened_on_deny
|
|
285
|
+
real_acl.output.check_ipstring args[0]
|
|
286
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
287
|
+
else
|
|
288
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
289
|
+
real_acl.output.check_socket(self)
|
|
290
|
+
end
|
|
291
|
+
@useables = IPAccess::ObjectsReferences
|
|
292
|
+
return self
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# block passing wrapper for Ruby 1.8
|
|
296
|
+
def initialize(*args, &block)
|
|
297
|
+
__ipacall__initialize(block, *args)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# this hook will be called each time @acl is reassigned
|
|
301
|
+
define_method :acl_recheck do
|
|
302
|
+
return nil if self.closed?
|
|
303
|
+
real_acl.output.check_socket(self, self) { try_terminate }
|
|
304
|
+
return nil
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# This method returns default access list indicator
|
|
308
|
+
# used by protected object; in this case it's +:output+.
|
|
309
|
+
define_method :default_list do
|
|
310
|
+
:output
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
end # base.class_eval
|
|
314
|
+
|
|
315
|
+
end # self.included
|
|
316
|
+
|
|
317
|
+
end # module SOCKSSocket
|
|
318
|
+
|
|
319
|
+
###################################################################
|
|
320
|
+
# TCPSocket class with IP access control.
|
|
321
|
+
# It uses output access lists.
|
|
322
|
+
|
|
323
|
+
module TCPSocket
|
|
324
|
+
|
|
325
|
+
include IPAccess::Patches::ACL
|
|
326
|
+
|
|
327
|
+
def self.included(base)
|
|
328
|
+
|
|
329
|
+
marker = (base.name =~ /IPAccess/) ? base.superclass : base
|
|
330
|
+
return if marker.instance_variable_defined?(:@uses_ipaccess)
|
|
331
|
+
base.instance_variable_set(:@uses_ipaccess, true)
|
|
332
|
+
|
|
333
|
+
base.class_eval do
|
|
334
|
+
|
|
335
|
+
orig_initialize = self.instance_method :initialize
|
|
336
|
+
|
|
337
|
+
# initialize on steroids.
|
|
338
|
+
define_method :__ipacall__initialize do |block, *args|
|
|
339
|
+
@opened_on_deny = false
|
|
340
|
+
args.delete_if { |x| @opened_on_deny = true if (x.is_a?(Symbol) && x == :opened_on_deny) }
|
|
341
|
+
args.pop if args.last.nil?
|
|
342
|
+
self.acl = valid_acl?(args.last) ? args.pop : :global
|
|
343
|
+
args[0] = self.class.getaddress(args[0])
|
|
344
|
+
@useables = IPAccess::ObjectsReferences
|
|
345
|
+
unless @opened_on_deny
|
|
346
|
+
real_acl.output.check_ipstring(args[0], :none)
|
|
347
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
348
|
+
else
|
|
349
|
+
orig_initialize.bind(self).call(*args, &block)
|
|
350
|
+
real_acl.output.check_socket(self)
|
|
351
|
+
end
|
|
352
|
+
return self
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# block passing wrapper for Ruby 1.8
|
|
356
|
+
def initialize(*args, &block)
|
|
357
|
+
__ipacall__initialize(block, *args)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# this hook will be called each time @acl is reassigned
|
|
361
|
+
define_method :acl_recheck do
|
|
362
|
+
return nil if self.closed?
|
|
363
|
+
real_acl.output.check_socket(self, self) { try_terminate }
|
|
364
|
+
return nil
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# This method returns default access list indicator
|
|
368
|
+
# used by protected object; in this case it's +:output+.
|
|
369
|
+
define_method :default_list do
|
|
370
|
+
:output
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
end # base.class_eval
|
|
374
|
+
|
|
375
|
+
end # self.included
|
|
376
|
+
|
|
377
|
+
end # module TCPSocket
|
|
378
|
+
|
|
379
|
+
###################################################################
|
|
380
|
+
# TCPServer class with IP access control.
|
|
381
|
+
# It uses input access lists.
|
|
382
|
+
|
|
383
|
+
module TCPServer
|
|
384
|
+
|
|
385
|
+
include IPAccess::Patches::ACL
|
|
386
|
+
|
|
387
|
+
def self.included(base)
|
|
388
|
+
|
|
389
|
+
marker = (base.name =~ /IPAccess/) ? base.superclass : base
|
|
390
|
+
return if marker.instance_variable_defined?(:@uses_ipaccess)
|
|
391
|
+
base.instance_variable_set(:@uses_ipaccess, true)
|
|
392
|
+
|
|
393
|
+
base.class_eval do
|
|
394
|
+
|
|
395
|
+
orig_initialize = self.instance_method :initialize
|
|
396
|
+
orig_accept = self.instance_method :accept
|
|
397
|
+
orig_accept_nonblock = self.instance_method :accept_nonblock
|
|
398
|
+
orig_sysaccept = self.instance_method :sysaccept
|
|
399
|
+
|
|
400
|
+
# initialize on steroids.
|
|
401
|
+
define_method :__ipacall__initialize do |block, *args|
|
|
402
|
+
@opened_on_deny = false
|
|
403
|
+
args.delete_if { |x| @opened_on_deny = true if (x.is_a?(Symbol) && x == :opened_on_deny) }
|
|
404
|
+
args.pop if args.last.nil?
|
|
405
|
+
self.acl = valid_acl?(args.last) ? args.pop : :global
|
|
406
|
+
return orig_initialize.bind(self).call(*args, &block)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# block passing wrapper for Ruby 1.8
|
|
410
|
+
def initialize(*args, &block)
|
|
411
|
+
__ipacall__initialize(block, *args)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# accept on steroids.
|
|
415
|
+
define_method :accept do |*args|
|
|
416
|
+
r = orig_accept.bind(self).call(*args)
|
|
417
|
+
real_acl.input.check_socket(r, r) { try_terminate_subsocket(r) }
|
|
418
|
+
return r
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# accept_nonblock on steroids.
|
|
422
|
+
define_method :accept_nonblock do |*args|
|
|
423
|
+
r = orig_accept_nonblock.bind(self).call(*args)
|
|
424
|
+
real_acl.input.check_socket(r, r) { try_terminate_subsocket(r) }
|
|
425
|
+
return r
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# sysaccept on steroids.
|
|
429
|
+
define_method :sysaccept do |*args|
|
|
430
|
+
r = orig_sysaccept.bind(self).call(*args)
|
|
431
|
+
real_acl.input.check_fd(r, r) { try_terminate_subsocket(::Socket.for_fd(r)) }
|
|
432
|
+
return r
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# this hook will be called each time @acl is reassigned
|
|
436
|
+
define_method :acl_recheck do
|
|
437
|
+
return nil if self.closed?
|
|
438
|
+
real_acl.output.check_socket(self, self) { try_terminate }
|
|
439
|
+
return nil
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# This method returns default access list indicator
|
|
443
|
+
# used by protected object; in this case it's +:input+.
|
|
444
|
+
define_method :default_list do
|
|
445
|
+
:input
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
end # base.class_eval
|
|
449
|
+
|
|
450
|
+
end # self.included
|
|
451
|
+
|
|
452
|
+
end # module TCPServer
|
|
453
|
+
|
|
454
|
+
###################################################################
|
|
455
|
+
# Helper methods for easy checking and arming sockets.
|
|
456
|
+
|
|
457
|
+
module ACL
|
|
458
|
+
|
|
459
|
+
# This method helps to obtain real socket object.
|
|
460
|
+
# It check whether object is SOCKSSocket or SSLSocket
|
|
461
|
+
# and calls the right method if needed.
|
|
462
|
+
#
|
|
463
|
+
# It returns socket object or +nil+ if something went wrong.
|
|
464
|
+
|
|
465
|
+
def real_socket(obj)
|
|
466
|
+
obj = obj.io if (obj.respond_to?(:io) && obj.io.respond_to?(:getpeername))
|
|
467
|
+
case obj.class.name.to_sym
|
|
468
|
+
when :TCPSocket, :UDPSocket, :TCPServer, :SOCKSSocket, :Socket
|
|
469
|
+
return obj
|
|
470
|
+
else
|
|
471
|
+
return nil
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
private :real_socket
|
|
475
|
+
|
|
476
|
+
# This method is used to safely
|
|
477
|
+
# re-raise an eventual exception
|
|
478
|
+
# and add current object's reference
|
|
479
|
+
# to its useables field and to useables
|
|
480
|
+
# of a socket object.
|
|
481
|
+
#
|
|
482
|
+
# The passed block should contain
|
|
483
|
+
# code that should be wrapped and
|
|
484
|
+
# the given object should point to
|
|
485
|
+
# useable.
|
|
486
|
+
#
|
|
487
|
+
# The block should return new socket
|
|
488
|
+
# object.
|
|
489
|
+
#
|
|
490
|
+
# This method is present only when
|
|
491
|
+
# patching/arming engine had been loaded.
|
|
492
|
+
|
|
493
|
+
def take_care
|
|
494
|
+
begin
|
|
495
|
+
ipa_socket = nil
|
|
496
|
+
ipa_socket = yield
|
|
497
|
+
rescue IPAccessDenied => e
|
|
498
|
+
e.originator = self
|
|
499
|
+
try_terminate
|
|
500
|
+
raise
|
|
501
|
+
ensure
|
|
502
|
+
ipa_socket.useables.add(useable) if ipa_socket.respond_to?(:useables)
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
private :take_care
|
|
506
|
+
|
|
507
|
+
# This method tries to arm socket object.
|
|
508
|
+
# If a wanted access set and an object's access
|
|
509
|
+
# set is no different then acl_recheck is called
|
|
510
|
+
# by force. It sets armed socket's +opened_on_deny+
|
|
511
|
+
# flag to +true+.
|
|
512
|
+
|
|
513
|
+
def try_arm_socket(obj, initial_acl=nil)
|
|
514
|
+
late_sock = real_socket(obj)
|
|
515
|
+
unless late_sock.nil?
|
|
516
|
+
take_care do
|
|
517
|
+
initial_acl = real_acl if initial_acl.nil?
|
|
518
|
+
IPAccess.arm(late_sock, acl, :opened_on_deny) unless late_sock.respond_to?(:acl)
|
|
519
|
+
late_sock.acl = initial_acl if late_sock.acl != initial_acl
|
|
520
|
+
late_sock
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
return obj
|
|
524
|
+
end
|
|
525
|
+
private :try_arm_socket
|
|
526
|
+
|
|
527
|
+
# This method tries to arm socket object and then
|
|
528
|
+
# tries to set up correct ACL for it. If the ACL
|
|
529
|
+
# had changed then it assumes that underlying routines
|
|
530
|
+
# took care about rechecking socket's IP against
|
|
531
|
+
# correct access list (input or output). By taking
|
|
532
|
+
# care we mean automatic triggering of acl_recheck
|
|
533
|
+
# when object's acl= method had been called.
|
|
534
|
+
# The acl_recheck will be called without any conditions.
|
|
535
|
+
# This method sets armed socket's +opened_on_deny+
|
|
536
|
+
# flag to +true+. When exception will happen during check
|
|
537
|
+
# it will fill up +originator+ attribute of IPAccessDenied
|
|
538
|
+
# kind of object. The originator will be set to
|
|
539
|
+
# +self+.
|
|
540
|
+
#
|
|
541
|
+
# This method returns the given object.
|
|
542
|
+
|
|
543
|
+
def try_arm_and_check_socket(obj, initial_acl=nil)
|
|
544
|
+
late_sock = real_socket(obj)
|
|
545
|
+
unless late_sock.nil?
|
|
546
|
+
take_care do
|
|
547
|
+
initial_acl = real_acl if initial_acl.nil?
|
|
548
|
+
IPAccess.arm(late_sock, acl, :opened_on_deny) unless late_sock.respond_to?(:acl)
|
|
549
|
+
if late_sock.acl != initial_acl
|
|
550
|
+
late_sock.acl = initial_acl
|
|
551
|
+
else
|
|
552
|
+
late_sock.acl_recheck
|
|
553
|
+
end
|
|
554
|
+
late_sock
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
return obj
|
|
558
|
+
end
|
|
559
|
+
private :try_arm_and_check_socket
|
|
560
|
+
|
|
561
|
+
def try_check_out_socket_acl(obj, used_acl)
|
|
562
|
+
take_care do
|
|
563
|
+
late_sock = real_socket(obj)
|
|
564
|
+
used_acl.output.check_socket(late_sock, self) { try_terminate } unless late_sock.nil?
|
|
565
|
+
late_sock
|
|
566
|
+
end
|
|
567
|
+
return obj
|
|
568
|
+
end
|
|
569
|
+
private :try_check_out_socket_acl
|
|
570
|
+
|
|
571
|
+
def try_check_in_socket_acl(obj, used_acl)
|
|
572
|
+
take_care do
|
|
573
|
+
late_sock = real_socket(obj)
|
|
574
|
+
used_acl.input.check_socket(late_sock, self) { try_terminate } unless late_sock.nil?
|
|
575
|
+
late_sock
|
|
576
|
+
end
|
|
577
|
+
return obj
|
|
578
|
+
end
|
|
579
|
+
private :try_check_in_socket_acl
|
|
580
|
+
|
|
581
|
+
end # module ACL
|
|
582
|
+
|
|
583
|
+
end # module IPAccess::Patches
|
|
584
|
+
|
|
585
|
+
# :startdoc:
|
|
586
|
+
|