Browse Source

Merge pull request #695 from majestrate/merge-websocket

add optional websocket ui
pull/697/merge
orignal 8 years ago committed by GitHub
parent
commit
4dce35b1e6
  1. 2
      ClientContext.cpp
  2. 2
      ClientContext.h
  3. 9
      Config.cpp
  4. 34
      Daemon.cpp
  5. 30
      Event.cpp
  6. 36
      Event.h
  7. 1
      I2NPProtocol.cpp
  8. 4
      Log.h
  9. 6
      Makefile
  10. 4
      NTCPSession.cpp
  11. 5
      SSUData.cpp
  12. 2
      TransportSession.h
  13. 44
      Transports.cpp
  14. 24
      Tunnel.cpp
  15. 51
      Tunnel.h
  16. 2
      TunnelBase.h
  17. 10
      TunnelPool.cpp
  18. 137
      Websocket.cpp
  19. 28
      Websocket.h
  20. 11
      build/CMakeLists.txt
  21. 1
      docs/building/requirements.md
  22. 3
      docs/building/unix.md
  23. 2
      filelist.mk

2
ClientContext.cpp

@ -290,7 +290,7 @@ namespace client
} }
return infos; return infos;
} }
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {

2
ClientContext.h

@ -68,7 +68,7 @@ namespace client
const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination); std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
private: private:
void ReadTunnels (); void ReadTunnels ();

9
Config.cpp

@ -179,6 +179,12 @@ namespace config {
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers") ("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?"); ("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
m_OptionsDesc m_OptionsDesc
.add(general) .add(general)
.add(limits) .add(limits)
@ -193,7 +199,8 @@ namespace config {
.add(precomputation) .add(precomputation)
.add(reseed) .add(reseed)
.add(addressbook) .add(addressbook)
.add(trust) .add(trust)
.add(websocket)
; ;
} }

34
Daemon.cpp

@ -25,6 +25,9 @@
#include "UPnP.h" #include "UPnP.h"
#include "util.h" #include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p namespace i2p
{ {
namespace util namespace util
@ -38,6 +41,9 @@ namespace i2p
std::unique_ptr<i2p::http::HTTPServer> httpServer; std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService; std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
std::unique_ptr<i2p::transport::UPnP> UPnP; std::unique_ptr<i2p::transport::UPnP> UPnP;
#ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
#endif
}; };
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {} Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
@ -290,12 +296,27 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start (); d.m_I2PControlService->Start ();
} }
#ifdef WITH_EVENTS
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
if(websocket) {
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
d.m_WebsocketServer->Start();
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
}
#endif
return true; return true;
} }
bool Daemon_Singleton::stop() bool Daemon_Singleton::stop()
{ {
#ifdef WITH_EVENTS
i2p::event::core.SetListener(nullptr);
#endif
LogPrint(eLogInfo, "Daemon: shutting down"); LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client"); LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop(); i2p::client::context.Stop();
@ -321,10 +342,17 @@ namespace i2p
LogPrint(eLogInfo, "Daemon: stopping I2PControl"); LogPrint(eLogInfo, "Daemon: stopping I2PControl");
d.m_I2PControlService->Stop (); d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr; d.m_I2PControlService = nullptr;
} }
#ifdef WITH_EVENTS
if (d.m_WebsocketServer) {
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
d.m_WebsocketServer->Stop();
d.m_WebsocketServer = nullptr;
}
#endif
i2p::crypto::TerminateCrypto (); i2p::crypto::TerminateCrypto ();
return true; return true;
} }
} }
} }

30
Event.cpp

@ -0,0 +1,30 @@
#include "Event.h"
#include "Log.h"
namespace i2p
{
namespace event
{
EventCore core;
void EventCore::SetListener(EventListener * l)
{
m_listener = l;
LogPrint(eLogInfo, "Event: listener set");
}
void EventCore::QueueEvent(const EventType & ev)
{
if(m_listener)
m_listener->HandleEvent(ev);
}
}
}
void EmitEvent(const EventType & e)
{
#ifdef WITH_EVENTS
i2p::event::core.QueueEvent(e);
#endif
}

