Browse Source

Merge pull request #712 from PurpleI2P/openssl

recent changes
pull/63/merge
orignal 8 years ago committed by GitHub
parent
commit
fb59d80897
  1. 2
      .dir-locals.el
  2. 4
      ChangeLog
  3. 10
      ClientContext.cpp
  4. 6
      Config.cpp
  5. 19
      Crypto.cpp
  6. 21
      Crypto.h
  7. 16
      Destination.cpp
  8. 7
      Destination.h
  9. 2
      Family.cpp
  10. 4
      Garlic.cpp
  11. 19
      HTTPServer.cpp
  12. 2
      Identity.cpp
  13. 93
      NetDb.cpp
  14. 9
      NetDb.h
  15. 7
      NetDbRequests.cpp
  16. 34
      Reseed.cpp
  17. 18
      Signature.h
  18. 6
      Tag.h
  19. 20
      Transports.cpp
  20. 6
      Transports.h
  21. 36
      Tunnel.cpp
  22. 28
      Tunnel.h
  23. 1
      TunnelBase.h
  24. 23
      TunnelEndpoint.cpp
  25. 3
      TunnelEndpoint.h
  26. 74
      TunnelPool.cpp
  27. 15
      TunnelPool.h
  28. 6
      debian/changelog
  29. 114
      docs/hacking.md
  30. 8
      docs/usage.md

2
.dir-locals.el

@ -0,0 +1,2 @@
((c++-mode . ((indent-tabs-mode . t)))
(c-mode . ((mode . c++))))

4
ChangeLog

@ -1,6 +1,10 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.10.1] - 2016-11-07
### Fixed
- Fixed some performance issues for Windows and Android
## [2.10.0] - 2016-10-17 ## [2.10.0] - 2016-10-17
### Added ### Added
- Datagram i2p tunnels - Datagram i2p tunnels

10
ClientContext.cpp

@ -52,7 +52,7 @@ namespace client
if (httpProxyKeys.length () > 0) if (httpProxyKeys.length () > 0)
{ {
i2p::data::PrivateKeys keys; i2p::data::PrivateKeys keys;
if(LoadPrivateKeys (keys, httpProxyKeys)) if(LoadPrivateKeys (keys, httpProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
{ {
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("httpproxy.", params); ReadI2CPOptionsFromConfig ("httpproxy.", params);
@ -82,7 +82,7 @@ namespace client
if (socksProxyKeys.length () > 0) if (socksProxyKeys.length () > 0)
{ {
i2p::data::PrivateKeys keys; i2p::data::PrivateKeys keys;
if (LoadPrivateKeys (keys, socksProxyKeys)) if (LoadPrivateKeys (keys, socksProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
{ {
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("socksproxy.", params); ReadI2CPOptionsFromConfig ("socksproxy.", params);
@ -372,6 +372,8 @@ namespace client
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); 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<std::string, std::string>& options) const void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
@ -385,6 +387,10 @@ namespace client
options[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; 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 () void ClientContext::ReadTunnels ()

6
Config.cpp

@ -87,6 +87,8 @@ namespace config {
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity") ("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
; ;
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
@ -99,6 +101,8 @@ namespace config {
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity") ("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity") ("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") ("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
; ;
@ -158,6 +162,7 @@ namespace config {
options_description reseed("Reseed options"); options_description reseed("Reseed options");
reseed.add_options() reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature") ("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file") ("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
("reseed.urls", value<std::string>()->default_value( ("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/," "https://reseed.i2p-projekt.de/,"
@ -165,7 +170,6 @@ namespace config {
"https://netdb.i2p2.no/," "https://netdb.i2p2.no/,"
"https://us.reseed.i2p2.no:444/," "https://us.reseed.i2p2.no:444/,"
"https://uk.reseed.i2p2.no:444/," "https://uk.reseed.i2p2.no:444/,"
"https://i2p.manas.ca:8443/,"
"https://i2p-0.manas.ca:8443/," "https://i2p-0.manas.ca:8443/,"
"https://reseed.i2p.vzaws.com:8443/," "https://reseed.i2p.vzaws.com:8443/,"
"https://download.xxlspeed.com/," "https://download.xxlspeed.com/,"

19
Crypto.cpp

@ -224,7 +224,7 @@ namespace crypto
// DH // DH
DHKeys::DHKeys (): m_IsUpdated (true) DHKeys::DHKeys ()
{ {
m_DH = DH_new (); m_DH = DH_new ();
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg)); DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
@ -236,7 +236,7 @@ namespace crypto
DH_free (m_DH); DH_free (m_DH);
} }
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub) void DHKeys::GenerateKeys ()
{ {
BIGNUM * priv_key = NULL, * pub_key = NULL; BIGNUM * priv_key = NULL, * pub_key = NULL;
#if !defined(__x86_64__) // use short exponent for non x64 #if !defined(__x86_64__) // use short exponent for non x64
@ -261,20 +261,7 @@ namespace crypto
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key); DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
} }
if (priv) bn2buf (priv_key, priv, 256); bn2buf (pub_key, m_PublicKey, 256);
if (pub) bn2buf (pub_key, pub, 256);
m_IsUpdated = true;
}
const uint8_t * DHKeys::GetPublicKey ()
{
if (m_IsUpdated)
{
bn2buf (m_DH->pub_key, m_PublicKey, 256);
BN_free (m_DH->pub_key); m_DH->pub_key = NULL;
m_IsUpdated= false;
}
return m_PublicKey;
} }
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared) void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)

21
Crypto.h

@ -7,8 +7,10 @@
#include <openssl/dh.h> #include <openssl/dh.h>
#include <openssl/aes.h> #include <openssl/aes.h>
#include <openssl/dsa.h> #include <openssl/dsa.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include "Base.h" #include "Base.h"
@ -34,15 +36,14 @@ namespace crypto
DHKeys (); DHKeys ();
~DHKeys (); ~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr); void GenerateKeys ();
const uint8_t * GetPublicKey (); const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared); void Agree (const uint8_t * pub, uint8_t * shared);
private: private:
DH * m_DH; DH * m_DH;
uint8_t m_PublicKey[256]; uint8_t m_PublicKey[256];
bool m_IsUpdated;
}; };
// ElGamal // ElGamal
@ -280,6 +281,8 @@ namespace crypto
void InitCrypto (bool precomputation); void InitCrypto (bool precomputation);
void TerminateCrypto (); void TerminateCrypto ();
}
}
// take care about openssl version // take care about openssl version
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
@ -296,6 +299,11 @@ inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; } { *pr = sig->r; *ps = sig->s; }
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{ sig->r = r; sig->s = s; return 1; }
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{ r->n = n; r->e = e; r->d = d; return 1; } { r->n = n; r->e = e; r->d = d; return 1; }
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
@ -312,9 +320,10 @@ inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; } { *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
inline int EVP_PKEY_base_id(const EVP_PKEY *pkey)
{ return EVP_PKEY_type(pkey->type); }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; }
#endif #endif
}
}
#endif #endif

16
Destination.cpp

@ -63,6 +63,22 @@ namespace client
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
if (explicitPeers) if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers); m_Pool->SetExplicitPeers (explicitPeers);
if(params)
{
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
if (itr != params->end()) {
auto maxlatency = std::stoi(itr->second);
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
if (itr != params->end()) {
auto minlatency = std::stoi(itr->second);
if ( minlatency > 0 && maxlatency > 0 ) {
// set tunnel pool latency
LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
m_Pool->RequireLatency(minlatency, maxlatency);
}
}
}
}
} }
LeaseSetDestination::~LeaseSetDestination () LeaseSetDestination::~LeaseSetDestination ()

7
Destination.h

@ -50,6 +50,12 @@ namespace client
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40; const int DEFAULT_TAGS_TO_SEND = 40;
// latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
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<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class LeaseSetDestination: public i2p::garlic::GarlicDestination, class LeaseSetDestination: public i2p::garlic::GarlicDestination,
@ -143,6 +149,7 @@ namespace client
// for HTTP only // for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
}; };
class ClientDestination: public LeaseSetDestination class ClientDestination: public LeaseSetDestination

2
Family.cpp

@ -40,7 +40,7 @@ namespace data
if (family) family[0] = 0; if (family) family[0] = 0;
} }
auto pkey = X509_get_pubkey (cert); auto pkey = X509_get_pubkey (cert);
int keyType = EVP_PKEY_type(pkey->type); int keyType = EVP_PKEY_base_id (pkey);
switch (keyType) switch (keyType)
{ {
case EVP_PKEY_DSA: case EVP_PKEY_DSA:

4
Garlic.cpp

@ -277,6 +277,7 @@ namespace garlic
{ {
newTags->msgID = msgID; newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.emplace_back (newTags); m_UnconfirmedTagsMsgs.emplace_back (newTags);
newTags = nullptr; // got acquired
} }
m_Owner->DeliveryStatusSent (shared_from_this (), msgID); m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
} }
@ -300,13 +301,14 @@ namespace garlic
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
(*numCloves)++; (*numCloves)++;
} }
memset (payload + size, 0, 3); // certificate of message memset (payload + size, 0, 3); // certificate of message
size += 3; size += 3;
htobe32buf (payload + size, msgID); // MessageID htobe32buf (payload + size, msgID); // MessageID
size += 4; size += 4;
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
size += 8; size += 8;
if (newTags) delete newTags; // not acquired, delete
return size; return size;
} }

19
HTTPServer.cpp

@ -225,7 +225,7 @@ namespace http {
else else
s << numKBytesSent / 1024 / 1024 << " GiB"; s << numKBytesSent / 1024 / 1024 << " GiB";
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n"; s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n"; s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n"; s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n"; s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
@ -253,7 +253,7 @@ namespace http {
s << address->host.to_string() << ":" << address->port << "<br>\r\n"; s << address->host.to_string() << ":" << address->port << "<br>\r\n";
} }
s << "</p>\r\n</div>\r\n"; s << "</p>\r\n</div>\r\n";
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " "; s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " "; s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n"; s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@ -287,18 +287,29 @@ namespace http {
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">"; s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n"; s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n"; s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
for(auto& it: dest->GetLeaseSets ())
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
}
auto pool = dest->GetTunnelPool (); auto pool = dest->GetTunnelPool ();
if (pool) if (pool)
{ {
s << "<b>Inbound tunnels:</b><br>\r\n"; s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetInboundTunnels ()) { for (auto & it : pool->GetInboundTunnels ()) {
it->Print(s); it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n"; s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetOutboundTunnels ()) { for (auto & it : pool->GetOutboundTunnels ()) {
it->Print(s); it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
} }
} }
@ -394,12 +405,16 @@ namespace http {
s << "<b>Inbound tunnels:</b><br>\r\n"; s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
it->Print(s); it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n"; s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
it->Print(s); it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
} }
s << "<br>\r\n"; s << "<br>\r\n";

2
Identity.cpp

@ -488,7 +488,7 @@ namespace data
switch (m_Public->GetSigningKeyType ()) switch (m_Public->GetSigningKeyType ())
{ {
case SIGNING_KEY_TYPE_DSA_SHA1: case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey)); m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey)); m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));

93
NetDb.cpp

@ -14,6 +14,7 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "Garlic.h" #include "Garlic.h"
#include "NetDb.h" #include "NetDb.h"
#include "Config.h"
using namespace i2p::transport; using namespace i2p::transport;
@ -23,7 +24,7 @@ namespace data
{ {
NetDb netdb; NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false) NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_FloodfillBootstrap(nullptr), m_HiddenMode(false)
{ {
} }
@ -140,6 +141,8 @@ namespace data
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed"); LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
break; break;
} }
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || ts - lastExploratory >= 90) if (numRouters < 2500 || ts - lastExploratory >= 90)
{ {
numRouters = 800/numRouters; numRouters = 800/numRouters;
@ -185,25 +188,36 @@ namespace data
// TODO: check if floodfill has been changed // TODO: check if floodfill has been changed
} }
else else
{
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
}
} }
else else
{ {
r = std::make_shared<RouterInfo> (buf, len); r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable ()) if (!r->IsUnreachable ())
{ {
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); bool inserted = false;
{ {
std::unique_lock<std::mutex> l(m_RouterInfosMutex); std::unique_lock<std::mutex> l(m_RouterInfosMutex);
m_RouterInfos[r->GetIdentHash ()] = r; inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second;
} }
if (inserted)
{
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
{ {
std::unique_lock<std::mutex> l(m_FloodfillsMutex); std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r); m_Floodfills.push_back (r);
} }
} }
else
{
LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64());
updated = false;
}
}
else else
updated = false; updated = false;
} }
@ -296,12 +310,61 @@ namespace data
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
} }
int reseedRetries = 0; int reseedRetries = 0;
// try reseeding from floodfill first if specified
std::string riPath;
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill()) {
const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen();
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
// bad router info
LogPrint(eLogError, "NetDb: bad router info");
return;
}
m_FloodfillBootstrap = ri;
ReseedFromFloodfill(*ri);
// don't try reseed servers if trying to boostrap from floodfill
return;
}
}
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ()) while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
reseedRetries++; reseedRetries++;
if (reseedRetries >= 10) if (reseedRetries >= 10)
LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts"); LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts");
} }
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
{
LogPrint(eLogInfo, "NetDB: reseeding from floodfill ", ri.GetIdentHashBase64());
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
i2p::data::IdentHash ih = ri.GetIdentHash();
i2p::data::IdentHash randomIdent;
// make floodfill lookups
while(numFloodfills > 0) {
randomIdent.Randomize();
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false);
requests.push_back(msg);
numFloodfills --;
}
// make regular router lookups
while(numRouters > 0) {
randomIdent.Randomize();
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true);
requests.push_back(msg);
numRouters --;
}
// send them off
i2p::transport::transports.SendMessages(ih, requests);
}
bool NetDb::LoadRouterInfo (const std::string & path) bool NetDb::LoadRouterInfo (const std::string & path)
{ {
auto r = std::make_shared<RouterInfo>(path); auto r = std::make_shared<RouterInfo>(path);
@ -499,6 +562,21 @@ namespace data
} }
} }
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete)
{
auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already");
return;
}
LogPrint(eLogInfo, "NetDb: destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
// direct
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
}
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m) void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
{ {
const uint8_t * buf = m->GetPayload (); const uint8_t * buf = m->GetPayload ();
@ -620,7 +698,7 @@ namespace data
if (!dest->IsExploratory ()) if (!dest->IsExploratory ())
{ {
// reply to our destination. Try other floodfills // reply to our destination. Try other floodfills
if (outbound && inbound ) if (outbound && inbound)
{ {
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size (); auto count = dest->GetExcludedPeers ().size ();
@ -664,7 +742,7 @@ namespace data
// no more requests for detination possible. delete it // no more requests for detination possible. delete it
m_Requests.RequestComplete (ident, nullptr); m_Requests.RequestComplete (ident, nullptr);
} }
else else if(!m_FloodfillBootstrap)
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found"); LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
// try responses // try responses
@ -681,6 +759,9 @@ namespace data
{ {
// router with ident not found or too old (1 hour) // router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ..."); LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
if(m_FloodfillBootstrap)
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
else
RequestDestination (router); RequestDestination (router);
} }
else else

9
NetDb.h

@ -60,6 +60,7 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const; std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
@ -98,6 +99,9 @@ namespace data
void VisitRouterInfos(RouterInfoVisitor v); void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */ /** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n); size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); };
private: private:
void Load (); void Load ();
@ -110,6 +114,8 @@ namespace data
void ManageRequests (); void ManageRequests ();
void ManageLookupResponses (); void ManageLookupResponses ();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const; std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
@ -135,6 +141,9 @@ namespace data
friend class NetDbRequests; friend class NetDbRequests;
NetDbRequests m_Requests; NetDbRequests m_Requests;
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp) std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
/** true if in hidden mode */ /** true if in hidden mode */

7
NetDbRequests.cpp

@ -11,9 +11,14 @@ namespace data
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{ {
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, std::shared_ptr<I2NPMessage> msg;
if(replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers); &m_ExcludedPeers);
else
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
if(router)
m_ExcludedPeers.insert (router->GetIdentHash ()); m_ExcludedPeers.insert (router->GetIdentHash ());
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg; return msg;

34
Reseed.cpp

@ -305,6 +305,34 @@ namespace data
if (end - contentPos >= contentLength) if (end - contentPos >= contentLength)
break; // we are beyond contentLength break; // we are beyond contentLength
} }
if (numFiles) // check if routers are not outdated
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numOutdated = 0;
i2p::data::netdb.VisitRouterInfos (
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
{
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
{
LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
numOutdated++;
}
});
if (numOutdated > numFiles/2) // more than half
{
LogPrint (eLogError, "Reseed: mammoth's shit\n"
" *_____*\n"
" *_*****_*\n"
" *_(O)_(O)_*\n"
" **____V____**\n"
" **_________**\n"
" **_________**\n"
" *_________*\n"
" ***___***");
i2p::data::netdb.ClearRouterInfos ();
numFiles = 0;
}
}
return numFiles; return numFiles;
} }
@ -350,9 +378,11 @@ namespace data
if (terminator) terminator[0] = 0; if (terminator) terminator[0] = 0;
} }
// extract RSA key (we need n only, e = 65537) // extract RSA key (we need n only, e = 65537)
RSA * key = X509_get_pubkey (cert)->pkey.rsa; RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
const BIGNUM * n, * e, * d;
RSA_get0_key(key, &n, &e, &d);
PublicKey value; PublicKey value;
i2p::crypto::bn2buf (key->n, value, 512); i2p::crypto::bn2buf (n, value, 512);
if (cn) if (cn)
m_SigningKeys[cn] = value; m_SigningKeys[cn] = value;
else else

18
Signature.h

@ -77,10 +77,11 @@ namespace crypto
{ {
public: public:
DSASigner (const uint8_t * signingPrivateKey) DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
// openssl 1.1 always requires DSA public key even for signing
{ {
m_PrivateKey = CreateDSA (); m_PrivateKey = CreateDSA ();
DSA_set0_key (m_PrivateKey, NULL, BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
} }
~DSASigner () ~DSASigner ()
@ -169,8 +170,9 @@ namespace crypto
uint8_t digest[Hash::hashLen]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new(); ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL); auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL); auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification // ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey); int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig); ECDSA_SIG_free(sig);
@ -207,9 +209,11 @@ namespace crypto
uint8_t digest[Hash::hashLen]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey); ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
const BIGNUM * r, * s;
ECDSA_SIG_get0 (sig, &r, &s);
// signatureLen = keyLen // signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2); bn2buf (r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2); bn2buf (s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig); ECDSA_SIG_free(sig);
} }
@ -271,7 +275,6 @@ namespace crypto
RSAVerifier (const uint8_t * signingKey) RSAVerifier (const uint8_t * signingKey)
{ {
m_PublicKey = RSA_new (); m_PublicKey = RSA_new ();
memset (m_PublicKey, 0, sizeof (RSA));
RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL); RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL);
} }
@ -304,7 +307,6 @@ namespace crypto
RSASigner (const uint8_t * signingPrivateKey) RSASigner (const uint8_t * signingPrivateKey)
{ {
m_PrivateKey = RSA_new (); m_PrivateKey = RSA_new ();
memset (m_PrivateKey, 0, sizeof (RSA));
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */ RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */); BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
} }

6
Tag.h

@ -11,6 +11,7 @@
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
#include <string.h> #include <string.h>
#include <openssl/rand.h>
#include "Base.h" #include "Base.h"
namespace i2p { namespace i2p {
@ -50,6 +51,11 @@ public:
memset(m_Buf, c, sz); memset(m_Buf, c, sz);
} }
void Randomize()
{
RAND_bytes(m_Buf, sz);
}
std::string ToBase64 () const std::string ToBase64 () const
{ {
char str[sz*2]; char str[sz*2];

20
Transports.cpp

@ -108,7 +108,8 @@ namespace transport
Transports transports; Transports transports;
Transports::Transports (): Transports::Transports ():
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service), m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_PeerCleanupTimer (m_Service), m_PeerTestTimer (m_Service),
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0), m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0) m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
@ -168,11 +169,14 @@ namespace transport
} }
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
} }
void Transports::Stop () void Transports::Stop ()
{ {
m_PeerCleanupTimer.cancel (); m_PeerCleanupTimer.cancel ();
m_PeerTestTimer.cancel ();
m_Peers.clear (); m_Peers.clear ();
if (m_SSUServer) if (m_SSUServer)
{ {
@ -256,7 +260,8 @@ namespace transport
{ {
// we send it to ourself // we send it to ourself
for (auto& it: msgs) for (auto& it: msgs)
i2p::HandleI2NPMessage (it); m_LoopbackHandler.PutNextMessage (it);
m_LoopbackHandler.Flush ();
return; return;
} }
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return; if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
@ -547,7 +552,6 @@ namespace transport
if (RoutesRestricted()) return; if (RoutesRestricted()) return;
if (m_SSUServer) if (m_SSUServer)
{ {
bool statusChanged = false; bool statusChanged = false;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@ -688,6 +692,16 @@ namespace transport
} }
} }
void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
PeerTest ();
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
}
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{ {
if (m_Peers.empty ()) return nullptr; if (m_Peers.empty ()) return nullptr;

6
Transports.h

@ -66,6 +66,7 @@ namespace transport
}; };
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes
const int MAX_NUM_DELAYED_MESSAGES = 50; const int MAX_NUM_DELAYED_MESSAGES = 50;
class Transports class Transports
{ {
@ -127,6 +128,7 @@ namespace transport
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router); void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer); bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode); void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident); void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
@ -144,7 +146,7 @@ namespace transport
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service m_Service; boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work; boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_PeerCleanupTimer; boost::asio::deadline_timer m_PeerCleanupTimer, m_PeerTestTimer;
NTCPServer * m_NTCPServer; NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer; SSUServer * m_SSUServer;
@ -166,6 +168,8 @@ namespace transport
std::vector<i2p::data::IdentHash> m_TrustedRouters; std::vector<i2p::data::IdentHash> m_TrustedRouters;
mutable std::mutex m_TrustedRoutersMutex; mutable std::mutex m_TrustedRoutersMutex;
i2p::I2NPMessagesHandler m_LoopbackHandler;
public: public:
// for HTTP only // for HTTP only

36
Tunnel.cpp

@ -21,6 +21,31 @@ namespace i2p
namespace tunnel namespace tunnel
{ {
void TunnelLatency::AddSample(Sample s)
{
std::unique_lock<std::mutex> l(m_access);
m_samples.push_back(s);
}
bool TunnelLatency::HasSamples() const
{
std::unique_lock<std::mutex> l(m_access);
return m_samples.size() > 0;
}
TunnelLatency::Latency TunnelLatency::GetMeanLatency() const
{
std::unique_lock<std::mutex> lock(m_access);
if (m_samples.size() > 0) {
Latency l = 0;
for(auto s : m_samples)
l += s;
return l / m_samples.size();
}
return 0;
}
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config): Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
@ -162,6 +187,12 @@ namespace tunnel
return established; return established;
} }
bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const
{
auto latency = GetMeanLatency();
return latency >= lower && latency <= upper;
}
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{ {
const uint8_t * inPayload = in->GetPayload () + 4; const uint8_t * inPayload = in->GetPayload () + 4;
@ -714,6 +745,8 @@ namespace tunnel
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring); tunnel->SetState (eTunnelStateExpiring);
else // we don't need to cleanup expiring tunnels
tunnel->Cleanup ();
} }
it++; it++;
} }
@ -763,9 +796,12 @@ namespace tunnel
it = m_TransitTunnels.erase (it); it = m_TransitTunnels.erase (it);
} }
else else
{
tunnel->Cleanup ();
it++; it++;
} }
} }
}
void Tunnels::ManageTunnelPools () void Tunnels::ManageTunnelPools ()
{ {

28
Tunnel.h

@ -79,6 +79,21 @@ namespace tunnel
eTunnelStateExpiring eTunnelStateExpiring
}; };
/** @brief for storing latency history */
struct TunnelLatency
{
typedef uint64_t Sample;
typedef uint64_t Latency;
void AddSample(Sample s);
bool HasSamples() const;
Latency GetMeanLatency() const;
std::vector<Sample> m_samples;
mutable std::mutex m_access;
};
class OutboundTunnel; class OutboundTunnel;
class InboundTunnel; class InboundTunnel;
class Tunnel: public TunnelBase class Tunnel: public TunnelBase
@ -118,6 +133,14 @@ namespace tunnel
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out); void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
/** @brief add latency sample */
void AddLatencySample(const uint64_t ms) { m_Latency.AddSample(ms); }
/** @brief get this tunnel's estimated latency */
uint64_t GetMeanLatency() const { return m_Latency.GetMeanLatency(); }
/** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */
bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
bool LatencyIsKnown() const { return m_Latency.HasSamples(); }
protected: protected:
void PrintHops (std::stringstream& s) const; void PrintHops (std::stringstream& s) const;
@ -129,6 +152,7 @@ namespace tunnel
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State; TunnelState m_State;
bool m_IsRecreated; bool m_IsRecreated;
TunnelLatency m_Latency;
}; };
class OutboundTunnel: public Tunnel class OutboundTunnel: public Tunnel
@ -165,6 +189,10 @@ namespace tunnel
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; } bool IsInbound() const { return true; }
// override TunnelBase
void Cleanup () { m_Endpoint.Cleanup (); };
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;

1
TunnelBase.h

@ -37,6 +37,7 @@ namespace tunnel
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent), m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {}; virtual ~TunnelBase () {};
virtual void Cleanup () {};
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0; virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0; virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;

23
TunnelEndpoint.cpp

@ -6,6 +6,7 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Transports.h" #include "Transports.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Timestamp.h"
#include "TunnelEndpoint.h" #include "TunnelEndpoint.h"
namespace i2p namespace i2p
@ -192,7 +193,7 @@ namespace tunnel
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data) void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
{ {
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {fragmentNum, isLastFragment, data}}).second) if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
} }
@ -214,9 +215,7 @@ namespace tunnel
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum}); auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
if (it != m_OutOfSequenceFragments.end ()) if (it != m_OutOfSequenceFragments.end ())
{ {
if (it->second.fragmentNum == msg.nextFragmentNum) LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
{
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength (); size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen) if (msg.data->len + size > msg.data->maxLen)
{ {
@ -235,9 +234,6 @@ namespace tunnel
m_OutOfSequenceFragments.erase (it); m_OutOfSequenceFragments.erase (it);
return true; return true;
} }
else
LogPrint (eLogError, "Tunnel message: next fragment ", (int)it->second.fragmentNum, " of message ", msgID, " mismatch. ", (int)msg.nextFragmentNum, " expected");
}
return false; return false;
} }
@ -276,5 +272,18 @@ namespace tunnel
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
}; };
} }
void TunnelEndpoint::Cleanup ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// out-of-sequence fragments
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
{
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
it = m_OutOfSequenceFragments.erase (it);
else
++it;
}
}
} }
} }

