Browse Source

Merge pull request #719 from PurpleI2P/openssl

recent changes
pull/63/merge
orignal 8 years ago committed by GitHub
parent
commit
c166bc9b18
  1. 1
      ClientContext.h
  2. 2
      Config.cpp
  3. 18
      Crypto.cpp
  4. 16
      Crypto.h
  5. 3
      Daemon.cpp
  6. 34
      Destination.cpp
  7. 8
      Destination.h
  8. 63
      Garlic.cpp
  9. 6
      Garlic.h
  10. 13
      HTTPProxy.cpp
  11. 125
      HTTPServer.cpp
  12. 13
      I2CP.cpp
  13. 5
      I2CP.h
  14. 59
      Log.cpp
  15. 20
      Log.h
  16. 20
      NTCPSession.cpp
  17. 14
      Reseed.cpp
  18. 2
      Reseed.h
  19. 2
      RouterContext.cpp
  20. 13
      Streaming.cpp
  21. 2
      TunnelPool.cpp
  22. 3
      UPnP.cpp
  23. 2
      Win32/PurpleI2P.nsi
  24. 2
      Win32/Resource.rc
  25. 1
      Win32/installer.iss
  26. BIN
      Win32/mask.bmp
  27. BIN
      Win32/mask.ico
  28. 3
      api.cpp
  29. 2
      build/CMakeLists.txt
  30. 4
      docs/i2pd.conf

1
ClientContext.h

@ -66,6 +66,7 @@ namespace client
AddressBook& GetAddressBook () { return m_AddressBook; }; AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination); std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);

2
Config.cpp

@ -163,7 +163,7 @@ namespace config {
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.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 local .su3 file or HTTPS URL to reseed from")
("reseed.urls", value<std::string>()->default_value( ("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/," "https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/," "https://i2p.mooo.com/netDb/,"

18
Crypto.cpp

@ -595,16 +595,16 @@ namespace crypto
"jnz 1b \n" "jnz 1b \n"
"movups %%xmm1, (%[iv]) \n" "movups %%xmm1, (%[iv]) \n"
: :
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
: "%xmm0", "%xmm1", "cc", "memory" : "%xmm0", "%xmm1", "cc", "memory"
); );
#else #else
for (int i = 0; i < numBlocks; i++) for (int i = 0; i < numBlocks; i++)
{ {
m_LastBlock ^= in[i]; *m_LastBlock.GetChipherBlock () ^= in[i];
m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock); m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
out[i] = m_LastBlock; out[i] = *m_LastBlock.GetChipherBlock ();
} }
#endif #endif
} }
@ -629,7 +629,7 @@ namespace crypto
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"movups %%xmm0, (%[iv]) \n" "movups %%xmm0, (%[iv]) \n"
: :
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out) [in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory" : "%xmm0", "%xmm1", "memory"
); );
@ -657,7 +657,7 @@ namespace crypto
"jnz 1b \n" "jnz 1b \n"
"movups %%xmm1, (%[iv]) \n" "movups %%xmm1, (%[iv]) \n"
: :
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory" : "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
); );
@ -666,8 +666,8 @@ namespace crypto
{ {
ChipherBlock tmp = in[i]; ChipherBlock tmp = in[i];
m_ECBDecryption.Decrypt (in + i, out + i); m_ECBDecryption.Decrypt (in + i, out + i);
out[i] ^= m_IV; out[i] ^= *m_IV.GetChipherBlock ();
m_IV = tmp; *m_IV.GetChipherBlock () = tmp;
} }
#endif #endif
} }
@ -691,7 +691,7 @@ namespace crypto
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
: :
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out) [in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory" : "%xmm0", "%xmm1", "memory"
); );

16
Crypto.h

@ -112,6 +112,8 @@ namespace crypto
operator uint8_t * () { return m_Buf; }; operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; }; operator const uint8_t * () const { return m_Buf; };
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
private: private:
@ -200,10 +202,10 @@ namespace crypto
{ {
public: public:
CBCEncryption () { memset (m_LastBlock.buf, 0, 16); }; CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
@ -211,7 +213,7 @@ namespace crypto
private: private:
ChipherBlock m_LastBlock; AESAlignedBuffer<16> m_LastBlock;
ECBEncryption m_ECBEncryption; ECBEncryption m_ECBEncryption;
}; };
@ -220,10 +222,10 @@ namespace crypto
{ {
public: public:
CBCDecryption () { memset (m_IV.buf, 0, 16); }; CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
@ -231,7 +233,7 @@ namespace crypto
private: private:
ChipherBlock m_IV; AESAlignedBuffer<16> m_IV;
ECBDecryption m_ECBDecryption; ECBDecryption m_ECBDecryption;
}; };
@ -320,8 +322,6 @@ 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) inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; } { return pkey->pkey.rsa; }
#endif #endif

3
Daemon.cpp

