torrents rpc authorization

This commit is contained in:
orignal committed 2026-10-14 11:55:08 -04:00
1 parent 4cd981f88e
commit 2058b43f5d
4 files changed
+56 -4

No files matched your search

+8 -1
View File
@@ -1094,7 +1094,14 @@ namespace client
if (server)
{
auto path = section.second.get<std::string> (TORRENTS_TUNNEL_RPC_PATH, "");
server->AddTunnel (path, torrentsTunnel);
auto auth = section.second.get<bool> (TORRENTS_TUNNEL_RPC_AUTH, false);
std::string user, pass;
if (auth)
{
user = section.second.get<std::string> (TORRENTS_TUNNEL_RPC_USER, "");
pass = section.second.get<std::string> (TORRENTS_TUNNEL_RPC_PASS, "");
}
server->AddTunnel (path, torrentsTunnel, auth, user, pass);
}
}
}
+3
View File
@@ -81,6 +81,9 @@ namespace client
const char TORRENTS_TUNNEL_RPC_PORT[] = "rpcport";
const char TORRENTS_TUNNEL_RPC_PATH[] = "rpcpath";
const char TORRENTS_TUNNEL_RPC_ADDRESS[] = "rpcaddress";
const char TORRENTS_TUNNEL_RPC_AUTH[] = "rpcauth";
const char TORRENTS_TUNNEL_RPC_USER[] = "rpcuser";
const char TORRENTS_TUNNEL_RPC_PASS[] = "rpcpass";
const char TORRENTS_TUNNEL_DHT[] = "dht";
const char TORRENTS_TUNNEL_WEB[] = "web";
+39 -2
View File
@@ -904,6 +904,21 @@ namespace torrents
});
}
void TorrentsRPCSession::SendUnauthorizedResponse ()
{
m_Response.version (11); // HTTP/1.1
m_Response.result (boost::beast::http::status::unauthorized);
m_Response.set (boost::beast::http::field::server, "i2pd torrents RPC");
m_Response.set (boost::beast::http::field::www_authenticate, "Basic realm=\"Transmission\"");
m_Response.prepare_payload ();
boost::beast::http::async_write (m_Socket, m_Response,
[s = shared_from_this ()](const boost::system::error_code& ecode, size_t bytes_transferred)
{
if (ecode)
LogPrint (eLogWarning, "TorrentsRPC: Failed to send response: ", ecode.message ());
});
}
void TorrentsRPCSession::HandleWeb (const boost::beast::http::request<boost::beast::http::string_body>& req)
{
static const std::map<std::string_view, std::pair<std::string_view, bool> > contentTypes =
@@ -940,7 +955,8 @@ namespace torrents
subUrlPath++;
if (subUrlPath != urlPath.end ())
{
auto tunnel = m_Server.GetTunnel (std::string ("/") + subUrlPath->string () + "/rpc");
std::string tunnelPath { "/" + subUrlPath->string () + "/rpc" };
auto tunnel = m_Server.GetTunnel (tunnelPath);
if (tunnel && tunnel->SupportsWeb ())
{
subUrlPath++;
@@ -948,6 +964,12 @@ namespace torrents
isPathValid = *subUrlPath == "web";
if (isPathValid)
{
auto [authRequired, authString] = m_Server.GetAuth (tunnelPath);
if (authRequired && req[boost::beast::http::field::authorization] != authString)
{
SendUnauthorizedResponse ();
return;
}
subUrlPath++;
fileSubUrl = std::accumulate (subUrlPath, urlPath.end (), std::filesystem::path{}, std::divides{});
}
@@ -1005,6 +1027,7 @@ namespace torrents
m_WebResponse->set (boost::beast::http::field::server, "i2pd torrents web");
if (!contentType.empty ())
m_WebResponse->set (boost::beast::http::field::content_type, contentType);
m_WebResponse->content_length (body.size());
m_WebResponse->body () = std::move (body);
boost::beast::http::async_write (m_Socket, *m_WebResponse,
[s = shared_from_this ()](const boost::system::error_code& ecode, size_t bytes_transferred)
@@ -1038,7 +1061,8 @@ namespace torrents
}
}
void TorrentsRPCServer::AddTunnel (std::string_view path, std::shared_ptr<TorrentsTunnel> tunnel)
void TorrentsRPCServer::AddTunnel (std::string_view path, std::shared_ptr<TorrentsTunnel> tunnel,
bool auth, const std::string& user, const std::string& pass)
{
std::string rpcPath ("/");
if (!path.empty ())
@@ -1046,8 +1070,13 @@ namespace torrents
// insert both pathes rpc and rpc/
rpcPath += "rpc";
m_Tunnels.emplace (rpcPath, tunnel);
if (auth)
m_Auth.emplace (rpcPath, i2p::http::CreateBasicAuthorizationString (user, pass));
rpcPath += "/";
m_Tunnels.emplace (rpcPath, tunnel);
if (auth)
m_Auth.emplace (rpcPath, i2p::http::CreateBasicAuthorizationString (user, pass));
}
std::pair<boost::asio::ip::tcp::endpoint, std::string> TorrentsRPCServer::GetTunnelEndpoint (std::shared_ptr<TorrentsTunnel> tunnel) const
@@ -1070,6 +1099,14 @@ namespace torrents
return nullptr;
}
std::pair<bool, std::string> TorrentsRPCServer::GetAuth (std::string_view path) const
{
auto it = m_Auth.find (path);
if (it == m_Auth.end ())
return { false, "" };
return { true, it->second };
}
void TorrentsRPCServer::Accept ()
{
m_Acceptor.async_accept (boost::asio::make_strand (GetService ()),
+6 -1
View File
@@ -39,6 +39,7 @@ namespace torrents
void HandleRequest (const boost::system::error_code& ecode, size_t bytes_transferred);
void SendResponse (boost::beast::http::status result, std::string_view data = "", bool isOptions = false);
void SendUnauthorizedResponse ();
void HandleWeb (const boost::beast::http::request<boost::beast::http::string_body>& req);
@@ -62,13 +63,16 @@ namespace torrents
void Start ();
void Stop ();
void AddTunnel (std::string_view path, std::shared_ptr<TorrentsTunnel> tunnel);
void AddTunnel (std::string_view path, std::shared_ptr<TorrentsTunnel> tunnel,
bool auth = false, const std::string& user = "", const std::string& pass = "");
std::shared_ptr<TorrentsTunnel> GetTunnel (std::string_view path) const;
std::pair<bool, std::string> GetAuth (std::string_view path) const; // return (requered, auth string)
std::pair<boost::asio::ip::tcp::endpoint, std::string> GetTunnelEndpoint (std::shared_ptr<TorrentsTunnel> tunnel) const;
const auto& GetTunnels() const { return m_Tunnels; }
const auto& GetAcceptor() const { return m_Acceptor; }
private:
void Accept ();
@@ -77,6 +81,7 @@ namespace torrents
boost::asio::ip::tcp::acceptor m_Acceptor;
std::map<std::string, std::weak_ptr<TorrentsTunnel>, std::less<> > m_Tunnels; // path->tunnel
std::map<std::string, std::string, std::less<> > m_Auth; // path-> basic authorization string
};
}
}