3
TunnelEndpoint.h

@ -20,9 +20,9 @@ namespace tunnel
struct Fragment struct Fragment
{ {
uint8_t fragmentNum;
bool isLastFragment; bool isLastFragment;
std::shared_ptr<I2NPMessage> data; std::shared_ptr<I2NPMessage> data;
uint64_t receiveTime; // milliseconds since epoch
}; };
public: public:
@ -30,6 +30,7 @@ namespace tunnel
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
~TunnelEndpoint (); ~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Cleanup ();
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg); void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);

74
TunnelPool.cpp

@ -164,6 +164,21 @@ namespace tunnel
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
typename TTunnels::value_type tunnel = nullptr; typename TTunnels::value_type tunnel = nullptr;
for (const auto& it: tunnels) for (const auto& it: tunnels)
{
if (it->IsEstablished () && it != excluded)
{
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
i ++;
continue;
}
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
if(HasLatencyRequirement() && !tunnel) {
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
for (const auto& it: tunnels)
{ {
if (it->IsEstablished () && it != excluded) if (it->IsEstablished () && it != excluded)
{ {
@ -172,6 +187,7 @@ namespace tunnel
} }
if (i > ind && tunnel) break; if (i > ind && tunnel) break;
} }
}
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded; if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
return tunnel; return tunnel;
} }
@ -200,21 +216,24 @@ namespace tunnel
{ {
int num = 0; int num = 0;
{ {
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (const auto& it : m_InboundTunnels) for (const auto& it : m_OutboundTunnels)
if (it->IsEstablished ()) num++; if (it->IsEstablished ()) num++;
} }
for (int i = num; i < m_NumInboundTunnels; i++) for (int i = num; i < m_NumOutboundTunnels; i++)
CreateInboundTunnel (); CreateOutboundTunnel ();
num = 0; num = 0;
{ {
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_OutboundTunnels) for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++; if (it->IsEstablished ()) num++;
} }
for (int i = num; i < m_NumOutboundTunnels; i++) for (int i = num; i < m_NumInboundTunnels; i++)
CreateOutboundTunnel (); CreateInboundTunnel ();
if (num > 0 && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
} }
void TunnelPool::TestTunnels () void TunnelPool::TestTunnels ()
@ -322,7 +341,12 @@ namespace tunnel
test.first->SetState (eTunnelStateEstablished); test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed) if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished); test.second->SetState (eTunnelStateEstablished);
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds"); uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
// update latency
uint64_t latency = dlt / 2;
test.first->AddLatencySample(latency);
test.second->AddLatencySample(latency);
} }
else else
{ {
@ -523,5 +547,37 @@ namespace tunnel
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex); std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
return m_CustomPeerSelector != nullptr; return m_CustomPeerSelector != nullptr;
} }
std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
{
std::shared_ptr<InboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
uint64_t min = 1000000;
for (const auto & itr : m_InboundTunnels) {
if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency();
if (l >= min) continue;
tun = itr;
if(tun == exclude) continue;
min = l;
}
return tun;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
{
std::shared_ptr<OutboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
uint64_t min = 1000000;
for (const auto & itr : m_OutboundTunnels) {
if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency();
if (l >= min) continue;
tun = itr;
if(tun == exclude) continue;
min = l;
}
return tun;
}
} }
} }

15
TunnelPool.h

