lokinet/llarp/service/endpoint.cpp

1801 lines
50 KiB
C++
Raw Normal View History

2018-12-12 02:15:08 +00:00
#include <buffer.hpp>
2018-12-12 00:48:54 +00:00
#include <dht/messages/findintro.hpp>
#include <logic.hpp>
#include <messages/dht.hpp>
2018-12-12 02:15:08 +00:00
#include <router.hpp>
#include <service/endpoint.hpp>
#include <service/protocol.hpp>
namespace llarp
{
namespace service
{
Endpoint::Endpoint(const std::string& name, llarp::Router* r)
: path::Builder(r, r->dht, 6, DEFAULT_HOP_LENGTH)
, m_Router(r)
, m_Name(name)
{
2018-07-18 22:50:05 +00:00
m_Tag.Zero();
}
bool
Endpoint::SetOption(const std::string& k, const std::string& v)
{
if(k == "keyfile")
{
m_Keyfile = v;
}
2018-07-18 03:10:21 +00:00
if(k == "tag")
{
m_Tag = v;
llarp::LogInfo("Setting tag to ", v);
2018-07-18 03:10:21 +00:00
}
if(k == "prefetch-tag")
{
m_PrefetchTags.insert(v);
}
if(k == "prefetch-addr")
{
Address addr;
if(addr.FromString(v))
m_PrefetchAddrs.insert(addr);
}
if(k == "min-latency")
{
auto val = atoi(v.c_str());
if(val > 0)
m_MinPathLatency = val;
}
2018-08-09 19:02:17 +00:00
return true;
}
bool
Endpoint::IsolateNetwork()
{
2018-11-19 11:56:40 +00:00
return false;
}
llarp_ev_loop*
Endpoint::EndpointNetLoop()
{
if(m_IsolatedNetLoop)
return m_IsolatedNetLoop;
else
return m_Router->netloop;
}
2018-08-16 14:34:15 +00:00
bool
Endpoint::NetworkIsIsolated() const
{
return m_IsolatedLogic && m_IsolatedWorker;
}
2018-08-09 19:02:17 +00:00
bool
2018-08-18 14:01:21 +00:00
Endpoint::SetupIsolatedNetwork(void* user, bool failed)
2018-08-09 19:02:17 +00:00
{
2018-08-20 19:12:12 +00:00
return static_cast< Endpoint* >(user)->DoNetworkIsolation(!failed);
2018-08-09 19:02:17 +00:00
}
2018-08-10 03:51:38 +00:00
bool
Endpoint::HasPendingPathToService(const Address& addr) const
{
return m_PendingServiceLookups.find(addr)
!= m_PendingServiceLookups.end();
}
void
Endpoint::RegenAndPublishIntroSet(llarp_time_t now, bool forceRebuild)
{
std::set< Introduction > I;
if(!GetCurrentIntroductionsWithFilter(
I, [now](const service::Introduction& intro) -> bool {
return now < intro.expiresAt
&& intro.expiresAt - now > (2 * 60 * 1000);
}))
{
llarp::LogWarn("could not publish descriptors for endpoint ", Name(),
" because we couldn't get enough valid introductions");
2018-10-29 16:48:36 +00:00
if(ShouldBuildMore(now) || forceRebuild)
2018-11-22 15:52:04 +00:00
ManualRebuild(1);
return;
}
m_IntroSet.I.clear();
for(const auto& intro : I)
{
m_IntroSet.I.push_back(intro);
}
if(m_IntroSet.I.size() == 0)
{
llarp::LogWarn("not enough intros to publish introset for ", Name());
return;
}
m_IntroSet.topic = m_Tag;
2018-10-29 16:48:36 +00:00
if(!m_Identity.SignIntroSet(m_IntroSet, &m_Router->crypto, now))
{
llarp::LogWarn("failed to sign introset for endpoint ", Name());
return;
}
if(PublishIntroSet(m_Router))
{
2018-09-17 15:32:37 +00:00
llarp::LogInfo("(re)publishing introset for endpoint ", Name());
}
else
{
llarp::LogWarn("failed to publish intro set for endpoint ", Name());
}
}
2018-12-12 18:37:03 +00:00
void
Endpoint::FlushSNodeTraffic()
{
auto itr = m_SNodeSessions.begin();
while(itr != m_SNodeSessions.end())
{
2018-12-20 12:41:17 +00:00
itr->second->Flush();
2018-12-12 18:37:03 +00:00
++itr;
}
}
void
2018-07-18 22:50:05 +00:00
Endpoint::Tick(llarp_time_t now)
{
2018-07-19 04:58:39 +00:00
// publish descriptors
2018-07-18 22:50:05 +00:00
if(ShouldPublishDescriptors(now))
{
RegenAndPublishIntroSet(now);
}
2018-12-13 12:27:14 +00:00
// expire snode sessions
{
auto itr = m_SNodeSessions.begin();
while(itr != m_SNodeSessions.end())
{
if(itr->second->IsExpired(now))
itr = m_SNodeSessions.erase(itr);
else
++itr;
}
}
// expire pending tx
{
2018-08-14 21:17:18 +00:00
std::set< service::IntroSet > empty;
auto itr = m_PendingLookups.begin();
while(itr != m_PendingLookups.end())
{
if(itr->second->IsTimedOut(now))
{
2018-08-14 21:17:18 +00:00
std::unique_ptr< IServiceLookup > lookup = std::move(itr->second);
llarp::LogInfo(lookup->name, " timed out txid=", lookup->txid);
lookup->HandleResponse(empty);
itr = m_PendingLookups.erase(itr);
}
else
++itr;
}
}
2018-08-14 21:17:18 +00:00
// expire pending router lookups
{
auto itr = m_PendingRouters.begin();
while(itr != m_PendingRouters.end())
{
if(itr->second.IsExpired(now))
2018-08-14 22:07:58 +00:00
{
llarp::LogInfo("lookup for ", itr->first, " timed out");
2018-08-14 21:17:18 +00:00
itr = m_PendingRouters.erase(itr);
2018-08-14 22:07:58 +00:00
}
2018-08-14 21:17:18 +00:00
else
++itr;
}
}
// prefetch addrs
for(const auto& addr : m_PrefetchAddrs)
{
if(!HasPathToService(addr))
{
2018-08-22 15:52:10 +00:00
if(!EnsurePathToService(
addr,
[](__attribute__((unused)) Address addr,
__attribute__((unused)) OutboundContext* ctx) {},
10000))
{
llarp::LogWarn("failed to ensure path to ", addr);
}
}
}
#ifdef TESTNET
2018-07-19 04:58:39 +00:00
// prefetch tags
2018-07-18 03:10:21 +00:00
for(const auto& tag : m_PrefetchTags)
{
auto itr = m_PrefetchedTags.find(tag);
if(itr == m_PrefetchedTags.end())
{
2018-11-08 15:15:02 +00:00
itr = m_PrefetchedTags
.insert(std::make_pair(tag, CachedTagResult(tag, this)))
.first;
2018-07-19 04:58:39 +00:00
}
for(const auto& introset : itr->second.result)
{
2018-08-10 03:51:38 +00:00
if(HasPendingPathToService(introset.A.Addr()))
continue;
byte_t tmp[1024] = {0};
auto buf = StackBuffer< decltype(tmp) >(tmp);
2018-12-27 18:27:43 +00:00
if(!SendToServiceOrQueue(introset.A.Addr().data().data(), buf,
eProtocolText))
{
llarp::LogWarn(Name(), " failed to send/queue data to ",
introset.A.Addr(), " for tag ", tag.ToString());
}
2018-07-18 03:10:21 +00:00
}
2018-07-18 22:50:05 +00:00
itr->second.Expire(now);
if(itr->second.ShouldRefresh(now))
2018-07-18 03:10:21 +00:00
{
auto path = PickRandomEstablishedPath();
if(path)
{
2018-08-14 21:17:18 +00:00
auto job = new TagLookupJob(this, &itr->second);
if(!job->SendRequestViaPath(path, Router()))
llarp::LogError(Name(), " failed to send tag lookup");
}
else
{
llarp::LogError(Name(), " has no paths for tag lookup");
2018-07-18 03:10:21 +00:00
}
}
}
#endif
// tick remote sessions
{
auto itr = m_RemoteSessions.begin();
while(itr != m_RemoteSessions.end())
{
if(itr->second->Tick(now))
{
itr->second->Stop();
m_DeadSessions.insert(
std::make_pair(itr->first, std::move(itr->second)));
itr = m_RemoteSessions.erase(itr);
}
else
++itr;
}
}
2018-09-24 15:52:25 +00:00
// deregister dead sessions
{
auto itr = m_DeadSessions.begin();
while(itr != m_DeadSessions.end())
{
if(itr->second->IsDone(now))
itr = m_DeadSessions.erase(itr);
else
++itr;
}
}
}
bool
Endpoint::OutboundContext::Stop()
{
markedBad = true;
return llarp::path::Builder::Stop();
}
bool
Endpoint::Stop()
{
// stop remote sessions
for(auto& item : m_RemoteSessions)
{
item.second->Stop();
}
// stop snode sessions
for(auto& item : m_SNodeSessions)
{
item.second->Stop();
}
return llarp::path::Builder::Stop();
}
2018-09-24 15:52:25 +00:00
bool
Endpoint::OutboundContext::IsDone(llarp_time_t now) const
{
return now - lastGoodSend > DEFAULT_PATH_LIFETIME && ShouldRemove();
2018-07-18 03:10:21 +00:00
}
uint64_t
Endpoint::GenTXID()
{
uint64_t txid = llarp::randint();
2018-07-18 03:10:21 +00:00
while(m_PendingLookups.find(txid) != m_PendingLookups.end())
++txid;
return txid;
}
2018-07-16 03:32:13 +00:00
std::string
Endpoint::Name() const
{
return m_Name + ":" + m_Identity.pub.Name();
}
bool
Endpoint::HasPathToService(const Address& addr) const
{
2018-11-03 20:17:28 +00:00
auto range = m_RemoteSessions.equal_range(addr);
Sessions::const_iterator itr = range.first;
while(itr != range.second)
{
2018-11-03 20:17:28 +00:00
if(itr->second->ReadyToSend())
return true;
++itr;
}
return false;
}
2018-08-04 02:59:32 +00:00
void
Endpoint::PutLookup(IServiceLookup* lookup, uint64_t txid)
{
// std::unique_ptr< service::IServiceLookup > ptr(lookup);
2018-08-17 10:39:11 +00:00
// m_PendingLookups.insert(std::make_pair(txid, ptr));
// m_PendingLookups[txid] = std::move(ptr);
2018-08-18 15:34:06 +00:00
m_PendingLookups.insert(
std::make_pair(txid, std::unique_ptr< IServiceLookup >(lookup)));
2018-08-04 02:59:32 +00:00
}
bool
Endpoint::HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg)
{
auto crypto = &m_Router->crypto;
2018-07-18 03:10:21 +00:00
std::set< IntroSet > remote;
for(const auto& introset : msg->I)
{
2018-10-29 16:48:36 +00:00
if(!introset.Verify(crypto, Now()))
{
2018-07-19 04:58:39 +00:00
if(m_Identity.pub == introset.A && m_CurrentPublishTX == msg->T)
{
IntroSetPublishFail();
}
else
{
auto itr = m_PendingLookups.find(msg->T);
if(itr == m_PendingLookups.end())
{
llarp::LogWarn(
"invalid lookup response for hidden service endpoint ",
Name(), " txid=", msg->T);
return true;
}
std::unique_ptr< IServiceLookup > lookup = std::move(itr->second);
m_PendingLookups.erase(itr);
lookup->HandleResponse({});
return true;
}
return true;
2018-07-18 22:50:05 +00:00
}
2018-07-19 04:58:39 +00:00
if(m_Identity.pub == introset.A && m_CurrentPublishTX == msg->T)
2018-07-18 22:50:05 +00:00
{
llarp::LogInfo(
"got introset publish confirmation for hidden service endpoint ",
2018-07-16 03:32:13 +00:00
Name());
2018-07-17 06:17:13 +00:00
IntroSetPublished();
2018-07-18 03:10:21 +00:00
return true;
}
else
{
2018-07-18 03:10:21 +00:00
remote.insert(introset);
}
}
2018-07-18 03:10:21 +00:00
auto itr = m_PendingLookups.find(msg->T);
if(itr == m_PendingLookups.end())
{
llarp::LogWarn("invalid lookup response for hidden service endpoint ",
Name(), " txid=", msg->T);
2018-07-20 04:50:28 +00:00
return true;
2018-07-18 03:10:21 +00:00
}
2018-08-14 21:17:18 +00:00
std::unique_ptr< IServiceLookup > lookup = std::move(itr->second);
2018-07-18 03:10:21 +00:00
m_PendingLookups.erase(itr);
2018-08-14 21:17:18 +00:00
lookup->HandleResponse(remote);
return true;
}
2018-08-09 19:02:17 +00:00
void
Endpoint::PutSenderFor(const ConvoTag& tag, const ServiceInfo& info)
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
{
itr = m_Sessions.insert(std::make_pair(tag, Session{})).first;
}
itr->second.remote = info;
2018-10-29 16:48:36 +00:00
itr->second.lastUsed = Now();
2018-08-09 19:02:17 +00:00
}
bool
Endpoint::GetSenderFor(const ConvoTag& tag, ServiceInfo& si) const
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
return false;
si = itr->second.remote;
return true;
}
void
Endpoint::PutIntroFor(const ConvoTag& tag, const Introduction& intro)
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
{
itr = m_Sessions.insert(std::make_pair(tag, Session{})).first;
}
itr->second.intro = intro;
2018-10-29 16:48:36 +00:00
itr->second.lastUsed = Now();
2018-08-09 19:02:17 +00:00
}
bool
Endpoint::GetIntroFor(const ConvoTag& tag, Introduction& intro) const
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
return false;
intro = itr->second.intro;
return true;
}
bool
Endpoint::GetConvoTagsForService(const ServiceInfo& info,
std::set< ConvoTag >& tags) const
{
bool inserted = false;
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end())
{
if(itr->second.remote == info)
{
inserted |= tags.insert(itr->first).second;
}
2018-09-17 11:45:35 +00:00
++itr;
2018-08-09 19:02:17 +00:00
}
return inserted;
}
bool
Endpoint::GetCachedSessionKeyFor(const ConvoTag& tag,
SharedSecret& secret) const
2018-08-09 19:02:17 +00:00
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
return false;
secret = itr->second.sharedKey;
2018-08-09 19:02:17 +00:00
return true;
}
void
Endpoint::PutCachedSessionKeyFor(const ConvoTag& tag, const SharedSecret& k)
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
{
itr = m_Sessions.insert(std::make_pair(tag, Session{})).first;
}
itr->second.sharedKey = k;
2018-10-29 16:48:36 +00:00
itr->second.lastUsed = Now();
2018-08-09 19:02:17 +00:00
}
bool
Endpoint::Start()
{
auto crypto = &m_Router->crypto;
if(m_Keyfile.size())
{
if(!m_Identity.EnsureKeys(m_Keyfile, crypto))
return false;
}
else
{
m_Identity.RegenerateKeys(crypto);
}
2018-08-09 19:02:17 +00:00
if(!m_DataHandler)
{
m_DataHandler = this;
}
2018-08-16 14:34:15 +00:00
// this does network isolation
2018-08-09 19:02:17 +00:00
while(m_OnInit.size())
{
if(m_OnInit.front()())
m_OnInit.pop_front();
else
return false;
}
return true;
}
Endpoint::~Endpoint()
{
}
2018-09-19 16:20:34 +00:00
2018-07-18 03:10:21 +00:00
bool
Endpoint::CachedTagResult::HandleResponse(
2018-07-18 22:50:05 +00:00
const std::set< IntroSet >& introsets)
2018-07-18 03:10:21 +00:00
{
2018-10-29 16:48:36 +00:00
auto now = parent->Now();
2018-07-19 04:58:39 +00:00
2018-07-18 22:50:05 +00:00
for(const auto& introset : introsets)
2018-07-19 04:58:39 +00:00
if(result.insert(introset).second)
lastModified = now;
llarp::LogInfo("Tag result for ", tag.ToString(), " got ",
introsets.size(), " results from lookup, have ",
result.size(), " cached last modified at ", lastModified,
" is ", now - lastModified, "ms old");
2018-07-18 03:10:21 +00:00
return true;
}
2018-07-18 22:50:05 +00:00
void
Endpoint::CachedTagResult::Expire(llarp_time_t now)
{
auto itr = result.begin();
while(itr != result.end())
{
if(itr->HasExpiredIntros(now))
{
2018-07-19 04:58:39 +00:00
llarp::LogInfo("Removing expired tag Entry ", itr->A.Name());
itr = result.erase(itr);
lastModified = now;
2018-07-18 22:50:05 +00:00
}
else
{
++itr;
}
}
}
2018-07-18 03:10:21 +00:00
llarp::routing::IMessage*
2018-08-14 21:17:18 +00:00
Endpoint::CachedTagResult::BuildRequestMessage(uint64_t txid)
2018-07-18 03:10:21 +00:00
{
llarp::routing::DHTMessage* msg = new llarp::routing::DHTMessage();
msg->M.emplace_back(new llarp::dht::FindIntroMessage(tag, txid));
2018-10-29 16:48:36 +00:00
lastRequest = parent->Now();
2018-07-18 03:10:21 +00:00
return msg;
}
bool
Endpoint::PublishIntroSet(llarp::Router* r)
2018-07-18 03:10:21 +00:00
{
// publish via near router
RouterID location = m_Identity.pub.Addr().as_array();
2018-09-24 14:31:58 +00:00
auto path = GetEstablishedPathClosestTo(location);
2018-10-23 17:15:22 +00:00
return path && PublishIntroSetVia(r, path);
2018-07-18 03:10:21 +00:00
}
2018-09-18 14:48:06 +00:00
struct PublishIntroSetJob : public IServiceLookup
{
IntroSet m_IntroSet;
Endpoint* m_Endpoint;
PublishIntroSetJob(Endpoint* parent, uint64_t id,
const IntroSet& introset)
: IServiceLookup(parent, id, "PublishIntroSet")
, m_IntroSet(introset)
, m_Endpoint(parent)
{
}
llarp::routing::IMessage*
BuildRequestMessage()
{
llarp::routing::DHTMessage* msg = new llarp::routing::DHTMessage();
msg->M.emplace_back(
2018-10-23 17:04:35 +00:00
new llarp::dht::PublishIntroMessage(m_IntroSet, txid, 1));
2018-09-18 14:48:06 +00:00
return msg;
}
bool
HandleResponse(const std::set< IntroSet >& response)
{
if(response.size())
m_Endpoint->IntroSetPublished();
else
m_Endpoint->IntroSetPublishFail();
return true;
}
};
2018-07-18 03:10:21 +00:00
void
Endpoint::IntroSetPublishFail()
{
2018-09-18 14:48:06 +00:00
// TODO: linear backoff
}
bool
Endpoint::PublishIntroSetVia(llarp::Router* r, path::Path* path)
2018-09-18 14:48:06 +00:00
{
auto job = new PublishIntroSetJob(this, GenTXID(), m_IntroSet);
if(job->SendRequestViaPath(path, r))
{
2018-10-29 16:48:36 +00:00
m_LastPublishAttempt = Now();
2018-09-18 14:48:06 +00:00
return true;
}
return false;
2018-07-18 03:10:21 +00:00
}
bool
2018-07-18 22:50:05 +00:00
Endpoint::ShouldPublishDescriptors(llarp_time_t now) const
2018-07-18 03:10:21 +00:00
{
2018-10-06 16:04:46 +00:00
if(NumInStatus(llarp::path::ePathEstablished) < 3)
return false;
2018-07-18 22:50:05 +00:00
if(m_IntroSet.HasExpiredIntros(now))
return now - m_LastPublishAttempt >= INTROSET_PUBLISH_RETRY_INTERVAL;
2018-09-18 14:48:06 +00:00
return now - m_LastPublishAttempt >= INTROSET_PUBLISH_INTERVAL;
2018-07-18 03:10:21 +00:00
}
void
Endpoint::IntroSetPublished()
{
2018-10-29 16:48:36 +00:00
m_LastPublish = Now();
2018-07-18 03:10:21 +00:00
llarp::LogInfo(Name(), " IntroSet publish confirmed");
}
struct HiddenServiceAddressLookup : public IServiceLookup
{
2018-08-14 21:17:18 +00:00
~HiddenServiceAddressLookup()
{
}
2018-07-22 23:14:29 +00:00
Address remote;
2018-10-15 15:43:41 +00:00
typedef std::function< bool(const Address&, const IntroSet*,
const RouterID&) >
HandlerFunc;
2018-08-10 21:34:11 +00:00
HandlerFunc handle;
2018-08-04 02:59:32 +00:00
2018-08-10 21:34:11 +00:00
HiddenServiceAddressLookup(Endpoint* p, HandlerFunc h,
const Address& addr, uint64_t tx)
2018-08-14 21:17:18 +00:00
: IServiceLookup(p, tx, "HSLookup"), remote(addr), handle(h)
{
}
bool
HandleResponse(const std::set< IntroSet >& results)
{
2018-08-10 03:51:38 +00:00
llarp::LogInfo("found ", results.size(), " for ", remote.ToString());
if(results.size() > 0)
{
2018-10-15 15:43:41 +00:00
return handle(remote, &*results.begin(), endpoint);
}
2018-10-15 15:43:41 +00:00
return handle(remote, nullptr, endpoint);
}
2018-07-22 23:14:29 +00:00
llarp::routing::IMessage*
BuildRequestMessage()
{
llarp::routing::DHTMessage* msg = new llarp::routing::DHTMessage();
2018-11-08 15:15:02 +00:00
msg->M.emplace_back(new llarp::dht::FindIntroMessage(txid, remote, 0));
2018-07-22 23:14:29 +00:00
return msg;
}
};
2018-08-09 19:02:17 +00:00
bool
2018-08-18 14:01:21 +00:00
Endpoint::DoNetworkIsolation(bool failed)
2018-08-09 19:02:17 +00:00
{
2018-08-18 14:01:21 +00:00
if(failed)
return IsolationFailed();
llarp_ev_loop_alloc(&m_IsolatedNetLoop);
return SetupNetworking();
}
void
Endpoint::RunIsolatedMainLoop(void* user)
{
Endpoint* self = static_cast< Endpoint* >(user);
llarp_ev_loop_run_single_process(self->m_IsolatedNetLoop,
self->m_IsolatedWorker,
self->m_IsolatedLogic);
2018-08-09 19:02:17 +00:00
}
2018-07-22 23:14:29 +00:00
void
Endpoint::PutNewOutboundContext(const llarp::service::IntroSet& introset)
{
Address addr;
introset.A.CalculateAddress(addr.as_array());
2018-07-22 23:14:29 +00:00
if(m_RemoteSessions.count(addr) >= MAX_OUTBOUND_CONTEXT_COUNT)
2018-07-22 23:14:29 +00:00
{
auto itr = m_RemoteSessions.find(addr);
auto i = m_PendingServiceLookups.find(addr);
if(i != m_PendingServiceLookups.end())
{
auto f = i->second;
m_PendingServiceLookups.erase(i);
f(addr, itr->second.get());
}
return;
2018-07-22 23:14:29 +00:00
}
OutboundContext* ctx = new OutboundContext(introset, this);
m_RemoteSessions.insert(
std::make_pair(addr, std::unique_ptr< OutboundContext >(ctx)));
llarp::LogInfo("Created New outbound context for ", addr.ToString());
2018-07-22 23:14:29 +00:00
// inform pending
auto itr = m_PendingServiceLookups.find(addr);
if(itr != m_PendingServiceLookups.end())
{
2018-08-14 21:17:18 +00:00
auto f = itr->second;
2018-07-22 23:14:29 +00:00
m_PendingServiceLookups.erase(itr);
f(addr, ctx);
2018-07-22 23:14:29 +00:00
}
}
2018-08-10 21:34:11 +00:00
bool
Endpoint::HandleGotRouterMessage(const llarp::dht::GotRouterMessage* msg)
{
bool success = false;
if(msg->R.size() == 1)
{
auto itr = m_PendingRouters.find(msg->R[0].pubkey);
if(itr == m_PendingRouters.end())
return false;
llarp_async_verify_rc* job = new llarp_async_verify_rc;
job->nodedb = m_Router->nodedb;
job->cryptoworker = m_Router->tp;
job->diskworker = m_Router->disk;
2018-12-19 17:48:29 +00:00
job->logic = m_Router->logic;
2018-08-10 21:34:11 +00:00
job->hook = nullptr;
2018-08-30 18:48:43 +00:00
job->rc = msg->R[0];
2018-08-10 21:34:11 +00:00
llarp_nodedb_async_verify(job);
return true;
}
return success;
}
void
Endpoint::EnsureRouterIsKnown(const RouterID& router)
{
2018-08-14 22:07:58 +00:00
if(router.IsZero())
return;
2018-08-30 18:48:43 +00:00
RouterContact rc;
if(!m_Router->nodedb->Get(router, rc))
2018-08-10 21:34:11 +00:00
{
2018-12-19 17:48:29 +00:00
LookupRouterAnon(router);
}
}
2018-08-10 21:34:11 +00:00
2018-12-19 17:48:29 +00:00
bool
Endpoint::LookupRouterAnon(RouterID router)
{
if(m_PendingRouters.find(router) == m_PendingRouters.end())
{
auto path = GetEstablishedPathClosestTo(router);
routing::DHTMessage msg;
auto txid = GenTXID();
msg.M.emplace_back(new dht::FindRouterMessage(txid, router));
if(path && path->SendRoutingMessage(&msg, m_Router))
{
llarp::LogInfo(Name(), " looking up ", router);
m_PendingRouters.insert(
std::make_pair(router, RouterLookupJob(this)));
return true;
2018-08-10 21:34:11 +00:00
}
2018-12-19 17:48:29 +00:00
else
llarp::LogError("failed to send request for router lookup");
2018-08-10 21:34:11 +00:00
}
2018-12-19 17:48:29 +00:00
return false;
2018-08-10 21:34:11 +00:00
}
void
Endpoint::HandlePathBuilt(path::Path* p)
{
p->SetDataHandler(std::bind(&Endpoint::HandleHiddenServiceFrame, this,
std::placeholders::_1,
std::placeholders::_2));
p->SetDropHandler(std::bind(&Endpoint::HandleDataDrop, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
p->SetDeadChecker(std::bind(&Endpoint::CheckPathIsDead, this,
std::placeholders::_1,
std::placeholders::_2));
path::Builder::HandlePathBuilt(p);
}
bool
Endpoint::HandleDataDrop(path::Path* p, const PathID_t& dst, uint64_t seq)
{
llarp::LogWarn(Name(), " message ", seq, " dropped by endpoint ",
p->Endpoint(), " via ", dst);
return true;
}
bool
Endpoint::OutboundContext::HandleDataDrop(path::Path* p,
const PathID_t& dst, uint64_t seq)
{
// pick another intro
2018-09-19 16:20:34 +00:00
if(dst == remoteIntro.pathID && remoteIntro.router == p->Endpoint())
{
llarp::LogWarn(Name(), " message ", seq, " dropped by endpoint ",
p->Endpoint(), " via ", dst);
2018-10-29 16:48:36 +00:00
if(MarkCurrentIntroBad(Now()))
2018-10-04 18:15:22 +00:00
{
llarp::LogInfo(Name(), " switched intros to ", remoteIntro.router,
" via ", remoteIntro.pathID);
2018-10-04 18:15:22 +00:00
}
UpdateIntroSet(true);
}
return true;
}
bool
Endpoint::HandleDataMessage(__attribute__((unused)) const PathID_t& src,
ProtocolMessage* msg)
{
msg->sender.UpdateAddr();
2018-09-18 22:54:04 +00:00
PutIntroFor(msg->tag, msg->introReply);
EnsureReplyPath(msg->sender);
2018-09-18 17:48:26 +00:00
return ProcessDataMessage(msg);
}
2018-11-29 14:01:13 +00:00
bool
Endpoint::HasPathToSNode(const llarp::RouterID& ident) const
2018-11-29 14:01:13 +00:00
{
auto range = m_SNodeSessions.equal_range(ident);
auto itr = range.first;
2018-11-29 14:01:13 +00:00
while(itr != range.second)
{
2018-11-29 14:01:13 +00:00
if(itr->second->IsReady())
{
return true;
}
++itr;
}
return false;
}
2018-11-29 13:12:35 +00:00
bool
Endpoint::ProcessDataMessage(ProtocolMessage* msg)
2018-11-29 13:12:35 +00:00
{
if(msg->proto == eProtocolTraffic)
{
auto buf = llarp::Buffer(msg->payload);
return HandleWriteIPPacket(buf,
std::bind(&Endpoint::ObtainIPForAddr, this,
msg->sender.Addr(), false));
2018-11-29 13:12:35 +00:00
}
else if(msg->proto == eProtocolText)
2018-11-29 13:12:35 +00:00
{
// TODO: implement me (?)
return true;
}
return false;
}
bool
Endpoint::HandleHiddenServiceFrame(path::Path* p,
const ProtocolFrame* frame)
{
return frame->AsyncDecryptAndVerify(EndpointLogic(), Crypto(), p->RXID(),
Worker(), m_Identity, m_DataHandler);
}
Endpoint::SendContext::SendContext(const ServiceInfo& ident,
const Introduction& intro, PathSet* send,
Endpoint* ep)
: remoteIdent(ident)
, remoteIntro(intro)
, m_PathSet(send)
, m_DataHandler(ep)
, m_Endpoint(ep)
{
2018-10-29 16:48:36 +00:00
createdAt = ep->Now();
}
void
Endpoint::OutboundContext::HandlePathBuilt(path::Path* p)
{
path::Builder::HandlePathBuilt(p);
/// don't use it if we are marked bad
if(markedBad)
return;
p->SetDataHandler(
std::bind(&Endpoint::OutboundContext::HandleHiddenServiceFrame, this,
std::placeholders::_1, std::placeholders::_2));
p->SetDropHandler(std::bind(
&Endpoint::OutboundContext::HandleDataDrop, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
p->SetDeadChecker(std::bind(&Endpoint::CheckPathIsDead, m_Endpoint,
std::placeholders::_1,
std::placeholders::_2));
}
2018-09-17 15:32:37 +00:00
void
Endpoint::HandlePathDead(void* user)
{
Endpoint* self = static_cast< Endpoint* >(user);
2018-10-29 16:48:36 +00:00
self->RegenAndPublishIntroSet(self->Now(), true);
2018-09-17 15:32:37 +00:00
}
bool
Endpoint::CheckPathIsDead(__attribute__((unused)) path::Path* p,
llarp_time_t latency)
{
2018-09-17 15:32:37 +00:00
if(latency >= m_MinPathLatency)
{
}
return false;
}
bool
Endpoint::OutboundContext::HandleHiddenServiceFrame(
path::Path* p, const ProtocolFrame* frame)
{
return m_Endpoint->HandleHiddenServiceFrame(p, frame);
}
2018-08-10 21:34:11 +00:00
bool
2018-10-15 15:43:41 +00:00
Endpoint::OnLookup(const Address& addr, const IntroSet* introset,
const RouterID& endpoint)
2018-08-10 21:34:11 +00:00
{
2018-10-29 16:48:36 +00:00
auto now = Now();
2018-10-10 21:31:03 +00:00
if(introset == nullptr || introset->IsExpired(now))
{
2018-10-15 15:43:41 +00:00
llarp::LogError(Name(), " failed to lookup ", addr.ToString(), " from ",
endpoint);
2018-10-23 17:04:35 +00:00
m_ServiceLookupFails[endpoint] = m_ServiceLookupFails[endpoint] + 1;
auto itr = m_PendingServiceLookups.find(addr);
if(itr != m_PendingServiceLookups.end())
{
2018-10-09 12:10:20 +00:00
auto func = itr->second;
2018-09-10 19:31:29 +00:00
m_PendingServiceLookups.erase(itr);
2018-10-09 12:10:20 +00:00
func(addr, nullptr);
}
2018-08-10 21:34:11 +00:00
return false;
}
2018-08-10 21:34:11 +00:00
PutNewOutboundContext(*introset);
return true;
}
2018-07-19 04:58:39 +00:00
bool
Endpoint::EnsurePathToService(const Address& remote, PathEnsureHook hook,
__attribute__((unused))
llarp_time_t timeoutMS,
bool randomPath)
2018-07-19 04:58:39 +00:00
{
2018-10-16 11:14:41 +00:00
path::Path* path = nullptr;
if(randomPath)
path = PickRandomEstablishedPath();
else
path = GetEstablishedPathClosestTo(remote.ToRouter());
if(!path)
{
llarp::LogWarn("No outbound path for lookup yet");
2018-12-02 15:26:26 +00:00
BuildOne();
return false;
}
llarp::LogInfo(Name(), " Ensure Path to ", remote.ToString());
2018-07-22 23:14:29 +00:00
{
auto itr = m_RemoteSessions.find(remote);
if(itr != m_RemoteSessions.end())
{
2018-08-22 15:52:10 +00:00
hook(itr->first, itr->second.get());
2018-07-22 23:14:29 +00:00
return true;
}
}
{
auto itr = m_PendingServiceLookups.find(remote);
if(itr != m_PendingServiceLookups.end())
{
// duplicate
llarp::LogWarn("duplicate pending service lookup to ",
remote.ToString());
return false;
}
2018-07-22 23:14:29 +00:00
}
2018-10-23 12:40:34 +00:00
2018-07-22 23:14:29 +00:00
m_PendingServiceLookups.insert(std::make_pair(remote, hook));
{
RouterID endpoint = path->Endpoint();
2018-10-16 11:14:41 +00:00
auto itr = m_ServiceLookupFails.find(endpoint);
if(itr != m_ServiceLookupFails.end())
{
2018-10-23 17:04:35 +00:00
path = PickRandomEstablishedPath();
}
}
2018-10-16 11:14:41 +00:00
if(!path)
{
2018-10-16 15:57:17 +00:00
path = PickRandomEstablishedPath();
if(!path)
{
2018-10-19 11:34:27 +00:00
llarp::LogError(Name(), "no working paths for lookup");
2018-10-16 15:57:17 +00:00
hook(remote, nullptr);
return false;
}
2018-10-16 11:14:41 +00:00
}
2018-08-10 21:34:11 +00:00
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
this,
2018-10-15 15:43:41 +00:00
std::bind(&Endpoint::OnLookup, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3),
2018-08-10 21:34:11 +00:00
remote, GenTXID());
2018-08-10 21:34:11 +00:00
if(job->SendRequestViaPath(path, Router()))
return true;
llarp::LogError("send via path failed");
return false;
2018-07-19 04:58:39 +00:00
}
2018-10-04 13:03:48 +00:00
Endpoint::OutboundContext::OutboundContext(const IntroSet& introset,
Endpoint* parent)
: path::Builder(parent->m_Router, parent->m_Router->dht, 3,
DEFAULT_HOP_LENGTH)
2018-10-04 13:03:48 +00:00
, SendContext(introset.A, {}, this, parent)
, currentIntroSet(introset)
2018-07-12 18:21:44 +00:00
{
2018-09-12 13:29:42 +00:00
updatingIntroSet = false;
2018-10-04 13:03:48 +00:00
for(const auto intro : introset.I)
{
if(intro.expiresAt > m_NextIntro.expiresAt)
{
m_NextIntro = intro;
remoteIntro = intro;
}
}
2018-07-12 18:21:44 +00:00
}
Endpoint::OutboundContext::~OutboundContext()
{
}
2018-10-23 17:04:35 +00:00
/// actually swap intros
2018-10-04 13:03:48 +00:00
void
Endpoint::OutboundContext::SwapIntros()
{
remoteIntro = m_NextIntro;
2018-10-04 16:48:26 +00:00
// prepare next intro
2018-10-29 16:48:36 +00:00
auto now = Now();
2018-10-04 16:48:26 +00:00
for(const auto& intro : currentIntroSet.I)
{
if(intro.ExpiresSoon(now))
continue;
if(m_BadIntros.find(intro) == m_BadIntros.end()
&& remoteIntro.router == intro.router)
{
m_NextIntro = intro;
return;
}
}
for(const auto& intro : currentIntroSet.I)
{
if(intro.ExpiresSoon(now))
continue;
if(m_BadIntros.find(intro) == m_BadIntros.end())
{
m_NextIntro = intro;
return;
}
}
2018-10-04 13:03:48 +00:00
}
2018-08-10 21:34:11 +00:00
bool
Endpoint::OutboundContext::OnIntroSetUpdate(__attribute__((unused))
const Address& addr,
2018-10-15 15:43:41 +00:00
const IntroSet* i,
const RouterID& endpoint)
2018-07-22 23:14:29 +00:00
{
if(markedBad)
return true;
updatingIntroSet = false;
2018-10-01 17:16:15 +00:00
if(i)
2018-07-22 23:14:29 +00:00
{
2018-10-01 17:16:15 +00:00
if(currentIntroSet.T >= i->T)
{
llarp::LogInfo("introset is old, dropping");
return true;
}
2018-10-29 16:48:36 +00:00
auto now = Now();
2018-10-10 21:31:03 +00:00
if(i->IsExpired(now))
{
2018-10-15 15:43:41 +00:00
llarp::LogError("got expired introset from lookup from ", endpoint);
2018-10-10 21:31:03 +00:00
return true;
}
2018-09-17 16:12:42 +00:00
currentIntroSet = *i;
2018-10-01 17:16:15 +00:00
if(!ShiftIntroduction())
{
llarp::LogWarn("failed to pick new intro during introset update");
}
2018-10-04 13:03:48 +00:00
if(GetPathByRouter(m_NextIntro.router) == nullptr)
BuildOneAlignedTo(m_NextIntro.router);
else
SwapIntros();
2018-07-22 23:14:29 +00:00
}
2018-08-10 21:34:11 +00:00
return true;
2018-07-22 23:14:29 +00:00
}
bool
Endpoint::OutboundContext::ReadyToSend() const
{
2018-10-04 13:03:48 +00:00
return (!remoteIntro.router.IsZero())
&& GetPathByRouter(remoteIntro.router) != nullptr;
}
2018-11-29 13:12:35 +00:00
void
Endpoint::EnsurePathToSNode(const RouterID& snode)
2018-11-29 13:12:35 +00:00
{
2018-12-12 18:37:03 +00:00
if(m_SNodeSessions.count(snode) == 0)
2018-11-29 13:12:35 +00:00
{
auto themIP = ObtainIPForAddr(snode, true);
m_SNodeSessions.emplace(std::make_pair(
snode,
std::unique_ptr< llarp::exit::BaseSession >(
new llarp::exit::SNodeSession(
snode,
std::bind(&Endpoint::HandleWriteIPPacket, this,
std::placeholders::_1,
[themIP]() -> huint32_t { return themIP; }),
m_Router, 2, numHops))));
2018-11-29 13:12:35 +00:00
}
}
bool
Endpoint::SendToSNodeOrQueue(const RouterID& addr, llarp_buffer_t buf)
2018-11-29 13:12:35 +00:00
{
llarp::net::IPv4Packet pkt;
if(!pkt.Load(buf))
return false;
auto range = m_SNodeSessions.equal_range(addr);
auto itr = range.first;
2018-11-29 13:12:35 +00:00
while(itr != range.second)
{
if(itr->second->IsReady())
{
if(itr->second->QueueUpstreamTraffic(pkt,
llarp::routing::ExitPadSize))
2018-11-29 13:12:35 +00:00
{
return true;
}
}
++itr;
}
return false;
}
2018-08-22 15:52:10 +00:00
bool
Endpoint::SendToServiceOrQueue(const RouterID& addr, llarp_buffer_t data,
ProtocolType t)
2018-08-22 15:52:10 +00:00
{
service::Address remote(addr.as_array());
2018-11-14 12:23:08 +00:00
// inbound converstation
2018-10-29 16:48:36 +00:00
auto now = Now();
2018-11-14 12:23:08 +00:00
{
auto itr = m_AddressToService.find(remote);
if(itr != m_AddressToService.end())
{
routing::PathTransferMessage transfer;
ProtocolFrame& f = transfer.T;
path::Path* p = nullptr;
std::set< ConvoTag > tags;
if(!GetConvoTagsForService(itr->second, tags))
{
llarp::LogError("no convo tag");
return false;
}
2018-09-18 17:48:26 +00:00
Introduction remoteIntro;
SharedSecret K;
for(const auto& tag : tags)
{
if(tag.IsZero())
continue;
2018-09-18 17:48:26 +00:00
if(p == nullptr && GetIntroFor(tag, remoteIntro))
{
2018-09-19 16:20:34 +00:00
if(!remoteIntro.ExpiresSoon(now))
p = GetPathByRouter(remoteIntro.router);
if(p)
{
f.T = tag;
if(!GetCachedSessionKeyFor(tag, K))
{
llarp::LogError("no cached session key");
return false;
}
}
}
}
if(p)
{
// TODO: check expiration of our end
ProtocolMessage m(f.T);
m.proto = t;
m.introReply = p->intro;
m.sender = m_Identity.pub;
m.PutBuffer(data);
f.N.Randomize();
f.S = GetSeqNoForConvo(f.T);
f.C.Zero();
transfer.Y.Randomize();
transfer.P = remoteIntro.pathID;
if(!f.EncryptAndSign(&Router()->crypto, m, K, m_Identity))
{
llarp::LogError("failed to encrypt and sign");
return false;
}
llarp::LogDebug(Name(), " send ", data.sz, " via ",
remoteIntro.router);
return p->SendRoutingMessage(&transfer, Router());
}
}
}
// outbound converstation
2018-08-22 15:52:10 +00:00
if(HasPathToService(remote))
{
auto range = m_RemoteSessions.equal_range(remote);
auto itr = range.first;
while(itr != range.second)
{
if(itr->second->ReadyToSend())
{
itr->second->AsyncEncryptAndSendTo(data, t);
return true;
}
++itr;
}
2018-08-22 15:52:10 +00:00
}
// no converstation
2018-11-22 15:53:11 +00:00
return EnsurePathToService(remote,
[](Address, OutboundContext* c) {
if(c)
c->UpdateIntroSet(true);
},
5000, false);
}
2018-08-22 15:52:10 +00:00
bool
Endpoint::OutboundContext::BuildOneAlignedTo(const RouterID& remote)
{
llarp::LogInfo(Name(), " building path to ", remote);
auto nodedb = m_Endpoint->Router()->nodedb;
std::vector< RouterContact > hops;
hops.resize(numHops);
for(size_t hop = 0; hop < numHops; ++hop)
{
if(hop == 0)
{
// first hop
if(router->NumberOfConnectedRouters())
{
if(!router->GetRandomConnectedRouter(hops[0]))
return false;
}
2018-11-29 14:18:53 +00:00
else
return false;
}
else if(hop == numHops - 1)
{
// last hop
if(!nodedb->Get(remote, hops[hop]))
return false;
}
// middle hop
2018-10-04 17:51:45 +00:00
else
{
size_t tries = 5;
do
{
nodedb->select_random_hop(hops[hop - 1], hops[hop], hop);
2018-10-04 17:51:45 +00:00
--tries;
} while(m_Endpoint->Router()->routerProfiling.IsBad(hops[hop].pubkey)
&& tries > 0);
return tries > 0;
}
return false;
}
2018-11-22 15:52:04 +00:00
Build(hops);
return true;
}
bool
Endpoint::OutboundContext::MarkCurrentIntroBad(llarp_time_t now)
{
// insert bad intro
2018-10-01 15:45:55 +00:00
m_BadIntros[remoteIntro] = now;
// unconditional shift
bool shiftedRouter = false;
bool shiftedIntro = false;
// try same router
for(const auto& intro : currentIntroSet.I)
{
if(intro.ExpiresSoon(now))
continue;
auto itr = m_BadIntros.find(intro);
2018-10-04 13:03:48 +00:00
if(itr == m_BadIntros.end() && intro.router == m_NextIntro.router)
2018-10-01 15:45:55 +00:00
{
shiftedIntro = true;
2018-10-04 13:03:48 +00:00
m_NextIntro = intro;
2018-10-01 15:45:55 +00:00
break;
}
}
if(!shiftedIntro)
2018-10-01 15:45:55 +00:00
{
// try any router
for(const auto& intro : currentIntroSet.I)
2018-10-01 15:45:55 +00:00
{
if(intro.ExpiresSoon(now))
continue;
auto itr = m_BadIntros.find(intro);
if(itr == m_BadIntros.end())
{
// TODO: this should always be true but idk if it really is
2018-10-04 13:03:48 +00:00
shiftedRouter = m_NextIntro.router != intro.router;
shiftedIntro = true;
2018-10-04 13:03:48 +00:00
m_NextIntro = intro;
break;
}
2018-10-01 15:45:55 +00:00
}
}
if(shiftedRouter)
{
lastShift = now;
2018-10-04 13:03:48 +00:00
BuildOneAlignedTo(m_NextIntro.router);
2018-10-01 15:45:55 +00:00
}
2018-10-08 20:41:50 +00:00
else if(shiftedIntro)
{
SwapIntros();
}
else
{
llarp::LogInfo(Name(), " updating introset");
UpdateIntroSet(false);
}
2018-10-01 15:45:55 +00:00
return shiftedIntro;
}
bool
2018-08-10 21:34:11 +00:00
Endpoint::OutboundContext::ShiftIntroduction()
2018-07-12 18:21:44 +00:00
{
bool success = false;
2018-10-29 16:48:36 +00:00
auto now = Now();
2018-09-18 17:48:26 +00:00
if(now - lastShift < MIN_SHIFT_INTERVAL)
return false;
bool shifted = false;
2018-09-27 17:29:26 +00:00
// to find a intro on the same router as before
for(const auto& intro : currentIntroSet.I)
{
if(intro.ExpiresSoon(now))
continue;
if(m_BadIntros.find(intro) == m_BadIntros.end()
&& remoteIntro.router == intro.router)
{
2018-10-04 13:03:48 +00:00
m_NextIntro = intro;
2018-09-27 17:29:26 +00:00
return true;
}
}
2018-08-10 21:34:11 +00:00
for(const auto& intro : currentIntroSet.I)
{
2018-09-18 17:48:26 +00:00
m_Endpoint->EnsureRouterIsKnown(intro.router);
if(intro.ExpiresSoon(now))
continue;
2018-10-04 13:03:48 +00:00
if(m_BadIntros.find(intro) == m_BadIntros.end() && m_NextIntro != intro)
{
2018-10-04 13:03:48 +00:00
shifted = intro.router != m_NextIntro.router
|| (now < intro.expiresAt
&& intro.expiresAt - now
> 10 * 1000); // TODO: hardcoded value
2018-10-04 13:03:48 +00:00
m_NextIntro = intro;
success = true;
break;
2018-09-17 15:32:37 +00:00
}
}
if(shifted)
2018-09-18 17:48:26 +00:00
{
lastShift = now;
2018-10-04 13:03:48 +00:00
BuildOneAlignedTo(m_NextIntro.router);
2018-09-18 17:48:26 +00:00
}
return success;
2018-07-12 18:21:44 +00:00
}
2018-07-19 04:58:39 +00:00
void
2018-09-18 17:48:26 +00:00
Endpoint::SendContext::AsyncEncryptAndSendTo(llarp_buffer_t data,
ProtocolType protocol)
2018-07-19 04:58:39 +00:00
{
2018-10-29 16:48:36 +00:00
auto now = m_Endpoint->Now();
if(remoteIntro.ExpiresSoon(now))
{
if(!MarkCurrentIntroBad(now))
{
llarp::LogWarn("no good path yet, your message may drop");
}
}
2018-07-19 04:58:39 +00:00
if(sequenceNo)
{
2018-09-18 17:48:26 +00:00
EncryptAndSendTo(data, protocol);
2018-07-19 04:58:39 +00:00
}
else
{
2018-09-18 17:48:26 +00:00
AsyncGenIntro(data, protocol);
2018-07-19 04:58:39 +00:00
}
}
struct AsyncKeyExchange
2018-07-19 04:58:39 +00:00
{
2018-12-10 14:14:55 +00:00
llarp::Logic* logic;
llarp::Crypto* crypto;
SharedSecret sharedKey;
2018-08-09 19:02:17 +00:00
ServiceInfo remote;
const Identity& m_LocalIdentity;
2018-07-22 23:14:29 +00:00
ProtocolMessage msg;
ProtocolFrame frame;
2018-08-09 19:02:17 +00:00
Introduction intro;
2018-08-14 21:17:18 +00:00
const PQPubKey introPubKey;
2018-09-17 16:22:11 +00:00
Introduction remoteIntro;
2018-07-22 23:14:29 +00:00
std::function< void(ProtocolFrame&) > hook;
2018-08-09 19:02:17 +00:00
IDataHandler* handler;
2018-10-01 15:45:55 +00:00
ConvoTag tag;
2018-07-19 04:58:39 +00:00
AsyncKeyExchange(llarp::Logic* l, llarp::Crypto* c, const ServiceInfo& r,
const Identity& localident,
2018-09-18 22:54:04 +00:00
const PQPubKey& introsetPubKey,
2018-10-01 15:45:55 +00:00
const Introduction& remote, IDataHandler* h,
const ConvoTag& t)
2018-07-19 04:58:39 +00:00
: logic(l)
, crypto(c)
2018-08-09 19:02:17 +00:00
, remote(r)
2018-07-22 23:14:29 +00:00
, m_LocalIdentity(localident)
2018-08-14 21:17:18 +00:00
, introPubKey(introsetPubKey)
2018-09-18 22:54:04 +00:00
, remoteIntro(remote)
2018-08-09 19:02:17 +00:00
, handler(h)
2018-10-01 15:45:55 +00:00
, tag(t)
2018-07-19 04:58:39 +00:00
{
}
2018-07-22 23:14:29 +00:00
static void
Result(void* user)
{
AsyncKeyExchange* self = static_cast< AsyncKeyExchange* >(user);
2018-08-09 19:02:17 +00:00
// put values
self->handler->PutCachedSessionKeyFor(self->msg.tag, self->sharedKey);
2018-09-17 16:22:11 +00:00
self->handler->PutIntroFor(self->msg.tag, self->remoteIntro);
2018-08-09 19:02:17 +00:00
self->handler->PutSenderFor(self->msg.tag, self->remote);
2018-07-22 23:14:29 +00:00
self->hook(self->frame);
delete self;
}
/// given protocol message make protocol frame
2018-07-19 04:58:39 +00:00
static void
Encrypt(void* user)
2018-07-19 04:58:39 +00:00
{
AsyncKeyExchange* self = static_cast< AsyncKeyExchange* >(user);
// derive ntru session key component
SharedSecret K;
self->crypto->pqe_encrypt(self->frame.C, K, self->introPubKey);
// randomize Nonce
2018-07-22 23:14:29 +00:00
self->frame.N.Randomize();
// compure post handshake session key
// PKE (A, B, N)
SharedSecret sharedSecret;
if(!self->m_LocalIdentity.KeyExchange(self->crypto->dh_client,
sharedSecret, self->remote,
self->frame.N))
{
llarp::LogError("failed to derive x25519 shared key component");
}
std::array< byte_t, 64 > tmp;
// K
std::copy(K.begin(), K.end(), tmp.begin());
// H (K + PKE(A, B, N))
std::copy(sharedSecret.begin(), sharedSecret.end(), tmp.begin() + 32);
self->crypto->shorthash(self->sharedKey,
llarp::StackBuffer< decltype(tmp) >(tmp));
2018-10-01 15:45:55 +00:00
// set tag
self->msg.tag = self->tag;
2018-08-09 19:02:17 +00:00
// set sender
self->msg.sender = self->m_LocalIdentity.pub;
2018-09-17 15:32:37 +00:00
// set version
self->msg.version = LLARP_PROTO_VERSION;
// set protocol
self->msg.proto = eProtocolTraffic;
2018-07-22 23:14:29 +00:00
// encrypt and sign
if(self->frame.EncryptAndSign(self->crypto, self->msg, K,
self->m_LocalIdentity))
2018-12-10 14:14:55 +00:00
self->logic->queue_job({self, &Result});
else
2018-09-12 23:21:59 +00:00
{
llarp::LogError("failed to encrypt and sign");
2018-09-12 23:21:59 +00:00
delete self;
}
2018-07-19 04:58:39 +00:00
}
};
void
Endpoint::EnsureReplyPath(const ServiceInfo& ident)
{
m_AddressToService[ident.Addr()] = ident;
}
void
2018-09-18 17:48:26 +00:00
Endpoint::OutboundContext::AsyncGenIntro(llarp_buffer_t payload,
__attribute__((unused))
2018-08-14 21:17:18 +00:00
ProtocolType t)
2018-07-19 04:58:39 +00:00
{
2018-09-18 17:48:26 +00:00
auto path = m_PathSet->GetPathByRouter(remoteIntro.router);
if(path == nullptr)
{
// try parent as fallback
path = m_Endpoint->GetPathByRouter(remoteIntro.router);
if(path == nullptr)
{
llarp::LogWarn(Name(), " dropping intro frame, no path to ",
remoteIntro.router);
return;
}
}
2018-10-01 15:45:55 +00:00
currentConvoTag.Randomize();
AsyncKeyExchange* ex = new AsyncKeyExchange(
m_Endpoint->RouterLogic(), m_Endpoint->Crypto(), remoteIdent,
m_Endpoint->GetIdentity(), currentIntroSet.K, remoteIntro,
m_DataHandler, currentConvoTag);
2018-09-18 17:48:26 +00:00
ex->hook = std::bind(&Endpoint::OutboundContext::Send, this,
2018-07-22 23:14:29 +00:00
std::placeholders::_1);
2018-10-01 15:45:55 +00:00
2018-07-22 23:14:29 +00:00
ex->msg.PutBuffer(payload);
ex->msg.introReply = path->intro;
llarp_threadpool_queue_job(m_Endpoint->Worker(),
{ex, &AsyncKeyExchange::Encrypt});
2018-07-19 04:58:39 +00:00
}
void
2018-09-18 17:48:26 +00:00
Endpoint::SendContext::Send(ProtocolFrame& msg)
2018-07-19 04:58:39 +00:00
{
2018-09-18 17:48:26 +00:00
auto path = m_PathSet->GetPathByRouter(remoteIntro.router);
if(path == nullptr)
path = m_Endpoint->GetPathByRouter(remoteIntro.router);
2018-07-22 23:14:29 +00:00
if(path)
{
++sequenceNo;
routing::PathTransferMessage transfer(msg, remoteIntro.pathID);
2018-09-24 15:34:56 +00:00
if(path->SendRoutingMessage(&transfer, m_Endpoint->Router()))
{
llarp::LogDebug("sent data to ", remoteIntro.pathID, " on ",
remoteIntro.router);
2018-10-29 16:48:36 +00:00
lastGoodSend = m_Endpoint->Now();
}
2018-09-24 15:34:56 +00:00
else
2018-08-14 21:17:18 +00:00
llarp::LogError("Failed to send frame on path");
2018-07-22 23:14:29 +00:00
}
2018-09-17 15:32:37 +00:00
else
llarp::LogError("cannot send because we have no path to ",
2018-09-18 17:48:26 +00:00
remoteIntro.router);
}
2018-08-04 02:59:32 +00:00
std::string
Endpoint::OutboundContext::Name() const
{
return "OBContext:" + m_Endpoint->Name() + "-"
2018-08-04 02:59:32 +00:00
+ currentIntroSet.A.Addr().ToString();
}
void
Endpoint::OutboundContext::UpdateIntroSet(bool randomizePath)
{
if(updatingIntroSet || markedBad)
2018-09-12 13:29:42 +00:00
return;
2018-08-10 21:34:11 +00:00
auto addr = currentIntroSet.A.Addr();
2018-10-16 11:14:41 +00:00
path::Path* path = nullptr;
if(randomizePath)
path = m_Endpoint->PickRandomEstablishedPath();
else
path = m_Endpoint->GetEstablishedPathClosestTo(addr.as_array());
2018-10-16 11:14:41 +00:00
if(path)
{
2018-08-10 21:34:11 +00:00
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
m_Endpoint,
2018-08-10 21:34:11 +00:00
std::bind(&Endpoint::OutboundContext::OnIntroSetUpdate, this,
2018-10-15 15:43:41 +00:00
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3),
addr, m_Endpoint->GenTXID());
2018-08-10 21:34:11 +00:00
updatingIntroSet = job->SendRequestViaPath(path, m_Endpoint->Router());
}
else
{
llarp::LogWarn(
"Cannot update introset no path for outbound session to ",
currentIntroSet.A.Addr().ToString());
}
}
bool
Endpoint::OutboundContext::Tick(llarp_time_t now)
{
2018-10-23 17:04:35 +00:00
// check for expiration
if(remoteIntro.ExpiresSoon(now))
{
// shift intro if it expires "soon"
ShiftIntroduction();
2018-10-23 17:04:35 +00:00
}
// swap if we can
if(remoteIntro != m_NextIntro)
{
if(GetPathByRouter(m_NextIntro.router) != nullptr)
2018-10-04 13:03:48 +00:00
{
2018-10-23 17:04:35 +00:00
// we can safely set remoteIntro to the next one
SwapIntros();
llarp::LogInfo(Name(), "swapped intro");
2018-10-04 13:03:48 +00:00
}
}
// lookup router in intro if set and unknown
if(!remoteIntro.router.IsZero())
m_Endpoint->EnsureRouterIsKnown(remoteIntro.router);
// expire bad intros
2018-09-20 14:32:31 +00:00
auto itr = m_BadIntros.begin();
while(itr != m_BadIntros.end())
{
if(now - itr->second > DEFAULT_PATH_LIFETIME)
2018-09-20 14:32:31 +00:00
itr = m_BadIntros.erase(itr);
else
++itr;
}
// if we are dead return true so we are removed
2018-09-24 15:34:56 +00:00
return lastGoodSend
2018-09-24 17:33:54 +00:00
? (now >= lastGoodSend && now - lastGoodSend > sendTimeout)
: (now >= createdAt && now - createdAt > connectTimeout);
2018-07-19 04:58:39 +00:00
}
bool
2018-08-30 18:48:43 +00:00
Endpoint::OutboundContext::SelectHop(llarp_nodedb* db,
const RouterContact& prev,
RouterContact& cur, size_t hop,
llarp::path::PathRole roles)
{
2018-10-04 13:03:48 +00:00
if(m_NextIntro.router.IsZero())
2018-09-20 11:27:18 +00:00
return false;
2018-08-12 17:22:29 +00:00
if(hop == numHops - 1)
{
if(db->Get(m_NextIntro.router, cur))
{
return true;
}
else
{
// we don't have it?
llarp::LogError(
2018-08-09 19:02:17 +00:00
"cannot build aligned path, don't have router for "
"introduction ",
2018-10-04 13:03:48 +00:00
m_NextIntro);
m_Endpoint->EnsureRouterIsKnown(m_NextIntro.router);
return false;
}
}
2018-11-21 14:22:45 +00:00
(void)roles;
2018-11-22 15:52:04 +00:00
return path::Builder::SelectHop(db, prev, cur, hop, roles);
}
2018-08-09 19:02:17 +00:00
uint64_t
Endpoint::GetSeqNoForConvo(const ConvoTag& tag)
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
return 0;
return ++(itr->second.seqno);
}
bool
2018-10-29 16:48:36 +00:00
Endpoint::OutboundContext::ShouldBuildMore(llarp_time_t now) const
{
if(markedBad)
return false;
2018-11-22 15:52:04 +00:00
bool should = path::Builder::ShouldBuildMore(now);
// determinte newest intro
Introduction intro;
if(!GetNewestIntro(intro))
return should;
// time from now that the newest intro expires at
2018-09-27 11:07:20 +00:00
if(now >= intro.expiresAt)
return should;
auto dlt = now - intro.expiresAt;
2018-09-27 11:09:00 +00:00
return should
|| ( // try spacing tunnel builds out evenly in time
(dlt < (DEFAULT_PATH_LIFETIME / 2))
&& (NumInStatus(path::ePathBuilding) < m_NumPaths)
&& (dlt > buildIntervalLimit));
}
2018-09-19 13:59:14 +00:00
/// send on an established convo tag
2018-07-19 04:58:39 +00:00
void
2018-09-18 17:48:26 +00:00
Endpoint::SendContext::EncryptAndSendTo(llarp_buffer_t payload,
ProtocolType t)
2018-07-19 04:58:39 +00:00
{
auto crypto = m_Endpoint->Router()->crypto;
SharedSecret shared;
routing::PathTransferMessage msg;
ProtocolFrame& f = msg.T;
f.N.Randomize();
2018-10-01 15:45:55 +00:00
f.T = currentConvoTag;
f.S = m_Endpoint->GetSeqNoForConvo(f.T);
2018-10-29 16:48:36 +00:00
auto now = m_Endpoint->Now();
2018-09-19 13:59:14 +00:00
if(remoteIntro.ExpiresSoon(now))
{
// shift intro
2018-11-03 13:35:54 +00:00
if(MarkCurrentIntroBad(now))
{
llarp::LogInfo("intro shifted");
}
2018-09-19 13:59:14 +00:00
}
auto path = m_PathSet->GetNewestPathByRouter(remoteIntro.router);
if(!path)
{
2018-11-03 13:35:54 +00:00
llarp::LogError("cannot encrypt and send: no path for intro ",
remoteIntro);
return;
}
2018-08-09 19:02:17 +00:00
if(m_DataHandler->GetCachedSessionKeyFor(f.T, shared))
{
ProtocolMessage m;
2018-09-18 10:18:57 +00:00
m.proto = t;
m_DataHandler->PutIntroFor(f.T, remoteIntro);
m.introReply = path->intro;
m.sender = m_Endpoint->m_Identity.pub;
m.PutBuffer(payload);
m.tag = f.T;
2018-08-09 19:02:17 +00:00
if(!f.EncryptAndSign(&crypto, m, shared, m_Endpoint->m_Identity))
2018-08-09 19:02:17 +00:00
{
llarp::LogError("failed to sign");
return;
2018-08-09 19:02:17 +00:00
}
}
else
{
llarp::LogError("No cached session key");
return;
}
msg.P = remoteIntro.pathID;
msg.Y.Randomize();
++sequenceNo;
if(path->SendRoutingMessage(&msg, m_Endpoint->Router()))
{
llarp::LogDebug("sent message via ", remoteIntro.pathID, " on ",
remoteIntro.router);
2018-10-06 15:41:24 +00:00
lastGoodSend = now;
}
else
{
llarp::LogWarn("Failed to send routing message for data");
2018-08-09 19:02:17 +00:00
}
2018-07-19 04:58:39 +00:00
}
2018-12-10 14:14:55 +00:00
llarp::Logic*
2018-08-09 19:02:17 +00:00
Endpoint::RouterLogic()
2018-07-19 04:58:39 +00:00
{
return m_Router->logic;
}
2018-12-10 14:14:55 +00:00
llarp::Logic*
2018-08-09 19:02:17 +00:00
Endpoint::EndpointLogic()
{
return m_IsolatedLogic ? m_IsolatedLogic : m_Router->logic;
}
llarp::Crypto*
2018-07-19 04:58:39 +00:00
Endpoint::Crypto()
{
return &m_Router->crypto;
}
llarp_threadpool*
Endpoint::Worker()
{
return m_Router->tp;
}
2018-07-12 18:21:44 +00:00
} // namespace service
2018-07-16 03:32:13 +00:00
} // namespace llarp