arachni-reactor 0.1.0.beta5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +3 -3
- data/lib/arachni/reactor.rb +54 -34
- data/lib/arachni/reactor/connection.rb +90 -57
- data/lib/arachni/reactor/connection/error.rb +1 -1
- data/lib/arachni/reactor/connection/tls.rb +75 -21
- data/lib/arachni/reactor/version.rb +1 -1
- data/spec/arachni/reactor/connection/tls_spec.rb +30 -17
- data/spec/arachni/reactor/connection_spec.rb +19 -3
- data/spec/support/helpers/utilities.rb +14 -0
- data/spec/support/lib/servers.rb +2 -2
- data/spec/support/servers/echo.rb +3 -3
- data/spec/support/servers/echo_tls.rb +2 -2
- data/spec/support/servers/echo_unix.rb +2 -2
- data/spec/support/servers/echo_unix_tls.rb +2 -2
- data/spec/support/shared/connection.rb +58 -140
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8f90922fd68d3df405cf0aa7537924db3f3c118
|
4
|
+
data.tar.gz: 5bd9e5042618276a83909b43879651d60f8e0538
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff7c11326a30b31030b95ac93cd7ee3e5c37ab6f7097bb6f9a4829354b13bc683d484e8243bb9d14e88b8248bd69481a9a43ac01d475a708e61199595ec4632b
|
7
|
+
data.tar.gz: f1563d0bafe856937029267327fa8c049b05a56122e8fa8dd5c1fd0b45bb7e9784f250eb8dfd4d6438d8b866cd402de4340b9382d5a304c204fd54552c4a389c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# ChangeLog
|
2
2
|
|
3
|
+
## Version 0.1.0
|
4
|
+
|
5
|
+
- Cleaned up connection handling structure for JRuby support.
|
6
|
+
- `Connection`
|
7
|
+
- `PeerInfo` now not included by default and only available for servers.
|
8
|
+
|
3
9
|
## Version 0.1.0.beta5 _(September 4, 2014)_
|
4
10
|
|
5
11
|
- `Tasks::OneOff#call`: Ensure that the task is marked as done even if an
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<table>
|
4
4
|
<tr>
|
5
5
|
<th>Version</th>
|
6
|
-
<td>0.1.0
|
6
|
+
<td>0.1.0</td>
|
7
7
|
</tr>
|
8
8
|
<tr>
|
9
9
|
<th>Github page</th>
|
@@ -51,7 +51,7 @@ on network connections -- and less so on generic tasks.
|
|
51
51
|
- Rubies:
|
52
52
|
- MRI >= 1.9
|
53
53
|
- Rubinius
|
54
|
-
- JRuby
|
54
|
+
- JRuby
|
55
55
|
- Operating Systems:
|
56
56
|
- Linux
|
57
57
|
- OSX
|
@@ -63,7 +63,7 @@ For examples please see the `examples/` directory.
|
|
63
63
|
|
64
64
|
## Installation
|
65
65
|
|
66
|
-
gem install arachni-reactor
|
66
|
+
gem install arachni-reactor
|
67
67
|
|
68
68
|
## Running the Specs
|
69
69
|
|
data/lib/arachni/reactor.rb
CHANGED
@@ -199,10 +199,9 @@ class Reactor
|
|
199
199
|
begin
|
200
200
|
Connection::Error.translate do
|
201
201
|
socket = options[:unix_socket] ?
|
202
|
-
connect_unix( options[:unix_socket] ) :
|
203
|
-
connect_tcp( options[:host], options[:port] )
|
202
|
+
connect_unix( options[:unix_socket] ) : connect_tcp
|
204
203
|
|
205
|
-
connection.configure socket, :client
|
204
|
+
connection.configure options.merge( socket: socket, role: :client )
|
206
205
|
attach connection
|
207
206
|
end
|
208
207
|
rescue Connection::Error => e
|
@@ -266,7 +265,7 @@ class Reactor
|
|
266
265
|
listen_unix( options[:unix_socket] ) :
|
267
266
|
listen_tcp( options[:host], options[:port] )
|
268
267
|
|
269
|
-
server.configure socket, :server, server_handler
|
268
|
+
server.configure options.merge( socket: socket, role: :server, server_handler: server_handler )
|
270
269
|
attach server
|
271
270
|
end
|
272
271
|
rescue Connection::Error => e
|
@@ -500,7 +499,7 @@ class Reactor
|
|
500
499
|
|
501
500
|
schedule do
|
502
501
|
connection.reactor = self
|
503
|
-
@connections[connection.
|
502
|
+
@connections[connection.to_io] = connection
|
504
503
|
connection.on_attach
|
505
504
|
end
|
506
505
|
end
|
@@ -517,7 +516,7 @@ class Reactor
|
|
517
516
|
|
518
517
|
schedule do
|
519
518
|
connection.on_detach
|
520
|
-
@connections.delete connection.
|
519
|
+
@connections.delete connection.to_io
|
521
520
|
connection.reactor = nil
|
522
521
|
end
|
523
522
|
end
|
@@ -525,7 +524,7 @@ class Reactor
|
|
525
524
|
# @return [Bool]
|
526
525
|
# `true` if the connection is attached, `false` otherwise.
|
527
526
|
def attached?( connection )
|
528
|
-
@connections.include? connection.
|
527
|
+
@connections.include? connection.to_io
|
529
528
|
end
|
530
529
|
|
531
530
|
private
|
@@ -558,6 +557,13 @@ class Reactor
|
|
558
557
|
return
|
559
558
|
end
|
560
559
|
|
560
|
+
# Required for OSX as it connects immediately and then #select returns
|
561
|
+
# nothing as there's no activity, given that, OpenSSL doesn't get a chance
|
562
|
+
# to do its handshake so explicitly connect pending sockets, bypassing #select.
|
563
|
+
@connections.each do |_, connection|
|
564
|
+
connection._connect if !connection.connected?
|
565
|
+
end
|
566
|
+
|
561
567
|
# Get connections with available events - :read, :write, :error.
|
562
568
|
selected = select_connections
|
563
569
|
|
@@ -599,26 +605,13 @@ class Reactor
|
|
599
605
|
|
600
606
|
# @return [Socket]
|
601
607
|
# Connected socket.
|
602
|
-
def connect_tcp
|
608
|
+
def connect_tcp
|
603
609
|
socket = Socket.new(
|
604
610
|
Socket::Constants::AF_INET,
|
605
611
|
Socket::Constants::SOCK_STREAM,
|
606
612
|
Socket::Constants::IPPROTO_IP
|
607
613
|
)
|
608
614
|
socket.do_not_reverse_lookup = true
|
609
|
-
|
610
|
-
# JRuby throws java.nio.channels.NotYetConnectedException even after
|
611
|
-
# it returns the socket from Kernel.select, so wait for it to connect
|
612
|
-
# before moving on.
|
613
|
-
if self.class.jruby?
|
614
|
-
socket.connect( Socket.sockaddr_in( port, host ) )
|
615
|
-
else
|
616
|
-
begin
|
617
|
-
socket.connect_nonblock( Socket.sockaddr_in( port, host ) )
|
618
|
-
rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
|
619
|
-
end
|
620
|
-
end
|
621
|
-
|
622
615
|
socket
|
623
616
|
end
|
624
617
|
|
@@ -650,43 +643,70 @@ class Reactor
|
|
650
643
|
# {Connection#has_outgoing_data? outgoing buffer).
|
651
644
|
# * `:error`
|
652
645
|
def select_connections
|
653
|
-
|
646
|
+
readables = read_sockets
|
647
|
+
|
648
|
+
selected_sockets =
|
654
649
|
begin
|
655
650
|
Connection::Error.translate do
|
656
651
|
select(
|
657
|
-
|
652
|
+
readables,
|
658
653
|
write_sockets,
|
659
|
-
|
654
|
+
all_sockets,
|
660
655
|
@select_timeout
|
661
656
|
)
|
662
657
|
end
|
663
|
-
rescue Connection::Error
|
658
|
+
rescue Connection::Error => e
|
659
|
+
nil
|
664
660
|
end
|
665
661
|
|
666
|
-
|
662
|
+
selected_sockets ||= [[],[],[]]
|
663
|
+
|
664
|
+
# SSL sockets maintain their own buffer whose state can't be checked by
|
665
|
+
# Kernel.select, leading to cases where the SSL buffer isn't empty,
|
666
|
+
# even though Kernel.select says that there's nothing to read.
|
667
|
+
#
|
668
|
+
# So force a read for SSL sockets to cover all our bases.
|
669
|
+
#
|
670
|
+
# This is apparent especially on JRuby.
|
671
|
+
(readables - selected_sockets[0]).each do |socket|
|
672
|
+
next if !socket.is_a?( OpenSSL::SSL::SSLSocket )
|
673
|
+
selected_sockets[0] << socket
|
674
|
+
end
|
675
|
+
|
676
|
+
if selected_sockets[0].empty? && selected_sockets[1].empty? &&
|
677
|
+
selected_sockets[2].empty?
|
678
|
+
return {}
|
679
|
+
end
|
667
680
|
|
668
681
|
{
|
669
682
|
# Since these will be processed in order, it's better have the write
|
670
683
|
# ones first to flush the buffers ASAP.
|
671
|
-
write:
|
672
|
-
read:
|
673
|
-
error:
|
684
|
+
write: connections_from_sockets( selected_sockets[1] ),
|
685
|
+
read: connections_from_sockets( selected_sockets[0] ),
|
686
|
+
error: connections_from_sockets( selected_sockets[2] )
|
674
687
|
}
|
675
688
|
end
|
676
689
|
|
677
690
|
# @return [Array<Socket>]
|
678
691
|
# Sockets of all connections, we want to be ready to read at any time.
|
679
692
|
def read_sockets
|
680
|
-
@connections.
|
693
|
+
@connections.map do |_, connection|
|
694
|
+
next if !connection.listener? && !connection.connected?
|
695
|
+
connection.socket
|
696
|
+
end.compact
|
697
|
+
end
|
698
|
+
|
699
|
+
def all_sockets
|
700
|
+
@connections.values.map(&:socket)
|
681
701
|
end
|
682
702
|
|
683
703
|
# @return [Array<Socket>]
|
684
704
|
# Sockets of connections with
|
685
705
|
# {Connection#has_outgoing_data? outgoing data}.
|
686
706
|
def write_sockets
|
687
|
-
@connections.map do |
|
688
|
-
next if !connection.has_outgoing_data?
|
689
|
-
socket
|
707
|
+
@connections.map do |_, connection|
|
708
|
+
next if connection.connected? && !connection.has_outgoing_data?
|
709
|
+
connection.socket
|
690
710
|
end.compact
|
691
711
|
end
|
692
712
|
|
@@ -695,7 +715,7 @@ class Reactor
|
|
695
715
|
end
|
696
716
|
|
697
717
|
def connection_from_socket( socket )
|
698
|
-
@connections[socket]
|
718
|
+
@connections[socket.to_io]
|
699
719
|
end
|
700
720
|
|
701
721
|
end
|
@@ -7,8 +7,8 @@
|
|
7
7
|
=end
|
8
8
|
|
9
9
|
require_relative 'connection/error'
|
10
|
-
require_relative 'connection/peer_info'
|
11
10
|
require_relative 'connection/callbacks'
|
11
|
+
require_relative 'connection/peer_info'
|
12
12
|
require_relative 'connection/tls'
|
13
13
|
|
14
14
|
module Arachni
|
@@ -16,7 +16,6 @@ class Reactor
|
|
16
16
|
|
17
17
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
18
18
|
class Connection
|
19
|
-
include PeerInfo
|
20
19
|
include Callbacks
|
21
20
|
|
22
21
|
# Maximum amount of data to be written or read at a time.
|
@@ -176,6 +175,92 @@ class Connection
|
|
176
175
|
nil
|
177
176
|
end
|
178
177
|
|
178
|
+
# Accepts a new client connection.
|
179
|
+
#
|
180
|
+
# @return [Connection, nil]
|
181
|
+
# New connection or `nil` if the socket isn't ready to accept new
|
182
|
+
# connections yet.
|
183
|
+
#
|
184
|
+
# @private
|
185
|
+
def accept
|
186
|
+
return if !(accepted = socket_accept)
|
187
|
+
|
188
|
+
connection = @server_handler.call
|
189
|
+
connection.configure socket: accepted, role: :server
|
190
|
+
@reactor.attach connection
|
191
|
+
connection
|
192
|
+
end
|
193
|
+
|
194
|
+
# @param [Socket] socket
|
195
|
+
# Ruby `Socket` associated with this connection.
|
196
|
+
# @param [Symbol] role
|
197
|
+
# `:server` or `:client`.
|
198
|
+
# @param [Block] server_handler
|
199
|
+
# Block that generates a handler as specified in {Reactor#listen}.
|
200
|
+
#
|
201
|
+
# @private
|
202
|
+
def configure( options = {} )
|
203
|
+
@socket = options[:socket]
|
204
|
+
@role = options[:role]
|
205
|
+
@host = options[:host]
|
206
|
+
@port = options[:port]
|
207
|
+
@server_handler = options[:server_handler]
|
208
|
+
|
209
|
+
# If we're a server without a handler then we're an accepted connection.
|
210
|
+
if unix? || role == :server
|
211
|
+
@connected = true
|
212
|
+
on_connect
|
213
|
+
end
|
214
|
+
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
|
218
|
+
def connected?
|
219
|
+
!!@connected
|
220
|
+
end
|
221
|
+
|
222
|
+
# @private
|
223
|
+
def _connect
|
224
|
+
return true if unix? || connected?
|
225
|
+
|
226
|
+
begin
|
227
|
+
Error.translate do
|
228
|
+
socket.connect_nonblock( Socket.sockaddr_in( @port, @host ) )
|
229
|
+
end
|
230
|
+
# Already connected. :)
|
231
|
+
rescue Errno::EISCONN
|
232
|
+
end
|
233
|
+
|
234
|
+
@connected = true
|
235
|
+
on_connect
|
236
|
+
|
237
|
+
true
|
238
|
+
rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
|
239
|
+
rescue Error => e
|
240
|
+
close e
|
241
|
+
end
|
242
|
+
|
243
|
+
# @note If this is a server {#listener?} it will delegate to {#accept}.
|
244
|
+
# @note If this is a normal socket it will read {BLOCK_SIZE} amount of data.
|
245
|
+
# and pass it to {#on_read}.
|
246
|
+
#
|
247
|
+
# Processes a `read` event for this connection.
|
248
|
+
#
|
249
|
+
# @private
|
250
|
+
def _read
|
251
|
+
return _connect if !listener? && !connected?
|
252
|
+
return accept if listener?
|
253
|
+
|
254
|
+
Error.translate do
|
255
|
+
on_read @socket.read_nonblock( BLOCK_SIZE )
|
256
|
+
end
|
257
|
+
|
258
|
+
# Not ready to read or write yet, we'll catch it on future Reactor ticks.
|
259
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
260
|
+
rescue Error => e
|
261
|
+
close e
|
262
|
+
end
|
263
|
+
|
179
264
|
# @note Will call {#on_write} every time any of the buffer is consumed,
|
180
265
|
# can be multiple times when performing partial writes.
|
181
266
|
# @note Will call {#on_flush} once all of the buffer has been consumed.
|
@@ -190,12 +275,14 @@ class Connection
|
|
190
275
|
#
|
191
276
|
# @private
|
192
277
|
def _write
|
278
|
+
return _connect if !connected?
|
279
|
+
|
193
280
|
chunk = write_buffer.slice( 0, BLOCK_SIZE )
|
194
281
|
total_written = 0
|
195
282
|
|
196
283
|
begin
|
197
284
|
Error.translate do
|
198
|
-
# Send out the
|
285
|
+
# Send out the chunk, **all** of it, or at least try to.
|
199
286
|
loop do
|
200
287
|
total_written += written = @socket.write_nonblock( chunk )
|
201
288
|
write_buffer.slice!( 0, written )
|
@@ -222,60 +309,6 @@ class Connection
|
|
222
309
|
close e
|
223
310
|
end
|
224
311
|
|
225
|
-
# @note If this is a server {#listener?} it will delegate to {#accept}.
|
226
|
-
# @note If this is a normal socket it will read {BLOCK_SIZE} amount of data.
|
227
|
-
# and pass it to {#on_read}.
|
228
|
-
#
|
229
|
-
# Processes a `read` event for this connection.
|
230
|
-
#
|
231
|
-
# @private
|
232
|
-
def _read
|
233
|
-
return accept if listener?
|
234
|
-
|
235
|
-
Error.translate do
|
236
|
-
on_read @socket.read_nonblock( BLOCK_SIZE )
|
237
|
-
end
|
238
|
-
|
239
|
-
# Not ready to read or write yet, we'll catch it on future Reactor ticks.
|
240
|
-
rescue IO::WaitReadable, IO::WaitWritable
|
241
|
-
rescue Error => e
|
242
|
-
close e
|
243
|
-
end
|
244
|
-
|
245
|
-
# Accepts a new client connection.
|
246
|
-
#
|
247
|
-
# @return [Connection, nil]
|
248
|
-
# New connection or `nil` if the socket isn't ready to accept new
|
249
|
-
# connections yet.
|
250
|
-
#
|
251
|
-
# @private
|
252
|
-
def accept
|
253
|
-
return if !(accepted = socket_accept)
|
254
|
-
|
255
|
-
connection = @server_handler.call
|
256
|
-
connection.configure accepted, :server
|
257
|
-
@reactor.attach connection
|
258
|
-
connection
|
259
|
-
end
|
260
|
-
|
261
|
-
# @param [Socket] socket
|
262
|
-
# Ruby `Socket` associated with this connection.
|
263
|
-
# @param [Symbol] role
|
264
|
-
# `:server` or `:client`.
|
265
|
-
# @param [Block] server_handler
|
266
|
-
# Block that generates a handler as specified in {Reactor#listen}.
|
267
|
-
#
|
268
|
-
# @private
|
269
|
-
def configure( socket, role, server_handler = nil )
|
270
|
-
@socket = socket
|
271
|
-
@role = role
|
272
|
-
@server_handler = server_handler
|
273
|
-
|
274
|
-
on_connect
|
275
|
-
|
276
|
-
nil
|
277
|
-
end
|
278
|
-
|
279
312
|
private
|
280
313
|
|
281
314
|
def write_buffer
|
@@ -51,7 +51,7 @@ class Error < Arachni::Reactor::Error
|
|
51
51
|
# aren't any specific exceptions for these.
|
52
52
|
#
|
53
53
|
# Why make things easy and clean, right?
|
54
|
-
rescue OpenSSL::OpenSSLError => e
|
54
|
+
rescue OpenSSL::SSL::SSLError, OpenSSL::OpenSSLError => e
|
55
55
|
raise_with_proper_backtrace( e, SSL )
|
56
56
|
end
|
57
57
|
|
@@ -62,17 +62,78 @@ module TLS
|
|
62
62
|
@socket = OpenSSL::SSL::SSLSocket.new( @socket, @ssl_context )
|
63
63
|
@socket.sync_close = true
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
# We've switched to SSL, a connection needs to be re-established
|
66
|
+
# via the SSL handshake.
|
67
|
+
@connected = false
|
68
|
+
|
69
|
+
_connect if unix?
|
69
70
|
end
|
70
71
|
|
71
72
|
@socket
|
72
73
|
end
|
73
74
|
|
75
|
+
# Performs an SSL handshake in addition to a plaintext connect operation.
|
76
|
+
#
|
77
|
+
# @private
|
78
|
+
def _connect
|
79
|
+
return if @ssl_connected
|
80
|
+
|
81
|
+
Error.translate do
|
82
|
+
@plaintext_connected ||= super
|
83
|
+
return if !@plaintext_connected
|
84
|
+
|
85
|
+
# Mark the connection as not connected due to the pending SSL handshake.
|
86
|
+
@connected = false
|
87
|
+
|
88
|
+
@socket.connect_nonblock
|
89
|
+
@ssl_connected = @connected = true
|
90
|
+
end
|
91
|
+
rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
|
92
|
+
rescue Error => e
|
93
|
+
close e
|
94
|
+
end
|
95
|
+
|
96
|
+
# First checks if there's a pending SSL #accept operation when this
|
97
|
+
# connection is a server handler which has been passed an accepted
|
98
|
+
# plaintext connection.
|
99
|
+
#
|
100
|
+
# @private
|
101
|
+
def _write( *args )
|
102
|
+
return ssl_accept if accept?
|
103
|
+
|
104
|
+
super( *args )
|
105
|
+
end
|
106
|
+
|
107
|
+
# First checks if there's a pending SSL #accept operation when this
|
108
|
+
# connection is a server handler which has been passed an accepted
|
109
|
+
# plaintext connection.
|
110
|
+
#
|
111
|
+
# @private
|
112
|
+
def _read
|
113
|
+
return ssl_accept if accept?
|
114
|
+
|
115
|
+
super
|
116
|
+
end
|
117
|
+
|
74
118
|
private
|
75
119
|
|
120
|
+
def ssl_accept
|
121
|
+
Error.translate do
|
122
|
+
@accepted = !!@socket.accept_nonblock
|
123
|
+
end
|
124
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
125
|
+
rescue Error => e
|
126
|
+
close e
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
def accept?
|
131
|
+
return false if @accepted
|
132
|
+
return false if role != :server || !@socket.is_a?( OpenSSL::SSL::SSLSocket )
|
133
|
+
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
76
137
|
# Accepts a new SSL client connection.
|
77
138
|
#
|
78
139
|
# @return [OpenSSL::SSL::SSLSocket, nil]
|
@@ -81,26 +142,19 @@ module TLS
|
|
81
142
|
#
|
82
143
|
# @private
|
83
144
|
def socket_accept
|
84
|
-
|
85
|
-
begin
|
145
|
+
Error.translate do
|
86
146
|
socket = to_io.accept_nonblock
|
87
|
-
rescue IO::WaitReadable, IO::WaitWritable
|
88
|
-
return
|
89
|
-
end
|
90
|
-
|
91
|
-
socket = OpenSSL::SSL::SSLSocket.new(
|
92
|
-
socket,
|
93
|
-
@ssl_context
|
94
|
-
)
|
95
|
-
socket.sync_close = true
|
96
147
|
|
97
|
-
|
98
|
-
|
99
|
-
|
148
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(
|
149
|
+
socket,
|
150
|
+
@ssl_context
|
151
|
+
)
|
152
|
+
ssl_socket.sync_close = true
|
153
|
+
ssl_socket
|
100
154
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
155
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
156
|
+
rescue Error => e
|
157
|
+
close e
|
104
158
|
end
|
105
159
|
|
106
160
|
end
|
@@ -43,16 +43,32 @@ describe Arachni::Reactor::Connection::TLS do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
before :each do
|
47
|
+
@accept_q = Queue.new
|
48
|
+
@accepted = nil
|
49
|
+
end
|
50
|
+
|
46
51
|
let(:unix_socket) { unix_connect( @unix_socket ) }
|
47
52
|
let(:unix_server_socket) { unix_server( port_to_socket( Servers.available_port ) ) }
|
48
53
|
|
49
|
-
let(:echo_client) {
|
54
|
+
let(:echo_client) { tcp_socket }
|
50
55
|
let(:echo_client_handler) { EchoClientTLS.new }
|
51
56
|
|
52
57
|
let(:peer_client_socket) { tcp_ssl_connect( host, port ) }
|
53
|
-
let(:peer_server_socket)
|
58
|
+
let(:peer_server_socket) do
|
59
|
+
s = tcp_ssl_server( host, port )
|
60
|
+
Thread.new do
|
61
|
+
begin
|
62
|
+
@accept_q << s.accept
|
63
|
+
rescue => e
|
64
|
+
ap e
|
65
|
+
end
|
66
|
+
end
|
67
|
+
s
|
68
|
+
end
|
69
|
+
let(:accepted) { @accepted ||= @accept_q.pop }
|
54
70
|
|
55
|
-
let(:client_socket) {
|
71
|
+
let(:client_socket) { tcp_socket }
|
56
72
|
let(:server_socket) { tcp_server( host, port ) }
|
57
73
|
|
58
74
|
let(:connection) { TLSHandler.new }
|
@@ -244,7 +260,15 @@ describe Arachni::Reactor::Connection::TLS do
|
|
244
260
|
|
245
261
|
context 'when connecting to a server' do
|
246
262
|
let(:server) do
|
247
|
-
tcp_ssl_server( host, port, server_ssl_options )
|
263
|
+
s = tcp_ssl_server( host, port, server_ssl_options )
|
264
|
+
Thread.new do
|
265
|
+
begin
|
266
|
+
@accept_q << s.accept
|
267
|
+
rescue => e
|
268
|
+
# ap e
|
269
|
+
end
|
270
|
+
end
|
271
|
+
s
|
248
272
|
end
|
249
273
|
|
250
274
|
before :each do
|
@@ -258,9 +282,7 @@ describe Arachni::Reactor::Connection::TLS do
|
|
258
282
|
it 'connects successfully' do
|
259
283
|
received = nil
|
260
284
|
Thread.new do
|
261
|
-
|
262
|
-
received = s.gets
|
263
|
-
|
285
|
+
received = accepted.gets
|
264
286
|
reactor.stop
|
265
287
|
end
|
266
288
|
|
@@ -279,10 +301,6 @@ describe Arachni::Reactor::Connection::TLS do
|
|
279
301
|
|
280
302
|
context 'and no options have been provided' do
|
281
303
|
it "passes #{Arachni::Reactor::Connection::Error} to #on_error" do
|
282
|
-
Thread.new do
|
283
|
-
server.accept
|
284
|
-
end
|
285
|
-
|
286
304
|
connection = nil
|
287
305
|
reactor.run do
|
288
306
|
connection = reactor.connect( host, port, TLSHandler )
|
@@ -297,8 +315,7 @@ describe Arachni::Reactor::Connection::TLS do
|
|
297
315
|
it 'connects successfully' do
|
298
316
|
received = nil
|
299
317
|
t = Thread.new do
|
300
|
-
|
301
|
-
received = s.gets
|
318
|
+
received = accepted.gets
|
302
319
|
reactor.stop
|
303
320
|
end
|
304
321
|
|
@@ -313,10 +330,6 @@ describe Arachni::Reactor::Connection::TLS do
|
|
313
330
|
|
314
331
|
context 'and are invalid' do
|
315
332
|
it "passes #{Arachni::Reactor::Connection::Error} to #on_error" do
|
316
|
-
Thread.new do
|
317
|
-
server.accept
|
318
|
-
end
|
319
|
-
|
320
333
|
connection = nil
|
321
334
|
reactor.run do
|
322
335
|
connection = reactor.connect( host, port, TLSHandler, client_invalid_ssl_options )
|
@@ -39,16 +39,32 @@ describe Arachni::Reactor::Connection do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
before :each do
|
43
|
+
@accept_q = Queue.new
|
44
|
+
@accepted = nil
|
45
|
+
end
|
46
|
+
|
42
47
|
let(:unix_socket) { unix_connect( @unix_socket ) }
|
43
48
|
let(:unix_server_socket) { unix_server( port_to_socket( Servers.available_port ) ) }
|
44
49
|
|
45
|
-
let(:echo_client) {
|
50
|
+
let(:echo_client) { tcp_socket }
|
46
51
|
let(:echo_client_handler) { EchoClient.new }
|
47
52
|
|
48
53
|
let(:peer_client_socket) { tcp_connect( host, port ) }
|
49
|
-
let(:peer_server_socket)
|
54
|
+
let(:peer_server_socket) do
|
55
|
+
s = tcp_server( host, port )
|
56
|
+
Thread.new do
|
57
|
+
begin
|
58
|
+
@accept_q << s.accept
|
59
|
+
rescue => e
|
60
|
+
ap e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
s
|
64
|
+
end
|
65
|
+
let(:accepted) { @accepted ||= @accept_q.pop }
|
50
66
|
|
51
|
-
let(:client_socket) {
|
67
|
+
let(:client_socket) { tcp_socket }
|
52
68
|
let(:server_socket) { tcp_server( host, port ) }
|
53
69
|
|
54
70
|
let(:connection) { Handler.new }
|
@@ -17,6 +17,16 @@ def tcp_connect( host, port )
|
|
17
17
|
TCPSocket.new( host, port )
|
18
18
|
end
|
19
19
|
|
20
|
+
def tcp_socket
|
21
|
+
socket = Socket.new(
|
22
|
+
Socket::Constants::AF_INET,
|
23
|
+
Socket::Constants::SOCK_STREAM,
|
24
|
+
Socket::Constants::IPPROTO_IP
|
25
|
+
)
|
26
|
+
socket.do_not_reverse_lookup = true
|
27
|
+
socket
|
28
|
+
end
|
29
|
+
|
20
30
|
def tcp_write( host, port, data )
|
21
31
|
s = tcp_connect( host, port )
|
22
32
|
s.write data
|
@@ -45,6 +55,10 @@ def tcp_server( host, port )
|
|
45
55
|
TCPServer.new( host, port )
|
46
56
|
end
|
47
57
|
|
58
|
+
def tcp_ssl_socket( host, port, options = {} )
|
59
|
+
convert_client_to_ssl( tcp_socket, options )
|
60
|
+
end
|
61
|
+
|
48
62
|
def tcp_ssl_connect( host, port, options = {} )
|
49
63
|
convert_client_to_ssl( tcp_connect( host, port ), options )
|
50
64
|
end
|
data/spec/support/lib/servers.rb
CHANGED
@@ -26,7 +26,7 @@ class Servers
|
|
26
26
|
return [host_for(name), port_for(name)] if server_info[:pid] && up?( name )
|
27
27
|
|
28
28
|
server_info[:pid] = Process.spawn(
|
29
|
-
|
29
|
+
RbConfig.ruby, RUNNER, server_info[:path], '-p', server_info[:port].to_s,
|
30
30
|
'-o', host_for( name )
|
31
31
|
)
|
32
32
|
|
@@ -77,7 +77,7 @@ class Servers
|
|
77
77
|
return if !server_info[:pid]
|
78
78
|
|
79
79
|
begin
|
80
|
-
Process.kill( 'KILL', server_info[:pid] ) while sleep 0.1
|
80
|
+
Process.kill( Gem.win_platform? ? 'QUIT' : 'KILL', server_info[:pid] ) while sleep 0.1
|
81
81
|
rescue Errno::ESRCH
|
82
82
|
server_info.delete(:pid)
|
83
83
|
|
@@ -4,10 +4,10 @@ loop do
|
|
4
4
|
Thread.new server.accept do |socket|
|
5
5
|
begin
|
6
6
|
loop do
|
7
|
-
next if
|
8
|
-
socket.write(
|
7
|
+
next if (data = socket.gets).to_s.empty?
|
8
|
+
socket.write( data )
|
9
9
|
end
|
10
|
-
rescue EOFError, Errno::EPIPE
|
10
|
+
rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
|
11
11
|
socket.close
|
12
12
|
end
|
13
13
|
end
|
@@ -16,117 +16,28 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
16
16
|
let(:data) { 'b' * 5 * block_size }
|
17
17
|
let(:configured) do
|
18
18
|
connection.reactor = reactor
|
19
|
-
connection.configure
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
Thread.new do
|
36
|
-
s = s.accept
|
37
|
-
end
|
38
|
-
|
39
|
-
IO.select( nil, [configured.socket] )
|
40
|
-
|
41
|
-
info = {}
|
42
|
-
info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr
|
43
|
-
configured.peer_address_info.should == info
|
44
|
-
|
45
|
-
info = {}
|
46
|
-
info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr(false)
|
47
|
-
configured.peer_address_info(false).should == info
|
48
|
-
|
49
|
-
info = {}
|
50
|
-
info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr(true)
|
51
|
-
configured.peer_address_info(true).should == info
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'when using UNIX-domain socket',
|
56
|
-
if: Arachni::Reactor.supports_unix_sockets? do
|
57
|
-
|
58
|
-
let(:connection) { echo_client_handler }
|
59
|
-
let(:role) { :client }
|
60
|
-
let(:socket) { unix_socket }
|
61
|
-
|
62
|
-
it 'returns socket information' do
|
63
|
-
configured
|
64
|
-
|
65
|
-
IO.select( nil, [configured.socket] )
|
66
|
-
|
67
|
-
info = {}
|
68
|
-
info[:protocol], info[:path] = 'AF_UNIX', socket.path
|
69
|
-
configured.peer_address_info.should == info
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
describe '#peer_hostname' do
|
75
|
-
let(:connection) { echo_client_handler }
|
76
|
-
let(:role) { :client }
|
77
|
-
let(:socket) { client_socket }
|
78
|
-
|
79
|
-
it 'returns the peer hostname' do
|
80
|
-
s = peer_server_socket
|
81
|
-
configured
|
82
|
-
|
83
|
-
Thread.new do
|
84
|
-
s = s.accept
|
85
|
-
end
|
86
|
-
|
87
|
-
IO.select( nil, [configured.socket] )
|
88
|
-
|
89
|
-
configured.peer_hostname.should == s.to_io.addr(true)[2]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe '#peer_ip_address' do
|
94
|
-
let(:connection) { echo_client_handler }
|
95
|
-
let(:role) { :client }
|
96
|
-
let(:socket) { client_socket }
|
97
|
-
|
98
|
-
it 'returns the peer IP address' do
|
99
|
-
s = peer_server_socket
|
100
|
-
configured
|
101
|
-
|
102
|
-
Thread.new do
|
103
|
-
s = s.accept
|
19
|
+
connection.configure(
|
20
|
+
host: host,
|
21
|
+
port: port,
|
22
|
+
socket: socket,
|
23
|
+
role: role,
|
24
|
+
server_handler: server_handler
|
25
|
+
)
|
26
|
+
|
27
|
+
if role == :client
|
28
|
+
while !connection.connected?
|
29
|
+
begin
|
30
|
+
IO.select( [connection.socket], [connection.socket], nil, 0.1 )
|
31
|
+
rescue => e
|
32
|
+
ap e
|
33
|
+
break
|
104
34
|
end
|
105
35
|
|
106
|
-
|
107
|
-
|
108
|
-
configured.peer_ip_address.should == s.to_io.addr[3]
|
36
|
+
connection._connect
|
109
37
|
end
|
110
38
|
end
|
111
39
|
|
112
|
-
|
113
|
-
let(:connection) { echo_client_handler }
|
114
|
-
let(:role) { :client }
|
115
|
-
let(:socket) { client_socket }
|
116
|
-
|
117
|
-
it 'returns the peer IP address' do
|
118
|
-
s = peer_server_socket
|
119
|
-
configured
|
120
|
-
|
121
|
-
Thread.new do
|
122
|
-
s = s.accept
|
123
|
-
end
|
124
|
-
|
125
|
-
IO.select( nil, [configured.socket] )
|
126
|
-
|
127
|
-
configured.peer_port.should == s.to_io.addr[1]
|
128
|
-
end
|
129
|
-
end
|
40
|
+
connection
|
130
41
|
end
|
131
42
|
|
132
43
|
describe '#configure' do
|
@@ -157,12 +68,12 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
157
68
|
end
|
158
69
|
end
|
159
70
|
|
160
|
-
it 'calls #on_connect' do
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
end
|
71
|
+
# it 'calls #on_connect' do
|
72
|
+
# peer_server_socket
|
73
|
+
# connection.should receive(:on_connect)
|
74
|
+
# connection.reactor = reactor
|
75
|
+
# connection.configure socket: socket, role: role
|
76
|
+
# end
|
166
77
|
end
|
167
78
|
|
168
79
|
describe '#unix?' do
|
@@ -172,7 +83,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
172
83
|
let(:socket) { client_socket }
|
173
84
|
|
174
85
|
it 'returns false' do
|
175
|
-
|
86
|
+
peer_server_socket
|
176
87
|
configured
|
177
88
|
configured.should_not be_unix
|
178
89
|
end
|
@@ -186,7 +97,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
186
97
|
let(:socket) { unix_socket }
|
187
98
|
|
188
99
|
it 'returns true' do
|
189
|
-
|
100
|
+
peer_server_socket
|
190
101
|
configured
|
191
102
|
configured.should be_unix
|
192
103
|
end
|
@@ -200,7 +111,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
200
111
|
let(:socket) { client_socket }
|
201
112
|
|
202
113
|
it 'returns false' do
|
203
|
-
|
114
|
+
peer_server_socket
|
204
115
|
configured
|
205
116
|
configured.should be_inet
|
206
117
|
end
|
@@ -214,7 +125,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
214
125
|
let(:socket) { unix_socket }
|
215
126
|
|
216
127
|
it 'returns false' do
|
217
|
-
|
128
|
+
peer_server_socket
|
218
129
|
configured
|
219
130
|
configured.should_not be_inet
|
220
131
|
end
|
@@ -240,7 +151,6 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
240
151
|
if: Arachni::Reactor.supports_unix_sockets? do
|
241
152
|
|
242
153
|
let(:connection) { echo_client_handler }
|
243
|
-
let(:role) { :client }
|
244
154
|
let(:socket) { unix_server_socket }
|
245
155
|
|
246
156
|
it 'returns UNIXServer' do
|
@@ -278,7 +188,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
278
188
|
|
279
189
|
it 'returns TCPSocket' do
|
280
190
|
peer_server_socket
|
281
|
-
configured.to_io.should be_instance_of
|
191
|
+
configured.to_io.should be_instance_of Socket
|
282
192
|
end
|
283
193
|
end
|
284
194
|
|
@@ -315,7 +225,6 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
315
225
|
if: Arachni::Reactor.supports_unix_sockets? do
|
316
226
|
|
317
227
|
let(:connection) { echo_client_handler }
|
318
|
-
let(:role) { :client }
|
319
228
|
let(:socket) { unix_server_socket }
|
320
229
|
|
321
230
|
it 'returns true' do
|
@@ -479,7 +388,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
479
388
|
let(:socket) { client_socket }
|
480
389
|
|
481
390
|
it 'appends the given data to the send-buffer' do
|
482
|
-
|
391
|
+
peer_server_socket
|
483
392
|
reactor.run_in_thread
|
484
393
|
|
485
394
|
configured
|
@@ -488,8 +397,8 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
488
397
|
received = ''
|
489
398
|
|
490
399
|
t = Thread.new do
|
491
|
-
|
492
|
-
received <<
|
400
|
+
sleep 0.1 while !accepted
|
401
|
+
received << accepted.read( data.size ) while received.size != data.size
|
493
402
|
all_read = true
|
494
403
|
end
|
495
404
|
|
@@ -548,13 +457,13 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
548
457
|
let(:socket) { client_socket }
|
549
458
|
|
550
459
|
it "reads a maximum of #{Arachni::Reactor::Connection::BLOCK_SIZE} bytes at a time" do
|
551
|
-
|
460
|
+
peer_server_socket
|
552
461
|
configured
|
553
462
|
|
554
463
|
Thread.new do
|
555
|
-
|
556
|
-
|
557
|
-
|
464
|
+
sleep 0.1 while !accepted
|
465
|
+
accepted.write data
|
466
|
+
accepted.flush
|
558
467
|
end
|
559
468
|
|
560
469
|
while configured.received_data.to_s.size != data.size
|
@@ -567,15 +476,15 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
567
476
|
end
|
568
477
|
|
569
478
|
it 'passes the data to #on_read' do
|
570
|
-
|
479
|
+
peer_server_socket
|
571
480
|
configured
|
572
481
|
|
573
482
|
data = "test\n"
|
574
483
|
|
575
484
|
Thread.new do
|
576
|
-
|
577
|
-
|
578
|
-
|
485
|
+
sleep 0.1 while !accepted
|
486
|
+
accepted.write data
|
487
|
+
accepted.flush
|
579
488
|
end
|
580
489
|
|
581
490
|
configured._read while !configured.received_data
|
@@ -618,6 +527,8 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
618
527
|
reactor.run_in_thread
|
619
528
|
end
|
620
529
|
|
530
|
+
let(:port) { @port }
|
531
|
+
let(:host) { @host }
|
621
532
|
let(:connection) { echo_client_handler }
|
622
533
|
let(:role) { :client }
|
623
534
|
let(:socket) { echo_client }
|
@@ -634,7 +545,7 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
634
545
|
next
|
635
546
|
end
|
636
547
|
|
637
|
-
written.should
|
548
|
+
written.should <= block_size
|
638
549
|
writes += 1
|
639
550
|
end
|
640
551
|
|
@@ -647,17 +558,14 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
647
558
|
|
648
559
|
writes = 0
|
649
560
|
while configured.has_outgoing_data?
|
650
|
-
|
651
561
|
IO.select( nil, [configured.socket] )
|
652
|
-
|
653
|
-
|
654
|
-
next
|
655
|
-
end
|
562
|
+
|
563
|
+
next if configured._write == 0
|
656
564
|
|
657
565
|
writes += 1
|
658
566
|
end
|
659
567
|
|
660
|
-
configured.on_write_count.should
|
568
|
+
configured.on_write_count.should >= writes
|
661
569
|
end
|
662
570
|
|
663
571
|
context 'when the buffer is entirely consumed' do
|
@@ -668,12 +576,10 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
668
576
|
while configured.has_outgoing_data?
|
669
577
|
IO.select( nil, [configured.socket] )
|
670
578
|
|
671
|
-
if
|
579
|
+
if configured._write == 0
|
672
580
|
IO.select( [configured.socket] )
|
673
581
|
next
|
674
582
|
end
|
675
|
-
|
676
|
-
written.should == block_size
|
677
583
|
end
|
678
584
|
|
679
585
|
configured.called_on_flush.should be_true
|
@@ -682,6 +588,9 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
682
588
|
end
|
683
589
|
|
684
590
|
describe '#has_outgoing_data?' do
|
591
|
+
let(:port) { @port }
|
592
|
+
let(:host) { @host }
|
593
|
+
|
685
594
|
let(:role) { :client }
|
686
595
|
let(:socket) { echo_client }
|
687
596
|
|
@@ -704,6 +613,9 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
704
613
|
end
|
705
614
|
|
706
615
|
describe '#closed?' do
|
616
|
+
let(:port) { @port }
|
617
|
+
let(:host) { @host }
|
618
|
+
|
707
619
|
let(:role) { :client }
|
708
620
|
let(:socket) { echo_client }
|
709
621
|
|
@@ -725,6 +637,9 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
725
637
|
end
|
726
638
|
|
727
639
|
describe '#close_without_callback' do
|
640
|
+
let(:port) { @port }
|
641
|
+
let(:host) { @host }
|
642
|
+
|
728
643
|
let(:role) { :client }
|
729
644
|
let(:socket) { echo_client }
|
730
645
|
|
@@ -753,6 +668,9 @@ shared_examples_for 'Arachni::Reactor::Connection' do
|
|
753
668
|
end
|
754
669
|
|
755
670
|
describe '#close' do
|
671
|
+
let(:port) { @port }
|
672
|
+
let(:host) { @host }
|
673
|
+
|
756
674
|
let(:role) { :client }
|
757
675
|
let(:socket) { echo_client }
|
758
676
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arachni-reactor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tasos Laskos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Arachni::Reactor is a simple, lightweight, pure-Ruby implementation of the Reactor
|
@@ -93,12 +93,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
93
|
version: '0'
|
94
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- - "
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
98
|
+
version: '0'
|
99
99
|
requirements: []
|
100
100
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.
|
101
|
+
rubygems_version: 2.4.5
|
102
102
|
signing_key:
|
103
103
|
specification_version: 4
|
104
104
|
summary: A pure-Ruby implementation of the Reactor pattern.
|