mirror of https://github.com/oxen-io/lokinet
Compare commits
22 Commits
1ef77cccbd
...
d520e1d2c4
Author | SHA1 | Date |
---|---|---|
dr7ana | d520e1d2c4 | 6 months ago |
Thomas Winget | 5bf520d0f1 | 6 months ago |
Thomas Winget | 76d45ec802 | 6 months ago |
Thomas Winget | b044622a21 | 6 months ago |
Thomas Winget | 2425652696 | 6 months ago |
Thomas Winget | 29ec72f0da | 6 months ago |
Thomas Winget | 27aea62994 | 6 months ago |
Thomas Winget | ad9d0b19c1 | 6 months ago |
dr7ana | 28047ae72f | 6 months ago |
dr7ana | e58e8473f8 | 6 months ago |
Thomas Winget | feaf0b9193 | 6 months ago |
Thomas Winget | 2e5c856cf3 | 6 months ago |
Thomas Winget | d7e2e52ee4 | 6 months ago |
Thomas Winget | e6eeda0f15 | 6 months ago |
Thomas Winget | bd4f239aa3 | 6 months ago |
Jason Rhinelander | 1ca852d2f5 | 6 months ago |
Thomas Winget | 32395caec1 | 6 months ago |
Thomas Winget | 9e9c1ea732 | 6 months ago |
Thomas Winget | abb2f63ec6 | 6 months ago |
Thomas Winget | e7632d0a30 | 6 months ago |
Thomas Winget | b0fb194e2c | 6 months ago |
Thomas Winget | c25ced50a3 | 6 months ago |
@ -1,127 +0,0 @@
|
||||
#include "encrypted_frame.hpp"
|
||||
|
||||
#include "crypto.hpp"
|
||||
|
||||
#include <llarp/util/logging.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
bool
|
||||
EncryptedFrame::DoEncrypt(const SharedSecret& shared, bool noDH)
|
||||
{
|
||||
uint8_t* hash_ptr = data();
|
||||
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
|
||||
uint8_t* pubkey_ptr = nonce_ptr + TUNNONCESIZE;
|
||||
uint8_t* body_ptr = pubkey_ptr + PUBKEYSIZE;
|
||||
|
||||
if (noDH)
|
||||
{
|
||||
crypto::randbytes(nonce_ptr, TUNNONCESIZE);
|
||||
crypto::randbytes(pubkey_ptr, PUBKEYSIZE);
|
||||
}
|
||||
|
||||
TunnelNonce nonce(nonce_ptr);
|
||||
|
||||
// encrypt body
|
||||
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
|
||||
{
|
||||
llarp::LogError("encrypt failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!crypto::hmac(hash_ptr, nonce_ptr, size() - SHORTHASHSIZE, shared))
|
||||
{
|
||||
llarp::LogError("Failed to generate message auth");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EncryptedFrame::EncryptInPlace(const SecretKey& ourSecretKey, const PubKey& otherPubkey)
|
||||
{
|
||||
// format of frame is
|
||||
// <32 bytes keyed hash of following data>
|
||||
// <32 bytes nonce>
|
||||
// <32 bytes pubkey>
|
||||
// <N bytes encrypted payload>
|
||||
//
|
||||
byte_t* hash = data();
|
||||
byte_t* noncePtr = hash + SHORTHASHSIZE;
|
||||
byte_t* pubkey = noncePtr + TUNNONCESIZE;
|
||||
|
||||
SharedSecret shared;
|
||||
|
||||
// set our pubkey
|
||||
memcpy(pubkey, ourSecretKey.toPublic().data(), PUBKEYSIZE);
|
||||
// randomize nonce
|
||||
crypto::randbytes(noncePtr, TUNNONCESIZE);
|
||||
TunnelNonce nonce(noncePtr);
|
||||
|
||||
// derive shared key
|
||||
if (!crypto::dh_client(shared, otherPubkey, ourSecretKey, nonce))
|
||||
{
|
||||
llarp::LogError("DH failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoEncrypt(shared, false);
|
||||
}
|
||||
|
||||
bool
|
||||
EncryptedFrame::DoDecrypt(const SharedSecret& shared)
|
||||
{
|
||||
uint8_t* hash_ptr = data();
|
||||
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
|
||||
uint8_t* body_ptr = hash_ptr + EncryptedFrameOverheadSize;
|
||||
|
||||
TunnelNonce nonce(nonce_ptr);
|
||||
|
||||
ShortHash digest;
|
||||
if (!crypto::hmac(digest.data(), nonce_ptr, size() - SHORTHASHSIZE, shared))
|
||||
{
|
||||
llarp::LogError("Digest failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!std::equal(digest.begin(), digest.end(), hash_ptr))
|
||||
{
|
||||
llarp::LogError("message authentication failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
|
||||
{
|
||||
llarp::LogError("decrypt failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EncryptedFrame::DecryptInPlace(const SecretKey& ourSecretKey)
|
||||
{
|
||||
// format of frame is
|
||||
// <32 bytes keyed hash of following data>
|
||||
// <32 bytes nonce>
|
||||
// <32 bytes pubkey>
|
||||
// <N bytes encrypted payload>
|
||||
//
|
||||
byte_t* noncePtr = data() + SHORTHASHSIZE;
|
||||
TunnelNonce nonce(noncePtr);
|
||||
PubKey otherPubkey(noncePtr + TUNNONCESIZE);
|
||||
|
||||
SharedSecret shared;
|
||||
|
||||
// use dh_server because we are not the creator of this message
|
||||
if (!crypto::dh_server(shared, otherPubkey, ourSecretKey, nonce))
|
||||
{
|
||||
llarp::LogError("DH failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoDecrypt(shared);
|
||||
}
|
||||
} // namespace llarp
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "encrypted.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/mem.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
static constexpr size_t EncryptedFrameOverheadSize = PUBKEYSIZE + TUNNONCESIZE + SHORTHASHSIZE;
|
||||
static constexpr size_t EncryptedFrameBodySize = 128 * 6;
|
||||
static constexpr size_t EncryptedFrameSize = EncryptedFrameOverheadSize + EncryptedFrameBodySize;
|
||||
|
||||
struct EncryptedFrame : public Encrypted<EncryptedFrameSize>
|
||||
{
|
||||
EncryptedFrame() : EncryptedFrame(EncryptedFrameBodySize)
|
||||
{}
|
||||
|
||||
EncryptedFrame(size_t sz)
|
||||
: Encrypted<EncryptedFrameSize>(
|
||||
std::min(sz, EncryptedFrameBodySize) + EncryptedFrameOverheadSize)
|
||||
{}
|
||||
|
||||
void
|
||||
Resize(size_t sz)
|
||||
{
|
||||
if (sz <= EncryptedFrameSize)
|
||||
{
|
||||
_sz = sz;
|
||||
UpdateBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DoEncrypt(const SharedSecret& shared, bool noDH = false);
|
||||
|
||||
bool
|
||||
DecryptInPlace(const SecretKey& seckey);
|
||||
|
||||
bool
|
||||
DoDecrypt(const SharedSecret& shared);
|
||||
|
||||
bool
|
||||
EncryptInPlace(const SecretKey& seckey, const PubKey& other);
|
||||
};
|
||||
|
||||
template <typename User>
|
||||
struct AsyncFrameDecrypter
|
||||
{
|
||||
using User_ptr = std::shared_ptr<User>;
|
||||
using DecryptHandler = std::function<void(llarp_buffer_t*, User_ptr)>;
|
||||
|
||||
void
|
||||
Decrypt(User_ptr user)
|
||||
{
|
||||
if (target.DecryptInPlace(seckey))
|
||||
{
|
||||
auto buf = target.Buffer();
|
||||
buf->cur = buf->base + EncryptedFrameOverheadSize;
|
||||
result(buf, user);
|
||||
}
|
||||
else
|
||||
result(nullptr, user);
|
||||
}
|
||||
|
||||
AsyncFrameDecrypter(const SecretKey& secretkey, DecryptHandler h)
|
||||
: result(std::move(h)), seckey(secretkey)
|
||||
{}
|
||||
|
||||
DecryptHandler result;
|
||||
const SecretKey& seckey;
|
||||
EncryptedFrame target;
|
||||
|
||||
using WorkFunc_t = std::function<void(void)>;
|
||||
using WorkerFunction_t = std::function<void(WorkFunc_t)>;
|
||||
|
||||
void
|
||||
AsyncDecrypt(const EncryptedFrame& frame, User_ptr u, WorkerFunction_t worker)
|
||||
{
|
||||
target = frame;
|
||||
worker([this, u = std::move(u)]() mutable { Decrypt(std::move(u)); });
|
||||
}
|
||||
};
|
||||
} // namespace llarp
|
File diff suppressed because it is too large
Load Diff
@ -1,121 +0,0 @@
|
||||
#include "relay.hpp"
|
||||
|
||||
#include <llarp/path/path_context.hpp>
|
||||
#include <llarp/router/router.hpp>
|
||||
#include <llarp/util/bencode.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
void
|
||||
RelayUpstreamMessage::clear()
|
||||
{
|
||||
pathid.Zero();
|
||||
enc.Clear();
|
||||
nonce.Zero();
|
||||
version = 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
RelayUpstreamMessage::bt_encode() const
|
||||
{
|
||||
oxenc::bt_dict_producer btdp;
|
||||
|
||||
try
|
||||
{
|
||||
btdp.append("a", "u");
|
||||
btdp.append("p", pathid.ToView());
|
||||
btdp.append("v", llarp::constants::proto_version);
|
||||
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
|
||||
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
log::critical(link_cat, "Error: RelayUpstreamMessage failed to bt encode contents!");
|
||||
}
|
||||
|
||||
return std::move(btdp).str();
|
||||
}
|
||||
|
||||
bool
|
||||
RelayUpstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
bool read = false;
|
||||
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
|
||||
return false;
|
||||
return read;
|
||||
}
|
||||
|
||||
bool
|
||||
RelayUpstreamMessage::handle_message(Router* r) const
|
||||
{
|
||||
auto path = r->path_context().GetByDownstream(conn->remote_rc.router_id(), pathid);
|
||||
if (path)
|
||||
{
|
||||
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
RelayDownstreamMessage::clear()
|
||||
{
|
||||
pathid.Zero();
|
||||
enc.Clear();
|
||||
nonce.Zero();
|
||||
version = 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
RelayDownstreamMessage::bt_encode() const
|
||||
{
|
||||
oxenc::bt_dict_producer btdp;
|
||||
|
||||
try
|
||||
{
|
||||
btdp.append("a", "d");
|
||||
btdp.append("p", pathid.ToView());
|
||||
btdp.append("v", llarp::constants::proto_version);
|
||||
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
|
||||
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
log::critical(link_cat, "Error: RelayDownstreamMessage failed to bt encode contents!");
|
||||
}
|
||||
|
||||
return std::move(btdp).str();
|
||||
}
|
||||
|
||||
bool
|
||||
RelayDownstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
bool read = false;
|
||||
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
|
||||
return false;
|
||||
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
|
||||
return false;
|
||||
return read;
|
||||
}
|
||||
|
||||
bool
|
||||
RelayDownstreamMessage::handle_message(Router* r) const
|
||||
{
|
||||
auto path = r->path_context().GetByUpstream(conn->remote_rc.router_id(), pathid);
|
||||
if (path)
|
||||
{
|
||||
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);
|
||||
}
|
||||
llarp::LogWarn("no path for downstream message id=", pathid);
|
||||
return false;
|
||||
}
|
||||
} // namespace llarp
|
@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "link_message.hpp"
|
||||
|
||||
#include <llarp/crypto/encrypted.hpp>
|
||||
#include <llarp/crypto/types.hpp>
|
||||
#include <llarp/path/path_types.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/*
|
||||
Data messages to be sent via quic datagrams
|
||||
*/
|
||||
|
||||
struct RelayUpstreamMessage final : public AbstractLinkMessage
|
||||
{
|
||||
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
|
||||
TunnelNonce nonce;
|
||||
|
||||
bool
|
||||
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
|
||||
|
||||
std::string
|
||||
bt_encode() const override;
|
||||
|
||||
bool
|
||||
handle_message(Router* router) const override;
|
||||
|
||||
void
|
||||
clear() override;
|
||||
|
||||
const char*
|
||||
name() const override
|
||||
{
|
||||
return "RelayUpstream";
|
||||
}
|
||||
uint16_t
|
||||
priority() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct RelayDownstreamMessage final : public AbstractLinkMessage
|
||||
{
|
||||
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
|
||||
TunnelNonce nonce;
|
||||
|
||||
bool
|
||||
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
|
||||
|
||||
std::string
|
||||
bt_encode() const override;
|
||||
|
||||
bool
|
||||
handle_message(Router* router) const override;
|
||||
|
||||
void
|
||||
clear() override;
|
||||
|
||||
const char*
|
||||
name() const override
|
||||
{
|
||||
return "RelayDownstream";
|
||||
}
|
||||
|
||||
uint16_t
|
||||
priority() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
} // namespace llarp
|
@ -1,151 +0,0 @@
|
||||
#include "rc_gossiper.hpp"
|
||||
|
||||
#include <llarp/router_contact.hpp>
|
||||
#include <llarp/util/time.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
// 30 minutes
|
||||
static constexpr auto RCGossipFilterDecayInterval = 30min;
|
||||
// (30 minutes * 2) - 5 minutes
|
||||
static constexpr auto GossipOurRCInterval = (RCGossipFilterDecayInterval * 2) - (5min);
|
||||
|
||||
RCGossiper::RCGossiper() : filter(std::chrono::duration_cast<Time_t>(RCGossipFilterDecayInterval))
|
||||
{}
|
||||
|
||||
void
|
||||
RCGossiper::Init(LinkManager* l, const RouterID& ourID, Router* r)
|
||||
{
|
||||
rid = ourID;
|
||||
link_manager = l;
|
||||
router = r;
|
||||
}
|
||||
|
||||
bool
|
||||
RCGossiper::ShouldGossipOurRC(Time_t now) const
|
||||
{
|
||||
return now >= (last_rc_gossip + GossipOurRCInterval);
|
||||
}
|
||||
|
||||
bool
|
||||
RCGossiper::IsOurRC(const LocalRC& rc) const
|
||||
{
|
||||
return rc.router_id() == rid;
|
||||
}
|
||||
|
||||
void
|
||||
RCGossiper::Decay(Time_t now)
|
||||
{
|
||||
filter.Decay(now);
|
||||
}
|
||||
|
||||
void
|
||||
RCGossiper::Forget(const RouterID& pk)
|
||||
{
|
||||
filter.Remove(pk);
|
||||
if (rid == pk)
|
||||
last_rc_gossip = 0s;
|
||||
}
|
||||
|
||||
TimePoint_t
|
||||
RCGossiper::NextGossipAt() const
|
||||
{
|
||||
if (auto maybe = LastGossipAt())
|
||||
return *maybe + GossipOurRCInterval;
|
||||
return DateClock_t::now();
|
||||
}
|
||||
|
||||
std::optional<TimePoint_t>
|
||||
RCGossiper::LastGossipAt() const
|
||||
{
|
||||
if (last_rc_gossip == 0s)
|
||||
return std::nullopt;
|
||||
return DateClock_t::time_point{last_rc_gossip};
|
||||
}
|
||||
|
||||
bool
|
||||
RCGossiper::GossipRC(const LocalRC& rc)
|
||||
{
|
||||
// only distribute public routers
|
||||
if (not rc.is_public_router())
|
||||
return false;
|
||||
if (link_manager == nullptr)
|
||||
return false;
|
||||
const RouterID pubkey(rc.router_id());
|
||||
// filter check
|
||||
if (filter.Contains(pubkey))
|
||||
return false;
|
||||
filter.Insert(pubkey);
|
||||
|
||||
const auto now = time_now_ms();
|
||||
// is this our rc?
|
||||
if (IsOurRC(rc))
|
||||
{
|
||||
// should we gossip our rc?
|
||||
if (not ShouldGossipOurRC(now))
|
||||
{
|
||||
// nah drop it
|
||||
return false;
|
||||
}
|
||||
// ya pop it
|
||||
last_rc_gossip = now;
|
||||
}
|
||||
|
||||
// send a GRCM as gossip method
|
||||
// DHTImmediateMessage gossip;
|
||||
// gossip.msgs.emplace_back(new dht::GotRouterMessage(dht::Key_t{}, 0, {rc}, false));
|
||||
|
||||
// std::vector<RouterID> gossipTo;
|
||||
|
||||
/*
|
||||
* TODO: gossip RC via libquic
|
||||
*
|
||||
// select peers to gossip to
|
||||
m_LinkManager->ForEachPeer(
|
||||
[&](const AbstractLinkSession* peerSession, bool) {
|
||||
// ensure connected session
|
||||
if (not(peerSession && peerSession->IsEstablished()))
|
||||
return;
|
||||
// check if public router
|
||||
const auto other_rc = peerSession->GetRemoteRC();
|
||||
if (not other_rc.IsPublicRouter())
|
||||
return;
|
||||
gossipTo.emplace_back(other_rc.pubkey);
|
||||
},
|
||||
true);
|
||||
|
||||
std::unordered_set<RouterID> keys;
|
||||
// grab the keys we want to use
|
||||
std::sample(
|
||||
gossipTo.begin(), gossipTo.end(), std::inserter(keys, keys.end()), MaxGossipPeers,
|
||||
llarp::csrng);
|
||||
|
||||
m_LinkManager->ForEachPeer([&](AbstractLinkSession* peerSession) {
|
||||
if (not(peerSession && peerSession->IsEstablished()))
|
||||
return;
|
||||
|
||||
// exclude from gossip as we have not selected to use it
|
||||
if (keys.count(peerSession->GetPubKey()) == 0)
|
||||
return;
|
||||
|
||||
// encode message
|
||||
AbstractLinkSession::Message_t msg{};
|
||||
msg.resize(MAX_LINK_MSG_SIZE / 2);
|
||||
llarp_buffer_t buf(msg);
|
||||
if (not gossip.BEncode(&buf))
|
||||
return;
|
||||
msg.resize(buf.cur - buf.base);
|
||||
|
||||
m_router->NotifyRouterEvent<tooling::RCGossipSentEvent>(m_router->pubkey(), rc);
|
||||
|
||||
// send message
|
||||
peerSession->SendMessageBuffer(std::move(msg), nullptr, gossip.Priority());
|
||||
});
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace llarp
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <llarp/router_id.hpp>
|
||||
#include <llarp/util/decaying_hashset.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
struct Router;
|
||||
|
||||
/// The maximum number of peers we will flood a gossiped RC to when propagating an RC
|
||||
constexpr size_t MaxGossipPeers = 20;
|
||||
struct LinkManager;
|
||||
struct LocalRC;
|
||||
|
||||
struct RCGossiper
|
||||
{
|
||||
using Time_t = Duration_t;
|
||||
|
||||
RCGossiper();
|
||||
|
||||
~RCGossiper() = default;
|
||||
|
||||
bool
|
||||
GossipRC(const LocalRC& rc);
|
||||
|
||||
void
|
||||
Decay(Time_t now);
|
||||
|
||||
bool
|
||||
ShouldGossipOurRC(Time_t now) const;
|
||||
|
||||
bool
|
||||
IsOurRC(const LocalRC& rc) const;
|
||||
|
||||
void
|
||||
Init(LinkManager*, const RouterID&, Router*);
|
||||
|
||||
void
|
||||
Forget(const RouterID& router);
|
||||
|
||||
TimePoint_t
|
||||
NextGossipAt() const;
|
||||
|
||||
std::optional<TimePoint_t>
|
||||
LastGossipAt() const;
|
||||
|
||||
private:
|
||||
RouterID rid;
|
||||
Time_t last_rc_gossip = 0s;
|
||||
LinkManager* link_manager = nullptr;
|
||||
util::DecayingHashSet<RouterID> filter;
|
||||
|
||||
Router* router;
|
||||
};
|
||||
} // namespace llarp
|
@ -1,381 +0,0 @@
|
||||
#include "rc_lookup_handler.hpp"
|
||||
|
||||
#include "router.hpp"
|
||||
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
#include <llarp/link/contacts.hpp>
|
||||
#include <llarp/link/link_manager.hpp>
|
||||
#include <llarp/nodedb.hpp>
|
||||
#include <llarp/router_contact.hpp>
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/util/types.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
void
|
||||
RCLookupHandler::add_valid_router(const RouterID& rid)
|
||||
{
|
||||
router->loop()->call([this, rid]() { router_whitelist.insert(rid); });
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::remove_valid_router(const RouterID& rid)
|
||||
{
|
||||
router->loop()->call([this, rid]() { router_whitelist.erase(rid); });
|
||||
}
|
||||
|
||||
static void
|
||||
loadColourList(std::unordered_set<RouterID>& beigelist, const std::vector<RouterID>& new_beige)
|
||||
{
|
||||
beigelist.clear();
|
||||
beigelist.insert(new_beige.begin(), new_beige.end());
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::set_router_whitelist(
|
||||
const std::vector<RouterID>& whitelist,
|
||||
const std::vector<RouterID>& greylist,
|
||||
const std::vector<RouterID>& greenlist)
|
||||
{
|
||||
if (whitelist.empty())
|
||||
return;
|
||||
|
||||
router->loop()->call([this, whitelist, greylist, greenlist]() {
|
||||
loadColourList(router_whitelist, whitelist);
|
||||
loadColourList(router_greylist, greylist);
|
||||
loadColourList(router_greenlist, greenlist);
|
||||
LogInfo("lokinet service node list now has ", router_whitelist.size(), " active routers");
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::has_received_whitelist() const
|
||||
{
|
||||
return router->loop()->call_get([this]() { return not router_whitelist.empty(); });
|
||||
}
|
||||
|
||||
std::unordered_set<RouterID>
|
||||
RCLookupHandler::whitelist() const
|
||||
{
|
||||
return router->loop()->call_get([this]() { return router_whitelist; });
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup)
|
||||
{
|
||||
RemoteRC remoteRC;
|
||||
|
||||
if (not forceLookup)
|
||||
{
|
||||
if (const auto maybe = node_db->get_rc(rid); maybe.has_value())
|
||||
{
|
||||
remoteRC = *maybe;
|
||||
|
||||
if (callback)
|
||||
{
|
||||
callback(rid, remoteRC, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto lookup_cb = [this, callback, rid](oxen::quic::message m) mutable {
|
||||
auto& r = link_manager->router();
|
||||
|
||||
if (m)
|
||||
{
|
||||
std::string payload;
|
||||
|
||||
try
|
||||
{
|
||||
oxenc::bt_dict_consumer btdc{m.body()};
|
||||
payload = btdc.require<std::string>("RC");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
log::warning(link_cat, "Failed to parse Find Router response!");
|
||||
throw;
|
||||
}
|
||||
|
||||
RemoteRC result{std::move(payload)};
|
||||
|
||||
if (callback)
|
||||
callback(result.router_id(), result, true);
|
||||
else
|
||||
r.node_db()->put_rc_if_newer(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback)
|
||||
callback(rid, std::nullopt, false);
|
||||
else
|
||||
link_manager->handle_find_router_error(std::move(m));
|
||||
}
|
||||
};
|
||||
|
||||
// if we are a client try using the hidden service endpoints
|
||||
if (!isServiceNode)
|
||||
{
|
||||
bool sent = false;
|
||||
LogInfo("Lookup ", rid, " anonymously");
|
||||
hidden_service_context->ForEachService(
|
||||
[&, cb = lookup_cb](
|
||||
const std::string&, const std::shared_ptr<service::Endpoint>& ep) -> bool {
|
||||
const bool success = ep->lookup_router(rid, cb);
|
||||
sent = sent || success;
|
||||
return !success;
|
||||
});
|
||||
if (sent)
|
||||
return;
|
||||
LogWarn("cannot lookup ", rid, " anonymously");
|
||||
}
|
||||
|
||||
contacts->lookup_router(rid, lookup_cb);
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_grey_listed(const RouterID& remote) const
|
||||
{
|
||||
if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(remote) == 0
|
||||
&& !is_remote_in_bootstrap(remote))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not isServiceNode)
|
||||
return false;
|
||||
|
||||
return router->loop()->call_get([this, remote]() { return router_greylist.count(remote); });
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_green_listed(const RouterID& remote) const
|
||||
{
|
||||
return router->loop()->call_get([this, remote]() { return router_greenlist.count(remote); });
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_registered(const RouterID& rid) const
|
||||
{
|
||||
return router->loop()->call_get([this, rid]() {
|
||||
return router_whitelist.count(rid) || router_greylist.count(rid)
|
||||
|| router_greenlist.count(rid);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_path_allowed(const RouterID& rid) const
|
||||
{
|
||||
return router->loop()->call_get([this, rid]() {
|
||||
if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(rid) == 0
|
||||
&& !is_remote_in_bootstrap(rid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not isServiceNode)
|
||||
return true;
|
||||
|
||||
return router_whitelist.count(rid) != 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_session_allowed(const RouterID& rid) const
|
||||
{
|
||||
return router->loop()->call_get([this, rid]() {
|
||||
if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(rid) == 0
|
||||
&& !is_remote_in_bootstrap(rid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not isServiceNode)
|
||||
return true;
|
||||
|
||||
return router_whitelist.count(rid) or router_greylist.count(rid);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::check_rc(const RemoteRC& rc) const
|
||||
{
|
||||
if (not is_session_allowed(rc.router_id()))
|
||||
{
|
||||
contacts->delete_rc_node_async(dht::Key_t{rc.router_id()});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not rc.verify())
|
||||
{
|
||||
log::info(link_cat, "Invalid RC (rid: {})", rc.router_id());
|
||||
return false;
|
||||
}
|
||||
|
||||
// update nodedb if required
|
||||
if (rc.is_public_router())
|
||||
{
|
||||
log::info(link_cat, "Adding or updating RC (rid: {}) to nodeDB and DHT", rc.router_id());
|
||||
node_db->put_rc_if_newer(rc);
|
||||
contacts->put_rc_node_async(rc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
RCLookupHandler::num_strict_connect_routers() const
|
||||
{
|
||||
return strict_connect_pubkeys.size();
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::get_random_whitelist_router(RouterID& rid) const
|
||||
{
|
||||
return router->loop()->call_get([this, rid]() mutable {
|
||||
const auto sz = router_whitelist.size();
|
||||
auto itr = router_whitelist.begin();
|
||||
if (sz == 0)
|
||||
return false;
|
||||
if (sz > 1)
|
||||
std::advance(itr, randint() % sz);
|
||||
rid = *itr;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::periodic_update(llarp_time_t now)
|
||||
{
|
||||
// try looking up stale routers
|
||||
std::unordered_set<RouterID> routersToLookUp;
|
||||
|
||||
node_db->VisitInsertedBefore(
|
||||
[&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); },
|
||||
now - RouterContact::REPUBLISH);
|
||||
|
||||
for (const auto& router : routersToLookUp)
|
||||
{
|
||||
get_rc(router, nullptr, true);
|
||||
}
|
||||
|
||||
node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::STALE);
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::explore_network()
|
||||
{
|
||||
const size_t known = node_db->num_loaded();
|
||||
if (bootstrap_rc_list.empty() && known == 0)
|
||||
{
|
||||
LogError("we have no bootstrap nodes specified");
|
||||
}
|
||||
else if (known <= bootstrap_rc_list.size())
|
||||
{
|
||||
for (const auto& rc : bootstrap_rc_list)
|
||||
{
|
||||
const auto& rid = rc.router_id();
|
||||
log::info(link_cat, "Doing explore via bootstrap node: {}", rid);
|
||||
|
||||
// TODO: replace this concept
|
||||
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
}
|
||||
}
|
||||
|
||||
if (isServiceNode)
|
||||
{
|
||||
static constexpr size_t LookupPerTick = 5;
|
||||
|
||||
std::vector<RouterID> lookup_routers = router->loop()->call_get([this]() {
|
||||
std::vector<RouterID> lookups;
|
||||
lookups.reserve(LookupPerTick);
|
||||
|
||||
for (const auto& r : router_whitelist)
|
||||
{
|
||||
if (not node_db->has_router(r))
|
||||
lookups.emplace_back(r);
|
||||
}
|
||||
|
||||
return lookups;
|
||||
});
|
||||
|
||||
if (lookup_routers.size() > LookupPerTick)
|
||||
{
|
||||
std::shuffle(lookup_routers.begin(), lookup_routers.end(), llarp::csrng);
|
||||
lookup_routers.resize(LookupPerTick);
|
||||
}
|
||||
|
||||
for (const auto& r : lookup_routers)
|
||||
get_rc(r, nullptr, true);
|
||||
return;
|
||||
}
|
||||
// service nodes gossip, not explore
|
||||
if (contacts->router()->is_service_node())
|
||||
return;
|
||||
|
||||
// explore via every connected peer
|
||||
/*
|
||||
* TODO: DHT explore via libquic
|
||||
*
|
||||
_linkManager->ForEachPeer([&](ILinkSession* s) {
|
||||
if (!s->IsEstablished())
|
||||
return;
|
||||
const RouterContact rc = s->GetRemoteRC();
|
||||
if (rc.IsPublicRouter() && (_bootstrapRCList.find(rc) == _bootstrapRCList.end()))
|
||||
{
|
||||
LogDebug("Doing explore via public node: ", RouterID(rc.pubkey));
|
||||
_dht->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
}
|
||||
});
|
||||
*
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::init(
|
||||
std::shared_ptr<Contacts> c,
|
||||
std::shared_ptr<NodeDB> nodedb,
|
||||
EventLoop_ptr l,
|
||||
std::function<void(std::function<void()>)> dowork,
|
||||
LinkManager* linkManager,
|
||||
service::Context* hiddenServiceContext,
|
||||
const std::unordered_set<RouterID>& strictConnectPubkeys,
|
||||
const std::set<RemoteRC>& bootstrapRCList,
|
||||
bool isServiceNode_arg)
|
||||
{
|
||||
contacts = c;
|
||||
node_db = std::move(nodedb);
|
||||
loop = std::move(l);
|
||||
work_func = std::move(dowork);
|
||||
hidden_service_context = hiddenServiceContext;
|
||||
strict_connect_pubkeys = strictConnectPubkeys;
|
||||
bootstrap_rc_list = bootstrapRCList;
|
||||
link_manager = linkManager;
|
||||
router = &link_manager->router();
|
||||
isServiceNode = isServiceNode_arg;
|
||||
|
||||
for (const auto& rc : bootstrap_rc_list)
|
||||
{
|
||||
boostrap_rid_list.insert(rc.router_id());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RCLookupHandler::is_remote_in_bootstrap(const RouterID& remote) const
|
||||
{
|
||||
for (const auto& rc : bootstrap_rc_list)
|
||||
{
|
||||
if (rc.router_id() == remote)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace llarp
|
@ -1,143 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <llarp/router_contact.hpp>
|
||||
#include <llarp/router_id.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
struct llarp_dht_context;
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
class NodeDB;
|
||||
struct Router;
|
||||
class EventLoop;
|
||||
|
||||
namespace service
|
||||
{
|
||||
struct Context;
|
||||
} // namespace service
|
||||
|
||||
struct Contacts;
|
||||
struct LinkManager;
|
||||
|
||||
enum class RCRequestResult
|
||||
{
|
||||
Success,
|
||||
InvalidRouter,
|
||||
RouterNotFound,
|
||||
BadRC
|
||||
};
|
||||
|
||||
using RCRequestCallback =
|
||||
std::function<void(const RouterID&, std::optional<RemoteRC>, bool success)>;
|
||||
|
||||
struct RCLookupHandler
|
||||
{
|
||||
public:
|
||||
~RCLookupHandler() = default;
|
||||
|
||||
void
|
||||
add_valid_router(const RouterID& router);
|
||||
|
||||
void
|
||||
remove_valid_router(const RouterID& router);
|
||||
|
||||
void
|
||||
set_router_whitelist(
|
||||
const std::vector<RouterID>& whitelist,
|
||||
const std::vector<RouterID>& greylist,
|
||||
const std::vector<RouterID>& greenlist);
|
||||
|
||||
bool
|
||||
has_received_whitelist() const;
|
||||
|
||||
void
|
||||
get_rc(const RouterID& router, RCRequestCallback callback, bool forceLookup = false);
|
||||
|
||||
bool
|
||||
is_path_allowed(const RouterID& remote) const;
|
||||
|
||||
bool
|
||||
is_session_allowed(const RouterID& remote) const;
|
||||
|
||||
bool
|
||||
is_grey_listed(const RouterID& remote) const;
|
||||
|
||||
// "greenlist" = new routers (i.e. "green") that aren't fully funded yet
|
||||
bool
|
||||
is_green_listed(const RouterID& remote) const;
|
||||
|
||||
// registered just means that there is at least an operator stake, but doesn't require the node
|
||||
// be fully funded, active, or not decommed. (In other words: it is any of the white, grey, or
|
||||
// green list).
|
||||
bool
|
||||
is_registered(const RouterID& remote) const;
|
||||
|
||||
bool
|
||||
check_rc(const RemoteRC& rc) const;
|
||||
|
||||
bool
|
||||
get_random_whitelist_router(RouterID& router) const;
|
||||
|
||||
void
|
||||
periodic_update(llarp_time_t now);
|
||||
|
||||
void
|
||||
explore_network();
|
||||
|
||||
size_t
|
||||
num_strict_connect_routers() const;
|
||||
|
||||
void
|
||||
init(
|
||||
std::shared_ptr<Contacts> contacts,
|
||||
std::shared_ptr<NodeDB> nodedb,
|
||||
std::shared_ptr<EventLoop> loop,
|
||||
std::function<void(std::function<void()>)> dowork,
|
||||
LinkManager* linkManager,
|
||||
service::Context* hiddenServiceContext,
|
||||
const std::unordered_set<RouterID>& strictConnectPubkeys,
|
||||
const std::set<RemoteRC>& bootstrapRCList,
|
||||
bool isServiceNode_arg);
|
||||
|
||||
std::unordered_set<RouterID>
|
||||
whitelist() const;
|
||||
|
||||
private:
|
||||
bool
|
||||
is_remote_in_bootstrap(const RouterID& remote) const;
|
||||
|
||||
std::shared_ptr<Contacts> contacts = nullptr;
|
||||
std::shared_ptr<NodeDB> node_db;
|
||||
std::shared_ptr<EventLoop> loop;
|
||||
std::function<void(std::function<void()>)> work_func = nullptr;
|
||||
service::Context* hidden_service_context = nullptr;
|
||||
LinkManager* link_manager = nullptr;
|
||||
Router* router;
|
||||
|
||||
/// explicit whitelist of routers we will connect to directly (not for
|
||||
/// service nodes)
|
||||
std::unordered_set<RouterID> strict_connect_pubkeys;
|
||||
|
||||
std::set<RemoteRC> bootstrap_rc_list;
|
||||
std::unordered_set<RouterID> boostrap_rid_list;
|
||||
|
||||
// Now that all calls are made through the event loop, any access to these
|
||||
// booleans is not guarded by a mutex
|
||||
std::atomic<bool> isServiceNode = false;
|
||||
|
||||
// whitelist = active routers
|
||||
std::unordered_set<RouterID> router_whitelist;
|
||||
// greylist = fully funded, but decommissioned routers
|
||||
std::unordered_set<RouterID> router_greylist;
|
||||
// greenlist = registered but not fully-staked routers
|
||||
std::unordered_set<RouterID> router_greenlist;
|
||||
};
|
||||
|
||||
} // namespace llarp
|
@ -1,72 +0,0 @@
|
||||
#ifndef LLARP_UTIL_MEMFN
|
||||
#define LLARP_UTIL_MEMFN
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp::util
|
||||
{
|
||||
// Wraps a member function and instance into a callable object that invokes
|
||||
// the method (non-const overload).
|
||||
template <
|
||||
typename Return,
|
||||
typename Class,
|
||||
typename Derived,
|
||||
typename... Arg,
|
||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...), Derived* self)
|
||||
{
|
||||
return [f, self](Arg... args) -> Return { return (self->*f)(std::forward<Arg>(args)...); };
|
||||
}
|
||||
|
||||
// Wraps a member function and instance into a lambda that invokes the
|
||||
// method (const overload).
|
||||
template <
|
||||
typename Return,
|
||||
typename Class,
|
||||
typename Derived,
|
||||
typename... Arg,
|
||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...) const, const Derived* self)
|
||||
{
|
||||
return [f, self](Arg... args) -> Return { return (self->*f)(std::forward<Arg>(args)...); };
|
||||
}
|
||||
|
||||
// Wraps a member function and shared pointer to an instance into a lambda
|
||||
// that invokes the method.
|
||||
template <
|
||||
typename Return,
|
||||
typename Class,
|
||||
typename Derived,
|
||||
typename... Arg,
|
||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...), std::shared_ptr<Derived> self)
|
||||
{
|
||||
return [f, self = std::move(self)](Arg... args) -> Return {
|
||||
return (self.get()->*f)(std::forward<Arg>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
// Wraps a member function and shared pointer to an instance into a lambda
|
||||
// that invokes the method (const method overload).
|
||||
template <
|
||||
typename Return,
|
||||
typename Class,
|
||||
typename Derived,
|
||||
typename... Arg,
|
||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
||||
auto
|
||||
memFn(Return (Class::*f)(Arg...) const, std::shared_ptr<Derived> self)
|
||||
{
|
||||
return [f, self = std::move(self)](Arg... args) -> Return {
|
||||
return (self.get()->*f)(std::forward<Arg>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace llarp::util
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue