diff --git a/ClientContext.cpp b/ClientContext.cpp index 3edafd88..d516def8 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -10,20 +10,21 @@ #include "ClientContext.h" #include "SOCKS.h" #include "WebSocks.h" +#include "MatchedDestination.h" namespace i2p { namespace client { - ClientContext context; + ClientContext context; ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), - m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), + m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr) { } - - ClientContext::~ClientContext () + + ClientContext::~ClientContext () { delete m_HttpProxy; delete m_SocksProxy; @@ -31,20 +32,20 @@ namespace client delete m_BOBCommandChannel; delete m_I2CPServer; } - + void ClientContext::Start () { if (!m_SharedLocalDestination) - { + { m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination; m_SharedLocalDestination->Start (); } - - m_AddressBook.Start (); - - std::shared_ptr localDestination; + + m_AddressBook.Start (); + + std::shared_ptr localDestination; bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy); if (httproxy) { std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); @@ -60,7 +61,7 @@ namespace client std::map params; ReadI2CPOptionsFromConfig ("httpproxy.", params); localDestination = CreateNewLocalDestination (keys, false, ¶ms); - } + } else LogPrint(eLogError, "Clients: failed to load HTTP Proxy key"); } @@ -71,10 +72,10 @@ namespace client LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what()); } } - + localDestination = nullptr; bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); - if (socksproxy) + if (socksproxy) { std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); @@ -104,8 +105,8 @@ namespace client } // I2P tunnels - ReadTunnels (); - + ReadTunnels (); + // SAM bool sam; i2p::config::GetOption("sam.enabled", sam); if (sam) { @@ -118,7 +119,7 @@ namespace client } catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); } - } + } // BOB bool bob; i2p::config::GetOption("bob.enabled", bob); @@ -132,27 +133,27 @@ namespace client } catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what()); } - } + } // I2CP bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp); - if (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 + try { m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort); m_I2CPServer->Start (); - } - catch (std::exception& e) + } + catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); } - } + } - m_AddressBook.StartResolvers (); + m_AddressBook.StartResolvers (); // start UDP cleanup if (!m_ServerForwards.empty ()) @@ -161,7 +162,7 @@ namespace client ScheduleCleanupUDP(); } } - + void ClientContext::Stop () { if (m_HttpProxy) @@ -185,28 +186,28 @@ namespace client LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first); it.second->Stop (); } - m_ClientTunnels.clear (); + m_ClientTunnels.clear (); for (auto& it: m_ServerTunnels) { LogPrint(eLogInfo, "Clients: stopping I2P server tunnel"); it.second->Stop (); } - m_ServerTunnels.clear (); + m_ServerTunnels.clear (); if (m_SamBridge) { LogPrint(eLogInfo, "Clients: stopping SAM bridge"); m_SamBridge->Stop (); - delete m_SamBridge; + delete m_SamBridge; m_SamBridge = nullptr; - } + } if (m_BOBCommandChannel) { LogPrint(eLogInfo, "Clients: stopping BOB command channel"); m_BOBCommandChannel->Stop (); - delete m_BOBCommandChannel; + delete m_BOBCommandChannel; m_BOBCommandChannel = nullptr; } @@ -214,7 +215,7 @@ namespace client { LogPrint(eLogInfo, "Clients: stopping I2CP"); m_I2CPServer->Stop (); - delete m_I2CPServer; + delete m_I2CPServer; m_I2CPServer = nullptr; } @@ -226,18 +227,18 @@ namespace client m_ServerForwards.clear(); m_ClientForwards.clear(); } - + if (m_CleanupUDPTimer) { - m_CleanupUDPTimer->cancel (); - m_CleanupUDPTimer = nullptr; + m_CleanupUDPTimer->cancel (); + m_CleanupUDPTimer = nullptr; } for (auto& it: m_Destinations) it.second->Stop (); m_Destinations.clear (); m_SharedLocalDestination = nullptr; - } + } void ClientContext::ReloadConfig () { @@ -246,14 +247,14 @@ namespace client Stop(); Start(); } - + bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType) { bool success = true; std::string fullPath = i2p::fs::DataDirPath (filename); std::ifstream s(fullPath, std::ifstream::binary); - if (s.is_open ()) - { + if (s.is_open ()) + { s.seekg (0, std::ios::end); size_t len = s.tellg(); s.seekg (0, std::ios::beg); @@ -267,18 +268,18 @@ namespace client else LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded"); delete[] buf; - } + } else { LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType); - keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); + keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); size_t len = keys.GetFullLen (); uint8_t * buf = new uint8_t[len]; len = keys.ToBuffer (buf, len); f.write ((char *)buf, len); delete[] buf; - + LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); } return success; @@ -306,9 +307,9 @@ namespace client } return infos; } - + std::shared_ptr ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, - const std::map * params) + const std::map * params) { i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); auto localDestination = std::make_shared (keys, isPublic, params); @@ -318,6 +319,16 @@ namespace client return localDestination; } + std::shared_ptr ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params) + { + MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params); + auto localDestination = std::shared_ptr(cl); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[localDestination->GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } + void ClientContext::DeleteLocalDestination (std::shared_ptr destination) { if (!destination) return; @@ -328,7 +339,7 @@ namespace client { std::unique_lock l(m_DestinationsMutex); m_Destinations.erase (it); - } + } d->Stop (); } } @@ -341,32 +352,32 @@ namespace client { LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); if (!it->second->IsRunning ()) - { + { it->second->Start (); return it->second; - } + } return nullptr; - } + } auto localDestination = std::make_shared (keys, isPublic, params); std::unique_lock l(m_DestinationsMutex); m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination; localDestination->Start (); return localDestination; } - + std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const { auto it = m_Destinations.find (destination); if (it != m_Destinations.end ()) return it->second; return nullptr; - } + } template std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const { return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); - } + } template void ClientContext::ReadI2CPOptions (const Section& section, std::map& options) const @@ -378,24 +389,24 @@ namespace client options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); - } + } void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const { - std::string value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value)) + std::string value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value)) options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) + options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; - } + } void ClientContext::ReadTunnels () { @@ -411,20 +422,20 @@ namespace client } } LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf); - try + try { boost::property_tree::read_ini (tunConf, pt); - } - catch (std::exception& ex) + } + catch (std::exception& ex) { LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ()); return; } - + int numClientTunnels = 0, numServerTunnels = 0; for (auto& section: pt) { - std::string name = section.first; + std::string name = section.first; try { std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); @@ -440,13 +451,14 @@ namespace client dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); // optional params + bool matchTunnels = section.second.get(I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, ""); std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); // I2CP - std::map options; - ReadI2CPOptions (section, options); + std::map options; + ReadI2CPOptions (section, options); std::shared_ptr localDestination = nullptr; if (keys.length () > 0) @@ -456,9 +468,15 @@ namespace client { localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); if (!localDestination) - localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); + { + if(matchTunnels) + localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); + else + localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); + } } } + if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // udp client // TODO: hostnames @@ -515,7 +533,7 @@ namespace client || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) - { + { // mandatory params std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); int port = section.second.get (I2P_SERVER_TUNNEL_PORT); @@ -532,15 +550,15 @@ namespace client bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); // I2CP - std::map options; - ReadI2CPOptions (section, options); + std::map options; + ReadI2CPOptions (section, options); std::shared_ptr localDestination = nullptr; i2p::data::PrivateKeys k; if(!LoadPrivateKeys (k, keys, sigType)) continue; localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) + if (!localDestination) localDestination = CreateNewLocalDestination (k, true, &options); if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { @@ -549,7 +567,7 @@ namespace client auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port); - if(!isUniqueLocal) + if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); serverTunnel->SetUniqueLocal(isUniqueLocal); @@ -566,10 +584,10 @@ namespace client } else LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists"); - + continue; } - + I2PServerTunnel * serverTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip); @@ -594,7 +612,7 @@ namespace client { comma = accessList.find (',', pos); i2p::data::IdentHash ident; - ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); + ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); idents.insert (ident); pos = comma + 1; } @@ -602,7 +620,7 @@ namespace client serverTunnel->SetAccessList (idents); } if (m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), + std::make_pair (localDestination->GetIdentHash (), inPort), std::unique_ptr(serverTunnel))).second) { serverTunnel->Start (); @@ -610,21 +628,21 @@ namespace client } else LogPrint (eLogError, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); - + } else LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf); - + } catch (std::exception& ex) { LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ()); } - } + } LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created"); LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); } - + void ClientContext::ScheduleCleanupUDP() { if (m_CleanupUDPTimer) @@ -644,5 +662,5 @@ namespace client ScheduleCleanupUDP(); } } -} -} +} +} diff --git a/ClientContext.h b/ClientContext.h index 1aa1e7f0..5fade60c 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -34,19 +34,21 @@ namespace client const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; - const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; - const char I2P_SERVER_TUNNEL_HOST[] = "host"; - const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; + const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; + const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; + const char I2P_SERVER_TUNNEL_HOST[] = "host"; + const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; const char I2P_SERVER_TUNNEL_PORT[] = "port"; const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; - const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; + const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; + class ClientContext { public: @@ -62,10 +64,11 @@ namespace client std::shared_ptr GetSharedLocalDestination () const { return m_SharedLocalDestination; }; std::shared_ptr CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1, const std::map * params = nullptr); // transient - std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); + std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); + std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params = nullptr); void DeleteLocalDestination (std::shared_ptr destination); - std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; + std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); AddressBook& GetAddressBook () { return m_AddressBook; }; @@ -81,16 +84,16 @@ namespace client std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const; template void ReadI2CPOptions (const Section& section, std::map& options) const; - void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const; + void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const; void CleanupUDP(const boost::system::error_code & ecode); void ScheduleCleanupUDP(); - + private: std::mutex m_DestinationsMutex; std::map > m_Destinations; - std::shared_ptr m_SharedLocalDestination; + std::shared_ptr m_SharedLocalDestination; AddressBook m_AddressBook; @@ -99,16 +102,16 @@ namespace client std::map > m_ClientTunnels; // local endpoint->tunnel std::map, std::unique_ptr > m_ServerTunnels; // ->tunnel - std::mutex m_ForwardsMutex; + std::mutex m_ForwardsMutex; std::map > m_ClientForwards; // local endpoint -> udp tunnel std::map, std::unique_ptr > m_ServerForwards; // -> udp tunnel - + SAMBridge * m_SamBridge; BOBCommandChannel * m_BOBCommandChannel; I2CPServer * m_I2CPServer; std::unique_ptr m_CleanupUDPTimer; - + public: // for HTTP const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; @@ -119,9 +122,9 @@ namespace client const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; } const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; } }; - - extern ClientContext context; -} -} + + extern ClientContext context; +} +} #endif diff --git a/Datagram.h b/Datagram.h index b317d101..0ed31707 100644 --- a/Datagram.h +++ b/Datagram.h @@ -19,7 +19,7 @@ namespace client } namespace datagram { - // milliseconds for max session idle time + // milliseconds for max session idle time const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000; // milliseconds for how long we try sticking to a dead routing path before trying to switch const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000; @@ -33,14 +33,14 @@ namespace datagram const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; // max 64 messages buffered in send queue for each datagram session const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; - + class DatagramSession : public std::enable_shared_from_this { public: DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent); void Start (); - void Stop (); + void Stop (); /** @brief ack the garlic routing path */ @@ -56,7 +56,7 @@ namespace datagram std::shared_ptr IBGW; std::shared_ptr OBEP; const uint64_t activity; - + Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : activity(a) { @@ -77,7 +77,7 @@ namespace datagram void HandleSend(std::shared_ptr msg); std::shared_ptr GetSharedRoutingPath(); - + void HandleLeaseSetUpdated(std::shared_ptr ls); private: @@ -95,16 +95,16 @@ namespace datagram typedef std::shared_ptr DatagramSession_ptr; - const size_t MAX_DATAGRAM_SIZE = 32768; + const size_t MAX_DATAGRAM_SIZE = 32768; class DatagramDestination { typedef std::function Receiver; public: - - DatagramDestination (std::shared_ptr owner); - ~DatagramDestination (); + + DatagramDestination (std::shared_ptr owner); + ~DatagramDestination (); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); @@ -116,21 +116,21 @@ namespace datagram void ResetReceiver (uint16_t port) { std::lock_guard lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); }; std::shared_ptr GetInfoForRemote(const i2p::data::IdentHash & remote); - + // clean up stale sessions void CleanUp (); private: - + std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); - + std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len); /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ Receiver FindReceiver(uint16_t port); - + private: i2p::client::ClientDestination * m_Owner; i2p::data::IdentityEx m_Identity; @@ -142,9 +142,8 @@ namespace datagram i2p::data::GzipInflator m_Inflator; i2p::data::GzipDeflator m_Deflator; - }; + }; } } #endif - diff --git a/Destination.h b/Destination.h index e077c016..12acf56a 100644 --- a/Destination.h +++ b/Destination.h @@ -27,16 +27,16 @@ namespace client { const uint8_t PROTOCOL_TYPE_STREAMING = 6; const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; - const uint8_t PROTOCOL_TYPE_RAW = 18; + const uint8_t PROTOCOL_TYPE_RAW = 18; const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish - const int PUBLISH_MIN_INTERVAL = 20; // in seconds - const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically + const int PUBLISH_MIN_INTERVAL = 20; // in seconds + const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds - const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes + const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; - + // I2CP const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; @@ -56,7 +56,7 @@ namespace client const int DEFAULT_MIN_TUNNEL_LATENCY = 0; const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max"; const int DEFAULT_MAX_TUNNEL_LATENCY = 0; - + typedef std::function stream)> StreamRequestComplete; class LeaseSetDestination: public i2p::garlic::GarlicDestination, @@ -78,24 +78,24 @@ namespace client { for (auto& it: requestComplete) it (ls); requestComplete.clear (); - } - }; - - + } + }; + + public: LeaseSetDestination (bool isPublic, const std::map * params = nullptr); - ~LeaseSetDestination (); + ~LeaseSetDestination (); virtual bool Start (); virtual bool Stop (); bool IsRunning () const { return m_IsRunning; }; boost::asio::io_service& GetService () { return m_Service; }; - std::shared_ptr GetTunnelPool () { return m_Pool; }; + std::shared_ptr GetTunnelPool () { return m_Pool; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); - void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); + void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); // implements GarlicDestination std::shared_ptr GetLeaseSet (); @@ -105,7 +105,7 @@ namespace client // override GarlicDestination bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); + void ProcessDeliveryStatusMessage (std::shared_ptr msg); void SetLeaseSetUpdated (); protected: @@ -115,10 +115,10 @@ namespace client // I2CP virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; virtual void CreateNewLeaseSet (std::vector > tunnels) = 0; - + private: - - void Run (); + + void Run (); void UpdateLeaseSet (); void Publish (); void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); @@ -126,18 +126,18 @@ namespace client void HandlePublishDelayTimer (const boost::system::error_code& ecode); void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); - void HandleDeliveryStatusMessage (std::shared_ptr msg); + void HandleDeliveryStatusMessage (std::shared_ptr msg); void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete); - bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); + bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); - void CleanupRemoteLeaseSets (); - + void CleanupRemoteLeaseSets (); + private: volatile bool m_IsRunning; - std::thread * m_Thread; + std::thread * m_Thread; boost::asio::io_service m_Service; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; @@ -150,16 +150,16 @@ namespace client uint32_t m_PublishReplyToken; uint64_t m_LastSubmissionTime; // in seconds std::set m_ExcludedFloodfills; // for publishing - - boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, + + boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_PublishDelayTimer, m_CleanupTimer; public: - + // for HTTP only int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; }; - }; + }; class ClientDestination: public LeaseSetDestination { @@ -171,16 +171,16 @@ namespace client // if cancelled before ready, informs promise with nullptr void Ready(ReadyPromise & p); #endif - + ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); ~ClientDestination (); - - bool Start (); - bool Stop (); - + + virtual bool Start (); + virtual bool Stop (); + const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; - + void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; + // streaming std::shared_ptr CreateStreamingDestination (int port, bool gzip = true); // additional std::shared_ptr GetStreamingDestination (int port = 0) const; @@ -191,31 +191,31 @@ namespace client void StopAcceptingStreams (); bool IsAcceptingStreams () const; void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor); - + // datagram i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; i2p::datagram::DatagramDestination * CreateDatagramDestination (); - - // implements LocalDestination + + // implements LocalDestination const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; - std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; + std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; protected: - + void CleanupDestination (); // I2CP void HandleDataMessage (const uint8_t * buf, size_t len); void CreateNewLeaseSet (std::vector > tunnels); - + private: std::shared_ptr GetSharedFromThis () - { return std::static_pointer_cast(shared_from_this ()); } + { return std::static_pointer_cast(shared_from_this ()); } void PersistTemporaryKeys (); #ifdef I2LUA void ScheduleCheckForReady(ReadyPromise * p); void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p); -#endif +#endif private: i2p::data::PrivateKeys m_Keys; @@ -226,13 +226,13 @@ namespace client i2p::datagram::DatagramDestination * m_DatagramDestination; boost::asio::deadline_timer m_ReadyChecker; - + public: // for HTTP only std::vector > GetAllStreams () const; - }; -} -} + }; +} +} #endif diff --git a/I2PTunnel.h b/I2PTunnel.h index f0580554..dd78d0fe 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -19,9 +19,9 @@ namespace i2p namespace client { const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; - const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds + const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds - // for HTTP tunnels + // for HTTP tunnels const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address @@ -33,21 +33,21 @@ namespace client I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr leaseSet, int port = 0); // to I2P I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, + std::shared_ptr stream); // to I2P using simplified API + I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P ~I2PTunnelConnection (); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); void Connect (bool isUniqueLocal = true); - + protected: - void Terminate (); + void Terminate (); void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); virtual void Write (const uint8_t * buf, size_t len); // can be overloaded - void HandleWrite (const boost::system::error_code& ecode); + void HandleWrite (const boost::system::error_code& ecode); void StreamReceive (); void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -75,28 +75,28 @@ namespace client protected: - void Write (const uint8_t * buf, size_t len); - - private: + void Write (const uint8_t * buf, size_t len); + + private: std::stringstream m_InHeader, m_OutHeader; bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; - }; - + }; + class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection { public: - + I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host); + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint& target, const std::string& host); protected: void Write (const uint8_t * buf, size_t len); private: - + std::string m_Host; std::stringstream m_InHeader, m_OutHeader; bool m_HeaderSent; @@ -106,21 +106,21 @@ namespace client class I2PTunnelConnectionIRC: public I2PTunnelConnection { public: - + I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); protected: void Write (const uint8_t * buf, size_t len); private: - + std::shared_ptr m_From; std::stringstream m_OutPacket, m_InPacket; bool m_NeedsWebIrc; - std::string m_WebircPass; + std::string m_WebircPass; }; @@ -133,24 +133,24 @@ namespace client public: - I2PClientTunnel (const std::string& name, const std::string& destination, + I2PClientTunnel (const std::string& name, const std::string& destination, const std::string& address, int port, std::shared_ptr localDestination, int destinationPort = 0); ~I2PClientTunnel () {} - + void Start (); void Stop (); - const char* GetName() { return m_Name.c_str (); } - + const char* GetName() { return m_Name.c_str (); } + private: const i2p::data::IdentHash * GetIdentHash (); private: - + std::string m_Name, m_Destination; const i2p::data::IdentHash * m_DestinationIdentHash; - int m_DestinationPort; + int m_DestinationPort; }; @@ -173,7 +173,7 @@ namespace client uint16_t RemotePort; uint8_t m_Buffer[I2P_UDP_MAX_MTU]; - + UDPSession(boost::asio::ip::udp::endpoint localEndpoint, const std::shared_ptr & localDestination, boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident, @@ -182,7 +182,7 @@ namespace client void Receive(); }; - + /** read only info about a datagram session */ struct DatagramSessionInfo { @@ -205,7 +205,7 @@ namespace client }; typedef std::shared_ptr UDPSessionPtr; - + /** server side udp tunnel, many i2p inbound to 1 ip outbound */ class I2PUDPServerTunnel { @@ -239,7 +239,7 @@ namespace client std::shared_ptr m_LocalDest; }; - class I2PUDPClientTunnel + class I2PUDPClientTunnel { public: I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, @@ -249,7 +249,7 @@ namespace client void Start(); const char * GetName() const { return m_Name.c_str(); } std::vector > GetSessions(); - + bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } std::shared_ptr GetLocalDestination () const { return m_LocalDest; } @@ -275,18 +275,18 @@ namespace client uint16_t RemotePort; bool m_cancel_resolve; }; - + class I2PServerTunnel: public I2PService { public: - I2PServerTunnel (const std::string& name, const std::string& address, int port, - std::shared_ptr localDestination, int inport = 0, bool gzip = true); + I2PServerTunnel (const std::string& name, const std::string& address, int port, + std::shared_ptr localDestination, int inport = 0, bool gzip = true); void Start (); void Stop (); - void SetAccessList (const std::set& accessList); + void SetAccessList (const std::set& accessList); void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } bool IsUniqueLocal () const { return m_IsUniqueLocal; } @@ -296,13 +296,13 @@ namespace client uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } - const char* GetName() { return m_Name.c_str (); } + const char* GetName() { return m_Name.c_str (); } void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); } private: - void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, + void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, std::shared_ptr resolver); void Accept (); @@ -310,11 +310,11 @@ namespace client virtual std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: - + bool m_IsUniqueLocal; std::string m_Name, m_Address; int m_Port; - boost::asio::ip::tcp::endpoint m_Endpoint; + boost::asio::ip::tcp::endpoint m_Endpoint; std::shared_ptr m_PortDestination; std::set m_AccessList; bool m_IsAccessList; @@ -324,9 +324,9 @@ namespace client { public: - I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port, + I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, const std::string& host, - int inport = 0, bool gzip = true); + int inport = 0, bool gzip = true); private: @@ -336,14 +336,14 @@ namespace client std::string m_Host; }; - + class I2PServerTunnelIRC: public I2PServerTunnel { public: - I2PServerTunnelIRC (const std::string& name, const std::string& address, int port, + I2PServerTunnelIRC (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, const std::string& webircpass, - int inport = 0, bool gzip = true); + int inport = 0, bool gzip = true); private: @@ -355,6 +355,6 @@ namespace client }; } -} +} #endif diff --git a/Makefile.linux b/Makefile.linux index c4d78447..8173b4a5 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -12,7 +12,7 @@ INCFLAGS ?= # detect proper flag for c++11 support by compilers CXXVER := $(shell $(CXX) -dumpversion) ifeq ($(shell expr match $(CXX) 'clang'),5) - NEEDED_CXXFLAGS += -std=c++11 + NEEDED_CXXFLAGS += -std=c++11 else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10 NEEDED_CXXFLAGS += -std=c++11 else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7 diff --git a/MatchedDestination.cpp b/MatchedDestination.cpp new file mode 100644 index 00000000..674a5f09 --- /dev/null +++ b/MatchedDestination.cpp @@ -0,0 +1,105 @@ +#include "MatchedDestination.h" +#include "Log.h" +#include "ClientContext.h" + + +namespace i2p +{ +namespace client +{ + MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map * params) + : ClientDestination(keys, false, params), + m_RemoteName(remoteName) {} + + void MatchedTunnelDestination::ResolveCurrentLeaseSet() + { + if(i2p::client::context.GetAddressBook().GetIdentHash(m_RemoteName, m_RemoteIdent)) + { + auto ls = FindLeaseSet(m_RemoteIdent); + if(ls) + { + HandleFoundCurrentLeaseSet(ls); + } + else + RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); + } + else + LogPrint(eLogWarning, "Destination: failed to resolve ", m_RemoteName); + } + + void MatchedTunnelDestination::HandleFoundCurrentLeaseSet(std::shared_ptr ls) + { + if(ls) + { + LogPrint(eLogDebug, "Destination: resolved remote lease set for ", m_RemoteName); + m_RemoteLeaseSet = ls; + } + else + ResolveCurrentLeaseSet(); + } + + + bool MatchedTunnelDestination::Start() + { + if(ClientDestination::Start()) + { + GetTunnelPool()->SetCustomPeerSelector(this); + ResolveCurrentLeaseSet(); + return true; + } + else + return false; + } + + bool MatchedTunnelDestination::Stop() + { + if(ClientDestination::Stop()) + { + + return true; + } + else + return false; + } + + + bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) + { + auto pool = GetTunnelPool(); + if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1))) + return false; + // more here for outbound tunnels + if(!inbound && m_RemoteLeaseSet) + { + if(m_RemoteLeaseSet->IsExpired()) + { + ResolveCurrentLeaseSet(); + } + if(m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired()) + { + // remote lease set is good + auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(); + // pick lease + std::shared_ptr obep; + while(!obep && leases.size() > 0) { + auto idx = rand() % leases.size(); + auto lease = leases[idx]; + obep = i2p::data::netdb.FindRouter(lease->tunnelGateway); + leases.erase(leases.begin()+idx); + } + if(obep) { + path.push_back(obep->GetRouterIdentity()); + LogPrint(eLogDebug, "Destination: found OBEP matching IBGW"); + } else + LogPrint(eLogWarning, "Destination: could not find proper IBGW for matched outbound tunnel"); + } + } + return true; + } + + bool MatchedTunnelDestination::OnBuildResult(const i2p::tunnel::Path & path, bool inbound, i2p::tunnel::TunnelBuildResult result) + { + return true; + } +} +} diff --git a/MatchedDestination.h b/MatchedDestination.h new file mode 100644 index 00000000..daf55c2d --- /dev/null +++ b/MatchedDestination.h @@ -0,0 +1,35 @@ +#ifndef MATCHED_DESTINATION_H_ +#define MATCHED_DESTINATION_H_ +#include "Destination.h" +#include + +namespace i2p +{ +namespace client +{ + /** + client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination + */ + class MatchedTunnelDestination : public ClientDestination, public i2p::tunnel::ITunnelPeerSelector + { + public: + MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, const std::map * params = nullptr); + bool Start(); + bool Stop(); + + bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); + bool OnBuildResult(const i2p::tunnel::Path & peers, bool inbound, i2p::tunnel::TunnelBuildResult result); + + private: + void ResolveCurrentLeaseSet(); + void HandleFoundCurrentLeaseSet(std::shared_ptr ls); + + private: + std::string m_RemoteName; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + }; +} +} + +#endif diff --git a/TunnelPool.cpp b/TunnelPool.cpp index dfdfbf58..60f81841 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -18,7 +18,7 @@ namespace i2p { namespace tunnel { - + TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true), @@ -37,25 +37,25 @@ namespace tunnel if (m_ExplicitPeers) { int size = m_ExplicitPeers->size (); - if (m_NumInboundHops > size) + if (m_NumInboundHops > size) { m_NumInboundHops = size; LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } - if (m_NumOutboundHops > size) + } + if (m_NumOutboundHops > size) { m_NumOutboundHops = size; LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } + } m_NumInboundTunnels = 1; m_NumOutboundTunnels = 1; - } + } } void TunnelPool::DetachTunnels () { { - std::unique_lock l(m_InboundTunnelsMutex); + std::unique_lock l(m_InboundTunnelsMutex); for (auto& it: m_InboundTunnels) it->SetTunnelPool (nullptr); m_InboundTunnels.clear (); @@ -67,21 +67,21 @@ namespace tunnel m_OutboundTunnels.clear (); } m_Tests.clear (); - } - + } + void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) { if (!m_IsActive) return; { -#ifdef WITH_EVENTS +#ifdef WITH_EVENTS EmitTunnelEvent("tunnels.created", createdTunnel); -#endif +#endif std::unique_lock l(m_InboundTunnelsMutex); m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); - + OnTunnelBuildResult(createdTunnel, eBuildResultOkay); } @@ -89,25 +89,25 @@ namespace tunnel { if (expiredTunnel) { -#ifdef WITH_EVENTS +#ifdef WITH_EVENTS EmitTunnelEvent("tunnels.expired", expiredTunnel); -#endif +#endif expiredTunnel->SetTunnelPool (nullptr); for (auto& it: m_Tests) if (it.second.second == expiredTunnel) it.second.second = nullptr; std::unique_lock l(m_InboundTunnelsMutex); m_InboundTunnels.erase (expiredTunnel); - } - } + } + } void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) { if (!m_IsActive) return; { -#ifdef WITH_EVENTS +#ifdef WITH_EVENTS EmitTunnelEvent("tunnels.created", createdTunnel); -#endif +#endif std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.insert (createdTunnel); } @@ -120,9 +120,9 @@ namespace tunnel { if (expiredTunnel) { -#ifdef WITH_EVENTS +#ifdef WITH_EVENTS EmitTunnelEvent("tunnels.expired", expiredTunnel); -#endif +#endif expiredTunnel->SetTunnelPool (nullptr); for (auto& it: m_Tests) if (it.second.first == expiredTunnel) it.second.first = nullptr; @@ -131,7 +131,7 @@ namespace tunnel m_OutboundTunnels.erase (expiredTunnel); } } - + std::vector > TunnelPool::GetInboundTunnels (int num) const { std::vector > v; @@ -144,8 +144,8 @@ namespace tunnel { v.push_back (it); i++; - } - } + } + } return v; } @@ -153,7 +153,7 @@ namespace tunnel { std::unique_lock l(m_OutboundTunnelsMutex); return GetNextTunnel (m_OutboundTunnels, excluded); - } + } std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded) const { @@ -164,11 +164,11 @@ namespace tunnel template typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const { - if (tunnels.empty ()) return nullptr; + if (tunnels.empty ()) return nullptr; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; typename TTunnels::value_type tunnel = nullptr; for (const auto& it: tunnels) - { + { if (it->IsEstablished () && it != excluded) { if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) { @@ -183,7 +183,7 @@ namespace tunnel if(HasLatencyRequirement() && !tunnel) { ind = rand () % (tunnels.size ()/2 + 1), i = 0; for (const auto& it: tunnels) - { + { if (it->IsEstablished () && it != excluded) { tunnel = it; @@ -199,10 +199,10 @@ namespace tunnel std::shared_ptr TunnelPool::GetNewOutboundTunnel (std::shared_ptr old) const { if (old && old->IsEstablished ()) return old; - std::shared_ptr tunnel; + std::shared_ptr tunnel; if (old) { - std::unique_lock l(m_OutboundTunnelsMutex); + std::unique_lock l(m_OutboundTunnelsMutex); for (const auto& it: m_OutboundTunnels) if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ()) { @@ -210,9 +210,9 @@ namespace tunnel break; } } - + if (!tunnel) - tunnel = GetNextOutboundTunnel (); + tunnel = GetNextOutboundTunnel (); return tunnel; } @@ -220,12 +220,12 @@ namespace tunnel { int num = 0; { - std::unique_lock l(m_OutboundTunnelsMutex); + std::unique_lock l(m_OutboundTunnelsMutex); for (const auto& it : m_OutboundTunnels) if (it->IsEstablished ()) num++; } for (int i = num; i < m_NumOutboundTunnels; i++) - CreateOutboundTunnel (); + CreateOutboundTunnel (); num = 0; { @@ -253,20 +253,20 @@ namespace tunnel LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed"); // if test failed again with another tunnel we consider it failed if (it.second.first) - { + { if (it.second.first->GetState () == eTunnelStateTestFailed) - { + { it.second.first->SetState (eTunnelStateFailed); std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.erase (it.second.first); } else it.second.first->SetState (eTunnelStateTestFailed); - } + } if (it.second.second) { if (it.second.second->GetState () == eTunnelStateTestFailed) - { + { it.second.second->SetState (eTunnelStateFailed); { std::unique_lock l(m_InboundTunnelsMutex); @@ -274,25 +274,25 @@ namespace tunnel } if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); - } + } else it.second.second->SetState (eTunnelStateTestFailed); - } + } } - - // new tests + + // new tests auto it1 = m_OutboundTunnels.begin (); auto it2 = m_InboundTunnels.begin (); while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) { bool failed = false; if ((*it1)->IsFailed ()) - { + { failed = true; ++it1; } if ((*it2)->IsFailed ()) - { + { failed = true; ++it2; } @@ -307,7 +307,7 @@ namespace tunnel (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), CreateDeliveryStatusMsg (msgID)); ++it1; ++it2; - } + } } } @@ -317,24 +317,24 @@ namespace tunnel m_LocalDestination->ProcessGarlicMessage (msg); else LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped"); - } - + } + void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) { const uint8_t * buf = msg->GetPayload (); uint32_t msgID = bufbe32toh (buf); - buf += 4; + buf += 4; uint64_t timestamp = bufbe64toh (buf); decltype(m_Tests)::mapped_type test; - bool found = false; + bool found = false; { std::unique_lock l(m_TestsMutex); auto it = m_Tests.find (msgID); if (it != m_Tests.end ()) { found = true; - test = it->second; + test = it->second; m_Tests.erase (it); } } @@ -358,35 +358,23 @@ namespace tunnel m_LocalDestination->ProcessDeliveryStatusMessage (msg); else LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } + } } std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop) const { bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ()); - auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): + auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); if (!hop || hop->GetProfile ()->IsBad ()) hop = i2p::data::netdb.GetRandomRouter (prevHop); - return hop; - } + return hop; + } - bool TunnelPool::SelectPeers (std::vector >& peers, bool isInbound) + bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop) { - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - // peers is empty - if (numHops <= 0) return true; - // custom peer selector in use ? - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - if (m_CustomPeerSelector) - return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); - } - // explicit peers in use - if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); - - auto prevHop = i2p::context.GetSharedRouterInfo (); + auto prevHop = i2p::context.GetSharedRouterInfo (); if(i2p::transport::transports.RoutesRestricted()) { /** if routes are restricted prepend trusted first hop */ @@ -399,36 +387,52 @@ namespace tunnel { auto r = i2p::transport::transports.GetRandomPeer (); if (r && !r->GetProfile ()->IsBad ()) - { - prevHop = r; - peers.push_back (r->GetRouterIdentity ()); - numHops--; - } + { + prevHop = r; + peers.push_back (r->GetRouterIdentity ()); + numHops--; + } } - + for(int i = 0; i < numHops; i++ ) { - auto hop = SelectNextHop (prevHop); + auto hop = nextHop (prevHop); if (!hop) { LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); return false; - } + } prevHop = hop; peers.push_back (hop->GetRouterIdentity ()); } return true; - } - + } + + bool TunnelPool::SelectPeers (std::vector >& peers, bool isInbound) + { + int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + // peers is empty + if (numHops <= 0) return true; + // custom peer selector in use ? + { + std::lock_guard lock(m_CustomPeerSelectorMutex); + if (m_CustomPeerSelector) + return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); + } + // explicit peers in use + if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); + return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1)); + } + bool TunnelPool::SelectExplicitPeers (std::vector >& peers, bool isInbound) { int size = m_ExplicitPeers->size (); std::vector peerIndicies; for (int i = 0; i < size; i++) peerIndicies.push_back(i); std::random_shuffle (peerIndicies.begin(), peerIndicies.end()); - - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - for (int i = 0; i < numHops; i++) + + int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + for (int i = 0; i < numHops; i++) { auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; auto r = i2p::data::netdb.FindRouter (ident); @@ -443,7 +447,7 @@ namespace tunnel } return true; } - + void TunnelPool::CreateInboundTunnel () { auto outboundTunnel = GetNextOutboundTunnel (); @@ -455,15 +459,15 @@ namespace tunnel { std::shared_ptr config; if (m_NumInboundHops > 0) - { - std::reverse (peers.begin (), peers.end ()); + { + std::reverse (peers.begin (), peers.end ()); config = std::make_shared (peers); - } + } auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); tunnel->SetTunnelPool (shared_from_this ()); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); - } + } else LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available"); } @@ -480,41 +484,41 @@ namespace tunnel newTunnel->SetTunnelPool (shared_from_this()); if (newTunnel->IsEstablished ()) // zero hops TunnelCreated (newTunnel); - } - + } + void TunnelPool::CreateOutboundTunnel () { auto inboundTunnel = GetNextInboundTunnel (); if (!inboundTunnel) inboundTunnel = tunnels.GetNextInboundTunnel (); if (inboundTunnel) - { + { LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); std::vector > peers; if (SelectPeers (peers, false)) - { + { std::shared_ptr config; - if (m_NumOutboundHops > 0) + if (m_NumOutboundHops > 0) config = std::make_shared(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); auto tunnel = tunnels.CreateOutboundTunnel (config); tunnel->SetTunnelPool (shared_from_this ()); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); - } + } else LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); - } + } else LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); - } - + } + void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) { auto inboundTunnel = GetNextInboundTunnel (); if (!inboundTunnel) inboundTunnel = tunnels.GetNextInboundTunnel (); if (inboundTunnel) - { + { LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); std::shared_ptr config; if (m_NumOutboundHops > 0) @@ -523,10 +527,10 @@ namespace tunnel newTunnel->SetTunnelPool (shared_from_this ()); if (newTunnel->IsEstablished ()) // zero hops TunnelCreated (newTunnel); - } + } else LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); - } + } void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) { @@ -535,7 +539,7 @@ namespace tunnel tunnel->SetTunnelPool (shared_from_this ()); } - void TunnelPool::SetCustomPeerSelector(TunnelPeerSelector selector) + void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector) { std::lock_guard lock(m_CustomPeerSelectorMutex); m_CustomPeerSelector = selector; @@ -567,7 +571,7 @@ namespace tunnel } return tun; } - + std::shared_ptr TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr exclude) const { std::shared_ptr tun = nullptr; diff --git a/TunnelPool.h b/TunnelPool.h index 9e2a3e24..d7e47876 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -30,25 +30,29 @@ namespace tunnel eBuildResultTimeout // tunnel build timed out }; + typedef std::shared_ptr Peer; + typedef std::vector Path; + /** interface for custom tunnel peer selection algorithm */ struct ITunnelPeerSelector { - typedef std::shared_ptr Peer; - typedef std::vector TunnelPath; - - virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0; - virtual bool OnBuildResult(TunnelPath & peers, bool isInbound, TunnelBuildResult result) = 0; + virtual ~ITunnelPeerSelector() {}; + virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0; + virtual bool OnBuildResult(const Path & peers, bool isInbound, TunnelBuildResult result) = 0; }; - typedef std::shared_ptr TunnelPeerSelector; - + + typedef std::function(std::shared_ptr)> SelectHopFunc; + // standard peer selection algorithm + bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop); + class TunnelPool: public std::enable_shared_from_this // per local destination { public: TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels); ~TunnelPool (); - + std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; void SetLocalDestination (std::shared_ptr destination) { m_LocalDestination = destination; }; void SetExplicitPeers (std::shared_ptr > explicitPeers); @@ -62,7 +66,7 @@ namespace tunnel void RecreateOutboundTunnel (std::shared_ptr tunnel); std::vector > GetInboundTunnels (int num) const; std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr) const; - std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr) const; + std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr) const; std::shared_ptr GetNewOutboundTunnel (std::shared_ptr old) const; void TestTunnels (); void ProcessGarlicMessage (std::shared_ptr msg); @@ -75,7 +79,7 @@ namespace tunnel int GetNumInboundTunnels () const { return m_NumInboundTunnels; }; int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; }; - void SetCustomPeerSelector(TunnelPeerSelector selector); + void SetCustomPeerSelector(ITunnelPeerSelector * selector); void UnsetCustomPeerSelector(); bool HasCustomPeerSelector(); @@ -90,23 +94,25 @@ namespace tunnel std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude=nullptr) const; void OnTunnelBuildResult(std::shared_ptr tunnel, TunnelBuildResult result); - + + // for overriding tunnel peer selection + std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; + private: - - void CreateInboundTunnel (); + + void CreateInboundTunnel (); void CreateOutboundTunnel (); void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); template typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; - std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; bool SelectPeers (std::vector >& hops, bool isInbound); - bool SelectExplicitPeers (std::vector >& hops, bool isInbound); + bool SelectExplicitPeers (std::vector >& hops, bool isInbound); private: std::shared_ptr m_LocalDestination; int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels; - std::shared_ptr > m_ExplicitPeers; + std::shared_ptr > m_ExplicitPeers; mutable std::mutex m_InboundTunnelsMutex; std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first mutable std::mutex m_OutboundTunnelsMutex; @@ -115,20 +121,19 @@ namespace tunnel std::map, std::shared_ptr > > m_Tests; bool m_IsActive; std::mutex m_CustomPeerSelectorMutex; - TunnelPeerSelector m_CustomPeerSelector; + ITunnelPeerSelector * m_CustomPeerSelector; uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms - + public: // for HTTP only const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - }; + }; } } #endif - diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 32aff387..0acf54ca 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -37,7 +37,7 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/FS.cpp" "${CMAKE_SOURCE_DIR}/Log.cpp" "${CMAKE_SOURCE_DIR}/NTCPSession.cpp" - "${CMAKE_SOURCE_DIR}/NetDbRequests.cpp" + "${CMAKE_SOURCE_DIR}/NetDbRequests.cpp" "${CMAKE_SOURCE_DIR}/NetDb.cpp" "${CMAKE_SOURCE_DIR}/Profiling.cpp" "${CMAKE_SOURCE_DIR}/Reseed.cpp" @@ -47,7 +47,7 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/SSUData.cpp" "${CMAKE_SOURCE_DIR}/SSUSession.cpp" "${CMAKE_SOURCE_DIR}/Streaming.cpp" - "${CMAKE_SOURCE_DIR}/Destination.cpp" + "${CMAKE_SOURCE_DIR}/Destination.cpp" "${CMAKE_SOURCE_DIR}/TransitTunnel.cpp" "${CMAKE_SOURCE_DIR}/Tunnel.cpp" "${CMAKE_SOURCE_DIR}/TunnelGateway.cpp" @@ -59,16 +59,16 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/Datagram.cpp" "${CMAKE_SOURCE_DIR}/Family.cpp" "${CMAKE_SOURCE_DIR}/Signature.cpp" - "${CMAKE_SOURCE_DIR}/Timestamp.cpp" + "${CMAKE_SOURCE_DIR}/Timestamp.cpp" "${CMAKE_SOURCE_DIR}/api.cpp" "${CMAKE_SOURCE_DIR}/Event.cpp" - "${CMAKE_SOURCE_DIR}/Gost.cpp" + "${CMAKE_SOURCE_DIR}/Gost.cpp" ) if (WITH_WEBSOCKETS) add_definitions(-DWITH_EVENTS) find_package(websocketpp REQUIRED) -endif () +endif () if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR MSYS) list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp") @@ -90,8 +90,9 @@ install(TARGETS libi2pd set (CLIENT_SRC "${CMAKE_SOURCE_DIR}/AddressBook.cpp" - "${CMAKE_SOURCE_DIR}/BOB.cpp" + "${CMAKE_SOURCE_DIR}/BOB.cpp" "${CMAKE_SOURCE_DIR}/ClientContext.cpp" + "${CMAKE_SOURCE_DIR}/MatchedDestination.cpp" "${CMAKE_SOURCE_DIR}/I2PTunnel.cpp" "${CMAKE_SOURCE_DIR}/I2PService.cpp" "${CMAKE_SOURCE_DIR}/SAM.cpp" @@ -357,7 +358,7 @@ include_directories( SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_ # warn if for meshnet if (WITH_MESHNET) message(STATUS "Building for testnet") - message(WARNING "This build will NOT work on mainline i2p") + message(WARNING "This build will NOT work on mainline i2p") endif() diff --git a/filelist.mk b/filelist.mk index 9149eefa..5fb5ea1d 100644 --- a/filelist.mk +++ b/filelist.mk @@ -8,10 +8,9 @@ LIB_SRC = \ Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Event.cpp Gost.cpp LIB_CLIENT_SRC = \ - AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ + AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \ SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp WebSocks.cpp # also: Daemon{Linux,Win32}.cpp will be added later DAEMON_SRC = \ HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp -