diff --git a/ClientContext.cpp b/ClientContext.cpp index df0de4e5..db1c8e6a 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -114,6 +114,24 @@ namespace client } } + // I2CP + bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp); + if (i2cp) + { + std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); + uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); + LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort); + try + { + m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort); + m_I2CPServer->Start (); + } + catch (std::exception& e) + { + LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); + } + } + m_AddressBook.StartResolvers (); } @@ -165,6 +183,14 @@ namespace client m_BOBCommandChannel = nullptr; } + if (m_I2CPServer) + { + LogPrint(eLogInfo, "Clients: stopping I2CP"); + m_I2CPServer->Stop (); + delete m_I2CPServer; + m_I2CPServer = nullptr; + } + LogPrint(eLogInfo, "Clients: stopping AddressBook"); m_AddressBook.Stop (); for (auto it: m_Destinations) diff --git a/Config.cpp b/Config.cpp index 44dec286..d9b4091d 100644 --- a/Config.cpp +++ b/Config.cpp @@ -178,6 +178,13 @@ namespace config { ("bob.port", value()->default_value(2827), "BOB listen port") ; + options_description i2cp("I2CP options"); + bob.add_options() + ("i2cp.enabled", value()->default_value(false), "Enable or disable I2CP") + ("i2cp.address", value()->default_value("127.0.0.1"), "I2CP listen address") + ("i2cp.port", value()->default_value(7654), "I2CP listen port") + ; + options_description i2pcontrol("I2PControl options"); i2pcontrol.add_options() ("i2pcontrol.enabled", value()->default_value(false), "Enable or disable I2P Control Protocol") @@ -207,6 +214,7 @@ namespace config { .add(socksproxy) .add(sam) .add(bob) + .add(i2cp) .add(i2pcontrol) .add(precomputation) ; diff --git a/I2CP.cpp b/I2CP.cpp index 8d0bedf1..94d5db7f 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -61,9 +61,9 @@ namespace client I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): m_Owner (owner), m_Socket (socket), - m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0), - m_SessionID (0) + m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0) { + RAND_bytes ((uint8_t *)&m_SessionID, 2); ReadProtocolByte (); } @@ -72,6 +72,10 @@ namespace client delete[] m_NextMessage; } + void I2CPSession::Close () + { + } + void I2CPSession::ReadProtocolByte () { if (m_Socket) @@ -148,6 +152,12 @@ namespace client void I2CPSession::Terminate () { + if (m_Destination) + { + m_Destination->Stop (); + m_Destination = nullptr; + } + m_Owner.RemoveSession (GetSessionID ()); } void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) @@ -212,7 +222,7 @@ namespace client if (identity->Verify (buf, offset, buf + offset)) // signature { m_Destination = std::make_shared(*this, identity, false); - RAND_bytes ((uint8_t *)&m_SessionID, 2); + m_Destination->Start (); SendSessionStatusMessage (1); // created } else @@ -264,7 +274,9 @@ namespace client LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); } - I2CPServer::I2CPServer (const std::string& interface, int port) + I2CPServer::I2CPServer (const std::string& interface, int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) { memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; @@ -272,6 +284,84 @@ namespace client m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; } + + I2CPServer::~I2CPServer () + { + if (m_IsRunning) + Stop (); + } + + void I2CPServer::Start () + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&I2CPServer::Run, this)); + } + + void I2CPServer::Stop () + { + m_IsRunning = false; + m_Acceptor.cancel (); + for (auto it: m_Sessions) + it.second->Close (); + m_Sessions.clear (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void I2CPServer::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ()); + } + } + } + + void I2CPServer::Accept () + { + auto newSocket = std::make_shared (m_Service); + m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void I2CPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (!ecode && socket) + { + boost::system::error_code ec; + auto ep = socket->remote_endpoint (ec); + if (!ec) + { + LogPrint (eLogDebug, "I2CP: new connection from ", ep); + auto session = std::make_shared(*this, socket); + m_Sessions[session->GetSessionID ()] = session; + } + else + LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ()); + } + else + LogPrint (eLogError, "I2CP: accept error: ", ecode.message ()); + + if (ecode != boost::asio::error::operation_aborted) + Accept (); + } + + void I2CPServer::RemoveSession (uint16_t sessionID) + { + m_Sessions.erase (sessionID); + } } } diff --git a/I2CP.h b/I2CP.h index fca3317d..74ccb3f2 100644 --- a/I2CP.h +++ b/I2CP.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include "Destination.h" @@ -72,6 +74,7 @@ namespace client ~I2CPSession (); uint16_t GetSessionID () const { return m_SessionID; }; + void Close (); void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); // message handlers @@ -111,10 +114,30 @@ namespace client public: I2CPServer (const std::string& interface, int port); + ~I2CPServer (); + + void Start (); + void Stop (); + boost::asio::io_service& GetService () { return m_Service; }; + + void RemoveSession (uint16_t sessionID); + + private: + + void Run (); + + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); private: I2CPMessageHandler m_MessagesHandlers[256]; + std::map > m_Sessions; + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; public: