From 69d93146f2f7d7fb5ac0b80caaae4f4f475b564d Mon Sep 17 00:00:00 2001 From: EinMByte Date: Sun, 2 Aug 2015 16:32:54 +0200 Subject: [PATCH] Expire tokens, use std::shared_ptr for I2PControlSession. --- i2pcontrol/I2PControl.cpp | 46 +++++++++++++++++++++++++++++---- i2pcontrol/I2PControl.h | 26 +++++++++++++++---- i2pcontrol/I2PControlServer.cpp | 9 ++++--- i2pcontrol/I2PControlServer.h | 2 +- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/i2pcontrol/I2PControl.cpp b/i2pcontrol/I2PControl.cpp index e3bdfb87..071dd60a 100644 --- a/i2pcontrol/I2PControl.cpp +++ b/i2pcontrol/I2PControl.cpp @@ -101,7 +101,8 @@ void I2PControlSession::Response::setId(const std::string& identifier) } I2PControlSession::I2PControlSession(boost::asio::io_service& ios) - : password(I2P_CONTROL_DEFAULT_PASSWORD), tokens(), service(ios), shutdownTimer(ios) + : password(I2P_CONTROL_DEFAULT_PASSWORD), tokens(), tokensMutex(), + service(ios), shutdownTimer(ios), expireTokensTimer(ios) { // Method handlers methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate; @@ -127,15 +128,16 @@ I2PControlSession::I2PControlSession(boost::asio::io_service& ios) routerManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlSession::handleReseed; } -I2PControlSession::~I2PControlSession() +void I2PControlSession::start() { - stop(); + startExpireTokensJob(); } void I2PControlSession::stop() { boost::system::error_code e; // Make sure this doesn't throw shutdownTimer.cancel(e); + expireTokensTimer.cancel(e); } I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream& request) @@ -176,10 +178,17 @@ bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response) { try { std::string token = pt.get(I2P_CONTROL_PARAM_TOKEN); - if(tokens.find(token) == tokens.end()) { + + std::lock_guard lock(tokensMutex); + auto it = tokens.find(token); + if(it == tokens.end()) { response.setError(ErrorCode::NonexistantToken); return false; + } else if(util::GetSecondsSinceEpoch() - it->second > I2P_CONTROL_TOKEN_LIFETIME) { + response.setError(ErrorCode::ExpiredToken); + return false; } + } catch(const boost::property_tree::ptree_error& error) { response.setError(ErrorCode::NoToken); return false; @@ -219,7 +228,9 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res const std::string token = generateToken(); response.setParam(I2P_CONTROL_PARAM_API, api); response.setParam(I2P_CONTROL_PARAM_TOKEN, token); - tokens.insert(token); + + std::lock_guard lock(tokensMutex); + tokens.insert(std::make_pair(token, util::GetSecondsSinceEpoch())); } void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response) @@ -367,5 +378,30 @@ void I2PControlSession::handleReseed(Response& response) i2p::data::netdb.Reseed(); } +void I2PControlSession::expireTokens(const boost::system::error_code& error) +{ + if(error == boost::asio::error::operation_aborted) + return; // Do not restart timer, shutting down + + startExpireTokensJob(); + LogPrint(eLogDebug, "I2PControl is expiring tokens."); + const uint64_t now = util::GetSecondsSinceEpoch(); + std::lock_guard lock(tokensMutex); + for(auto it = tokens.begin(); it != tokens.end(); ) { + if(now - it->second > I2P_CONTROL_TOKEN_LIFETIME) + it = tokens.erase(it); + else + ++it; + } +} + +void I2PControlSession::startExpireTokensJob() +{ + expireTokensTimer.expires_from_now(boost::posix_time::seconds(I2P_CONTROL_TOKEN_LIFETIME)); + expireTokensTimer.async_wait(std::bind( + &I2PControlSession::expireTokens, shared_from_this(), std::placeholders::_1 + )); +} + } } diff --git a/i2pcontrol/I2PControl.h b/i2pcontrol/I2PControl.h index 3f3b52be..ef423bd2 100644 --- a/i2pcontrol/I2PControl.h +++ b/i2pcontrol/I2PControl.h @@ -4,14 +4,15 @@ #include #include #include -#include #include +#include #include namespace i2p { namespace client { const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie"; +const uint64_t I2P_CONTROL_TOKEN_LIFETIME = 600; // Token lifetime in seconds const char I2P_CONTROL_PROPERTY_ID[] = "id"; const char I2P_CONTROL_PROPERTY_METHOD[] = "method"; @@ -57,9 +58,10 @@ const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed"; /** * "Null" I2P control implementation, does not do actual networking. * @note authentication tokens are per-session + * @note I2PControlSession must always be used as a std::shared_ptr * @warning an I2PControlSession must be destroyed before its io_service */ -class I2PControlSession { +class I2PControlSession : public std::enable_shared_from_this { public: enum class ErrorCode { @@ -110,11 +112,16 @@ public: */ I2PControlSession(boost::asio::io_service& ios); - ~I2PControlSession(); + /** + * Starts the I2PControlSession. + * In essence, this starts the expireTokensTimer. + * @note should always be called after construction + */ + void start(); /** * Cancels all operations that are waiting. - * @note must not be called before destruction, destructor handles this + * @note it's a good idea to call this before destruction (shared_ptr reset) */ void stop(); @@ -143,6 +150,13 @@ private: */ std::string generateToken() const; + void startExpireTokensJob(); + + /** + * Expire tokens that are too old. + */ + void expireTokens(const boost::system::error_code& error); + // Method handlers void handleAuthenticate(const PropertyTree& pt, Response& response); void handleEcho(const PropertyTree& pt, Response& response); @@ -168,7 +182,8 @@ private: void handleReseed(Response& response); std::string password; - std::set tokens; + std::map tokens; + std::mutex tokensMutex; std::map methodHandlers; std::map routerInfoHandlers; @@ -177,6 +192,7 @@ private: boost::asio::io_service& service; boost::asio::deadline_timer shutdownTimer; + boost::asio::deadline_timer expireTokensTimer; }; } diff --git a/i2pcontrol/I2PControlServer.cpp b/i2pcontrol/I2PControlServer.cpp index 119e3835..79b599be 100644 --- a/i2pcontrol/I2PControlServer.cpp +++ b/i2pcontrol/I2PControlServer.cpp @@ -11,7 +11,8 @@ namespace i2p namespace client { I2PControlService::I2PControlService(const std::string& address, int port) - : m_Session(new I2PControlSession(m_Service)), m_IsRunning(false), m_Thread(nullptr), + : m_Session(std::make_shared(m_Service)), + m_IsRunning(false), m_Thread(nullptr), m_Acceptor(m_Service, boost::asio::ip::tcp::endpoint( boost::asio::ip::address::from_string(address), port) ) @@ -29,6 +30,7 @@ namespace client if (!m_IsRunning) { Accept (); + m_Session->start(); m_IsRunning = true; m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); } @@ -40,8 +42,9 @@ namespace client { m_IsRunning = false; m_Acceptor.cancel (); - // Delete the session before the io_service is stopped and destroyed - delete m_Session; + m_Session->stop(); + // Release ownership before the io_service is stopped and destroyed + m_Session.reset(); m_Service.stop (); if (m_Thread) { diff --git a/i2pcontrol/I2PControlServer.h b/i2pcontrol/I2PControlServer.h index eaffb213..8f3a3782 100644 --- a/i2pcontrol/I2PControlServer.h +++ b/i2pcontrol/I2PControlServer.h @@ -48,7 +48,7 @@ private: boost::asio::io_service m_Service; boost::asio::ip::tcp::acceptor m_Acceptor; - I2PControlSession* m_Session; + std::shared_ptr m_Session; }; }