Skip to content

Instantly share code, notes, and snippets.

@Zapotek
Created November 15, 2011 10:03
Show Gist options
  • Save Zapotek/1366618 to your computer and use it in GitHub Desktop.
Save Zapotek/1366618 to your computer and use it in GitHub Desktop.
Example pure Ruby client of the Arachni-RPC protocol
require 'socket'
require 'openssl'
require 'yaml'
require 'ap'
class Connection
def initialize( opts )
@opts = opts
socket = TCPSocket.new( opts[:host], opts[:port] )
@ssl_context = OpenSSL::SSL::SSLContext.new
if opts[:ssl_cert] && opts[:ssl_pkey]
@ssl_context.cert = OpenSSL::X509::Certificate.new( File.open( opts[:ssl_cert] ) )
@ssl_context.key = OpenSSL::PKey::RSA.new( File.open( opts[:ssl_pkey] ) )
end
@ssl_socket = OpenSSL::SSL::SSLSocket.new( socket, @ssl_context )
@ssl_socket.sync_close = true
@ssl_socket.connect
end
def close
@ssl_socket.close
end
def send_rcv_object( obj )
send_object( obj )
receive_object
end
def send_object( obj )
serialized = serializer.dump( obj )
@ssl_socket.puts( [ serialized.bytesize, serialized ].pack( 'Na*' ) )
end
def receive_object
while data = @ssl_socket.gets
(@buf ||= '') << data
while @buf.size >= 4
if @buf.size >= 4 + ( size = @buf.unpack( 'N' ).first )
@buf.slice!(0,4)
complete = @buf.slice!( 0, size )
@buf = ''
return serializer.load( complete )
else
break
end
end
end
end
private
def serializer
@opts[:serializer] ? @opts[:serializer] : YAML
end
end
class Client
def initialize( opts )
@opts = opts
end
def call( msg, *args )
conn = Connection.new( @opts )
obj = conn.send_rcv_object( prep_request( msg, *args ) )['obj']
conn.close
handle_exception( obj )
return obj
end
private
def handle_exception( obj )
if exception?( obj )
e = RuntimeError.new( obj['exception'] )
e.set_backtrace( obj['backtrace'] )
raise e
end
end
def exception?( obj )
obj.is_a?( Hash ) && obj.include?( 'exception' )
end
def prep_request( msg, *args )
{
'message' => msg, # RPC message in the form of 'handler.method'
'args' => args, # optional array of arguments for the remote method
'token' => @opts[:token], # optional authentication token,
}
end
end
client = Client.new(
:host => 'localhost',
:port => 7331,
:token => 'superdupersecret'
)
begin
ap client.call( 'bench.foo' )
# Will throw exception because the remote method expects an argument
#
# ./examples/server.rb:15:in `foo': wrong number of arguments (0 for 1) (Exception)
# from /home/zapotek/workspace/arachni-rpc/lib/arachni/rpc/server.rb:345:in `call'
# from /home/zapotek/workspace/arachni-rpc/lib/arachni/rpc/server.rb:105:in `receive_request'
# from /home/zapotek/workspace/arachni-rpc/lib/arachni/rpc/protocol.rb:66:in `receive_object'
rescue
end
# This is just an echo method so it will return: 2
ap client.call( 'bench.foo', 2 )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment