From 8a2c4ab3debba75129d35087e4f3776334a6e03e Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 1 Feb 2017 15:20:03 -0500 Subject: [PATCH 01/27] don't create identity if presented in netdb already --- NTCPSession.cpp | 7 +++++-- SSUSession.cpp | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 883f0a11..48b4aecb 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -346,12 +346,15 @@ namespace transport m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); uint8_t * buf = m_ReceiveBuffer; uint16_t size = bufbe16toh (buf); - SetRemoteIdentity (std::make_shared (buf + 2, size)); - if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ())) + auto identity = std::make_shared (buf + 2, size); + if (m_Server.FindNTCPSession (identity->GetIdentHash ())) { LogPrint (eLogInfo, "NTCP: session already exists"); Terminate (); } + auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already + SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity); + size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen (); size_t paddingLen = expectedSize & 0x0F; if (paddingLen) paddingLen = (16 - paddingLen); diff --git a/SSUSession.cpp b/SSUSession.cpp index 844ed2e4..d54a1137 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -4,6 +4,7 @@ #include "Timestamp.h" #include "RouterContext.h" #include "Transports.h" +#include "NetDb.h" #include "SSU.h" #include "SSUSession.h" @@ -317,7 +318,9 @@ namespace transport payload++; // identity fragment info uint16_t identitySize = bufbe16toh (payload); payload += 2; // size of identity fragment - SetRemoteIdentity (std::make_shared (payload, identitySize)); + auto identity = std::make_shared (payload, identitySize); + auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already + SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity); m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ()); payload += identitySize; // identity auto ts = i2p::util::GetSecondsSinceEpoch (); From 21e23d5511e79819313115c7c790847b3e72ed5d Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Wed, 1 Feb 2017 17:17:25 -0500 Subject: [PATCH 02/27] Added option to reseed from ZIP file --- Config.cpp | 1 + NetDb.cpp | 41 +++++++++++++++++++++++++++++++++++------ Reseed.cpp | 18 +++--------------- Reseed.h | 8 ++++---- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Config.cpp b/Config.cpp index 7e396b88..bf6080a1 100644 --- a/Config.cpp +++ b/Config.cpp @@ -167,6 +167,7 @@ namespace config { ("reseed.verify", value()->default_value(false), "Verify .su3 signature") ("reseed.floodfill", value()->default_value(""), "Path to router info of floodfill to reseed from") ("reseed.file", value()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") + ("reseed.zipfile", value()->default_value(""), "Path to local .zip file to reseed from") ("reseed.urls", value()->default_value( "https://reseed.i2p-projekt.de/," "https://i2p.mooo.com/netDb/," diff --git a/NetDb.cpp b/NetDb.cpp index 8b4ec88a..1c5758df 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -308,7 +308,6 @@ namespace data m_Reseeder = new Reseeder (); m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification } - int reseedRetries = 0; // try reseeding from floodfill first if specified std::string riPath; @@ -328,11 +327,41 @@ namespace data return; } } - - while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ()) - reseedRetries++; - if (reseedRetries >= 10) - LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts"); + + + std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); + std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); + + if (su3FileName.length() > 0) // bootstrap from SU3 file or URL + { + int num; + if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") + { + num = m_Reseeder->ReseedFromSU3Url (su3FileName); // from https URL + } + else + { + num = m_Reseeder->ProcessSU3File (su3FileName.c_str ()); + } + if (num == 0) + LogPrint (eLogWarning, "NetDb: failed to reseed from ", su3FileName); + return; + } + else if (zipFileName.length() > 0) // bootstrap from ZIP file + { + int num = m_Reseeder->ProcessZIPFile (zipFileName.c_str ()); + if (num == 0) + LogPrint (eLogWarning, "NetDb: failed to reseed from ", zipFileName); + return; + } + else // bootstrap from reseed servers + { + int reseedRetries = 0; + while (reseedRetries < 10 && !m_Reseeder->ReseedFromServers ()) + reseedRetries++; + if (reseedRetries >= 10) + LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts"); + } } void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) diff --git a/Reseed.cpp b/Reseed.cpp index 9387d2ae..10f3cf5a 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -32,30 +32,18 @@ namespace data { } - int Reseeder::ReseedNowSU3 () + int Reseeder::ReseedFromServers () { std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); std::vector httpsReseedHostList; boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); - std::string filename; i2p::config::GetOption("reseed.file", filename); - 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 ()); - if (num > 0) return num; // success - LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts"); - } - } auto ind = rand () % httpsReseedHostList.size (); std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3"; - return ReseedFromSU3 (reseedUrl); + return ReseedFromSU3Url (reseedUrl); } - int Reseeder::ReseedFromSU3 (const std::string& url) + int Reseeder::ReseedFromSU3Url (const std::string& url) { LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url); std::string su3 = HttpsRequest (url); diff --git a/Reseed.h b/Reseed.h index 3ef8767c..72ff4c55 100644 --- a/Reseed.h +++ b/Reseed.h @@ -21,7 +21,10 @@ namespace data Reseeder(); ~Reseeder(); - int ReseedNowSU3 (); + int ReseedFromServers (); + int ReseedFromSU3Url (const std::string& url); + int ProcessSU3File (const char * filename); + int ProcessZIPFile (const char * filename); void LoadCertificates (); @@ -29,9 +32,6 @@ namespace data void LoadCertificate (const std::string& filename); - int ReseedFromSU3 (const std::string& url); - int ProcessSU3File (const char * filename); - int ProcessZIPFile (const char * filename); int ProcessSU3Stream (std::istream& s); int ProcessZIPStream (std::istream& s, uint64_t contentLength); From a03bf891905c146a30f886eb63b579120bfa3c39 Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Thu, 2 Feb 2017 15:25:25 -0500 Subject: [PATCH 03/27] Refactored code to Reseed module --- NetDb.cpp | 35 +---------------------------- Reseed.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++----- Reseed.h | 1 + 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/NetDb.cpp b/NetDb.cpp index 1c5758df..e246cb7b 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -328,40 +328,7 @@ namespace data } } - - std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); - std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); - - if (su3FileName.length() > 0) // bootstrap from SU3 file or URL - { - int num; - if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") - { - num = m_Reseeder->ReseedFromSU3Url (su3FileName); // from https URL - } - else - { - num = m_Reseeder->ProcessSU3File (su3FileName.c_str ()); - } - if (num == 0) - LogPrint (eLogWarning, "NetDb: failed to reseed from ", su3FileName); - return; - } - else if (zipFileName.length() > 0) // bootstrap from ZIP file - { - int num = m_Reseeder->ProcessZIPFile (zipFileName.c_str ()); - if (num == 0) - LogPrint (eLogWarning, "NetDb: failed to reseed from ", zipFileName); - return; - } - else // bootstrap from reseed servers - { - int reseedRetries = 0; - while (reseedRetries < 10 && !m_Reseeder->ReseedFromServers ()) - reseedRetries++; - if (reseedRetries >= 10) - LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts"); - } + m_Reseeder->Bootstrap (); } void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) diff --git a/Reseed.cpp b/Reseed.cpp index 10f3cf5a..24df13f7 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -32,17 +32,73 @@ namespace data { } + /** @brief tries to bootstrap into I2P network (from local files and servers, with respect of options) + */ + void Reseeder::Bootstrap () + { + std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); + std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); + + if (su3FileName.length() > 0) // bootstrap from SU3 file or URL + { + int num; + if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") + { + num = ReseedFromSU3Url (su3FileName); // from https URL + } + else + { + num = ProcessSU3File (su3FileName.c_str ()); + } + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName); + } + else if (zipFileName.length() > 0) // bootstrap from ZIP file + { + int num = ProcessZIPFile (zipFileName.c_str ()); + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName); + } + else // bootstrap from reseed servers + { + int num = ReseedFromServers (); + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from servers"); + } + } + + /** @brief bootstrap from random server, retry 10 times + * @return number of entries added to netDb + */ int Reseeder::ReseedFromServers () { std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); - std::vector httpsReseedHostList; - boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); + std::vector httpsReseedHostList; + boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); - auto ind = rand () % httpsReseedHostList.size (); - std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3"; - return ReseedFromSU3Url (reseedUrl); + if (reseedURLs.length () == 0) + { + LogPrint (eLogWarning, "Reseed: No reseed servers specified"); + return 0; + } + + int reseedRetries = 0; + while (reseedRetries < 10) + { + auto ind = rand () % httpsReseedHostList.size (); + std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3"; + auto num = ReseedFromSU3Url (reseedUrl); + if (num > 0) return num; // success + reseedRetries++; + } + LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts"); + return 0; } + /** @brief bootstrap from HTTPS URL with SU3 file + * @param url + * @return number of entries added to netDb + */ int Reseeder::ReseedFromSU3Url (const std::string& url) { LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url); diff --git a/Reseed.h b/Reseed.h index 72ff4c55..6b09f911 100644 --- a/Reseed.h +++ b/Reseed.h @@ -21,6 +21,7 @@ namespace data Reseeder(); ~Reseeder(); + void Bootstrap (); int ReseedFromServers (); int ReseedFromSU3Url (const std::string& url); int ProcessSU3File (const char * filename); From 33310732a6aaa941473240a3bc02ac521181f2d2 Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Thu, 2 Feb 2017 15:40:57 -0500 Subject: [PATCH 04/27] Add reseed.threshold option --- Config.cpp | 1 + NetDb.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Config.cpp b/Config.cpp index bf6080a1..0ed09ae8 100644 --- a/Config.cpp +++ b/Config.cpp @@ -165,6 +165,7 @@ namespace config { options_description reseed("Reseed options"); reseed.add_options() ("reseed.verify", value()->default_value(false), "Verify .su3 signature") + ("reseed.threshold", value()->default_value(25), "Minimum number of known routers before requesting reseed") ("reseed.floodfill", value()->default_value(""), "Path to router info of floodfill to reseed from") ("reseed.file", value()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") ("reseed.zipfile", value()->default_value(""), "Path to local .zip file to reseed from") diff --git a/NetDb.cpp b/NetDb.cpp index e246cb7b..b4ad6399 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -41,7 +41,9 @@ namespace data InitProfilesStorage (); m_Families.LoadCertificates (); Load (); - if (m_RouterInfos.size () < 25) // reseed if # of router less than 50 + + uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); + if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold Reseed (); m_IsRunning = true; From 9e57a4ea2854d48d44c87c2e0f4a968cc558d184 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 2 Feb 2017 20:45:33 -0500 Subject: [PATCH 05/27] use I2NP message of tunnel data length for TunnelData --- I2NPProtocol.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 96b30cb6..0a5031e7 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -27,6 +27,13 @@ namespace i2p return std::make_shared >(); } + std::shared_ptr NewI2NPTunnelMessage () + { + auto msg = new I2NPMessageBuffer(); // reserved for alignment and NTCP 16 + 6 + 12 + msg->Align (12); + return std::shared_ptr(msg); + } + std::shared_ptr NewI2NPMessage (size_t len) { return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); @@ -464,7 +471,7 @@ namespace i2p std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) { - auto msg = NewI2NPShortMessage (); + auto msg = NewI2NPTunnelMessage (); msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); msg->FillI2NPMessageHeader (eI2NPTunnelData); return msg; @@ -472,7 +479,7 @@ namespace i2p std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) { - auto msg = NewI2NPShortMessage (); + auto msg = NewI2NPTunnelMessage (); htobe32buf (msg->GetPayload (), tunnelID); msg->len += 4; // tunnelID msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); @@ -482,7 +489,7 @@ namespace i2p std::shared_ptr CreateEmptyTunnelDataMsg () { - auto msg = NewI2NPShortMessage (); + auto msg = NewI2NPTunnelMessage (); msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; return msg; } From 99436c1334440bb0b6a0534476c2470020f7ea5b Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Fri, 3 Feb 2017 14:13:55 -0500 Subject: [PATCH 06/27] Added protection from clickjacking (#706) --- HTTPServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 184e02b1..1b4512eb 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -875,6 +875,7 @@ namespace http { void HTTPConnection::SendReply (HTTPRes& reply, std::string& content) { + reply.add_header("X-Frame-Options", "SAMEORIGIN"); reply.add_header("Content-Type", "text/html"); reply.body = content; From 7ae38a71cc7f0f0b1d8fb6e057e67e658776be71 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 3 Feb 2017 20:57:04 -0500 Subject: [PATCH 07/27] reduced I2NP message size for tunnel gateway --- TunnelGateway.cpp | 18 ++++++++++-------- TunnelGateway.h | 9 ++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index 9f13d84c..6ac709d2 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -10,8 +10,8 @@ namespace i2p { namespace tunnel { - TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), - m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) + TunnelGatewayBuffer::TunnelGatewayBuffer (): + m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) { RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) @@ -165,7 +165,6 @@ namespace tunnel m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); - htobe32buf (buf, m_TunnelID); RAND_bytes (buf + 4, 16); // original IV memcpy (payload + size, buf + 4, 16); // copy IV for checksum uint8_t hash[32]; @@ -203,15 +202,18 @@ namespace tunnel void TunnelGateway::SendBuffer () { m_Buffer.CompleteCurrentTunnelDataMessage (); - const auto & tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); - for (auto& tunnelMsg : tunnelMsgs) + std::vector > newTunnelMsgs; + for (auto& tunnelMsg : m_Buffer.GetTunnelDataMsgs ()) { - m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg); - tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); + auto newMsg = CreateEmptyTunnelDataMsg (); + m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); + htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); + newMsg->FillI2NPMessageHeader (eI2NPTunnelData); + newTunnelMsgs.push_back (newMsg); m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; } - i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs); m_Buffer.ClearTunnelDataMsgs (); + i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs); } } } diff --git a/TunnelGateway.h b/TunnelGateway.h index ea88317b..3642874c 100644 --- a/TunnelGateway.h +++ b/TunnelGateway.h @@ -14,10 +14,10 @@ namespace tunnel class TunnelGatewayBuffer { public: - TunnelGatewayBuffer (uint32_t tunnelID); + TunnelGatewayBuffer (); ~TunnelGatewayBuffer (); void PutI2NPMsg (const TunnelMessageBlock& block); - const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; + const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; void ClearTunnelDataMsgs (); void CompleteCurrentTunnelDataMessage (); @@ -27,8 +27,7 @@ namespace tunnel private: - uint32_t m_TunnelID; - std::vector > m_TunnelDataMsgs; + std::vector > m_TunnelDataMsgs; std::shared_ptr m_CurrentTunnelDataMsg; size_t m_RemainingSize; uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; @@ -39,7 +38,7 @@ namespace tunnel public: TunnelGateway (TunnelBase * tunnel): - m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {}; + m_Tunnel (tunnel), m_NumSentBytes (0) {}; void SendTunnelDataMsg (const TunnelMessageBlock& block); void PutTunnelDataMsg (const TunnelMessageBlock& block); void SendBuffer (); From 66f3bd186f58d74aa4d0cbcda9b7cf4626c050d6 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 4 Feb 2017 22:39:54 -0500 Subject: [PATCH 08/27] send http headers in original order --- AddressBook.cpp | 10 +++---- HTTP.cpp | 77 +++++++++++++++++++++++++++++++++++++------------ HTTP.h | 15 +++++++--- HTTPProxy.cpp | 67 ++++++++++++++++++++---------------------- HTTPServer.cpp | 6 ++-- Reseed.cpp | 4 +-- 6 files changed, 113 insertions(+), 66 deletions(-) diff --git a/AddressBook.cpp b/AddressBook.cpp index 4dad5614..20a118fb 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -703,13 +703,13 @@ namespace client int dest_port = url.port ? url.port : 80; /* create http request & send it */ i2p::http::HTTPReq req; - req.add_header("Host", dest_host); - req.add_header("User-Agent", "Wget/1.11.4"); - req.add_header("Connection", "close"); + req.AddHeader("Host", dest_host); + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Connection", "close"); if (!m_Etag.empty()) - req.add_header("If-None-Match", m_Etag); + req.AddHeader("If-None-Match", m_Etag); if (!m_LastModified.empty()) - req.add_header("If-Modified-Since", m_LastModified); + req.AddHeader("If-Modified-Since", m_LastModified); /* convert url to relative */ url.schema = ""; url.host = ""; diff --git a/HTTP.cpp b/HTTP.cpp index 201a4e3d..6ab6ecf4 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -1,14 +1,15 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2017, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ +#include +#include #include "util.h" #include "HTTP.h" -#include #include namespace i2p { @@ -43,18 +44,16 @@ namespace http { } } - bool parse_header_line(const std::string & line, std::map & headers) { + static std::pair parse_header_line(const std::string& line) + { std::size_t pos = 0; std::size_t len = 2; /* strlen(": ") */ std::size_t max = line.length(); if ((pos = line.find(": ", pos)) == std::string::npos) - return false; + return std::make_pair("", ""); while ((pos + len) < max && isspace(line.at(pos + len))) len++; - std::string name = line.substr(0, pos); - std::string value = line.substr(pos + len); - headers[name] = value; - return true; + return std::make_pair(line.substr(0, pos), line.substr(pos + len)); } void gen_rfc1123_date(std::string & out) { @@ -247,10 +246,15 @@ namespace http { uri = tokens[1]; version = tokens[2]; expect = HEADER_LINE; - } else { + } + else + { std::string line = str.substr(pos, eol - pos); - if (!parse_header_line(line, headers)) - return -1; + auto p = parse_header_line(line); + if (p.first.length () > 0) + headers.push_back (p); + else + return -1; } pos = eol + strlen(CRLF); if (pos >= eoh) @@ -259,12 +263,12 @@ namespace http { return eoh + strlen(HTTP_EOH); } - void HTTPReq::write(std::ostream & o) { - o << method << " " << uri << " " << version << CRLF; - for (auto & h : headers) { - o << h.first << ": " << h.second << CRLF; - } - o << CRLF; + void HTTPReq::write(std::ostream & o) + { + o << method << " " << uri << " " << version << CRLF; + for (auto & h : headers) + o << h.first << ": " << h.second << CRLF; + o << CRLF; } std::string HTTPReq::to_string() @@ -274,6 +278,40 @@ namespace http { return ss.str(); } + void HTTPReq::AddHeader (const std::string& name, const std::string& value) + { + headers.push_back (std::make_pair(name, value)); + } + + void HTTPReq::UpdateHeader (const std::string& name, const std::string& value) + { + for (auto& it : headers) + if (it.first == name) + { + it.second = value; + break; + } + } + + void HTTPReq::RemoveHeader (const std::string& name) + { + for (auto it = headers.begin (); it != headers.end ();) + { + if (!it->first.compare(0, name.length (), name)) + it = headers.erase (it); + else + it++; + } + } + + std::string HTTPReq::GetHeader (const std::string& name) const + { + for (auto& it : headers) + if (it.first == name) + return it.second; + return ""; + } + bool HTTPRes::is_chunked() { auto it = headers.find("Transfer-Encoding"); if (it == headers.end()) @@ -335,7 +373,10 @@ namespace http { expect = HEADER_LINE; } else { std::string line = str.substr(pos, eol - pos); - if (!parse_header_line(line, headers)) + auto p = parse_header_line(line); + if (p.first.length () > 0) + headers.insert (p); + else return -1; } pos = eol + strlen(CRLF); diff --git a/HTTP.h b/HTTP.h index 581e4a34..3175cb79 100644 --- a/HTTP.h +++ b/HTTP.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,8 @@ namespace http { std::string to_string (); }; - struct HTTPMsg { + struct HTTPMsg + { std::map headers; void add_header(const char *name, std::string & value, bool replace = false); @@ -65,7 +67,9 @@ namespace http { long int content_length(); }; - struct HTTPReq : HTTPMsg { + struct HTTPReq + { + std::list > headers; std::string version; std::string method; std::string uri; @@ -82,9 +86,12 @@ namespace http { /** @brief Serialize HTTP request to string */ std::string to_string(); - - void write(std::ostream & o); + void write(std::ostream & o); + void AddHeader (const std::string& name, const std::string& value); + void UpdateHeader (const std::string& name, const std::string& value); + void RemoveHeader (const std::string& name); + std::string GetHeader (const std::string& name) const; }; struct HTTPRes : HTTPMsg { diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 33a2a85c..e4d765ac 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -200,26 +200,16 @@ namespace proxy { void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) { /* drop common headers */ - req.del_header("Referer"); - req.del_header("Via"); - req.del_header("Forwarded"); + req.RemoveHeader ("Referer"); + req.RemoveHeader("Via"); + req.RemoveHeader("Forwarded"); /* drop proxy-disclosing headers */ - std::vector toErase; - for (const auto& it : req.headers) { - if (it.first.compare(0, 12, "X-Forwarded-") == 0) { - toErase.push_back(it.first); - } else if (it.first.compare(0, 6, "Proxy-") == 0) { - toErase.push_back(it.first); - } else { - /* allow */ - } - } - for (const auto& header : toErase) { - req.headers.erase(header); - } + req.RemoveHeader("X-Forwarded"); + req.RemoveHeader("Proxy-"); /* replace headers */ - req.add_header("Connection", "close", true); /* keep-alive conns not supported yet */ - req.add_header("User-Agent", "MYOB/6.66 (AN/ON)", true); /* privacy */ + req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); + /* add headers */ + req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */ } /** @@ -263,29 +253,36 @@ namespace proxy { std::string dest_host = m_RequestURL.host; uint16_t dest_port = m_RequestURL.port; /* always set port, even if missing in request */ - if (!dest_port) { + if (!dest_port) dest_port = (m_RequestURL.schema == "https") ? 443 : 80; - } /* detect dest_host, set proper 'Host' header in upstream request */ - auto h = m_ClientRequest.headers.find("Host"); - if (dest_host != "") { + if (dest_host != "") + { /* absolute url, replace 'Host' header */ std::string h = dest_host; if (dest_port != 0 && dest_port != 80) h += ":" + std::to_string(dest_port); - m_ClientRequest.add_header("Host", h, true); - } else if (h != m_ClientRequest.headers.end()) { - /* relative url and 'Host' header provided. transparent proxy mode? */ - i2p::http::URL u; - std::string t = "http://" + h->second; - u.parse(t); - dest_host = u.host; - dest_port = u.port; - } else { - /* relative url and missing 'Host' header */ - GenericProxyError("Invalid request", "Can't detect destination host from request"); - return true; - } + m_ClientRequest.UpdateHeader("Host", h); + } + else + { + auto h = m_ClientRequest.GetHeader ("Host"); + if (h.length () > 0) + { + /* relative url and 'Host' header provided. transparent proxy mode? */ + i2p::http::URL u; + std::string t = "http://" + h; + u.parse(t); + dest_host = u.host; + dest_port = u.port; + } + else + { + /* relative url and missing 'Host' header */ + GenericProxyError("Invalid request", "Can't detect destination host from request"); + return true; + } + } /* check dest_host really exists and inside I2P network */ i2p::data::IdentHash identHash; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 1b4512eb..154dce5e 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -714,9 +714,11 @@ namespace http { return true; } /* method #2: 'Authorization' header sent */ - if (req.headers.count("Authorization") > 0) { + auto provided = req.GetHeader ("Authorization"); + if (provided.length () > 0) + { bool result = false; - std::string provided = req.headers.find("Authorization")->second; + std::string expected = user + ":" + pass; size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1; char * b64_creds = new char[b64_sz]; diff --git a/Reseed.cpp b/Reseed.cpp index 24df13f7..1766e1f6 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -517,8 +517,8 @@ namespace data LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port); i2p::http::HTTPReq req; req.uri = url.to_string(); - req.add_header("User-Agent", "Wget/1.11.4"); - req.add_header("Connection", "close"); + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Connection", "close"); s.write_some (boost::asio::buffer (req.to_string())); // read response std::stringstream rs; From 35b5dcdb229f55b5aa58e37f6d929aceb54cc7d5 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 5 Feb 2017 17:08:42 -0500 Subject: [PATCH 09/27] new reseed --- Config.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Config.cpp b/Config.cpp index 0ed09ae8..b8008bc3 100644 --- a/Config.cpp +++ b/Config.cpp @@ -180,8 +180,9 @@ namespace config { "https://download.xxlspeed.com/," "https://reseed-ru.lngserv.ru/," "https://reseed.atomike.ninja/," - "https://reseed.memcpy.io/", - "https://reseed.onion.im/" + "https://reseed.memcpy.io/," + "https://reseed.onion.im/," + "https://itoopie.atomike.ninja/" ), "Reseed URLs, separated by comma") ; From d96dbe936539d2b2105c97796acbd4b7727a5b13 Mon Sep 17 00:00:00 2001 From: r4sas Date: Mon, 6 Feb 2017 16:18:23 +0300 Subject: [PATCH 10/27] use _USE_32BIT_TIME_T in win32 build Add i2pd.exe in .gitignore --- .gitignore | 1 + Timestamp.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 73297488..961b53b5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ netDb /i2pd /libi2pd.a /libi2pdclient.a +i2pd.exe # Autotools diff --git a/Timestamp.cpp b/Timestamp.cpp index 6079c881..3131fb6a 100644 --- a/Timestamp.cpp +++ b/Timestamp.cpp @@ -5,6 +5,12 @@ #include "I2PEndian.h" #include "Timestamp.h" +#ifdef WIN32 + #ifndef _WIN64 + #define _USE_32BIT_TIME_T + #endif +#endif + namespace i2p { namespace util @@ -33,17 +39,17 @@ namespace util socket.send_to (boost::asio::buffer (buf, 48), ep); int i = 0; while (!socket.available() && i < 10) // 10 seconds max - { + { std::this_thread::sleep_for (std::chrono::seconds(1)); i++; - } + } if (socket.available ()) len = socket.receive_from (boost::asio::buffer (buf, 48), ep); } catch (std::exception& e) { LogPrint (eLogError, "NTP error: ", e.what ()); - } + } if (len >= 8) { auto ourTs = GetSecondsSinceEpoch (); @@ -51,10 +57,10 @@ namespace util if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 g_TimeOffset = ts - ourTs; LogPrint (eLogInfo, address, " time offset from system time is ", g_TimeOffset, " seconds"); - } + } } } - } + } } } From d2edbfd6fa2f6d7cb3a2d86ae6ef383296a070c1 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 6 Feb 2017 12:50:54 -0500 Subject: [PATCH 11/27] eliminate extra copy --- TunnelGateway.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index 6ac709d2..f8c5d031 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -203,7 +203,8 @@ namespace tunnel { m_Buffer.CompleteCurrentTunnelDataMessage (); std::vector > newTunnelMsgs; - for (auto& tunnelMsg : m_Buffer.GetTunnelDataMsgs ()) + const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); + for (auto& tunnelMsg : tunnelDataMsgs) { auto newMsg = CreateEmptyTunnelDataMsg (); m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); From a8778e358ddf6006bb939547d4cdd997b10830f3 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 6 Feb 2017 21:39:15 -0500 Subject: [PATCH 12/27] handle HTTP response --- HTTPProxy.cpp | 2 +- I2PTunnel.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++--- I2PTunnel.h | 23 +++++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index e4d765ac..5abd6112 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -498,7 +498,7 @@ namespace proxy { if (Kill()) return; LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID()); - auto connection = std::make_shared(GetOwner(), m_sock, stream); + auto connection = std::make_shared(GetOwner(), m_sock, stream); GetOwner()->AddHandler (connection); connection->I2PConnect (reinterpret_cast(m_send_buf.data()), m_send_buf.length()); Done (shared_from_this()); diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 6a9b3ffd..3f22ed03 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -236,14 +236,63 @@ namespace client } } - I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, + void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) + { + if (m_HeaderSent) + I2PTunnelConnection::Write (buf, len); + else + { + m_InHeader.clear (); + m_InHeader.write ((const char *)buf, len); + std::string line; + bool endOfHeader = false; + while (!endOfHeader) + { + std::getline(m_InHeader, line); + if (!m_InHeader.fail ()) + { + if (line == "\r") endOfHeader = true; + else + { + if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) + { + m_OutHeader << "Connection: close\r\n"; + m_ConnectionSent = true; + } + else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) + { + m_OutHeader << "Proxy-Connection: close\r\n"; + m_ProxyConnectionSent = true; + } + else + m_OutHeader << line << "\n"; + } + } + else + break; + } + + if (endOfHeader) + { + if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; + if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; + m_OutHeader << "\r\n"; // end of header + m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header + m_InHeader.str (""); + m_HeaderSent = true; + I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); + } + } + } + + I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host): I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ()) { } - void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) + void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) { if (m_HeaderSent) I2PTunnelConnection::Write (buf, len); @@ -282,6 +331,7 @@ namespace client { m_OutHeader << "\r\n"; // end of header m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header + m_InHeader.str (""); m_HeaderSent = true; I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); } @@ -532,7 +582,7 @@ namespace client std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) { - return std::make_shared (this, stream, + return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), m_Host); } diff --git a/I2PTunnel.h b/I2PTunnel.h index 68f46e43..f0580554 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -64,11 +64,30 @@ namespace client bool m_IsQuiet; // don't send destination }; - class I2PTunnelConnectionHTTP: public I2PTunnelConnection + class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection + { + public: + + I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr socket, + std::shared_ptr stream): + I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false), + m_ConnectionSent (false), m_ProxyConnectionSent (false) {}; + + protected: + + void Write (const uint8_t * buf, size_t len); + + private: + + std::stringstream m_InHeader, m_OutHeader; + bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; + }; + + class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection { public: - I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, + I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host); From 051e642c0c83e0713252437f5dfda25e5bce151d Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 9 Feb 2017 11:05:42 -0500 Subject: [PATCH 13/27] fixed #798. Correct buffer size --- SSU.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSU.h b/SSU.h index c9881fd0..c241b903 100644 --- a/SSU.h +++ b/SSU.h @@ -30,7 +30,7 @@ namespace transport struct SSUPacket { - i2p::crypto::AESAlignedBuffer<1500> buf; + i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size boost::asio::ip::udp::endpoint from; size_t len; }; From 277d4d9333833d39d83d688b9948cc1978dd3f8f Mon Sep 17 00:00:00 2001 From: r4sas Date: Thu, 9 Feb 2017 19:45:22 +0300 Subject: [PATCH 14/27] Added status output to main window --- Win32/Win32App.cpp | 486 +++++++++++++++++++++++++-------------------- 1 file changed, 276 insertions(+), 210 deletions(-) diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index 7da8f882..984ab685 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -1,8 +1,12 @@ #include #include #include +#include "../ClientContext.h" #include "../Config.h" +#include "../NetDb.h" #include "../RouterContext.h" +#include "../Transports.h" +#include "../Tunnel.h" #include "../version.h" #include "resource.h" #include "Win32App.h" @@ -22,230 +26,292 @@ #define WM_TRAYICON (WM_USER + 1) #define IDT_GRACEFUL_SHUTDOWN_TIMER 2100 +#define FRAME_UPDATE_TIMER 2101 namespace i2p { namespace win32 { - static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) - { - HMENU hPopup = CreatePopupMenu(); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); - SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); - SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); + static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) + { + HMENU hPopup = CreatePopupMenu(); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); + SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); + SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); - POINT p; - if (!curpos) - { - GetCursorPos (&p); - curpos = &p; - } + POINT p; + if (!curpos) + { + GetCursorPos (&p); + curpos = &p; + } - WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL); - SendMessage (hWnd, WM_COMMAND, cmd, 0); + WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL); + SendMessage (hWnd, WM_COMMAND, cmd, 0); - DestroyMenu(hPopup); - } + DestroyMenu(hPopup); + } - static void AddTrayIcon (HWND hWnd) - { - NOTIFYICONDATA nid; - memset(&nid, 0, sizeof(nid)); - nid.cbSize = sizeof(nid); - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; - nid.uCallbackMessage = WM_TRAYICON; - nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); - strcpy (nid.szTip, "i2pd"); - strcpy (nid.szInfo, "i2pd is running"); - Shell_NotifyIcon(NIM_ADD, &nid ); - } + static void AddTrayIcon (HWND hWnd) + { + NOTIFYICONDATA nid; + memset(&nid, 0, sizeof(nid)); + nid.cbSize = sizeof(nid); + nid.hWnd = hWnd; + nid.uID = ID_TRAY_ICON; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; + nid.uCallbackMessage = WM_TRAYICON; + nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); + strcpy (nid.szTip, "i2pd"); + strcpy (nid.szInfo, "i2pd is running"); + Shell_NotifyIcon(NIM_ADD, &nid ); + } - static void RemoveTrayIcon (HWND hWnd) - { - NOTIFYICONDATA nid; - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - Shell_NotifyIcon (NIM_DELETE, &nid); - } + static void RemoveTrayIcon (HWND hWnd) + { + NOTIFYICONDATA nid; + nid.hWnd = hWnd; + nid.uID = ID_TRAY_ICON; + Shell_NotifyIcon (NIM_DELETE, &nid); + } - static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - switch (uMsg) - { - case WM_CREATE: - { - AddTrayIcon (hWnd); - break; - } - case WM_CLOSE: - { - RemoveTrayIcon (hWnd); - KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); - PostQuitMessage (0); - break; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_ABOUT: - { - std::stringstream text; - text << "Version: " << I2PD_VERSION << " " << CODENAME; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_EXIT: - { - PostMessage (hWnd, WM_CLOSE, 0, 0); - return 0; - } - case ID_GRACEFUL_SHUTDOWN: - { - i2p::context.SetAcceptsTunnels (false); - SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes - return 0; - } - case ID_CONSOLE: - { - char buf[30]; - std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); - ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); - return 0; - } - case ID_APP: - { - ShowWindow(hWnd, SW_SHOW); - return 0; - } - } - break; - } - case WM_SYSCOMMAND: - { - switch (wParam) - { - case SC_MINIMIZE: - { - ShowWindow(hWnd, SW_HIDE); - return 0; - } - case SC_CLOSE: - { - std::string close; i2p::config::GetOption("close", close); - if (0 == close.compare("ask")) - switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?" - " You can add 'close' configuration option. Valid values are: ask, minimize, exit.", - "Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1)) - { - case IDYES: close = "minimize"; break; - case IDNO: close = "exit"; break; - default: return 0; - } - if (0 == close.compare("minimize")) - { - ShowWindow(hWnd, SW_HIDE); - return 0; - } - if (0 != close.compare("exit")) - { - ::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING); - return 0; - } - } - } - } - case WM_TRAYICON: - { - switch (lParam) - { - case WM_LBUTTONUP: - case WM_RBUTTONUP: - { - SetForegroundWindow (hWnd); - ShowPopupMenu(hWnd, NULL, -1); - PostMessage (hWnd, WM_APP + 1, 0, 0); - break; - } - } - break; - } - case WM_TIMER: - { - if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER) - { - PostMessage (hWnd, WM_CLOSE, 0, 0); // exit - return 0; - } - break; - } - } - return DefWindowProc( hWnd, uMsg, wParam, lParam); - } + void PrintMainWindowText (std::stringstream& s) + { + s << "NetStatus: "; + switch (i2p::context.GetStatus()) + { + case eRouterStatusOK: s << "OK"; break; + case eRouterStatusTesting: s << "Testing"; break; + case eRouterStatusFirewalled: s << "Firewalled"; break; + case eRouterStatusError: + { + switch (i2p::context.GetError()) + { + case eRouterErrorClockSkew: s << "Clock skew"; break; + default: s << "Error"; + } + break; + } + default: s << "Unknown"; + } + s << "; "; + s << "SuccRate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; + s << "Uptime: " << i2p::context.GetUptime() << " seconds\n"; + s << "\n"; + s << "InBand: " << i2p::transport::transports.GetInBandwidth() << " Bytes/s; "; + s << "OutBand: " << i2p::transport::transports.GetOutBandwidth() << " Bytes/s\n"; + s << "Recv: " << i2p::transport::transports.GetTotalReceivedBytes() << " Bytes; "; + s << "Sent: " << i2p::transport::transports.GetTotalSentBytes() << " Bytes\n"; + s << "\n"; + s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; "; + s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; "; + s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n"; + s << "Tunnels: "; + s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; "; + s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; "; + s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n"; + } - bool StartWin32App () - { - if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"))) - { - MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK); - return false; - } - // register main window - auto hInst = GetModuleHandle(NULL); - WNDCLASSEX wclx; - memset (&wclx, 0, sizeof(wclx)); - wclx.cbSize = sizeof(wclx); - wclx.style = 0; - wclx.lpfnWndProc = WndProc; - wclx.cbClsExtra = 0; - wclx.cbWndExtra = 0; - wclx.hInstance = hInst; - wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); - wclx.hCursor = LoadCursor (NULL, IDC_ARROW); - wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wclx.lpszMenuName = NULL; - wclx.lpszClassName = I2PD_WIN32_CLASSNAME; - RegisterClassEx (&wclx); - // create new window - if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 549, 738, NULL, NULL, hInst, NULL)) - { - MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); - return false; - } - return true; - } + static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_CREATE: + { + AddTrayIcon (hWnd); + break; + } + case WM_CLOSE: + { + RemoveTrayIcon (hWnd); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); + PostQuitMessage (0); + break; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case ID_ABOUT: + { + std::stringstream text; + text << "Version: " << I2PD_VERSION << " " << CODENAME; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_EXIT: + { + PostMessage (hWnd, WM_CLOSE, 0, 0); + return 0; + } + case ID_GRACEFUL_SHUTDOWN: + { + i2p::context.SetAcceptsTunnels (false); + SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes + return 0; + } + case ID_CONSOLE: + { + char buf[30]; + std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); + ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); + return 0; + } + case ID_APP: + { + ShowWindow(hWnd, SW_SHOW); + SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL); + return 0; + } + } + break; + } + case WM_SYSCOMMAND: + { + switch (wParam) + { + case SC_MINIMIZE: + { + ShowWindow(hWnd, SW_HIDE); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + return 0; + } + case SC_CLOSE: + { + std::string close; i2p::config::GetOption("close", close); + if (0 == close.compare("ask")) + switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?" + " You can add 'close' configuration option. Valid values are: ask, minimize, exit.", + "Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1)) + { + case IDYES: close = "minimize"; break; + case IDNO: close = "exit"; break; + default: return 0; + } + if (0 == close.compare("minimize")) + { + ShowWindow(hWnd, SW_HIDE); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + return 0; + } + if (0 != close.compare("exit")) + { + ::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING); + return 0; + } + } + } + } + case WM_TRAYICON: + { + switch (lParam) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + SetForegroundWindow (hWnd); + ShowPopupMenu(hWnd, NULL, -1); + PostMessage (hWnd, WM_APP + 1, 0, 0); + break; + } + } + break; + } + case WM_TIMER: + { + if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER) + { + PostMessage (hWnd, WM_CLOSE, 0, 0); // exit + return 0; + } + if (wParam == FRAME_UPDATE_TIMER) + { + InvalidateRect(hWnd, NULL, TRUE); + } + break; + } + case WM_PAINT: + { + HDC hDC; + PAINTSTRUCT ps; + RECT rp; + HFONT hFont; + std::stringstream s; PrintMainWindowText (s); + hDC = BeginPaint (hWnd, &ps); + GetClientRect(hWnd, &rp); + SetTextColor(hDC, 0x00D43B69); + hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman")); + SelectObject(hDC,hFont); + DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER); + EndPaint(hWnd, &ps); + break; + } + } + return DefWindowProc( hWnd, uMsg, wParam, lParam); + } - int RunWin32App () - { - MSG msg; - while (GetMessage (&msg, NULL, 0, 0 )) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - return msg.wParam; - } + bool StartWin32App () + { + if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"))) + { + MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK); + return false; + } + // register main window + auto hInst = GetModuleHandle(NULL); + WNDCLASSEX wclx; + memset (&wclx, 0, sizeof(wclx)); + wclx.cbSize = sizeof(wclx); + wclx.style = 0; + wclx.lpfnWndProc = WndProc; + wclx.cbClsExtra = 0; + wclx.cbWndExtra = 0; + wclx.hInstance = hInst; + wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); + wclx.hCursor = LoadCursor (NULL, IDC_ARROW); + wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wclx.lpszMenuName = NULL; + wclx.lpszClassName = I2PD_WIN32_CLASSNAME; + RegisterClassEx (&wclx); + // create new window + if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 400, 180, NULL, NULL, hInst, NULL)) + { + MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); + return false; + } + return true; + } - void StopWin32App () - { - UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL)); - } + int RunWin32App () + { + MSG msg; + while (GetMessage (&msg, NULL, 0, 0 )) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + return msg.wParam; + } - bool GracefulShutdown () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0); - return hWnd; - } + void StopWin32App () + { + UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL)); + } + + bool GracefulShutdown () + { + HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); + if (hWnd) + PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0); + return hWnd; + } } } From 2057531e8cb3ea28226f703ca97cef5462351786 Mon Sep 17 00:00:00 2001 From: r4sas Date: Thu, 9 Feb 2017 21:41:52 +0300 Subject: [PATCH 15/27] Processing transferred data (winapi) --- Win32/Win32App.cpp | 56 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index 984ab685..ad03e75f 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -80,9 +80,47 @@ namespace win32 Shell_NotifyIcon (NIM_DELETE, &nid); } - void PrintMainWindowText (std::stringstream& s) + static void ShowUptime (std::stringstream& s, int seconds) { - s << "NetStatus: "; + int num; + + if ((num = seconds / 86400) > 0) { + s << num << " days, "; + seconds -= num * 86400; + } + if ((num = seconds / 3600) > 0) { + s << num << " hours, "; + seconds -= num * 3600; + } + if ((num = seconds / 60) > 0) { + s << num << " min, "; + seconds -= num * 60; + } + s << seconds << " seconds\n"; + } + + static void ShowTransfered (std::stringstream& s, int transfer) + { + auto bytes = transfer & 0x03ff; + transfer >>= 10; + auto kbytes = transfer & 0x03ff; + transfer >>= 10; + auto mbytes = transfer & 0x03ff; + transfer >>= 10; + auto gbytes = transfer & 0x03ff; + + if (gbytes) + s << gbytes << " GB, "; + if (mbytes) + s << mbytes << " MB, "; + if (kbytes) + s << kbytes << " KB, "; + s << bytes << " Bytes\n"; + } + + static void PrintMainWindowText (std::stringstream& s) + { + s << "Status: "; switch (i2p::context.GetStatus()) { case eRouterStatusOK: s << "OK"; break; @@ -100,13 +138,13 @@ namespace win32 default: s << "Unknown"; } s << "; "; - s << "SuccRate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; - s << "Uptime: " << i2p::context.GetUptime() << " seconds\n"; + s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; + s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); s << "\n"; - s << "InBand: " << i2p::transport::transports.GetInBandwidth() << " Bytes/s; "; - s << "OutBand: " << i2p::transport::transports.GetOutBandwidth() << " Bytes/s\n"; - s << "Recv: " << i2p::transport::transports.GetTotalReceivedBytes() << " Bytes; "; - s << "Sent: " << i2p::transport::transports.GetTotalSentBytes() << " Bytes\n"; + s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; "; + s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n"; + s << "Recvieved: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes()); + s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes()); s << "\n"; s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; "; @@ -282,7 +320,7 @@ namespace win32 wclx.lpszClassName = I2PD_WIN32_CLASSNAME; RegisterClassEx (&wclx); // create new window - if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 400, 180, NULL, NULL, hInst, NULL)) + if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 180, NULL, NULL, hInst, NULL)) { MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); return false; From c010c83654e5c7baabf492f82aa43999eacaf44a Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 10 Feb 2017 12:51:55 -0500 Subject: [PATCH 16/27] signaturetype ofr HTTP and SOCKS proxy --- ClientContext.cpp | 6 ++++-- Config.cpp | 3 +++ docs/configuration.md | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index 0354e708..3edafd88 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -50,11 +50,12 @@ namespace client std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); + i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); if (httpProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; - if(LoadPrivateKeys (keys, httpProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)) + if(LoadPrivateKeys (keys, httpProxyKeys, sigType)) { std::map params; ReadI2CPOptionsFromConfig ("httpproxy.", params); @@ -80,11 +81,12 @@ namespace client uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); + i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); if (socksProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; - if (LoadPrivateKeys (keys, socksProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)) + if (LoadPrivateKeys (keys, socksProxyKeys, sigType)) { std::map params; ReadI2CPOptionsFromConfig ("socksproxy.", params); diff --git a/Config.cpp b/Config.cpp index b8008bc3..7f8f2f53 100644 --- a/Config.cpp +++ b/Config.cpp @@ -16,6 +16,7 @@ #include #include +#include "Identity.h" #include "Config.h" #include "version.h" @@ -85,6 +86,7 @@ namespace config { ("httpproxy.address", value()->default_value("127.0.0.1"), "HTTP Proxy listen address") ("httpproxy.port", value()->default_value(4444), "HTTP Proxy listen port") ("httpproxy.keys", value()->default_value(""), "File to persist HTTP Proxy keys") + ("httpproxy.signaturetype", value()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("httpproxy.inbound.length", value()->default_value("3"), "HTTP proxy inbound tunnel length") ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") @@ -100,6 +102,7 @@ namespace config { ("socksproxy.address", value()->default_value("127.0.0.1"), "SOCKS Proxy listen address") ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") ("socksproxy.keys", value()->default_value(""), "File to persist SOCKS Proxy keys") + ("socksproxy.signaturetype", value()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("socksproxy.inbound.length", value()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") diff --git a/docs/configuration.md b/docs/configuration.md index 4894bfe3..8f17b220 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -46,6 +46,7 @@ All options below still possible in cmdline, but better write it in config file: * --httpproxy.address= - The address to listen on (HTTP Proxy) * --httpproxy.port= - The port to listen on (HTTP Proxy) 4444 by default * --httpproxy.keys= - optional keys file for HTTP proxy local destination +* --httpproxy.signaturetype= - signature type for new keys if keys file is set. 7 by default * --httpproxy.inbound.length= - Inbound tunnels length if keys is set. 3 by default * --httpproxy.inbound.quantity= - Inbound tunnels quantity if keys is set. 5 by default * --httpproxy.outbound.length= - Outbound tunnels length if keys is set. 3 by default @@ -55,6 +56,7 @@ All options below still possible in cmdline, but better write it in config file: * --socksproxy.address= - The address to listen on (SOCKS Proxy) * --socksproxy.port= - The port to listen on (SOCKS Proxy). 4447 by default * --socksproxy.keys= - optional keys file for SOCKS proxy local destination +* --socksproxy.signaturetype= - signature type for new keys if keys file is set. 7 by default * --socksproxy.inbound.length= - Inbound tunnels length if keys is set. 3 by default * --socksproxy.inbound.quantity= - Inbound tunnels quantity if keys is set. 5 by default * --socksproxy.outbound.length= - Outbound tunnels length if keys is set. 3 by default From 25559f1772774c81392ab07314fc3878cda67d59 Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Fri, 10 Feb 2017 13:51:19 -0500 Subject: [PATCH 17/27] Added AppArmor profile --- contrib/apparmor/usr.sbin.i2pd | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 contrib/apparmor/usr.sbin.i2pd diff --git a/contrib/apparmor/usr.sbin.i2pd b/contrib/apparmor/usr.sbin.i2pd new file mode 100644 index 00000000..ab6c8b44 --- /dev/null +++ b/contrib/apparmor/usr.sbin.i2pd @@ -0,0 +1,30 @@ +# Basic profile for i2pd +# Should work without modifications with Ubuntu/Debian packages +# Author: Darknet Villain +# +#include + +/usr/sbin/i2pd { + #include + + network inet dgram, + network inet stream, + network inet6 dgram, + network inet6 stream, + network netlink raw, + + /etc/gai.conf r, + /etc/host.conf r, + /etc/hosts r, + /etc/nsswitch.conf r, + /run/resolvconf/resolv.conf r, + + # path specific (feel free to modify if you have another paths) + /etc/i2pd/** r, + /var/lib/i2pd/** rw, + /var/log/i2pd.log w, + /var/run/i2pd/i2pd.pid rw, + /usr/sbin/i2pd mr, + + +} From 13111c4b424eb83d28c913b16f8400ce5e0d55ab Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 11 Feb 2017 18:18:37 -0500 Subject: [PATCH 18/27] don't re-schedule resend timer if nothing to resend --- SSUData.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SSUData.cpp b/SSUData.cpp index 5ca4dac3..32e66e22 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -462,6 +462,7 @@ namespace transport else ++it; } + if (m_SentMessages.empty ()) return; // nothing to resend if (numResent < MAX_OUTGOING_WINDOW_SIZE) ScheduleResend (); else From 1ddc96f965ecc35357f25fabbd1fe559a3c512d6 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 12 Feb 2017 10:08:52 -0500 Subject: [PATCH 19/27] correct publication verification --- Destination.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Destination.cpp b/Destination.cpp index f3cb9af8..ac812da4 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -477,13 +477,18 @@ namespace client // "this" added due to bug in gcc 4.7-4.8 [s,this](std::shared_ptr leaseSet) { - if (leaseSet && s->m_LeaseSet) + if (leaseSet) { - // we got latest LeasetSet - LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32()); - s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); - s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); - return; + if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet) + { + // we got latest LeasetSet + LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32()); + s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); + s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); + return; + } + else + LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", GetIdentHash().ToBase32()); } else LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32()); From c231eff4b1d74c2f09b1bd3584923f7e1ffc718c Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 12 Feb 2017 10:12:12 -0500 Subject: [PATCH 20/27] MTU size of 1488 for ipv6 --- SSU.h | 2 +- SSUData.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SSU.h b/SSU.h index c241b903..ee49c6a4 100644 --- a/SSU.h +++ b/SSU.h @@ -30,7 +30,7 @@ namespace transport struct SSUPacket { - i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size + i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size boost::asio::ip::udp::endpoint from; size_t len; }; diff --git a/SSUData.h b/SSUData.h index f3ad9ea8..8f625fe1 100644 --- a/SSUData.h +++ b/SSUData.h @@ -21,13 +21,13 @@ namespace transport #ifdef MESHNET const size_t SSU_MTU_V6 = 1286; #else - const size_t SSU_MTU_V6 = 1472; + const size_t SSU_MTU_V6 = 1488; #endif const size_t IPV4_HEADER_SIZE = 20; const size_t IPV6_HEADER_SIZE = 40; const size_t UDP_HEADER_SIZE = 8; const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 - const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424 + const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 const int RESEND_INTERVAL = 3; // in seconds const int MAX_NUM_RESENDS = 5; const int DECAY_INTERVAL = 20; // in seconds From b097938f4711fa6a721d97e56e96fb7aa159959a Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 12 Feb 2017 15:11:19 -0500 Subject: [PATCH 21/27] compressed addressbook request --- AddressBook.cpp | 50 ++++++++++++++++++++++++++++++------------------- HTTP.cpp | 13 +++++++++---- HTTP.h | 6 +++--- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/AddressBook.cpp b/AddressBook.cpp index 20a118fb..c41dec50 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -705,6 +705,7 @@ namespace client i2p::http::HTTPReq req; req.AddHeader("Host", dest_host); req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"); req.AddHeader("Connection", "close"); if (!m_Etag.empty()) req.AddHeader("If-None-Match", m_Etag); @@ -721,7 +722,9 @@ namespace client std::string response; uint8_t recv_buf[4096]; bool end = false; - while (!end) { + int numAttempts = 5; + while (!end) + { stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096), [&](const boost::system::error_code& ecode, std::size_t bytes_transferred) { @@ -734,60 +737,69 @@ namespace client 30); // wait for 30 seconds std::unique_lock l(newDataReceivedMutex); if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + { LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); + numAttempts++; + if (numAttempts > 5) end = true; + } } // process remaining buffer - while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) { + while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) response.append ((char *)recv_buf, len); - } /* parse response */ i2p::http::HTTPRes res; int res_head_len = res.parse(response); - if (res_head_len < 0) { + if (res_head_len < 0) + { LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host); return false; } - if (res_head_len == 0) { + if (res_head_len == 0) + { LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout"); return false; } /* assert: res_head_len > 0 */ response.erase(0, res_head_len); - if (res.code == 304) { + if (res.code == 304) + { LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304"); return false; } - if (res.code != 200) { + if (res.code != 200) + { LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code); return false; } int len = res.content_length(); - if (response.empty()) { + if (response.empty()) + { LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes"); return false; } - if (len > 0 && len != (int) response.length()) { - LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes"); + if (!res.is_gzipped () && len > 0 && len != (int) response.length()) + { + LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); return false; } /* assert: res.code == 200 */ auto it = res.headers.find("ETag"); - if (it != res.headers.end()) { - m_Etag = it->second; - } + if (it != res.headers.end()) m_Etag = it->second; it = res.headers.find("If-Modified-Since"); - if (it != res.headers.end()) { - m_LastModified = it->second; - } - if (res.is_chunked()) { + if (it != res.headers.end()) m_LastModified = it->second; + if (res.is_chunked()) + { std::stringstream in(response), out; i2p::http::MergeChunkedResponse (in, out); response = out.str(); - } else if (res.is_gzipped()) { + } + else if (res.is_gzipped()) + { std::stringstream out; i2p::data::GzipInflator inflator; inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); - if (out.fail()) { + if (out.fail()) + { LogPrint(eLogError, "Addressbook: can't gunzip http response"); return false; } diff --git a/HTTP.cpp b/HTTP.cpp index 6ab6ecf4..77922686 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -312,7 +312,8 @@ namespace http { return ""; } - bool HTTPRes::is_chunked() { + bool HTTPRes::is_chunked() const + { auto it = headers.find("Transfer-Encoding"); if (it == headers.end()) return false; @@ -321,16 +322,20 @@ namespace http { return false; } - bool HTTPRes::is_gzipped() { + bool HTTPRes::is_gzipped(bool includingI2PGzip) const + { auto it = headers.find("Content-Encoding"); if (it == headers.end()) return false; /* no header */ if (it->second.find("gzip") != std::string::npos) return true; /* gotcha! */ + if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos) + return true; return false; } - - long int HTTPMsg::content_length() { + + long int HTTPMsg::content_length() const + { unsigned long int length = 0; auto it = headers.find("Content-Length"); if (it == headers.end()) diff --git a/HTTP.h b/HTTP.h index 3175cb79..251d98bc 100644 --- a/HTTP.h +++ b/HTTP.h @@ -64,7 +64,7 @@ namespace http { void del_header(const char *name); /** @brief Returns declared message length or -1 if unknown */ - long int content_length(); + long int content_length() const; }; struct HTTPReq @@ -129,10 +129,10 @@ namespace http { void write(std::ostream & o); /** @brief Checks that response declared as chunked data */ - bool is_chunked(); + bool is_chunked() const ; /** @brief Checks that response contains compressed data */ - bool is_gzipped(); + bool is_gzipped(bool includingI2PGzip = true) const; }; /** From 422f8b36607a81a3780d5857a5a7b1bba1cd1cbd Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 12 Feb 2017 20:52:46 -0500 Subject: [PATCH 22/27] publish with min interval of 20 seconds --- Destination.cpp | 21 +++++++++++++++++++-- Destination.h | 6 +++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Destination.cpp b/Destination.cpp index ac812da4..06fcf6b3 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -14,8 +14,8 @@ namespace client { LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map * params): m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic), - m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service), - m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service) + m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), + m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service) { int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH; int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY; @@ -426,6 +426,16 @@ namespace client LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending"); return; } + auto ts = i2p::util::GetSecondsSinceEpoch (); + if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL) + { + LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds"); + m_PublishDelayTimer.cancel (); + m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL)); + m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer, + shared_from_this (), std::placeholders::_1)); + return; + } auto outbound = m_Pool->GetNextOutboundTunnel (); if (!outbound) { @@ -453,6 +463,7 @@ namespace client m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, shared_from_this (), std::placeholders::_1)); outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); + m_LastSubmissionTime = ts; } void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) @@ -498,6 +509,12 @@ namespace client } } + void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + Publish (); + } + bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) { if (!m_Pool || !IsReady ()) diff --git a/Destination.h b/Destination.h index 7a4e0b64..e077c016 100644 --- a/Destination.h +++ b/Destination.h @@ -30,6 +30,7 @@ namespace client const uint8_t PROTOCOL_TYPE_RAW = 18; const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish + const int PUBLISH_MIN_INTERVAL = 20; // in seconds const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds @@ -122,6 +123,7 @@ namespace client void Publish (); void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); void HandlePublishVerificationTimer (const boost::system::error_code& ecode); + void HandlePublishDelayTimer (const boost::system::error_code& ecode); void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); void HandleDeliveryStatusMessage (std::shared_ptr msg); @@ -146,9 +148,11 @@ namespace client std::shared_ptr m_LeaseSet; bool m_IsPublic; uint32_t m_PublishReplyToken; + uint64_t m_LastSubmissionTime; // in seconds std::set m_ExcludedFloodfills; // for publishing - boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer; + boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, + m_PublishDelayTimer, m_CleanupTimer; public: From cf6d445080f70f53cc4b83e457464cef1b33fc8d Mon Sep 17 00:00:00 2001 From: r4sas Date: Mon, 13 Feb 2017 14:12:48 +0300 Subject: [PATCH 23/27] winapi - fix style, delete hFont object after drawing (fixes overflow) --- Win32/Win32App.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index ad03e75f..5a447ad9 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -289,6 +289,7 @@ namespace win32 hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman")); SelectObject(hDC,hFont); DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER); + DeleteObject(hFont); EndPaint(hWnd, &ps); break; } @@ -310,12 +311,13 @@ namespace win32 wclx.cbSize = sizeof(wclx); wclx.style = 0; wclx.lpfnWndProc = WndProc; - wclx.cbClsExtra = 0; - wclx.cbWndExtra = 0; + //wclx.cbClsExtra = 0; + //wclx.cbWndExtra = 0; wclx.hInstance = hInst; wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); wclx.hCursor = LoadCursor (NULL, IDC_ARROW); - wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + //wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wclx.lpszMenuName = NULL; wclx.lpszClassName = I2PD_WIN32_CLASSNAME; RegisterClassEx (&wclx); From 3a5a0837c79e9edf0ae3d0d9cb8c3316130e1fef Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 14 Feb 2017 12:11:30 -0500 Subject: [PATCH 24/27] don't show error if stream closed --- I2PTunnel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 3f22ed03..71b09e1a 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -187,9 +187,9 @@ namespace client { if (ecode) { - LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) { + LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ()); if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) From d773647a201c26ed5e89aab8271e019066ffa744 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 14 Feb 2017 12:11:43 -0500 Subject: [PATCH 25/27] 2.12.0 --- Win32/installer.iss | 2 +- android/AndroidManifest.xml | 2 +- debian/changelog | 6 ++++++ qt/i2pd_qt/android/AndroidManifest.xml | 2 +- version.h | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Win32/installer.iss b/Win32/installer.iss index 2408efdb..358ffaf8 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.11.0" +#define I2Pd_ver "2.12.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 40688197..2870dd29 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="2.12.0"> diff --git a/debian/changelog b/debian/changelog index 67af8310..22842831 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i2pd (2.12.0-1) unstable; urgency=low + + * updated to version 2.12.0/0.9.28 + + -- orignal Tue, 14 Feb 2016 17:59:30 +0000 + i2pd (2.11.0-1) unstable; urgency=low * updated to version 2.11.0/0.9.28 diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml index b1f71567..75086b4a 100644 --- a/qt/i2pd_qt/android/AndroidManifest.xml +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/version.h b/version.h index 501f7272..3e9fca8b 100644 --- a/version.h +++ b/version.h @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 11 +#define I2PD_VERSION_MINOR 12 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) From bcc8529bfcaf0480f71f87cf6cd41e0c7a435ce4 Mon Sep 17 00:00:00 2001 From: r4sas Date: Tue, 14 Feb 2017 20:17:20 +0300 Subject: [PATCH 26/27] update year, maintainer, ulimit -n 4096 default --- debian/control | 2 +- debian/copyright | 7 ++++--- debian/i2pd.default | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/debian/control b/debian/control index ac6f5e28..87f31ccb 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,7 @@ Source: i2pd Section: net Priority: extra -Maintainer: hagen +Maintainer: R4SAS Build-Depends: debhelper (>= 9.0.0), dpkg-dev (>= 1.16.1~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), diff --git a/debian/copyright b/debian/copyright index 606d059b..6aeefebf 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,9 +3,9 @@ Upstream-Name: i2pd Source: https://github.com/PurpleI2P Files: * -Copyright: 2013-2016 PurpleI2P +Copyright: 2013-2017 PurpleI2P License: BSD-3-clause - Copyright (c) 2013-2016, The PurpleI2P Project + Copyright (c) 2013-2017, The PurpleI2P Project . All rights reserved. . @@ -34,7 +34,8 @@ License: BSD-3-clause SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: debian/* -Copyright: 2014-2016 hagen +Copyright: 2016-2017 R4SAS + 2014-2016 hagen 2013-2015 Kill Your TV License: GPL-2.0+ This package is free software; you can redistribute it and/or modify diff --git a/debian/i2pd.default b/debian/i2pd.default index c45eff9d..5ac311c3 100644 --- a/debian/i2pd.default +++ b/debian/i2pd.default @@ -8,4 +8,4 @@ I2PD_ENABLED="yes" DAEMON_OPTS="" # If you have problems with hunging i2pd, you can try enable this -#ulimit -n 4096 +ulimit -n 4096 From 960d9a85346f9e47c9f088588b3053023d839987 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 14 Feb 2017 12:36:54 -0500 Subject: [PATCH 27/27] updated ChangeLog --- ChangeLog | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3b7bacd0..f94c6436 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,22 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.12.0] - 2017-02-14 +### Added +- Additional HTTP and SOCKS proxy tunnels +- Reseed from ZIP achieve +- Some stats in a main window for Windows version +### Changed +- Reseed servers list +- MTU of 1488 for ipv6 +- Android and Mac OS X versions use OpenSSL 1.1 +- New logo for Android +### Fixed +- Multiple memory leaks +- Incomptibility of some EdDSA private keys with Java +- Clock skew for Windows XP +- Occasional crashes with I2PSnark + ## [2.11.0] - 2016-12-18 ### Added - Websockets support