36
Event.h

@ -0,0 +1,36 @@
#ifndef EVENT_H__
#define EVENT_H__
#include <map>
#include <string>
#include <memory>
#include <boost/asio.hpp>
typedef std::map<std::string, std::string> EventType;
namespace i2p
{
namespace event
{
class EventListener {
public:
virtual ~EventListener() {};
virtual void HandleEvent(const EventType & ev) = 0;
};
class EventCore
{
public:
void QueueEvent(const EventType & ev);
void SetListener(EventListener * l);
private:
EventListener * m_listener = nullptr;
};
extern EventCore core;
}
}
void EmitEvent(const EventType & ev);
#endif

1
I2NPProtocol.cpp

@ -547,7 +547,6 @@ namespace i2p
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + I2NP_HEADER_SIZE; uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID) switch (typeID)

4
Log.h

@ -152,13 +152,13 @@ namespace log {
std::string text; /**< message text as single string */ std::string text; /**< message text as single string */
LogLevel level; /**< message level */ LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */ std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {}; LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
}; };
Log & Logger(); Log & Logger();
} // log } // log
} // i2p }
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue> template<typename TValue>

6
Makefile

@ -14,6 +14,12 @@ USE_STATIC := no
USE_MESHNET := no USE_MESHNET := no
USE_UPNP := no USE_UPNP := no
ifeq ($(WEBSOCKETS),1)
NEEDED_CXXFLAGS += -DWITH_EVENTS
DAEMON_SRC += Websocket.cpp
endif
ifeq ($(UNAME),Darwin) ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp DAEMON_SRC += DaemonLinux.cpp
ifeq ($(HOMEBREW),1) ifeq ($(HOMEBREW),1)

4
NTCPSession.cpp

@ -11,6 +11,7 @@
#include "Transports.h" #include "Transports.h"
#include "NetDb.h" #include "NetDb.h"
#include "NTCPSession.h" #include "NTCPSession.h"
#include "Event.h"
using namespace i2p::crypto; using namespace i2p::crypto;
@ -604,7 +605,10 @@ namespace transport
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4)) if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
{ {
if (!m_NextMessage->IsExpired ()) if (!m_NextMessage->IsExpired ())
{
EmitEvent({{"type", "transport.recvmsg"} , {"ident", GetIdentHashBase64()}, {"number", "1"}});
m_Handler.PutNextMessage (m_NextMessage); m_Handler.PutNextMessage (m_NextMessage);
}
else else
LogPrint (eLogInfo, "NTCP: message expired"); LogPrint (eLogInfo, "NTCP: message expired");
} }

5
SSUData.cpp

@ -5,6 +5,7 @@
#include "NetDb.h" #include "NetDb.h"
#include "SSU.h" #include "SSU.h"
#include "SSUData.h" #include "SSUData.h"
#include "Event.h"
namespace i2p namespace i2p
{ {
@ -234,8 +235,10 @@ namespace transport
{ {
m_ReceivedMessages.insert (msgID); m_ReceivedMessages.insert (msgID);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ()) if (!msg->IsExpired ()) {
EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}});
m_Handler.PutNextMessage (msg); m_Handler.PutNextMessage (msg);
}
else else
LogPrint (eLogDebug, "SSU: message expired"); LogPrint (eLogDebug, "SSU: message expired");
} }

2
TransportSession.h

@ -64,6 +64,8 @@ namespace transport
virtual ~TransportSession () {}; virtual ~TransportSession () {};
virtual void Done () = 0; virtual void Done () = 0;
std::string GetIdentHashBase64() const { return m_RemoteIdentity->GetIdentHash().ToBase64(); }
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; }; std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; }; void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };

44
Transports.cpp

