Compare commits

...

5 Commits

Author SHA1 Message Date
dr7ana 1939ba0b3d
Merge pull request #2218 from dr7ana/rc-bencode
RC Refactor + (some) BT-encode fixes
7 months ago
dr7ana af0ac28119 Review fixes + misc fixes 7 months ago
Jason Rhinelander 8b70e0ad2b Untangle Endpoint::LookupServiceAsync
- .snodes don't need to support SRV records, so remove that
- untangle the mess of captured lambdas capturing other lambdas
  capturing other lambdas; we still need a chain of nested lambdas
  because we have a chain of callbacked events, but hiding the nesting
  by capturing them in other lambdas didn't improve anything.
7 months ago
dr7ana fa4471f566 {Remote,Local}RC's
- RemoteRC supplants most of the functionality throughout the code of RouterContact
- Next step will be to sort out CI issues, then see if we can get rid of either LocalRC (and therefore RouterContact entirely)
7 months ago
dr7ana 07271f9ae7 RC refactor layout
- Local and Remote RC's now implemented with discrete functionalities and uses
7 months ago

@ -41,7 +41,7 @@ if(APPLE)
set(LOKINET_APPLE_BUILD 5) set(LOKINET_APPLE_BUILD 5)
endif() endif()
set(RELEASE_MOTTO "Our Lord And Savior" CACHE STRING "Release motto") set(LOKINET_RELEASE_MOTTO "Anonymous, decentralized, IP-based overlay network" CACHE STRING "Release motto")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")

@ -86,7 +86,7 @@ main(int argc, char* argv[])
#else #else
cpr::Get( cpr::Get(
cpr::Url{bootstrap_url}, cpr::Url{bootstrap_url},
cpr::Header{{"User-Agent", std::string{llarp::VERSION_FULL}}}, cpr::Header{{"User-Agent", std::string{llarp::LOKINET_VERSION_FULL}}},
cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()})); cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()}));
#endif #endif
if (resp.status_code != 200) if (resp.status_code != 200)

