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 @@ @@ -0,0 +1,2 @@
((c++-mode . ((indent-tabs-mode . t)))
(c-mode . ((mode . c++))))

4
ChangeLog

@ -1,6 +1,10 @@ @@ -1,6 +1,10 @@
# for this file format description,
# 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
### Added
- Datagram i2p tunnels

10
ClientContext.cpp

@ -52,7 +52,7 @@ namespace client @@ -52,7 +52,7 @@ namespace client
if (httpProxyKeys.length () > 0)
{
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;
ReadI2CPOptionsFromConfig ("httpproxy.", params);
@ -82,7 +82,7 @@ namespace client @@ -82,7 +82,7 @@ namespace client
if (socksProxyKeys.length () > 0)
{
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;
ReadI2CPOptionsFromConfig ("socksproxy.", params);
@ -372,6 +372,8 @@ namespace client @@ -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_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_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
@ -385,6 +387,10 @@ namespace client @@ -385,6 +387,10 @@ namespace client
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = 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 ()

6
Config.cpp

@ -87,6 +87,8 @@ namespace config { @@ -87,6 +87,8 @@ namespace config {
("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.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");
@ -99,6 +101,8 @@ namespace config { @@ -99,6 +101,8 @@ namespace config {
("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.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.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
;
@ -158,6 +162,7 @@ namespace config { @@ -158,6 +162,7 @@ namespace config {
options_description reseed("Reseed options");
reseed.add_options()
("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.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/,"
@ -165,7 +170,6 @@ namespace config { @@ -165,7 +170,6 @@ namespace config {
"https://netdb.i2p2.no/,"
"https://us.reseed.i2p2.no:444/,"
"https://uk.reseed.i2p2.no:444/,"
"https://i2p.manas.ca:8443/,"
"https://i2p-0.manas.ca:8443/,"
"https://reseed.i2p.vzaws.com:8443/,"
"https://download.xxlspeed.com/,"

19
Crypto.cpp

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

21
Crypto.h

@ -7,8 +7,10 @@ @@ -7,8 +7,10 @@
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "Base.h"
@ -34,15 +36,14 @@ namespace crypto @@ -34,15 +36,14 @@ namespace crypto
DHKeys ();
~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
const uint8_t * GetPublicKey ();
void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared);
private:
DH * m_DH;
uint8_t m_PublicKey[256];
bool m_IsUpdated;
};
// ElGamal
@ -280,6 +281,8 @@ namespace crypto @@ -280,6 +281,8 @@ namespace crypto
void InitCrypto (bool precomputation);
void TerminateCrypto ();
}
}
// take care about openssl version
#include <openssl/opensslv.h>
@ -296,6 +299,11 @@ inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) @@ -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)
{ *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)
{ 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)
@ -312,9 +320,10 @@ inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) @@ -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)
{ *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

16
Destination.cpp

@ -63,6 +63,22 @@ namespace client @@ -63,6 +63,22 @@ namespace client
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
if (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 ()

7
Destination.h

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

2
Family.cpp

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

4
Garlic.cpp

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

19
HTTPServer.cpp

@ -225,7 +225,7 @@ namespace http { @@ -225,7 +225,7 @@ namespace http {
else
s << numKBytesSent / 1024 / 1024 << " GiB";
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 << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
@ -253,7 +253,7 @@ namespace http { @@ -253,7 +253,7 @@ namespace http {
s << address->host.to_string() << ":" << address->port << "<br>\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>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@ -287,18 +287,29 @@ namespace http { @@ -287,18 +287,29 @@ namespace http {
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 << "<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 ();
if (pool)
{
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetInboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetOutboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
}
@ -394,12 +405,16 @@ namespace http { @@ -394,12 +405,16 @@ namespace http {
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
s << "<br>\r\n";

2
Identity.cpp

@ -488,7 +488,7 @@ namespace data @@ -488,7 +488,7 @@ namespace data
switch (m_Public->GetSigningKeyType ())
{
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;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));

93
NetDb.cpp

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Config.h"
using namespace i2p::transport;
@ -23,7 +24,7 @@ namespace data @@ -23,7 +24,7 @@ namespace data
{
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 @@ -140,6 +141,8 @@ namespace data
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
break;
}
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || ts - lastExploratory >= 90)
{
numRouters = 800/numRouters;
@ -185,25 +188,36 @@ namespace data @@ -185,25 +188,36 @@ namespace data
// TODO: check if floodfill has been changed
}
else
{
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
}
}
else
{
r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable ())
{
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
bool inserted = false;
{
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
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r);
}
}
else
{
LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64());
updated = false;
}
}
else
updated = false;
}
@ -296,12 +310,61 @@ namespace data @@ -296,12 +310,61 @@ namespace data
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
}
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 ())
reseedRetries++;
if (reseedRetries >= 10)
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)
{
auto r = std::make_shared<RouterInfo>(path);
@ -499,6 +562,21 @@ namespace data @@ -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)
{
const uint8_t * buf = m->GetPayload ();
@ -620,7 +698,7 @@ namespace data @@ -620,7 +698,7 @@ namespace data
if (!dest->IsExploratory ())
{
// reply to our destination. Try other floodfills
if (outbound && inbound )
if (outbound && inbound)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size ();
@ -664,7 +742,7 @@ namespace data @@ -664,7 +742,7 @@ namespace data
// no more requests for detination possible. delete it
m_Requests.RequestComplete (ident, nullptr);
}
else
else if(!m_FloodfillBootstrap)
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
// try responses
@ -681,6 +759,9 @@ namespace data @@ -681,6 +759,9 @@ namespace data
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
if(m_FloodfillBootstrap)
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
else
RequestDestination (router);
}
else

9
NetDb.h

@ -60,6 +60,7 @@ namespace data @@ -60,6 +60,7 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
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 HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
@ -98,6 +99,9 @@ namespace data @@ -98,6 +99,9 @@ namespace data
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 */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); };
private:
void Load ();
@ -110,6 +114,8 @@ namespace data @@ -110,6 +114,8 @@ namespace data
void ManageRequests ();
void ManageLookupResponses ();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
@ -135,6 +141,9 @@ namespace data @@ -135,6 +141,9 @@ namespace data
friend class NetDbRequests;
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)
/** true if in hidden mode */

7
NetDbRequests.cpp

@ -11,9 +11,14 @@ namespace data @@ -11,9 +11,14 @@ namespace data
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
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,
&m_ExcludedPeers);
else
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
if(router)
m_ExcludedPeers.insert (router->GetIdentHash ());
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;

34
Reseed.cpp

@ -305,6 +305,34 @@ namespace data @@ -305,6 +305,34 @@ namespace data
if (end - contentPos >= 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;
}
@ -350,9 +378,11 @@ namespace data @@ -350,9 +378,11 @@ namespace data
if (terminator) terminator[0] = 0;
}
// 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;
i2p::crypto::bn2buf (key->n, value, 512);
i2p::crypto::bn2buf (n, value, 512);
if (cn)
m_SigningKeys[cn] = value;
else

18
Signature.h

@ -77,10 +77,11 @@ namespace crypto @@ -77,10 +77,11 @@ namespace crypto
{
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 ();
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 ()
@ -169,8 +170,9 @@ namespace crypto @@ -169,8 +170,9 @@ namespace crypto
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
@ -207,9 +209,11 @@ namespace crypto @@ -207,9 +209,11 @@ namespace crypto
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
const BIGNUM * r, * s;
ECDSA_SIG_get0 (sig, &r, &s);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
bn2buf (r, signature, keyLen/2);
bn2buf (s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
}
@ -271,7 +275,6 @@ namespace crypto @@ -271,7 +275,6 @@ namespace crypto
RSAVerifier (const uint8_t * signingKey)
{
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);
}
@ -304,7 +307,6 @@ namespace crypto @@ -304,7 +307,6 @@ namespace crypto
RSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey = RSA_new ();
memset (m_PrivateKey, 0, sizeof (RSA));
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
}

6
Tag.h

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

20
Transports.cpp

@ -108,7 +108,8 @@ namespace transport @@ -108,7 +108,8 @@ namespace transport
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_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
@ -168,11 +169,14 @@ namespace transport @@ -168,11 +169,14 @@ namespace transport
}
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_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 ()
{
m_PeerCleanupTimer.cancel ();
m_PeerTestTimer.cancel ();
m_Peers.clear ();
if (m_SSUServer)
{
@ -256,7 +260,8 @@ namespace transport @@ -256,7 +260,8 @@ namespace transport
{
// we send it to ourself
for (auto& it: msgs)
i2p::HandleI2NPMessage (it);
m_LoopbackHandler.PutNextMessage (it);
m_LoopbackHandler.Flush ();
return;
}
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
@ -547,7 +552,6 @@ namespace transport @@ -547,7 +552,6 @@ namespace transport
if (RoutesRestricted()) return;
if (m_SSUServer)
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
@ -688,6 +692,16 @@ namespace transport @@ -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
{
if (m_Peers.empty ()) return nullptr;

6
Transports.h

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

36
Tunnel.cpp

@ -21,6 +21,31 @@ namespace i2p @@ -21,6 +21,31 @@ namespace i2p
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):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
@ -162,6 +187,12 @@ namespace tunnel @@ -162,6 +187,12 @@ namespace tunnel
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)
{
const uint8_t * inPayload = in->GetPayload () + 4;
@ -714,6 +745,8 @@ namespace tunnel @@ -714,6 +745,8 @@ namespace tunnel
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
else // we don't need to cleanup expiring tunnels
tunnel->Cleanup ();
}
it++;
}
@ -763,9 +796,12 @@ namespace tunnel @@ -763,9 +796,12 @@ namespace tunnel
it = m_TransitTunnels.erase (it);
}
else
{
tunnel->Cleanup ();
it++;
}
}
}
void Tunnels::ManageTunnelPools ()
{

28
Tunnel.h

@ -79,6 +79,21 @@ namespace tunnel @@ -79,6 +79,21 @@ namespace tunnel
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 InboundTunnel;
class Tunnel: public TunnelBase
@ -118,6 +133,14 @@ namespace tunnel @@ -118,6 +133,14 @@ namespace tunnel
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
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:
void PrintHops (std::stringstream& s) const;
@ -129,6 +152,7 @@ namespace tunnel @@ -129,6 +152,7 @@ namespace tunnel
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State;
bool m_IsRecreated;
TunnelLatency m_Latency;
};
class OutboundTunnel: public Tunnel
@ -165,6 +189,10 @@ namespace tunnel @@ -165,6 +189,10 @@ namespace tunnel
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const;
bool IsInbound() const { return true; }
// override TunnelBase
void Cleanup () { m_Endpoint.Cleanup (); };
private:
TunnelEndpoint m_Endpoint;

