arachni-rpc-em 0.1.2 → 0.1.3dev1

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,7 +49,7 @@ module EM
49
49
  if !::EM::reactor_running?
50
50
 
51
51
  Thread.new do
52
- ::EM::run do
52
+ ::EM.run do
53
53
  ::EM.error_handler do |e|
54
54
  $stderr.puts "Exception raised during event loop: " +
55
55
  "#{e.message} (#{e.class})\n#{(e.backtrace ||
@@ -88,7 +88,7 @@ module Protocol
88
88
  # cut them out as soon as possible
89
89
  #
90
90
  # don't buffer any data from unverified peers if SSL peer
91
- # veification has been enabled
91
+ # verification has been enabled
92
92
  #
93
93
  if ssl_opts? && !verified_peer? && @role == :server
94
94
  e = Arachni::RPC::Exceptions::SSLPeerVerificationFailed.new( 'Could not verify peer.' )
@@ -107,7 +107,7 @@ module Protocol
107
107
  while @buf.size >= 4
108
108
  if @buf.size >= 4 + ( size = @buf.unpack( 'N' ).first )
109
109
  @buf.slice!( 0, 4 )
110
- receive_object( serializer.load( @buf.slice!( 0, size ) ) )
110
+ receive_object( unserialize( @buf.slice!( 0, size ) ) )
111
111
  else
112
112
  break
113
113
  end
@@ -120,7 +120,7 @@ module Protocol
120
120
  # Will split the object in chunks of MAX_CHUNK_SIZE and transmit one at a time.
121
121
  #
122
122
  def send_object( obj )
123
- data = serializer.dump( obj )
123
+ data = serialize( obj )
124
124
  packed = [data.bytesize, data].pack( 'Na*' )
125
125
 
126
126
  while packed
@@ -134,7 +134,7 @@ module Protocol
134
134
  end
135
135
 
136
136
  #
137
- # Returns the preferred serializer based on the 'serializer' option of the server.
137
+ # Returns the preferred based on the 'serializer' option of the server.
138
138
  #
139
139
  # Defaults to <i>YAML</i>.
140
140
  #
@@ -143,9 +143,38 @@ module Protocol
143
143
  # @see http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
144
144
  #
145
145
  def serializer
146
+ return @client_serializer if @client_serializer
147
+
146
148
  @opts[:serializer] ? @opts[:serializer] : YAML
147
149
  end
148
150
 
151
+ def fallback_serializer
152
+ @opts[:fallback_serializer] ? @opts[:serializer] : YAML
153
+ end
154
+
155
+ def serialize( obj )
156
+ serializer.dump obj
157
+ end
158
+
159
+ def unserialize( obj )
160
+ begin
161
+ r = serializer.load( obj )
162
+
163
+ if !r.is_a?( Hash ) && @opts[:fallback_serializer]
164
+ r = @opts[:fallback_serializer].load( obj )
165
+ @client_serializer = @opts[:fallback_serializer]
166
+ end
167
+
168
+ r
169
+ rescue Exception => e
170
+ raise if !@opts[:fallback_serializer]
171
+
172
+ @client_serializer = @opts[:fallback_serializer]
173
+
174
+ @opts[:fallback_serializer].load obj
175
+ end
176
+ end
177
+
149
178
  end
150
179
 
151
180
  end
@@ -82,53 +82,46 @@ class Server
82
82
 
83
83
  # the method call may block a little so tell EventMachine to
84
84
  # stick it in its own thread.
85
- # ::EM.defer( proc {
86
- res = Response.new
87
- peer = peer_ip_addr
85
+ res = Response.new
86
+ peer = peer_ip_addr
88
87
 
89
- begin
90
- # token-based authentication
91
- authenticate!
88
+ begin
89
+ # token-based authentication
90
+ authenticate!
92
91
 
93
- # grab the result of the method call
94
- res.merge!( @server.call( self ) )
92
+ # grab the result of the method call
93
+ res.merge!( @server.call( self ) )
95
94
 
96
- # handle exceptions and convert them to a simple hash,
97
- # ready to be passed to the client.
98
- rescue Exception => e
95
+ # handle exceptions and convert them to a simple hash,
96
+ # ready to be passed to the client.
97
+ rescue Exception => e
99
98
 
100
- type = ''
99
+ type = ''
101
100
 
102
- # if it's an RPC exception pass the type along as is
103
- if e.rpc_exception?
104
- type = e.class.name.split( ':' )[-1]
105
-
106
- # otherwise set it to a RemoteExeption
107
- else
108
- type = 'RemoteException'
109
- end
101
+ # if it's an RPC exception pass the type along as is
102
+ if e.rpc_exception?
103
+ type = e.class.name.split( ':' )[-1]
104
+ # otherwise set it to a RemoteExeption
105
+ else
106
+ type = 'RemoteException'
107
+ end
110
108
 