@ -113,7 +113,6 @@ namespace i2p
} else { } else {
// use stdout -- default // use stdout -- default
} }
i2p::log::Logger().Ready();
LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config); LogPrint(eLogDebug, "FS: main config file: ", config);
@ -246,6 +245,7 @@ namespace i2p
bool Daemon_Singleton::start() bool Daemon_Singleton::start()
{ {
i2p::log::Logger().Start();
LogPrint(eLogInfo, "Daemon: starting NetDB"); LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start(); i2p::data::netdb.Start();
@ -351,6 +351,7 @@ namespace i2p
} }
#endif #endif
i2p::crypto::TerminateCrypto (); i2p::crypto::TerminateCrypto ();
i2p::log::Logger().Stop();
return true; return true;
} }

34
Destination.cpp

@ -86,7 +86,7 @@ namespace client
if (m_IsRunning) if (m_IsRunning)
Stop (); Stop ();
for (auto& it: m_LeaseSetRequests) for (auto& it: m_LeaseSetRequests)
if (it.second->requestComplete) it.second->requestComplete (nullptr); it.second->Complete (nullptr);
m_LeaseSetRequests.clear (); m_LeaseSetRequests.clear ();
if (m_Pool) if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
@ -345,7 +345,7 @@ namespace client
if (it1 != m_LeaseSetRequests.end ()) if (it1 != m_LeaseSetRequests.end ())
{ {
it1->second->requestTimeoutTimer.cancel (); it1->second->requestTimeoutTimer.cancel ();
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet); if (it1->second) it1->second->Complete (leaseSet);
m_LeaseSetRequests.erase (it1); m_LeaseSetRequests.erase (it1);
} }
} }
@ -383,7 +383,7 @@ namespace client
if (!found) if (!found)
{ {
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
if (request->requestComplete) request->requestComplete (nullptr); request->Complete (nullptr);
m_LeaseSetRequests.erase (key); m_LeaseSetRequests.erase (key);
} }
} }
@ -512,9 +512,9 @@ namespace client
auto it = s->m_LeaseSetRequests.find (dest); auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ()) if (it != s->m_LeaseSetRequests.end ())
{ {
auto requestComplete = it->second->requestComplete; auto requestComplete = it->second;
s->m_LeaseSetRequests.erase (it); s->m_LeaseSetRequests.erase (it);
if (notify && requestComplete) requestComplete (nullptr); if (notify && requestComplete) requestComplete->Complete (nullptr);
} }
}); });
} }
@ -526,28 +526,33 @@ namespace client
if (floodfill) if (floodfill)
{ {
auto request = std::make_shared<LeaseSetRequest> (m_Service); auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete; request->requestComplete.push_back (requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch ();
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request)); auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted if (ret.second) // inserted
{ {
request->requestTime = ts;
if (!SendLeaseSetRequest (dest, floodfill, request)) if (!SendLeaseSetRequest (dest, floodfill, request))
{ {
// request failed // request failed
m_LeaseSetRequests.erase (dest); m_LeaseSetRequests.erase (ret.first);
if (request->requestComplete) request->requestComplete (nullptr); requestComplete (nullptr);
} }
} }
else // duplicate else // duplicate
{ {
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already"); LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
// TODO: queue up requests // TODO: implement it properly
if (request->requestComplete) request->requestComplete (nullptr); //ret.first->second->requestComplete.push_back (requestComplete);
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
m_LeaseSetRequests.erase (ret.first);
requestComplete (nullptr);
} }
} }
else else
{ {
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found"); LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
if (requestComplete) requestComplete (nullptr); requestComplete (nullptr);
} }
} }
@ -564,7 +569,6 @@ namespace client
if (request->replyTunnel && request->outboundTunnel) if (request->replyTunnel && request->outboundTunnel)
{ {
request->excluded.insert (nextFloodfill->GetIdentHash ()); request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel (); request->requestTimeoutTimer.cancel ();
uint8_t replyKey[32], replyTag[32]; uint8_t replyKey[32], replyTag[32];
@ -622,9 +626,9 @@ namespace client
if (done) if (done)
{ {
auto requestComplete = it->second->requestComplete; auto requestComplete = it->second;
m_LeaseSetRequests.erase (it); m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr); if (requestComplete) requestComplete->Complete (nullptr);
} }
} }
} }

8
Destination.h

@ -69,9 +69,15 @@ namespace client
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime; uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer; boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete; std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel; std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel; std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{
for (auto& it: requestComplete) it (ls);
requestComplete.clear ();
}
}; };

63
Garlic.cpp