1
TunnelBase.h

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

23
TunnelEndpoint.cpp

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "Timestamp.h"
#include "TunnelEndpoint.h"
namespace i2p
@ -192,7 +193,7 @@ namespace tunnel @@ -192,7 +193,7 @@ namespace tunnel
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);
}
@ -214,9 +215,7 @@ namespace tunnel @@ -214,9 +215,7 @@ namespace tunnel
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
if (it != m_OutOfSequenceFragments.end ())
{
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
@ -235,9 +234,6 @@ namespace tunnel @@ -235,9 +234,6 @@ namespace tunnel
m_OutOfSequenceFragments.erase (it);
return true;
}
else
LogPrint (eLogError, "Tunnel message: next fragment ", (int)it->second.fragmentNum, " of message ", msgID, " mismatch. ", (int)msg.nextFragmentNum, " expected");
}
return false;
}
@ -276,5 +272,18 @@ namespace tunnel @@ -276,5 +272,18 @@ namespace tunnel
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 @@ -20,9 +20,9 @@ namespace tunnel
struct Fragment
{
uint8_t fragmentNum;
bool isLastFragment;
std::shared_ptr<I2NPMessage> data;
uint64_t receiveTime; // milliseconds since epoch
};
public:
@ -30,6 +30,7 @@ namespace tunnel @@ -30,6 +30,7 @@ namespace tunnel
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Cleanup ();
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);

74
TunnelPool.cpp

@ -164,6 +164,21 @@ namespace tunnel @@ -164,6 +164,21 @@ namespace tunnel
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)) {
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)
{
@ -172,6 +187,7 @@ namespace tunnel @@ -172,6 +187,7 @@ namespace tunnel
}
if (i > ind && tunnel) break;
}
}
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
return tunnel;
}
@ -200,21 +216,24 @@ namespace tunnel @@ -200,21 +216,24 @@ namespace tunnel
{
int num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_InboundTunnels)
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (const auto& it : m_OutboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
for (int i = num; i < m_NumOutboundTunnels; i++)
CreateOutboundTunnel ();
num = 0;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (const auto& it : m_OutboundTunnels)
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumOutboundTunnels; i++)
CreateOutboundTunnel ();
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
if (num > 0 && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
}
void TunnelPool::TestTunnels ()
@ -322,7 +341,12 @@ namespace tunnel @@ -322,7 +341,12 @@ namespace tunnel
test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed)
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
{
@ -523,5 +547,37 @@ namespace tunnel @@ -523,5 +547,37 @@ namespace tunnel
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
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 @@ -69,6 +69,17 @@ namespace tunnel
void SetCustomPeerSelector(TunnelPeerSelector selector);
void UnsetCustomPeerSelector();
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:
void CreateInboundTunnel ();
@ -94,6 +105,10 @@ namespace tunnel @@ -94,6 +105,10 @@ namespace tunnel
bool m_IsActive;
std::mutex m_CustomPeerSelectorMutex;
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:
// for HTTP only

6
debian/changelog vendored

@ -1,3 +1,9 @@ @@ -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
* updated to version 2.10.0/0.9.27

114
docs/hacking.md

@ -0,0 +1,114 @@ @@ -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: @@ -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`.
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:
TERM - Graceful shutdown. i2pd will wait for 10 minutes and stop. Send second TERM signal to shutdown i2pd immediately.
HUP - Reload configuration files.
* INT - Graceful shutdown. i2pd will wait for 10 minutes and stop. Send second INT signal to shutdown i2pd immediately.
* HUP - Reload configuration files.
### systemd unit
@ -48,7 +48,7 @@ Enable/disable i2pd to be started on bootup: @@ -48,7 +48,7 @@ Enable/disable i2pd to be started on bootup:
## Configuring i2pd
See [configuration page](i2pd.readthedocs.io/page/configuration.html).
See [configuration documentation](/page/configuration.html).
## Browsing and hosting websites

Loading…
Cancel
Save