111
- res.obj = {
112
- 'exception' => e.to_s,
113
- 'backtrace' => e.backtrace,
114
- 'type' => type
115
- }
109
+ res.obj = {
110
+ 'exception' => e.to_s,
111
+ 'backtrace' => e.backtrace,
112
+ 'type' => type
113
+ }
116
114
 
117
- msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
118
- @server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
119
- end
115
+ msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
116
+ @server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
117
+ end
120
118
 
121
- # res
122
- # }, proc {
123
- # |res|
124
-
125
- #
126
- # pass the result of the RPC call back to the client
127
- # along with the callback ID but *only* if it wan't async
128
- # because server.call() will have already taken care of it
129
- #
130
- send_response( res ) if !res.async?
131
- # })
119
+ #
120
+ # pass the result of the RPC call back to the client
121
+ # along with the callback ID but *only* if it wan't async
122
+ # because server.call() will have already taken care of it
123
+ #
124
+ send_response( res ) if !res.async?
132
125
  end
133
126
 
134
127
  #
@@ -136,9 +129,6 @@ class Server
136
129
  #
137
130
  # It will raise an exception if the token doesn't check-out.
138
131
  #
139
- # @param [String] peer IP address of the client
140
- # @param [Hash] req request
141
- #
142
132
  def authenticate!
143
133
  if !valid_token?( @request.token )
144
134
 
@@ -148,7 +138,7 @@ class Server
148
138
  msg + " [on behalf of #{peer_ip_addr}]"
149
139
  }
150
140
 
151
- raise InvalidToken.new( msg )
141
+ fail InvalidToken.new( msg )
152
142
  end
153
143
  end
154
144
 
@@ -187,6 +177,9 @@ class Server
187
177
  # # http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
188
178
  # :serializer => Marshal,
189
179
  #
180
+ # # serializer to use if the first choice fails
181
+ # :fallback_serializer => YAML,
182
+ #
190
183
  # #
191
184
  # # In order to enable peer verification one must first provide
192
185
  # # the following:
@@ -232,15 +225,14 @@ class Server
232
225
  # you can just decide dynamically based on the plethora of data which Ruby provides
233
226
  # by its 'Method' class.
234
227
  #
235
- # server.add_async_check {
236
- # |method|
228
+ # server.add_async_check do |method|
237
229
  # #
238
230
  # # Must return 'true' for async and 'false' for sync.
239
231
  # #
240
232
  # # Very simple check here...
241
233
  # #
242
234
  # 'async' == method.name.to_s.split( '_' )[0]
243
- # }
235
+ # end
244
236
  #
245
237
  # @param [Proc] &block
246
238
  #
@@ -310,13 +302,13 @@ class Server
310
302
  if !object_exist?( obj_name )
311
303
  msg = "Trying to access non-existent object '#{obj_name}'."
312
304
  @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" }
313
- raise( InvalidObject.new( msg ) )
305
+ raise InvalidObject.new( msg )
314
306
  end
315
307
 
316
308
  if !public_method?( obj_name, meth_name )
317
309
  msg = "Trying to access non-public method '#{meth_name}'."
318
310
  @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" }
319
- raise( InvalidMethod.new( msg ) )
311
+ raise InvalidMethod.new( msg )
320
312
  end
321
313
 
322
314
  # the proxy needs to know whether this is an async call because if it
@@ -352,7 +344,7 @@ class Server
352
344
  @logger.info( 'System' ){ "Shutting down in #{wait_for} seconds..." }
353
345
 
354
346
  # don't die before returning
355
- EventMachine::add_timer( wait_for ) { ::EM.stop }
347
+ ::EM.add_timer( wait_for ) { ::EM.stop }
356
348
  true
357
349
  end
358
350
 
@@ -88,7 +88,7 @@ module SSL
88
88
  #
89
89
  def log( severity, progname, msg )
90
90
  warn "#{progname}: #{msg}" if severity == :error
91
- raise "#{progname}: #{msg}" if severity == :fatal
91
+ fail "#{progname}: #{msg}" if severity == :fatal
92
92
  end
93
93
 
94
94
  #
@@ -100,7 +100,7 @@ module SSL
100
100
  @ca_store = OpenSSL::X509::Store.new
101
101
  @ca_store.add_file( file )
102
102
  else
103
- raise "No CA certificate has been provided."
103
+ fail "No CA certificate has been provided."
104
104
  end
105
105
  end
106
106
 
@@ -144,17 +144,14 @@ module SSL
144
144
  # @see http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000270
145
145
  #
146
146
  def ssl_handshake_completed