@ -20,6 +20,7 @@ namespace garlic
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet): std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags), m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_LeaseSetUpdateMsgID (0),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ())) m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{ {
// create new session tags and session key // create new session tags and session key
@ -28,7 +29,7 @@ namespace garlic
} }
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend) m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
{ {
memcpy (m_SessionKey, sessionKey, 32); memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey); m_Encryption.SetKey (m_SessionKey);
@ -83,6 +84,7 @@ namespace garlic
if (msgID == m_LeaseSetUpdateMsgID) if (msgID == m_LeaseSetUpdateMsgID)
{ {
m_LeaseSetUpdateStatus = eLeaseSetUpToDate; m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
m_LeaseSetUpdateMsgID = 0;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed"); LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
} }
else else
@ -92,32 +94,22 @@ namespace garlic
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{ {
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) auto it = m_UnconfirmedTagsMsgs.find (msgID);
{ if (it != m_UnconfirmedTagsMsgs.end ())
auto& tags = *it;
if (tags->msgID == msgID)
{ {
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{ {
for (int i = 0; i < tags->numTags; i++) for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]); m_SessionTags.push_back (tags->sessionTags[i]);
} }
it = m_UnconfirmedTagsMsgs.erase (it); m_UnconfirmedTagsMsgs.erase (it);
}
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
++it;
} }
} }
bool GarlicRoutingSession::CleanupExpiredTags () bool GarlicRoutingSession::CleanupExpiredTags ()
{ {
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{ {
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
@ -126,6 +118,12 @@ namespace garlic
++it; ++it;
} }
CleanupUnconfirmedTags (); CleanupUnconfirmedTags ();
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
m_LeaseSetUpdateMsgID = 0;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
} }
@ -136,10 +134,10 @@ namespace garlic
// delete expired unconfirmed tags // delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{ {
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{ {
if (m_Owner) if (m_Owner)
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID); m_Owner->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it); it = m_UnconfirmedTagsMsgs.erase (it);
ret = true; ret = true;
} }
@ -276,7 +274,7 @@ namespace garlic
if (newTags) // new tags created if (newTags) // new tags created
{ {
newTags->msgID = msgID; newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.emplace_back (newTags); m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
newTags = nullptr; // got acquired newTags = nullptr; // got acquired
} }
m_Owner->DeliveryStatusSent (shared_from_this (), msgID); m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
@ -287,6 +285,7 @@ namespace garlic
// attach LeaseSet // attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
{ {
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
m_LeaseSetUpdateStatus = eLeaseSetSubmitted; m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID; m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = ts; m_LeaseSetSubmissionTime = ts;
@ -625,6 +624,7 @@ namespace garlic
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
// outgoing // outgoing
{
std::unique_lock<std::mutex> l(m_SessionsMutex); std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{ {
@ -632,35 +632,56 @@ namespace garlic
if (!it->second->CleanupExpiredTags ()) if (!it->second->CleanupExpiredTags ())
{ {
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted"); LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it->second->SetOwner (nullptr);
it = m_Sessions.erase (it); it = m_Sessions.erase (it);
} }
else else
++it; ++it;
} }
} }
// delivery status sessions
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); )
{
if (it->second->GetOwner () != this)
it = m_DeliveryStatusSessions.erase (it);
else
++it;
}
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{ {
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions.erase (msgID); m_DeliveryStatusSessions.erase (msgID);
} }
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID) void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{ {
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions[msgID] = session; m_DeliveryStatusSessions[msgID] = session;
} }
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
uint32_t msgID = bufbe32toh (msg->GetPayload ()); uint32_t msgID = bufbe32toh (msg->GetPayload ());
GarlicRoutingSessionPtr session;
{ {
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
auto it = m_DeliveryStatusSessions.find (msgID); auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ()) if (it != m_DeliveryStatusSessions.end ())
{ {
it->second->MessageConfirmed (msgID); session = it->second;
m_DeliveryStatusSessions.erase (it); m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
} }
} }
if (session)
{
session->MessageConfirmed (msgID);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
} }
void GarlicDestination::SetLeaseSetUpdated () void GarlicDestination::SetLeaseSetUpdated ()

6
Garlic.h

@ -111,6 +111,9 @@ namespace garlic
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath (); std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path); void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
const GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
private: private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg); size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
@ -128,7 +131,7 @@ namespace garlic
i2p::crypto::AESKey m_SessionKey; i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags; std::list<SessionTag> m_SessionTags;
int m_NumTags; int m_NumTags;
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
LeaseSetUpdateStatus m_LeaseSetUpdateStatus; LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID; uint32_t m_LeaseSetUpdateMsgID;
@ -192,6 +195,7 @@ namespace garlic
// incoming // incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags; std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
// DeliveryStatus // DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public: public:

13
HTTPProxy.cpp

