2019-04-21 16:44:27 +00:00
|
|
|
#include <service/outbound_context.hpp>
|
|
|
|
|
|
|
|
#include <router/abstractrouter.hpp>
|
|
|
|
#include <service/async_key_exchange.hpp>
|
|
|
|
#include <service/hidden_service_address_lookup.hpp>
|
|
|
|
#include <service/endpoint.hpp>
|
|
|
|
#include <nodedb.hpp>
|
|
|
|
#include <profiling.hpp>
|
2019-09-01 12:38:03 +00:00
|
|
|
#include <util/meta/memfn.hpp>
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2020-02-10 18:46:42 +00:00
|
|
|
#include <service/endpoint_util.hpp>
|
|
|
|
|
2020-01-02 22:08:45 +00:00
|
|
|
#include <random>
|
|
|
|
#include <algorithm>
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
namespace llarp
|
|
|
|
{
|
|
|
|
namespace service
|
|
|
|
{
|
|
|
|
bool
|
|
|
|
OutboundContext::Stop()
|
|
|
|
{
|
|
|
|
markedBad = true;
|
|
|
|
return path::Builder::Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::IsDone(llarp_time_t now) const
|
|
|
|
{
|
|
|
|
(void)now;
|
|
|
|
return AvailablePaths(path::ePathRoleAny) == 0 && ShouldRemove();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::ShouldBundleRC() const
|
|
|
|
{
|
|
|
|
return m_Endpoint->ShouldBundleRC();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-04-23 14:28:59 +00:00
|
|
|
OutboundContext::HandleDataDrop(path::Path_ptr p, const PathID_t& dst,
|
2019-04-21 16:44:27 +00:00
|
|
|
uint64_t seq)
|
|
|
|
{
|
|
|
|
// pick another intro
|
|
|
|
if(dst == remoteIntro.pathID && remoteIntro.router == p->Endpoint())
|
|
|
|
{
|
|
|
|
LogWarn(Name(), " message ", seq, " dropped by endpoint ",
|
|
|
|
p->Endpoint(), " via ", dst);
|
|
|
|
if(MarkCurrentIntroBad(Now()))
|
|
|
|
{
|
2019-06-12 14:27:25 +00:00
|
|
|
SwapIntros();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2020-02-14 20:14:43 +00:00
|
|
|
UpdateIntroSet();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
|
2019-09-20 16:56:19 +00:00
|
|
|
: path::Builder(parent->Router(), 4, path::default_len)
|
2019-04-21 16:44:27 +00:00
|
|
|
, SendContext(introset.A, {}, this, parent)
|
2020-01-27 21:30:41 +00:00
|
|
|
, location(introset.A.Addr().ToKey())
|
2019-04-21 16:44:27 +00:00
|
|
|
, currentIntroSet(introset)
|
|
|
|
|
|
|
|
{
|
|
|
|
updatingIntroSet = false;
|
2019-09-20 16:56:19 +00:00
|
|
|
for(const auto& intro : introset.I)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
if(intro.expiresAt > m_NextIntro.expiresAt)
|
|
|
|
m_NextIntro = intro;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 23:42:13 +00:00
|
|
|
OutboundContext::~OutboundContext() = default;
|
2019-04-21 16:44:27 +00:00
|
|
|
|
|
|
|
/// actually swap intros
|
|
|
|
void
|
|
|
|
OutboundContext::SwapIntros()
|
|
|
|
{
|
2019-06-26 13:09:40 +00:00
|
|
|
if(remoteIntro != m_NextIntro)
|
|
|
|
{
|
2019-09-20 16:56:19 +00:00
|
|
|
LogInfo(Name(), " swap intro to use ", RouterID(m_NextIntro.router));
|
2019-06-26 13:09:40 +00:00
|
|
|
remoteIntro = m_NextIntro;
|
|
|
|
m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro);
|
|
|
|
ShiftIntroduction(false);
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2020-02-19 21:58:01 +00:00
|
|
|
OutboundContext::OnIntroSetUpdate(const Address&,
|
|
|
|
nonstd::optional< IntroSet > foundIntro,
|
|
|
|
const RouterID& endpoint)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
if(markedBad)
|
|
|
|
return true;
|
|
|
|
updatingIntroSet = false;
|
2020-02-06 19:35:31 +00:00
|
|
|
if(foundIntro.has_value())
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-02-24 19:40:45 +00:00
|
|
|
if(foundIntro->T == 0s)
|
2020-02-06 19:35:31 +00:00
|
|
|
{
|
|
|
|
LogWarn(Name(),
|
|
|
|
" got introset with zero timestamp: ", foundIntro.value());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if(currentIntroSet.T > foundIntro->T)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
LogInfo("introset is old, dropping");
|
|
|
|
return true;
|
|
|
|
}
|
2019-07-01 21:35:49 +00:00
|
|
|
|
2019-12-30 10:19:03 +00:00
|
|
|
const llarp_time_t now = Now();
|
2020-02-06 19:35:31 +00:00
|
|
|
if(foundIntro->IsExpired(now))
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
LogError("got expired introset from lookup from ", endpoint);
|
|
|
|
return true;
|
|
|
|
}
|
2020-02-06 19:35:31 +00:00
|
|
|
currentIntroSet = foundIntro.value();
|
2020-02-21 20:57:25 +00:00
|
|
|
ShiftIntroduction(false);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
else
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
2019-04-21 16:44:27 +00:00
|
|
|
++m_LookupFails;
|
2019-09-20 16:56:19 +00:00
|
|
|
LogWarn(Name(), " failed to look up introset, fails=", m_LookupFails);
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::ReadyToSend() const
|
|
|
|
{
|
|
|
|
return (!remoteIntro.router.IsZero())
|
|
|
|
&& GetPathByRouter(remoteIntro.router) != nullptr;
|
|
|
|
}
|
|
|
|
|
2019-09-20 16:56:19 +00:00
|
|
|
void
|
|
|
|
OutboundContext::ShiftIntroRouter(const RouterID r)
|
|
|
|
{
|
|
|
|
const auto now = Now();
|
|
|
|
Introduction selectedIntro;
|
|
|
|
for(const auto& intro : currentIntroSet.I)
|
|
|
|
{
|
|
|
|
if(intro.expiresAt > selectedIntro.expiresAt && intro.router != r)
|
|
|
|
{
|
|
|
|
selectedIntro = intro;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(selectedIntro.router.IsZero() || selectedIntro.ExpiresSoon(now))
|
|
|
|
return;
|
|
|
|
LogWarn(Name(), " shfiting intro off of ", r, " to ",
|
|
|
|
RouterID(selectedIntro.router));
|
|
|
|
m_NextIntro = selectedIntro;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutboundContext::HandlePathBuildTimeout(path::Path_ptr p)
|
|
|
|
{
|
|
|
|
ShiftIntroRouter(p->Endpoint());
|
|
|
|
path::Builder::HandlePathBuildTimeout(p);
|
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
void
|
2019-04-23 14:28:59 +00:00
|
|
|
OutboundContext::HandlePathBuilt(path::Path_ptr p)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
path::Builder::HandlePathBuilt(p);
|
|
|
|
/// don't use it if we are marked bad
|
|
|
|
if(markedBad)
|
|
|
|
return;
|
2019-06-02 21:19:10 +00:00
|
|
|
p->SetDataHandler(
|
|
|
|
util::memFn(&OutboundContext::HandleHiddenServiceFrame, this));
|
|
|
|
p->SetDropHandler(util::memFn(&OutboundContext::HandleDataDrop, this));
|
2019-05-07 12:31:34 +00:00
|
|
|
// we now have a path to the next intro, swap intros
|
2019-09-02 16:44:08 +00:00
|
|
|
if(p->Endpoint() == m_NextIntro.router)
|
2019-05-07 12:31:34 +00:00
|
|
|
SwapIntros();
|
2019-09-20 16:56:19 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
LogInfo(Name(), " built to non aligned router: ", p->Endpoint());
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload,
|
2019-04-23 17:35:20 +00:00
|
|
|
ProtocolType t)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-11-29 05:15:05 +00:00
|
|
|
if(not currentConvoTag.IsZero())
|
|
|
|
return;
|
2019-05-07 14:11:10 +00:00
|
|
|
if(remoteIntro.router.IsZero())
|
|
|
|
SwapIntros();
|
|
|
|
|
2019-04-23 17:35:20 +00:00
|
|
|
auto path = m_PathSet->GetNewestPathByRouter(remoteIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
if(path == nullptr)
|
|
|
|
{
|
|
|
|
// try parent as fallback
|
|
|
|
path = m_Endpoint->GetPathByRouter(remoteIntro.router);
|
|
|
|
if(path == nullptr)
|
|
|
|
{
|
2019-05-07 12:31:34 +00:00
|
|
|
if(!BuildCooldownHit(Now()))
|
|
|
|
BuildOneAlignedTo(remoteIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
LogWarn(Name(), " dropping intro frame, no path to ",
|
|
|
|
remoteIntro.router);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-11-29 05:15:05 +00:00
|
|
|
currentConvoTag.Randomize();
|
2019-11-27 15:59:36 +00:00
|
|
|
auto frame = std::make_shared< ProtocolFrame >();
|
|
|
|
auto ex = std::make_shared< AsyncKeyExchange >(
|
2019-05-28 19:45:08 +00:00
|
|
|
m_Endpoint->RouterLogic(), remoteIdent, m_Endpoint->GetIdentity(),
|
2019-06-11 16:44:05 +00:00
|
|
|
currentIntroSet.K, remoteIntro, m_DataHandler, currentConvoTag, t);
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2020-01-10 12:23:54 +00:00
|
|
|
ex->hook = std::bind(&OutboundContext::Send, shared_from_this(),
|
|
|
|
std::placeholders::_1, path);
|
2019-04-21 16:44:27 +00:00
|
|
|
|
|
|
|
ex->msg.PutBuffer(payload);
|
|
|
|
ex->msg.introReply = path->intro;
|
2019-11-27 15:59:36 +00:00
|
|
|
frame->F = ex->msg.introReply.pathID;
|
2019-07-09 13:47:24 +00:00
|
|
|
m_Endpoint->CryptoWorker()->addJob(
|
2019-11-27 15:59:36 +00:00
|
|
|
std::bind(&AsyncKeyExchange::Encrypt, ex, frame));
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
OutboundContext::Name() const
|
|
|
|
{
|
|
|
|
return "OBContext:" + m_Endpoint->Name() + "-"
|
|
|
|
+ currentIntroSet.A.Addr().ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-14 20:14:43 +00:00
|
|
|
OutboundContext::UpdateIntroSet()
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
if(updatingIntroSet || markedBad)
|
|
|
|
return;
|
2019-12-30 10:19:03 +00:00
|
|
|
const auto addr = currentIntroSet.A.Addr();
|
2020-02-18 18:30:24 +00:00
|
|
|
// we want to use the parent endpoint's paths because outbound context
|
|
|
|
// does not implement path::PathSet::HandleGotIntroMessage
|
2020-02-18 14:11:08 +00:00
|
|
|
const auto paths = GetManyPathsWithUniqueEndpoints(m_Endpoint, 2);
|
2020-02-14 18:06:19 +00:00
|
|
|
uint64_t relayOrder = 0;
|
2020-02-12 16:40:48 +00:00
|
|
|
for(const auto& path : paths)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
|
2020-01-10 12:23:54 +00:00
|
|
|
m_Endpoint,
|
|
|
|
util::memFn(&OutboundContext::OnIntroSetUpdate, shared_from_this()),
|
2020-02-13 22:19:12 +00:00
|
|
|
location, PubKey{addr.as_array()}, relayOrder,
|
|
|
|
m_Endpoint->GenTXID());
|
|
|
|
relayOrder++;
|
2020-02-10 18:46:42 +00:00
|
|
|
if(job->SendRequestViaPath(path, m_Endpoint->Router()))
|
|
|
|
updatingIntroSet = true;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
util::StatusObject
|
|
|
|
OutboundContext::ExtractStatus() const
|
|
|
|
{
|
2019-08-19 09:33:26 +00:00
|
|
|
auto obj = path::Builder::ExtractStatus();
|
|
|
|
obj["currentConvoTag"] = currentConvoTag.ToHex();
|
|
|
|
obj["remoteIntro"] = remoteIntro.ExtractStatus();
|
2020-02-25 17:05:13 +00:00
|
|
|
obj["sessionCreatedAt"] = to_json(createdAt);
|
|
|
|
obj["lastGoodSend"] = to_json(lastGoodSend);
|
2019-08-19 09:33:26 +00:00
|
|
|
obj["seqno"] = sequenceNo;
|
|
|
|
obj["markedBad"] = markedBad;
|
2020-02-25 17:05:13 +00:00
|
|
|
obj["lastShift"] = to_json(lastShift);
|
2019-08-19 09:33:26 +00:00
|
|
|
obj["remoteIdentity"] = remoteIdent.Addr().ToString();
|
|
|
|
obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus();
|
|
|
|
obj["nextIntro"] = m_NextIntro.ExtractStatus();
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
std::transform(m_BadIntros.begin(), m_BadIntros.end(),
|
2019-08-19 09:33:26 +00:00
|
|
|
std::back_inserter(obj["badIntros"]),
|
2019-04-21 16:44:27 +00:00
|
|
|
[](const auto& item) -> util::StatusObject {
|
2020-02-24 19:40:45 +00:00
|
|
|
return item.first.ExtractStatus();
|
2019-04-21 16:44:27 +00:00
|
|
|
});
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-04-23 16:13:22 +00:00
|
|
|
OutboundContext::Pump(llarp_time_t now)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
// we are probably dead af
|
|
|
|
if(m_LookupFails > 16 || m_BuildFails > 10)
|
|
|
|
return true;
|
2019-09-20 16:56:19 +00:00
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
// check for expiration
|
|
|
|
if(remoteIntro.ExpiresSoon(now))
|
|
|
|
{
|
2020-02-18 16:00:45 +00:00
|
|
|
UpdateIntroSet();
|
2019-04-21 16:44:27 +00:00
|
|
|
// shift intro if it expires "soon"
|
2019-09-19 14:41:31 +00:00
|
|
|
if(ShiftIntroduction())
|
|
|
|
SwapIntros(); // swap intros if we shifted
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
// lookup router in intro if set and unknown
|
2019-05-02 16:23:31 +00:00
|
|
|
m_Endpoint->EnsureRouterIsKnown(remoteIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
// expire bad intros
|
|
|
|
auto itr = m_BadIntros.begin();
|
|
|
|
while(itr != m_BadIntros.end())
|
|
|
|
{
|
2019-09-19 14:41:31 +00:00
|
|
|
if(now > itr->second && now - itr->second > path::default_lifetime)
|
2019-04-21 16:44:27 +00:00
|
|
|
itr = m_BadIntros.erase(itr);
|
|
|
|
else
|
|
|
|
++itr;
|
|
|
|
}
|
|
|
|
// send control message if we look too quiet
|
2020-02-24 19:40:45 +00:00
|
|
|
if(lastGoodSend > 0s)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
if(now - lastGoodSend > (sendTimeout / 2))
|
|
|
|
{
|
|
|
|
if(!GetNewestPathByRouter(remoteIntro.router))
|
|
|
|
{
|
2019-05-07 12:31:34 +00:00
|
|
|
if(!BuildCooldownHit(now))
|
|
|
|
BuildOneAlignedTo(remoteIntro.router);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Encrypted< 64 > tmp;
|
|
|
|
tmp.Randomize();
|
|
|
|
llarp_buffer_t buf(tmp.data(), tmp.size());
|
|
|
|
AsyncEncryptAndSendTo(buf, eProtocolControl);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if we are dead return true so we are removed
|
2020-02-24 19:40:45 +00:00
|
|
|
return lastGoodSend > 0s
|
2019-04-21 16:44:27 +00:00
|
|
|
? (now >= lastGoodSend && now - lastGoodSend > sendTimeout)
|
|
|
|
: (now >= createdAt && now - createdAt > connectTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-05-08 14:01:31 +00:00
|
|
|
OutboundContext::SelectHop(llarp_nodedb* db,
|
|
|
|
const std::set< RouterID >& prev,
|
2019-04-21 16:44:27 +00:00
|
|
|
RouterContact& cur, size_t hop,
|
|
|
|
path::PathRole roles)
|
|
|
|
{
|
2019-05-08 14:54:03 +00:00
|
|
|
if(m_NextIntro.router.IsZero() || prev.count(m_NextIntro.router))
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-09-20 16:56:19 +00:00
|
|
|
ShiftIntroduction(false);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2019-09-20 16:56:19 +00:00
|
|
|
if(m_NextIntro.router.IsZero())
|
|
|
|
return false;
|
2019-05-08 14:01:31 +00:00
|
|
|
std::set< RouterID > exclude = prev;
|
|
|
|
exclude.insert(m_NextIntro.router);
|
2019-07-15 09:15:51 +00:00
|
|
|
for(const auto& snode : m_Endpoint->SnodeBlacklist())
|
2019-05-10 16:19:33 +00:00
|
|
|
exclude.insert(snode);
|
2019-09-03 15:56:56 +00:00
|
|
|
if(hop == 0)
|
|
|
|
{
|
|
|
|
// exclude any exits as our first hop
|
|
|
|
const auto exits = m_Endpoint->GetExitRouters();
|
|
|
|
exclude.insert(exits.begin(), exits.end());
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
if(hop == numHops - 1)
|
|
|
|
{
|
2019-05-07 12:31:34 +00:00
|
|
|
m_Endpoint->EnsureRouterIsKnown(m_NextIntro.router);
|
|
|
|
if(db->Get(m_NextIntro.router, cur))
|
2019-04-21 16:44:27 +00:00
|
|
|
return true;
|
|
|
|
++m_BuildFails;
|
|
|
|
return false;
|
|
|
|
}
|
2019-05-08 14:49:09 +00:00
|
|
|
return path::Builder::SelectHop(db, exclude, cur, hop, roles);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::ShouldBuildMore(llarp_time_t now) const
|
|
|
|
{
|
2020-02-17 20:40:10 +00:00
|
|
|
if(markedBad || path::Builder::BuildCooldownHit(now))
|
2019-04-21 16:44:27 +00:00
|
|
|
return false;
|
2020-02-17 20:40:10 +00:00
|
|
|
const bool canBuild = NumInStatus(path::ePathBuilding) == 0
|
|
|
|
and path::Builder::ShouldBuildMore(now);
|
2020-02-17 20:44:23 +00:00
|
|
|
if(not canBuild)
|
|
|
|
return false;
|
2020-02-24 19:40:45 +00:00
|
|
|
llarp_time_t t = 0s;
|
2019-07-01 13:44:25 +00:00
|
|
|
ForEachPath([&t](path::Path_ptr path) {
|
2019-06-28 15:27:53 +00:00
|
|
|
if(path->IsReady())
|
|
|
|
t = std::max(path->ExpireTime(), t);
|
2019-06-28 15:19:12 +00:00
|
|
|
});
|
2020-02-17 20:47:57 +00:00
|
|
|
return t >= now + path::default_lifetime / 4;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::MarkCurrentIntroBad(llarp_time_t now)
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
|
|
|
return MarkIntroBad(remoteIntro, now);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::MarkIntroBad(const Introduction& intro, llarp_time_t now)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
// insert bad intro
|
2019-09-20 16:56:19 +00:00
|
|
|
m_BadIntros[intro] = now;
|
2019-05-23 12:22:48 +00:00
|
|
|
// try shifting intro without rebuild
|
|
|
|
if(ShiftIntroduction(false))
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-05-23 12:22:48 +00:00
|
|
|
// we shifted
|
|
|
|
// check if we have a path to the next intro router
|
|
|
|
if(GetNewestPathByRouter(m_NextIntro.router))
|
|
|
|
return true;
|
|
|
|
// we don't have a path build one if we aren't building too fast
|
2019-05-07 12:31:34 +00:00
|
|
|
if(!BuildCooldownHit(now))
|
|
|
|
BuildOneAlignedTo(m_NextIntro.router);
|
2019-05-23 12:22:48 +00:00
|
|
|
return true;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2019-07-06 17:03:40 +00:00
|
|
|
|
|
|
|
// we didn't shift check if we should update introset
|
|
|
|
if(now - lastShift >= MIN_SHIFT_INTERVAL
|
|
|
|
|| currentIntroSet.HasExpiredIntros(now)
|
|
|
|
|| currentIntroSet.IsExpired(now))
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-07-06 17:03:40 +00:00
|
|
|
// update introset
|
|
|
|
LogInfo(Name(), " updating introset");
|
2020-02-14 20:14:43 +00:00
|
|
|
UpdateIntroSet();
|
2019-07-06 17:03:40 +00:00
|
|
|
return true;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2019-07-06 17:03:40 +00:00
|
|
|
return false;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::ShiftIntroduction(bool rebuild)
|
|
|
|
{
|
|
|
|
bool success = false;
|
|
|
|
auto now = Now();
|
|
|
|
if(now - lastShift < MIN_SHIFT_INTERVAL)
|
|
|
|
return false;
|
2020-01-02 22:08:45 +00:00
|
|
|
bool shifted = false;
|
|
|
|
std::vector< Introduction > intros = currentIntroSet.I;
|
2020-01-03 19:50:12 +00:00
|
|
|
if(intros.size() > 1)
|
2020-01-02 22:08:45 +00:00
|
|
|
{
|
|
|
|
std::random_device rd;
|
|
|
|
std::mt19937 g(rd());
|
|
|
|
std::shuffle(intros.begin(), intros.end(), g);
|
|
|
|
}
|
|
|
|
|
2019-05-07 12:31:34 +00:00
|
|
|
// to find a intro on the same router as before that is newer
|
2020-01-02 22:08:45 +00:00
|
|
|
for(const auto& intro : intros)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
if(intro.ExpiresSoon(now))
|
|
|
|
continue;
|
2019-07-15 09:15:51 +00:00
|
|
|
if(m_Endpoint->SnodeBlacklist().count(intro.router))
|
2019-05-10 16:19:33 +00:00
|
|
|
continue;
|
2019-04-21 16:44:27 +00:00
|
|
|
if(m_BadIntros.find(intro) == m_BadIntros.end()
|
|
|
|
&& remoteIntro.router == intro.router)
|
|
|
|
{
|
2019-05-07 12:31:34 +00:00
|
|
|
if(intro.expiresAt > m_NextIntro.expiresAt)
|
|
|
|
{
|
2019-05-23 12:22:48 +00:00
|
|
|
success = true;
|
2019-05-07 12:31:34 +00:00
|
|
|
m_NextIntro = intro;
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-23 12:22:48 +00:00
|
|
|
if(!success)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-05-23 12:22:48 +00:00
|
|
|
/// pick newer intro not on same router
|
2020-01-02 22:08:45 +00:00
|
|
|
for(const auto& intro : intros)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2019-07-15 09:15:51 +00:00
|
|
|
if(m_Endpoint->SnodeBlacklist().count(intro.router))
|
2019-05-23 12:22:48 +00:00
|
|
|
continue;
|
|
|
|
m_Endpoint->EnsureRouterIsKnown(intro.router);
|
|
|
|
if(intro.ExpiresSoon(now))
|
|
|
|
continue;
|
|
|
|
if(m_BadIntros.find(intro) == m_BadIntros.end()
|
|
|
|
&& m_NextIntro != intro)
|
2019-05-07 12:31:34 +00:00
|
|
|
{
|
2019-05-23 12:22:48 +00:00
|
|
|
if(intro.expiresAt > m_NextIntro.expiresAt)
|
|
|
|
{
|
|
|
|
shifted = intro.router != m_NextIntro.router;
|
|
|
|
m_NextIntro = intro;
|
|
|
|
success = true;
|
|
|
|
}
|
2019-05-07 12:31:34 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-07 12:31:34 +00:00
|
|
|
if(m_NextIntro.router.IsZero())
|
|
|
|
return false;
|
|
|
|
if(shifted)
|
2019-04-21 16:44:27 +00:00
|
|
|
lastShift = now;
|
2019-05-23 12:22:48 +00:00
|
|
|
if(rebuild && !BuildCooldownHit(Now()))
|
|
|
|
BuildOneAlignedTo(m_NextIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-04-23 14:28:59 +00:00
|
|
|
OutboundContext::HandlePathDied(path::Path_ptr path)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
// unconditionally update introset
|
2020-02-14 20:14:43 +00:00
|
|
|
UpdateIntroSet();
|
2019-04-21 16:44:27 +00:00
|
|
|
const RouterID endpoint(path->Endpoint());
|
|
|
|
// if a path to our current intro died...
|
|
|
|
if(endpoint == remoteIntro.router)
|
|
|
|
{
|
|
|
|
// figure out how many paths to this router we have
|
|
|
|
size_t num = 0;
|
2019-04-23 16:13:22 +00:00
|
|
|
ForEachPath([&](const path::Path_ptr& p) {
|
2019-04-21 16:44:27 +00:00
|
|
|
if(p->Endpoint() == endpoint && p->IsReady())
|
|
|
|
++num;
|
|
|
|
});
|
|
|
|
// if we have more than two then we are probably fine
|
|
|
|
if(num > 2)
|
|
|
|
return;
|
|
|
|
// if we have one working one ...
|
|
|
|
if(num == 1)
|
|
|
|
{
|
|
|
|
num = 0;
|
2019-04-23 16:13:22 +00:00
|
|
|
ForEachPath([&](const path::Path_ptr& p) {
|
2019-04-21 16:44:27 +00:00
|
|
|
if(p->Endpoint() == endpoint)
|
|
|
|
++num;
|
|
|
|
});
|
|
|
|
// if we have 2 or more established or pending don't do anything
|
|
|
|
if(num > 2)
|
|
|
|
return;
|
|
|
|
BuildOneAlignedTo(endpoint);
|
|
|
|
}
|
|
|
|
else if(num == 0)
|
|
|
|
{
|
|
|
|
// we have no paths to this router right now
|
|
|
|
// hop off it
|
|
|
|
Introduction picked;
|
|
|
|
// get the latest intro that isn't on that endpoint
|
|
|
|
for(const auto& intro : currentIntroSet.I)
|
|
|
|
{
|
|
|
|
if(intro.router == endpoint)
|
|
|
|
continue;
|
|
|
|
if(intro.expiresAt > picked.expiresAt)
|
|
|
|
picked = intro;
|
|
|
|
}
|
|
|
|
// we got nothing
|
|
|
|
if(picked.router.IsZero())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_NextIntro = picked;
|
|
|
|
// check if we have a path to this router
|
|
|
|
num = 0;
|
2019-04-23 16:13:22 +00:00
|
|
|
ForEachPath([&](const path::Path_ptr& p) {
|
2019-05-23 13:13:03 +00:00
|
|
|
// don't count timed out paths
|
|
|
|
if(p->Status() != path::ePathTimeout
|
|
|
|
&& p->Endpoint() == m_NextIntro.router)
|
2019-04-21 16:44:27 +00:00
|
|
|
++num;
|
|
|
|
});
|
|
|
|
// build a path if one isn't already pending build or established
|
2019-05-07 12:31:34 +00:00
|
|
|
BuildOneAlignedTo(m_NextIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-04-23 14:28:59 +00:00
|
|
|
OutboundContext::HandleHiddenServiceFrame(path::Path_ptr p,
|
2019-04-22 17:38:29 +00:00
|
|
|
const ProtocolFrame& frame)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
return m_Endpoint->HandleHiddenServiceFrame(p, frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace service
|
|
|
|
|
|
|
|
} // namespace llarp
|