mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-07 10:42:31 +00:00
187 lines
5.2 KiB
C++
187 lines
5.2 KiB
C++
#include "config.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include <torrent/exceptions.h>
|
|
#include <torrent/utils/string_manip.h>
|
|
|
|
#include "parse_commands.h"
|
|
#include "rpc/rpc_manager.h"
|
|
|
|
namespace rpc {
|
|
|
|
CommandMap commands;
|
|
RpcManager rpc;
|
|
ExecFile execFile;
|
|
|
|
// Trusted/untrusted XMLRPC connection model.
|
|
//
|
|
// The trust state is set per-request by the SCGI layer based on the
|
|
// UNTRUSTED_CONNECTION header. Commands without flag_untrusted_safe
|
|
// are blocked for untrusted connections. The check is in
|
|
// CommandMap::call_command(), which catches all command execution
|
|
// including nested calls through argument expansion.
|
|
|
|
bool
|
|
RpcManager::is_trusted() const {
|
|
return m_trusted;
|
|
}
|
|
|
|
void
|
|
RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target, std::function<void()>* deleter) {
|
|
if (!obj.is_string())
|
|
throw torrent::input_error("invalid parameters: target must be a string");
|
|
|
|
std::string target_string = obj.as_string();
|
|
bool require_index = (call_flags & (CommandMap::flag_tracker_target | CommandMap::flag_file_target));
|
|
|
|
if (target_string.size() == 0 && !require_index)
|
|
return;
|
|
|
|
// Length of SHA1 hash is 40
|
|
if (target_string.size() < 40)
|
|
throw torrent::input_error("invalid parameters: invalid target");
|
|
|
|
char type = 'd';
|
|
std::string hash;
|
|
std::string index;
|
|
|
|
const auto& delim_pos = target_string.find_first_of(':', 40);
|
|
|
|
if (delim_pos == target_string.npos || delim_pos + 2 >= target_string.size()) {
|
|
if (require_index) {
|
|
throw torrent::input_error("invalid parameters: no index");
|
|
}
|
|
|
|
hash = target_string;
|
|
|
|
} else {
|
|
hash = target_string.substr(0, delim_pos);
|
|
type = target_string[delim_pos + 1];
|
|
index = target_string.substr(delim_pos + 2);
|
|
}
|
|
|
|
core::Download* download = rpc.slot_find_download()(hash.c_str());
|
|
|
|
if (download == nullptr)
|
|
throw torrent::input_error("invalid parameters: info-hash not found");
|
|
|
|
try {
|
|
torrent::tracker::Tracker* tracker{};
|
|
|
|
switch (type) {
|
|
case 'd':
|
|
*target = rpc::make_target(download);
|
|
break;
|
|
|
|
case 'f':
|
|
*target = rpc::make_target(command_base::target_file,
|
|
rpc.slot_find_file()(download, std::stoi(std::string(index))));
|
|
|
|
break;
|
|
|
|
case 't':
|
|
tracker = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, std::stoi(std::string(index))));
|
|
|
|
*target = rpc::make_target(command_base::target_tracker, tracker);
|
|
*deleter = [tracker]() { delete tracker; };
|
|
break;
|
|
|
|
case 'p': {
|
|
if (index.size() != 40)
|
|
throw torrent::input_error("invalid parameters: target is not 40 bytes long");
|
|
|
|
torrent::HashString hash;
|
|
|
|
if (torrent::utils::transform_from_hex(index.c_str(), index.c_str() + 40, hash) != hash.end())
|
|
throw torrent::input_error("invalid parameters: target is not a hex string");
|
|
|
|
*target = rpc::make_target(command_base::target_peer, rpc.slot_find_peer()(download, hash));
|
|
|
|
break;
|
|
}
|
|
default:
|
|
throw torrent::input_error("invalid parameters: unexpected target type");
|
|
}
|
|
|
|
} catch (const std::logic_error&) {
|
|
throw torrent::input_error("invalid parameters: invalid index");
|
|
}
|
|
}
|
|
|
|
bool
|
|
RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
|
switch (type) {
|
|
case RPCType::XML:
|
|
if (!m_xmlrpc.is_valid() || !m_use_xmlrpc) {
|
|
const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></value></fault></methodResponse>";
|
|
return callback(response.c_str(), response.size());
|
|
}
|
|
|
|
return m_xmlrpc.process(in_buffer, length, callback);
|
|
|
|
case RPCType::JSON:
|
|
if (!m_use_jsonrpc) {
|
|
const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}";
|
|
return callback(response.c_str(), response.size());
|
|
}
|
|
|
|
return m_jsonrpc.process(in_buffer, length, callback);
|
|
|
|
default:
|
|
throw torrent::input_error("invalid parameters: unknown RPC type");
|
|
}
|
|
}
|
|
|
|
bool
|
|
RpcManager::process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
|
bool previous = m_trusted;
|
|
m_trusted = false;
|
|
|
|
try {
|
|
bool result = process(type, in_buffer, length, callback);
|
|
m_trusted = previous;
|
|
return result;
|
|
} catch (...) {
|
|
m_trusted = previous;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void
|
|
RpcManager::initialize_handlers() {
|
|
if (m_handlers_initialized)
|
|
throw torrent::internal_error("handlers already initialized");
|
|
|
|
m_xmlrpc.initialize();
|
|
m_jsonrpc.initialize();
|
|
|
|
m_handlers_initialized = true;
|
|
}
|
|
|
|
void
|
|
RpcManager::cleanup() {
|
|
m_handlers_initialized = false;
|
|
|
|
m_xmlrpc.cleanup();
|
|
m_jsonrpc.cleanup();
|
|
}
|
|
|
|
void
|
|
RpcManager::insert_command(const char* name, const char* parm, const char* doc) {
|
|
m_xmlrpc.insert_command(name, parm, doc);
|
|
m_jsonrpc.insert_command(name, parm, doc);
|
|
}
|
|
|
|
void
|
|
RpcManager::mark_safe(const std::string& key) {
|
|
auto itr = commands.find(key);
|
|
|
|
if (itr == commands.end())
|
|
return;
|
|
|
|
itr->second.m_flags |= CommandMap::flag_untrusted_safe;
|
|
}
|
|
|
|
} // namespace rpc
|