@ -29,7 +29,7 @@ namespace proxy {
static const char *pageHead = static const char *pageHead =
"<head>\r\n" "<head>\r\n"
" <title>I2P HTTP proxy: error</title>\r\n" " <title>I2Pd HTTP proxy</title>\r\n"
" <style type=\"text/css\">\r\n" " <style type=\"text/css\">\r\n"
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n" " .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
@ -60,6 +60,7 @@ namespace proxy {
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */ /* error helpers */
void GenericProxyError(const char *title, const char *description); void GenericProxyError(const char *title, const char *description);
void GenericProxyInfo(const char *title, const char *description);
void HostNotFound(std::string & host); void HostNotFound(std::string & host);
void SendProxyError(std::string & content); void SendProxyError(std::string & content);
@ -107,6 +108,14 @@ namespace proxy {
SendProxyError(content); SendProxyError(content);
} }
void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
std::stringstream ss;
ss << "<h1>Proxy info: " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPReqHandler::HostNotFound(std::string & host) { void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss; std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n" ss << "<h1>Proxy error: Host not found</h1>\r\n"
@ -216,7 +225,7 @@ namespace proxy {
std::stringstream ss; std::stringstream ss;
ss << "Host " << url.host << " added to router's addressbook from helper. " ss << "Host " << url.host << " added to router's addressbook from helper. "
<< "Click <a href=\"" << full_url << "\">here</a> to proceed."; << "Click <a href=\"" << full_url << "\">here</a> to proceed.";
GenericProxyError("Addresshelper found", ss.str().c_str()); GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */ return true; /* request processed */
} }

125
HTTPServer.cpp

@ -33,18 +33,21 @@ namespace i2p {
namespace http { namespace http {
const char *itoopieFavicon = const char *itoopieFavicon =
"data:image/png;base64," "data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv" "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
"8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO" "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
"EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO" "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
"oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG" "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
"ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ" "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
"p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9" "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
"ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM" "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
"rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL" "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
"fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW" "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
"R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9" "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
"MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA" "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
"U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg=="; "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg==";
const char *cssStyles = const char *cssStyles =
"<style>\r\n" "<style>\r\n"
@ -71,6 +74,7 @@ namespace http {
const char HTTP_PAGE_TRANSPORTS[] = "transports"; const char HTTP_PAGE_TRANSPORTS[] = "transports";
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations"; const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination"; const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions"; const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
const char HTTP_PAGE_SAM_SESSION[] = "sam_session"; const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels"; const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
@ -86,7 +90,8 @@ namespace http {
const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address"; const char HTTP_PARAM_ADDRESS[] = "address";
void ShowUptime (std::stringstream& s, int seconds) { static void ShowUptime (std::stringstream& s, int seconds)
{
int num; int num;
if ((num = seconds / 86400) > 0) { if ((num = seconds / 86400) > 0) {
@ -104,7 +109,7 @@ namespace http {
s << seconds << " seconds"; s << seconds << " seconds";
} }
void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
{ {
std::string state; std::string state;
switch (eState) { switch (eState) {
@ -121,7 +126,7 @@ namespace http {
s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n"; s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n";
} }
void ShowPageHead (std::stringstream& s) static void ShowPageHead (std::stringstream& s)
{ {
s << s <<
"<!DOCTYPE html>\r\n" "<!DOCTYPE html>\r\n"
@ -156,7 +161,7 @@ namespace http {
"<div class=right>"; "<div class=right>";
} }
void ShowPageTail (std::stringstream& s) static void ShowPageTail (std::stringstream& s)
{ {
s << s <<
"</div></div>\r\n" "</div></div>\r\n"
@ -164,12 +169,12 @@ namespace http {
"</html>\r\n"; "</html>\r\n";
} }
void ShowError(std::stringstream& s, const std::string& string) static void ShowError(std::stringstream& s, const std::string& string)
{ {
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n"; s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
} }
void ShowStatus (std::stringstream& s) static void ShowStatus (std::stringstream& s)
{ {
s << "<b>Uptime:</b> "; s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ()); ShowUptime(s, i2p::context.GetUptime ());
@ -265,24 +270,34 @@ namespace http {
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n"; s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
} }
void ShowLocalDestinations (std::stringstream& s) static void ShowLocalDestinations (std::stringstream& s)
{ {
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n"; s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetDestinations ()) for (auto& it: i2p::client::context.GetDestinations ())
{ {
auto ident = it.second->GetIdentHash ();; auto ident = it.second->GetIdentHash ();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
} }
}
void ShowLocalDestination (std::stringstream& s, const std::string& b32) auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
{ {
s << "<b>Local Destination:</b><br>\r\n<br>\r\n"; s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
i2p::data::IdentHash ident; for (auto& it: i2cpServer->GetSessions ())
ident.FromBase32 (b32); {
auto dest = i2p::client::context.FindLocalDestination (ident); auto dest = it.second->GetDestination ();
if (dest) if (dest)
{
auto ident = dest->GetIdentHash ();
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
}
}
}
}
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{ {
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";
@ -321,18 +336,18 @@ namespace http {
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl; s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
} }
s << "<br>" << std::endl; s << "<br>" << std::endl;
// s << "<br>\r\n<b>Streams:</b><br>\r\n"; }
// for (auto it: dest->GetStreamingDestination ()->GetStreams ())
// { static void ShowLocalDestination (std::stringstream& s, const std::string& b32)
// s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; {
// s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
// s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; i2p::data::IdentHash ident;
// s << "[buf:" << it.second->GetSendBufferSize () << "]"; ident.FromBase32 (b32);
// s << "[RTT:" << it.second->GetRTT () << "]"; auto dest = i2p::client::context.FindLocalDestination (ident);
// s << "[Window:" << it.second->GetWindowSize () << "]"; if (dest)
// s << "[Status:" << (int)it.second->GetStatus () << "]"; {
// s << "<br>\r\n"<< std::endl; ShowLeaseSetDestination (s, dest);
// } // show streams
s << "<br>\r\n<table><caption>Streams</caption><tr>"; s << "<br>\r\n<table><caption>Streams</caption><tr>";
s << "<th>StreamID</th>"; s << "<th>StreamID</th>";
s << "<th>Destination</th>"; s << "<th>Destination</th>";
@ -365,7 +380,23 @@ namespace http {
} }
} }
void ShowLeasesSets(std::stringstream& s) static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{
auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
{
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination ());
else
ShowError(s, "I2CP session not found");
}
else
ShowError(s, "I2CP is not enabled");
}
static void ShowLeasesSets(std::stringstream& s)
{ {
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n"; s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
int counter = 1; int counter = 1;
@ -398,7 +429,7 @@ namespace http {
// end for each lease set // end for each lease set
} }
void ShowTunnels (std::stringstream& s) static void ShowTunnels (std::stringstream& s)
{ {
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n"; s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
@ -420,7 +451,7 @@ namespace http {
s << "<br>\r\n"; s << "<br>\r\n";
} }
void ShowCommands (std::stringstream& s) static void ShowCommands (std::stringstream& s)
{ {
/* commands */ /* commands */
s << "<b>Router Commands</b><br>\r\n"; s << "<b>Router Commands</b><br>\r\n";
@ -442,7 +473,7 @@ namespace http {
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n"; s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
} }
void ShowTransitTunnels (std::stringstream& s) static void ShowTransitTunnels (std::stringstream& s)
{ {
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n"; s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
@ -457,7 +488,7 @@ namespace http {
} }
} }
void ShowTransports (std::stringstream& s) static void ShowTransports (std::stringstream& s)
{ {
s << "<b>Transports:</b><br>\r\n<br>\r\n"; s << "<b>Transports:</b><br>\r\n<br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer (); auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
@ -506,7 +537,7 @@ namespace http {
} }
} }
void ShowSAMSessions (std::stringstream& s) static void ShowSAMSessions (std::stringstream& s)
{ {
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) { if (!sam) {
@ -521,7 +552,7 @@ namespace http {
} }
} }
void ShowSAMSession (std::stringstream& s, const std::string& id) static void ShowSAMSession (std::stringstream& s, const std::string& id)
{ {
s << "<b>SAM Session:</b><br>\r\n<br>\r\n"; s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
@ -553,7 +584,7 @@ namespace http {
} }
} }
void ShowI2PTunnels (std::stringstream& s) static void ShowI2PTunnels (std::stringstream& s)
{ {
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n"; s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetClientTunnels ()) for (auto& it: i2p::client::context.GetClientTunnels ())
@ -743,6 +774,8 @@ namespace http {
ShowLocalDestinations (s); ShowLocalDestinations (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATION) else if (page == HTTP_PAGE_LOCAL_DESTINATION)
ShowLocalDestination (s, params["b32"]); ShowLocalDestination (s, params["b32"]);
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
ShowI2CPLocalDestination (s, params["i2cp_id"]);
else if (page == HTTP_PAGE_SAM_SESSIONS) else if (page == HTTP_PAGE_SAM_SESSIONS)
ShowSAMSessions (s); ShowSAMSessions (s);
else if (page == HTTP_PAGE_SAM_SESSION) else if (page == HTTP_PAGE_SAM_SESSION)

13
I2CP.cpp

@ -346,6 +346,7 @@ namespace client
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
{ {
RAND_bytes ((uint8_t *)&m_SessionID, 2); RAND_bytes ((uint8_t *)&m_SessionID, 2);
m_Owner.InsertSession (shared_from_this ());
auto identity = std::make_shared<i2p::data::IdentityEx>(); auto identity = std::make_shared<i2p::data::IdentityEx>();
size_t offset = identity->FromBuffer (buf, len); size_t offset = identity->FromBuffer (buf, len);
if (!offset) if (!offset)
@ -711,7 +712,6 @@ namespace client
{ {
LogPrint (eLogDebug, "I2CP: new connection from ", ep); LogPrint (eLogDebug, "I2CP: new connection from ", ep);
auto session = std::make_shared<I2CPSession>(*this, socket); auto session = std::make_shared<I2CPSession>(*this, socket);
m_Sessions[session->GetSessionID ()] = session;
session->Start (); session->Start ();
} }
else else
@ -724,6 +724,17 @@ namespace client
Accept (); Accept ();
} }
bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
{
if (!session) return false;
if (!m_Sessions.insert({session->GetSessionID (), session}).second)
{
LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ());
return false;
}
return true;
}
void I2CPServer::RemoveSession (uint16_t sessionID) void I2CPServer::RemoveSession (uint16_t sessionID)
{ {
m_Sessions.erase (sessionID); m_Sessions.erase (sessionID);

5
I2CP.h

@ -112,6 +112,7 @@ namespace client
void Start (); void Start ();
void Stop (); void Stop ();
uint16_t GetSessionID () const { return m_SessionID; }; uint16_t GetSessionID () const { return m_SessionID; };
std::shared_ptr<const I2CPDestination> GetDestination () const { return m_Destination; };
// called from I2CPDestination // called from I2CPDestination
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
@ -173,6 +174,7 @@ namespace client
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
bool InsertSession (std::shared_ptr<I2CPSession> session);
void RemoveSession (uint16_t sessionID); void RemoveSession (uint16_t sessionID);
private: private:
@ -196,6 +198,9 @@ namespace client
public: public:
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; }; const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
}; };
} }
} }

59
Log.cpp

@ -58,13 +58,30 @@ namespace log {
Log::Log(): Log::Log():
m_Destination(eLogStdout), m_MinLevel(eLogInfo), m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false), m_HasColors(true) m_LogStream (nullptr), m_Logfile(""), m_HasColors(true),
m_IsRunning (false), m_Thread (nullptr)
{ {
} }
Log::~Log () Log::~Log ()
{ {
switch (m_Destination) { delete m_Thread;
}
void Log::Start ()
{
if (!m_IsRunning)
{
Reopen ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Log::Run, this));
}
}
void Log::Stop ()
{
switch (m_Destination)
{
#ifndef _WIN32 #ifndef _WIN32
case eLogSyslog : case eLogSyslog :
closelog(); closelog();
@ -78,7 +95,14 @@ namespace log {
/* do nothing */ /* do nothing */
break; break;
} }
Process(); m_IsRunning = false;
m_Queue.WakeUp ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
} }
void Log::SetLogLevel (const std::string& level) { void Log::SetLogLevel (const std::string& level) {
@ -106,14 +130,11 @@ namespace log {
* Unfortunately, with current startup process with late fork() this * Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example. * will give us nothing but pain. Maybe later. See in NetDb as example.
*/ */
void Log::Process() { void Log::Process(std::shared_ptr<LogMsg> msg)
std::unique_lock<std::mutex> l(m_OutputLock); {
if (!msg) return;
std::hash<std::thread::id> hasher; std::hash<std::thread::id> hasher;
unsigned short short_tid; unsigned short short_tid;
while (1) {
auto msg = m_Queue.GetNextWithTimeout (1);
if (!msg)
break;
short_tid = (short) (hasher(msg->tid) % 1000); short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) { switch (m_Destination) {
#ifndef _WIN32 #ifndef _WIN32
@ -137,14 +158,24 @@ namespace log {
<< " - " << msg->text << std::endl; << " - " << msg->text << std::endl;
break; break;
} // switch } // switch
} // while
} }
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) { void Log::Run ()
{
while (m_IsRunning)
{
std::shared_ptr<LogMsg> msg;
while (msg = m_Queue.Get ())
Process (msg);
if (m_LogStream) m_LogStream->flush();
if (m_IsRunning)
m_Queue.Wait ();
}
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
{
m_Queue.Put(msg); m_Queue.Put(msg);
if (!m_IsReady)
return;
Process();
} }
void Log::SendTo (const std::string& path) void Log::SendTo (const std::string& path)

20
Log.h

@ -16,6 +16,7 @@
#include <sstream> #include <sstream>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <thread>
#include "Queue.h" #include "Queue.h"
#ifndef _WIN32 #ifndef _WIN32
@ -56,9 +57,9 @@ namespace log {
std::time_t m_LastTimestamp; std::time_t m_LastTimestamp;
char m_LastDateTime[64]; char m_LastDateTime[64];
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue; i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
volatile bool m_IsReady;
bool m_HasColors; bool m_HasColors;
mutable std::mutex m_OutputLock; volatile bool m_IsRunning;
std::thread * m_Thread;
private: private:
@ -66,10 +67,8 @@ namespace log {
Log (const Log &); Log (const Log &);
const Log& operator=(const Log&); const Log& operator=(const Log&);
/** void Run ();
* @brief process stored messages in queue void Process (std::shared_ptr<LogMsg> msg);
*/
void Process ();
/** /**
* @brief Makes formatted string from unix timestamp * @brief Makes formatted string from unix timestamp
@ -87,6 +86,9 @@ namespace log {
LogType GetLogType () { return m_Destination; }; LogType GetLogType () { return m_Destination; };
LogLevel GetLogLevel () { return m_MinLevel; }; LogLevel GetLogLevel () { return m_MinLevel; };
void Start ();
void Stop ();
/** /**
* @brief Sets minimal allowed level for log messages * @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level * @param level String with wanted minimal msg level
@ -120,12 +122,6 @@ namespace log {
*/ */
void Append(std::shared_ptr<i2p::log::LogMsg> &); void Append(std::shared_ptr<i2p::log::LogMsg> &);
/** @brief Allow log output */
void Ready() { m_IsReady = true; }
/** @brief Flushes the output log stream */
void Flush();
/** @brief Reopen log file */ /** @brief Reopen log file */
void Reopen(); void Reopen();
}; };

20
NTCPSession.cpp

@ -574,17 +574,17 @@ namespace transport
if (dataSize) if (dataSize)
{ {
// new message // new message
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding if (dataSize + 16U + 15U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
{ {
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size"); LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
return false; return false;
} }
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage (); m_NextMessage = (dataSize + 16U + 15U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
m_NextMessage = msg; m_NextMessage->Align (16);
memcpy (m_NextMessage->buf, buf, 16); m_NextMessage->offset += 2; // size field
m_NextMessage->len = m_NextMessage->offset + dataSize;
memcpy (m_NextMessage->GetBuffer () - 2, buf, 16);
m_NextMessageOffset = 16; m_NextMessageOffset = 16;
m_NextMessage->offset = 2; // size field
m_NextMessage->len = dataSize + 2;
} }
else else
{ {
@ -595,16 +595,16 @@ namespace transport
} }
else // message continues else // message continues
{ {
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset); m_Decryption.Decrypt (encrypted, m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset);
m_NextMessageOffset += 16; m_NextMessageOffset += 16;
} }
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum if (m_NextMessageOffset >= m_NextMessage->GetLength () + 2 + 4) // +checksum
{ {
// we have a complete I2NP message // we have a complete I2NP message
uint8_t checksum[4]; uint8_t checksum[4];
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4)); htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->GetBuffer () - 2, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4)) if (!memcmp (m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset - 4, checksum, 4))
{ {
if (!m_NextMessage->IsExpired ()) if (!m_NextMessage->IsExpired ())
{ {

14
Reseed.cpp

@ -41,19 +41,23 @@ namespace data
std::string filename; i2p::config::GetOption("reseed.file", filename); std::string filename; i2p::config::GetOption("reseed.file", filename);
if (filename.length() > 0) // reseed file is specified if (filename.length() > 0) // reseed file is specified
{ {
if (filename.length() > 8 && filename.substr(0, 8) == "https://")
{
return ReseedFromSU3 (filename); // reseed from https URL
} else {
auto num = ProcessSU3File (filename.c_str ()); auto num = ProcessSU3File (filename.c_str ());
if (num > 0) return num; // success if (num > 0) return num; // success
LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts"); LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts");
} }
}
auto ind = rand () % httpsReseedHostList.size (); auto ind = rand () % httpsReseedHostList.size ();
std::string& reseedHost = httpsReseedHostList[ind]; std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3";
return ReseedFromSU3 (reseedHost); return ReseedFromSU3 (reseedUrl);
} }
int Reseeder::ReseedFromSU3 (const std::string& host) int Reseeder::ReseedFromSU3 (const std::string& url)
{ {
std::string url = host + "i2pseeds.su3"; LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url);
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
std::string su3 = HttpsRequest (url); std::string su3 = HttpsRequest (url);
if (su3.length () > 0) if (su3.length () > 0)
{ {

2
Reseed.h

@ -29,7 +29,7 @@ namespace data
void LoadCertificate (const std::string& filename); void LoadCertificate (const std::string& filename);
int ReseedFromSU3 (const std::string& host); int ReseedFromSU3 (const std::string& url);
int ProcessSU3File (const char * filename); int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s); int ProcessSU3Stream (std::istream& s);

2
RouterContext.cpp

@ -215,8 +215,8 @@ namespace i2p
switch (type) switch (type)
{ {
case low : /* not set */; break; case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
} }
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
UpdateRouterInfo (); UpdateRouterInfo ();

13
Streaming.cpp

@ -611,13 +611,11 @@ namespace stream
return; return;
} }
} }
if (!m_CurrentOutboundTunnel) // first message to send if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) // expired and detached
{
// try to get shared path first
if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
if (m_RoutingSession) if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send
{ {
// try to get shared path first
auto routingPath = m_RoutingSession->GetSharedRoutingPath (); auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
if (routingPath) if (routingPath)
{ {
@ -627,7 +625,6 @@ namespace stream
m_RTO = m_RTT*1.5; // TODO: implement it better m_RTO = m_RTT*1.5; // TODO: implement it better
} }
} }
}
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel) if (!m_CurrentOutboundTunnel)
@ -900,13 +897,13 @@ namespace stream
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{ {
uint32_t receiveStreamID = packet->GetReceiveStreamID (); uint32_t receiveStreamID = packet->GetReceiveStreamID ();
/* if (receiveStreamID == m_LastIncomingReceiveStreamID) if (receiveStreamID == m_LastIncomingReceiveStreamID)
{ {
// already pending // already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
delete packet; // drop it, because previous should be connected delete packet; // drop it, because previous should be connected
return; return;
} */ }
auto incomingStream = CreateNewIncomingStream (); auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet); // SYN incomingStream->HandleNextPacket (packet); // SYN
auto ident = incomingStream->GetRemoteIdentity(); auto ident = incomingStream->GetRemoteIdentity();

2
TunnelPool.cpp

@ -232,7 +232,7 @@ namespace tunnel
for (int i = num; i < m_NumInboundTunnels; i++) for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel (); CreateInboundTunnel ();
if (num > 0 && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
} }

3
UPnP.cpp

@ -66,10 +66,13 @@ namespace transport
try try
{ {
m_Service.run (); m_Service.run ();
// Discover failed
break; // terminate the thread
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ()); LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
PortMapping ();
} }
} }
} }

2
Win32/PurpleI2P.nsi

@ -14,7 +14,7 @@ ShowInstDetails show
!define URL "https://i2p.io" !define URL "https://i2p.io"
# MUI Symbol Definitions # MUI Symbol Definitions
!define MUI_ICON "ictoopie.ico" !define MUI_ICON "mask.ico"
#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp" #!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
!define MUI_HEADERIMAGE !define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_RIGHT !define MUI_HEADERIMAGE_RIGHT

2
Win32/Resource.rc

@ -52,7 +52,7 @@ END
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
MAINICON ICON "ictoopie.ico" MAINICON ICON "mask.ico"
//MAINICON ICON "anke.ico" //MAINICON ICON "anke.ico"
#endif // English (United States) resources #endif // English (United States) resources

1
Win32/installer.iss

@ -12,6 +12,7 @@ UninstallDisplayIcon={app}\I2Pd.exe
OutputDir=. OutputDir=.
LicenseFile=../LICENSE LicenseFile=../LICENSE
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver} OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
SetupIconFile=mask.ico
InternalCompressLevel=ultra64 InternalCompressLevel=ultra64
Compression=lzma/ultra64 Compression=lzma/ultra64
SolidCompression=true SolidCompression=true

BIN
Win32/mask.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
Win32/mask.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

3
api.cpp

@ -47,7 +47,7 @@ namespace api
i2p::log::Logger().SendTo (logStream); i2p::log::Logger().SendTo (logStream);
else else
i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log"));
i2p::log::Logger().Ready(); i2p::log::Logger().Start ();
LogPrint(eLogInfo, "API: starting NetDB"); LogPrint(eLogInfo, "API: starting NetDB");
i2p::data::netdb.Start(); i2p::data::netdb.Start();
LogPrint(eLogInfo, "API: starting Transports"); LogPrint(eLogInfo, "API: starting Transports");
@ -65,6 +65,7 @@ namespace api
i2p::transport::transports.Stop(); i2p::transport::transports.Stop();
LogPrint(eLogInfo, "API: stopping NetDB"); LogPrint(eLogInfo, "API: stopping NetDB");
i2p::data::netdb.Stop(); i2p::data::netdb.Stop();
i2p::log::Logger().Stop ();
} }
void RunPeerTest () void RunPeerTest ()

