Class: Msf::MCP::RpcManager
- Inherits:
-
Object
- Object
- Msf::MCP::RpcManager
- Defined in:
- lib/msf/core/mcp/rpc_manager.rb
Overview
Manages the lifecycle of a Metasploit RPC server process.
Probes the configured RPC port, auto-starts the server via Process.spawn of msfrpcd, and cleans up the child process on shutdown.
Constant Summary collapse
- LOCALHOST_HOSTS =
%w[localhost 127.0.0.1 ::1].freeze
- DEFAULT_WAIT_TIMEOUT =
30- DEFAULT_WAIT_INTERVAL =
1- STOP_GRACE_PERIOD =
5
Instance Attribute Summary collapse
-
#rpc_pid ⇒ Object
readonly
Returns the value of attribute rpc_pid.
Instance Method Summary collapse
-
#auto_start_enabled? ⇒ Boolean
Whether auto-start is enabled based on config, API type, and host.
-
#ensure_rpc_available ⇒ void
Ensure an RPC server is available, auto-starting if needed.
-
#initialize(config:, output:) ⇒ RpcManager
constructor
A new instance of RpcManager.
-
#rpc_available? ⇒ Boolean
Probe the configured RPC port to check if a server is listening.
-
#rpc_managed? ⇒ Boolean
Whether this manager started and is managing an RPC server process.
-
#start_rpc_server ⇒ void
Start the Metasploit RPC server by spawning msfrpcd.
-
#stop_rpc_server ⇒ void
Stop the managed RPC server process.
-
#wait_for_rpc(timeout: DEFAULT_WAIT_TIMEOUT, interval: DEFAULT_WAIT_INTERVAL) ⇒ true
Wait for the RPC server to become available.
Constructor Details
#initialize(config:, output:) ⇒ RpcManager
Returns a new instance of RpcManager.
21 22 23 24 25 26 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 21 def initialize(config:, output:) @config = config @output = output @rpc_pid = nil @rpc_managed = false end |
Instance Attribute Details
#rpc_pid ⇒ Object (readonly)
Returns the value of attribute rpc_pid.
17 18 19 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 17 def rpc_pid @rpc_pid end |
Instance Method Details
#auto_start_enabled? ⇒ Boolean
Whether auto-start is enabled based on config, API type, and host.
Auto-start is only supported for:
-
MessagePack API type (not JSON-RPC)
-
Localhost connections (cannot start a remote RPC server)
-
When auto_start_rpc config is not explicitly false
62 63 64 65 66 67 68 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 62 def auto_start_enabled? return false if @config[:msf_api][:type] != 'messagepack' return false unless localhost? return false if @config[:msf_api][:auto_start_rpc] == false true end |
#ensure_rpc_available ⇒ void
This method returns an undefined value.
Ensure an RPC server is available, auto-starting if needed.
When the RPC server is already listening, verifies that credentials (or a token for JSON-RPC) are available for the caller to authenticate.
When the server is not available, auto-start is attempted only for MessagePack on localhost with auto_start_rpc enabled. Random credentials are generated when none are provided.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 169 def ensure_rpc_available if rpc_available? @output.puts 'Metasploit RPC server is already running' validate_credentials_for_existing_server! return end if @config[:msf_api][:type] == 'json-rpc' raise Msf::MCP::Metasploit::RpcStartupError, 'RPC server is not running and auto-start is not supported for JSON-RPC API type.' end unless localhost? = "RPC server is not available at #{@config[:msf_api][:host]}:#{@config[:msf_api][:port]}." << ' Cannot auto-start RPC on remote hosts. Please start the RPC server manually.' if auto_start_enabled? raise Msf::MCP::Metasploit::RpcStartupError, end unless auto_start_enabled? raise Msf::MCP::Metasploit::RpcStartupError, "RPC server is not running on #{@config[:msf_api][:host]}:#{@config[:msf_api][:port]} " \ 'and auto-start is disabled.' end generate_random_credentials unless credentials_provided? start_rpc_server wait_for_rpc end |
#rpc_available? ⇒ Boolean
Probe the configured RPC port to check if a server is listening.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 38 def rpc_available? host = @config[:msf_api][:host] port = @config[:msf_api][:port] socket = Rex::Socket::Tcp.create( 'PeerHost' => host, 'PeerPort' => port ) socket.close dlog({ message: "RPC server is available at #{Rex::Socket.(host, port)}" }, LOG_SOURCE, LOG_DEBUG) true rescue Rex::ConnectionError false end |
#rpc_managed? ⇒ Boolean
Whether this manager started and is managing an RPC server process.
31 32 33 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 31 def rpc_managed? @rpc_managed end |
#start_rpc_server ⇒ void
This method returns an undefined value.
Start the Metasploit RPC server by spawning msfrpcd.
Credentials are passed via environment variables to avoid exposing them on the command line.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 77 def start_rpc_server if @rpc_managed @output.puts 'RPC server is already managed by this process' return end @output.puts 'Starting Metasploit RPC server...' ilog({ message: 'Starting Metasploit RPC server' }, LOG_SOURCE, LOG_INFO) unless File.executable?(MSFRPCD_PATH) raise Msf::MCP::Metasploit::RpcStartupError, 'msfrpcd executable not found. Cannot auto-start RPC server.' end args = build_msfrpcd_args env = { 'MSF_RPC_USER' => @config[:msf_api][:user].to_s, 'MSF_RPC_PASS' => @config[:msf_api][:password].to_s } pid = Process.spawn(env, MSFRPCD_PATH, *args, %i[out err] => File::NULL) @rpc_pid = pid @rpc_managed = true @output.puts "RPC server started via msfrpcd (PID: #{pid})" end |
#stop_rpc_server ⇒ void
This method returns an undefined value.
Stop the managed RPC server process.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 136 def stop_rpc_server return unless @rpc_managed @output.puts 'Stopping managed RPC server...' ilog({ message: "Stopping managed RPC server (PID: #{@rpc_pid})" }, LOG_SOURCE, LOG_INFO) begin Process.kill('TERM', @rpc_pid) graceful_wait rescue Errno::ESRCH # Process already dead — that's fine rescue Errno::EPERM @output.puts "Warning: no permission to stop RPC process #{@rpc_pid}" end @rpc_pid = nil @rpc_managed = false end |
#wait_for_rpc(timeout: DEFAULT_WAIT_TIMEOUT, interval: DEFAULT_WAIT_INTERVAL) ⇒ true
Wait for the RPC server to become available.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/msf/core/mcp/rpc_manager.rb', line 112 def wait_for_rpc(timeout: DEFAULT_WAIT_TIMEOUT, interval: DEFAULT_WAIT_INTERVAL) deadline = Time.now + timeout loop do if rpc_available? @output.puts 'RPC server is ready' return true end check_managed_process_alive! if @rpc_managed if Time.now >= deadline raise Msf::MCP::Metasploit::ConnectionError, "Timed out waiting for RPC server after #{timeout} seconds" end @output.puts 'Waiting for RPC server to become available...' sleep(interval) end end |