@ -403,7 +403,7 @@ namespace
{ {
if (options.version) if (options.version)
{ {
std::cout << llarp::VERSION_FULL << std::endl; std::cout << llarp::LOKINET_VERSION_FULL << std::endl;
return 0; return 0;
} }
@ -548,7 +548,8 @@ namespace
static void static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts) run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{ {
llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); llarp::LogInfo(fmt::format(
"starting up {} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
try try
{ {
std::shared_ptr<llarp::Config> conf; std::shared_ptr<llarp::Config> conf;

@ -91,7 +91,6 @@ add_dependencies(lokinet-utils genversion)
lokinet_add_library(lokinet-time-place lokinet_add_library(lokinet-time-place
ev/ev.cpp ev/ev.cpp
ev/libuv.cpp ev/libuv.cpp
net/exit_info.cpp # only router_contact
net/ip.cpp net/ip.cpp
net/ip_address.cpp net/ip_address.cpp
net/ip_packet.cpp net/ip_packet.cpp
@ -99,6 +98,8 @@ lokinet_add_library(lokinet-time-place
net/net_int.cpp net/net_int.cpp
net/sock_addr.cpp net/sock_addr.cpp
router_contact.cpp router_contact.cpp
router_contact_local.cpp
router_contact_remote.cpp
router_id.cpp router_id.cpp
router_version.cpp # to be deleted shortly router_version.cpp # to be deleted shortly
service/address.cpp service/address.cpp

@ -9,17 +9,18 @@ namespace llarp
load_bootstrap_fallbacks() load_bootstrap_fallbacks()
{ {
std::unordered_map<std::string, BootstrapList> fallbacks; std::unordered_map<std::string, BootstrapList> fallbacks;
using init_list = std::initializer_list<std::pair<std::string, std::string_view>>;
// clang-format off for (const auto& [network, bootstrap] : std::initializer_list<std::pair<std::string, std::string_view>>{
for (const auto& [network, bootstrap] : init_list{
@BOOTSTRAP_FALLBACKS@ @BOOTSTRAP_FALLBACKS@
}) })
// clang-format on
{ {
llarp_buffer_t buf{bootstrap.data(), bootstrap.size()}; if (network != RouterContact::ACTIVE_NETID)
continue;
auto& bsl = fallbacks[network]; auto& bsl = fallbacks[network];
bsl.BDecode(&buf); bsl.bt_decode(bootstrap);
} }
return fallbacks; return fallbacks;
} }
} // namespace llarp } // namespace llarp

@ -6,66 +6,65 @@
namespace llarp namespace llarp
{ {
void
BootstrapList::Clear()
{
clear();
}
bool bool
BootstrapList::BDecode(llarp_buffer_t* buf) BootstrapList::bt_decode(std::string_view buf)
{ {
return bencode_read_list( try
[&](llarp_buffer_t* b, bool more) -> bool { {
if (more) oxenc::bt_list_consumer btlc{buf};
{
RouterContact rc{}; while (not btlc.is_finished())
if (not rc.BDecode(b)) emplace(btlc.consume_dict_consumer());
{ }
LogError("invalid rc in bootstrap list: ", llarp::buffer_printer{*b}); catch (...)
return false; {
} log::warning(logcat, "Unable to decode bootstrap RemoteRC");
emplace(std::move(rc)); return false;
} }
return true;
}, return true;
buf);
} }
bool std::string_view
BootstrapList::BEncode(llarp_buffer_t* buf) const BootstrapList::bt_encode() const
{ {
return BEncodeWriteList(begin(), end(), buf); oxenc::bt_list_producer btlp{};
for (const auto& it : *this)
btlp.append(it.view());
return btlp.view();
} }
void void
BootstrapList::AddFromFile(fs::path fpath) BootstrapList::read_from_file(const fs::path& fpath)
{ {
bool isListFile = false; bool isListFile = false;
if (std::ifstream inf(fpath.c_str(), std::ios::binary); inf.is_open())
{ {
std::ifstream inf(fpath.c_str(), std::ios::binary); const char ch = inf.get();
if (inf.is_open()) isListFile = ch == 'l';
{
const char ch = inf.get();
isListFile = ch == 'l';
}
} }
if (isListFile) if (isListFile)
{ {
if (not BDecodeReadFile(fpath, *this)) auto content = util::file_to_string(fpath);
if (not bt_decode(content))
{ {
throw std::runtime_error{fmt::format("failed to read bootstrap list file '{}'", fpath)}; throw std::runtime_error{fmt::format("failed to read bootstrap list file '{}'", fpath)};
} }
} }
else else
{ {
RouterContact rc; RemoteRC rc;
if (not rc.Read(fpath)) if (not rc.read(fpath))
{ {
throw std::runtime_error{ throw std::runtime_error{
fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc)}; fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc.to_string())};
} }
this->insert(rc); insert(rc);
} }
} }
} // namespace llarp } // namespace llarp

@ -9,19 +9,22 @@
namespace llarp namespace llarp
{ {
struct BootstrapList final : public std::set<RouterContact> struct BootstrapList final : public std::set<RemoteRC>
{ {
bool bool
BDecode(llarp_buffer_t* buf); bt_decode(std::string_view buf);
bool std::string_view
BEncode(llarp_buffer_t* buf) const; bt_encode() const;
void void
AddFromFile(fs::path fpath); read_from_file(const fs::path& fpath);
void void
Clear(); clear_list()
{
clear();
}
}; };
std::unordered_map<std::string, BootstrapList> std::unordered_map<std::string, BootstrapList>

@ -10,17 +10,19 @@ namespace llarp
load_bootstrap_fallbacks() load_bootstrap_fallbacks()
{ {
std::unordered_map<std::string, BootstrapList> fallbacks; std::unordered_map<std::string, BootstrapList> fallbacks;
using init_list = std::initializer_list<std::pair<std::string, std::string_view>>;
// clang-format off for (const auto& [network, bootstrap] :
for (const auto& [network, bootstrap] : init_list{ std::initializer_list<std::pair<std::string, std::string_view>>{
// //
}) })
// clang-format on
{ {
llarp_buffer_t buf{bootstrap.data(), bootstrap.size()}; if (network != RouterContact::ACTIVE_NETID)
continue;
auto& bsl = fallbacks[network]; auto& bsl = fallbacks[network];
bsl.BDecode(&buf); bsl.bt_decode(bootstrap);
} }
return fallbacks; return fallbacks;
} }
} // namespace llarp } // namespace llarp

@ -58,14 +58,15 @@ namespace llarp
conf.defineOption<std::string>( conf.defineOption<std::string>(
"router", "router",
"netid", "netid",
Default{llarp::DEFAULT_NETID}, Default{llarp::LOKINET_DEFAULT_NETID},
Comment{ Comment{
"Network ID; this is '"s + llarp::DEFAULT_NETID + "' for mainnet, 'gamma' for testnet.", "Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID
+ "' for mainnet, 'gamma' for testnet.",
}, },
[this](std::string arg) { [this](std::string arg) {
if (arg.size() > NetID::size()) if (arg.size() > NETID_SIZE)
throw std::invalid_argument{ throw std::invalid_argument{
fmt::format("netid is too long, max length is {}", NetID::size())}; fmt::format("netid is too long, max length is {}", NETID_SIZE)};
m_netId = std::move(arg); m_netId = std::move(arg);
}); });
@ -1321,7 +1322,7 @@ namespace llarp
} }
bool bool
PeerSelectionConfig::Acceptable(const std::set<RouterContact>& rcs) const PeerSelectionConfig::Acceptable(const std::set<RemoteRC>& rcs) const
{ {
if (m_UniqueHopsNetmaskSize == 0) if (m_UniqueHopsNetmaskSize == 0)
return true; return true;
@ -1329,7 +1330,7 @@ namespace llarp
std::set<IPRange> seenRanges; std::set<IPRange> seenRanges;
for (const auto& hop : rcs) for (const auto& hop : rcs)
{ {
const auto network_addr = net::In6ToHUInt(hop.addr.in6().sin6_addr) & netmask; const auto network_addr = net::In6ToHUInt(hop.addr6()->in6().sin6_addr) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted) if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{ {
return false; return false;
@ -1444,7 +1445,7 @@ namespace llarp
{ {
try try
{ {
ini = util::slurp_file(*fname); ini = util::file_to_string(*fname);
} }
catch (const std::exception&) catch (const std::exception&)
{ {
@ -1529,7 +1530,7 @@ namespace llarp
// open a filestream // open a filestream
try try
{ {
util::dump_file(confFile, confStr); util::buffer_to_file(confFile, confStr);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -100,7 +100,7 @@ namespace llarp
/// return true if this set of router contacts is acceptable against this config /// return true if this set of router contacts is acceptable against this config
bool bool
Acceptable(const std::set<RouterContact>& hops) const; Acceptable(const std::set<RemoteRC>& hops) const;
}; };
struct NetworkConfig struct NetworkConfig

@ -15,7 +15,7 @@ namespace llarp
{ {
try try
{ {
m_Data = util::slurp_file(fname); m_Data = util::file_to_string(fname);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -51,8 +51,8 @@ namespace llarp
m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile); m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile);
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile); m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile);
RouterContact rc; RemoteRC rc;
bool exists = rc.Read(m_rcPath); bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent) if (not exists and not genIfAbsent)
{ {
LogError("Could not read RouterContact at path ", m_rcPath); LogError("Could not read RouterContact at path ", m_rcPath);
@ -61,7 +61,7 @@ namespace llarp
// we need to back up keys if our self.signed doesn't appear to have a // we need to back up keys if our self.signed doesn't appear to have a
// valid signature // valid signature
m_needBackup = (isSNode and not rc.VerifySignature()); m_needBackup = (isSNode and not rc.verify());
// if our RC file can't be verified, assume it is out of date (e.g. uses // if our RC file can't be verified, assume it is out of date (e.g. uses
// older encryption) and needs to be regenerated. before doing so, backup // older encryption) and needs to be regenerated. before doing so, backup

@ -4,12 +4,11 @@
namespace llarp namespace llarp
{ {
// clang-format off // clang-format off
const std::array<uint16_t, 3> VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; const std::array<uint16_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const std::array<uint64_t, 4> ROUTER_VERSION{{llarp::constants::proto_version, @lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; const char* const LOKINET_VERSION_TAG = "@VERSIONTAG@";
const char* const VERSION_TAG = "@VERSIONTAG@"; const char* const LOKINET_VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@LOKINET_VERSION_TAG@";
const char* const VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@VERSIONTAG@";
const char* const RELEASE_MOTTO = "@RELEASE_MOTTO@"; const char* const LOKINET_RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const DEFAULT_NETID = "lokinet"; const char* const LOKINET_DEFAULT_NETID = "lokinet";
// clang-format on // clang-format on
} // namespace llarp } // namespace llarp

@ -6,11 +6,10 @@
namespace llarp namespace llarp
{ {
// Given a full lokinet version of: lokinet-1.2.3-abc these are: // Given a full lokinet version of: lokinet-1.2.3-abc these are:
extern const std::array<uint16_t, 3> VERSION; // [1, 2, 3] extern const std::array<uint16_t, 3> LOKINET_VERSION;
extern const std::array<uint64_t, 4> ROUTER_VERSION; // [proto, 1, 2, 3] extern const char* const LOKINET_VERSION_TAG;
extern const char* const VERSION_TAG; // "abc" extern const char* const LOKINET_VERSION_FULL;
extern const char* const VERSION_FULL; // "lokinet-1.2.3-abc"
extern const char* const RELEASE_MOTTO; extern const char* const LOKINET_RELEASE_MOTTO;
extern const char* const DEFAULT_NETID; extern const char* const LOKINET_DEFAULT_NETID;
} // namespace llarp } // namespace llarp

@ -19,8 +19,6 @@
namespace llarp namespace llarp
{ {
static auto logcat = llarp::log::Cat("llarp-context");
bool bool
Context::CallSafe(std::function<void(void)> f) Context::CallSafe(std::function<void(void)> f)
{ {
@ -59,7 +57,8 @@ namespace llarp
throw std::runtime_error("Cannot call Setup() on context without a Config"); throw std::runtime_error("Cannot call Setup() on context without a Config");
if (opts.showBanner) if (opts.showBanner)
llarp::LogInfo(fmt::format("{} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); llarp::LogInfo(
fmt::format("{} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
if (!loop) if (!loop)
{ {
@ -162,7 +161,7 @@ namespace llarp
#ifndef _WIN32 #ifndef _WIN32
if (sig == SIGUSR1) if (sig == SIGUSR1)
{ {
if (router and not router->IsServiceNode()) if (router and not router->is_service_node())
{ {
LogInfo("SIGUSR1: resetting network state"); LogInfo("SIGUSR1: resetting network state");
router->Thaw(); router->Thaw();

@ -273,6 +273,14 @@ namespace llarp
return true; return true;
} }
bool
crypto::verify(const PubKey& pub, ustring_view data, ustring_view sig)
{
return (pub.size() == 32 && sig.size() == 64)
? crypto_sign_verify_detached(sig.data(), data.data(), data.size(), pub.data()) != -1
: false;
}
bool bool
crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig) crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig)
{ {
@ -428,11 +436,6 @@ namespace llarp
return true; return true;
} }
bool
crypto::seed_to_secretkey(llarp::SecretKey& secret, const llarp::IdentitySecret& seed)
{
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void void
crypto::randomize(uint8_t* buf, size_t len) crypto::randomize(uint8_t* buf, size_t len)
{ {
@ -517,19 +520,19 @@ namespace llarp
#endif #endif
const byte_t* const byte_t*
seckey_topublic(const SecretKey& sec) seckey_to_pubkey(const SecretKey& sec)
{ {
return sec.data() + 32; return sec.data() + 32;
} }
const byte_t* const byte_t*
pq_keypair_to_public(const PQKeyPair& k) pq_keypair_to_pubkey(const PQKeyPair& k)
{ {
return k.data() + PQ_SECRETKEYSIZE; return k.data() + PQ_SECRETKEYSIZE;
} }
const byte_t* const byte_t*
pq_keypair_to_secret(const PQKeyPair& k) pq_keypair_to_seckey(const PQKeyPair& k)
{ {
return k.data(); return k.data();
} }

@ -64,6 +64,8 @@ namespace llarp
sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size); sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size);
/// ed25519 verify /// ed25519 verify
bool bool
verify(const PubKey&, ustring_view, ustring_view);
bool
verify(const PubKey&, uint8_t*, size_t, const Signature&); verify(const PubKey&, uint8_t*, size_t, const Signature&);
bool verify(ustring_view, ustring_view, ustring_view); bool verify(ustring_view, ustring_view, ustring_view);
bool bool
@ -87,9 +89,6 @@ namespace llarp
uint64_t key_n, uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr); const AlignedBuffer<32>* hash = nullptr);
/// seed to secretkey
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&);
/// randomize buffer /// randomize buffer
void void
randomize(uint8_t* buf, size_t len); randomize(uint8_t* buf, size_t len);
@ -124,13 +123,13 @@ namespace llarp
randint(); randint();
const byte_t* const byte_t*
seckey_topublic(const SecretKey& secret); seckey_to_pubkey(const SecretKey& secret);
const byte_t* const byte_t*
pq_keypair_to_public(const PQKeyPair& keypair); pq_keypair_to_pubkey(const PQKeyPair& keypair);
const byte_t* const byte_t*
pq_keypair_to_secret(const PQKeyPair& keypair); pq_keypair_to_seckey(const PQKeyPair& keypair);
/// rng type that uses llarp::randint(), which is cryptographically secure /// rng type that uses llarp::randint(), which is cryptographically secure
struct CSRNG struct CSRNG

@ -1,5 +1,6 @@
#include "types.hpp" #include "types.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/util/file.hpp> #include <llarp/util/file.hpp>
@ -32,6 +33,30 @@ namespace llarp
return oxenc::to_hex(begin(), end()); return oxenc::to_hex(begin(), end());
} }
PubKey::operator RouterID() const
{
return {as_array()};
}
PubKey&
PubKey::operator=(const byte_t* ptr)
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
bool
operator==(const PubKey& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const PubKey& lhs, const RouterID& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool bool
SecretKey::LoadFromFile(const fs::path& fname) SecretKey::LoadFromFile(const fs::path& fname)
{ {
@ -39,7 +64,7 @@ namespace llarp
std::array<byte_t, 128> tmp; std::array<byte_t, 128> tmp;
try try
{ {
sz = util::slurp_file(fname, tmp.data(), tmp.size()); sz = util::file_to_buffer(fname, tmp.data(), tmp.size());
} }
catch (const std::exception&) catch (const std::exception&)
{ {
@ -93,67 +118,17 @@ namespace llarp
bool bool
SecretKey::SaveToFile(const fs::path& fname) const SecretKey::SaveToFile(const fs::path& fname) const
{ {
std::string tmp(128, 0); auto bte = bt_encode();
llarp_buffer_t buf(tmp);
if (!bt_encode(&buf))
return false;
tmp.resize(buf.cur - buf.base);
try try
{ {
util::dump_file(fname, tmp); util::buffer_to_file(fname, bte);
}
catch (const std::exception&)
{
return false;
}
return true;
}
bool
IdentitySecret::LoadFromFile(const fs::path& fname)
{
std::array<byte_t, SIZE> buf;
size_t sz;
try
{
sz = util::slurp_file(fname, buf.data(), buf.size());
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
llarp::LogError("failed to load service node seed: ", e.what());
return false; return false;
} }
if (sz != SIZE)
{
llarp::LogError("service node seed size invalid: ", sz, " != ", SIZE);
return false;
}
std::copy(buf.begin(), buf.end(), begin());
return true;
}
byte_t* return true;
Signature::Lo()
{
return data();
}
const byte_t*
Signature::Lo() const
{
return data();
}
byte_t*
Signature::Hi()
{
return data() + 32;
}
const byte_t*
Signature::Hi() const
{
return data() + 32;
} }
} // namespace llarp } // namespace llarp

@ -2,7 +2,6 @@
#include "constants.hpp" #include "constants.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/aligned.hpp> #include <llarp/util/aligned.hpp>
#include <llarp/util/fs.hpp> #include <llarp/util/fs.hpp>
#include <llarp/util/types.hpp> #include <llarp/util/types.hpp>
@ -15,7 +14,9 @@ namespace llarp
using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>; using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>;
using KeyExchangeNonce = AlignedBuffer<32>; using KeyExchangeNonce = AlignedBuffer<32>;
struct PubKey final : public AlignedBuffer<PUBKEYSIZE> struct RouterID;
struct PubKey : public AlignedBuffer<PUBKEYSIZE>
{ {
PubKey() = default; PubKey() = default;
@ -37,36 +38,17 @@ namespace llarp
static PubKey static PubKey
from_string(const std::string& s); from_string(const std::string& s);
operator RouterID() const operator RouterID() const;
{
return {as_array()};
}
PubKey& PubKey&
operator=(const byte_t* ptr) operator=(const byte_t* ptr);
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
}; };
inline bool bool
operator==(const PubKey& lhs, const PubKey& rhs) operator==(const PubKey& lhs, const PubKey& rhs);
{
return lhs.as_array() == rhs.as_array();
}
inline bool bool
operator==(const PubKey& lhs, const RouterID& rhs) operator==(const PubKey& lhs, const RouterID& rhs);
{
return lhs.as_array() == rhs.as_array();
}
inline bool
operator==(const RouterID& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
struct PrivateKey; struct PrivateKey;
@ -161,58 +143,24 @@ namespace llarp
toPublic(PubKey& pubkey) const; toPublic(PubKey& pubkey) const;
}; };
/// IdentitySecret is a secret key from a service node secret seed
struct IdentitySecret final : public AlignedBuffer<32>
{
IdentitySecret() : AlignedBuffer<32>()
{}
/// no copy constructor
explicit IdentitySecret(const IdentitySecret&) = delete;
// no byte data constructor
explicit IdentitySecret(const byte_t*) = delete;
/// load service node seed from file
bool
LoadFromFile(const fs::path& fname);
std::string_view
ToString() const
{
return "[IdentitySecret]";
}
};
template <> template <>
constexpr inline bool IsToStringFormattable<PubKey> = true; constexpr inline bool IsToStringFormattable<PubKey> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<SecretKey> = true; constexpr inline bool IsToStringFormattable<SecretKey> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<PrivateKey> = true; constexpr inline bool IsToStringFormattable<PrivateKey> = true;
template <>
constexpr inline bool IsToStringFormattable<IdentitySecret> = true;
using ShortHash = AlignedBuffer<SHORTHASHSIZE>; using ShortHash = AlignedBuffer<SHORTHASHSIZE>;
using LongHash = AlignedBuffer<HASHSIZE>; using LongHash = AlignedBuffer<HASHSIZE>;
struct Signature final : public AlignedBuffer<SIGSIZE> struct Signature final : public AlignedBuffer<SIGSIZE>
{ {
byte_t* //
Hi();
const byte_t*
Hi() const;
byte_t*
Lo();
const byte_t*
Lo() const;
}; };
using TunnelNonce = AlignedBuffer<TUNNONCESIZE>; using TunnelNonce = AlignedBuffer<TUNNONCESIZE>;
using SymmNonce = AlignedBuffer<NONCESIZE>; using SymmNonce = AlignedBuffer<NONCESIZE>;
using SymmKey = AlignedBuffer<32>; using SymmKey = AlignedBuffer<32>; // not used
using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>; using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>;
using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>; using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>;

@ -22,7 +22,7 @@ namespace llarp::dht
bool bool
operator()(const RouterContact& left, const RouterContact& right) const operator()(const RouterContact& left, const RouterContact& right) const
{ {
return (left.pubkey ^ us) < (right.pubkey ^ us); return (left.router_id() ^ us) < (right.router_id() ^ us);
} }
}; };
} // namespace llarp::dht } // namespace llarp::dht

@ -19,19 +19,19 @@ namespace llarp::dht
ID.Zero(); ID.Zero();
} }
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey) RCNode(const RouterContact& other) : rc(other), ID(other.router_id())
{} {}
util::StatusObject util::StatusObject
ExtractStatus() const ExtractStatus() const
{ {
return rc.ExtractStatus(); return rc.extract_status();
} }
bool bool
operator<(const RCNode& other) const operator<(const RCNode& other) const
{ {
return rc.last_updated < other.rc.last_updated; return rc.timestamp() < other.rc.timestamp();
} }
}; };

@ -69,13 +69,13 @@ namespace llarp::exit
snode_blacklist.insert(std::move(snode)); snode_blacklist.insert(std::move(snode));
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
BaseSession::GetHopsForBuild() BaseSession::GetHopsForBuild()
{ {
if (numHops == 1) if (numHops == 1)
{ {
if (auto maybe = router->node_db()->get_rc(exit_router)) if (auto maybe = router->node_db()->get_rc(exit_router))
return std::vector<RouterContact>{*maybe}; return std::vector<RemoteRC>{*maybe};
return std::nullopt; return std::nullopt;
} }
@ -292,7 +292,7 @@ namespace llarp::exit
throw; throw;
} }
RouterContact result{std::move(payload)}; RemoteRC result{std::move(payload)};
r->node_db()->put_rc_if_newer(result); r->node_db()->put_rc_if_newer(result);
r->connect_to(result); r->connect_to(result);
} }

@ -72,7 +72,7 @@ namespace llarp
bool bool
CheckPathDead(path::Path_ptr p, llarp_time_t dlt); CheckPathDead(path::Path_ptr p, llarp_time_t dlt);
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsForBuild() override; GetHopsForBuild() override;
bool bool

@ -578,29 +578,7 @@ namespace llarp::handlers
void void
ExitEndpoint::SRVRecordsChanged() ExitEndpoint::SRVRecordsChanged()
{ {
router->modify_rc( // TODO: Investigate the usage or the term exit RE: service nodes acting as exits
[srvRecords = SRVRecords()](RouterContact rc) -> std::optional<RouterContact> {
// check if there are any new srv records
bool shouldUpdate = false;
for (const auto& rcSrv : rc.srvRecords)
{
if (srvRecords.count(rcSrv) == 0)
shouldUpdate = true;
}
// no new records so don't modify
if (not shouldUpdate)
return std::nullopt;
// we got new entries so we clear the whole vector on the rc and recreate it
rc.srvRecords.clear();
for (auto& record : srvRecords)
rc.srvRecords.emplace_back(record);
// set the verssion to 1 because we have srv records
rc.version = 1;
return rc;
});
} }
std::optional<EndpointBase::SendStat> std::optional<EndpointBase::SendStat>

@ -623,7 +623,7 @@ namespace llarp::handlers
throw; throw;
} }
r->node_db()->put_rc_if_newer(RouterContact{payload}); r->node_db()->put_rc_if_newer(RemoteRC{payload});
msg.AddTXTReply(payload); msg.AddTXTReply(payload);
} }
else else
@ -658,7 +658,7 @@ namespace llarp::handlers
} }
else if (subdomain == "netid") else if (subdomain == "netid")
{ {
msg.AddTXTReply(fmt::format("netid={};", router()->rc().netID)); msg.AddTXTReply(fmt::format("netid={};", RouterContact::ACTIVE_NETID));
} }
else else
{ {

@ -5,7 +5,7 @@ namespace llarp::link
Connection::Connection( Connection::Connection(
std::shared_ptr<oxen::quic::connection_interface>& c, std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s, std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RouterContact& rc) const RemoteRC& rc)
: conn{c}, control_stream{s}, remote_rc{std::move(rc)} : conn{c}, control_stream{s}, remote_rc{std::move(rc)}
{} {}

@ -11,7 +11,7 @@ namespace llarp::link
{ {
std::shared_ptr<oxen::quic::connection_interface> conn; std::shared_ptr<oxen::quic::connection_interface> conn;
std::shared_ptr<oxen::quic::BTRequestStream> control_stream; std::shared_ptr<oxen::quic::BTRequestStream> control_stream;
RouterContact remote_rc; RemoteRC remote_rc;
// one side of a connection will be responsible for some things, e.g. heartbeat // one side of a connection will be responsible for some things, e.g. heartbeat
bool inbound{false}; bool inbound{false};
@ -20,7 +20,7 @@ namespace llarp::link
Connection( Connection(
std::shared_ptr<oxen::quic::connection_interface>& c, std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s, std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RouterContact& rc); const RemoteRC& rc);
}; };
} // namespace llarp::link } // namespace llarp::link

@ -39,7 +39,7 @@ namespace llarp
while (itr != nodes.end()) while (itr != nodes.end())
{ {
if (itr->second.rc.IsExpired(now)) if (itr->second.rc.is_expired(now))
itr = nodes.erase(itr); itr = nodes.erase(itr);
else else
++itr; ++itr;

@ -18,9 +18,9 @@ namespace llarp
namespace link namespace link
{ {
std::shared_ptr<link::Connection> std::shared_ptr<link::Connection>
Endpoint::get_conn(const RouterContact& rc) const Endpoint::get_conn(const RemoteRC& rc) const
{ {
if (auto itr = conns.find(rc.pubkey); itr != conns.end()) if (auto itr = conns.find(rc.router_id()); itr != conns.end())
return itr->second; return itr->second;
return nullptr; return nullptr;
@ -83,7 +83,7 @@ namespace llarp
} }
bool bool
Endpoint::get_random_connection(RouterContact& router) const Endpoint::get_random_connection(RemoteRC& router) const
{ {
if (const auto size = conns.size(); size) if (const auto size = conns.size(); size)
{ {
@ -301,16 +301,16 @@ namespace llarp
// This function assumes the RC has already had its signature verified and connection is allowed. // This function assumes the RC has already had its signature verified and connection is allowed.
void void
LinkManager::connect_to(const RouterContact& rc) LinkManager::connect_to(const RemoteRC& rc)
{ {
if (auto conn = ep.get_conn(rc.pubkey); conn) if (auto conn = ep.get_conn(rc.router_id()); conn)
{ {
// TODO: should implement some connection failed logic, but not the same logic that // TODO: should implement some connection failed logic, but not the same logic that
// would be executed for another failure case // would be executed for another failure case
return; return;
} }
auto& remote_addr = rc.addr; const auto& remote_addr = rc.addr();
// TODO: confirm remote end is using the expected pubkey (RouterID). // TODO: confirm remote end is using the expected pubkey (RouterID).
// TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation) // TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation)
@ -451,7 +451,7 @@ namespace llarp
} }
bool bool
LinkManager::get_random_connected(RouterContact& router) const LinkManager::get_random_connected(RemoteRC& router) const
{ {
return ep.get_random_connection(router); return ep.get_random_connection(router);
} }
@ -487,13 +487,15 @@ namespace llarp
do do
{ {
auto filter = [exclude](const auto& rc) -> bool { return exclude.count(rc.pubkey) == 0; }; auto filter = [exclude](const auto& rc) -> bool {
return exclude.count(rc.router_id()) == 0;
};
if (auto maybe_other = node_db->GetRandom(filter)) if (auto maybe_other = node_db->GetRandom(filter))
{ {
exclude.insert(maybe_other->pubkey); exclude.insert(maybe_other->router_id());
if (not rc_lookup->is_session_allowed(maybe_other->pubkey)) if (not rc_lookup->is_session_allowed(maybe_other->router_id()))
continue; continue;
connect_to(*maybe_other); connect_to(*maybe_other);
@ -609,18 +611,19 @@ namespace llarp
target_rid.FromString(target_key); target_rid.FromString(target_key);
const auto target_addr = dht::Key_t{reinterpret_cast<uint8_t*>(target_key.data())}; const auto target_addr = dht::Key_t{reinterpret_cast<uint8_t*>(target_key.data())};
const auto& local_rid = _router.rc().pubkey; const auto& local_rid = _router.rc().router_id();
const auto local_key = dht::Key_t{local_rid}; const auto local_key = dht::Key_t{local_rid};
if (is_exploratory) if (is_exploratory)
{ {
std::string neighbors{}; std::string neighbors{};
const auto closest_rcs = const auto closest_rcs =
_router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY); _router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY);
for (const auto& rc : closest_rcs) for (const auto& rc : closest_rcs)
{ {
const auto& rid = rc.pubkey; const auto& rid = rc.router_id();
if (_router.router_profiling().IsBadForConnect(rid) || target_rid == rid if (_router.router_profiling().IsBadForConnect(rid) || target_rid == rid
|| local_rid == rid) || local_rid == rid)
continue; continue;
@ -635,12 +638,12 @@ namespace llarp
else else
{ {
const auto closest_rc = _router.node_db()->find_closest_to(target_addr); const auto closest_rc = _router.node_db()->find_closest_to(target_addr);
const auto& closest_rid = closest_rc.pubkey; const auto& closest_rid = closest_rc.router_id();
const auto closest_key = dht::Key_t{closest_rid}; const auto closest_key = dht::Key_t{closest_rid};
if (target_addr == closest_key) if (target_addr == closest_key)
{ {
if (closest_rc.ExpiresSoon(llarp::time_now_ms())) if (closest_rc.expires_within_delta(llarp::time_now_ms()))
{ {
send_control_message( send_control_message(
target_rid, target_rid,
@ -652,7 +655,7 @@ namespace llarp
} }
else else
{ {
m.respond(serialize_response({{"RC", closest_rc.bt_encode()}})); m.respond(serialize_response({{"RC", closest_rc.view()}}));
} }
} }
else if (not is_iterative) else if (not is_iterative)
@ -718,7 +721,7 @@ namespace llarp
if (m) if (m)
{ {
_router.node_db()->put_rc_if_newer(RouterContact{payload}); _router.node_db()->put_rc_if_newer(RemoteRC{payload});
} }
else else
{ {
@ -837,7 +840,7 @@ namespace llarp
const auto now = _router.now(); const auto now = _router.now();
const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())}; const auto addr = dht::Key_t{reinterpret_cast<uint8_t*>(derived_signing_key.data())};
const auto local_key = _router.rc().pubkey; const auto local_key = _router.rc().router_id();
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig)) if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
{ {
@ -878,7 +881,7 @@ namespace llarp
log::info(link_cat, "Relaying PublishIntroMessage for {}", addr); log::info(link_cat, "Relaying PublishIntroMessage for {}", addr);
const auto& peer_rc = closest_rcs[relay_order]; const auto& peer_rc = closest_rcs[relay_order];
const auto& peer_key = peer_rc.pubkey; const auto& peer_key = peer_rc.router_id();
if (peer_key == local_key) if (peer_key == local_key)
{ {
@ -908,7 +911,7 @@ namespace llarp
for (const auto& rc : closest_rcs) for (const auto& rc : closest_rcs)
{ {
if (rc.pubkey == local_key) if (rc.router_id() == local_key)
{ {
rc_index = index; rc_index = index;
break; break;
@ -1024,7 +1027,7 @@ namespace llarp
log::info(link_cat, "Relaying FindIntroMessage for {}", addr); log::info(link_cat, "Relaying FindIntroMessage for {}", addr);
const auto& peer_rc = closest_rcs[relay_order]; const auto& peer_rc = closest_rcs[relay_order];
const auto& peer_key = peer_rc.pubkey; const auto& peer_key = peer_rc.router_id();
send_control_message( send_control_message(
peer_key, peer_key,

@ -52,7 +52,8 @@ namespace llarp
// TODO: see which of these is actually useful and delete the other // TODO: see which of these is actually useful and delete the other
std::shared_ptr<link::Connection> std::shared_ptr<link::Connection>
get_conn(const RouterContact&) const; get_conn(const RemoteRC&) const;
std::shared_ptr<link::Connection> std::shared_ptr<link::Connection>
get_conn(const RouterID&) const; get_conn(const RouterID&) const;
@ -66,12 +67,11 @@ namespace llarp
num_connected(bool clients_only) const; num_connected(bool clients_only) const;
bool bool
get_random_connection(RouterContact& router) const; get_random_connection(RemoteRC& router) const;
template <typename... Opt> template <typename... Opt>
bool bool
establish_connection( establish_connection(const oxen::quic::Address& remote, const RemoteRC& rc, Opt&&... opts);
const oxen::quic::Address& remote, const RouterContact& rc, Opt&&... opts);
void void
for_each_connection(std::function<void(link::Connection&)> func); for_each_connection(std::function<void(link::Connection&)> func);
@ -239,7 +239,7 @@ namespace llarp
connect_to(const RouterID& router); connect_to(const RouterID& router);
void void
connect_to(const RouterContact& rc); connect_to(const RemoteRC& rc);
void void
close_connection(RouterID rid); close_connection(RouterID rid);
@ -257,7 +257,7 @@ namespace llarp
get_num_connected_clients() const; get_num_connected_clients() const;
bool bool
get_random_connected(RouterContact& router) const; get_random_connected(RemoteRC& router) const;
void void
check_persisting_conns(llarp_time_t now); check_persisting_conns(llarp_time_t now);
@ -364,7 +364,7 @@ namespace llarp
template <typename... Opt> template <typename... Opt>
bool bool
Endpoint::establish_connection( Endpoint::establish_connection(
const oxen::quic::Address& remote, const RouterContact& rc, Opt&&... opts) const oxen::quic::Address& remote, const RemoteRC& rc, Opt&&... opts)
{ {
try try
{ {
@ -372,8 +372,8 @@ namespace llarp
endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...); endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...);
// emplace immediately for connection open callback to find scid // emplace immediately for connection open callback to find scid
connid_map.emplace(conn_interface->scid(), rc.pubkey); connid_map.emplace(conn_interface->scid(), rc.router_id());
auto [itr, b] = conns.emplace(rc.pubkey, nullptr); auto [itr, b] = conns.emplace(rc.router_id(), nullptr);
auto control_stream = auto control_stream =
conn_interface->template get_new_stream<oxen::quic::BTRequestStream>(); conn_interface->template get_new_stream<oxen::quic::BTRequestStream>();

@ -22,7 +22,7 @@ namespace llarp
hop.nonce.Randomize(); hop.nonce.Randomize();
// do key exchange // do key exchange
if (!crypto::dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce)) if (!crypto::dh_client(hop.shared, hop.rc.router_id(), hop.commkey, hop.nonce))
{ {
auto err = fmt::format("Failed to generate shared key for path build!"); auto err = fmt::format("Failed to generate shared key for path build!");
log::warning(path_cat, err); log::warning(path_cat, err);
@ -60,7 +60,7 @@ namespace llarp
outer_nonce.Randomize(); outer_nonce.Randomize();
// derive (outer) shared key // derive (outer) shared key
if (!crypto::dh_client(shared, hop.rc.pubkey, framekey, outer_nonce)) if (!crypto::dh_client(shared, hop.rc.router_id(), framekey, outer_nonce))
{ {
log::warning(path_cat, "DH client failed during hop info encryption!"); log::warning(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"}; throw std::runtime_error{"DH failed during hop info encryption"};

@ -54,7 +54,7 @@ namespace llarp
bool bool
RelayUpstreamMessage::handle_message(Router* r) const RelayUpstreamMessage::handle_message(Router* r) const
{ {
auto path = r->path_context().GetByDownstream(conn->remote_rc.pubkey, pathid); auto path = r->path_context().GetByDownstream(conn->remote_rc.router_id(), pathid);
if (path) if (path)
{ {
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r); return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
@ -110,7 +110,7 @@ namespace llarp
bool bool
RelayDownstreamMessage::handle_message(Router* r) const RelayDownstreamMessage::handle_message(Router* r) const
{ {
auto path = r->path_context().GetByUpstream(conn->remote_rc.pubkey, pathid); auto path = r->path_context().GetByUpstream(conn->remote_rc.router_id(), pathid);
if (path) if (path)
{ {
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r); return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);

@ -1,121 +0,0 @@
#ifndef _WIN32
#include <arpa/inet.h>
#endif
#include "exit_info.hpp"
#include <llarp/util/bencode.h>
#include <cstring>
namespace llarp
{
bool
ExitInfo::bt_encode(llarp_buffer_t* buf) const
{
SockAddr exitaddr = ipAddress.createSockAddr();
const auto* exitaddr6 = static_cast<const sockaddr_in6*>(exitaddr);
SockAddr netmaskaddr = netmask.createSockAddr();
const auto* netmaskaddr6 = static_cast<const sockaddr_in6*>(netmaskaddr);
char tmp[128] = {0};
if (!bencode_start_dict(buf))
return false;
if (!inet_ntop(AF_INET6, &exitaddr6->sin6_addr, tmp, sizeof(tmp)))
return false;
if (!BEncodeWriteDictString("a", std::string(tmp), buf))
return false;
if (!inet_ntop(AF_INET6, &netmaskaddr6->sin6_addr, tmp, sizeof(tmp)))
return false;
if (!BEncodeWriteDictString("b", std::string(tmp), buf))
return false;
if (!BEncodeWriteDictEntry("k", pubkey, buf))
return false;
if (!BEncodeWriteDictInt("v", version, buf))
return false;
return bencode_end(buf);
}
static bool
bdecode_ip_string(llarp_buffer_t* buf, in6_addr& ip)
{
char tmp[128] = {0};
llarp_buffer_t strbuf;
if (!bencode_read_string(buf, &strbuf))
return false;
if (strbuf.sz >= sizeof(tmp))
return false;
memcpy(tmp, strbuf.base, strbuf.sz);
tmp[strbuf.sz] = 0;
return inet_pton(AF_INET6, tmp, &ip.s6_addr[0]) == 1;
}
bool
ExitInfo::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("k", pubkey, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("v", version, read, k, buf))
return false;
if (k.startswith("a"))
{
in6_addr tmp;
if (not bdecode_ip_string(buf, tmp))
return false;
SockAddr addr(tmp);
ipAddress = IpAddress(addr);
return true;
}
if (k.startswith("b"))
{
in6_addr tmp;
if (not bdecode_ip_string(buf, tmp))
return false;
SockAddr addr(tmp);
netmask = IpAddress(addr);
return true;
}
return read;
}
std::string
ExitInfo::ToString() const
{
/*
// TODO: derive these from ipAdress
throw std::runtime_error("FIXME: need in6_addr and netmask from IpAddress");
in6_addr address;
in6_addr netmask;
Printer printer(stream, level, spaces);
std::ostringstream ss;
char tmp[128] = {0};
if (inet_ntop(AF_INET6, (void*)&address, tmp, sizeof(tmp)))
ss << tmp;
else
return stream;
ss << std::string("/");
#if defined(ANDROID)
snprintf(tmp, sizeof(tmp), "%zu", llarp::bits::count_array_bits(netmask.s6_addr));
ss << tmp;
#else
ss << std::to_string(llarp::bits::count_array_bits(netmask.s6_addr));
#endif
printer.printValue(ss.str());
*/
return fmt::format("[Exit {}]", ipAddress.ToString());
}
} // namespace llarp

@ -1,51 +0,0 @@
#pragma once
#include "ip_address.hpp"
#include <llarp/crypto/types.hpp>
#include <llarp/util/bencode.hpp>
#include <iosfwd>
/**
* exit_info.h
*
* utilities for handling exits on the llarp network
*/
/// Exit info model
namespace llarp
{
/// deprecated don't use me , this is only for backwards compat
struct ExitInfo
{
IpAddress ipAddress;
IpAddress netmask;
PubKey pubkey;
uint64_t version = llarp::constants::proto_version;
ExitInfo() = default;
ExitInfo(const PubKey& pk, const IpAddress& address) : ipAddress(address), pubkey(pk)
{}
bool
bt_encode(llarp_buffer_t* buf) const;
bool
BDecode(llarp_buffer_t* buf)
{
return bencode_decode_dict(*this, buf);
}
bool
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
std::string
ToString() const;
};
template <>
constexpr inline bool IsToStringFormattable<ExitInfo> = true;
} // namespace llarp

@ -14,9 +14,7 @@ static const std::string RC_FILE_EXT = ".signed";
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("nodedb"); NodeDB::Entry::Entry(RemoteRC value) : rc(std::move(value)), insertedAt(llarp::time_now_ms())
NodeDB::Entry::Entry(RouterContact value) : rc(std::move(value)), insertedAt(llarp::time_now_ms())
{} {}
static void static void
@ -72,14 +70,16 @@ namespace llarp
router.loop()->call([this]() { router.loop()->call([this]() {
m_NextFlushAt += FlushInterval; m_NextFlushAt += FlushInterval;
// make copy of all rcs // make copy of all rcs
std::vector<RouterContact> copy; std::vector<RemoteRC> copy;
for (const auto& item : entries) for (const auto& item : entries)
copy.push_back(item.second.rc); copy.push_back(item.second.rc);
// flush them to disk in one big job // flush them to disk in one big job
// TODO: split this up? idk maybe some day... // TODO: split this up? idk maybe some day...
disk([this, data = std::move(copy)]() { disk([this, data = std::move(copy)]() {
for (const auto& rc : data) for (const auto& rc : data)
rc.Write(get_path_by_pubkey(rc.pubkey)); rc.write(get_path_by_pubkey(rc.router_id()));
}); });
}); });
} }
@ -121,22 +121,16 @@ namespace llarp
if (not(fs::is_regular_file(f) and f.extension() == RC_FILE_EXT)) if (not(fs::is_regular_file(f) and f.extension() == RC_FILE_EXT))
return true; return true;
RouterContact rc{}; RemoteRC rc{};
if (not rc.Read(f)) if (not rc.read(f))
{ {
// try loading it, purge it if it is junk // try loading it, purge it if it is junk
purge.emplace(f); purge.emplace(f);
return true; return true;
} }
if (not rc.FromOurNetwork()) if (rc.is_expired(time_now_ms()))
{
// skip entries that are not from our network
return true;
}
if (rc.IsExpired(time_now_ms()))
{ {
// rc expired dont load it and purge it later // rc expired dont load it and purge it later
purge.emplace(f); purge.emplace(f);
@ -145,8 +139,8 @@ namespace llarp
// validate signature and purge entries with invalid signatures // validate signature and purge entries with invalid signatures
// load ones with valid signatures // load ones with valid signatures
if (rc.VerifySignature()) if (rc.verify())
entries.emplace(rc.pubkey, rc); entries.emplace(rc.router_id(), rc);
else else
purge.emplace(f); purge.emplace(f);
@ -172,7 +166,7 @@ namespace llarp
router.loop()->call([this]() { router.loop()->call([this]() {
for (const auto& item : entries) for (const auto& item : entries)
item.second.rc.Write(get_path_by_pubkey(item.first)); item.second.rc.write(get_path_by_pubkey(item.first));
}); });
} }
@ -183,10 +177,10 @@ namespace llarp
[this, pk]() -> bool { return entries.find(pk) != entries.end(); }); [this, pk]() -> bool { return entries.find(pk) != entries.end(); });
} }
std::optional<RouterContact> std::optional<RemoteRC>
NodeDB::get_rc(RouterID pk) const NodeDB::get_rc(RouterID pk) const
{ {
return router.loop()->call_get([this, pk]() -> std::optional<RouterContact> { return router.loop()->call_get([this, pk]() -> std::optional<RemoteRC> {
const auto itr = entries.find(pk); const auto itr = entries.find(pk);
if (itr == entries.end()) if (itr == entries.end())
@ -213,9 +207,9 @@ namespace llarp
auto itr = entries.begin(); auto itr = entries.begin();
while (itr != entries.end()) while (itr != entries.end())
{ {
if (itr->second.insertedAt < cutoff and keep.count(itr->second.rc.pubkey) == 0) if (itr->second.insertedAt < cutoff and keep.count(itr->second.rc.router_id()) == 0)
{ {
removed.insert(itr->second.rc.pubkey); removed.insert(itr->second.rc.router_id());
itr = entries.erase(itr); itr = entries.erase(itr);
} }
else else
@ -227,11 +221,12 @@ namespace llarp
} }
void void
NodeDB::put_rc(RouterContact rc) NodeDB::put_rc(RemoteRC rc)
{ {
router.loop()->call([this, rc]() { router.loop()->call([this, rc]() {
entries.erase(rc.pubkey); const auto& rid = rc.router_id();
entries.emplace(rc.pubkey, rc); entries.erase(rid);
entries.emplace(rid, rc);
}); });
} }
@ -242,17 +237,17 @@ namespace llarp
} }
void void
NodeDB::put_rc_if_newer(RouterContact rc) NodeDB::put_rc_if_newer(RemoteRC rc)
{ {
router.loop()->call([this, rc]() { router.loop()->call([this, rc]() {
auto itr = entries.find(rc.pubkey); auto itr = entries.find(rc.router_id());
if (itr == entries.end() or itr->second.rc.OtherIsNewer(rc)) if (itr == entries.end() or itr->second.rc.other_is_newer(rc))
{ {
// delete if existing // delete if existing
if (itr != entries.end()) if (itr != entries.end())
entries.erase(itr); entries.erase(itr);
// add new entry // add new entry
entries.emplace(rc.pubkey, rc); entries.emplace(rc.router_id(), rc);
} }
}); });
} }
@ -275,32 +270,31 @@ namespace llarp
}); });
} }
llarp::RouterContact RemoteRC
NodeDB::find_closest_to(llarp::dht::Key_t location) const NodeDB::find_closest_to(llarp::dht::Key_t location) const
{ {
return router.loop()->call_get([this, location]() { return router.loop()->call_get([this, location]() -> RemoteRC {
llarp::RouterContact rc; RemoteRC rc;
const llarp::dht::XorMetric compare(location); const llarp::dht::XorMetric compare(location);
VisitAll([&rc, compare](const auto& otherRC) { VisitAll([&rc, compare](const auto& otherRC) {
if (rc.pubkey.IsZero()) const auto& rid = rc.router_id();
if (rid.IsZero() || compare(dht::Key_t{otherRC.router_id()}, dht::Key_t{rid}))
{ {
rc = otherRC; rc = otherRC;
return; return;
} }
if (compare(
llarp::dht::Key_t{otherRC.pubkey.as_array()},
llarp::dht::Key_t{rc.pubkey.as_array()}))
rc = otherRC;
}); });
return rc; return rc;
}); });
} }
std::vector<RouterContact> std::vector<RemoteRC>
NodeDB::find_many_closest_to(llarp::dht::Key_t location, uint32_t numRouters) const NodeDB::find_many_closest_to(llarp::dht::Key_t location, uint32_t numRouters) const
{ {
return router.loop()->call_get([this, location, numRouters]() { return router.loop()->call_get([this, location, numRouters]() -> std::vector<RemoteRC> {
std::vector<const RouterContact*> all; std::vector<const RemoteRC*> all;
all.reserve(entries.size()); all.reserve(entries.size());
for (auto& entry : entries) for (auto& entry : entries)
@ -314,7 +308,7 @@ namespace llarp
return compare(*a, *b); return compare(*a, *b);
}); });
std::vector<RouterContact> closest; std::vector<RemoteRC> closest;
closest.reserve(numRouters); closest.reserve(numRouters);
for (auto it = all.begin(); it != it_mid; ++it) for (auto it = all.begin(); it != it_mid; ++it)
closest.push_back(**it); closest.push_back(**it);

@ -26,9 +26,9 @@ namespace llarp
{ {
struct Entry struct Entry
{ {
const RouterContact rc; const RemoteRC rc;
llarp_time_t insertedAt; llarp_time_t insertedAt;
explicit Entry(RouterContact rc); explicit Entry(RemoteRC rc);
}; };
using NodeMap = std::unordered_map<RouterID, Entry>; using NodeMap = std::unordered_map<RouterID, Entry>;
@ -73,11 +73,11 @@ namespace llarp
Tick(llarp_time_t now); Tick(llarp_time_t now);
/// find the absolute closets router to a dht location /// find the absolute closets router to a dht location
RouterContact RemoteRC
find_closest_to(dht::Key_t location) const; find_closest_to(dht::Key_t location) const;
/// find many routers closest to dht key /// find many routers closest to dht key
std::vector<RouterContact> std::vector<RemoteRC>
find_many_closest_to(dht::Key_t location, uint32_t numRouters) const; find_many_closest_to(dht::Key_t location, uint32_t numRouters) const;
/// return true if we have an rc by its ident pubkey /// return true if we have an rc by its ident pubkey
@ -85,14 +85,14 @@ namespace llarp
has_router(RouterID pk) const; has_router(RouterID pk) const;
/// maybe get an rc by its ident pubkey /// maybe get an rc by its ident pubkey
std::optional<RouterContact> std::optional<RemoteRC>
get_rc(RouterID pk) const; get_rc(RouterID pk) const;
template <typename Filter> template <typename Filter>
std::optional<RouterContact> std::optional<RemoteRC>
GetRandom(Filter visit) const GetRandom(Filter visit) const
{ {
return router.loop()->call_get([visit]() -> std::optional<RouterContact> { return router.loop()->call_get([visit]() -> std::optional<RemoteRC> {
std::vector<const decltype(entries)::value_type*> entries; std::vector<const decltype(entries)::value_type*> entries;
for (const auto& entry : entries) for (const auto& entry : entries)
entries.push_back(entry); entries.push_back(entry);
@ -150,7 +150,7 @@ namespace llarp
{ {
if (visit(itr->second.rc)) if (visit(itr->second.rc))
{ {
removed.insert(itr->second.rc.pubkey); removed.insert(itr->second.rc.router_id());
itr = entries.erase(itr); itr = entries.erase(itr);
} }
else else
@ -167,10 +167,10 @@ namespace llarp
/// put this rc into the cache if it is not there or newer than the one there already /// put this rc into the cache if it is not there or newer than the one there already
void void
put_rc_if_newer(RouterContact rc); put_rc_if_newer(RemoteRC rc);
/// unconditional put of rc into cache /// unconditional put of rc into cache
void void
put_rc(RouterContact rc); put_rc(RemoteRC rc);
}; };
} // namespace llarp } // namespace llarp

@ -10,7 +10,7 @@ namespace llarp::path
{ {
Path::Path( Path::Path(
Router* rtr, Router* rtr,
const std::vector<RouterContact>& h, const std::vector<RemoteRC>& h,
std::weak_ptr<PathSet> pathset, std::weak_ptr<PathSet> pathset,
PathRole startingRoles, PathRole startingRoles,
std::string shortName) std::string shortName)
@ -40,7 +40,7 @@ namespace llarp::path
hops[idx].txID = hops[idx + 1].rxID; hops[idx].txID = hops[idx + 1].rxID;
} }
// initialize parts of the introduction // initialize parts of the introduction
intro.router = hops[hsz - 1].rc.pubkey; intro.router = hops[hsz - 1].rc.router_id();
intro.path_id = hops[hsz - 1].txID; intro.path_id = hops[hsz - 1].txID;
if (auto parent = m_PathSet.lock()) if (auto parent = m_PathSet.lock())
EnterState(ePathBuilding, parent->Now()); EnterState(ePathBuilding, parent->Now());
@ -152,13 +152,13 @@ namespace llarp::path
RouterID RouterID
Path::Endpoint() const Path::Endpoint() const
{ {
return hops[hops.size() - 1].rc.pubkey; return hops[hops.size() - 1].rc.router_id();
} }
PubKey PubKey
Path::EndpointPubKey() const Path::EndpointPubKey() const
{ {
return hops[hops.size() - 1].rc.pubkey; return hops[hops.size() - 1].rc.router_id();
} }
PathID_t PathID_t
@ -184,13 +184,13 @@ namespace llarp::path
bool bool
Path::is_endpoint(const RouterID& r, const PathID_t& id) const Path::is_endpoint(const RouterID& r, const PathID_t& id) const
{ {
return hops[hops.size() - 1].rc.pubkey == r && hops[hops.size() - 1].txID == id; return hops[hops.size() - 1].rc.router_id() == r && hops[hops.size() - 1].txID == id;
} }
RouterID RouterID
Path::upstream() const Path::upstream() const
{ {
return hops[0].rc.pubkey; return hops[0].rc.router_id();
} }
const std::string& const std::string&
@ -208,7 +208,7 @@ namespace llarp::path
{ {
if (!hops.empty()) if (!hops.empty())
hops_str += " -> "; hops_str += " -> ";
hops_str += RouterID(hop.rc.pubkey).ToString(); hops_str += hop.rc.router_id().ToView();
} }
return hops_str; return hops_str;
} }
@ -262,9 +262,9 @@ namespace llarp::path
PathHopConfig::ExtractStatus() const PathHopConfig::ExtractStatus() const
{ {
util::StatusObject obj{ util::StatusObject obj{
{"ip", rc.addr.to_string()}, {"ip", rc.addr().to_string()},
{"lifetime", to_json(lifetime)}, {"lifetime", to_json(lifetime)},
{"router", rc.pubkey.ToHex()}, {"router", rc.router_id().ToHex()},
{"txid", txID.ToHex()}, {"txid", txID.ToHex()},
{"rxid", rxID.ToHex()}}; {"rxid", rxID.ToHex()}};
return obj; return obj;
@ -330,11 +330,13 @@ namespace llarp::path
{ {
if (auto parent = m_PathSet.lock()) if (auto parent = m_PathSet.lock())
{ {
std::vector<RouterContact> newHops; std::vector<RemoteRC> new_hops;
for (const auto& hop : hops) for (const auto& hop : hops)
newHops.emplace_back(hop.rc); new_hops.emplace_back(hop.rc);
LogInfo(name(), " rebuilding on ", ShortName()); LogInfo(name(), " rebuilding on ", ShortName());
parent->Build(newHops); parent->Build(new_hops);
} }
} }

@ -69,7 +69,7 @@ namespace llarp
Path( Path(
Router* rtr, Router* rtr,
const std::vector<RouterContact>& routers, const std::vector<RemoteRC>& routers,
std::weak_ptr<PathSet> parent, std::weak_ptr<PathSet> parent,
PathRole startingRoles, PathRole startingRoles,
std::string shortName); std::string shortName);

@ -26,7 +26,7 @@ namespace llarp
/// path id /// path id
PathID_t txID, rxID; PathID_t txID, rxID;
// router contact of router // router contact of router
RouterContact rc; RemoteRC rc;
// temp public encryption key // temp public encryption key
SecretKey commkey; SecretKey commkey;
/// shared secret at this hop /// shared secret at this hop

@ -83,7 +83,7 @@ namespace llarp
hop.nonce.Randomize(); hop.nonce.Randomize();
// do key exchange // do key exchange
if (!crypto::dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce)) if (!crypto::dh_client(hop.shared, hop.rc.router_id(), hop.commkey, hop.nonce))
{ {
auto err = fmt::format("{} failed to generate shared key for path build!", Name()); auto err = fmt::format("{} failed to generate shared key for path build!", Name());
log::error(path_cat, err); log::error(path_cat, err);
@ -121,7 +121,7 @@ namespace llarp
outer_nonce.Randomize(); outer_nonce.Randomize();
// derive (outer) shared key // derive (outer) shared key
if (!crypto::dh_client(shared, hop.rc.pubkey, framekey, outer_nonce)) if (!crypto::dh_client(shared, hop.rc.router_id(), framekey, outer_nonce))
{ {
log::error(path_cat, "DH client failed during hop info encryption!"); log::error(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"}; throw std::runtime_error{"DH failed during hop info encryption"};
@ -211,23 +211,25 @@ namespace llarp
return obj; return obj;
} }
std::optional<RouterContact> std::optional<RemoteRC>
Builder::SelectFirstHop(const std::set<RouterID>& exclude) const Builder::SelectFirstHop(const std::set<RouterID>& exclude) const
{ {
std::optional<RouterContact> found = std::nullopt; std::optional<RemoteRC> found = std::nullopt;
router->for_each_connection([&](link::Connection& conn) { router->for_each_connection([&](link::Connection& conn) {
const auto& rc = conn.remote_rc; const auto& rc = conn.remote_rc;
const auto& rid = rc.router_id();
#ifndef TESTNET #ifndef TESTNET
if (router->IsBootstrapNode(rc.pubkey)) if (router->IsBootstrapNode(rid))
return; return;
#endif #endif
if (exclude.count(rc.pubkey)) if (exclude.count(rid))
return; return;
if (BuildCooldownHit(rc.pubkey)) if (BuildCooldownHit(rid))
return; return;
if (router->router_profiling().IsBadForPath(rc.pubkey)) if (router->router_profiling().IsBadForPath(rid))
return; return;
found = rc; found = rc;
@ -235,15 +237,15 @@ namespace llarp
return found; return found;
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
Builder::GetHopsForBuild() Builder::GetHopsForBuild()
{ {
auto filter = [r = router](const auto& rc) -> bool { auto filter = [r = router](const auto& rc) -> bool {
return not r->router_profiling().IsBadForPath(rc.pubkey, 1); return not r->router_profiling().IsBadForPath(rc.router_id(), 1);
}; };
if (const auto maybe = router->node_db()->GetRandom(filter)) if (const auto maybe = router->node_db()->GetRandom(filter))
{ {
return GetHopsAlignedToForBuild(maybe->pubkey); return GetHopsAlignedToForBuild(maybe->router_id());
} }
return std::nullopt; return std::nullopt;
} }
@ -308,12 +310,12 @@ namespace llarp
return buildIntervalLimit > MIN_PATH_BUILD_INTERVAL * 4; return buildIntervalLimit > MIN_PATH_BUILD_INTERVAL * 4;
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
Builder::GetHopsAlignedToForBuild(RouterID endpoint, const std::set<RouterID>& exclude) Builder::GetHopsAlignedToForBuild(RouterID endpoint, const std::set<RouterID>& exclude)
{ {
const auto pathConfig = router->config()->paths; const auto pathConfig = router->config()->paths;
std::vector<RouterContact> hops; std::vector<RemoteRC> hops;
{ {
const auto maybe = SelectFirstHop(exclude); const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value()) if (not maybe.has_value())
@ -324,7 +326,7 @@ namespace llarp
hops.emplace_back(*maybe); hops.emplace_back(*maybe);
}; };
RouterContact endpointRC; RemoteRC endpointRC;
if (const auto maybe = router->node_db()->get_rc(endpoint)) if (const auto maybe = router->node_db()->get_rc(endpoint))
{ {
endpointRC = *maybe; endpointRC = *maybe;
@ -341,19 +343,21 @@ namespace llarp
else else
{ {
auto filter = auto filter =
[&hops, r = router, endpointRC, pathConfig, exclude](const auto& rc) -> bool { [&hops, r = router, endpointRC, pathConfig, exclude](const RemoteRC& rc) -> bool {
if (exclude.count(rc.pubkey)) const auto& rid = rc.router_id();
if (exclude.count(rid))
return false; return false;
std::set<RouterContact> hopsSet; std::set<RemoteRC> hopsSet;
hopsSet.insert(endpointRC); hopsSet.insert(endpointRC);
hopsSet.insert(hops.begin(), hops.end()); hopsSet.insert(hops.begin(), hops.end());
if (r->router_profiling().IsBadForPath(rc.pubkey, 1)) if (r->router_profiling().IsBadForPath(rid, 1))
return false; return false;
for (const auto& hop : hopsSet) for (const auto& hop : hopsSet)
{ {
if (hop.pubkey == rc.pubkey) if (hop.router_id() == rid)
return false; return false;
} }
@ -362,7 +366,7 @@ namespace llarp
if (not pathConfig.Acceptable(hopsSet)) if (not pathConfig.Acceptable(hopsSet))
return false; return false;
#endif #endif
return rc.pubkey != endpointRC.pubkey; return rc.router_id() != endpointRC.router_id();
}; };
if (const auto maybe = router->node_db()->GetRandom(filter)) if (const auto maybe = router->node_db()->GetRandom(filter))
@ -393,7 +397,7 @@ namespace llarp
} }
void void
Builder::Build(std::vector<RouterContact> hops, PathRole roles) Builder::Build(std::vector<RemoteRC> hops, PathRole roles)
{ {
if (IsStopped()) if (IsStopped())
{ {
@ -402,7 +406,7 @@ namespace llarp
} }
lastBuild = llarp::time_now_ms(); lastBuild = llarp::time_now_ms();
const RouterID edge{hops[0].pubkey}; const auto& edge = hops[0].router_id();
if (not router->pathbuild_limiter().Attempt(edge)) if (not router->pathbuild_limiter().Attempt(edge))
{ {
@ -429,7 +433,8 @@ namespace llarp
{ {
bool lastHop = (i == (n_hops - 1)); bool lastHop = (i == (n_hops - 1));
const auto& nextHop = lastHop ? path_hops[i].rc.pubkey : path_hops[i + 1].rc.pubkey; const auto& nextHop =
lastHop ? path_hops[i].rc.router_id() : path_hops[i + 1].rc.router_id();
PathBuildMessage::setup_hop_keys(path_hops[i], nextHop); PathBuildMessage::setup_hop_keys(path_hops[i], nextHop);
auto frame_str = PathBuildMessage::serialize(path_hops[i]); auto frame_str = PathBuildMessage::serialize(path_hops[i]);
@ -533,7 +538,7 @@ namespace llarp
DoPathBuildBackoff(); DoPathBuildBackoff();
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
const RouterID target{hop.rc.pubkey}; const auto& target = hop.rc.router_id();
// look up router and see if it's still on the network // look up router and see if it's still on the network
log::info(path_cat, "Looking up RouterID {} due to path build timeout", target); log::info(path_cat, "Looking up RouterID {} due to path build timeout", target);

@ -115,17 +115,17 @@ namespace llarp::path
bool bool
BuildOneAlignedTo(const RouterID endpoint) override; BuildOneAlignedTo(const RouterID endpoint) override;
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsAlignedToForBuild(RouterID endpoint, const std::set<RouterID>& exclude = {}); GetHopsAlignedToForBuild(RouterID endpoint, const std::set<RouterID>& exclude = {});
void void
Build(std::vector<RouterContact> hops, PathRole roles = ePathRoleAny) override; Build(std::vector<RemoteRC> hops, PathRole roles = ePathRoleAny) override;
/// pick a first hop /// pick a first hop
std::optional<RouterContact> std::optional<RemoteRC>
SelectFirstHop(const std::set<RouterID>& exclude = {}) const; SelectFirstHop(const std::set<RouterID>& exclude = {}) const;
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsForBuild() override; GetHopsForBuild() override;
void void

@ -122,7 +122,7 @@ namespace llarp
/// manual build on these hops /// manual build on these hops
virtual void virtual void
Build(std::vector<RouterContact> hops, PathRole roles = ePathRoleAny) = 0; Build(std::vector<RemoteRC> hops, PathRole roles = ePathRoleAny) = 0;
/// tick owned paths /// tick owned paths
virtual void virtual void
@ -252,7 +252,7 @@ namespace llarp
virtual void virtual void
send_packet_to_remote(std::string buf) = 0; send_packet_to_remote(std::string buf) = 0;
virtual std::optional<std::vector<RouterContact>> virtual std::optional<std::vector<RemoteRC>>
GetHopsForBuild() = 0; GetHopsForBuild() = 0;
void void

@ -10,8 +10,6 @@ using oxenc::bt_dict_producer;
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("profiling");
RouterProfile::RouterProfile(bt_dict_consumer dict) RouterProfile::RouterProfile(bt_dict_consumer dict)
{ {
BDecode(std::move(dict)); BDecode(std::move(dict));
@ -204,7 +202,7 @@ namespace llarp
first = false; first = false;
else else
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
profile.pathFailCount += 1; profile.pathFailCount += 1;
profile.lastUpdated = llarp::time_now_ms(); profile.lastUpdated = llarp::time_now_ms();
} }
@ -217,7 +215,7 @@ namespace llarp
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
profile.pathTimeoutCount += 1; profile.pathTimeoutCount += 1;
profile.lastUpdated = llarp::time_now_ms(); profile.lastUpdated = llarp::time_now_ms();
} }
@ -230,7 +228,7 @@ namespace llarp
const auto sz = p->hops.size(); const auto sz = p->hops.size();
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
auto& profile = m_Profiles[hop.rc.pubkey]; auto& profile = m_Profiles[hop.rc.router_id()];
// redeem previous fails by halfing the fail count and setting timeout to zero // redeem previous fails by halfing the fail count and setting timeout to zero
profile.pathFailCount /= 2; profile.pathFailCount /= 2;
profile.pathTimeoutCount = 0; profile.pathTimeoutCount = 0;
@ -262,7 +260,7 @@ namespace llarp
try try
{ {
util::dump_file(fpath, buf); util::buffer_to_file(fpath, buf);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -299,7 +297,7 @@ namespace llarp
{ {
try try
{ {
std::string data = util::slurp_file(fname); std::string data = util::file_to_string(fname);
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
BDecode(bt_dict_consumer{data}); BDecode(bt_dict_consumer{data});
} }

@ -28,9 +28,9 @@ namespace llarp
} }
bool bool
RCGossiper::IsOurRC(const RouterContact& rc) const RCGossiper::IsOurRC(const LocalRC& rc) const
{ {
return rc.pubkey == rid; return rc.router_id() == rid;
} }
void void
@ -64,14 +64,14 @@ namespace llarp
} }
bool bool
RCGossiper::GossipRC(const RouterContact& rc) RCGossiper::GossipRC(const LocalRC& rc)
{ {
// only distribute public routers // only distribute public routers
if (not rc.IsPublicRouter()) if (not rc.is_public_router())
return false; return false;
if (link_manager == nullptr) if (link_manager == nullptr)
return false; return false;
const RouterID pubkey(rc.pubkey); const RouterID pubkey(rc.router_id());
// filter check // filter check
if (filter.Contains(pubkey)) if (filter.Contains(pubkey))
return false; return false;

@ -12,7 +12,7 @@ namespace llarp
/// The maximum number of peers we will flood a gossiped RC to when propagating an RC /// The maximum number of peers we will flood a gossiped RC to when propagating an RC
constexpr size_t MaxGossipPeers = 20; constexpr size_t MaxGossipPeers = 20;
struct LinkManager; struct LinkManager;
struct RouterContact; struct LocalRC;
struct RCGossiper struct RCGossiper
{ {
@ -23,7 +23,7 @@ namespace llarp
~RCGossiper() = default; ~RCGossiper() = default;
bool bool
GossipRC(const RouterContact& rc); GossipRC(const LocalRC& rc);
void void
Decay(Time_t now); Decay(Time_t now);
@ -32,7 +32,7 @@ namespace llarp
ShouldGossipOurRC(Time_t now) const; ShouldGossipOurRC(Time_t now) const;
bool bool
IsOurRC(const RouterContact& rc) const; IsOurRC(const LocalRC& rc) const;
void void
Init(LinkManager*, const RouterID&, Router*); Init(LinkManager*, const RouterID&, Router*);

@ -66,7 +66,7 @@ namespace llarp
void void
RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup) RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup)
{ {
RouterContact remoteRC; RemoteRC remoteRC;
if (not forceLookup) if (not forceLookup)
{ {
@ -101,7 +101,7 @@ namespace llarp
throw; throw;
} }
RouterContact result{std::move(payload)}; RemoteRC result{std::move(payload)};
if (callback) if (callback)
callback(result.router_id(), result, true); callback(result.router_id(), result, true);
@ -202,24 +202,24 @@ namespace llarp
} }
bool bool
RCLookupHandler::check_rc(const RouterContact& rc) const RCLookupHandler::check_rc(const RemoteRC& rc) const
{ {
if (not is_session_allowed(rc.pubkey)) if (not is_session_allowed(rc.router_id()))
{ {
contacts->delete_rc_node_async(dht::Key_t{rc.pubkey}); contacts->delete_rc_node_async(dht::Key_t{rc.router_id()});
return false; return false;
} }
if (not rc.Verify(llarp::time_now_ms())) if (not rc.verify())
{ {
LogWarn("RC for ", RouterID(rc.pubkey), " is invalid"); log::info(link_cat, "Invalid RC (rid: {})", rc.router_id());
return false; return false;
} }
// update nodedb if required // update nodedb if required
if (rc.IsPublicRouter()) if (rc.is_public_router())
{ {
LogDebug("Adding or updating RC for ", RouterID(rc.pubkey), " to nodedb and dht."); log::info(link_cat, "Adding or updating RC (rid: {}) to nodeDB and DHT", rc.router_id());
node_db->put_rc_if_newer(rc); node_db->put_rc_if_newer(rc);
contacts->put_rc_node_async(rc); contacts->put_rc_node_async(rc);
} }
@ -248,29 +248,6 @@ namespace llarp
}); });
} }
bool
RCLookupHandler::check_renegotiate_valid(RouterContact newrc, RouterContact oldrc)
{
// mismatch of identity ?
if (newrc.pubkey != oldrc.pubkey)
return false;
if (!is_session_allowed(newrc.pubkey))
return false;
auto func = [this, newrc] { check_rc(newrc); };
work_func(func);
// update dht if required
if (contacts->rc_nodes()->HasNode(dht::Key_t{newrc.pubkey}))
{
contacts->rc_nodes()->PutNode(newrc);
}
// TODO: check for other places that need updating the RC
return true;
}
void void
RCLookupHandler::periodic_update(llarp_time_t now) RCLookupHandler::periodic_update(llarp_time_t now)
{ {
@ -278,15 +255,15 @@ namespace llarp
std::unordered_set<RouterID> routersToLookUp; std::unordered_set<RouterID> routersToLookUp;
node_db->VisitInsertedBefore( node_db->VisitInsertedBefore(
[&](const RouterContact& rc) { routersToLookUp.insert(rc.pubkey); }, [&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); },
now - RouterContact::UpdateInterval); now - RouterContact::REPUBLISH);
for (const auto& router : routersToLookUp) for (const auto& router : routersToLookUp)
{ {
get_rc(router, nullptr, true); get_rc(router, nullptr, true);
} }
node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::StaleInsertionAge); node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::STALE);
} }
void void
@ -301,7 +278,8 @@ namespace llarp
{ {
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
log::info(link_cat, "Doing explore via bootstrap node: {}", RouterID(rc.pubkey)); const auto& rid = rc.router_id();
log::info(link_cat, "Doing explore via bootstrap node: {}", rid);
// TODO: replace this concept // TODO: replace this concept
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey}); // dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
@ -336,7 +314,7 @@ namespace llarp
return; return;
} }
// service nodes gossip, not explore // service nodes gossip, not explore
if (contacts->router()->IsServiceNode()) if (contacts->router()->is_service_node())
return; return;
// explore via every connected peer // explore via every connected peer
@ -367,7 +345,7 @@ namespace llarp
LinkManager* linkManager, LinkManager* linkManager,
service::Context* hiddenServiceContext, service::Context* hiddenServiceContext,
const std::unordered_set<RouterID>& strictConnectPubkeys, const std::unordered_set<RouterID>& strictConnectPubkeys,
const std::set<RouterContact>& bootstrapRCList, const std::set<RemoteRC>& bootstrapRCList,
bool isServiceNode_arg) bool isServiceNode_arg)
{ {
contacts = c; contacts = c;
@ -383,7 +361,7 @@ namespace llarp
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
boostrap_rid_list.insert(rc.pubkey); boostrap_rid_list.insert(rc.router_id());
} }
} }
@ -392,7 +370,7 @@ namespace llarp
{ {
for (const auto& rc : bootstrap_rc_list) for (const auto& rc : bootstrap_rc_list)
{ {
if (rc.pubkey == remote) if (rc.router_id() == remote)
{ {
return true; return true;
} }

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <llarp/router_contact.hpp>
#include <llarp/router_id.hpp> #include <llarp/router_id.hpp>
#include <llarp/util/thread/threading.hpp> #include <llarp/util/thread/threading.hpp>
@ -24,7 +25,6 @@ namespace llarp
struct Contacts; struct Contacts;
struct LinkManager; struct LinkManager;
struct RouterContact;
enum class RCRequestResult enum class RCRequestResult
{ {
@ -35,7 +35,7 @@ namespace llarp
}; };
using RCRequestCallback = using RCRequestCallback =
std::function<void(const RouterID&, std::optional<RouterContact>, bool success)>; std::function<void(const RouterID&, std::optional<RemoteRC>, bool success)>;
struct RCLookupHandler struct RCLookupHandler
{ {
@ -80,14 +80,11 @@ namespace llarp
is_registered(const RouterID& remote) const; is_registered(const RouterID& remote) const;
bool bool
check_rc(const RouterContact& rc) const; check_rc(const RemoteRC& rc) const;
bool bool
get_random_whitelist_router(RouterID& router) const; get_random_whitelist_router(RouterID& router) const;
bool
check_renegotiate_valid(RouterContact newrc, RouterContact oldrc);
void void
periodic_update(llarp_time_t now); periodic_update(llarp_time_t now);
@ -106,7 +103,7 @@ namespace llarp
LinkManager* linkManager, LinkManager* linkManager,
service::Context* hiddenServiceContext, service::Context* hiddenServiceContext,
const std::unordered_set<RouterID>& strictConnectPubkeys, const std::unordered_set<RouterID>& strictConnectPubkeys,
const std::set<RouterContact>& bootstrapRCList, const std::set<RemoteRC>& bootstrapRCList,
bool isServiceNode_arg); bool isServiceNode_arg);
std::unordered_set<RouterID> std::unordered_set<RouterID>
@ -128,7 +125,7 @@ namespace llarp
/// service nodes) /// service nodes)
std::unordered_set<RouterID> strict_connect_pubkeys; std::unordered_set<RouterID> strict_connect_pubkeys;
std::set<RouterContact> bootstrap_rc_list; std::set<RemoteRC> bootstrap_rc_list;
std::unordered_set<RouterID> boostrap_rid_list; std::unordered_set<RouterID> boostrap_rid_list;
// Now that all calls are made through the event loop, any access to these // Now that all calls are made through the event loop, any access to these

@ -6,8 +6,6 @@
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("route-poker");
void void
RoutePoker::add_route(oxen::quic::Address ip) RoutePoker::add_route(oxen::quic::Address ip)
{ {
@ -119,7 +117,7 @@ namespace llarp
bool bool
RoutePoker::is_enabled() const RoutePoker::is_enabled() const
{ {
if (router.IsServiceNode()) if (router.is_service_node())
return false; return false;
if (const auto& conf = router.config()) if (const auto& conf = router.config())
return conf->network.m_EnableRoutePoker; return conf->network.m_EnableRoutePoker;
@ -221,7 +219,7 @@ namespace llarp
// explicit route pokes for first hops // explicit route pokes for first hops
router.for_each_connection( router.for_each_connection(
[this](link::Connection conn) { add_route(conn.remote_rc.addr); }); [this](link::Connection conn) { add_route(conn.remote_rc.addr()); });
add_route(router.link_manager().local()); add_route(router.link_manager().local());
// add default route // add default route
@ -240,7 +238,7 @@ namespace llarp
{ {
// unpoke routes for first hops // unpoke routes for first hops
router.for_each_connection( router.for_each_connection(
[this](link::Connection conn) { delete_route(conn.remote_rc.addr); }); [this](link::Connection conn) { delete_route(conn.remote_rc.addr()); });
if (is_enabled() and is_up) if (is_enabled() and is_up)
{ {
vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager();

@ -36,8 +36,6 @@ static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 250ms;
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("router");
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform) Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
: _route_poker{std::make_shared<RoutePoker>(*this)} : _route_poker{std::make_shared<RoutePoker>(*this)}
, _lmq{std::make_shared<oxenmq::OxenMQ>()} , _lmq{std::make_shared<oxenmq::OxenMQ>()}
@ -178,7 +176,7 @@ namespace llarp
util::StatusObject stats{ util::StatusObject stats{
{"running", true}, {"running", true},
{"version", llarp::VERSION_FULL}, {"version", llarp::LOKINET_VERSION_FULL},
{"uptime", to_json(Uptime())}, {"uptime", to_json(Uptime())},
{"numPathsBuilt", pathsCount}, {"numPathsBuilt", pathsCount},
{"numPeersConnected", peers}, {"numPeersConnected", peers},
@ -201,7 +199,7 @@ namespace llarp
void void
Router::Freeze() Router::Freeze()
{ {
if (IsServiceNode()) if (is_service_node())
return; return;
for_each_connection( for_each_connection(
@ -211,13 +209,14 @@ namespace llarp
void void
Router::Thaw() Router::Thaw()
{ {
if (IsServiceNode()) if (is_service_node())
return; return;
std::unordered_set<RouterID> peer_pubkeys; std::unordered_set<RouterID> peer_pubkeys;
for_each_connection( for_each_connection([&peer_pubkeys](link::Connection& conn) {
[&peer_pubkeys](link::Connection& conn) { peer_pubkeys.emplace(conn.remote_rc.pubkey); }); peer_pubkeys.emplace(conn.remote_rc.router_id());
});
loop()->call([this, &peer_pubkeys]() { loop()->call([this, &peer_pubkeys]() {
for (auto& pk : peer_pubkeys) for (auto& pk : peer_pubkeys)
@ -232,10 +231,10 @@ namespace llarp
} }
void void
Router::GossipRCIfNeeded(const RouterContact rc) Router::GossipRCIfNeeded(const LocalRC rc)
{ {
/// if we are not a service node forget about gossip /// if we are not a service node forget about gossip
if (not IsServiceNode()) if (not is_service_node())
return; return;
/// wait for random uptime /// wait for random uptime
if (std::chrono::milliseconds{Uptime()} < _randomStartDelay) if (std::chrono::milliseconds{Uptime()} < _randomStartDelay)
@ -246,14 +245,14 @@ namespace llarp
bool bool
Router::GetRandomGoodRouter(RouterID& router) Router::GetRandomGoodRouter(RouterID& router)
{ {
if (IsServiceNode()) if (is_service_node())
{ {
return _rc_lookup_handler.get_random_whitelist_router(router); return _rc_lookup_handler.get_random_whitelist_router(router);
} }
if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; })) if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; }))
{ {
router = maybe->pubkey; router = maybe->router_id();
return true; return true;
} }
return false; return false;
@ -272,7 +271,7 @@ namespace llarp
} }
void void
Router::connect_to(const RouterContact& rc) Router::connect_to(const RemoteRC& rc)
{ {
_link_manager.connect_to(rc); _link_manager.connect_to(rc);
} }
@ -315,7 +314,7 @@ namespace llarp
{ {
_encryption = _key_manager->encryptionKey; _encryption = _key_manager->encryptionKey;
if (IsServiceNode()) if (is_service_node())
{ {
#if defined(ANDROID) || defined(IOS) #if defined(ANDROID) || defined(IOS)
LogError("running a service node on mobile device is not possible."); LogError("running a service node on mobile device is not possible.");
@ -397,9 +396,9 @@ namespace llarp
log::debug(logcat, "Configuring router"); log::debug(logcat, "Configuring router");
is_service_node = conf.router.m_isRelay; _is_service_node = conf.router.m_isRelay;
if (is_service_node) if (_is_service_node)
{ {
rpc_addr = oxenmq::address(conf.lokid.lokidRPCAddr); rpc_addr = oxenmq::address(conf.lokid.lokidRPCAddr);
_rpc_client = std::make_shared<rpc::LokidRpcClient>(_lmq, weak_from_this()); _rpc_client = std::make_shared<rpc::LokidRpcClient>(_lmq, weak_from_this());
@ -418,9 +417,9 @@ namespace llarp
_node_db = std::move(nodedb); _node_db = std::move(nodedb);
log::debug( log::debug(
logcat, is_service_node ? "Running as a relay (service node)" : "Running as a client"); logcat, _is_service_node ? "Running as a relay (service node)" : "Running as a client");
if (is_service_node) if (_is_service_node)
{ {
_rpc_client->ConnectAsync(rpc_addr); _rpc_client->ConnectAsync(rpc_addr);
} }
@ -439,34 +438,10 @@ namespace llarp
return true; return true;
} }
/// called in disk worker thread
void
Router::HandleSaveRC() const
{
std::string fname = our_rc_file.string();
router_contact.Write(fname.c_str());
}
bool
Router::SaveRC()
{
LogDebug("verify RC signature");
if (!router_contact.Verify(now()))
{
Dump<MAX_RC_SIZE>(rc());
LogError("RC is invalid, not saving");
return false;
}
if (is_service_node)
_node_db->put_rc(router_contact);
queue_disk_io([&]() { HandleSaveRC(); });
return true;
}
bool bool
Router::IsServiceNode() const Router::is_service_node() const
{ {
return is_service_node; return _is_service_node;
} }
bool bool
@ -508,7 +483,7 @@ namespace llarp
bool bool
Router::have_snode_whitelist() const Router::have_snode_whitelist() const
{ {
return IsServiceNode() and _rc_lookup_handler.has_received_whitelist(); return is_service_node() and _rc_lookup_handler.has_received_whitelist();
} }
bool bool
@ -564,18 +539,20 @@ namespace llarp
return _link_manager.get_num_connected_clients(); return _link_manager.get_num_connected_clients();
} }
void
Router::save_rc()
{
_node_db->put_rc(router_contact.view());
queue_disk_io([&]() { router_contact.write(our_rc_file); });
}
bool bool
Router::update_rc() Router::update_rc()
{ {
SecretKey nextOnionKey; router_contact.resign();
RouterContact nextRC = router_contact; if (is_service_node())
if (!nextRC.Sign(identity())) save_rc();
return false;
if (!nextRC.Verify(time_now_ms(), false))
return false;
router_contact = std::move(nextRC);
if (IsServiceNode())
return SaveRC();
return true; return true;
} }
@ -585,20 +562,17 @@ namespace llarp
// Set netid before anything else // Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.m_netId); log::debug(logcat, "Network ID set to {}", conf.router.m_netId);
if (!conf.router.m_netId.empty() if (!conf.router.m_netId.empty()
&& strcmp(conf.router.m_netId.c_str(), llarp::DEFAULT_NETID) != 0) && strcmp(conf.router.m_netId.c_str(), llarp::LOKINET_DEFAULT_NETID) != 0)
{ {
const auto& netid = conf.router.m_netId; const auto& netid = conf.router.m_netId;
llarp::LogWarn( llarp::LogWarn(
"!!!! you have manually set netid to be '", "!!!! you have manually set netid to be '",
netid, netid,
"' which does not equal '", "' which does not equal '",
llarp::DEFAULT_NETID, llarp::LOKINET_DEFAULT_NETID,
"' you will run as a different network, good luck " "' you will run as a different network, good luck "
"and don't forget: something something MUH traffic " "and don't forget: something something MUH traffic "
"shape correlation !!!!"); "shape correlation !!!!");
NetID::DefaultValue() = NetID(reinterpret_cast<const byte_t*>(netid.c_str()));
// reset netid in our rc
router_contact.netID = llarp::NetID();
} }
// Router config // Router config
@ -628,16 +602,17 @@ namespace llarp
else else
log::debug(logcat, "No explicit public address given; will auto-detect during link setup"); log::debug(logcat, "No explicit public address given; will auto-detect during link setup");
RouterContact::BlockBogons = conf.router.m_blockBogons; RouterContact::BLOCK_BOGONS = conf.router.m_blockBogons;
auto& networkConfig = conf.network; auto& networkConfig = conf.network;
/// build a set of strictConnectPubkeys ( /// build a set of strictConnectPubkeys
std::unordered_set<RouterID> strictConnectPubkeys; std::unordered_set<RouterID> strictConnectPubkeys;
if (not networkConfig.m_strictConnect.empty()) if (not networkConfig.m_strictConnect.empty())
{ {
const auto& val = networkConfig.m_strictConnect; const auto& val = networkConfig.m_strictConnect;
if (IsServiceNode()) if (is_service_node())
throw std::runtime_error("cannot use strict-connect option as service node"); throw std::runtime_error("cannot use strict-connect option as service node");
if (val.size() < 2) if (val.size() < 2)
throw std::runtime_error( throw std::runtime_error(
@ -666,7 +641,7 @@ namespace llarp
for (const auto& router : configRouters) for (const auto& router : configRouters)
{ {
log::debug(logcat, "Loading bootstrap router list from {}", defaultBootstrapFile); log::debug(logcat, "Loading bootstrap router list from {}", defaultBootstrapFile);
bootstrap_rc_list.AddFromFile(router); bootstrap_rc_list.read_from_file(router);
} }
for (const auto& rc : conf.bootstrap.routers) for (const auto& rc : conf.bootstrap.routers)
@ -674,37 +649,10 @@ namespace llarp
bootstrap_rc_list.emplace(rc); bootstrap_rc_list.emplace(rc);
} }
// in case someone has an old bootstrap file and is trying to use a bootstrap
// that no longer exists
auto clearBadRCs = [this]() {
for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();)
{
if (it->IsObsoleteBootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->pubkey});
else if (not it->Verify(now()))
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{it->pubkey});
else
{
++it;
continue;
}
// we are in one of the above error cases that we warned about:
it = bootstrap_rc_list.erase(it);
}
};
clearBadRCs();
if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode) if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode)
{ {
auto fallbacks = llarp::load_bootstrap_fallbacks(); auto fallbacks = llarp::load_bootstrap_fallbacks();
if (auto itr = fallbacks.find(router_contact.netID.ToString()); itr != fallbacks.end())
{
bootstrap_rc_list = itr->second;
log::debug(
logcat, "loaded {} default fallback bootstrap routers", bootstrap_rc_list.size());
clearBadRCs();
}
if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode) if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode)
{ {
// empty after trying fallback, if set // empty after trying fallback, if set
@ -717,6 +665,24 @@ namespace llarp
} }
} }
// in case someone has an old bootstrap file and is trying to use a bootstrap
// that no longer exists
for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();)
{
if (it->is_obsolete_bootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", it->router_id());
else if (not it->verify())
log::warning(logcat, "ignoring invalid bootstrap RC: {}", it->router_id());
else
{
++it;
continue;
}
// we are in one of the above error cases that we warned about:
it = bootstrap_rc_list.erase(it);
}
if (conf.bootstrap.seednode) if (conf.bootstrap.seednode)
LogInfo("we are a seed node"); LogInfo("we are a seed node");
else else
@ -733,10 +699,10 @@ namespace llarp
&_hidden_service_context, &_hidden_service_context,
strictConnectPubkeys, strictConnectPubkeys,
bootstrap_rc_list, bootstrap_rc_list,
is_service_node); _is_service_node);
// FIXME: kludge for now, will be part of larger cleanup effort. // FIXME: kludge for now, will be part of larger cleanup effort.
if (is_service_node) if (_is_service_node)
InitInboundLinks(); InitInboundLinks();
else else
InitOutboundLinks(); InitOutboundLinks();
@ -765,7 +731,7 @@ namespace llarp
} }
// API config // API config
if (not IsServiceNode()) if (not is_service_node())
{ {
hidden_service_context().AddEndpoint(conf); hidden_service_context().AddEndpoint(conf);
} }
@ -773,19 +739,13 @@ namespace llarp
return true; return true;
} }
bool
Router::CheckRenegotiateValid(RouterContact newrc, RouterContact oldrc)
{
return _rc_lookup_handler.check_renegotiate_valid(newrc, oldrc);
}
bool bool
Router::IsBootstrapNode(const RouterID r) const Router::IsBootstrapNode(const RouterID r) const
{ {
return std::count_if( return std::count_if(
bootstrap_rc_list.begin(), bootstrap_rc_list.begin(),
bootstrap_rc_list.end(), bootstrap_rc_list.end(),
[r](const RouterContact& rc) -> bool { return rc.pubkey == r; }) [r](const RemoteRC& rc) -> bool { return rc.router_id() == r; })
> 0; > 0;
} }
@ -800,17 +760,24 @@ namespace llarp
Router::report_stats() Router::report_stats()
{ {
const auto now = llarp::time_now_ms(); const auto now = llarp::time_now_ms();
LogInfo(node_db()->num_loaded(), " RCs loaded"); log::info(
LogInfo(bootstrap_rc_list.size(), " bootstrap peers"); logcat,
LogInfo(NumberOfConnectedRouters(), " router connections"); "{} RCs loaded with {} bootstrap peers and {} router connections!",
if (IsServiceNode()) node_db()->num_loaded(),
bootstrap_rc_list.size(),
NumberOfConnectedRouters());
if (is_service_node())
{ {
LogInfo(NumberOfConnectedClients(), " client connections"); log::info(
LogInfo(ToString(router_contact.Age(now)), " since we last updated our RC"); logcat,
LogInfo(ToString(router_contact.TimeUntilExpires(now)), " until our RC expires"); "Local service node has {} client connections since last RC update ({} to expiry)",
NumberOfConnectedClients(),
router_contact.age(now),
router_contact.time_to_expiry(now));
} }
if (_last_stats_report > 0s) if (_last_stats_report > 0s)
LogInfo(ToString(now - _last_stats_report), " last reported stats"); log::info(logcat, "Last reported stats time {}", now - _last_stats_report);
_last_stats_report = now; _last_stats_report = now;
} }
@ -819,8 +786,8 @@ namespace llarp
{ {
std::string status; std::string status;
auto out = std::back_inserter(status); auto out = std::back_inserter(status);
fmt::format_to(out, "v{}", fmt::join(llarp::VERSION, ".")); fmt::format_to(out, "v{}", fmt::join(llarp::LOKINET_VERSION, "."));
if (IsServiceNode()) if (is_service_node())
{ {
fmt::format_to( fmt::format_to(
out, out,
@ -896,12 +863,13 @@ namespace llarp
_rc_lookup_handler.periodic_update(now); _rc_lookup_handler.periodic_update(now);
const bool has_whitelist = _rc_lookup_handler.has_received_whitelist(); const bool has_whitelist = _rc_lookup_handler.has_received_whitelist();
const bool is_snode = IsServiceNode(); const bool is_snode = is_service_node();
const bool is_decommed = appears_decommed(); const bool is_decommed = appears_decommed();
bool should_gossip = appears_funded(); bool should_gossip = appears_funded();
if (is_snode if (is_snode
and (router_contact.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000)) or (now - router_contact.last_updated) > rc_regen_interval)) and (router_contact.expires_within_delta(now, std::chrono::milliseconds(randint() % 10000))
or (now - router_contact.timestamp().time_since_epoch()) > rc_regen_interval))
{ {
LogInfo("regenerating RC"); LogInfo("regenerating RC");
if (update_rc()) if (update_rc())
@ -921,24 +889,24 @@ namespace llarp
GossipRCIfNeeded(router_contact); GossipRCIfNeeded(router_contact);
} }
// remove RCs for nodes that are no longer allowed by network policy // remove RCs for nodes that are no longer allowed by network policy
node_db()->RemoveIf([&](const RouterContact& rc) -> bool { node_db()->RemoveIf([&](const RemoteRC& rc) -> bool {
// don't purge bootstrap nodes from nodedb // don't purge bootstrap nodes from nodedb
if (IsBootstrapNode(rc.pubkey)) if (IsBootstrapNode(rc.router_id()))
{ {
log::trace(logcat, "Not removing {}: is bootstrap node", rc.pubkey); log::trace(logcat, "Not removing {}: is bootstrap node", rc.router_id());
return false; return false;
} }
// if for some reason we stored an RC that isn't a valid router // if for some reason we stored an RC that isn't a valid router
// purge this entry // purge this entry
if (not rc.IsPublicRouter()) if (not rc.is_public_router())
{ {
log::debug(logcat, "Removing {}: not a valid router", rc.pubkey); log::debug(logcat, "Removing {}: not a valid router", rc.router_id());
return true; return true;
} }
/// clear out a fully expired RC /// clear out a fully expired RC
if (rc.IsExpired(now)) if (rc.is_expired(now))
{ {
log::debug(logcat, "Removing {}: RC is expired", rc.pubkey); log::debug(logcat, "Removing {}: RC is expired", rc.router_id());
return true; return true;
} }
// clients have no notion of a whilelist // clients have no notion of a whilelist
@ -946,23 +914,23 @@ namespace llarp
// routers that are not whitelisted for first hops // routers that are not whitelisted for first hops
if (not is_snode) if (not is_snode)
{ {
log::trace(logcat, "Not removing {}: we are a client and it looks fine", rc.pubkey); log::trace(logcat, "Not removing {}: we are a client and it looks fine", rc.router_id());
return false; return false;
} }
// if we don't have the whitelist yet don't remove the entry // if we don't have the whitelist yet don't remove the entry
if (not has_whitelist) if (not has_whitelist)
{ {
log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.pubkey); log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.router_id());
return false; return false;
} }
// if we have no whitelist enabled or we have // if we have no whitelist enabled or we have
// the whitelist enabled and we got the whitelist // the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not // check against the whitelist and remove if it's not
// in the whitelist OR if there is no whitelist don't remove // in the whitelist OR if there is no whitelist don't remove
if (has_whitelist and not _rc_lookup_handler.is_session_allowed(rc.pubkey)) if (has_whitelist and not _rc_lookup_handler.is_session_allowed(rc.router_id()))
{ {
log::debug(logcat, "Removing {}: not a valid router", rc.pubkey); log::debug(logcat, "Removing {}: not a valid router", rc.router_id());
return true; return true;
} }
return false; return false;
@ -971,10 +939,10 @@ namespace llarp
if (not is_snode or not has_whitelist) if (not is_snode or not has_whitelist)
{ {
// find all deregistered relays // find all deregistered relays
std::unordered_set<PubKey> close_peers; std::unordered_set<RouterID> close_peers;
for_each_connection([this, &close_peers](link::Connection& conn) { for_each_connection([this, &close_peers](link::Connection& conn) {
const auto& pk = conn.remote_rc.pubkey; const auto& pk = conn.remote_rc.router_id();
if (conn.remote_is_relay and not _rc_lookup_handler.is_session_allowed(pk)) if (conn.remote_is_relay and not _rc_lookup_handler.is_session_allowed(pk))
close_peers.insert(pk); close_peers.insert(pk);
@ -982,7 +950,7 @@ namespace llarp
// mark peers as de-registered // mark peers as de-registered
for (auto& peer : close_peers) for (auto& peer : close_peers)
_link_manager.deregister_peer(std::move(peer)); _link_manager.deregister_peer(peer);
} }
_link_manager.check_persisting_conns(now); _link_manager.check_persisting_conns(now);
@ -1051,7 +1019,7 @@ namespace llarp
std::set<dht::Key_t> peer_keys; std::set<dht::Key_t> peer_keys;
for_each_connection( for_each_connection(
[&peer_keys](link::Connection& conn) { peer_keys.emplace(conn.remote_rc.pubkey); }); [&peer_keys](link::Connection& conn) { peer_keys.emplace(conn.remote_rc.router_id()); });
_contacts->rc_nodes()->RemoveIf( _contacts->rc_nodes()->RemoveIf(
[&peer_keys](const dht::Key_t& k) -> bool { return peer_keys.count(k) == 0; }); [&peer_keys](const dht::Key_t& k) -> bool { return peer_keys.count(k) == 0; });
@ -1062,32 +1030,12 @@ namespace llarp
_last_tick = llarp::time_now_ms(); _last_tick = llarp::time_now_ms();
} }
void
Router::modify_rc(std::function<std::optional<RouterContact>(RouterContact)> modify)
{
if (auto maybe = modify(rc()))
{
router_contact = *maybe;
update_rc();
_rcGossiper.GossipRC(rc());
}
}
bool bool
Router::GetRandomConnectedRouter(RouterContact& result) const Router::GetRandomConnectedRouter(RemoteRC& result) const
{ {
return _link_manager.get_random_connected(result); return _link_manager.get_random_connected(result);
} }
void
Router::HandleDHTLookupForExplore(RouterID /*remote*/, const std::vector<RouterContact>& results)
{
for (const auto& rc : results)
{
_rc_lookup_handler.check_rc(rc);
}
}
void void
Router::set_router_whitelist( Router::set_router_whitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& whitelist,
@ -1112,80 +1060,48 @@ namespace llarp
if (is_running || is_stopping) if (is_running || is_stopping)
return false; return false;
// set public signing key router_contact = LocalRC::make(identity(), public_ip());
router_contact.pubkey = seckey_topublic(identity());
// set router version if service node
if (IsServiceNode())
{
router_contact.routerVersion = RouterVersion(llarp::VERSION, llarp::constants::proto_version);
}
if (IsServiceNode() and not router_contact.IsPublicRouter()) if (is_service_node() and not router_contact.is_public_router())
{ {
LogError("we are configured as relay but have no reachable addresses"); if (not router_contact.is_public_router())
return false;
}
// set public encryption key
router_contact.enckey = seckey_topublic(encryption());
LogInfo("Signing rc...");
if (!router_contact.Sign(identity()))
{
LogError("failed to sign rc");
return false;
}
if (IsServiceNode())
{
if (!SaveRC())
{ {
LogError("failed to save RC"); log::error(logcat, "Router is configured as relay but has no reachable addresses!");
return false; return false;
} }
}
if (IsServiceNode()) save_rc();
{
// initialize as service node if (not init_service_node())
if (!InitServiceNode())
{ {
LogError("Failed to initialize service node"); log::error(logcat, "Router failed to initialize service node!");
return false; return false;
} }
log::info(logcat, "Router initialized as service node!");
const RouterID us = pubkey(); const RouterID us = pubkey();
LogInfo("initalized service node: ", us);
// init gossiper here
_rcGossiper.Init(&_link_manager, us, this); _rcGossiper.Init(&_link_manager, us, this);
// relays do not use profiling // relays do not use profiling
router_profiling().Disable(); router_profiling().Disable();
} }
else else
{ {
// we are a client // we are a client, regenerate keys and resign rc before everything else
// regenerate keys and resign rc before everything else
crypto::identity_keygen(_identity); crypto::identity_keygen(_identity);
crypto::encryption_keygen(_encryption); crypto::encryption_keygen(_encryption);
router_contact.pubkey = seckey_topublic(identity()); router_contact.set_router_id(seckey_to_pubkey(identity())); // resigns RC
router_contact.enckey = seckey_topublic(encryption());
if (!router_contact.Sign(identity()))
{
LogError("failed to regenerate keys and sign RC");
return false;
}
} }
LogInfo("starting hidden service context..."); log::info(logcat, "Starting hidden service context...");
if (!hidden_service_context().StartAll()) if (!hidden_service_context().StartAll())
{ {
LogError("Failed to start hidden service context"); log::error(logcat, "Failed to start hidden service context!");
return false; return false;
} }
{ log::info(logcat, "Loading NodeDB from disk...");
LogInfo("Loading nodedb from disk..."); _node_db->load_from_disk();
_node_db->load_from_disk();
}
_contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this); _contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this);
@ -1193,16 +1109,17 @@ namespace llarp
{ {
node_db()->put_rc(rc); node_db()->put_rc(rc);
_contacts->rc_nodes()->PutNode(rc); _contacts->rc_nodes()->PutNode(rc);
LogInfo("added bootstrap node ", RouterID{rc.pubkey}); log::info(logcat, "Added bootstrap node (rid: {})", rc.router_id());
} }
LogInfo("have ", _node_db->num_loaded(), " routers"); log::info(logcat, "Router populated NodeDB with {} routers", _node_db->num_loaded());
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); }); _loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
_route_poker->start(); _route_poker->start();
is_running.store(true); is_running.store(true);
_started_at = now(); _started_at = now();
if (IsServiceNode())
if (is_service_node())
{ {
// do service node testing if we are in service node whitelist mode // do service node testing if we are in service node whitelist mode
_loop->call_every(consensus::REACHABILITY_TESTING_TIMER_INTERVAL, weak_from_this(), [this] { _loop->call_every(consensus::REACHABILITY_TESTING_TIMER_INTERVAL, weak_from_this(), [this] {
@ -1223,13 +1140,16 @@ namespace llarp
{ {
if (not SessionToRouterAllowed(router)) if (not SessionToRouterAllowed(router))
{ {
LogDebug( log::debug(
router, logcat,
" is no longer a registered service node so we remove it from the testing list"); "{} is no longer a registered service node; dropping from test list",
router);
router_testing.remove_node_from_failing(router); router_testing.remove_node_from_failing(router);
continue; continue;
} }
LogDebug("Establishing session to ", router, " for SN testing");
log::debug(logcat, "Establishing session to {} for service node testing", router);
// try to make a session to this random router // try to make a session to this random router
// this will do a dht lookup if needed // this will do a dht lookup if needed
_link_manager.connect_to(router); _link_manager.connect_to(router);
@ -1406,7 +1326,7 @@ namespace llarp
} }
bool bool
Router::InitServiceNode() Router::init_service_node()
{ {
LogInfo("accepting transit traffic"); LogInfo("accepting transit traffic");
paths.AllowTransit(); paths.AllowTransit();
@ -1430,7 +1350,7 @@ namespace llarp
bool bool
Router::HasClientExit() const Router::HasClientExit() const
{ {
if (IsServiceNode()) if (is_service_node())
return false; return false;
const auto& ep = hidden_service_context().GetDefault(); const auto& ep = hidden_service_context().GetDefault();
return ep and ep->HasExit(); return ep and ep->HasExit();

@ -85,7 +85,7 @@ namespace llarp
// use file based logging? // use file based logging?
bool use_file_logging = false; bool use_file_logging = false;
// our router contact // our router contact
RouterContact router_contact; LocalRC router_contact;
std::shared_ptr<oxenmq::OxenMQ> _lmq; std::shared_ptr<oxenmq::OxenMQ> _lmq;
path::BuildLimiter _pathbuild_limiter; path::BuildLimiter _pathbuild_limiter;
std::shared_ptr<EventLoopWakeup> loop_wakeup; std::shared_ptr<EventLoopWakeup> loop_wakeup;
@ -94,7 +94,7 @@ namespace llarp
std::atomic<bool> is_running; std::atomic<bool> is_running;
int _outbound_udp_socket = -1; int _outbound_udp_socket = -1;
bool is_service_node = false; bool _is_service_node = false;
std::optional<SockAddr> _ourAddress; std::optional<SockAddr> _ourAddress;
oxen::quic::Address _local_addr; oxen::quic::Address _local_addr;
@ -147,6 +147,9 @@ namespace llarp
void void
report_stats(); report_stats();
void
save_rc();
bool bool
update_rc(); update_rc();
@ -167,7 +170,7 @@ namespace llarp
connect_to(const RouterID& rid); connect_to(const RouterID& rid);
void void
connect_to(const RouterContact& rc); connect_to(const RemoteRC& rc);
Contacts* Contacts*
contacts() const contacts() const
@ -274,7 +277,7 @@ namespace llarp
return paths; return paths;
} }
const RouterContact& const LocalRC&
rc() const rc() const
{ {
return router_contact; return router_contact;
@ -295,9 +298,6 @@ namespace llarp
return _rc_lookup_handler.whitelist(); return _rc_lookup_handler.whitelist();
} }
void
modify_rc(std::function<std::optional<RouterContact>(RouterContact)> modify);
void void
set_router_whitelist( set_router_whitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& whitelist,
@ -381,7 +381,7 @@ namespace llarp
status_line(); status_line();
void void
GossipRCIfNeeded(const RouterContact rc); GossipRCIfNeeded(const LocalRC rc);
void void
InitInboundLinks(); InitInboundLinks();
@ -395,14 +395,14 @@ namespace llarp
/// initialize us as a service node /// initialize us as a service node
/// return true on success /// return true on success
bool bool
InitServiceNode(); init_service_node();
bool bool
IsRunning() const; IsRunning() const;
/// return true if we are running in service node mode /// return true if we are running in service node mode
bool bool
IsServiceNode() const; is_service_node() const;
std::optional<std::string> std::optional<std::string>
OxendErrorState() const; OxendErrorState() const;
@ -452,12 +452,6 @@ namespace llarp
bool bool
PathToRouterAllowed(const RouterID& router) const; PathToRouterAllowed(const RouterID& router) const;
void
HandleSaveRC() const;
bool
SaveRC();
/// return true if we are a client with an exit configured /// return true if we are a client with an exit configured
bool bool
HasClientExit() const; HasClientExit() const;
@ -465,7 +459,7 @@ namespace llarp
const byte_t* const byte_t*
pubkey() const pubkey() const
{ {
return seckey_topublic(_identity); return seckey_to_pubkey(_identity);
} }
/// send to remote router or queue for sending /// send to remote router or queue for sending
@ -489,13 +483,6 @@ namespace llarp
bool IsBootstrapNode(RouterID) const; bool IsBootstrapNode(RouterID) const;
/// check if newRc matches oldRC and update local rc for this remote contact
/// if valid
/// returns true on valid and updated
/// returns false otherwise
bool
CheckRenegotiateValid(RouterContact newRc, RouterContact oldRC);
/// call internal router ticker /// call internal router ticker
void void
Tick(); Tick();
@ -525,10 +512,7 @@ namespace llarp
NumberOfConnectedClients() const; NumberOfConnectedClients() const;
bool bool
GetRandomConnectedRouter(RouterContact& result) const; GetRandomConnectedRouter(RemoteRC& result) const;
void
HandleDHTLookupForExplore(RouterID remote, const std::vector<RouterContact>& results);
bool bool
HasSessionTo(const RouterID& remote) const; HasSessionTo(const RouterID& remote) const;

@ -12,407 +12,273 @@
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("RC");
NetID& // RouterContact::RouterContact(std::string buf)
NetID::DefaultValue() // {
{ // try
static NetID defaultID(reinterpret_cast<const byte_t*>(llarp::DEFAULT_NETID)); // {
return defaultID; // oxenc::bt_list_consumer btlc{buf};
}
// // signature.from_string(btlc.consume_string());
bool RouterContact::BlockBogons = true; // signed_bt_dict = btlc.consume_string();
/// 1 day rc lifespan // // TODO: parse bt dict
constexpr auto rc_lifetime = 24h; // }
/// an RC inserted long enough ago (4 hrs) is considered stale and is removed // catch (...)
constexpr auto rc_stale_age = 4h; // {
/// window of time in which a router wil try to update their RC before it is marked stale // log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!");
constexpr auto rc_update_window = 5min; // }
/// update RCs shortly before they are about to expire // }
constexpr auto rc_update_interval = rc_stale_age - rc_update_window;
// std::string
llarp_time_t RouterContact::Lifetime = rc_lifetime; // RouterContact::bt_encode() const
llarp_time_t RouterContact::StaleInsertionAge = rc_stale_age; // {
llarp_time_t RouterContact::UpdateInterval = rc_update_interval; // oxenc::bt_dict_producer btdp;
// bt_encode(btdp);
/// how many rc lifetime intervals should we wait until purging an rc // return std::move(btdp).str();
constexpr auto expiration_lifetime_generations = 10; // }
/// the max age of an rc before we want to expire it
constexpr auto rc_expire_age = rc_lifetime * expiration_lifetime_generations;
NetID::NetID(const byte_t* val) void
RouterContact::bt_load(oxenc::bt_dict_consumer& data)
{ {
const size_t len = strnlen(reinterpret_cast<const char*>(val), size()); if (int rc_ver = data.require<uint8_t>(""); rc_ver != RC_VERSION)
std::copy(val, val + len, begin()); throw std::runtime_error{"Invalid RC: do not know how to parse v{} RCs"_format(rc_ver)};
}
NetID::NetID() : NetID(DefaultValue().data()) auto ipv4_port = data.require<std::string_view>("4");
{}
bool if (ipv4_port.size() != 6)
NetID::operator==(const NetID& other) const throw std::runtime_error{
{ "Invalid RC address: expected 6-byte IPv4 IP/port, got {}"_format(ipv4_port.size())};
return ToString() == other.ToString();
}
std::string sockaddr_in s4;
NetID::ToString() const s4.sin_family = AF_INET;
{
return {begin(), std::find(begin(), end(), '\0')};
}
bool
NetID::BDecode(llarp_buffer_t* buf)
{
Zero();
llarp_buffer_t strbuf;
if (!bencode_read_string(buf, &strbuf))
return false;
if (strbuf.sz > size()) std::memcpy(&s4.sin_addr.s_addr, ipv4_port.data(), 4);
return false; std::memcpy(&s4.sin_port, ipv4_port.data() + 4, 2);
std::copy(strbuf.base, strbuf.base + strbuf.sz, begin()); _addr = oxen::quic::Address{&s4};
return true;
}
bool if (!_addr.is_public())
NetID::BEncode(llarp_buffer_t* buf) const throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
{
auto term = std::find(begin(), end(), '\0');
return bencode_write_bytestring(buf, data(), std::distance(begin(), term));
}
RouterContact::RouterContact(std::string buf) if (auto ipv6_port = data.maybe<std::string_view>("6"))
{
try
{ {
oxenc::bt_list_consumer btlc{buf}; if (ipv6_port->size() != 18)
throw std::runtime_error{
"Invalid RC address: expected 18-byte IPv6 IP/port, got {}"_format(ipv6_port->size())};
signature.from_string(btlc.consume_string()); sockaddr_in6 s6{};
signed_bt_dict = btlc.consume_string(); s6.sin6_family = AF_INET6;
// TODO: parse bt dict std::memcpy(&s6.sin6_addr.s6_addr, ipv6_port->data(), 16);
} std::memcpy(&s6.sin6_port, ipv6_port->data() + 16, 2);
catch (...)
{
log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!");
}
}
std::string _addr6.emplace(&s6);
RouterContact::bt_encode() const if (!_addr6->is_public())
{ throw std::runtime_error{"Invalid RC: IPv6 address is not a publicly routable IP"};
oxenc::bt_list_producer btlp;
try
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
} }
catch (...) else
{ {
log::warning(llarp_cat, "Error: RouterContact failed to bt encode contents!"); _addr6.reset();
} }
return std::move(btlp).str(); auto netid = data.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
} if (netid != ACTIVE_NETID)
throw std::runtime_error{
void "Invalid RC netid: expected {}, got {}; this is an RC for a different network!"_format(
RouterContact::bt_encode_subdict(oxenc::bt_list_producer& btlp) const ACTIVE_NETID, netid)};
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
}
std::string
RouterContact::ToTXTRecord() const
{
std::string result;
auto out = std::back_inserter(result);
fmt::format_to(out, "addr={}; pk={}", addr.to_string(), pubkey);
fmt::format_to(out, "updated={}; onion_pk={}; ", last_updated.count(), enckey.ToHex());
if (routerVersion.has_value())
fmt::format_to(out, "router_version={}; ", *routerVersion);
return result;
}
bool
RouterContact::FromOurNetwork() const
{
return netID == NetID::DefaultValue();
}
std::string auto pk = data.require<std::string_view>("p");
RouterContact::bencode_signed_section() const
{
oxenc::bt_dict_producer btdp;
btdp.append("a", addr.to_string()); if (pk.size() != RouterID::SIZE)
btdp.append("i", netID.ToView()); throw std::runtime_error{"Invalid RC: router id has invalid size {}"_format(pk.size())};
btdp.append("k", pubkey.bt_encode());
btdp.append("p", enckey.ToView());
btdp.append("r", routerVersion->ToString());
if (not srvRecords.empty()) std::memcpy(_router_id.data(), pk.data(), RouterID::SIZE);
{
auto sublist = btdp.append_list("s");
for (auto& s : srvRecords) _timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
sublist.append(s.bt_encode());
}
btdp.append("u", last_updated.count()); auto ver = data.require<ustring_view>("v");
return std::move(btdp).str(); if (ver.size() != 3)
} throw std::runtime_error{
"Invalid RC router version: received {} bytes (!= 3)"_format(ver.size())};
void for (int i = 0; i < 3; i++)
RouterContact::Clear() _router_version[i] = ver[i];
{
signature.Zero();
enckey.Zero();
pubkey.Zero();
routerVersion = std::optional<RouterVersion>{};
last_updated = 0s;
srvRecords.clear();
version = llarp::constants::proto_version;
}
util::StatusObject
RouterContact::ExtractStatus() const
{
util::StatusObject obj{
{"lastUpdated", last_updated.count()},
{"publicRouter", IsPublicRouter()},
{"identity", pubkey.ToString()},
{"address", addr.to_string()}};
if (routerVersion)
{
obj["routerVersion"] = routerVersion->ToString();
}
std::vector<util::StatusObject> srv;
for (const auto& record : srvRecords)
{
srv.emplace_back(record.ExtractStatus());
}
obj["srvRecords"] = srv;
return obj;
} }
bool bool
RouterContact::BDecode(llarp_buffer_t* buf) RouterContact::write(const fs::path& fname) const
{ {
Clear(); auto bte = view();
if (*buf->cur == 'd') // old format
{
return DecodeVersion_0(buf);
}
else if (*buf->cur != 'l') // if not dict, should be new format and start with list
{
return false;
}
try try
{ {
std::string_view buf_view(reinterpret_cast<char*>(buf->cur), buf->size_left()); util::buffer_to_file(fname, bte.data(), bte.size());
oxenc::bt_list_consumer btlist(buf_view);
uint64_t outer_version = btlist.consume_integer<uint64_t>();
if (outer_version == 1)
{
bool decode_result = DecodeVersion_1(btlist);
// advance the llarp_buffer_t since lokimq serialization is unaware of it.
// FIXME: this is broken (current_buffer got dropped), but the whole thing is getting
// replaced.
// buf->cur += btlist.
// current_buffer().data() - buf_view.data() + 1;
return decode_result;
}
else
{
log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
return false;
}
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
log::debug(logcat, "RouterContact::BDecode failed: {}", e.what()); log::error(logcat, "Failed to write RC to {}: {}", fname, e.what());
return false;
} }
return true;
return false;
} }
bool util::StatusObject
RouterContact::DecodeVersion_0(llarp_buffer_t* buf) RouterContact::extract_status() const
{ {
return bencode_decode_dict(*this, buf); util::StatusObject obj{
{"lastUpdated", _timestamp.time_since_epoch().count()},
{"publicRouter", is_public_router()},
{"identity", _router_id.ToString()},
{"address", _addr.to_string()}};
// if (routerVersion)
// {
// obj["routerVersion"] = routerVersion->ToString();
// }
// std::vector<util::StatusObject> srv;
// for (const auto& record : srvRecords)
// {
// srv.emplace_back(record.ExtractStatus());
// }
// obj["srvRecords"] = srv;
return obj;
} }
bool bool
RouterContact::DecodeVersion_1(oxenc::bt_list_consumer& btlist) RouterContact::BDecode(llarp_buffer_t* buf)
{ {
auto signature_string = btlist.consume_string_view(); // TODO: unfuck all of this
signed_bt_dict = btlist.consume_dict_data();
if (not btlist.is_finished()) (void)buf;
{
log::debug(logcat, "RouterContact serialized list too long for specified version.");
return false;
}
llarp_buffer_t sigbuf(signature_string.data(), signature_string.size()); // clear();
if (not signature.FromBytestring(&sigbuf))
{ // if (*buf->cur == 'd') // old format
log::debug(logcat, "RouterContact serialized signature had invalid length."); // {
return false; // return DecodeVersion_0(buf);
} // }
// else if (*buf->cur != 'l') // if not dict, should be new format and start with list
// {
// return false;
// }
// try
// {
// std::string_view buf_view(reinterpret_cast<char*>(buf->cur), buf->size_left());
// oxenc::bt_list_consumer btlist(buf_view);
// uint64_t outer_version = btlist.consume_integer<uint64_t>();
// if (outer_version == 1)
// {
// bool decode_result = DecodeVersion_1(btlist);
// // advance the llarp_buffer_t since lokimq serialization is unaware of it.
// // FIXME: this is broken (current_buffer got dropped), but the whole thing is getting
// // replaced.
// // buf->cur += btlist.
// // current_buffer().data() - buf_view.data() + 1;
// return decode_result;
// }
// else
// {
// log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
// return false;
// }
// }
// catch (const std::exception& e)
// {
// log::debug(logcat, "RouterContact::BDecode failed: {}", e.what());
// }
llarp_buffer_t data_dict_buf(signed_bt_dict.data(), signed_bt_dict.size()); return false;
return bencode_decode_dict(*this, &data_dict_buf);
} }
bool bool
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{ {
bool read = false; bool read = false;
(void)key;
// TOFIX: fuck everything about llarp_buffer_t // TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false; // return false;
if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("k", _router_id, read, key, buf))
return false; // return false;
if (key.startswith("r")) // if (key.startswith("r"))
{ // {
RouterVersion r; // RouterVersion r;
if (not r.BDecode(buf)) // if (not r.BDecode(buf))
return false; // return false;
routerVersion = r; // routerVersion = r;
return true; // return true;
} // }
if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf)) // if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictInt("u", last_updated, read, key, buf)) // if (!BEncodeMaybeReadDictInt("u", _timestamp, read, key, buf))
return false; // return false;
if (!BEncodeMaybeReadDictInt("v", version, read, key, buf)) // if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
return false; // return false;
if (key.startswith("x") and serializeExit) // if (key.startswith("x") and serializeExit)
{ // {
return bencode_discard(buf); // return bencode_discard(buf);
} // }
if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf)) // if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
return false; // return false;
return read or bencode_discard(buf); return read or bencode_discard(buf);
} }
bool bool
RouterContact::IsPublicRouter() const RouterContact::is_public_router() const
{ {
if (not routerVersion) if (_router_version.empty())
return false; return false;
return addr.is_addressable(); return _addr.is_addressable();
} }
bool bool
RouterContact::IsExpired(llarp_time_t now) const RouterContact::is_expired(llarp_time_t now) const
{ {
return Age(now) >= rc_expire_age; return age(now) >= _timestamp.time_since_epoch() + LIFETIME;
} }
llarp_time_t llarp_time_t
RouterContact::TimeUntilExpires(llarp_time_t now) const RouterContact::time_to_expiry(llarp_time_t now) const
{ {
const auto expiresAt = last_updated + Lifetime; const auto expiry = _timestamp.time_since_epoch() + LIFETIME;
return now < expiresAt ? expiresAt - now : 0s; return now < expiry ? expiry - now : 0s;
} }
llarp_time_t llarp_time_t
RouterContact::Age(llarp_time_t now) const RouterContact::age(llarp_time_t now) const
{
return now > last_updated ? now - last_updated : 0s;
}
bool
RouterContact::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
{
return TimeUntilExpires(now) <= dlt;
}
bool
RouterContact::Sign(const SecretKey& secretkey)
{ {
pubkey = llarp::seckey_topublic(secretkey); auto delta = now - _timestamp.time_since_epoch();
signature.Zero(); return delta > 0s ? delta : 0s;
last_updated = time_now_ms();
signed_bt_dict = bencode_signed_section();
return crypto::sign(
signature,
secretkey,
reinterpret_cast<uint8_t*>(signed_bt_dict.data()),
signed_bt_dict.size());
}
bool
RouterContact::Verify(llarp_time_t now, bool allowExpired) const
{
if (netID != NetID::DefaultValue())
{
log::error(
logcat, "netid mismatch: '{}' (theirs) != '{}' (ours)", netID, NetID::DefaultValue());
return false;
}
if (IsExpired(now) and not allowExpired)
return false;
// TODO: make net* overridable
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr.in4()) && BlockBogons)
{
log::error(logcat, "invalid address info: {}", addr);
return false;
}
if (!VerifySignature())
{
log::error(logcat, "invalid signature: {}", *this);
return false;
}
return true;
} }
bool bool
RouterContact::VerifySignature() const RouterContact::expires_within_delta(llarp_time_t now, llarp_time_t dlt) const
{ {
RouterContact copy; return time_to_expiry(now) <= dlt;
copy = *this;
copy.signature.Zero();
auto bte = copy.bt_encode();
return crypto::verify(pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature);
} }
static constexpr std::array obsolete_bootstraps = { static constexpr std::array obsolete_bootstraps = {
@ -421,66 +287,13 @@ namespace llarp
}; };
bool bool
RouterContact::IsObsoleteBootstrap() const RouterContact::is_obsolete_bootstrap() const
{ {
for (const auto& k : obsolete_bootstraps) for (const auto& k : obsolete_bootstraps)
{ {
if (pubkey.ToHex() == k) if (_router_id.ToHex() == k)
return true; return true;
} }
return false; return false;
} }
bool
RouterContact::Write(const fs::path& fname) const
{
std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp);
auto bte = bt_encode();
buf.write(bte.begin(), bte.end());
try
{
util::dump_file(fname, tmp.data(), buf.cur - buf.base);
}
catch (const std::exception& e)
{
log::error(logcat, "Failed to write RC to {}: {}", fname, e.what());
return false;
}
return true;
}
bool
RouterContact::Read(const fs::path& fname)
{
std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp);
try
{
util::slurp_file(fname, tmp.data(), tmp.size());
}
catch (const std::exception& e)
{
log::error(logcat, "Failed to read RC from {}: {}", fname, e.what());
return false;
}
return BDecode(&buf);
}
std::string
RouterContact::ToString() const
{
return fmt::format(
"[RC k={} updated={} netid={} v={} ai={{{}}} e={} z={}]",
pubkey,
last_updated.count(),
netID,
version,
fmt::format("{}", addr),
enckey,
signature);
}
} // namespace llarp } // namespace llarp

@ -1,11 +1,11 @@
#pragma once #pragma once
#include "router_id.hpp"
#include "router_version.hpp" #include "router_version.hpp"
#include <llarp/constants/version.hpp> #include <llarp/constants/version.hpp>
#include <llarp/crypto/types.hpp> #include <llarp/crypto/types.hpp>
#include <llarp/dns/srv_data.hpp> #include <llarp/dns/srv_data.hpp>
#include <llarp/net/exit_info.hpp>
#include <llarp/util/aligned.hpp> #include <llarp/util/aligned.hpp>
#include <llarp/util/bencode.hpp> #include <llarp/util/bencode.hpp>
#include <llarp/util/status.hpp> #include <llarp/util/status.hpp>
@ -17,128 +17,148 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#define MAX_RC_SIZE (1024)
namespace oxenc
{
class bt_list_consumer;
} // namespace oxenc
namespace llarp namespace llarp
{ {
/// NetID static auto logcat = log::Cat("RC");
struct NetID final : public AlignedBuffer<8>
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
static inline constexpr size_t NETID_SIZE{8};
/// On the wire we encode the data as a dict containing:
/// "" -- the RC format version, which must be == RouterContact::Version for us to attempt to
/// parse the reset of the fields. (Future versions might have backwards-compat support
/// for lower versions).
/// "4" -- 6 byte packed IPv4 address & port: 4 bytes of IPv4 address followed by 2 bytes of
/// port, both encoded in network (i.e. big-endian) order.
/// "6" -- optional 18 byte IPv6 address & port: 16 byte raw IPv6 address followed by 2 bytes
/// of port in network order.
/// "i" -- optional network ID string of up to 8 bytes; this is omitted for the default network
/// ID ("lokinet") but included for others (such as "gamma" for testnet).
/// "p" -- 32-byte router pubkey
/// "t" -- timestamp when this RC record was created (which also implicitly determines when it
/// goes stale and when it expires).
/// "v" -- lokinet version of the router; this is a three-byte packed value of
/// MAJOR, MINOR, PATCH, e.g. \x00\x0a\x03 for 0.10.3.
/// "~" -- signature of all of the previous serialized data, signed by "p"
/// RouterContact
struct RouterContact
{ {
static NetID& static constexpr uint8_t RC_VERSION = 0;
DefaultValue();
NetID(); /// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected.
static inline bool BLOCK_BOGONS = true;
explicit NetID(const byte_t* val); static inline std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
NetID(const NetID& other) = default; static inline constexpr size_t MAX_RC_SIZE = 1024;
NetID&
operator=(const NetID& other) = default;
bool /// Timespans for RCs:
operator==(const NetID& other) const;
bool /// How long (relative to its timestamp) before an RC becomes stale. Stale records are used
operator!=(const NetID& other) const /// (e.g. for path building) only if there are no non-stale records available, such as might be
{ /// the case when a client has been turned off for a while.
return !(*this == other); static constexpr auto STALE = 12h;
}
std::string /// How long before an RC becomes invalid (and thus deleted).
ToString() const; static constexpr auto LIFETIME = 30 * 24h;
bool /// How long before a relay updates and re-publish its RC to the network. (Relays can
BDecode(llarp_buffer_t* buf); /// re-publish more frequently than this if needed; this is meant to apply only if there are no
/// changes i.e. just to push out a new confirmation of the details).
static constexpr auto REPUBLISH = STALE / 2 - 5min;
bool ustring_view
BEncode(llarp_buffer_t* buf) const; view() const
}; {
return _payload;
}
/// RouterContact /// Getters for private attributes
struct RouterContact const oxen::quic::Address&
{ addr() const
/// for unit tests {
static bool BlockBogons; return _addr;
}
static llarp_time_t Lifetime; const std::optional<oxen::quic::Address>&
static llarp_time_t UpdateInterval; addr6() const
static llarp_time_t StaleInsertionAge; {
return _addr6;
}
RouterContact() const RouterID&
router_id() const
{ {
Clear(); return _router_id;
} }
RouterContact(std::string buf); const rc_time&
timestamp() const
{
return _timestamp;
}
protected:
// advertised addresses // advertised addresses
oxen::quic::Address addr; oxen::quic::Address _addr; // refactor all 15 uses to use addr() method
// network identifier std::optional<oxen::quic::Address> _addr6; // optional ipv6
NetID netID;
// public encryption public key
llarp::PubKey enckey;
// public signing public key // public signing public key
llarp::PubKey pubkey; RouterID _router_id; // refactor all 103 uses to use router_id() method
// signature
llarp::Signature signature;
llarp_time_t last_updated = 0s; rc_time _timestamp{};
uint64_t version = llarp::constants::proto_version;
std::optional<RouterVersion> routerVersion; // Lokinet version at the time the RC was produced
/// should we serialize the exit info? std::array<uint8_t, 3> _router_version;
const static bool serializeExit = true;
std::string signed_bt_dict; // In both Remote and Local RC's, the entire bt-encoded payload given at construction is
// emplaced here.
//
// In a RemoteRC, this value will be held for the lifetime of the object
// s.t. it can be returned upon calls to ::bt_encode.
// In a LocalRC, this value will be supplanted any time a mutator is invoked, requiring
// the re-signing of the payload.
ustring _payload;
std::vector<dns::SRVData> srvRecords; public:
/// should we serialize the exit info?
const static bool serializeExit = true;
util::StatusObject util::StatusObject
ExtractStatus() const; extract_status() const;
RouterID nlohmann::json
router_id() const to_json() const
{ {
return pubkey; return extract_status();
} }
nlohmann::json virtual std::string
ToJson() const to_string() const
{ {
return ExtractStatus(); return fmt::format(
"[RC k={} updated={} v={} addr={}]",
_router_id.ToView(),
_timestamp.time_since_epoch().count(),
RC_VERSION,
_addr.to_string());
} }
std::string bool
ToString() const; write(const fs::path& fname) const;
std::string
bt_encode() const;
void
bt_encode_subdict(oxenc::bt_list_producer& btlp) const;
std::string
bencode_signed_section() const;
std::string
ToTXTRecord() const;
bool bool
operator==(const RouterContact& other) const operator==(const RouterContact& other) const
{ {
return addr == other.addr && enckey == other.enckey && pubkey == other.pubkey return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
&& signature == other.signature && last_updated == other.last_updated and _timestamp == other._timestamp and _router_version == other._router_version;
&& netID == other.netID;
} }
bool bool
operator<(const RouterContact& other) const operator<(const RouterContact& other) const
{ {
return pubkey < other.pubkey; return _router_id < other._router_id;
} }
bool bool
@ -147,14 +167,9 @@ namespace llarp
return !(*this == other); return !(*this == other);
} }
void virtual void
Clear(); clear()
{}
bool
IsExit() const
{
return false;
}
bool bool
BDecode(llarp_buffer_t* buf); BDecode(llarp_buffer_t* buf);
@ -163,66 +178,182 @@ namespace llarp
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf); decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
bool bool
IsPublicRouter() const; is_public_router() const;
bool
Verify(llarp_time_t now, bool allowExpired = true) const;
bool
Sign(const llarp::SecretKey& secret);
/// does this RC expire soon? default delta is 1 minute /// does this RC expire soon? default delta is 1 minute
bool bool
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 1min) const; expires_within_delta(llarp_time_t now, llarp_time_t dlt = 1min) const;
/// returns true if this RC is expired and should be removed /// returns true if this RC is expired and should be removed
bool bool
IsExpired(llarp_time_t now) const; is_expired(llarp_time_t now) const;
/// returns time in ms until we expire or 0 if we have expired /// returns time in ms until we expire or 0 if we have expired
llarp_time_t llarp_time_t
TimeUntilExpires(llarp_time_t now) const; time_to_expiry(llarp_time_t now) const;
/// get the age of this RC in ms /// get the age of this RC in ms
llarp_time_t llarp_time_t
Age(llarp_time_t now) const; age(llarp_time_t now) const;
bool bool
OtherIsNewer(const RouterContact& other) const other_is_newer(const RouterContact& other) const
{ {
return last_updated < other.last_updated; return _timestamp < other._timestamp;
} }
bool bool
Read(const fs::path& fname); is_obsolete_bootstrap() const;
bool void
Write(const fs::path& fname) const; bt_load(oxenc::bt_dict_consumer& data);
};
bool /// Extension of RouterContact used to store a local "RC," and inserts a RouterContact by
VerifySignature() const; /// re-parsing and sending it out. This sub-class contains a pubkey and all the other attributes
/// required for signing and serialization
///
/// Note: this class may be entirely superfluous, so it is used here as a placeholder until its
/// marginal utility is determined. It may end up as a free-floating method that reads in
/// parameters and outputs a bt-serialized string
struct LocalRC final : public RouterContact
{
static LocalRC
make(const SecretKey secret, oxen::quic::Address local);
/// return true if the netid in this rc is for the network id we are using private:
bool ustring _signature;
FromOurNetwork() const; SecretKey _secret_key;
void
bt_sign(oxenc::bt_dict_producer& btdp);
void
bt_encode(oxenc::bt_dict_producer& btdp);
LocalRC(const SecretKey secret, oxen::quic::Address local);
public:
LocalRC() = default;
explicit LocalRC(std::string payload, const SecretKey sk);
~LocalRC() = default;
void
resign();
void
clear() override
{
_addr = {};
_addr6.reset();
_router_id.Zero();
_timestamp = {};
_router_version.fill(0);
_signature.clear();
}
bool bool
IsObsoleteBootstrap() const; operator==(const LocalRC& other) const
{
return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
and _timestamp == other._timestamp and _router_version == other._router_version
and _signature == other._signature;
}
/// Mutators for the private member attributes. Calling on the mutators
/// will clear the current signature and re-sign the RC
void
set_addr(oxen::quic::Address new_addr)
{
_addr = std::move(new_addr);
resign();
}
void
set_addr6(oxen::quic::Address new_addr)
{
_addr6 = std::move(new_addr);
resign();
}
void
set_router_id(RouterID rid)
{
_router_id = std::move(rid);
resign();
}
void
set_timestamp(llarp_time_t ts)
{
set_timestamp(rc_time{std::chrono::duration_cast<std::chrono::seconds>(ts)});
}
void
set_timestamp(rc_time ts)
{
_timestamp = ts;
}
/// Sets RC timestamp to current system clock time
void
set_systime_timestamp()
{
set_timestamp(
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
}
};
/// Extension of RouterContact used in a "read-only" fashion. Parses the incoming RC to query
/// the data in the constructor, eliminating the need for a ::verify method/
struct RemoteRC final : public RouterContact
{
private: private:
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
public:
RemoteRC() = default;
RemoteRC(std::string_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
{}
RemoteRC(ustring_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
{
_payload = data;
}
explicit RemoteRC(oxenc::bt_dict_consumer btdc);
~RemoteRC() = default;
std::string_view
view() const
{
return {reinterpret_cast<const char*>(_payload.data()), _payload.size()};
}
bool bool
DecodeVersion_0(llarp_buffer_t* buf); verify() const;
bool bool
DecodeVersion_1(oxenc::bt_list_consumer& btlist); read(const fs::path& fname);
void
clear() override
{
_addr = {};
_addr6.reset();
_router_id.Zero();
_timestamp = {};
_router_version.fill(0);
}
}; };
template <>
constexpr inline bool IsToStringFormattable<NetID> = true;
template <> template <>
constexpr inline bool IsToStringFormattable<RouterContact> = true; constexpr inline bool IsToStringFormattable<RouterContact> = true;
template <>
constexpr inline bool IsToStringFormattable<RemoteRC> = true;
template <>
constexpr inline bool IsToStringFormattable<LocalRC> = true;
using RouterLookupHandler = std::function<void(const std::vector<RouterContact>&)>; using RouterLookupHandler = std::function<void(const std::vector<RemoteRC>&)>;
} // namespace llarp } // namespace llarp
namespace std namespace std
@ -233,7 +364,7 @@ namespace std
size_t size_t
operator()(const llarp::RouterContact& r) const operator()(const llarp::RouterContact& r) const
{ {
return std::hash<llarp::PubKey>{}(r.pubkey); return std::hash<llarp::PubKey>{}(r.router_id());
} }
}; };
} // namespace std } // namespace std

@ -0,0 +1,138 @@
#include "constants/version.hpp"
#include "crypto/crypto.hpp"
#include "net/net.hpp"
#include "router_contact.hpp"
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
namespace llarp
{
LocalRC
LocalRC::make(const SecretKey secret, oxen::quic::Address local)
{
return *new LocalRC{secret, local};
}
LocalRC::LocalRC(const SecretKey secret, oxen::quic::Address local)
: _secret_key{std::move(secret)}
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
_addr = std::move(local);
_addr6.emplace(&_addr.in6());
resign();
}
LocalRC::LocalRC(std::string payload, const SecretKey sk) : _secret_key{std::move(sk)}
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
try
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
btdc.require_signature("~", [this](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()))
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse LocalRC: {}", e.what());
throw;
}
}
void
LocalRC::bt_sign(oxenc::bt_dict_producer& btdp)
{
_signature.clear();
btdp.append_signature("~", [this](ustring_view to_sign) {
std::array<unsigned char, 64> sig;
if (!crypto::sign(const_cast<unsigned char*>(sig.data()), _secret_key, to_sign))
throw std::runtime_error{"Failed to sign RC"};
_signature = {sig.data(), sig.size()};
return sig;
});
_payload = btdp.view<unsigned char>();
}
void
LocalRC::bt_encode(oxenc::bt_dict_producer& btdp)
{
btdp.append("", RC_VERSION);
std::array<unsigned char, 18> buf;
{
if (not _addr.is_ipv4())
throw std::runtime_error{"Unable to encode RC: addr is not IPv4"};
auto in4 = _addr.in4();
std::memcpy(buf.data(), &in4.sin_addr.s_addr, 4);
std::memcpy(buf.data() + 4, &in4.sin_port, 2);
btdp.append("4", ustring_view{buf.data(), 6});
}
if (_addr6)
{
if (not _addr.is_ipv6())
throw std::runtime_error{"Unable to encode RC: addr6 is set but is not IPv6"};
auto in6 = _addr.in6();
std::memcpy(buf.data(), &in6.sin6_addr.s6_addr, 16);
std::memcpy(buf.data() + 16, &in6.sin6_port, 2);
btdp.append("6", ustring_view{buf.data(), 18});
}
if (ACTIVE_NETID != llarp::LOKINET_DEFAULT_NETID)
btdp.append("i", ACTIVE_NETID);
btdp.append("p", _router_id.ToView());
btdp.append("t", _timestamp.time_since_epoch().count());
static_assert(llarp::LOKINET_VERSION.size() == 3);
btdp.append(
"v", std::string_view{reinterpret_cast<const char*>(llarp::LOKINET_VERSION.data()), 3});
bt_sign(btdp);
}
void
LocalRC::resign()
{
set_systime_timestamp();
oxenc::bt_dict_producer btdp;
bt_encode(btdp);
bt_sign(btdp);
}
} // namespace llarp

@ -0,0 +1,106 @@
#include "constants/version.hpp"
#include "crypto/crypto.hpp"
#include "net/net.hpp"
#include "router_contact.hpp"
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
namespace llarp
{
RemoteRC::RemoteRC(oxenc::bt_dict_consumer btdc)
{
try
{
bt_load(btdc);
btdc.require_signature("~", [this](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()))
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse RemoteRC: {}", e.what());
throw;
}
}
void
RemoteRC::bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired) const
{
data.require_signature("~", [this, reject_expired](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()) and reject_expired)
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
}
bool
RemoteRC::read(const fs::path& fname)
{
ustring buf;
buf.reserve(MAX_RC_SIZE);
try
{
util::file_to_buffer(fname, buf.data(), MAX_RC_SIZE);
}
catch (const std::exception& e)
{
log::error(logcat, "Failed to read RC from {}: {}", fname, e.what());
return false;
}
oxenc::bt_dict_consumer btdc{buf};
bt_load(btdc);
bt_verify(btdc);
_payload = buf;
return true;
}
bool
RemoteRC::verify() const
{
oxenc::bt_dict_consumer btdc{_payload};
bt_verify(btdc);
return true;
}
} // namespace llarp

@ -3,9 +3,11 @@
#include "util/aligned.hpp" #include "util/aligned.hpp"
#include "util/status.hpp" #include "util/status.hpp"
#include <llarp/crypto/types.hpp>
namespace llarp namespace llarp
{ {
struct RouterID : public AlignedBuffer<32> struct RouterID : public PubKey
{ {
static constexpr size_t SIZE = 32; static constexpr size_t SIZE = 32;
@ -13,10 +15,10 @@ namespace llarp
RouterID() = default; RouterID() = default;
RouterID(const byte_t* buf) : AlignedBuffer<SIZE>(buf) RouterID(const byte_t* buf) : PubKey(buf)
{} {}
RouterID(const Data& data) : AlignedBuffer<SIZE>(data) RouterID(const Data& data) : PubKey(data)
{} {}
util::StatusObject util::StatusObject

@ -51,7 +51,7 @@ namespace llarp::rpc
{ {
if (auto router = m_Router.lock()) if (auto router = m_Router.lock())
{ {
if (not router->IsServiceNode()) if (not router->is_service_node())
{ {
throw std::runtime_error("we cannot talk to lokid while not a service node"); throw std::runtime_error("we cannot talk to lokid while not a service node");
} }
@ -181,7 +181,7 @@ namespace llarp::rpc
nlohmann::json payload = { nlohmann::json payload = {
{"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())}, {"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())},
{"version", {VERSION[0], VERSION[1], VERSION[2]}}}; {"version", {LOKINET_VERSION[0], LOKINET_VERSION[1], LOKINET_VERSION[2]}}};
if (auto err = r->OxendErrorState()) if (auto err = r->OxendErrorState())
payload["error"] = *err; payload["error"] = *err;

@ -74,7 +74,7 @@ namespace llarp::rpc
std::shared_ptr<EndpointBase> std::shared_ptr<EndpointBase>
GetEndpointByName(Router& r, std::string name) GetEndpointByName(Router& r, std::string name)
{ {
if (r.IsServiceNode()) if (r.is_service_node())
{ {
return r.exitContext().GetExitEndpoint(name); return r.exitContext().GetExitEndpoint(name);
} }
@ -154,7 +154,7 @@ namespace llarp::rpc
RPCServer::invoke(Version& version) RPCServer::invoke(Version& version)
{ {
util::StatusObject result{ util::StatusObject result{
{"version", llarp::VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}}; {"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}};
SetJSONResponse(result, version.response); SetJSONResponse(result, version.response);
} }
@ -309,7 +309,7 @@ namespace llarp::rpc
void void
RPCServer::invoke(LookupSnode& lookupsnode) RPCServer::invoke(LookupSnode& lookupsnode)
{ {
if (not m_Router.IsServiceNode()) if (not m_Router.is_service_node())
{ {
SetJSONError("Not supported", lookupsnode.response); SetJSONError("Not supported", lookupsnode.response);
return; return;

@ -206,97 +206,57 @@ namespace llarp::service
std::string service, std::string service,
std::function<void(std::vector<dns::SRVData>)> resultHandler) std::function<void(std::vector<dns::SRVData>)> resultHandler)
{ {
// handles when we aligned to a loki address // A lookup goes through a chain of events:
auto handleGotPathToService = [resultHandler, service, this](auto addr) { // - see if the name is ONS, and if so resolve it to a ADDR.loki
// we can probably get this info before we have a path to them but we do this after we // - once we've resolved to ADDR.loki then initiate a path to it
// have a path so when we send the response back they can send shit to them immediately // - once we have a path, consult the remote's introset to pull out the SRV records
const auto& container = _state->remote_sessions; // If we fail along the way (e.g. it's a .snode, we can't build a path, or whatever else) then
if (auto itr = container.find(addr); itr != container.end()) // we invoke the resultHandler with an empty vector.
{ lookup_name(
// parse the stuff we need from this guy name, [this, resultHandler, service = std::move(service)](oxen::quic::message m) mutable {
resultHandler(itr->second->GetCurrentIntroSet().GetMatchingSRVRecords(service)); if (!m)
return; return resultHandler({});
}
resultHandler({});
};
// handles when we resolved a .snode
auto handleResolvedSNodeName = [resultHandler, nodedb = router()->node_db()](auto router_id) {
std::vector<dns::SRVData> result{};
if (auto maybe_rc = nodedb->get_rc(router_id))
{
result = maybe_rc->srvRecords;
}
resultHandler(std::move(result));
};
// handles when we got a path to a remote thing
auto handleGotPathTo = [handleGotPathToService, handleResolvedSNodeName, resultHandler](
auto maybe_tag, auto address) {
if (not maybe_tag)
{
resultHandler({});
return;
}
if (auto* addr = std::get_if<Address>(&address))
{
// .loki case
handleGotPathToService(*addr);
}
else if (auto* router_id = std::get_if<RouterID>(&address))
{
// .snode case
handleResolvedSNodeName(*router_id);
}
else
{
// fallback case
// XXX: never should happen but we'll handle it anyways
resultHandler({});
}
};
// handles when we know a long address of a remote resource
auto handleGotAddress = [resultHandler, handleGotPathTo, this](AddressVariant_t address) {
// we will attempt a build to whatever we looked up
const auto result = EnsurePathTo(
address,
[address, handleGotPathTo](auto maybe_tag) { handleGotPathTo(maybe_tag, address); },
PathAlignmentTimeout());
// on path build start fail short circuit
if (not result)
resultHandler({});
};
// look up this name async and start the entire chain of events
lookup_name(name, [handleGotAddress, resultHandler](oxen::quic::message m) mutable {
if (m)
{
std::string name;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
name = btdc.require<std::string>("NAME");
}
catch (...)
{
log::warning(link_cat, "Failed to parse find name response!");
throw;
}
if (auto saddr = service::Address(); saddr.FromString(name)) std::string name;
handleGotAddress(saddr); try
{
oxenc::bt_dict_consumer btdc{m.body()};
name = btdc.require<std::string>("NAME");
}
catch (...)
{
log::warning(link_cat, "Failed to parse find name response!");
throw;
}
if (auto rid = RouterID(); rid.FromString(name)) auto saddr = service::Address();
handleGotAddress(rid); if (!saddr.FromString(name))
} return resultHandler({}); // Not a regular ADDR.loki so doesn't support SRV
else
{ // initiate path build
resultHandler({}); const auto build_started = EnsurePathTo(
} saddr,
}); [this, address = std::move(saddr), resultHandler, service = std::move(service)](
auto maybe_tag) {
if (not maybe_tag)
return resultHandler({});
// we can probably get this info before we have a path to them but we do this after
// we have a path so when we send the DNS response back they can talk to them
// immediately
const auto& container = _state->remote_sessions;
if (auto itr = container.find(address); itr != container.end())
// parse the stuff we need from this guy
resultHandler(itr->second->GetCurrentIntroSet().GetMatchingSRVRecords(service));
else
resultHandler({});
},
PathAlignmentTimeout());
// on path build start fail short circuit
if (not build_started)
resultHandler({});
});
} }
bool bool
@ -754,22 +714,22 @@ namespace llarp::service
return now >= next_pub; return now >= next_pub;
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
Endpoint::GetHopsForBuild() Endpoint::GetHopsForBuild()
{ {
std::unordered_set<RouterID> exclude; std::unordered_set<RouterID> exclude;
ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); }); ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); });
const auto maybe = const auto maybe =
router()->node_db()->GetRandom([exclude, r = router()](const auto& rc) -> bool { router()->node_db()->GetRandom([exclude, r = router()](const RemoteRC& rc) -> bool {
return exclude.count(rc.pubkey) == 0 const auto& rid = rc.router_id();
and not r->router_profiling().IsBadForPath(rc.pubkey); return exclude.count(rid) == 0 and not r->router_profiling().IsBadForPath(rid);
}); });
if (not maybe.has_value()) if (not maybe.has_value())
return std::nullopt; return std::nullopt;
return GetHopsForBuildWithEndpoint(maybe->pubkey); return GetHopsForBuildWithEndpoint(maybe->router_id());
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
Endpoint::GetHopsForBuildWithEndpoint(RouterID endpoint) Endpoint::GetHopsForBuildWithEndpoint(RouterID endpoint)
{ {
return path::Builder::GetHopsAlignedToForBuild(endpoint, SnodeBlacklist()); return path::Builder::GetHopsAlignedToForBuild(endpoint, SnodeBlacklist());

@ -409,10 +409,10 @@ namespace llarp
bool bool
HasExit() const; HasExit() const;
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsForBuild() override; GetHopsForBuild() override;
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsForBuildWithEndpoint(RouterID endpoint); GetHopsForBuildWithEndpoint(RouterID endpoint);
void void

@ -4,16 +4,32 @@
namespace llarp::service namespace llarp::service
{ {
bool std::string
Identity::BEncode(llarp_buffer_t* buf) const Identity::bt_encode() const
{ {
if (!bencode_start_dict(buf)) oxenc::bt_dict_producer btdp;
return false;
if (!BEncodeWriteDictEntry("s", signkey, buf)) btdp.append("s", signkey.ToView());
return false; btdp.append("v", version);
if (!BEncodeWriteDictInt("v", version, buf))
return false; return std::move(btdp).str();
return bencode_end(buf); }
void
Identity::bt_decode(std::string buf)
{
try
{
oxenc::bt_dict_consumer btdc{buf};
signkey.from_string(btdc.require<std::string>("s"));
version = btdc.require<uint64_t>("v");
}
catch (...)
{
log::warning(logcat, "Identity failed to parse bt-encoded contents!");
throw;
}
} }
bool bool
@ -44,7 +60,7 @@ namespace llarp::service
{ {
crypto::identity_keygen(signkey); crypto::identity_keygen(signkey);
crypto::encryption_keygen(enckey); crypto::encryption_keygen(enckey);
pub.Update(seckey_topublic(signkey), seckey_topublic(enckey)); pub.Update(seckey_to_pubkey(signkey), seckey_to_pubkey(enckey));
crypto::pqe_keygen(pq); crypto::pqe_keygen(pq);
if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1)) if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1))
{ {
@ -74,7 +90,7 @@ namespace llarp::service
// make sure we are empty // make sure we are empty
Clear(); Clear();
std::array<byte_t, 4096> tmp; std::string buf;
// this can throw // this can throw
bool exists = fs::exists(fname); bool exists = fs::exists(fname);
@ -88,16 +104,15 @@ namespace llarp::service
// check for file // check for file
if (!exists) if (!exists)
{ {
llarp_buffer_t buf{tmp};
// regen and encode // regen and encode
RegenerateKeys(); RegenerateKeys();
if (!BEncode(&buf))
throw std::length_error("failed to encode new identity"); buf = bt_encode();
const auto sz = buf.cur - buf.base;
// write // write
try try
{ {
util::dump_file(fname, tmp.data(), sz); util::buffer_to_file(fname, buf.data(), buf.size());
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -114,18 +129,15 @@ namespace llarp::service
// read file // read file
try try
{ {
util::slurp_file(fname, tmp.data(), tmp.size()); util::file_to_buffer(fname, buf.data(), buf.size());
} }
catch (const std::length_error&) catch (const std::length_error&)
{ {
throw std::length_error{"service identity too big"}; throw std::length_error{"service identity too big"};
} }
// (don't catch io error exceptions) // (don't catch io error exceptions)
{ bt_decode(buf);
llarp_buffer_t buf{tmp};
if (!bencode_decode_dict(*this, &buf))
throw std::length_error{"could not decode service identity"};
}
// ensure that the encryption key is set // ensure that the encryption key is set
if (enckey.IsZero()) if (enckey.IsZero())
@ -139,7 +151,7 @@ namespace llarp::service
if (!vanity.IsZero()) if (!vanity.IsZero())
van = vanity; van = vanity;
// update pubkeys // update pubkeys
pub.Update(seckey_topublic(signkey), seckey_topublic(enckey), van); pub.Update(seckey_to_pubkey(signkey), seckey_to_pubkey(enckey), van);
if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1)) if (not crypto::derive_subkey_private(derivedSignKey, signkey, 1))
{ {
throw std::runtime_error("failed to derive subkey"); throw std::runtime_error("failed to derive subkey");
@ -163,7 +175,7 @@ namespace llarp::service
// set service info // set service info
i.address_keys = pub; i.address_keys = pub;
// set public encryption key // set public encryption key
i.sntru_pubkey = pq_keypair_to_public(pq); i.sntru_pubkey = pq_keypair_to_pubkey(pq);
auto bte = i.bt_encode(); auto bte = i.bt_encode();

@ -31,8 +31,10 @@ namespace llarp::service
void void
RegenerateKeys(); RegenerateKeys();
bool std::string
BEncode(llarp_buffer_t* buf) const; bt_encode() const;
void bt_decode(std::string);
/// @param needBackup determines whether existing keys will be cycled /// @param needBackup determines whether existing keys will be cycled
void void

@ -350,7 +350,7 @@ namespace llarp::service
return false; return false;
} }
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
OutboundContext::GetHopsForBuild() OutboundContext::GetHopsForBuild()
{ {
if (next_intro.router.IsZero()) if (next_intro.router.IsZero())

@ -152,7 +152,7 @@ namespace llarp::service
void void
HandlePathBuildFailedAt(path::Path_ptr path, RouterID hop) override; HandlePathBuildFailedAt(path::Path_ptr path, RouterID hop) override;
std::optional<std::vector<RouterContact>> std::optional<std::vector<RemoteRC>>
GetHopsForBuild() override; GetHopsForBuild() override;
std::string std::string

@ -256,7 +256,7 @@ namespace llarp::service
// copy // copy
ProtocolFrameMessage frame(self->frame); ProtocolFrameMessage frame(self->frame);
if (!crypto::pqe_decrypt( if (!crypto::pqe_decrypt(
self->frame.cipher, K, pq_keypair_to_secret(self->m_LocalIdentity.pq))) self->frame.cipher, K, pq_keypair_to_seckey(self->m_LocalIdentity.pq)))
{ {
LogError("pqke failed C=", self->frame.cipher); LogError("pqke failed C=", self->frame.cipher);
self->msg.reset(); self->msg.reset();

@ -130,12 +130,6 @@ namespace llarp
std::string std::string
bt_encode() const; bt_encode() const;
bool
BDecode(llarp_buffer_t* buf)
{
return bencode_decode_dict(*this, buf);
}
void void
clear() clear()
{ {

@ -4,13 +4,10 @@
#include <utility> #include <utility>
namespace llarp namespace llarp::service
{ {
namespace service RouterLookupJob::RouterLookupJob(Endpoint* p, RouterLookupHandler h)
{ : handler(std::move(h)), txid(p->GenTXID()), started(p->Now())
RouterLookupJob::RouterLookupJob(Endpoint* p, RouterLookupHandler h) {}
: handler(std::move(h)), txid(p->GenTXID()), started(p->Now())
{}
} // namespace service } // namespace llarp::service
} // namespace llarp

@ -2,34 +2,31 @@
#include <llarp/router_contact.hpp> #include <llarp/router_contact.hpp>
namespace llarp namespace llarp::service
{ {
namespace service struct Endpoint;
{
struct Endpoint;
struct RouterLookupJob struct RouterLookupJob
{ {
RouterLookupJob(Endpoint* p, RouterLookupHandler h); RouterLookupJob(Endpoint* p, RouterLookupHandler h);
RouterLookupHandler handler; RouterLookupHandler handler;
uint64_t txid; uint64_t txid;
llarp_time_t started; llarp_time_t started;
bool bool
IsExpired(llarp_time_t now) const IsExpired(llarp_time_t now) const
{ {
if (now < started) if (now < started)
return false; return false;
return now - started > 30s; return now - started > 30s;
} }
void void
InformResult(std::vector<RouterContact> result) InformResult(std::vector<RemoteRC> result)
{ {
if (handler) if (handler)
handler(result); handler(result);
} }
}; };
} // namespace service } // namespace llarp::service
} // namespace llarp

@ -17,16 +17,17 @@ namespace llarp
{ {
static auto ben_cat = log::Cat("stupid.bencode"); static auto ben_cat = log::Cat("stupid.bencode");
template <typename T>
T
decode_key(oxenc::bt_dict_consumer& btdp, const char* key)
{
return btdp.require<T>(key);
}
template <typename List_t> template <typename List_t>
bool bool
BEncodeReadList(List_t& result, llarp_buffer_t* buf); BEncodeReadList(List_t& result, llarp_buffer_t* buf);
inline bool
BEncodeWriteDictMsgType(llarp_buffer_t* buf, const char* k, const char* t)
{
return bencode_write_bytestring(buf, k, 1) && bencode_write_bytestring(buf, t, 1);
}
template <typename Obj_t> template <typename Obj_t>
bool bool
BEncodeWriteDictString(const char* k, const Obj_t& str, llarp_buffer_t* buf) BEncodeWriteDictString(const char* k, const Obj_t& str, llarp_buffer_t* buf)
@ -320,23 +321,6 @@ namespace llarp
buffer); buffer);
} }
/// write an iterable container as a list
template <typename Set_t>
bool
BEncodeWriteSet(const Set_t& set, llarp_buffer_t* buffer)
{
if (not bencode_start_list(buffer))
return false;
for (const auto& item : set)
{
if (not item.bt_encode(buffer))
return false;
}
return bencode_end(buffer);
}
template <typename List_t> template <typename List_t>
bool bool
BEncodeWriteDictList(const char* k, List_t& list, llarp_buffer_t* buf) BEncodeWriteDictList(const char* k, List_t& list, llarp_buffer_t* buf)
@ -365,7 +349,7 @@ namespace llarp
std::string content; std::string content;
try try
{ {
content = util::slurp_file(fpath); content = util::file_to_string(fpath);
} }
catch (const std::exception&) catch (const std::exception&)
{ {
@ -389,7 +373,7 @@ namespace llarp
tmp.resize(buf.cur - buf.base); tmp.resize(buf.cur - buf.base);
try try
{ {
util::dump_file(fpath, tmp); util::buffer_to_file(fpath, tmp);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -19,7 +19,7 @@
namespace llarp::util namespace llarp::util
{ {
static std::streampos static std::streampos
slurp_file_open(const fs::path& filename, fs::ifstream& in) file_reader_impl(const fs::path& filename, fs::ifstream& in)
{ {
in.exceptions(std::ifstream::failbit | std::ifstream::badbit); in.exceptions(std::ifstream::failbit | std::ifstream::badbit);
in.open(filename, std::ios::binary | std::ios::in); in.open(filename, std::ios::binary | std::ios::in);
@ -30,21 +30,21 @@ namespace llarp::util
} }
std::string std::string
slurp_file(const fs::path& filename) file_to_string(const fs::path& filename)
{ {
fs::ifstream in; fs::ifstream in;
std::string contents; std::string contents;
auto size = slurp_file_open(filename, in); auto size = file_reader_impl(filename, in);
contents.resize(size); contents.resize(size);
in.read(contents.data(), size); in.read(contents.data(), size);
return contents; return contents;
} }
size_t size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size) file_to_buffer(const fs::path& filename, char* buffer, size_t buffer_size)
{ {
fs::ifstream in; fs::ifstream in;
auto size = slurp_file_open(filename, in); auto size = file_reader_impl(filename, in);
if (static_cast<size_t>(size) > buffer_size) if (static_cast<size_t>(size) > buffer_size)
throw std::length_error{"file is too large for buffer"}; throw std::length_error{"file is too large for buffer"};
in.read(buffer, size); in.read(buffer, size);
@ -52,7 +52,7 @@ namespace llarp::util
} }
void void
dump_file(const fs::path& filename, std::string_view contents) buffer_to_file(const fs::path& filename, std::string_view contents)
{ {
fs::ofstream out; fs::ofstream out;
out.exceptions(std::ifstream::failbit | std::ifstream::badbit); out.exceptions(std::ifstream::failbit | std::ifstream::badbit);

@ -14,34 +14,34 @@ namespace llarp::util
{ {
/// Reads a binary file from disk into a string. Throws on error. /// Reads a binary file from disk into a string. Throws on error.
std::string std::string
slurp_file(const fs::path& filename); file_to_string(const fs::path& filename);
/// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the /// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the
/// file is bigger than the buffer. Returns the bytes copied on success. /// file is bigger than the buffer. Returns the bytes copied on success.
size_t size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size); file_to_buffer(const fs::path& filename, char* buffer, size_t buffer_size);
/// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char). /// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char).
template < template <
typename Char, typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1> std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
inline size_t inline size_t
slurp_file(const fs::path& filename, Char* buffer, size_t buffer_size) file_to_buffer(const fs::path& filename, Char* buffer, size_t buffer_size)
{ {
return slurp_file(filename, reinterpret_cast<char*>(buffer), buffer_size); return file_to_buffer(filename, reinterpret_cast<char*>(buffer), buffer_size);
} }
/// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws /// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws
/// on error. /// on error.
void void
dump_file(const fs::path& filename, std::string_view contents); buffer_to_file(const fs::path& filename, std::string_view contents);
/// Same as above, but works via char-like buffer /// Same as above, but works via char-like buffer
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0> template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
inline void inline void
dump_file(const fs::path& filename, const Char* buffer, size_t buffer_size) buffer_to_file(const fs::path& filename, const Char* buffer, size_t buffer_size)
{ {
return dump_file( return buffer_to_file(
filename, std::string_view{reinterpret_cast<const char*>(buffer), buffer_size}); filename, std::string_view{reinterpret_cast<const char*>(buffer), buffer_size});
} }

Loading…
Cancel
Save