@ -69,6 +69,17 @@ namespace tunnel
void SetCustomPeerSelector(TunnelPeerSelector selector); void SetCustomPeerSelector(TunnelPeerSelector selector);
void UnsetCustomPeerSelector(); void UnsetCustomPeerSelector();
bool HasCustomPeerSelector(); bool HasCustomPeerSelector();
/** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */
void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; }
/** @brief return true if this tunnel pool has a latency requirement */
bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; }
/** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
private: private:
void CreateInboundTunnel (); void CreateInboundTunnel ();
@ -94,6 +105,10 @@ namespace tunnel
bool m_IsActive; bool m_IsActive;
std::mutex m_CustomPeerSelectorMutex; std::mutex m_CustomPeerSelectorMutex;
TunnelPeerSelector m_CustomPeerSelector; TunnelPeerSelector 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: public:
// for HTTP only // for HTTP only

6
debian/changelog vendored

@ -1,3 +1,9 @@
i2pd (2.10.1-1) unstable; urgency=low
* updated to version 2.10.1
-- orignal <orignal@i2pmail.org> Mon, 7 Nov 2016 14:18:30 +0000
i2pd (2.10.0-1) unstable; urgency=low i2pd (2.10.0-1) unstable; urgency=low
* updated to version 2.10.0/0.9.27 * updated to version 2.10.0/0.9.27

114
docs/hacking.md

@ -0,0 +1,114 @@
# Hacking on I2PD
This document contains notes compiled from hacking on i2pd
## prerequisites
This guide assumes:
* a decent understanding of c++
* basic understanding of how i2p works at i2np level and up
## general structure
Notes on multithreading
* every compontent runs in its own thread
* each component (usually) has a public function `GetService()` which can be used to obtain the `boost::asio::io_service` that it uses.
* when talking between components/threads, **always** use `GetService().post()` and be mindfull of stack allocated memory.
### NetDb
#### NetDb.h
The `i2p::data::netdb` is a `i2p::data::NetDb` instance processes and dispatches *inbound* i2np messages passed in from transports.
global singleton at `i2p::data::netdb` as of 2.10.1
#### NetDbRequests.h
For Pending RouterInfo/LeaseSet lookup and store requests
### ClientContext
#### ClientContext.h
`i2p::client::ClientContext` spawns all destinations used by the i2p router including the shared local destination.
global singleton at `i2p::client::context` as of 2.10.1
### Daemon
File: Daemon.cpp
`i2p::util::Daemon_Singleton_Private` subclasses implement the daemon start-up and tear-down, creates Http Webui and i2p control server.
### Destinations
#### Destination.h
each destination runs in its own thread
##### i2p::client::LeaseSetDestination
Base for `i2p::client::ClientDestination`
##### i2p::client::ClientDestination
Destination capable of creating (tcp/i2p) streams and datagram sessions.
#### Streaming.h
##### i2p::stream::StreamingDestination
Does not implement any destination related members, the name is a bit misleading.
Owns a `i2p::client::ClientDestination` and runs in the destination thread.
Anyone creating or using streams outside of the destination thread **MUST** be aware of the consequences of multithreaded c++ :^)
If you use streaming please consider running all code within the destination thread using `ClientDestination::GetService().post()`
#### Garlic.h
Provides Inter-Destination routing primatives.
##### i2p::garlic::GarlicDestination
sublcass of `i2p::client::LeaseSetDestination` for sending messages down shared routing paths.
##### i2p::garlic::GarlicRoutingSession
a point to point conversation between us and 1 other destination.
##### i2p::garlic::GarlicRoutingPath
A routing path currently used by a routing session. specifies which outbound tunnel to use and which remote lease set to use for `OBEP` to `IBGW` inter tunnel communication.
members:
* outboundTunnel (OBEP)
* remoteLease (IBGW)
* rtt (round trip time)
* updatedTime (last time this path's IBGW/OBEP was updated)
* numTimesUsesd (number of times this path was used)
### Transports
each transport runs in its own thread
#### Transports.h
`i2p::transport::Transports` contains NTCP and SSU transport instances

8
docs/usage.md

@ -23,12 +23,12 @@ To display all available options:
i2pd can be controlled with signals. Process ID by default is written to file `~/.i2pd/i2pd.pid` or `/var/run/i2pd/i2pd.pid`. i2pd can be controlled with signals. Process ID by default is written to file `~/.i2pd/i2pd.pid` or `/var/run/i2pd/i2pd.pid`.
You can use `kill` utility to send signals like this: You can use `kill` utility to send signals like this:
kill -TERM $( cat /var/run/i2pd/i2pd.pid ) kill -INT $( cat /var/run/i2pd/i2pd.pid )
i2pd supports the following signals: i2pd supports the following signals:
TERM - Graceful shutdown. i2pd will wait for 10 minutes and stop. Send second TERM signal to shutdown i2pd immediately. * INT - Graceful shutdown. i2pd will wait for 10 minutes and stop. Send second INT signal to shutdown i2pd immediately.
HUP - Reload configuration files. * HUP - Reload configuration files.
### systemd unit ### systemd unit
@ -48,7 +48,7 @@ Enable/disable i2pd to be started on bootup:
## Configuring i2pd ## Configuring i2pd
See [configuration page](i2pd.readthedocs.io/page/configuration.html). See [configuration documentation](/page/configuration.html).
## Browsing and hosting websites ## Browsing and hosting websites

Loading…
Cancel
Save