@ -5,6 +5,7 @@
#include "NetDb.h" #include "NetDb.h"
#include "Transports.h" #include "Transports.h"
#include "Config.h" #include "Config.h"
#include "Event.h"
using namespace i2p::data; using namespace i2p::data;
@ -230,6 +231,7 @@ namespace transport
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs) void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{ {
EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}});
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
} }
@ -504,10 +506,9 @@ namespace transport
} }
if (m_SSUServer) if (m_SSUServer)
{ {
bool nat; i2p::config::GetOption("nat", nat); bool nat; i2p::config::GetOption("nat", nat);
if (nat) if (nat)
i2p::context.SetStatus (eRouterStatusTesting); i2p::context.SetStatus (eRouterStatusTesting);
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
auto router = i2p::data::netdb.GetRandomPeerTestRouter (); auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
@ -569,6 +570,7 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
bool sendDatabaseStore = true; bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0) if (it->second.delayedMessages.size () > 0)
{ {
@ -594,20 +596,22 @@ namespace transport
session->Done(); session->Done();
return; return;
} }
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} })); m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
} }
}); });
} }
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session) void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{ {
m_Service.post([session, this]() m_Service.post([session, this]()
{ {
auto remoteIdentity = session->GetRemoteIdentity (); auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return; if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash (); auto ident = remoteIdentity->GetIdentHash ();
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
@ -671,13 +675,13 @@ namespace transport
std::advance (it, rand () % m_Peers.size ()); std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr; return it != m_Peers.end () ? it->second.router : nullptr;
} }
void Transports::RestrictRoutesToFamilies(std::set<std::string> families) void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
{ {
std::lock_guard<std::mutex> lock(m_FamilyMutex); std::lock_guard<std::mutex> lock(m_FamilyMutex);
m_TrustedFamilies.clear(); m_TrustedFamilies.clear();
for ( const auto& fam : families ) for ( const auto& fam : families )
m_TrustedFamilies.push_back(fam); m_TrustedFamilies.push_back(fam);
} }
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers) void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
{ {
@ -687,14 +691,14 @@ namespace transport
m_TrustedRouters.push_back(ri); m_TrustedRouters.push_back(ri);
} }
bool Transports::RoutesRestricted() const { bool Transports::RoutesRestricted() const {
std::unique_lock<std::mutex> famlock(m_FamilyMutex); std::unique_lock<std::mutex> famlock(m_FamilyMutex);
std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex); std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0; return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
} }
/** XXX: if routes are not restricted this dies */ /** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
{ {
{ {
std::lock_guard<std::mutex> l(m_FamilyMutex); std::lock_guard<std::mutex> l(m_FamilyMutex);
@ -727,7 +731,7 @@ namespace transport
} }
} }
return nullptr; return nullptr;
} }
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
{ {

24
Tunnel.cpp

@ -11,6 +11,7 @@
#include "Transports.h" #include "Transports.h"
#include "NetDb.h" #include "NetDb.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "TunnelPool.h"
namespace i2p namespace i2p
{ {
@ -29,12 +30,14 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel) void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
#ifdef WITH_EVENTS
std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64();
#endif
auto numHops = m_Config->GetNumHops (); auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
auto msg = NewI2NPShortMessage (); auto msg = NewI2NPShortMessage ();
*msg->GetPayload () = numRecords; *msg->GetPayload () = numRecords;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
// shuffle records // shuffle records
std::vector<int> recordIndicies; std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
@ -55,8 +58,14 @@ namespace tunnel
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID); hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
hop->recordIndex = idx; hop->recordIndex = idx;
i++; i++;
#ifdef WITH_EVENTS
peers += ":" + hop->ident->GetIdentHash().ToBase64();
#endif
hop = hop->next; hop = hop->next;
} }
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.build", this, peers);
#endif
// fill up fake records with random data // fill up fake records with random data
for (int i = numHops; i < numRecords; i++) for (int i = numHops; i < numRecords; i++)
{ {
@ -182,6 +191,13 @@ namespace tunnel
return ret; return ret;
} }
void Tunnel::SetState(TunnelState state)
{
m_State = state;
EmitTunnelEvent("tunnel.state", this, state);
}
void Tunnel::PrintHops (std::stringstream& s) const void Tunnel::PrintHops (std::stringstream& s) const
{ {
// hops are in inverted order, we must print in direct order // hops are in inverted order, we must print in direct order
@ -582,6 +598,7 @@ namespace tunnel
hop = hop->next; hop = hop->next;
} }
} }
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
// delete // delete
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; m_NumFailedTunnelCreations++;
@ -591,6 +608,9 @@ namespace tunnel
break; break;
case eTunnelStateBuildFailed: case eTunnelStateBuildFailed:
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted"); LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; m_NumFailedTunnelCreations++;
break; break;
@ -776,7 +796,7 @@ namespace tunnel
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel) std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
if (config) if (config)
return CreateTunnel<InboundTunnel>(config, outboundTunnel); return CreateTunnel<InboundTunnel>(config, outboundTunnel);
else else
return CreateZeroHopsInboundTunnel (); return CreateZeroHopsInboundTunnel ();

51
Tunnel.h

@ -19,11 +19,49 @@
#include "TunnelGateway.h" #include "TunnelGateway.h"
#include "TunnelBase.h" #include "TunnelBase.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Event.h"
namespace i2p namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, const TunnelT & t)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}});
#else
(void) ev;
(void) t;
#endif
}
template<typename TunnelT, typename T>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
@ -40,7 +78,7 @@ namespace tunnel
eTunnelStateFailed, eTunnelStateFailed,
eTunnelStateExpiring eTunnelStateExpiring
}; };
class OutboundTunnel; class OutboundTunnel;
class InboundTunnel; class InboundTunnel;
class Tunnel: public TunnelBase class Tunnel: public TunnelBase
@ -62,12 +100,13 @@ namespace tunnel
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
TunnelState GetState () const { return m_State; }; TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; }; void SetState (TunnelState state);
bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; }; bool IsRecreated () const { return m_IsRecreated; };
void SetIsRecreated () { m_IsRecreated = true; }; void SetIsRecreated () { m_IsRecreated = true; };
virtual bool IsInbound() const = 0;
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; }; std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; }; void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
@ -107,6 +146,8 @@ namespace tunnel
// implements TunnelBase // implements TunnelBase
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg); void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
bool IsInbound() const { return false; }
private: private:
@ -123,7 +164,7 @@ namespace tunnel
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg); void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const; void Print (std::stringstream& s) const;
bool IsInbound() const { return true; }
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;

2
TunnelBase.h

@ -48,7 +48,7 @@ namespace tunnel
uint32_t GetCreationTime () const { return m_CreationTime; }; uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; }; void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private: private:
uint32_t m_TunnelID, m_NextTunnelID; uint32_t m_TunnelID, m_NextTunnelID;

10
TunnelPool.cpp

@ -7,12 +7,16 @@
#include "Garlic.h" #include "Garlic.h"
#include "Transports.h" #include "Transports.h"
#include "Log.h" #include "Log.h"
#include "Tunnel.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "Destination.h"
#include "Event.h"
namespace i2p namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
@ -67,6 +71,7 @@ namespace tunnel
{ {
if (!m_IsActive) return; if (!m_IsActive) return;
{ {
EmitTunnelEvent("tunnels.created", createdTunnel);
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel); m_InboundTunnels.insert (createdTunnel);
} }
@ -77,7 +82,8 @@ namespace tunnel
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel) void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{ {
if (expiredTunnel) if (expiredTunnel)
{ {
EmitTunnelEvent("tunnels.expired", expiredTunnel);
expiredTunnel->SetTunnelPool (nullptr); expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests) for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr; if (it.second.second == expiredTunnel) it.second.second = nullptr;
@ -91,6 +97,7 @@ namespace tunnel
{ {
if (!m_IsActive) return; if (!m_IsActive) return;
{ {
EmitTunnelEvent("tunnels.created", createdTunnel);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel); m_OutboundTunnels.insert (createdTunnel);
} }
@ -101,6 +108,7 @@ namespace tunnel
{ {
if (expiredTunnel) if (expiredTunnel)
{ {
EmitTunnelEvent("tunnels.expired", expiredTunnel);
expiredTunnel->SetTunnelPool (nullptr); expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests) for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr; if (it.second.first == expiredTunnel) it.second.first = nullptr;

137
Websocket.cpp

@ -0,0 +1,137 @@
#include "Websocket.h"
#include "Log.h"
#include <set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <boost/property_tree/ini_parser.hpp>
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include <stdexcept>
namespace i2p
{
namespace event
{
typedef websocketpp::server<websocketpp::config::asio> ServerImpl;
typedef websocketpp::connection_hdl ServerConn;
class WebsocketServerImpl : public EventListener
{
private:
typedef ServerImpl::message_ptr MessagePtr;
public:
WebsocketServerImpl(const std::string & addr, int port) : m_run(false), m_thread(nullptr)
{
m_server.init_asio();
m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1));
m_server.set_close_handler(std::bind(&WebsocketServerImpl::ConnClosed, this, std::placeholders::_1));
m_server.set_message_handler(std::bind(&WebsocketServerImpl::OnConnMessage, this, std::placeholders::_1, std::placeholders::_2));
m_server.listen(boost::asio::ip::address::from_string(addr), port);
}
~WebsocketServerImpl()
{
}
void Start() {
m_run = true;
m_server.start_accept();
m_thread = new std::thread([&] () {
while(m_run) {
try {
m_server.run();
} catch (std::exception & e ) {
LogPrint(eLogError, "Websocket server: ", e.what());
}
}
});
}
void Stop() {
m_run = false;
m_server.stop();
if(m_thread) {
m_thread->join();
delete m_thread;
}
m_thread = nullptr;
}
void ConnOpened(ServerConn c)
{
std::lock_guard<std::mutex> lock(m_connsMutex);
m_conns.insert(c);
}
void ConnClosed(ServerConn c)
{
std::lock_guard<std::mutex> lock(m_connsMutex);
m_conns.erase(c);
}
void OnConnMessage(ServerConn conn, ServerImpl::message_ptr msg)
{
(void) conn;
(void) msg;
}
void HandleEvent(const EventType & ev)
{
std::lock_guard<std::mutex> lock(m_connsMutex);
LogPrint(eLogDebug, "websocket event");
boost::property_tree::ptree event;
for (const auto & item : ev) {
event.put(item.first, item.second);
}
std::ostringstream ss;
write_json(ss, event);
std::string s = ss.str();
ConnList::iterator it;
for (it = m_conns.begin(); it != m_conns.end(); ++it) {
ServerImpl::connection_ptr con = m_server.get_con_from_hdl(*it);
con->send(s);
}
}
private:
typedef std::set<ServerConn, std::owner_less<ServerConn> > ConnList;
bool m_run;
std::thread * m_thread;
std::mutex m_connsMutex;
ConnList m_conns;
ServerImpl m_server;
};
WebsocketServer::WebsocketServer(const std::string & addr, int port) : m_impl(new WebsocketServerImpl(addr, port)) {}
WebsocketServer::~WebsocketServer()
{
delete m_impl;
}
void WebsocketServer::Start()
{
m_impl->Start();
}
void WebsocketServer::Stop()
{
m_impl->Stop();
}
EventListener * WebsocketServer::ToListener()
{
return m_impl;
}
}
}

28
Websocket.h

@ -0,0 +1,28 @@
#ifndef WEBSOCKET_H__
#define WEBSOCKET_H__
#include "Event.h"
namespace i2p
{
namespace event
{
class WebsocketServerImpl;
class WebsocketServer
{
public:
WebsocketServer(const std::string & addr, int port);
~WebsocketServer();
void Start();
void Stop();
EventListener * ToListener();
private:
WebsocketServerImpl * m_impl;
};
}
}
#endif

11
build/CMakeLists.txt

@ -19,6 +19,7 @@ option(WITH_MESHNET "Build for cjdns test network" OFF)
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
option(WITH_I2LUA "Build for i2lua" OFF) option(WITH_I2LUA "Build for i2lua" OFF)
option(WITH_WEBSOCKETS "Build with websocket ui" OFF)
# paths # paths
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" )
@ -59,8 +60,14 @@ set (LIBI2PD_SRC
"${CMAKE_SOURCE_DIR}/Signature.cpp" "${CMAKE_SOURCE_DIR}/Signature.cpp"
"${CMAKE_SOURCE_DIR}/Timestamp.cpp" "${CMAKE_SOURCE_DIR}/Timestamp.cpp"
"${CMAKE_SOURCE_DIR}/api.cpp" "${CMAKE_SOURCE_DIR}/api.cpp"
"${CMAKE_SOURCE_DIR}/Event.cpp"
) )
if (WITH_WEBSOCKETS)
add_definitions(-DWITH_EVENTS)
find_package(websocketpp REQUIRED)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS) if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS)
list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp") list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp")
endif () endif ()
@ -92,6 +99,9 @@ set (CLIENT_SRC
"${CMAKE_SOURCE_DIR}/I2CP.cpp" "${CMAKE_SOURCE_DIR}/I2CP.cpp"
) )
if(WITH_WEBSOCKETS)
list (APPEND CLIENT_SRC "${CMAKE_SOURCE_DIR}/Websocket.cpp")
endif ()
add_library(i2pdclient ${CLIENT_SRC}) add_library(i2pdclient ${CLIENT_SRC})
set (DAEMON_SRC set (DAEMON_SRC
@ -367,6 +377,7 @@ message(STATUS " MESHNET : ${WITH_MESHNET}")
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
message(STATUS " THEADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS " THEADSANITIZER : ${WITH_THREADSANITIZER}")
message(STATUS " I2LUA : ${WITH_I2LUA}") message(STATUS " I2LUA : ${WITH_I2LUA}")
message(STATUS " WEBSOCKETS : ${WITH_WEBSOCKETS}")
message(STATUS "---------------------------------------") message(STATUS "---------------------------------------")
#Handle paths nicely #Handle paths nicely

1
docs/building/requirements.md

@ -12,3 +12,4 @@ Optional tools:
* cmake >= 2.8 (or 3.3+ if you want to use precompiled headers on windows) * cmake >= 2.8 (or 3.3+ if you want to use precompiled headers on windows)
* miniupnp library (for upnp support) * miniupnp library (for upnp support)
* [websocketpp](https://github.com/zaphoyd/websocketpp/) (for websocket ui)

3
docs/building/unix.md

@ -46,6 +46,9 @@ Available CMake options(each option has a form of `<key>=<value>`, for more info
* `WITH_AESNI` build with AES-NI support (ON/OFF) * `WITH_AESNI` build with AES-NI support (ON/OFF)
* `WITH_HARDENING` enable hardening features (ON/OFF) (gcc only) * `WITH_HARDENING` enable hardening features (ON/OFF) (gcc only)
* `WITH_PCH` use pre-compiled header (experimental, speeds up build) * `WITH_PCH` use pre-compiled header (experimental, speeds up build)
* `WITH_I2LUA` used when building i2lua
* `WITH_WEBSOCKETS` enable websocket server
Also there is `-L` flag for CMake that could be used to list current cached options: Also there is `-L` flag for CMake that could be used to list current cached options:

2
filelist.mk

@ -5,7 +5,7 @@ LIB_SRC = \
SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \ Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \ Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \
Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Event.cpp
LIB_CLIENT_SRC = \ LIB_CLIENT_SRC = \
AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \

Loading…
Cancel
Save