arachni-reactor 0.1.0.beta5 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7671f97abb22c56a6488378023cd84ad92a8f30
4
- data.tar.gz: ffd8c0513e0c41d4483249faaeeea3d9eea5aa21
3
+ metadata.gz: d8f90922fd68d3df405cf0aa7537924db3f3c118
4
+ data.tar.gz: 5bd9e5042618276a83909b43879651d60f8e0538
5
5
  SHA512:
6
- metadata.gz: 5678c3dd7c6e90aa2061f06c892a4889538baee925550db1f5a46fc3ff232a5c66849ae1fef8c24fe6ae06d1e066dc8bfc2570b6f996dfe89ce33effd6d8e86c
7
- data.tar.gz: 8c5ebfd3235211408111216a1dbf942266354f085e14cd629ae56eda7ddb91126b9b4ccbf3705aa2732cf28a45d45598a2aaeea7eabad9af08c1486abd5b26f5
6
+ metadata.gz: ff7c11326a30b31030b95ac93cd7ee3e5c37ab6f7097bb6f9a4829354b13bc683d484e8243bb9d14e88b8248bd69481a9a43ac01d475a708e61199595ec4632b
7
+ data.tar.gz: f1563d0bafe856937029267327fa8c049b05a56122e8fa8dd5c1fd0b45bb7e9784f250eb8dfd4d6438d8b866cd402de4340b9382d5a304c204fd54552c4a389c
@@ -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.beta5</td>
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 (Without OpenSSL support)
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 --pre
66
+ gem install arachni-reactor
67
67
 
68
68
  ## Running the Specs
69
69
 
@@ -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.socket] = 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.socket
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.socket
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( host, port )
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
- grouped_sockets =
646
+ readables = read_sockets
647
+
648
+ selected_sockets =
654
649
  begin
655
650
  Connection::Error.translate do
656
651
  select(
657
- read_sockets,
652
+ readables,
658
653
  write_sockets,
659
- read_sockets, # Read sockets are actually all sockets.
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
- return {} if !grouped_sockets
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: connections_from_sockets( grouped_sockets[1] ),
672
- read: connections_from_sockets( grouped_sockets[0] ),
673
- error: connections_from_sockets( grouped_sockets[2] )
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.keys
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 |socket, connection|
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 buffer, **all** of it, or at least try to.
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
- begin
66
- @socket.connect_nonblock
67
- rescue IO::WaitReadable, IO::WaitWritable
68
- end
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
- socket = nil
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
- begin
98
- socket.accept_nonblock
99
- rescue IO::WaitReadable, IO::WaitWritable
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
- socket
103
- rescue OpenSSL::SSL::SSLError
155
+ rescue IO::WaitReadable, IO::WaitWritable
156
+ rescue Error => e
157
+ close e
104
158
  end
105
159
 
106
160
  end
@@ -9,7 +9,7 @@
9
9
  module Arachni
10
10
  class Reactor
11
11
 
12
- VERSION = '0.1.0.beta5'
12
+ VERSION = '0.1.0'
13
13
 
14
14
  end
15
15
  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) { tcp_connect( @host, @port ) }
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) { tcp_ssl_server( host, port ) }
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) { tcp_connect( host, port ) }
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
- s = server.accept
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
- s = server.accept
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) { tcp_connect( @host, @port ) }
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) { tcp_server( host, port ) }
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) { tcp_connect( host, port ) }
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
@@ -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
- 'ruby', RUNNER, server_info[:path], '-p', server_info[:port].to_s,
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 !(line = socket.gets)
8
- socket.write( line )
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
@@ -12,8 +12,8 @@ loop do
12
12
  Thread.new do
13
13
  begin
14
14
  loop do
15
- next if (line = socket.gets).to_s.empty?
16
- socket.write( line )
15
+ next if (data = socket.gets).to_s.empty?
16
+ socket.write( data )
17
17
  end
18
18
  rescue EOFError, Errno::EPIPE
19
19
  socket.close
@@ -4,8 +4,8 @@ loop do
4
4
  Thread.new server.accept do |socket|
5
5
  begin
6
6
  loop do
7
- next if !(line = socket.gets)
8
- socket.write( line )
7
+ next if (data = socket.gets).to_s.empty?
8
+ socket.write( data )
9
9
  end
10
10
  rescue EOFError, Errno::EPIPE
11
11
  socket.close
@@ -12,8 +12,8 @@ loop do
12
12
  Thread.new do
13
13
  begin
14
14
  loop do
15
- next if (line = socket.gets).to_s.empty?
16
- socket.write( line )
15
+ next if (data = socket.gets).to_s.empty?
16
+ socket.write( data )
17
17
  end
18
18
  rescue EOFError, Errno::EPIPE
19
19
  socket.close
@@ -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 socket, role, server_handler
20
- connection
21
- end
22
-
23
- # This needs to be put in a file of its own.
24
- describe '::PeerInfo' do
25
- describe '#peer_address_info' do
26
- context 'when using an IP socket' do
27
- let(:connection) { echo_client_handler }
28
- let(:role) { :client }
29
- let(:socket) { client_socket }
30
-
31
- it 'returns IP address information' do
32
- s = peer_server_socket
33
- configured
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
- IO.select( nil, [configured.socket] )
107
-
108
- configured.peer_ip_address.should == s.to_io.addr[3]
36
+ connection._connect
109
37
  end
110
38
  end
111
39
 
112
- describe '#peer_port' do
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
- peer_server_socket
162
- connection.should receive(:on_connect)
163
- connection.reactor = reactor
164
- connection.configure socket, role
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
- s = peer_server_socket
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
- s = peer_server_socket
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
- s = peer_server_socket
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
- s = peer_server_socket
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 TCPSocket
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
- s = peer_server_socket
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
- s = s.accept
492
- received << s.read( data.size ) while received.size != data.size
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
- s = peer_server_socket
460
+ peer_server_socket
552
461
  configured
553
462
 
554
463
  Thread.new do
555
- s = s.accept
556
- s.write data
557
- s.flush
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
- s = peer_server_socket
479
+ peer_server_socket
571
480
  configured
572
481
 
573
482
  data = "test\n"
574
483
 
575
484
  Thread.new do
576
- s = s.accept
577
- s.write data
578
- s.flush
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 == block_size
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
- if configured._write == 0
653
- IO.select( [configured.socket] )
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 == writes
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 (written = configured._write) == 0
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.beta5
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: 2014-09-04 00:00:00.000000000 Z
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: 1.3.1
98
+ version: '0'
99
99
  requirements: []
100
100
  rubyforge_project:
101
- rubygems_version: 2.2.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.