2
build/CMakeLists.txt

@ -524,7 +524,7 @@ if((WIN32 OR MSYS) AND NOT UNIX)
# There is a bug in NSI that does not handle full unix paths properly. Make # There is a bug in NSI that does not handle full unix paths properly. Make
# sure there is at least one set of four (4) backlasshes. # sure there is at least one set of four (4) backlasshes.
set(CPACK_NSIS_DEFINES "RequestExecutionLevel user") set(CPACK_NSIS_DEFINES "RequestExecutionLevel user")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../Win32\\\\ictoopie.bmp") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../Win32\\\\mask.bmp")
set(CPACK_NSIS_INSTALLED_ICON_NAME "bin/i2pd.exe") set(CPACK_NSIS_INSTALLED_ICON_NAME "bin/i2pd.exe")
SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_NSIS_HELP_LINK "https:\\\\\\\\github.com\\\\PurpleI2P\\\\i2pd\\\\issues") set(CPACK_NSIS_HELP_LINK "https:\\\\\\\\github.com\\\\PurpleI2P\\\\i2pd\\\\issues")

4
docs/i2pd.conf

@ -97,8 +97,10 @@ verify = true
## URLs to request reseed data from, separated by comma ## URLs to request reseed data from, separated by comma
## Default: "mainline" I2P Network reseeds ## Default: "mainline" I2P Network reseeds
# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ # urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/
## Path to reseed data file (.su3) for manual reseeding ## Path to local reseed data file (.su3) for manual reseeding
# file = /path/to/i2pseeds.su3 # file = /path/to/i2pseeds.su3
## or HTTPS URL to reseed from
# file = https://legit-website.com/i2pseeds.su3
[addressbook] [addressbook]
## AddressBook subscription URL for initial setup ## AddressBook subscription URL for initial setup

Loading…
Cancel
Save