Landing #10884 - Add JSON-RPC Client
commit
97ee965c6e
11
Gemfile.lock
11
Gemfile.lock
|
@ -12,6 +12,7 @@ PATH
|
||||||
concurrent-ruby (= 1.0.5)
|
concurrent-ruby (= 1.0.5)
|
||||||
dnsruby
|
dnsruby
|
||||||
ed25519
|
ed25519
|
||||||
|
em-http-request
|
||||||
faker
|
faker
|
||||||
filesize
|
filesize
|
||||||
jsobfu
|
jsobfu
|
||||||
|
@ -119,6 +120,7 @@ GEM
|
||||||
builder (3.2.3)
|
builder (3.2.3)
|
||||||
coderay (1.1.2)
|
coderay (1.1.2)
|
||||||
concurrent-ruby (1.0.5)
|
concurrent-ruby (1.0.5)
|
||||||
|
cookiejar (0.3.3)
|
||||||
crass (1.0.4)
|
crass (1.0.4)
|
||||||
daemons (1.2.6)
|
daemons (1.2.6)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
|
@ -126,6 +128,14 @@ GEM
|
||||||
addressable (~> 2.5)
|
addressable (~> 2.5)
|
||||||
docile (1.3.1)
|
docile (1.3.1)
|
||||||
ed25519 (1.2.4)
|
ed25519 (1.2.4)
|
||||||
|
em-http-request (1.1.5)
|
||||||
|
addressable (>= 2.3.4)
|
||||||
|
cookiejar (!= 0.3.1)
|
||||||
|
em-socksify (>= 0.3)
|
||||||
|
eventmachine (>= 1.0.3)
|
||||||
|
http_parser.rb (>= 0.6.0)
|
||||||
|
em-socksify (0.3.2)
|
||||||
|
eventmachine (>= 1.0.0.beta.4)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
eventmachine (1.2.7)
|
eventmachine (1.2.7)
|
||||||
factory_bot (4.11.1)
|
factory_bot (4.11.1)
|
||||||
|
@ -140,6 +150,7 @@ GEM
|
||||||
filesize (0.2.0)
|
filesize (0.2.0)
|
||||||
fivemat (1.3.7)
|
fivemat (1.3.7)
|
||||||
hashery (2.1.2)
|
hashery (2.1.2)
|
||||||
|
http_parser.rb (0.6.0)
|
||||||
i18n (0.9.5)
|
i18n (0.9.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jsobfu (0.4.2)
|
jsobfu (0.4.2)
|
||||||
|
|
|
@ -17,12 +17,16 @@ module Msf::RPC
|
||||||
|
|
||||||
|
|
||||||
module JSON
|
module JSON
|
||||||
|
autoload :Client, 'msf/core/rpc/json/client'
|
||||||
autoload :Dispatcher, 'msf/core/rpc/json/dispatcher'
|
autoload :Dispatcher, 'msf/core/rpc/json/dispatcher'
|
||||||
autoload :DispatcherHelper, 'msf/core/rpc/json/dispatcher_helper'
|
autoload :DispatcherHelper, 'msf/core/rpc/json/dispatcher_helper'
|
||||||
|
autoload :Request, 'msf/core/rpc/json/request'
|
||||||
|
autoload :Response, 'msf/core/rpc/json/response'
|
||||||
autoload :RpcCommand, 'msf/core/rpc/json/rpc_command'
|
autoload :RpcCommand, 'msf/core/rpc/json/rpc_command'
|
||||||
autoload :RpcCommandFactory, 'msf/core/rpc/json/rpc_command_factory'
|
autoload :RpcCommandFactory, 'msf/core/rpc/json/rpc_command_factory'
|
||||||
|
|
||||||
# exception classes
|
# exception classes
|
||||||
|
# server
|
||||||
autoload :Error, 'msf/core/rpc/json/error'
|
autoload :Error, 'msf/core/rpc/json/error'
|
||||||
autoload :ParseError, 'msf/core/rpc/json/error'
|
autoload :ParseError, 'msf/core/rpc/json/error'
|
||||||
autoload :InvalidRequest, 'msf/core/rpc/json/error'
|
autoload :InvalidRequest, 'msf/core/rpc/json/error'
|
||||||
|
@ -31,5 +35,11 @@ module Msf::RPC
|
||||||
autoload :InternalError, 'msf/core/rpc/json/error'
|
autoload :InternalError, 'msf/core/rpc/json/error'
|
||||||
autoload :ServerError, 'msf/core/rpc/json/error'
|
autoload :ServerError, 'msf/core/rpc/json/error'
|
||||||
autoload :ApplicationServerError, 'msf/core/rpc/json/error'
|
autoload :ApplicationServerError, 'msf/core/rpc/json/error'
|
||||||
|
# client
|
||||||
|
autoload :ClientError, 'msf/core/rpc/json/error'
|
||||||
|
autoload :InvalidResponse, 'msf/core/rpc/json/error'
|
||||||
|
autoload :JSONParseError, 'msf/core/rpc/json/error'
|
||||||
|
autoload :ErrorResponse, 'msf/core/rpc/json/error'
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
require 'json'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
require 'msf/core/rpc'
|
||||||
|
|
||||||
|
module Msf::RPC::JSON
|
||||||
|
# JSON-RPC Client
|
||||||
|
# All client method call requests must be dispatched from within an
|
||||||
|
# EventMachine (reactor) run loop.
|
||||||
|
class Client
|
||||||
|
attr_reader :uri
|
||||||
|
attr_reader :api_token
|
||||||
|
attr_reader :symbolize_names
|
||||||
|
attr_accessor :namespace
|
||||||
|
|
||||||
|
# Instantiate a Client.
|
||||||
|
# @param uri [String] the JSON-RPC service URI
|
||||||
|
# @param api_token [String] the API token. Default: nil
|
||||||
|
# @param namespace [String] the namespace for the JSON-RPC method. The namespace will
|
||||||
|
# be prepended to the method name with a period separator. Default: nil
|
||||||
|
# @param symbolize_names [Boolean] If true, symbols are used for the names (keys) when
|
||||||
|
# processing JSON objects; otherwise, strings are used. Default: true
|
||||||
|
# @param private_key_file [String] the SSL private key file used for the HTTPS request. Default: nil
|
||||||
|
# @param cert_chain_file [String] the SSL cert chain file used for the HTTPS request. Default: nil
|
||||||
|
# @param verify_peer [Boolean] indicates whether a server should request a certificate
|
||||||
|
# from a peer, to be verified by user code. Default: nil
|
||||||
|
def initialize(uri, api_token: nil, namespace: nil, symbolize_names: true,
|
||||||
|
private_key_file: nil, cert_chain_file: nil, verify_peer: nil)
|
||||||
|
@uri = URI.parse(uri)
|
||||||
|
@api_token = api_token
|
||||||
|
@namespace = namespace
|
||||||
|
@symbolize_names = symbolize_names
|
||||||
|
@private_key_file = private_key_file
|
||||||
|
@cert_chain_file = cert_chain_file
|
||||||
|
@verify_peer = verify_peer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Invoked by Ruby when obj is sent a message it cannot handle, then processes
|
||||||
|
# the call as an RPC method invocation.
|
||||||
|
# @param symbol [Symbol] the symbol for the method called
|
||||||
|
# @param args [Array] any positional arguments passed to the method
|
||||||
|
# @param keyword_args [Hash] any keyword arguments passed to the method
|
||||||
|
# @returns [Msf::RPC::JSON::Request] an EM::Deferrable for the RPC method invocation.
|
||||||
|
def method_missing(symbol, *args, **keyword_args, &block)
|
||||||
|
# assemble method parameters
|
||||||
|
if !args.empty? && !keyword_args.empty?
|
||||||
|
params = args << keyword_args
|
||||||
|
elsif !args.empty?
|
||||||
|
params = args
|
||||||
|
elsif !keyword_args.empty?
|
||||||
|
params = keyword_args
|
||||||
|
else
|
||||||
|
params = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
process_call_async(symbol, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asynchronously processes the RPC method invocation.
|
||||||
|
# @param method [Symbol] the method
|
||||||
|
# @param params [Array, Hash] any arguments passed to the method
|
||||||
|
# @returns [Msf::RPC::JSON::Request] an EM::Deferrable for the RPC method invocation.
|
||||||
|
def process_call_async(method, params)
|
||||||
|
req = Request.new(@uri,
|
||||||
|
api_token: @api_token,
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
namespace: @namespace,
|
||||||
|
symbolize_names: @symbolize_names,
|
||||||
|
private_key_file: @private_key_file,
|
||||||
|
cert_chain_file: @cert_chain_file,
|
||||||
|
verify_peer: @verify_peer)
|
||||||
|
req.send
|
||||||
|
|
||||||
|
req
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -120,7 +120,7 @@ module Msf::RPC::JSON
|
||||||
|
|
||||||
# Validate the JSON-RPC request.
|
# Validate the JSON-RPC request.
|
||||||
# @param request [Hash] the JSON-RPC request
|
# @param request [Hash] the JSON-RPC request
|
||||||
# @returns [Boolean] true if the JSON-RPC request is a valid; otherwise, false.
|
# @returns [Boolean] true if the JSON-RPC request is valid; otherwise, false.
|
||||||
def validate_rpc_request(request)
|
def validate_rpc_request(request)
|
||||||
# validate request is an object
|
# validate request is an object
|
||||||
return false unless request.is_a?(Hash)
|
return false unless request.is_a?(Hash)
|
||||||
|
|
|
@ -133,4 +133,94 @@ module Msf::RPC::JSON
|
||||||
super(APPLICATION_SERVER_ERROR, ERROR_MESSAGES[APPLICATION_SERVER_ERROR] % {msg: message}, data: data)
|
super(APPLICATION_SERVER_ERROR, ERROR_MESSAGES[APPLICATION_SERVER_ERROR] % {msg: message}, data: data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Base class for all Msf::RPC::JSON client exceptions.
|
||||||
|
class ClientError < StandardError
|
||||||
|
attr_reader :response
|
||||||
|
|
||||||
|
# Instantiate a ClientError object.
|
||||||
|
#
|
||||||
|
# @param message [String] A String providing a short description of the error.
|
||||||
|
# @param response [Hash] A response hash. The default value is nil.
|
||||||
|
def initialize(message = nil, response: nil)
|
||||||
|
super(message)
|
||||||
|
@response = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class InvalidResponse < ClientError
|
||||||
|
# Instantiate an InvalidResponse object.
|
||||||
|
#
|
||||||
|
# @param message [String] A String providing a short description of the error.
|
||||||
|
# @param response [Hash] A response hash. The default value is nil.
|
||||||
|
def initialize(message = 'Invalid response from server', response: nil)
|
||||||
|
super(message, response: response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class JSONParseError < ClientError
|
||||||
|
# Instantiate an JSONParseError object.
|
||||||
|
#
|
||||||
|
# @param message [String] A String providing a short description of the error.
|
||||||
|
# @param response [Hash] A response hash. The default value is nil.
|
||||||
|
def initialize(message = 'Invalid JSON was received from the server', response: nil)
|
||||||
|
super(message, response: response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ErrorResponse < ClientError
|
||||||
|
attr_reader :id
|
||||||
|
attr_reader :code
|
||||||
|
attr_reader :message
|
||||||
|
attr_reader :data
|
||||||
|
|
||||||
|
# Parse response and return a new ErrorResponse instance.
|
||||||
|
# @param response [Hash] A response hash.
|
||||||
|
# @param symbolize_names [Boolean] If true, symbols are used for the names (keys) when
|
||||||
|
# processing JSON objects; otherwise, strings are used. Default: true
|
||||||
|
# @returns [ErrorResponse] ErrorResponse object that represents the response hash.
|
||||||
|
def self.parse(response, symbolize_names: true)
|
||||||
|
id_key = symbolize_names ? :id : :id.to_s
|
||||||
|
error_key = symbolize_names ? :error : :error.to_s
|
||||||
|
code_key = symbolize_names ? :code : :code.to_s
|
||||||
|
message_key = symbolize_names ? :message : :message.to_s
|
||||||
|
data_key = symbolize_names ? :data : :data.to_s
|
||||||
|
|
||||||
|
id = response[id_key]
|
||||||
|
error = response[error_key]
|
||||||
|
|
||||||
|
if !error.nil?
|
||||||
|
code = error[code_key]
|
||||||
|
message = error[message_key]
|
||||||
|
data = error[data_key]
|
||||||
|
else
|
||||||
|
code = nil
|
||||||
|
message = nil
|
||||||
|
data = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
ErrorResponse.new(id: id, code: code, message: message, data: data, response: response)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instantiate an ErrorResponse object.
|
||||||
|
#
|
||||||
|
# @param id [Integer, String, NilClass] It MUST be the same as the value of the
|
||||||
|
# id member in the Request Object. If there was an error in detecting the id
|
||||||
|
# in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.
|
||||||
|
# @param code [Integer] A Number that indicates the error type that occurred.
|
||||||
|
# @param message [String] A String providing a short description of the error.
|
||||||
|
# The message SHOULD be limited to a concise single sentence.
|
||||||
|
# @param data [Object] A Primitive or Structured value that contains additional
|
||||||
|
# information about the error. This may be omitted. The value of this member is
|
||||||
|
# defined by the Server (e.g. detailed error information, nested errors etc.).
|
||||||
|
# The default value is nil.
|
||||||
|
# @param response [Hash] A response hash. The default value is nil.
|
||||||
|
def initialize(id:, code:, message:, data: nil, response: nil)
|
||||||
|
super(message, response: response)
|
||||||
|
@id = id
|
||||||
|
@code = code
|
||||||
|
@message = message
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
require 'em-http-request'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
require 'msf/core/rpc'
|
||||||
|
|
||||||
|
module Msf::RPC::JSON
|
||||||
|
|
||||||
|
# Represents a JSON-RPC request. This is an EM::Deferrable class and instances
|
||||||
|
# respond to #callback and #errback to store callback actions.
|
||||||
|
class Request
|
||||||
|
include EM::Deferrable
|
||||||
|
|
||||||
|
JSON_MEDIA_TYPE = 'application/json'
|
||||||
|
JSON_RPC_VERSION = '2.0'
|
||||||
|
JSON_RPC_RESPONSE_REQUIRED_MEMBERS = %i(jsonrpc id)
|
||||||
|
JSON_RPC_RESPONSE_MEMBER_TYPES = {
|
||||||
|
# A String specifying the version of the JSON-RPC protocol.
|
||||||
|
jsonrpc: [String],
|
||||||
|
# An identifier established by the Client that MUST contain a String,
|
||||||
|
# Number, or NULL value if included. If it is not included it is assumed
|
||||||
|
# to be a notification. The value SHOULD normally not be Null [1] and
|
||||||
|
# Numbers SHOULD NOT contain fractional parts [2]
|
||||||
|
id: [Integer, String, NilClass],
|
||||||
|
}
|
||||||
|
JSON_RPC_ERROR_RESPONSE_REQUIRED_MEMBERS = %i(code message)
|
||||||
|
JSON_RPC_ERROR_RESPONSE_MEMBER_TYPES = {
|
||||||
|
# A Number that indicates the error type that occurred.
|
||||||
|
# This MUST be an integer.
|
||||||
|
code: [Integer],
|
||||||
|
# A String providing a short description of the error.
|
||||||
|
# The message SHOULD be limited to a concise single sentence.
|
||||||
|
message: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Instantiate a Request.
|
||||||
|
# @param uri [URI::HTTP] the JSON-RPC service URI
|
||||||
|
# @param api_token [String] the API token. Default: nil
|
||||||
|
# @param method [String] the JSON-RPC method name.
|
||||||
|
# @param params [Array, Hash] the JSON-RPC method parameters. Default: nil
|
||||||
|
# @param namespace [String] the namespace for the JSON-RPC method. The namespace will
|
||||||
|
# be prepended to the method name with a period separator. Default: nil
|
||||||
|
# @param symbolize_names [Boolean] If true, symbols are used for the names (keys) when
|
||||||
|
# processing JSON objects; otherwise, strings are used. Default: true
|
||||||
|
# @param is_notification [Boolean] If true, the request is created as a notification;
|
||||||
|
# otherwise, a standard request. Default: false
|
||||||
|
# @param private_key_file [String] the SSL private key file used for the HTTPS request. Default: nil
|
||||||
|
# @param cert_chain_file [String] the SSL cert chain file used for the HTTPS request. Default: nil
|
||||||
|
# @param verify_peer [Boolean] indicates whether a server should request a certificate
|
||||||
|
# from a peer, to be verified by user code. Default: nil
|
||||||
|
def initialize(uri, api_token: nil, method:, params: nil, namespace: nil,
|
||||||
|
symbolize_names: true, is_notification: false,
|
||||||
|
private_key_file: nil, cert_chain_file: nil, verify_peer: nil)
|
||||||
|
@uri = uri
|
||||||
|
@api_token = api_token
|
||||||
|
@namespace = namespace
|
||||||
|
@symbolize_names = symbolize_names
|
||||||
|
@is_notification = is_notification
|
||||||
|
@headers = {
|
||||||
|
'Accept': JSON_MEDIA_TYPE,
|
||||||
|
'Content-Type': JSON_MEDIA_TYPE,
|
||||||
|
'Authorization': "Bearer #{@api_token}"
|
||||||
|
}
|
||||||
|
|
||||||
|
absolute_method_name = @namespace.nil? ? method : "#{@namespace}.#{method}"
|
||||||
|
request_msg = {
|
||||||
|
jsonrpc: JSON_RPC_VERSION,
|
||||||
|
method: absolute_method_name
|
||||||
|
}
|
||||||
|
request_msg[:id] = Request.generate_id unless is_notification
|
||||||
|
request_msg[:params] = params unless params.nil?
|
||||||
|
|
||||||
|
@request_options = {
|
||||||
|
head: @headers,
|
||||||
|
body: request_msg.to_json
|
||||||
|
}
|
||||||
|
|
||||||
|
# add SSL options if specified
|
||||||
|
if !private_key_file.nil? || !cert_chain_file.nil? || verify_peer.is_a?(TrueClass) ||
|
||||||
|
verify_peer.is_a?(FalseClass)
|
||||||
|
ssl_options = {}
|
||||||
|
ssl_options[:private_key_file] = private_key_file unless private_key_file.nil?
|
||||||
|
ssl_options[:cert_chain_file] = cert_chain_file unless cert_chain_file.nil?
|
||||||
|
ssl_options[:verify_peer] = verify_peer if verify_peer.is_a?(TrueClass) || verify_peer.is_a?(FalseClass)
|
||||||
|
@request_options[:ssl] = ssl_options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends the JSON-RPC request using an EM::HttpRequest object, then validates and processes
|
||||||
|
# the JSON-RPC response.
|
||||||
|
def send
|
||||||
|
http = EM::HttpRequest.new(@uri).post(@request_options)
|
||||||
|
|
||||||
|
http.callback do
|
||||||
|
process(http.response)
|
||||||
|
end
|
||||||
|
|
||||||
|
http.errback do
|
||||||
|
fail(http.error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Process the JSON-RPC response.
|
||||||
|
# @param source [String] the JSON-RPC response
|
||||||
|
def process(source)
|
||||||
|
begin
|
||||||
|
response = JSON.parse(source, symbolize_names: @symbolize_names)
|
||||||
|
if response.is_a?(Array)
|
||||||
|
# process batch response
|
||||||
|
# TODO: implement batch response processing
|
||||||
|
fail("#{self.class.name}##{__method__} is not implemented for batch response")
|
||||||
|
else
|
||||||
|
process_response(response)
|
||||||
|
end
|
||||||
|
rescue JSON::ParserError
|
||||||
|
fail(JSONParseError.new(response: source))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Validate and process the JSON-RPC response.
|
||||||
|
# @param response [Hash] the JSON-RPC response
|
||||||
|
def process_response(response)
|
||||||
|
if !valid_rpc_response?(response)
|
||||||
|
fail(InvalidResponse.new(response: response))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
error_key = @symbolize_names ? :error : :error.to_s
|
||||||
|
if response.key?(error_key)
|
||||||
|
# process error response
|
||||||
|
fail(ErrorResponse.parse(response, symbolize_names: @symbolize_names))
|
||||||
|
else
|
||||||
|
# process successful response
|
||||||
|
succeed(Response.parse(response, symbolize_names: @symbolize_names))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate the JSON-RPC response.
|
||||||
|
# @param response [Hash] the JSON-RPC response
|
||||||
|
# @returns [Boolean] true if the JSON-RPC response is valid; otherwise, false.
|
||||||
|
def valid_rpc_response?(response)
|
||||||
|
# validate response is an object
|
||||||
|
return false unless response.is_a?(Hash)
|
||||||
|
|
||||||
|
JSON_RPC_RESPONSE_REQUIRED_MEMBERS.each do |member|
|
||||||
|
tmp_member = @symbolize_names ? member : member.to_s
|
||||||
|
return false unless response.key?(tmp_member)
|
||||||
|
end
|
||||||
|
|
||||||
|
# validate response members are correct types
|
||||||
|
response.each do |member, value|
|
||||||
|
tmp_member = @symbolize_names ? member : member.to_sym
|
||||||
|
return false if JSON_RPC_RESPONSE_MEMBER_TYPES.key?(tmp_member) &&
|
||||||
|
!JSON_RPC_RESPONSE_MEMBER_TYPES[tmp_member].one? { |type| value.is_a?(type) }
|
||||||
|
end
|
||||||
|
|
||||||
|
return false if response[:jsonrpc] != JSON_RPC_VERSION
|
||||||
|
|
||||||
|
result_key = @symbolize_names ? :result : :result.to_s
|
||||||
|
error_key = @symbolize_names ? :error : :error.to_s
|
||||||
|
|
||||||
|
return false if response.key?(result_key) && response.key?(error_key)
|
||||||
|
|
||||||
|
if response.key?(error_key)
|
||||||
|
error_response = response[error_key]
|
||||||
|
# validate error response is an object
|
||||||
|
return false unless error_response.is_a?(Hash)
|
||||||
|
|
||||||
|
JSON_RPC_ERROR_RESPONSE_REQUIRED_MEMBERS.each do |member|
|
||||||
|
tmp_member = @symbolize_names ? member : member.to_s
|
||||||
|
return false unless error_response.key?(tmp_member)
|
||||||
|
end
|
||||||
|
|
||||||
|
# validate error response members are correct types
|
||||||
|
error_response.each do |member, value|
|
||||||
|
tmp_member = @symbolize_names ? member : member.to_sym
|
||||||
|
return false if JSON_RPC_ERROR_RESPONSE_MEMBER_TYPES.key?(tmp_member) &&
|
||||||
|
!JSON_RPC_ERROR_RESPONSE_MEMBER_TYPES[tmp_member].one? { |type| value.is_a?(type) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generates a random id.
|
||||||
|
# @param n [Integer] Upper boundary for the random id.
|
||||||
|
# @return [Integer] A random id. If a positive integer is given for n,
|
||||||
|
# returns an integer: 0 <= id < n.
|
||||||
|
def self.generate_id(n = (2**(0.size * 8 - 1))-1)
|
||||||
|
SecureRandom.random_number(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represents a JSON-RPC Notification. This is an EM::Deferrable class and
|
||||||
|
# instances respond to #callback and #errback to store callback actions.
|
||||||
|
class Notification < Request
|
||||||
|
# Instantiate a Notification.
|
||||||
|
# @param uri [URI::HTTP] the JSON-RPC service URI
|
||||||
|
# @param api_token [String] the API token. Default: nil
|
||||||
|
# @param method [String] the JSON-RPC method name.
|
||||||
|
# @param params [Array, Hash] the JSON-RPC method parameters. Default: nil
|
||||||
|
# @param namespace [String] the namespace for the JSON-RPC method. The namespace will
|
||||||
|
# be prepended to the method name with a period separator. Default: nil
|
||||||
|
# @param symbolize_names [Boolean] If true, symbols are used for the names (keys) when
|
||||||
|
# processing JSON objects; otherwise, strings are used. Default: true
|
||||||
|
# @param private_key_file [String] the SSL private key file used for the HTTPS request. Default: nil
|
||||||
|
# @param cert_chain_file [String] the SSL cert chain file used for the HTTPS request. Default: nil
|
||||||
|
# @param verify_peer [Boolean] indicates whether a server should request a certificate
|
||||||
|
# from a peer, to be verified by user code. Default: nil
|
||||||
|
def initialize(uri, api_token: nil, method:, params: nil, namespace: nil,
|
||||||
|
symbolize_names: true, private_key_file: nil,
|
||||||
|
cert_chain_file: nil, verify_peer: nil)
|
||||||
|
super(uri,
|
||||||
|
api_token: api_token,
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
namespace: namespace,
|
||||||
|
symbolize_names: symbolize_names,
|
||||||
|
is_notification: true,
|
||||||
|
private_key_file: private_key_file,
|
||||||
|
cert_chain_file: cert_chain_file,
|
||||||
|
verify_peer: verify_peer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
module Msf::RPC::JSON
|
||||||
|
|
||||||
|
# Represents a JSON-RPC response.
|
||||||
|
class Response
|
||||||
|
attr_reader :response
|
||||||
|
attr_reader :id
|
||||||
|
attr_reader :result
|
||||||
|
|
||||||
|
# Parse response and return a new Response instance.
|
||||||
|
# @param response [Hash] A response hash.
|
||||||
|
# @param symbolize_names [Boolean] If true, symbols are used for the names (keys) when
|
||||||
|
# processing JSON objects; otherwise, strings are used. Default: true
|
||||||
|
# @returns [Response] Response object that represents the response hash.
|
||||||
|
def self.parse(response, symbolize_names: true)
|
||||||
|
id_key = symbolize_names ? :id : :id.to_s
|
||||||
|
result_key = symbolize_names ? :result : :result.to_s
|
||||||
|
|
||||||
|
id = response[id_key]
|
||||||
|
result = response[result_key]
|
||||||
|
|
||||||
|
Response.new(id: id, result: result, response: response)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instantiate a Response object.
|
||||||
|
#
|
||||||
|
# @param id [Integer, String, NilClass] It MUST be the same as the value of the
|
||||||
|
# id member in the Request Object. If there was an error in detecting the id
|
||||||
|
# in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.
|
||||||
|
# @param result [Integer, String, Array, Hash, NilClass] Result of the method.
|
||||||
|
# @param response [Hash] A response hash. The default value is nil.
|
||||||
|
def initialize(id:, result:, response: nil)
|
||||||
|
@id = id
|
||||||
|
@result = result
|
||||||
|
@response = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -105,6 +105,8 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_runtime_dependency 'sinatra'
|
spec.add_runtime_dependency 'sinatra'
|
||||||
spec.add_runtime_dependency 'sysrandom'
|
spec.add_runtime_dependency 'sysrandom'
|
||||||
spec.add_runtime_dependency 'warden'
|
spec.add_runtime_dependency 'warden'
|
||||||
|
# Required for JSON-RPC client
|
||||||
|
spec.add_runtime_dependency 'em-http-request'
|
||||||
# TimeZone info
|
# TimeZone info
|
||||||
spec.add_runtime_dependency 'tzinfo-data'
|
spec.add_runtime_dependency 'tzinfo-data'
|
||||||
# Gem for dealing with SSHKeys
|
# Gem for dealing with SSHKeys
|
||||||
|
|
Loading…
Reference in New Issue