diff --git a/AddressBook.cpp b/AddressBook.cpp index db424308..685acbc2 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -842,7 +842,7 @@ namespace client else memset (response + 8, 0, 32); // not found memset (response + 40, 0, 4); // set expiration time to zero - m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort); + m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort); } void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident) diff --git a/BloomFilter.cpp b/BloomFilter.cpp new file mode 100644 index 00000000..b92039df --- /dev/null +++ b/BloomFilter.cpp @@ -0,0 +1,69 @@ +#include "BloomFilter.h" +#include "I2PEndian.h" +#include +#include + +namespace i2p +{ +namespace util +{ + + /** @brief decaying bloom filter implementation */ + class DecayingBloomFilter : public IBloomFilter + { + public: + + DecayingBloomFilter(const std::size_t size) + { + m_Size = size; + m_Data = new uint8_t[size]; + } + + /** @brief implements IBloomFilter::~IBloomFilter */ + ~DecayingBloomFilter() + { + delete [] m_Data; + } + + /** @brief implements IBloomFilter::Add */ + bool Add(const uint8_t * data, std::size_t len) + { + std::size_t idx; + uint8_t mask; + Get(data, len, idx, mask); + if(m_Data[idx] & mask) return false; // filter hit + m_Data[idx] |= mask; + return true; + } + + /** @brief implements IBloomFilter::Decay */ + void Decay() + { + // reset bloom filter buffer + memset(m_Data, 0, m_Size); + } + + private: + /** @brief get bit index for for data */ + void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm) + { + bm = 1; + uint8_t digest[32]; + // TODO: use blake2 because it's faster + SHA256(data, len, digest); + uint64_t i = buf64toh(digest); + idx = i % m_Size; + bm <<= (i % 8); + } + + uint8_t * m_Data; + std::size_t m_Size; + }; + + + BloomFilterPtr BloomFilter(std::size_t capacity) + { + return std::make_shared(capacity); + } +} +} diff --git a/BloomFilter.h b/BloomFilter.h new file mode 100644 index 00000000..2745fbf5 --- /dev/null +++ b/BloomFilter.h @@ -0,0 +1,31 @@ +#ifndef BLOOM_FILTER_H_ +#define BLOOM_FILTER_H_ +#include +#include + +namespace i2p +{ +namespace util +{ + + /** @brief interface for bloom filter */ + struct IBloomFilter + { + + /** @brief destructor */ + virtual ~IBloomFilter() {}; + /** @brief add entry to bloom filter, return false if filter hit otherwise return true */ + virtual bool Add(const uint8_t * data, std::size_t len) = 0; + /** @brief optionally decay old entries */ + virtual void Decay() = 0; + }; + + typedef std::shared_ptr BloomFilterPtr; + + /** @brief create bloom filter */ + BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); + +} +} + +#endif diff --git a/ChangeLog b/ChangeLog index dd1bac57..3b7bacd0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,29 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.11.0] - 2016-12-18 +### Added +- Websockets support +- Reseed through a floodfill +- Tunnel configuration for HTTP and SOCKS proxy +- Zero-hops tunnels for destinations +- Multiple acceptors for SAM +### Changed +- Reseed servers list +- DHT uses AVX if applicable +- New logo +- LeaseSet lookups +### Fixed +- HTTP Proxy connection reset for Windows +- Crash upon SAM session termination +- Can't connect to a destination for a longer time after restart +- Mass packet loss for UDP tunnels + +## [2.10.2] - 2016-12-04 +### Fixed +- Fixes UPnP discovery bug, producing excessive CPU usage +- Fixes sudden SSU thread stop for Windows. + ## [2.10.1] - 2016-11-07 ### Fixed - Fixed some performance issues for Windows and Android diff --git a/Config.cpp b/Config.cpp index 6f4014ba..20036b83 100644 --- a/Config.cpp +++ b/Config.cpp @@ -171,7 +171,7 @@ namespace config { "https://i2p.mooo.com/netDb/," "https://netdb.i2p2.no/," "https://us.reseed.i2p2.no:444/," - "https://uk.reseed.i2p2.no:444/," +// "https://uk.reseed.i2p2.no:444/," // mamoth's shit "https://i2p-0.manas.ca:8443/," "https://reseed.i2p.vzaws.com:8443/," "https://download.xxlspeed.com/," diff --git a/Crypto.cpp b/Crypto.cpp index 91b0ad08..a4a794ac 100644 --- a/Crypto.cpp +++ b/Crypto.cpp @@ -386,44 +386,68 @@ namespace crypto // HMAC const uint64_t IPAD = 0x3636363636363636; const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; - + +#if defined(__AVX__) + static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD }; + static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD }; +#endif + void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) // key is 32 bytes // digest is 16 bytes // block size is 64 bytes { uint64_t buf[256]; + uint64_t hash[12]; // 96 bytes +#if defined(__AVX__) // for AVX + __asm__ + ( + "vmovups %[key], %%ymm0 \n" + "vmovups %[ipad], %%ymm1 \n" + "vmovups %%ymm1, 32(%[buf]) \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, (%[buf]) \n" + "vmovups %[opad], %%ymm1 \n" + "vmovups %%ymm1, 32(%[hash]) \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, (%[hash]) \n" + "vzeroall \n" // end of AVX + "movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes + : + : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads), + [buf]"r"(buf), [hash]"r"(hash) + : "memory", "%xmm0" // TODO: change to %ymm0 later + ); +#else // ikeypad buf[0] = key.GetLL ()[0] ^ IPAD; buf[1] = key.GetLL ()[1] ^ IPAD; buf[2] = key.GetLL ()[2] ^ IPAD; buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; + buf[4] = IPAD; + buf[5] = IPAD; + buf[6] = IPAD; + buf[7] = IPAD; + // okeypad + hash[0] = key.GetLL ()[0] ^ OPAD; + hash[1] = key.GetLL ()[1] ^ OPAD; + hash[2] = key.GetLL ()[2] ^ OPAD; + hash[3] = key.GetLL ()[3] ^ OPAD; + hash[4] = OPAD; + hash[5] = OPAD; + hash[6] = OPAD; + hash[7] = OPAD; + // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) + memset (hash + 10, 0, 16); +#endif + // concatenate with msg memcpy (buf + 8, msg, len); // calculate first hash - uint8_t hash[16]; // MD5 - MD5((uint8_t *)buf, len + 64, hash); - - // okeypad - buf[0] = key.GetLL ()[0] ^ OPAD; - buf[1] = key.GetLL ()[1] ^ OPAD; - buf[2] = key.GetLL ()[2] ^ OPAD; - buf[3] = key.GetLL ()[3] ^ OPAD; - buf[4] = OPAD; - buf[5] = OPAD; - buf[6] = OPAD; - buf[7] = OPAD; - // copy first hash after okeypad - memcpy (buf + 8, hash, 16); - // fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (buf + 10, 0, 16); + MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes // calculate digest - MD5((uint8_t *)buf, 96, digest); + MD5((uint8_t *)hash, 96, digest); } // AES diff --git a/Crypto.h b/Crypto.h index a04a93da..9e35f073 100644 --- a/Crypto.h +++ b/Crypto.h @@ -76,7 +76,18 @@ namespace crypto void operator^=(const ChipherBlock& other) // XOR { -#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE +#if defined(__AVX__) // AVX + __asm__ + ( + "vmovups (%[buf]), %%xmm0 \n" + "vmovups (%[other]), %%xmm1 \n" + "vxorps %%xmm0, %%xmm1, %%xmm0 \n" + "vmovups %%xmm0, (%[buf]) \n" + : + : [buf]"r"(buf), [other]"r"(other.buf) + : "%xmm0", "%xmm1", "memory" + ); +#elif defined(__SSE__) // SSE __asm__ ( "movups (%[buf]), %%xmm0 \n" diff --git a/Datagram.cpp b/Datagram.cpp index d0b0737a..852e54f6 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -22,9 +22,9 @@ namespace datagram { m_Sessions.clear(); } - - void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) - { + + void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) + { auto owner = m_Owner; std::vector v(MAX_DATAGRAM_SIZE); uint8_t * buf = v.data(); @@ -45,8 +45,7 @@ namespace datagram owner->Sign (buf1, len, signature); auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); - auto session = ObtainSession(ident); - session->SendMsg(msg); + ObtainSession(identity)->SendMsg(msg); } @@ -69,6 +68,8 @@ namespace datagram if (verified) { + auto h = identity.GetIdentHash(); + ObtainSession(h)->Ack(); auto r = FindReceiver(toPort); if(r) r(identity, fromPort, toPort, buf + headerLen, len -headerLen); @@ -138,15 +139,15 @@ namespace datagram } } - std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident) + std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity) { std::shared_ptr session = nullptr; std::lock_guard lock(m_SessionsMutex); - auto itr = m_Sessions.find(ident); + auto itr = m_Sessions.find(identity); if (itr == m_Sessions.end()) { // not found, create new session - session = std::make_shared(m_Owner, ident); - m_Sessions[ident] = session; + session = std::make_shared(m_Owner, identity); + m_Sessions[identity] = session; } else { session = itr->second; } @@ -164,13 +165,13 @@ namespace datagram } DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination, - const i2p::data::IdentHash & remoteIdent) : + const i2p::data::IdentHash & remoteIdent) : m_LocalDestination(localDestination), - m_RemoteIdentity(remoteIdent), - m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()), - m_LastPathChange(0), - m_LastSuccess(0) + m_RemoteIdent(remoteIdent), + m_SendQueueTimer(localDestination->GetService()) { + m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); + ScheduleFlushSendQueue(); } void DatagramSession::SendMsg(std::shared_ptr msg) @@ -184,262 +185,149 @@ namespace datagram DatagramSession::Info DatagramSession::GetSessionInfo() const { if(!m_RoutingSession) - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); auto routingPath = m_RoutingSession->GetSharedRoutingPath(); if (!routingPath) - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); auto lease = routingPath->remoteLease; auto tunnel = routingPath->outboundTunnel; if(lease) { if(tunnel) - return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); + return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); else - return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); } else if(tunnel) - return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); else - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); - } - - void DatagramSession::HandleSend(std::shared_ptr msg) - { - if(!m_RoutingSession) - { - // try to get one - if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - else - { - UpdateLeaseSet(msg); - return; - } - } - // do we have a routing session? - if(m_RoutingSession) - { - // should we switch paths? - if(ShouldUpdateRoutingPath ()) - { - LogPrint(eLogDebug, "DatagramSession: try getting new routing path"); - // try switching paths - auto path = GetNextRoutingPath(); - if(path) - UpdateRoutingPath (path); - else - ResetRoutingPath(); - } - auto routingPath = m_RoutingSession->GetSharedRoutingPath (); - // make sure we have a routing path - if (routingPath) - { - auto outboundTunnel = routingPath->outboundTunnel; - if (outboundTunnel) - { - if(outboundTunnel->IsEstablished()) - { - m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch (); - // we have a routing path and routing session and the outbound tunnel we are using is good - // wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW - auto m = m_RoutingSession->WrapSingleMessage(msg); - routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{ - i2p::tunnel::eDeliveryTypeTunnel, - routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, - m - }}); - return; - } - } - } - } - auto now = i2p::util::GetMillisecondsSinceEpoch (); - // if this path looks dead reset the routing path since we didn't seem to be able to get a path in time - if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath(); - UpdateLeaseSet(msg); - - } - - void DatagramSession::UpdateRoutingPath(const std::shared_ptr & path) - { - if(m_RoutingSession == nullptr && m_RemoteLeaseSet) - m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - if(!m_RoutingSession) return; - // set routing path and update time we last updated the routing path - m_RoutingSession->SetSharedRoutingPath (path); - m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch (); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); } - bool DatagramSession::ShouldUpdateRoutingPath() const + void DatagramSession::Ack() { - bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr; - auto now = i2p::util::GetMillisecondsSinceEpoch (); - // we need to rotate paths becuase the routing path is too old - // if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true; - // too fast switching paths - if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false; - // our path looks dead so we need to rotate paths - if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead; - // if we have a routing session and routing path we don't need to switch paths - return dead; + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + auto path = GetSharedRoutingPath(); + if(path) + path->updateTime = i2p::util::GetSecondsSinceEpoch (); } - - bool DatagramSession::ShouldSwitchLease() const - { - std::shared_ptr routingPath = nullptr; - std::shared_ptr currentLease = nullptr; - if(m_RoutingSession) - routingPath = m_RoutingSession->GetSharedRoutingPath (); - if(routingPath) - currentLease = routingPath->remoteLease; - if(currentLease) // if we have a lease return true if it's about to expire otherwise return false - return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE ); - // we have no current lease, we should switch - return currentLease == nullptr; - } - - std::shared_ptr DatagramSession::GetNextRoutingPath() + std::shared_ptr DatagramSession::GetSharedRoutingPath () { - std::shared_ptr outboundTunnel = nullptr; - std::shared_ptr routingPath = nullptr; - // get existing routing path if we have one - if(m_RoutingSession) - routingPath = m_RoutingSession->GetSharedRoutingPath(); - // do we have an existing outbound tunnel and routing path? - if(routingPath && routingPath->outboundTunnel) - { - // is the outbound tunnel we are using good? - if (routingPath->outboundTunnel->IsEstablished()) - { - // ya so let's stick with it - outboundTunnel = routingPath->outboundTunnel; + if(!m_RoutingSession) { + if(!m_RemoteLeaseSet) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); } - else - outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels + if(!m_RemoteLeaseSet) { + // no remote lease set + m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); + return nullptr; + } + m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); } - // do we have an outbound tunnel that works already ? - if(!outboundTunnel) - outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started - - if(outboundTunnel) - { - std::shared_ptr lease = nullptr; - // should we switch leases ? - if (ShouldSwitchLease ()) - { - // yes, get next available lease - lease = GetNextLease(); + auto path = m_RoutingSession->GetSharedRoutingPath(); + if(path) { + if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) { + // bad outbound tunnel, switch outbound tunnel + m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); + path->outboundTunnel = m_CurrentOutboundTunnel; } - else if (routingPath) - { - if(routingPath->remoteLease) - { - if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE)) - lease = GetNextLease(); - else - lease = routingPath->remoteLease; + if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + // bad lease, switch to next one + if(m_RemoteLeaseSet) { + auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { + return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway || l.endDate <= m_CurrentRemoteLease->endDate; + }); + auto sz = ls.size(); + if (sz) { + auto idx = rand() % sz; + m_CurrentRemoteLease = ls[idx]; + } + } else { + // no remote lease set? + LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); } + path->remoteLease = m_CurrentRemoteLease; } - else - lease = GetNextLease(); - if(lease) - { - // we have a valid lease to use and an outbound tunnel - // create new routing path - uint32_t now = i2p::util::GetSecondsSinceEpoch(); - routingPath = std::make_shared(i2p::garlic::GarlicRoutingPath{ - outboundTunnel, - lease, - 0, - now, - 0 - }); + } else { + // no current path, make one + path = std::make_shared(); + // switch outbound tunnel if bad + if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) { + m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); + } + // switch lease if bad + if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + if(!m_RemoteLeaseSet) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); + } + if(m_RemoteLeaseSet) { + // pick random next good lease + auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool { + if(m_CurrentRemoteLease) + return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway; + return false; + }); + auto sz = ls.size(); + if(sz) { + auto idx = rand() % sz; + m_CurrentRemoteLease = ls[idx]; + } + } else { + // no remote lease set currently, bail + LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); + return nullptr; + } } - else // we don't have a new routing path to give - routingPath = nullptr; + path->outboundTunnel = m_CurrentOutboundTunnel; + path->remoteLease = m_CurrentRemoteLease; + m_RoutingSession->SetSharedRoutingPath(path); } - return routingPath; + return path; + } - void DatagramSession::ResetRoutingPath() + void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) { - if(m_RoutingSession) - { - auto routingPath = m_RoutingSession->GetSharedRoutingPath(); - if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path - { - // get outbound tunnel on this path - auto outboundTunnel = routingPath->outboundTunnel; - // is this outbound tunnel there and established - if (outboundTunnel && outboundTunnel->IsEstablished()) - m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine - } - // reset the routing path - UpdateRoutingPath(nullptr); - } + if(!ls) return; + // only update lease set if found and newer than previous lease set + uint64_t oldExpire = 0; + if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); + if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; + } + + void DatagramSession::HandleSend(std::shared_ptr msg) + { + m_SendQueue.push_back(msg); + // flush queue right away if full + if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue(); } - std::shared_ptr DatagramSession::GetNextLease() + void DatagramSession::FlushSendQueue () { - auto now = i2p::util::GetMillisecondsSinceEpoch (); - std::shared_ptr next = nullptr; - if(m_RemoteLeaseSet) + + std::vector send; + auto routingPath = GetSharedRoutingPath(); + // if we don't have a routing path we will drop all queued messages + if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease) { - std::vector exclude; - for(const auto & ident : m_InvalidIBGW) - exclude.push_back(ident); - // find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge - auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool { - if(exclude.size()) - { - auto end = std::end(exclude); - return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool { - return ident == l.tunnelGateway; - }) != end; - } - else - return false; - }); - if(leases.size()) + for (const auto & msg : m_SendQueue) { - // pick random valid next lease - uint32_t idx = rand() % leases.size(); - next = leases[idx]; + auto m = m_RoutingSession->WrapSingleMessage(msg); + send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); } - else - LogPrint(eLogWarning, "DatagramDestination: no leases to use"); + routingPath->outboundTunnel->SendTunnelDataMsg(send); } - return next; - } - - void DatagramSession::UpdateLeaseSet(std::shared_ptr msg) - { - LogPrint(eLogInfo, "DatagramSession: updating lease set"); - m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg)); + m_SendQueue.clear(); + ScheduleFlushSendQueue(); } - void DatagramSession::HandleGotLeaseSet(std::shared_ptr remoteIdent, std::shared_ptr msg) + void DatagramSession::ScheduleFlushSendQueue() { - if(remoteIdent) - { - // update routing session - if(m_RoutingSession) - m_RoutingSession = nullptr; - m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true); - // clear invalid IBGW as we have a new lease set - m_InvalidIBGW.clear(); - m_RemoteLeaseSet = remoteIdent; - // update routing path - auto path = GetNextRoutingPath(); - if (path) - UpdateRoutingPath(path); - else - ResetRoutingPath(); - // send the message that was queued if it was provided - if(msg) - HandleSend(msg); - } + boost::posix_time::milliseconds dlt(100); + m_SendQueueTimer.expires_from_now(dlt); + m_SendQueueTimer.async_wait([&](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); }); } } } diff --git a/Datagram.h b/Datagram.h index dc63cccb..8891f0cc 100644 --- a/Datagram.h +++ b/Datagram.h @@ -31,29 +31,33 @@ namespace datagram const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; // milliseconds minimum time between path switches const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; + // max 64 messages buffered in send queue for each datagram session + const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; class DatagramSession { public: DatagramSession(i2p::client::ClientDestination * localDestination, - const i2p::data::IdentHash & remoteIdent); + const i2p::data::IdentHash & remoteIdent); + + + /** @brief ack the garlic routing path */ + void Ack(); /** send an i2np message to remote endpoint for this session */ void SendMsg(std::shared_ptr msg); /** get the last time in milliseconds for when we used this datagram session */ uint64_t LastActivity() const { return m_LastUse; } - /** get the last time in milliseconds when we successfully sent data */ - uint64_t LastSuccess() const { return m_LastSuccess; } + struct Info { std::shared_ptr IBGW; std::shared_ptr OBEP; const uint64_t activity; - const uint64_t success; - Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {} - Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) : - activity(a), - success(s) { + + Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} + Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : + activity(a) { if(ibgw) IBGW = std::make_shared(ibgw); else IBGW = nullptr; if(obep) OBEP = std::make_shared(obep); @@ -63,44 +67,28 @@ namespace datagram Info GetSessionInfo() const; - private: - /** update our routing path we are using, mark that we have changed paths */ - void UpdateRoutingPath(const std::shared_ptr & path); + void FlushSendQueue(); + void ScheduleFlushSendQueue(); - /** return true if we should switch routing paths because of path lifetime or timeout otherwise false */ - bool ShouldUpdateRoutingPath() const; + void HandleSend(std::shared_ptr msg); + + std::shared_ptr GetSharedRoutingPath(); + + void HandleLeaseSetUpdated(std::shared_ptr ls); - /** return true if we should switch the lease for out routing path otherwise return false */ - bool ShouldSwitchLease() const; - - /** get next usable routing path, try reusing outbound tunnels */ - std::shared_ptr GetNextRoutingPath(); - /** - * mark current routing path as invalid and clear it - * if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time - */ - void ResetRoutingPath(); - - /** get next usable lease, does not fetch or update if expired or have no lease set */ - std::shared_ptr GetNextLease(); - - void HandleSend(std::shared_ptr msg); - void HandleGotLeaseSet(std::shared_ptr remoteIdent, - std::shared_ptr msg); - void UpdateLeaseSet(std::shared_ptr msg=nullptr); - private: i2p::client::ClientDestination * m_LocalDestination; - i2p::data::IdentHash m_RemoteIdentity; - std::shared_ptr m_RoutingSession; - // Ident hash of IBGW that are invalid - std::vector m_InvalidIBGW; - std::shared_ptr m_RemoteLeaseSet; - uint64_t m_LastUse; - uint64_t m_LastPathChange; - uint64_t m_LastSuccess; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + std::shared_ptr m_CurrentRemoteLease; + std::shared_ptr m_CurrentOutboundTunnel; + boost::asio::deadline_timer m_SendQueueTimer; + std::vector > m_SendQueue; + uint64_t m_LastUse; + }; const size_t MAX_DATAGRAM_SIZE = 32768; @@ -112,9 +100,9 @@ namespace datagram DatagramDestination (std::shared_ptr owner); - ~DatagramDestination (); + ~DatagramDestination (); - void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); + void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; @@ -130,7 +118,7 @@ namespace datagram private: - std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); + std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); diff --git a/Destination.cpp b/Destination.cpp index 61960464..491a9f29 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -544,11 +544,14 @@ namespace client else // duplicate { LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already"); - // TODO: implement it properly - //ret.first->second->requestComplete.push_back (requestComplete); if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) + { + // something went wrong m_LeaseSetRequests.erase (ret.first); - if (requestComplete) requestComplete (nullptr); + if (requestComplete) requestComplete (nullptr); + } + else if (requestComplete) + ret.first->second->requestComplete.push_back (requestComplete); } } else diff --git a/Garlic.cpp b/Garlic.cpp index ab20ac8f..81941cfc 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -20,8 +20,7 @@ namespace garlic std::shared_ptr destination, int numTags, bool attachLeaseSet): m_Owner (owner), m_Destination (destination), m_NumTags (numTags), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), - m_LeaseSetUpdateMsgID (0), - m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ())) + m_LeaseSetUpdateMsgID (0) { // create new session tags and session key RAND_bytes (m_SessionKey, 32); @@ -29,7 +28,7 @@ namespace garlic } GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): - m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) + m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) { memcpy (m_SessionKey, sessionKey, 32); m_Encryption.SetKey (m_SessionKey); @@ -188,7 +187,8 @@ namespace garlic RAND_bytes (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); - m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, buf, true); + i2p::crypto::ElGamalEncryption elGamalEncryption (m_Destination->GetEncryptionPublicKey ()); + elGamalEncryption.Encrypt ((uint8_t *)&elGamal, buf, true); m_Encryption.SetIV (iv); buf += 514; len += 514; @@ -315,7 +315,7 @@ namespace garlic { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec size_t size = 0; - if (isDestination && m_Destination) + if (isDestination) { buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination size++; diff --git a/Garlic.h b/Garlic.h index a7e2d264..b5dcd1eb 100644 --- a/Garlic.h +++ b/Garlic.h @@ -128,6 +128,7 @@ namespace garlic GarlicDestination * m_Owner; std::shared_ptr m_Destination; + i2p::crypto::AESKey m_SessionKey; std::list m_SessionTags; int m_NumTags; @@ -138,7 +139,6 @@ namespace garlic uint64_t m_LeaseSetSubmissionTime; // in milliseconds i2p::crypto::CBCEncryption m_Encryption; - std::unique_ptr m_ElGamalEncryption; std::shared_ptr m_SharedRoutingPath; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index b358ccec..84818e8e 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -451,26 +451,26 @@ namespace http { s << "
\r\n"; } - static void ShowCommands (std::stringstream& s) + static void ShowCommands (std::stringstream& s, uint32_t token) { /* commands */ s << "Router Commands
\r\n"; - s << " Run peer test
\r\n"; + s << " Run peer test
\r\n"; //s << " Reload config
\r\n"; if (i2p::context.AcceptsTunnels ()) - s << " Decline transit tunnels
\r\n"; + s << " Decline transit tunnels
\r\n"; else - s << " Accept transit tunnels
\r\n"; + s << " Accept transit tunnels
\r\n"; #if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) if (Daemon.gracefulShutdownInterval) - s << " Cancel graceful shutdown
"; + s << " Cancel graceful shutdown
"; else - s << " Start graceful shutdown
\r\n"; + s << " Start graceful shutdown
\r\n"; #endif #ifdef WIN32_APP - s << " Graceful shutdown
\r\n"; + s << " Graceful shutdown
\r\n"; #endif - s << " Force shutdown
\r\n"; + s << " Force shutdown
\r\n"; } static void ShowTransitTunnels (std::stringstream& s) @@ -709,11 +709,15 @@ namespace http { char b64_creds[64]; std::size_t len = 0; len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds)); - b64_creds[len] = '\0'; - expected = "Basic "; - expected += b64_creds; - if (provided == expected) - return true; + /* if we decoded properly then check credentials */ + if(len) { + b64_creds[len] = '\0'; + expected = "Basic "; + expected += b64_creds; + return expected == provided; + } + /** we decoded wrong so it's not a correct login credential */ + return false; } LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ()); @@ -752,6 +756,7 @@ namespace http { SendReply (res, content); } + std::map HTTPConnection::m_Tokens; void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s) { std::map params; @@ -767,7 +772,20 @@ namespace http { else if (page == HTTP_PAGE_TUNNELS) ShowTunnels (s); else if (page == HTTP_PAGE_COMMANDS) - ShowCommands (s); + { + uint32_t token; + RAND_bytes ((uint8_t *)&token, 4); + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) + { + if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) + it = m_Tokens.erase (it); + else + ++it; + } + m_Tokens[token] = ts; + ShowCommands (s, token); + } else if (page == HTTP_PAGE_TRANSIT_TUNNELS) ShowTransitTunnels (s); else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) @@ -794,13 +812,19 @@ namespace http { void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s) { std::map params; - std::string cmd(""); URL url; url.parse(req.uri); url.parse_query(params); - cmd = params["cmd"]; + std::string token = params["token"]; + if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) + { + ShowError(s, "Invalid token"); + return; + } + + std::string cmd = params["cmd"]; if (cmd == HTTP_COMMAND_RUN_PEER_TEST) i2p::transport::transports.PeerTest (); else if (cmd == HTTP_COMMAND_RELOAD_CONFIG) diff --git a/HTTPServer.h b/HTTPServer.h index 4a32702d..ec56e08a 100644 --- a/HTTPServer.h +++ b/HTTPServer.h @@ -1,10 +1,20 @@ #ifndef HTTP_SERVER_H__ #define HTTP_SERVER_H__ -namespace i2p { -namespace http { - extern const char *itoopieFavicon; - const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; +#include +#include +#include +#include +#include +#include +#include "HTTP.h" + +namespace i2p +{ +namespace http +{ + const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; + const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds class HTTPConnection: public std::enable_shared_from_this { @@ -35,6 +45,8 @@ namespace http { bool needAuth; std::string user; std::string pass; + + static std::map m_Tokens; // token->timestamp in seconds }; class HTTPServer diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 93bcff0c..ff3e06a3 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -92,7 +92,9 @@ namespace client m_Stream->Close (); m_Stream.reset (); } + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send); // avoid RST m_Socket->close (); + Done(shared_from_this ()); } @@ -107,9 +109,11 @@ namespace client { if (ecode) { - LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ()); Terminate (); + } } else { @@ -173,11 +177,13 @@ namespace client { if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination - else + else if (ecode == boost::asio::error::timed_out && m_Stream->IsOpen ()) + StreamReceive (); + else Terminate (); } else - Terminate (); + Terminate (); } else Write (m_StreamBuffer, bytes_transferred); @@ -540,9 +546,13 @@ namespace client void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool { - return now - u->LastActivity >= delta; - }); + auto itr = m_Sessions.begin(); + while(itr != m_Sessions.end()) { + if(now - (*itr)->LastActivity >= delta ) + itr = m_Sessions.erase(itr); + else + ++itr; + } } UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) diff --git a/Identity.cpp b/Identity.cpp index f39e9bd8..19c1a240 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -593,11 +593,25 @@ namespace data XORMetric operator^(const IdentHash& key1, const IdentHash& key2) { XORMetric m; +#if defined(__AVX__) // for AVX + __asm__ + ( + "vmovups %1, %%ymm0 \n" + "vmovups %2, %%ymm1 \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, %0 \n" + : "=m"(*m.metric) + : "m"(*key1), "m"(*key2) + : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler + ); +#else const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); m.metric_ll[0] = hash1[0] ^ hash2[0]; m.metric_ll[1] = hash1[1] ^ hash2[1]; m.metric_ll[2] = hash1[2] ^ hash2[2]; m.metric_ll[3] = hash1[3] ^ hash2[3]; +#endif + return m; } } diff --git a/LeaseSet.cpp b/LeaseSet.cpp index 04dc77c5..bddb517e 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -91,7 +91,7 @@ namespace data if (m_StoreLeases) { auto ret = m_Leases.insert (std::make_shared(lease)); - if (!ret.second) *(*ret.first) = lease; // update existing + if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing (*ret.first)->isUpdated = true; // check if lease's gateway is in our netDb if (!netdb.FindRouter (lease.tunnelGateway)) diff --git a/Makefile b/Makefile index 61220efd..acdc56d4 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,11 @@ DEPS := obj/make.dep include filelist.mk -USE_AESNI := yes -USE_STATIC := no -USE_MESHNET := no -USE_UPNP := no +USE_AESNI := yes +USE_AVX := yes +USE_STATIC := no +USE_MESHNET := no +USE_UPNP := no ifeq ($(WEBSOCKETS),1) NEEDED_CXXFLAGS += -DWITH_EVENTS diff --git a/Makefile.linux b/Makefile.linux index 8def8299..02f099d2 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -60,7 +60,14 @@ ifeq ($(USE_AESNI),yes) ifeq ($(IS_64),1) #check if AES-NI is supported by CPU ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0) - CPU_FLAGS = -maes -DAESNI + CPU_FLAGS += -maes -DAESNI endif endif endif + +ifeq ($(USE_AVX),yes) +#check if AVX supported by CPU +ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0) + CPU_FLAGS += -mavx +endif +endif diff --git a/Makefile.mingw b/Makefile.mingw index e2dae747..40cf14b3 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -39,9 +39,13 @@ endif # don't change following line to ifeq ($(USE_AESNI),yes) !!! ifeq ($(USE_AESNI),1) - CPU_FLAGS = -maes -DAESNI + CPU_FLAGS += -maes -DAESNI else - CPU_FLAGS = -msse + CPU_FLAGS += -msse +endif + +ifeq ($(USE_AVX),1) + CPU_FLAGS += -mavx endif ifeq ($(USE_ASLR),yes) diff --git a/Makefile.osx b/Makefile.osx index f40ce1af..c8a7de2a 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -3,21 +3,26 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX #CXXFLAGS = -g -O2 -Wall -std=c++11 INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib + +ifeq ($(USE_STATIC),yes) +LDLIBS = -lz -lcrypto -lssl /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread +else LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread +endif ifeq ($(USE_UPNP),yes) LDFLAGS += -ldl CXXFLAGS += -DUSE_UPNP endif -# OSX Notes -# http://www.hutsby.net/2011/08/macs-with-aes-ni.html -# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2 -# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic ifeq ($(USE_AESNI),yes) CXXFLAGS += -maes -DAESNI endif +ifeq ($(USE_AVX),yes) + CXXFLAGS += -mavx +endif + # Disabled, since it will be the default make rule. I think its better # to define the default rule in Makefile and not Makefile. - torkel #install: all diff --git a/NetDb.cpp b/NetDb.cpp index e75cf217..88d5483f 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -118,7 +118,6 @@ namespace data { SaveUpdated (); ManageLeaseSets (); - ManageLookupResponses (); } lastSave = ts; } @@ -857,33 +856,17 @@ namespace data } if (!replyMsg) - { - LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); - // find or cleate response - std::vector closestFloodfills; - bool found = false; - if (!numExcluded) - { - auto it = m_LookupResponses.find (ident); - if (it != m_LookupResponses.end ()) - { - closestFloodfills = it->second.first; - found = true; - } - } - if (!found) - { - std::set excludedRouters; - const uint8_t * exclude_ident = excluded; - for (int i = 0; i < numExcluded; i++) - { - excludedRouters.insert (exclude_ident); - exclude_ident += 32; - } - closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true); - if (!numExcluded) // save if no excluded - m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ()); + { + std::set excludedRouters; + const uint8_t * exclude_ident = excluded; + for (int i = 0; i < numExcluded; i++) + { + excludedRouters.insert (exclude_ident); + exclude_ident += 32; } + auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true); + if (closestFloodfills.empty ()) + LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills); } } @@ -929,7 +912,6 @@ namespace data uint8_t randomHash[32]; std::vector msgs; - std::set floodfills; LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ..."); for (int i = 0; i < numDestinations; i++) { @@ -941,9 +923,8 @@ namespace data return; } auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); - if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once + if (floodfill) { - floodfills.insert (floodfill.get ()); if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) throughTunnels = false; if (throughTunnels) @@ -1191,17 +1172,5 @@ namespace data ++it; } } - - void NetDb::ManageLookupResponses () - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();) - { - if (ts > it->second.second + 180) // 3 minutes - it = m_LookupResponses.erase (it); - else - ++it; - } - } } } diff --git a/NetDb.h b/NetDb.h index 954cc74e..0a05c143 100644 --- a/NetDb.h +++ b/NetDb.h @@ -112,7 +112,6 @@ namespace data void Publish (); void ManageLeaseSets (); void ManageRequests (); - void ManageLookupResponses (); void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20); @@ -143,8 +142,7 @@ namespace data /** router info we are bootstrapping from or nullptr if we are not currently doing that*/ std::shared_ptr m_FloodfillBootstrap; - - std::map, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp) + /** true if in hidden mode */ bool m_HiddenMode; diff --git a/RouterInfo.cpp b/RouterInfo.cpp index b570d6c2..4ae2f9e5 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -167,19 +167,19 @@ namespace data { uint8_t supportedTransports = 0; bool isValidAddress = true; - Address address; - s.read ((char *)&address.cost, sizeof (address.cost)); - s.read ((char *)&address.date, sizeof (address.date)); + auto address = std::make_shared
(); + s.read ((char *)&address->cost, sizeof (address->cost)); + s.read ((char *)&address->date, sizeof (address->date)); char transportStyle[5]; ReadString (transportStyle, 5, s); if (!strcmp (transportStyle, "NTCP")) - address.transportStyle = eTransportNTCP; + address->transportStyle = eTransportNTCP; else if (!strcmp (transportStyle, "SSU")) - address.transportStyle = eTransportSSU; + address->transportStyle = eTransportSSU; else - address.transportStyle = eTransportUnknown; - address.port = 0; - address.mtu = 0; + address->transportStyle = eTransportUnknown; + address->port = 0; + address->mtu = 0; uint16_t size, r = 0; s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); @@ -194,35 +194,35 @@ namespace data if (!strcmp (key, "host")) { boost::system::error_code ecode; - address.host = boost::asio::ip::address::from_string (value, ecode); + address->host = boost::asio::ip::address::from_string (value, ecode); if (ecode) { - if (address.transportStyle == eTransportNTCP) + if (address->transportStyle == eTransportNTCP) { supportedTransports |= eNTCPV4; // TODO: - address.addressString = value; + address->addressString = value; } else { supportedTransports |= eSSUV4; // TODO: - address.addressString = value; + address->addressString = value; } } else { // add supported protocol - if (address.host.is_v4 ()) - supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; + if (address->host.is_v4 ()) + supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; else - supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; + supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; } } else if (!strcmp (key, "port")) - address.port = boost::lexical_cast(value); + address->port = boost::lexical_cast(value); else if (!strcmp (key, "mtu")) - address.mtu = boost::lexical_cast(value); + address->mtu = boost::lexical_cast(value); else if (!strcmp (key, "key")) - Base64ToByteStream (value, strlen (value), address.key, 32); + Base64ToByteStream (value, strlen (value), address->key, 32); else if (!strcmp (key, "caps")) ExtractCaps (value); else if (key[0] == 'i') @@ -237,9 +237,9 @@ namespace data LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); if (s) continue; else return; } - if (index >= address.introducers.size ()) - address.introducers.resize (index + 1); - Introducer& introducer = address.introducers.at (index); + if (index >= address->introducers.size ()) + address->introducers.resize (index + 1); + Introducer& introducer = address->introducers.at (index); if (!strcmp (key, "ihost")) { boost::system::error_code ecode; @@ -256,7 +256,7 @@ namespace data } if (isValidAddress) { - addresses->push_back(std::make_shared
(address)); + addresses->push_back(address); m_SupportedTransports |= supportedTransports; } } diff --git a/SAM.cpp b/SAM.cpp index 84f8dbf6..c61e8577 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -54,11 +54,7 @@ namespace client case eSAMSocketTypeAcceptor: { if (m_Session) - { m_Session->DelSocket (shared_from_this ()); - if (m_Session->localDestination) - m_Session->localDestination->StopAcceptingStreams (); - } break; } default: @@ -289,6 +285,11 @@ namespace client dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } + else + { + // start accepting streams because we're not a datagram session + m_Session->localDestination->AcceptStreams (std::bind (&SAMSession::AcceptI2P, m_Session, std::placeholders::_1)); + } if (m_Session->localDestination->IsReady ()) SendSessionCreateReplyOk (); @@ -401,20 +402,25 @@ namespace client m_Session = m_Owner.FindSession (id); if (m_Session) { - if (!m_Session->localDestination->IsAcceptingStreams ()) - { - m_SocketType = eSAMSocketTypeAcceptor; - m_Session->AddSocket (shared_from_this ()); - m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->AddSocket (shared_from_this ()); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } + void SAMSocket::Accept(std::shared_ptr stream) + { + if(stream) { + m_SocketType = eSAMSocketTypeStream; + HandleI2PAccept(stream); + } else { + SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + auto s = shared_from_this (); + m_Owner.GetService ().post ([s] { s->Terminate (); }); + } + } size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) { LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len); @@ -577,8 +583,8 @@ namespace client { if (!ecode) s->Receive (); - else - s->Terminate (); + else + s->m_Owner.GetService ().post ([s] { s->Terminate (); }); }); } } @@ -622,10 +628,16 @@ namespace client boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination else - Terminate (); + { + auto s = shared_from_this (); + m_Owner.GetService ().post ([s] { s->Terminate (); }); + } } else - Terminate (); + { + auto s = shared_from_this (); + m_Owner.GetService ().post ([s] { s->Terminate (); }); + } } else { @@ -653,10 +665,6 @@ namespace client LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID); m_Stream = stream; context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); - auto session = m_Owner.FindSession (m_ID); - if (session) - session->localDestination->StopAcceptingStreams (); - m_SocketType = eSAMSocketTypeStream; if (!m_IsSilent) { // get remote peer address @@ -698,26 +706,76 @@ namespace client } SAMSession::SAMSession (std::shared_ptr dest): - localDestination (dest) + localDestination (dest), + m_BacklogPumper(dest->GetService()) { + PumpBacklog(); } - - SAMSession::~SAMSession () + + void SAMSession::AcceptI2P(std::shared_ptr stream) { - CloseStreams(); - i2p::client::context.DeleteLocalDestination (localDestination); + if(!stream) return; // fail + std::unique_lock lock(m_SocketsMutex); + if(m_Backlog.size() > SAM_MAX_ACCEPT_BACKLOG) { + stream->Close(); + return; + } + m_Backlog.push_back(stream); } - void SAMSession::CloseStreams () + void SAMSession::PumpBacklog() + { + // pump backlog every 100ms + boost::posix_time::milliseconds dlt(100); + m_BacklogPumper.expires_from_now(dlt); + m_BacklogPumper.async_wait(std::bind(&SAMSession::HandlePumpBacklog, this, std::placeholders::_1)); + } + + std::shared_ptr SAMSession::FindAcceptor() { + for (auto & sock : m_Sockets) { + auto t = sock->GetSocketType(); + if(t == eSAMSocketTypeAcceptor) { + return sock; + } + } + return nullptr; + } + + void SAMSession::HandlePumpBacklog(const boost::system::error_code & ec) + { + if(ec) return; { + std::unique_lock lock(m_SocketsMutex); + auto itr = m_Backlog.begin(); + while(itr != m_Backlog.end()) { + auto sock = FindAcceptor(); + if (sock) { + sock->Accept(*itr); + itr = m_Backlog.erase(itr); + } else { + ++itr; + } + } + } + PumpBacklog(); + } + + void SAMSession::CloseStreams () + { + m_BacklogPumper.cancel(); + localDestination->GetService().post([&] () { std::lock_guard lock(m_SocketsMutex); for (auto& sock : m_Sockets) { sock->CloseStream(); } - } - // XXX: should this be done inside locked parts? - m_Sockets.clear(); + for(auto & stream : m_Backlog) { + stream->Close(); + } + m_Sockets.clear(); + m_Backlog.clear(); + i2p::client::context.DeleteLocalDestination (localDestination); + }); } SAMBridge::SAMBridge (const std::string& address, int port): @@ -828,8 +886,9 @@ namespace client auto session = std::make_shared(localDestination); std::unique_lock l(m_SessionsMutex); auto ret = m_Sessions.insert (std::make_pair(id, session)); - if (!ret.second) + if (!ret.second) { LogPrint (eLogWarning, "SAM: Session ", id, " already exists"); + } return ret.first->second; } return nullptr; diff --git a/SAM.h b/SAM.h index db08c5a0..7d7d6d3a 100644 --- a/SAM.h +++ b/SAM.h @@ -20,7 +20,8 @@ namespace client { const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const int SAM_MAX_ACCEPT_BACKLOG = 50; const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; @@ -84,6 +85,8 @@ namespace client void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; SAMSocketType GetSocketType () const { return m_SocketType; }; + void Accept(std::shared_ptr stream); + private: void Terminate (); @@ -134,6 +137,8 @@ namespace client struct SAMSession { std::shared_ptr localDestination; + boost::asio::deadline_timer m_BacklogPumper; + std::list > m_Backlog; std::list > m_Sockets; std::mutex m_SocketsMutex; @@ -158,9 +163,15 @@ namespace client } return l; } - - SAMSession (std::shared_ptr dest); - ~SAMSession (); + + SAMSession (std::shared_ptr dest); + + void AcceptI2P(std::shared_ptr stream); + + std::shared_ptr FindAcceptor(); + + void PumpBacklog(); + void HandlePumpBacklog(const boost::system::error_code & ec); void CloseStreams (); }; diff --git a/SSUSession.cpp b/SSUSession.cpp index 696d367f..0e277432 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -1101,7 +1101,7 @@ namespace transport { // we are Alice LogPrint (eLogDebug, "SSU: sending peer test"); - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false); + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ()); if (!address) { LogPrint (eLogInfo, "SSU is not supported. Can't send peer test"); diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index 23cf16f3..b4ddc109 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -116,6 +116,7 @@ namespace tunnel if (!isFollowOnFragment) // create new incomlete message { m.nextFragmentNum = 1; + m.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); auto ret = m_IncompleteMessages.insert (std::pair(msgID, m)); if (ret.second) HandleOutOfSequenceFragments (msgID, ret.first->second); @@ -284,6 +285,14 @@ namespace tunnel else ++it; } + // incomplete messages + for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + { + if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) + it = m_IncompleteMessages.erase (it); + else + ++it; + } } } } diff --git a/TunnelEndpoint.h b/TunnelEndpoint.h index 60c4fc0a..e13ced34 100644 --- a/TunnelEndpoint.h +++ b/TunnelEndpoint.h @@ -15,6 +15,7 @@ namespace tunnel { struct TunnelMessageBlockEx: public TunnelMessageBlock { + uint64_t receiveTime; // milliseconds since epoch uint8_t nextFragmentNum; }; diff --git a/Win32/installer.iss b/Win32/installer.iss index d51ae7f2..2408efdb 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.10.1" +#define I2Pd_ver "2.11.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index afe7e118..40688197 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="2.11.0"> diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a3d58ce6..c44594f0 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \ ../../TunnelPool.cpp \ ../../Timestamp.cpp \ ../../Event.cpp \ + ../../BloomFilter.cpp \ ../../util.cpp \ ../../i2pd.cpp ../../UPnP.cpp diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 8b42c063..a1fd19c2 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -26,6 +26,7 @@ set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) set ( CMAKE_SOURCE_DIR ".." ) set (LIBI2PD_SRC + "${CMAKE_SOURCE_DIR}/BloomFilter.cpp" "${CMAKE_SOURCE_DIR}/Config.cpp" "${CMAKE_SOURCE_DIR}/Crypto.cpp" "${CMAKE_SOURCE_DIR}/Garlic.cpp" diff --git a/debian/changelog b/debian/changelog index f71b2e4a..67af8310 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +i2pd (2.11.0-1) unstable; urgency=low + + * updated to version 2.11.0/0.9.28 + + -- orignal Sun, 18 Dec 2016 21:01:30 +0000 + +i2pd (2.10.2-1) unstable; urgency=low + + * updated to version 2.10.2 + + -- orignal Sun, 4 Dec 2016 19:38:30 +0000 + i2pd (2.10.1-1) unstable; urgency=low * updated to version 2.10.1 diff --git a/debian/patches/01-tune-build-opts.patch b/debian/patches/01-tune-build-opts.patch index fba12f4e..4420d387 100644 --- a/debian/patches/01-tune-build-opts.patch +++ b/debian/patches/01-tune-build-opts.patch @@ -1,16 +1,18 @@ diff --git a/Makefile b/Makefile -index b6fc795..abc3ace 100644 +index bdadfe0..2f71eec 100644 + --- a/Makefile +++ b/Makefile @@ -9,10 +9,10 @@ DEPS := obj/make.dep include filelist.mk --USE_AESNI := yes -+USE_AESNI := no - USE_STATIC := no - USE_MESHNET := no - USE_UPNP := no +-USE_AESNI := yes ++USE_AESNI := no +-USE_AVX := yes ++USE_AVX := no + USE_STATIC := no + USE_MESHNET := no + USE_UPNP := no ifeq ($(WEBSOCKETS),1) - NEEDED_CXXFLAGS += -DWITH_EVENTS diff --git a/filelist.mk b/filelist.mk index 94ce2f22..76f58785 100644 --- a/filelist.mk +++ b/filelist.mk @@ -1,5 +1,5 @@ LIB_SRC = \ - Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \ + BloomFilter.cpp Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \ Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \ Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml index 37a736fd..b1f71567 100644 --- a/qt/i2pd_qt/android/AndroidManifest.xml +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 143c7b8d..e51eb963 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -36,7 +36,7 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ ../../SSUData.cpp ../../SSUSession.cpp ../../Streaming.cpp ../../TransitTunnel.cpp \ ../../Transports.cpp ../../Tunnel.cpp ../../TunnelEndpoint.cpp ../../TunnelGateway.cpp \ ../../TunnelPool.cpp ../../UPnP.cpp ../../Gzip.cpp ../../Timestamp.cpp ../../util.cpp \ - ../../Event.cpp ../../i2pd.cpp + ../../Event.cpp ../../BloomFiler.cpp ../../i2pd.cpp HEADERS += DaemonQT.h mainwindow.h \ ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \ @@ -50,7 +50,8 @@ HEADERS += DaemonQT.h mainwindow.h \ ../../Streaming.h ../../Timestamp.h ../../TransitTunnel.h ../../Transports.h \ ../../TransportSession.h ../../Tunnel.h ../../TunnelBase.h ../../TunnelConfig.h \ ../../TunnelEndpoint.h ../../TunnelGateway.h ../../TunnelPool.h ../../UPnP.h \ - ../../util.h ../../version.h ../../Gzip.h ../../Tag.h ../../Event.h + ../../util.h ../../version.h ../../Gzip.h ../../Tag.h \ + ../../BloomFiler.h ../../Event.h FORMS += mainwindow.ui diff --git a/version.h b/version.h index 9aadf505..501f7272 100644 --- a/version.h +++ b/version.h @@ -7,8 +7,8 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 10 -#define I2PD_VERSION_MICRO 1 +#define I2PD_VERSION_MINOR 11 +#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define VERSION I2PD_VERSION @@ -21,7 +21,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 27 +#define I2P_VERSION_MICRO 28 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)