147
- if are_we_a_client? && ssl_opts? &&
148
- !OpenSSL::SSL.verify_certificate_identity( @last_seen_cert,
149
- @opts[:host] )
147
+ return if !are_we_a_client? || !ssl_opts? ||
148
+ OpenSSL::SSL.verify_certificate_identity( @last_seen_cert, @opts[:host] )
150
149
 
151
- log( :error, 'SSL',
152
- "The hostname '#{@server.opts[:host]}' " +
153
- "does not match the server certificate."
154
- )
150
+ log( :error, 'SSL',
151
+ "The hostname '#{@opts[:host]}' does not match the server certificate."
152
+ )
155
153
 
156
- connection_close
157
- end
154
+ close_connection
158
155
  end
159
156
 
160
157
  def are_we_a_client?
@@ -9,7 +9,7 @@
9
9
  module Arachni
10
10
  module RPC
11
11
  module EM
12
- VERSION = '0.1.2'
12
+ VERSION = '0.1.3dev1'
13
13
  end
14
14
  end
15
15
  end
@@ -8,6 +8,35 @@ describe Arachni::RPC::EM::Client do
8
8
  ]
9
9
  end
10
10
 
11
+ it "should be able to retain stability and consistency under heavy load" do
12
+ client = start_client( rpc_opts )
13
+
14
+ n = 400
15
+ cnt = 0
16
+
17
+ mismatches = []
18
+
19
+ n.times do |i|
20
+ client.call( 'test.foo', i ) do |res|
21
+ cnt += 1
22
+ mismatches << [i, res] if i != res
23
+ ::EM.stop if cnt == n || !mismatches.empty?
24
+ end
25
+ end
26
+
27
+ Arachni::RPC::EM.block
28
+ cnt.should > 0
29
+ mismatches.should be_empty
30
+ end
31
+
32
+ it "should throw error when connecting to inexistent server" do
33
+ start_client( rpc_opts.merge( :port => 9999 ) ).call( 'test.foo', @arg ) do |res|
34
+ res.rpc_connection_error?.should be_true
35
+ ::EM.stop
36
+ end
37
+ Arachni::RPC::EM.block
38
+ end
39
+
11
40
  describe "#initialize" do
12
41
  it "should be able to properly assign class options (including :role)" do
13
42
  opts = rpc_opts.merge( :role => :client )
@@ -15,6 +44,19 @@ describe Arachni::RPC::EM::Client do
15
44
  end
16
45
  end
17
46
 
47
+ context 'when using a fallback serializer' do
48
+ context 'and the primary serializer fails' do
49
+ it 'should use the fallback' do
50
+ opts = rpc_opts.merge( port: 7333, serializer: YAML )
51
+ start_client( opts ).call( 'test.foo', @arg ).should == @arg
52
+
53
+ opts = rpc_opts.merge( port: 7333, serializer: Marshal )
54
+ start_client( opts ).call( 'test.foo', @arg ).should == @arg
55
+ end
56
+ end
57
+
58
+ end
59
+
18
60
  describe "raw interface" do
19
61
 
20
62
  context "when using Threads" do
@@ -131,35 +173,6 @@ describe Arachni::RPC::EM::Client do
131
173
  end
132
174
  end
133
175
 
134
- it "should be able to retain stability and consistency under heavy load" do
135
- client = start_client( rpc_opts )
136
-
137
- n = 400
138
- cnt = 0
139
-
140
- mismatches = []
141
-
142
- n.times do |i|
143
- client.call( 'test.foo', i ) do |res|
144
- cnt += 1
145
- mismatches << [i, res] if i != res
146
- ::EM.stop if cnt == n || !mismatches.empty?
147
- end
148
- end
149
-
150
- Arachni::RPC::EM.block
151
- cnt.should > 0
152
- mismatches.should be_empty
153
- end
154
-
155
- it "should throw error when connecting to inexistent server" do
156
- start_client( rpc_opts.merge( :port => 9999 ) ).call( 'test.foo', @arg ) do |res|
157
- res.rpc_connection_error?.should be_true
158
- ::EM.stop
159
- end
160
- Arachni::RPC::EM.block
161
- end
162
-
163
176
  context "when using valid SSL primitives" do
164
177
  it "should be able to establish a connection" do
165
178
  res = start_client( rpc_opts_with_ssl_primitives ).call( 'test.foo', @arg )
