From 2058b43f5d39913c82d8f298f1605fe65cd0fce2 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 14 Oct 2026 11:55:08 -0400 Subject: [PATCH] torrents rpc authorization --- libi2pd_client/ClientContext.cpp | 9 ++++++- libi2pd_client/ClientContext.h | 3 +++ libi2pd_client/TorrentsRPC.cpp | 41 ++++++++++++++++++++++++++++++-- libi2pd_client/TorrentsRPC.h | 7 +++++- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index ab5be656..b638314e 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -1094,7 +1094,14 @@ namespace client if (server) { auto path = section.second.get (TORRENTS_TUNNEL_RPC_PATH, ""); - server->AddTunnel (path, torrentsTunnel); + auto auth = section.second.get (TORRENTS_TUNNEL_RPC_AUTH, false); + std::string user, pass; + if (auth) + { + user = section.second.get (TORRENTS_TUNNEL_RPC_USER, ""); + pass = section.second.get (TORRENTS_TUNNEL_RPC_PASS, ""); + } + server->AddTunnel (path, torrentsTunnel, auth, user, pass); } } } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index d946a3c6..543e92cb 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -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"; diff --git a/libi2pd_client/TorrentsRPC.cpp b/libi2pd_client/TorrentsRPC.cpp index a58e20f3..27b33560 100644 --- a/libi2pd_client/TorrentsRPC.cpp +++ b/libi2pd_client/TorrentsRPC.cpp @@ -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& req) { static const std::map > 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 tunnel) + void TorrentsRPCServer::AddTunnel (std::string_view path, std::shared_ptr 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 TorrentsRPCServer::GetTunnelEndpoint (std::shared_ptr tunnel) const @@ -1070,6 +1099,14 @@ namespace torrents return nullptr; } + std::pair 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 ()), diff --git a/libi2pd_client/TorrentsRPC.h b/libi2pd_client/TorrentsRPC.h index ac8c3263..19d32eb0 100644 --- a/libi2pd_client/TorrentsRPC.h +++ b/libi2pd_client/TorrentsRPC.h @@ -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& req); @@ -62,13 +63,16 @@ namespace torrents void Start (); void Stop (); - void AddTunnel (std::string_view path, std::shared_ptr tunnel); + void AddTunnel (std::string_view path, std::shared_ptr tunnel, + bool auth = false, const std::string& user = "", const std::string& pass = ""); std::shared_ptr GetTunnel (std::string_view path) const; + std::pair GetAuth (std::string_view path) const; // return (requered, auth string) std::pair GetTunnelEndpoint (std::shared_ptr 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::less<> > m_Tunnels; // path->tunnel + std::map > m_Auth; // path-> basic authorization string }; } }