Browse Source

Expire tokens, use std::shared_ptr for I2PControlSession.

pull/242/head
EinMByte 9 years ago
parent
commit
69d93146f2
  1. 46
      i2pcontrol/I2PControl.cpp
  2. 26
      i2pcontrol/I2PControl.h
  3. 9
      i2pcontrol/I2PControlServer.cpp
  4. 2
      i2pcontrol/I2PControlServer.h

46
i2pcontrol/I2PControl.cpp

@ -101,7 +101,8 @@ void I2PControlSession::Response::setId(const std::string& identifier)
} }
I2PControlSession::I2PControlSession(boost::asio::io_service& ios) 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 // Method handlers
methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate; 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; routerManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlSession::handleReseed;
} }
I2PControlSession::~I2PControlSession() void I2PControlSession::start()
{ {
stop(); startExpireTokensJob();
} }
void I2PControlSession::stop() void I2PControlSession::stop()
{ {
boost::system::error_code e; // Make sure this doesn't throw boost::system::error_code e; // Make sure this doesn't throw
shutdownTimer.cancel(e); shutdownTimer.cancel(e);
expireTokensTimer.cancel(e);
} }
I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream& request) I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream& request)
@ -176,10 +178,17 @@ bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response)
{ {
try { try {
std::string token = pt.get<std::string>(I2P_CONTROL_PARAM_TOKEN); std::string token = pt.get<std::string>(I2P_CONTROL_PARAM_TOKEN);
if(tokens.find(token) == tokens.end()) {
std::lock_guard<std::mutex> lock(tokensMutex);
auto it = tokens.find(token);
if(it == tokens.end()) {
response.setError(ErrorCode::NonexistantToken); response.setError(ErrorCode::NonexistantToken);
return false; 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) { } catch(const boost::property_tree::ptree_error& error) {
response.setError(ErrorCode::NoToken); response.setError(ErrorCode::NoToken);
return false; return false;
@ -219,7 +228,9 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res
const std::string token = generateToken(); const std::string token = generateToken();
response.setParam(I2P_CONTROL_PARAM_API, api); response.setParam(I2P_CONTROL_PARAM_API, api);
response.setParam(I2P_CONTROL_PARAM_TOKEN, token); response.setParam(I2P_CONTROL_PARAM_TOKEN, token);
tokens.insert(token);
std::lock_guard<std::mutex> lock(tokensMutex);
tokens.insert(std::make_pair(token, util::GetSecondsSinceEpoch()));
} }
void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response) void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response)
@ -367,5 +378,30 @@ void I2PControlSession::handleReseed(Response& response)
i2p::data::netdb.Reseed(); 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<std::mutex> 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
));
}
} }
} }

26
i2pcontrol/I2PControl.h

@ -4,14 +4,15 @@
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <string> #include <string>
#include <map> #include <map>
#include <set>
#include <functional> #include <functional>
#include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
namespace i2p { namespace i2p {
namespace client { namespace client {
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie"; 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_ID[] = "id";
const char I2P_CONTROL_PROPERTY_METHOD[] = "method"; 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. * "Null" I2P control implementation, does not do actual networking.
* @note authentication tokens are per-session * @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 * @warning an I2PControlSession must be destroyed before its io_service
*/ */
class I2PControlSession { class I2PControlSession : public std::enable_shared_from_this<I2PControlSession> {
public: public:
enum class ErrorCode { enum class ErrorCode {
@ -110,11 +112,16 @@ public:
*/ */
I2PControlSession(boost::asio::io_service& ios); 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. * 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(); void stop();
@ -143,6 +150,13 @@ private:
*/ */
std::string generateToken() const; std::string generateToken() const;
void startExpireTokensJob();
/**
* Expire tokens that are too old.
*/
void expireTokens(const boost::system::error_code& error);
// Method handlers // Method handlers
void handleAuthenticate(const PropertyTree& pt, Response& response); void handleAuthenticate(const PropertyTree& pt, Response& response);
void handleEcho(const PropertyTree& pt, Response& response); void handleEcho(const PropertyTree& pt, Response& response);
@ -168,7 +182,8 @@ private:
void handleReseed(Response& response); void handleReseed(Response& response);
std::string password; std::string password;
std::set<std::string> tokens; std::map<std::string, uint64_t> tokens;
std::mutex tokensMutex;
std::map<std::string, MethodHandler> methodHandlers; std::map<std::string, MethodHandler> methodHandlers;
std::map<std::string, RequestHandler> routerInfoHandlers; std::map<std::string, RequestHandler> routerInfoHandlers;
@ -177,6 +192,7 @@ private:
boost::asio::io_service& service; boost::asio::io_service& service;
boost::asio::deadline_timer shutdownTimer; boost::asio::deadline_timer shutdownTimer;
boost::asio::deadline_timer expireTokensTimer;
}; };
} }

9
i2pcontrol/I2PControlServer.cpp

@ -11,7 +11,8 @@ namespace i2p
namespace client namespace client
{ {
I2PControlService::I2PControlService(const std::string& address, int port) 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<I2PControlSession>(m_Service)),
m_IsRunning(false), m_Thread(nullptr),
m_Acceptor(m_Service, boost::asio::ip::tcp::endpoint( m_Acceptor(m_Service, boost::asio::ip::tcp::endpoint(
boost::asio::ip::address::from_string(address), port) boost::asio::ip::address::from_string(address), port)
) )
@ -29,6 +30,7 @@ namespace client
if (!m_IsRunning) if (!m_IsRunning)
{ {
Accept (); Accept ();
m_Session->start();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
} }
@ -40,8 +42,9 @@ namespace client
{ {
m_IsRunning = false; m_IsRunning = false;
m_Acceptor.cancel (); m_Acceptor.cancel ();
// Delete the session before the io_service is stopped and destroyed m_Session->stop();
delete m_Session; // Release ownership before the io_service is stopped and destroyed
m_Session.reset();
m_Service.stop (); m_Service.stop ();
if (m_Thread) if (m_Thread)
{ {

2
i2pcontrol/I2PControlServer.h

@ -48,7 +48,7 @@ private:
boost::asio::io_service m_Service; boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::tcp::acceptor m_Acceptor;
I2PControlSession* m_Session; std::shared_ptr<I2PControlSession> m_Session;
}; };
} }

Loading…
Cancel
Save