data/spec/pems/cacert.pem CHANGED
@@ -1,39 +1,37 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- +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
- 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
- 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
- 3crf4WF/EF69wx78Sz7ZDLx7MJj5iXmmL3Z5hukUWBEC28u8219xWMG5XeyJz+rx
18
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
38
- WC2Jc17gy6ZvkI3wzvqe/eSyq2N8zRzYQEgaotDhkrbL81N0klZ6RoKkxeI=
2
+ MIIGaDCCBFCgAwIBAgIJAPBYwqAog9f1MA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
3
+ BAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRlc3QtQ0ExKjAoBgkqhkiG9w0BCQEW
4
+ G2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAeFw0xMjExMDIxMDIyNTNaFw0y
5
+ MjEwMzExMDIyNTNaMFMxCzAJBgNVBAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRl
6
+ c3QtQ0ExKjAoBgkqhkiG9w0BCQEWG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNv
7
+ bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANHbVRqyD7FT8KSCZLm1
8
+ YrOwMj78OXRjMb+ucPR+fncXR1TEuPlEFojLtzqtsaA5OJXpj+0toFP+2T4DeFlu
9
+ YT4TBrRQ3DxmeY2b5b6ZuurLMfMc1vU+dE92Tj4nuInBz3Z09aDQ8ZWXWzJ7uD3J
10
+ eNjOcj7Jc94AGFx05Tii9VriUmX+jXQALv5S5WGVtKt4p67mLm/2xD4JhS+a0+B8
11
+ s/Xo7l6EXaFeTsH5jgZDiY+f0Dpk6cM+pZ5AJVJiNonDJs8/nl9vdWPRH40GHsyN
12
+ H0/lo/wTxuth/TvX3DBB5hTi/9V5eYbLTLtE1oyXgBxNKrjDu8FVn/jUl8DAIdJL
13
+ n4vXwXI70wLyS6aF8uu9ApNnmSbTTc2scAKDmnlINLHqGGlpyleojqphDy3MfQYk
14
+ YT9QSXNKHlO4NMLFqrqM1F3hvM3MEteeM12gAeeJLY6YYYJafMMMh/e7kK/y5u+J
15
+ ype5t5N8GKskSRp0RvlRYfoH/lnyJd6FEyh9P0QHA4CKAadZBCfOcmwmTY/G0Kjn
16
+ 3Y7r7BmJb4PIEcDqUjXwuyq6ZHjx7sawuGG6eXhIGln0JtSymy4j+h+xlh0S7O8B
17
+ Ti6dMVxg+DNTkEJz2O00IIBcyToqZ26XovFkN5ueRNOROB3YVpldmpbLyuOQae/D
18
+ 4Gc21bEWoR7OAaY2PRl4r563AgMBAAGjggE9MIIBOTAdBgNVHQ4EFgQU8VOtNUbZ
19
+ rnbX0gRWIds5yaT+uCwwgYMGA1UdIwR8MHqAFPFTrTVG2a5219IEViHbOcmk/rgs
20
+ oVekVTBTMQswCQYDVQQGEwJHUjEYMBYGA1UEAxMPQXJhY2huaS10ZXN0LUNBMSow
21
+ KAYJKoZIhvcNAQkBFhthcmFjaG5pQGFyYWNobmktc2Nhbm5lci5jb22CCQDwWMKg
22
+ KIPX9TAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIE
23
+ AjAAMCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRl
24
+ MCYGA1UdEQQfMB2BG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAOBgNVHQ8B
25
+ Af8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggIBAAAAebhmNydIUT+sZpvWM5JKNQdq
26
+ YtnghnpZyM37cxLmFIeDKKPTw/3lhDViRRqQaaFTI7ukcqgloPmhMuymQT4o11Fw
27
+ XLoQuIeSgS7Mg6eZF3CUKoEy1KnlluegDXtDI+WH/EQHlokBvoMaClltj6vsfqI/
28
+ K9K2MAXUKi8K7NRq6VYO1QPtrBfrX9VmLyndbYm8lSG5oDkGGh8NjVgHHZDgrQAQ
29
+ 2EmsWbE9yMZ6yl+AaaE5XrbPWnBI8rK77WP93JYVAhmcaQiJzPfMw3sb2QojKdak
30
+ 7fvJzAjBeXAoTP5Mu/E+BPPgELzB/DnRaWlrYsAQeRZBV+I3+k5CCVqdOAdJCk5Y
31
+ dFNTppHfwVaDj5qKOmodzdUDcDL6ynl15t6WHgj2yBwsDVpWsvbqyitZkemLFwrf
32
+ hAedR3dKr+IxrOynST1w4LorDorcGK/DqhD475bQ9EDel5nW18hotUeeeO+K3qc7
33
+ iGgj7zTKfmhzL/KMotir/SQYYxQbbtLkkhXDaNVlWiYkHotOzrNbpKAFM776CI47
34
+ KTXG88FydcycGHYU8SQLEbEDiowAikr2u+CUHKrJvz2Wr8jkWaMCSfgCyokcY6vR
35
+ IIqnrpYHhX2FmKf+tRB8o3KXM6uiOSUvaW23LHcs0OKqcJ0XHOkhTMDFZ82eDZzl
36
+ CXJQkVNhmc0Y9prF
39
37
  -----END CERTIFICATE-----