2021-03-09 22:24:35 +00:00
|
|
|
#include "outbound_context.hpp"
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2021-03-09 22:24:35 +00:00
|
|
|
#include <llarp/router/abstractrouter.hpp>
|
|
|
|
#include "async_key_exchange.hpp"
|
|
|
|
#include "hidden_service_address_lookup.hpp"
|
|
|
|
#include "endpoint.hpp"
|
|
|
|
#include <llarp/nodedb.hpp>
|
|
|
|
#include <llarp/profiling.hpp>
|
|
|
|
#include <llarp/util/meta/memfn.hpp>
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2021-03-09 22:24:35 +00:00
|
|
|
#include "endpoint_util.hpp"
|
2021-03-16 11:56:27 +00:00
|
|
|
#include "service/protocol_type.hpp"
|
2020-02-10 18:46:42 +00:00
|
|
|
|
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
|
2020-04-07 18:38:56 +00:00
|
|
|
OutboundContext::HandleDataDrop(path::Path_ptr p, const PathID_t& dst, uint64_t seq)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
// pick another intro
|
2020-04-07 18:38:56 +00:00
|
|
|
if (dst == remoteIntro.pathID && remoteIntro.router == p->Endpoint())
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
LogWarn(Name(), " message ", seq, " dropped by endpoint ", p->Endpoint(), " via ", dst);
|
2022-04-18 01:22:53 +00:00
|
|
|
markedBad = remoteIntro.IsExpired(Now());
|
2021-05-01 12:52:41 +00:00
|
|
|
MarkCurrentIntroBad(Now());
|
2022-04-18 01:22:53 +00:00
|
|
|
ShiftIntroRouter(p->Endpoint());
|
2021-05-16 20:26:32 +00:00
|
|
|
UpdateIntroSet();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-12 21:37:01 +00:00
|
|
|
constexpr auto OutboundContextNumPaths = 4;
|
2021-05-01 12:52:41 +00:00
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
|
2021-05-01 12:52:41 +00:00
|
|
|
: path::Builder{parent->Router(), OutboundContextNumPaths, parent->numHops}
|
|
|
|
, SendContext{introset.addressKeys, {}, this, parent}
|
|
|
|
, location{introset.addressKeys.Addr().ToKey()}
|
2021-06-02 19:52:13 +00:00
|
|
|
, addr{introset.addressKeys.Addr()}
|
2021-05-01 12:52:41 +00:00
|
|
|
, currentIntroSet{introset}
|
2019-04-21 16:44:27 +00:00
|
|
|
|
|
|
|
{
|
2021-10-12 21:37:01 +00:00
|
|
|
assert(not introset.intros.empty());
|
2019-04-21 16:44:27 +00:00
|
|
|
updatingIntroSet = false;
|
2021-10-12 21:37:01 +00:00
|
|
|
|
|
|
|
// pick random first intro
|
|
|
|
auto it = introset.intros.begin();
|
|
|
|
if (introset.intros.size() > 1)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-10-12 21:37:01 +00:00
|
|
|
CSRNG rng{};
|
|
|
|
it += std::uniform_int_distribution<size_t>{0, introset.intros.size() - 1}(rng);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2021-10-12 21:37:01 +00:00
|
|
|
m_NextIntro = *it;
|
2021-03-26 20:45:19 +00:00
|
|
|
currentConvoTag.Randomize();
|
2021-06-02 19:52:13 +00:00
|
|
|
lastShift = Now();
|
2021-06-04 11:27:37 +00:00
|
|
|
// add send and connect timeouts to the parent endpoints path alignment timeout
|
|
|
|
// this will make it so that there is less of a chance for timing races
|
|
|
|
sendTimeout += parent->PathAlignmentTimeout();
|
|
|
|
connectTimeout += parent->PathAlignmentTimeout();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
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()
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (remoteIntro != m_NextIntro)
|
2019-06-26 13:09:40 +00:00
|
|
|
{
|
|
|
|
remoteIntro = m_NextIntro;
|
2021-04-14 15:07:06 +00:00
|
|
|
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
|
2019-06-26 13:09:40 +00:00
|
|
|
m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro);
|
2021-06-02 19:50:14 +00:00
|
|
|
ShiftIntroRouter(m_NextIntro.router);
|
2021-06-03 13:00:28 +00:00
|
|
|
// if we have not made a handshake to the remote endpoint do so
|
|
|
|
if (not IntroGenerated())
|
|
|
|
{
|
|
|
|
KeepAlive();
|
|
|
|
}
|
2019-06-26 13:09:40 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 12:21:15 +00:00
|
|
|
Address
|
|
|
|
OutboundContext::Addr() const
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
bool
|
2020-04-07 18:38:56 +00:00
|
|
|
OutboundContext::OnIntroSetUpdate(
|
2021-06-02 19:50:14 +00:00
|
|
|
const Address&,
|
|
|
|
std::optional<IntroSet> foundIntro,
|
|
|
|
const RouterID& endpoint,
|
|
|
|
llarp_time_t,
|
|
|
|
uint64_t relayOrder)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (markedBad)
|
2019-04-21 16:44:27 +00:00
|
|
|
return true;
|
|
|
|
updatingIntroSet = false;
|
2020-05-20 19:46:08 +00:00
|
|
|
if (foundIntro)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-04-14 15:07:06 +00:00
|
|
|
if (foundIntro->timestampSignedAt == 0s)
|
2020-02-06 19:35:31 +00:00
|
|
|
{
|
2020-05-20 19:46:08 +00:00
|
|
|
LogWarn(Name(), " got introset with zero timestamp: ", *foundIntro);
|
2020-02-06 19:35:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-04-14 15:07:06 +00:00
|
|
|
if (currentIntroSet.timestampSignedAt > foundIntro->timestampSignedAt)
|
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-04-07 18:38:56 +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-05-20 19:46:08 +00:00
|
|
|
currentIntroSet = *foundIntro;
|
2021-06-02 19:50:14 +00:00
|
|
|
ShiftIntroRouter(RouterID{});
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2021-06-02 19:50:14 +00:00
|
|
|
else if (relayOrder > 0)
|
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
|
|
|
|
{
|
2020-09-03 22:22:22 +00:00
|
|
|
if (markedBad)
|
|
|
|
return false;
|
2021-03-31 10:54:28 +00:00
|
|
|
if (remoteIntro.router.IsZero())
|
|
|
|
return false;
|
2021-06-17 09:05:55 +00:00
|
|
|
return IntroSent() and GetPathByRouter(remoteIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
2019-09-20 16:56:19 +00:00
|
|
|
void
|
|
|
|
OutboundContext::ShiftIntroRouter(const RouterID r)
|
|
|
|
{
|
|
|
|
const auto now = Now();
|
2021-06-17 09:06:23 +00:00
|
|
|
Introduction selectedIntro{};
|
2021-04-14 15:07:06 +00:00
|
|
|
for (const auto& intro : currentIntroSet.intros)
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
2021-06-17 09:06:23 +00:00
|
|
|
if (intro.expiresAt > selectedIntro.expiresAt and intro.router != r)
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
|
|
|
selectedIntro = intro;
|
|
|
|
}
|
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (selectedIntro.router.IsZero() || selectedIntro.ExpiresSoon(now))
|
2019-09-20 16:56:19 +00:00
|
|
|
return;
|
|
|
|
m_NextIntro = selectedIntro;
|
2021-06-02 19:52:13 +00:00
|
|
|
lastShift = now;
|
2019-09-20 16:56:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutboundContext::HandlePathBuildTimeout(path::Path_ptr p)
|
|
|
|
{
|
|
|
|
ShiftIntroRouter(p->Endpoint());
|
|
|
|
path::Builder::HandlePathBuildTimeout(p);
|
|
|
|
}
|
|
|
|
|
2020-11-04 16:08:29 +00:00
|
|
|
void
|
2021-03-31 10:54:28 +00:00
|
|
|
OutboundContext::HandlePathBuildFailedAt(path::Path_ptr p, RouterID hop)
|
2020-11-04 16:08:29 +00:00
|
|
|
{
|
2021-03-31 10:54:28 +00:00
|
|
|
if (p->Endpoint() == hop)
|
|
|
|
{
|
|
|
|
// shift intro when we fail at the pivot
|
|
|
|
ShiftIntroRouter(p->Endpoint());
|
|
|
|
}
|
|
|
|
path::Builder::HandlePathBuildFailedAt(p, hop);
|
2020-11-04 16:08:29 +00:00
|
|
|
}
|
|
|
|
|
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);
|
2021-06-25 09:08:31 +00:00
|
|
|
p->SetDataHandler([self = weak_from_this()](auto path, auto frame) {
|
|
|
|
if (auto ptr = self.lock())
|
|
|
|
return ptr->HandleHiddenServiceFrame(path, frame);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
p->SetDropHandler([self = weak_from_this()](auto path, auto id, auto seqno) {
|
|
|
|
if (auto ptr = self.lock())
|
|
|
|
return ptr->HandleDataDrop(path, id, seqno);
|
|
|
|
return false;
|
|
|
|
});
|
2021-06-23 11:19:34 +00:00
|
|
|
if (markedBad)
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
2021-06-23 11:19:34 +00:00
|
|
|
// ignore new path if we are marked dead
|
|
|
|
LogInfo(Name(), " marked bad, ignoring new path");
|
|
|
|
p->EnterState(path::ePathIgnore, Now());
|
|
|
|
}
|
|
|
|
else if (p->Endpoint() == m_NextIntro.router)
|
|
|
|
{
|
|
|
|
// we now have a path to the next intro, swap intros
|
|
|
|
SwapIntros();
|
2019-09-20 16:56:19 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-04-07 18:38:56 +00:00
|
|
|
OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
if (generatedIntro)
|
2021-06-03 12:56:35 +00:00
|
|
|
{
|
|
|
|
LogWarn(Name(), " dropping packet as we are not fully handshaked right now");
|
2021-03-27 18:54:09 +00:00
|
|
|
return;
|
2021-06-03 12:56:35 +00:00
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (remoteIntro.router.IsZero())
|
2021-03-31 10:54:28 +00:00
|
|
|
{
|
|
|
|
LogWarn(Name(), " dropping intro frame we have no intro ready yet");
|
|
|
|
return;
|
|
|
|
}
|
2019-05-07 14:11:10 +00:00
|
|
|
|
2021-06-02 19:52:13 +00:00
|
|
|
auto path = GetPathByRouter(remoteIntro.router);
|
2020-04-07 18:38:56 +00:00
|
|
|
if (path == nullptr)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
LogError(Name(), " has no path to ", remoteIntro.router, " when we should have had one");
|
|
|
|
return;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
auto frame = std::make_shared<ProtocolFrame>();
|
2021-06-02 19:52:13 +00:00
|
|
|
frame->Clear();
|
2020-04-07 18:38:56 +00:00
|
|
|
auto ex = std::make_shared<AsyncKeyExchange>(
|
2021-03-02 07:02:59 +00:00
|
|
|
m_Endpoint->Loop(),
|
2020-04-07 18:38:56 +00:00
|
|
|
remoteIdent,
|
|
|
|
m_Endpoint->GetIdentity(),
|
2021-04-14 15:07:06 +00:00
|
|
|
currentIntroSet.sntrupKey,
|
2020-04-07 18:38:56 +00:00
|
|
|
remoteIntro,
|
|
|
|
m_DataHandler,
|
|
|
|
currentConvoTag,
|
|
|
|
t);
|
|
|
|
|
2021-03-02 15:23:38 +00:00
|
|
|
ex->hook = [self = shared_from_this(), path](auto frame) {
|
2021-06-02 19:52:13 +00:00
|
|
|
if (not self->Send(std::move(frame), path))
|
|
|
|
return;
|
2021-06-03 14:32:36 +00:00
|
|
|
self->m_Endpoint->Loop()->call_later(
|
|
|
|
self->remoteIntro.latency, [self]() { self->sentIntro = true; });
|
2021-03-02 15:23:38 +00:00
|
|
|
};
|
2019-04-21 16:44:27 +00:00
|
|
|
|
|
|
|
ex->msg.PutBuffer(payload);
|
|
|
|
ex->msg.introReply = path->intro;
|
2020-04-07 18:38:56 +00:00
|
|
|
frame->F = ex->msg.introReply.pathID;
|
2021-03-27 18:54:09 +00:00
|
|
|
frame->R = 0;
|
2021-06-02 19:52:13 +00:00
|
|
|
generatedIntro = true;
|
|
|
|
// ensure we have a sender put for this convo tag
|
|
|
|
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
|
|
|
|
// encrypt frame async
|
2021-03-02 07:02:59 +00:00
|
|
|
m_Endpoint->Router()->QueueWork([ex, frame] { return AsyncKeyExchange::Encrypt(ex, frame); });
|
2021-06-02 19:52:13 +00:00
|
|
|
|
|
|
|
LogInfo(Name(), " send intro frame T=", currentConvoTag);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
OutboundContext::Name() const
|
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
return "OBContext:" + currentIntroSet.addressKeys.Addr().ToString();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-14 20:14:43 +00:00
|
|
|
OutboundContext::UpdateIntroSet()
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-05-01 12:52:41 +00:00
|
|
|
constexpr auto IntrosetUpdateInterval = 10s;
|
|
|
|
const auto now = Now();
|
|
|
|
if (updatingIntroSet or markedBad or now < m_LastIntrosetUpdateAt + IntrosetUpdateInterval)
|
2019-04-21 16:44:27 +00:00
|
|
|
return;
|
2021-05-01 12:52:41 +00:00
|
|
|
LogInfo(Name(), " updating introset");
|
|
|
|
m_LastIntrosetUpdateAt = now;
|
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
|
2021-06-02 19:52:13 +00:00
|
|
|
const auto paths = GetManyPathsWithUniqueEndpoints(m_Endpoint, 2, location);
|
2020-02-14 18:06:19 +00:00
|
|
|
uint64_t relayOrder = 0;
|
2020-04-07 18:38:56 +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-04-07 18:38:56 +00:00
|
|
|
location,
|
|
|
|
PubKey{addr.as_array()},
|
2021-05-01 12:59:56 +00:00
|
|
|
path->Endpoint(),
|
2020-04-07 18:38:56 +00:00
|
|
|
relayOrder,
|
2021-03-27 18:54:09 +00:00
|
|
|
m_Endpoint->GenTXID(),
|
2021-06-17 12:08:46 +00:00
|
|
|
(IntrosetUpdateInterval / 2) + (2 * path->intro.latency) + IntrosetLookupGraceInterval);
|
2020-02-13 22:19:12 +00:00
|
|
|
relayOrder++;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (job->SendRequestViaPath(path, m_Endpoint->Router()))
|
2020-02-10 18:46:42 +00:00
|
|
|
updatingIntroSet = true;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
util::StatusObject
|
|
|
|
OutboundContext::ExtractStatus() const
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
auto obj = path::Builder::ExtractStatus();
|
2021-03-31 16:06:50 +00:00
|
|
|
obj["estimatedRTT"] = to_json(estimatedRTT);
|
2020-04-07 18:38:56 +00:00
|
|
|
obj["currentConvoTag"] = currentConvoTag.ToHex();
|
|
|
|
obj["remoteIntro"] = remoteIntro.ExtractStatus();
|
|
|
|
obj["sessionCreatedAt"] = to_json(createdAt);
|
|
|
|
obj["lastGoodSend"] = to_json(lastGoodSend);
|
2021-06-02 19:14:21 +00:00
|
|
|
obj["lastRecv"] = to_json(m_LastInboundTraffic);
|
|
|
|
obj["lastIntrosetUpdate"] = to_json(m_LastIntrosetUpdateAt);
|
2020-04-07 18:38:56 +00:00
|
|
|
obj["seqno"] = sequenceNo;
|
|
|
|
obj["markedBad"] = markedBad;
|
|
|
|
obj["lastShift"] = to_json(lastShift);
|
2021-06-02 19:52:13 +00:00
|
|
|
obj["remoteIdentity"] = addr.ToString();
|
2019-08-19 09:33:26 +00:00
|
|
|
obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus();
|
2020-04-07 18:38:56 +00:00
|
|
|
obj["nextIntro"] = m_NextIntro.ExtractStatus();
|
2021-06-02 19:14:21 +00:00
|
|
|
obj["readyToSend"] = ReadyToSend();
|
2019-04-21 16:44:27 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2021-03-27 18:54:09 +00:00
|
|
|
void
|
|
|
|
OutboundContext::KeepAlive()
|
|
|
|
{
|
|
|
|
Encrypted<64> tmp;
|
|
|
|
tmp.Randomize();
|
|
|
|
SendPacketToRemote(tmp, ProtocolType::Control);
|
2021-10-12 21:37:01 +00:00
|
|
|
m_LastKeepAliveAt = Now();
|
2021-03-27 18:54:09 +00:00
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
bool
|
2019-04-23 16:13:22 +00:00
|
|
|
OutboundContext::Pump(llarp_time_t now)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-03-27 18:54:09 +00:00
|
|
|
if (ReadyToSend() and remoteIntro.router.IsZero())
|
|
|
|
{
|
|
|
|
SwapIntros();
|
|
|
|
}
|
2021-06-17 09:06:57 +00:00
|
|
|
if (ReadyToSend())
|
2021-06-14 18:50:22 +00:00
|
|
|
{
|
|
|
|
// if we dont have a cached session key after sending intro we are in a fugged state so
|
|
|
|
// expunge
|
|
|
|
SharedSecret discardme;
|
|
|
|
if (not m_DataHandler->GetCachedSessionKeyFor(currentConvoTag, discardme))
|
2021-10-12 21:37:01 +00:00
|
|
|
{
|
|
|
|
LogError(Name(), " no cached key after sending intro, we are in a fugged state, oh no");
|
2021-06-14 18:50:22 +00:00
|
|
|
return true;
|
2021-10-12 21:37:01 +00:00
|
|
|
}
|
2021-06-14 18:50:22 +00:00
|
|
|
}
|
2021-03-27 18:54:09 +00:00
|
|
|
|
2021-06-02 19:52:13 +00:00
|
|
|
if (m_GotInboundTraffic and m_LastInboundTraffic + sendTimeout <= now)
|
2020-05-21 14:18:23 +00:00
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
// timeout on other side
|
|
|
|
UpdateIntroSet();
|
|
|
|
MarkCurrentIntroBad(now);
|
|
|
|
ShiftIntroRouter(remoteIntro.router);
|
2020-05-21 14:18:23 +00:00
|
|
|
}
|
2021-06-02 19:52:13 +00:00
|
|
|
// check for stale intros
|
|
|
|
// update the introset if we think we need to
|
2021-06-16 13:52:32 +00:00
|
|
|
if (currentIntroSet.HasStaleIntros(now, path::intro_path_spread)
|
|
|
|
or remoteIntro.ExpiresSoon(now, path::intro_path_spread))
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-02-18 16:00:45 +00:00
|
|
|
UpdateIntroSet();
|
2021-06-16 01:16:10 +00:00
|
|
|
ShiftIntroduction(false);
|
|
|
|
}
|
|
|
|
|
2021-06-16 13:52:32 +00:00
|
|
|
if (ReadyToSend())
|
2021-06-16 01:16:10 +00:00
|
|
|
{
|
2021-06-16 13:52:32 +00:00
|
|
|
if (not remoteIntro.router.IsZero() and not GetPathByRouter(remoteIntro.router))
|
2021-06-16 01:16:10 +00:00
|
|
|
{
|
2021-06-17 09:07:29 +00:00
|
|
|
// pick another good intro if we have no path on our current intro
|
|
|
|
std::vector<Introduction> otherIntros;
|
|
|
|
ForEachPath([now, router = remoteIntro.router, &otherIntros](auto path) {
|
|
|
|
if (path and path->IsReady() and path->Endpoint() != router
|
|
|
|
and not path->ExpiresSoon(now, path::intro_path_spread))
|
2021-06-16 13:52:32 +00:00
|
|
|
{
|
2021-06-17 09:07:29 +00:00
|
|
|
otherIntros.emplace_back(path->intro);
|
2021-06-16 13:52:32 +00:00
|
|
|
}
|
|
|
|
});
|
2021-06-17 09:07:29 +00:00
|
|
|
if (not otherIntros.empty())
|
2021-06-16 01:16:10 +00:00
|
|
|
{
|
2021-06-17 09:07:29 +00:00
|
|
|
std::shuffle(otherIntros.begin(), otherIntros.end(), CSRNG{});
|
|
|
|
remoteIntro = otherIntros[0];
|
2021-06-16 01:16:10 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
// lookup router in intro if set and unknown
|
2021-06-02 19:52:13 +00:00
|
|
|
if (not m_NextIntro.router.IsZero())
|
|
|
|
m_Endpoint->EnsureRouterIsKnown(m_NextIntro.router);
|
2021-03-27 18:54:09 +00:00
|
|
|
|
2021-06-02 19:52:13 +00:00
|
|
|
if (ReadyToSend() and not m_ReadyHooks.empty())
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-03-31 16:06:50 +00:00
|
|
|
const auto path = GetPathByRouter(remoteIntro.router);
|
|
|
|
if (not path)
|
|
|
|
{
|
|
|
|
LogWarn(Name(), " ready but no path to ", remoteIntro.router, " ???");
|
2021-05-16 23:31:59 +00:00
|
|
|
return true;
|
2021-03-31 16:06:50 +00:00
|
|
|
}
|
2021-06-02 19:52:13 +00:00
|
|
|
for (const auto& hook : m_ReadyHooks)
|
|
|
|
hook(this);
|
|
|
|
m_ReadyHooks.clear();
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
2021-03-27 18:54:09 +00:00
|
|
|
|
2021-06-05 13:22:43 +00:00
|
|
|
const auto timeout = std::max(lastGoodSend, m_LastInboundTraffic);
|
2021-06-02 19:52:13 +00:00
|
|
|
if (lastGoodSend > 0s and now >= timeout + (sendTimeout / 2))
|
2021-05-16 09:59:39 +00:00
|
|
|
{
|
|
|
|
// send a keep alive to keep this session alive
|
|
|
|
KeepAlive();
|
2021-06-21 20:02:03 +00:00
|
|
|
if (markedBad)
|
2021-10-12 21:37:01 +00:00
|
|
|
{
|
|
|
|
LogWarn(Name(), " keepalive timeout hit");
|
2021-06-21 20:02:03 +00:00
|
|
|
return true;
|
2021-10-12 21:37:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for half open state where we can send but we get nothing back
|
|
|
|
if (m_LastInboundTraffic == 0s and now - createdAt > connectTimeout)
|
|
|
|
{
|
|
|
|
LogWarn(Name(), " half open state, we can send but we got nothing back");
|
|
|
|
return true;
|
2021-05-16 09:59:39 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
// if we are dead return true so we are removed
|
2021-10-12 21:37:01 +00:00
|
|
|
const bool removeIt = timeout > 0s ? (now >= timeout && now - timeout > sendTimeout)
|
|
|
|
: (now >= createdAt && now - createdAt > connectTimeout);
|
|
|
|
if (removeIt)
|
|
|
|
{
|
|
|
|
LogInfo(Name(), " session is stale");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 18:54:09 +00:00
|
|
|
void
|
2021-06-02 19:52:13 +00:00
|
|
|
OutboundContext::AddReadyHook(std::function<void(OutboundContext*)> hook, llarp_time_t timeout)
|
2021-03-27 18:54:09 +00:00
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
if (ReadyToSend())
|
|
|
|
{
|
|
|
|
hook(this);
|
2021-03-27 18:54:09 +00:00
|
|
|
return;
|
2021-06-02 19:52:13 +00:00
|
|
|
}
|
|
|
|
if (m_ReadyHooks.empty())
|
|
|
|
{
|
|
|
|
m_router->loop()->call_later(timeout, [this]() {
|
|
|
|
LogWarn(Name(), " did not obtain session in time");
|
|
|
|
for (const auto& hook : m_ReadyHooks)
|
|
|
|
hook(nullptr);
|
|
|
|
m_ReadyHooks.clear();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
m_ReadyHooks.push_back(hook);
|
2021-03-27 18:54:09 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 14:35:40 +00:00
|
|
|
std::optional<std::vector<RouterContact>>
|
|
|
|
OutboundContext::GetHopsForBuild()
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-02-02 14:35:40 +00:00
|
|
|
if (m_NextIntro.router.IsZero())
|
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
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (m_NextIntro.router.IsZero())
|
2021-02-02 14:35:40 +00:00
|
|
|
return std::nullopt;
|
2021-02-18 13:28:53 +00:00
|
|
|
return GetHopsAlignedToForBuild(m_NextIntro.router, m_Endpoint->SnodeBlacklist());
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::ShouldBuildMore(llarp_time_t now) const
|
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
if (markedBad or path::Builder::BuildCooldownHit(now))
|
2019-04-21 16:44:27 +00:00
|
|
|
return false;
|
2022-04-01 04:10:02 +00:00
|
|
|
|
|
|
|
if (NumInStatus(path::ePathBuilding) >= std::max(numDesiredPaths / size_t{2}, size_t{1}))
|
2020-02-17 20:44:23 +00:00
|
|
|
return false;
|
2021-06-02 19:52:13 +00:00
|
|
|
|
|
|
|
size_t numValidPaths = 0;
|
2021-06-16 00:53:40 +00:00
|
|
|
bool havePathToNextIntro = false;
|
|
|
|
ForEachPath([now, this, &havePathToNextIntro, &numValidPaths](path::Path_ptr path) {
|
2021-06-02 19:52:13 +00:00
|
|
|
if (not path->IsReady())
|
|
|
|
return;
|
2021-06-16 00:53:40 +00:00
|
|
|
if (not path->intro.ExpiresSoon(now, path::default_lifetime - path::intro_path_spread))
|
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
numValidPaths++;
|
2021-06-16 00:53:40 +00:00
|
|
|
if (path->intro.router == m_NextIntro.router)
|
|
|
|
havePathToNextIntro = true;
|
|
|
|
}
|
2019-06-28 15:19:12 +00:00
|
|
|
});
|
2021-06-16 00:53:40 +00:00
|
|
|
return numValidPaths < numDesiredPaths or not havePathToNextIntro;
|
2021-06-16 13:52:32 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2021-05-01 12:52:41 +00:00
|
|
|
void
|
2019-04-21 16:44:27 +00:00
|
|
|
OutboundContext::MarkCurrentIntroBad(llarp_time_t now)
|
2019-09-20 16:56:19 +00:00
|
|
|
{
|
2021-05-01 12:52:41 +00:00
|
|
|
MarkIntroBad(remoteIntro, now);
|
2019-09-20 16:56:19 +00:00
|
|
|
}
|
|
|
|
|
2021-05-01 12:52:41 +00:00
|
|
|
void
|
2021-06-16 13:52:32 +00:00
|
|
|
OutboundContext::MarkIntroBad(const Introduction&, llarp_time_t)
|
|
|
|
{}
|
2019-04-21 16:44:27 +00:00
|
|
|
|
2021-06-02 19:52:13 +00:00
|
|
|
bool
|
|
|
|
OutboundContext::IntroSent() const
|
|
|
|
{
|
|
|
|
return sentIntro;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OutboundContext::IntroGenerated() const
|
|
|
|
{
|
2021-06-08 21:27:50 +00:00
|
|
|
return generatedIntro;
|
2021-06-02 19:52:13 +00:00
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
bool
|
|
|
|
OutboundContext::ShiftIntroduction(bool rebuild)
|
|
|
|
{
|
|
|
|
bool success = false;
|
2021-06-02 19:52:13 +00:00
|
|
|
const auto now = Now();
|
|
|
|
if (abs(now - lastShift) < shiftTimeout)
|
2019-04-21 16:44:27 +00:00
|
|
|
return false;
|
2020-04-07 18:38:56 +00:00
|
|
|
bool shifted = false;
|
2021-04-14 15:07:06 +00:00
|
|
|
std::vector<Introduction> intros = currentIntroSet.intros;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (intros.size() > 1)
|
2020-01-02 22:08:45 +00:00
|
|
|
{
|
2021-03-18 21:36:53 +00:00
|
|
|
std::shuffle(intros.begin(), intros.end(), CSRNG{});
|
2020-01-02 22:08:45 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 12:31:34 +00:00
|
|
|
// to find a intro on the same router as before that is newer
|
2020-04-07 18:38:56 +00:00
|
|
|
for (const auto& intro : intros)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (intro.ExpiresSoon(now))
|
2019-04-21 16:44:27 +00:00
|
|
|
continue;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (m_Endpoint->SnodeBlacklist().count(intro.router))
|
2019-05-10 16:19:33 +00:00
|
|
|
continue;
|
2021-06-16 01:16:10 +00:00
|
|
|
if (remoteIntro.router == intro.router)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (intro.expiresAt > m_NextIntro.expiresAt)
|
2019-05-07 12:31:34 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
success = true;
|
2019-05-07 12:31:34 +00:00
|
|
|
m_NextIntro = intro;
|
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 18:38:56 +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-04-07 18:38:56 +00:00
|
|
|
for (const auto& intro : intros)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (m_Endpoint->SnodeBlacklist().count(intro.router))
|
2019-05-23 12:22:48 +00:00
|
|
|
continue;
|
|
|
|
m_Endpoint->EnsureRouterIsKnown(intro.router);
|
2020-04-07 18:38:56 +00:00
|
|
|
if (intro.ExpiresSoon(now))
|
2019-05-23 12:22:48 +00:00
|
|
|
continue;
|
2021-06-16 01:16:10 +00:00
|
|
|
if (m_NextIntro != intro)
|
2019-05-07 12:31:34 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (intro.expiresAt > m_NextIntro.expiresAt)
|
2019-05-23 12:22:48 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
shifted = intro.router != m_NextIntro.router;
|
2019-05-23 12:22:48 +00:00
|
|
|
m_NextIntro = intro;
|
2020-04-07 18:38:56 +00:00
|
|
|
success = true;
|
2019-05-23 12:22:48 +00:00
|
|
|
}
|
2019-05-07 12:31:34 +00:00
|
|
|
}
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (m_NextIntro.router.IsZero())
|
2019-05-07 12:31:34 +00:00
|
|
|
return false;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (shifted)
|
2019-04-21 16:44:27 +00:00
|
|
|
lastShift = now;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (rebuild && !BuildCooldownHit(Now()))
|
2019-05-23 12:22:48 +00:00
|
|
|
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();
|
2021-06-02 19:52:13 +00:00
|
|
|
const RouterID endpoint{path->Endpoint()};
|
2019-04-21 16:44:27 +00:00
|
|
|
// if a path to our current intro died...
|
2020-04-07 18:38:56 +00:00
|
|
|
if (endpoint == remoteIntro.router)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
|
|
|
// 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) {
|
2020-04-07 18:38:56 +00:00
|
|
|
if (p->Endpoint() == endpoint && p->IsReady())
|
2019-04-21 16:44:27 +00:00
|
|
|
++num;
|
|
|
|
});
|
2021-06-02 19:52:13 +00:00
|
|
|
if (num == 0)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2021-06-02 19:52:13 +00:00
|
|
|
// we have no more paths to this endpoint so we want to pivot off of it
|
|
|
|
MarkCurrentIntroBad(Now());
|
|
|
|
ShiftIntroRouter(endpoint);
|
|
|
|
if (m_NextIntro.router != endpoint)
|
|
|
|
BuildOneAlignedTo(m_NextIntro.router);
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-12 21:37:01 +00:00
|
|
|
bool
|
|
|
|
OutboundContext::ShouldKeepAlive(llarp_time_t now) const
|
|
|
|
{
|
|
|
|
const auto SendKeepAliveInterval = sendTimeout / 2;
|
|
|
|
if (not m_GotInboundTraffic)
|
|
|
|
return false;
|
|
|
|
if (m_LastInboundTraffic == 0s)
|
|
|
|
return false;
|
|
|
|
return (now - m_LastKeepAliveAt) >= SendKeepAliveInterval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutboundContext::Tick(llarp_time_t now)
|
|
|
|
{
|
|
|
|
path::Builder::Tick(now);
|
|
|
|
if (ShouldKeepAlive(now))
|
|
|
|
KeepAlive();
|
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
bool
|
2020-04-07 18:38:56 +00:00
|
|
|
OutboundContext::HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrame& frame)
|
2019-04-21 16:44:27 +00:00
|
|
|
{
|
2020-05-21 14:18:23 +00:00
|
|
|
m_LastInboundTraffic = m_Endpoint->Now();
|
|
|
|
m_GotInboundTraffic = true;
|
2021-01-01 18:55:31 +00:00
|
|
|
if (frame.R)
|
|
|
|
{
|
|
|
|
// handle discard
|
|
|
|
ServiceInfo si;
|
|
|
|
if (!m_Endpoint->GetSenderFor(frame.T, si))
|
2021-03-27 18:54:09 +00:00
|
|
|
{
|
|
|
|
LogWarn("no sender for T=", frame.T);
|
2021-01-01 18:55:31 +00:00
|
|
|
return false;
|
2021-03-27 18:54:09 +00:00
|
|
|
}
|
2021-01-01 18:55:31 +00:00
|
|
|
// verify source
|
|
|
|
if (!frame.Verify(si))
|
2021-03-27 18:54:09 +00:00
|
|
|
{
|
2021-05-05 12:32:07 +00:00
|
|
|
LogWarn("signature verification failed, T=", frame.T);
|
2021-01-01 18:55:31 +00:00
|
|
|
return false;
|
2021-03-27 18:54:09 +00:00
|
|
|
}
|
2021-01-01 18:55:31 +00:00
|
|
|
// remove convotag it doesn't exist
|
2021-03-27 18:54:09 +00:00
|
|
|
LogWarn("remove convotag T=", frame.T, " R=", frame.R);
|
2021-02-24 12:14:15 +00:00
|
|
|
|
2021-02-24 18:41:23 +00:00
|
|
|
AuthResult result{AuthResultCode::eAuthFailed, "unknown reason"};
|
|
|
|
if (const auto maybe = AuthResultCodeFromInt(frame.R))
|
|
|
|
result.code = *maybe;
|
2021-02-24 12:14:15 +00:00
|
|
|
SharedSecret sessionKey{};
|
|
|
|
if (m_DataHandler->GetCachedSessionKeyFor(frame.T, sessionKey))
|
|
|
|
{
|
|
|
|
ProtocolMessage msg{};
|
|
|
|
if (frame.DecryptPayloadInto(sessionKey, msg))
|
|
|
|
{
|
2021-03-08 20:48:11 +00:00
|
|
|
if (msg.proto == ProtocolType::Auth and not msg.payload.empty())
|
2021-02-24 12:14:15 +00:00
|
|
|
{
|
2021-03-05 17:31:52 +00:00
|
|
|
result.reason = std::string{
|
|
|
|
reinterpret_cast<const char*>(msg.payload.data()), msg.payload.size()};
|
2021-02-24 12:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 18:55:31 +00:00
|
|
|
m_Endpoint->RemoveConvoTag(frame.T);
|
|
|
|
if (authResultListener)
|
|
|
|
{
|
2021-02-24 12:14:15 +00:00
|
|
|
authResultListener(result);
|
2021-01-01 18:55:31 +00:00
|
|
|
authResultListener = nullptr;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
std::function<void(std::shared_ptr<ProtocolMessage>)> hook = nullptr;
|
|
|
|
if (authResultListener)
|
|
|
|
{
|
|
|
|
std::function<void(AuthResult)> handler = authResultListener;
|
|
|
|
authResultListener = nullptr;
|
2021-02-24 12:14:15 +00:00
|
|
|
hook = [handler](std::shared_ptr<ProtocolMessage> msg) {
|
|
|
|
AuthResult result{AuthResultCode::eAuthAccepted, "OK"};
|
2021-03-08 20:48:11 +00:00
|
|
|
if (msg->proto == ProtocolType::Auth and not msg->payload.empty())
|
2021-02-24 12:14:15 +00:00
|
|
|
{
|
2021-03-05 17:31:52 +00:00
|
|
|
result.reason = std::string{
|
|
|
|
reinterpret_cast<const char*>(msg->payload.data()), msg->payload.size()};
|
2021-02-24 12:14:15 +00:00
|
|
|
}
|
|
|
|
handler(result);
|
|
|
|
};
|
2021-01-01 18:55:31 +00:00
|
|
|
}
|
|
|
|
const auto& ident = m_Endpoint->GetIdentity();
|
2021-03-02 07:02:59 +00:00
|
|
|
if (not frame.AsyncDecryptAndVerify(m_Endpoint->Loop(), p, ident, m_Endpoint, hook))
|
2021-01-01 18:55:31 +00:00
|
|
|
{
|
|
|
|
// send reset convo tag message
|
2021-03-27 18:54:09 +00:00
|
|
|
LogError("failed to decrypt and verify frame");
|
2021-01-01 18:55:31 +00:00
|
|
|
ProtocolFrame f;
|
|
|
|
f.R = 1;
|
|
|
|
f.T = frame.T;
|
|
|
|
f.F = p->intro.pathID;
|
|
|
|
|
|
|
|
f.Sign(ident);
|
|
|
|
{
|
|
|
|
LogWarn("invalidating convotag T=", frame.T);
|
|
|
|
m_Endpoint->RemoveConvoTag(frame.T);
|
|
|
|
m_Endpoint->m_SendQueue.tryPushBack(
|
2021-03-31 16:06:50 +00:00
|
|
|
SendEvent_t{std::make_shared<routing::PathTransferMessage>(f, frame.F), p});
|
2021-01-01 18:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2019-04-21 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 14:18:23 +00:00
|
|
|
void
|
2021-03-16 11:56:27 +00:00
|
|
|
OutboundContext::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
|
2020-05-21 14:18:23 +00:00
|
|
|
{
|
2021-03-16 11:56:27 +00:00
|
|
|
AsyncEncryptAndSendTo(buf, t);
|
2020-05-21 14:18:23 +00:00
|
|
|
}
|
|
|
|
|
2019-04-21 16:44:27 +00:00
|
|
|
} // namespace service
|
|
|
|
|
|
|
|
} // namespace llarp
|