Compare commits

...

5 Commits

Author SHA1 Message Date
dr7ana 1939ba0b3d
Merge pull request #2218 from dr7ana/rc-bencode
RC Refactor + (some) BT-encode fixes
6 months ago
dr7ana af0ac28119 Review fixes + misc fixes 6 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.
6 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)
6 months ago
dr7ana 07271f9ae7 RC refactor layout
- Local and Remote RC's now implemented with discrete functionalities and uses
6 months ago

@ -41,7 +41,7 @@ if(APPLE)
set(LOKINET_APPLE_BUILD 5)
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")

@ -86,7 +86,7 @@ main(int argc, char* argv[])
#else
cpr::Get(
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()}));
#endif
if (resp.status_code != 200)

@ -403,7 +403,7 @@ namespace
{
if (options.version)
{
std::cout << llarp::VERSION_FULL << std::endl;
std::cout << llarp::LOKINET_VERSION_FULL << std::endl;
return 0;
}
@ -548,7 +548,8 @@ namespace
static void
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
{
std::shared_ptr<llarp::Config> conf;

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

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

@ -6,66 +6,65 @@
namespace llarp
{
void
BootstrapList::Clear()
{
clear();
}
bool
BootstrapList::BDecode(llarp_buffer_t* buf)
BootstrapList::bt_decode(std::string_view buf)
{
return bencode_read_list(
[&](llarp_buffer_t* b, bool more) -> bool {
if (more)
{
RouterContact rc{};
if (not rc.BDecode(b))
{
LogError("invalid rc in bootstrap list: ", llarp::buffer_printer{*b});
return false;
}
emplace(std::move(rc));
}
return true;
},
buf);
try
{
oxenc::bt_list_consumer btlc{buf};
while (not btlc.is_finished())
emplace(btlc.consume_dict_consumer());
}
catch (...)
{
log::warning(logcat, "Unable to decode bootstrap RemoteRC");
return false;
}
return true;
}
bool
BootstrapList::BEncode(llarp_buffer_t* buf) const
std::string_view
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
BootstrapList::AddFromFile(fs::path fpath)
BootstrapList::read_from_file(const fs::path& fpath)
{
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);
if (inf.is_open())
{
const char ch = inf.get();
isListFile = ch == 'l';
}
const char ch = inf.get();
isListFile = ch == 'l';
}
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)};
}
}
else
{
RouterContact rc;
if (not rc.Read(fpath))
RemoteRC rc;
if (not rc.read(fpath))
{
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

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

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

@ -58,14 +58,15 @@ namespace llarp
conf.defineOption<std::string>(
"router",
"netid",
Default{llarp::DEFAULT_NETID},
Default{llarp::LOKINET_DEFAULT_NETID},
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) {
if (arg.size() > NetID::size())
if (arg.size() > NETID_SIZE)
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);
});
@ -1321,7 +1322,7 @@ namespace llarp
}
bool
PeerSelectionConfig::Acceptable(const std::set<RouterContact>& rcs) const
PeerSelectionConfig::Acceptable(const std::set<RemoteRC>& rcs) const
{
if (m_UniqueHopsNetmaskSize == 0)
return true;
@ -1329,7 +1330,7 @@ namespace llarp
std::set<IPRange> seenRanges;
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)
{
return false;
@ -1444,7 +1445,7 @@ namespace llarp
{
try
{
ini = util::slurp_file(*fname);
ini = util::file_to_string(*fname);
}
catch (const std::exception&)
{
@ -1529,7 +1530,7 @@ namespace llarp
// open a filestream
try
{
util::dump_file(confFile, confStr);
util::buffer_to_file(confFile, confStr);
}
catch (const std::exception& e)
{

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

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

@ -51,8 +51,8 @@ namespace llarp
m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile);
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile);
RouterContact rc;
bool exists = rc.Read(m_rcPath);
RemoteRC rc;
bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent)
{
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
// 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
// older encryption) and needs to be regenerated. before doing so, backup

@ -4,12 +4,11 @@
namespace llarp
{
// clang-format off
const std::array<uint16_t, 3> 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 VERSION_TAG = "@VERSIONTAG@";
const char* const VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@VERSIONTAG@";
const std::array<uint16_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const char* const LOKINET_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 RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const DEFAULT_NETID = "lokinet";
const char* const LOKINET_RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const LOKINET_DEFAULT_NETID = "lokinet";
// clang-format on
} // namespace llarp

@ -6,11 +6,10 @@
namespace llarp
{
// 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<uint64_t, 4> ROUTER_VERSION; // [proto, 1, 2, 3]
extern const char* const VERSION_TAG; // "abc"
extern const char* const VERSION_FULL; // "lokinet-1.2.3-abc"
extern const std::array<uint16_t, 3> LOKINET_VERSION;
extern const char* const LOKINET_VERSION_TAG;
extern const char* const LOKINET_VERSION_FULL;
extern const char* const RELEASE_MOTTO;
extern const char* const DEFAULT_NETID;
extern const char* const LOKINET_RELEASE_MOTTO;
extern const char* const LOKINET_DEFAULT_NETID;
} // namespace llarp

@ -19,8 +19,6 @@
namespace llarp
{
static auto logcat = llarp::log::Cat("llarp-context");
bool
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");
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)
{
@ -162,7 +161,7 @@ namespace llarp
#ifndef _WIN32
if (sig == SIGUSR1)
{
if (router and not router->IsServiceNode())
if (router and not router->is_service_node())
{
LogInfo("SIGUSR1: resetting network state");
router->Thaw();

@ -273,6 +273,14 @@ namespace llarp
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
crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig)
{
@ -428,11 +436,6 @@ namespace llarp
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
crypto::randomize(uint8_t* buf, size_t len)
{
@ -517,19 +520,19 @@ namespace llarp
#endif
const byte_t*
seckey_topublic(const SecretKey& sec)
seckey_to_pubkey(const SecretKey& sec)
{
return sec.data() + 32;
}
const byte_t*
pq_keypair_to_public(const PQKeyPair& k)
pq_keypair_to_pubkey(const PQKeyPair& k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t*
pq_keypair_to_secret(const PQKeyPair& k)
pq_keypair_to_seckey(const PQKeyPair& k)
{
return k.data();
}

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

@ -1,5 +1,6 @@
#include "types.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/buffer.hpp>
#include <llarp/util/file.hpp>
@ -32,6 +33,30 @@ namespace llarp
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
SecretKey::LoadFromFile(const fs::path& fname)
{
@ -39,7 +64,7 @@ namespace llarp
std::array<byte_t, 128> tmp;
try
{
sz = util::slurp_file(fname, tmp.data(), tmp.size());
sz = util::file_to_buffer(fname, tmp.data(), tmp.size());
}
catch (const std::exception&)
{
@ -93,67 +118,17 @@ namespace llarp
bool
SecretKey::SaveToFile(const fs::path& fname) const
{
std::string tmp(128, 0);
llarp_buffer_t buf(tmp);
if (!bt_encode(&buf))
return false;
auto bte = bt_encode();
tmp.resize(buf.cur - buf.base);
try
{
util::dump_file(fname, tmp);
}
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());
util::buffer_to_file(fname, bte);
}
catch (const std::exception& e)
{
llarp::LogError("failed to load service node seed: ", e.what());
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*
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;
return true;
}
} // namespace llarp

@ -2,7 +2,6 @@
#include "constants.hpp"
#include <llarp/router_id.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/types.hpp>
@ -15,7 +14,9 @@ namespace llarp
using SharedSecret = AlignedBuffer<SHAREDKEYSIZE>;
using KeyExchangeNonce = AlignedBuffer<32>;
struct PubKey final : public AlignedBuffer<PUBKEYSIZE>
struct RouterID;
struct PubKey : public AlignedBuffer<PUBKEYSIZE>
{
PubKey() = default;
@ -37,36 +38,17 @@ namespace llarp
static PubKey
from_string(const std::string& s);
operator RouterID() const
{
return {as_array()};
}
operator RouterID() const;
PubKey&
operator=(const byte_t* ptr)
{
std::copy(ptr, ptr + SIZE, begin());
return *this;
}
operator=(const byte_t* ptr);
};
inline bool
operator==(const PubKey& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const PubKey& lhs, const PubKey& rhs);
inline bool
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();
}
bool
operator==(const PubKey& lhs, const RouterID& rhs);
struct PrivateKey;
@ -161,58 +143,24 @@ namespace llarp
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 <>
constexpr inline bool IsToStringFormattable<PubKey> = true;
template <>
constexpr inline bool IsToStringFormattable<SecretKey> = true;
template <>
constexpr inline bool IsToStringFormattable<PrivateKey> = true;
template <>
constexpr inline bool IsToStringFormattable<IdentitySecret> = true;
using ShortHash = AlignedBuffer<SHORTHASHSIZE>;
using LongHash = AlignedBuffer<HASHSIZE>;
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 SymmNonce = AlignedBuffer<NONCESIZE>;
using SymmKey = AlignedBuffer<32>;
using SymmKey = AlignedBuffer<32>; // not used
using PQCipherBlock = AlignedBuffer<PQ_CIPHERTEXTSIZE + 1>;
using PQPubKey = AlignedBuffer<PQ_PUBKEYSIZE>;

@ -22,7 +22,7 @@ namespace llarp::dht
bool
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

@ -19,19 +19,19 @@ namespace llarp::dht
ID.Zero();
}
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey)
RCNode(const RouterContact& other) : rc(other), ID(other.router_id())
{}
util::StatusObject
ExtractStatus() const
{
return rc.ExtractStatus();
return rc.extract_status();
}
bool
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));
}
std::optional<std::vector<RouterContact>>
std::optional<std::vector<RemoteRC>>
BaseSession::GetHopsForBuild()
{
if (numHops == 1)
{
if (auto maybe = router->node_db()->get_rc(exit_router))
return std::vector<RouterContact>{*maybe};
return std::vector<RemoteRC>{*maybe};
return std::nullopt;
}
@ -292,7 +292,7 @@ namespace llarp::exit
throw;
}
RouterContact result{std::move(payload)};
RemoteRC result{std::move(payload)};
r->node_db()->put_rc_if_newer(result);
r->connect_to(result);
}

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

@ -578,29 +578,7 @@ namespace llarp::handlers
void
ExitEndpoint::SRVRecordsChanged()
{
router->modify_rc(
[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;
});
// TODO: Investigate the usage or the term exit RE: service nodes acting as exits
}
std::optional<EndpointBase::SendStat>

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

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

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

@ -18,9 +18,9 @@ namespace llarp
namespace link
{
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 nullptr;
@ -83,7 +83,7 @@ namespace llarp
}
bool
Endpoint::get_random_connection(RouterContact& router) const
Endpoint::get_random_connection(RemoteRC& router) const
{
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.
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
// would be executed for another failure case
return;
}
auto& remote_addr = rc.addr;
const auto& remote_addr = rc.addr();
// TODO: confirm remote end is using the expected pubkey (RouterID).
// TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation)
@ -451,7 +451,7 @@ namespace llarp
}
bool
LinkManager::get_random_connected(RouterContact& router) const
LinkManager::get_random_connected(RemoteRC& router) const
{
return ep.get_random_connection(router);
}
@ -487,13 +487,15 @@ namespace llarp
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))
{
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;
connect_to(*maybe_other);
@ -609,18 +611,19 @@ namespace llarp
target_rid.FromString(target_key);
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};
if (is_exploratory)
{
std::string neighbors{};
const auto closest_rcs =
_router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY);
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
|| local_rid == rid)
continue;
@ -635,12 +638,12 @@ namespace llarp
else
{
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};
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(
target_rid,
@ -652,7 +655,7 @@ namespace llarp
}
else
{
m.respond(serialize_response({{"RC", closest_rc.bt_encode()}}));
m.respond(serialize_response({{"RC", closest_rc.view()}}));
}
}
else if (not is_iterative)
@ -718,7 +721,7 @@ namespace llarp
if (m)
{
_router.node_db()->put_rc_if_newer(RouterContact{payload});
_router.node_db()->put_rc_if_newer(RemoteRC{payload});
}
else
{
@ -837,7 +840,7 @@ namespace llarp
const auto now = _router.now();
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))
{
@ -878,7 +881,7 @@ namespace llarp
log::info(link_cat, "Relaying PublishIntroMessage for {}", addr);
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)
{
@ -908,7 +911,7 @@ namespace llarp
for (const auto& rc : closest_rcs)
{
if (rc.pubkey == local_key)
if (rc.router_id() == local_key)
{
rc_index = index;
break;
@ -1024,7 +1027,7 @@ namespace llarp
log::info(link_cat, "Relaying FindIntroMessage for {}", addr);
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(
peer_key,

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

@ -22,7 +22,7 @@ namespace llarp
hop.nonce.Randomize();
// 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!");
log::warning(path_cat, err);
@ -60,7 +60,7 @@ namespace llarp
outer_nonce.Randomize();
// 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!");
throw std::runtime_error{"DH failed during hop info encryption"};

@ -54,7 +54,7 @@ namespace llarp
bool
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)
{
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
@ -110,7 +110,7 @@ namespace llarp
bool
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)
{
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
{
static auto logcat = log::Cat("nodedb");
NodeDB::Entry::Entry(RouterContact value) : rc(std::move(value)), insertedAt(llarp::time_now_ms())
NodeDB::Entry::Entry(RemoteRC value) : rc(std::move(value)), insertedAt(llarp::time_now_ms())
{}
static void
@ -72,14 +70,16 @@ namespace llarp
router.loop()->call([this]() {
m_NextFlushAt += FlushInterval;
// make copy of all rcs
std::vector<RouterContact> copy;
std::vector<RemoteRC> copy;
for (const auto& item : entries)
copy.push_back(item.second.rc);
// flush them to disk in one big job
// TODO: split this up? idk maybe some day...
disk([this, data = std::move(copy)]() {
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))
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
purge.emplace(f);
return true;
}
if (not rc.FromOurNetwork())
{
// skip entries that are not from our network
return true;
}
if (rc.IsExpired(time_now_ms()))
if (rc.is_expired(time_now_ms()))
{
// rc expired dont load it and purge it later
purge.emplace(f);
@ -145,8 +139,8 @@ namespace llarp
// validate signature and purge entries with invalid signatures
// load ones with valid signatures
if (rc.VerifySignature())
entries.emplace(rc.pubkey, rc);
if (rc.verify())
entries.emplace(rc.router_id(), rc);
else
purge.emplace(f);
@ -172,7 +166,7 @@ namespace llarp
router.loop()->call([this]() {
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(); });
}
std::optional<RouterContact>
std::optional<RemoteRC>
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);
if (itr == entries.end())
@ -213,9 +207,9 @@ namespace llarp
auto itr = entries.begin();
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);
}
else
@ -227,11 +221,12 @@ namespace llarp
}
void
NodeDB::put_rc(RouterContact rc)
NodeDB::put_rc(RemoteRC rc)
{
router.loop()->call([this, rc]() {
entries.erase(rc.pubkey);
entries.emplace(rc.pubkey, rc);
const auto& rid = rc.router_id();
entries.erase(rid);
entries.emplace(rid, rc);
});
}
@ -242,17 +237,17 @@ namespace llarp
}
void
NodeDB::put_rc_if_newer(RouterContact rc)
NodeDB::put_rc_if_newer(RemoteRC rc)
{
router.loop()->call([this, rc]() {
auto itr = entries.find(rc.pubkey);
if (itr == entries.end() or itr->second.rc.OtherIsNewer(rc))
auto itr = entries.find(rc.router_id());
if (itr == entries.end() or itr->second.rc.other_is_newer(rc))
{
// delete if existing
if (itr != entries.end())
entries.erase(itr);
// 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
{
return router.loop()->call_get([this, location]() {
llarp::RouterContact rc;
return router.loop()->call_get([this, location]() -> RemoteRC {
RemoteRC rc;
const llarp::dht::XorMetric compare(location);
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;
return;
}
if (compare(
llarp::dht::Key_t{otherRC.pubkey.as_array()},
llarp::dht::Key_t{rc.pubkey.as_array()}))
rc = otherRC;
});
return rc;
});
}
std::vector<RouterContact>
std::vector<RemoteRC>
NodeDB::find_many_closest_to(llarp::dht::Key_t location, uint32_t numRouters) const
{
return router.loop()->call_get([this, location, numRouters]() {
std::vector<const RouterContact*> all;
return router.loop()->call_get([this, location, numRouters]() -> std::vector<RemoteRC> {
std::vector<const RemoteRC*> all;
all.reserve(entries.size());
for (auto& entry : entries)
@ -314,7 +308,7 @@ namespace llarp
return compare(*a, *b);
});
std::vector<RouterContact> closest;
std::vector<RemoteRC> closest;
closest.reserve(numRouters);
for (auto it = all.begin(); it != it_mid; ++it)
closest.push_back(**it);

@ -26,9 +26,9 @@ namespace llarp
{
struct Entry
{
const RouterContact rc;
const RemoteRC rc;
llarp_time_t insertedAt;
explicit Entry(RouterContact rc);
explicit Entry(RemoteRC rc);
};
using NodeMap = std::unordered_map<RouterID, Entry>;
@ -73,11 +73,11 @@ namespace llarp
Tick(llarp_time_t now);
/// find the absolute closets router to a dht location
RouterContact
RemoteRC
find_closest_to(dht::Key_t location) const;
/// 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;
/// return true if we have an rc by its ident pubkey
@ -85,14 +85,14 @@ namespace llarp
has_router(RouterID pk) const;
/// maybe get an rc by its ident pubkey
std::optional<RouterContact>
std::optional<RemoteRC>
get_rc(RouterID pk) const;
template <typename Filter>
std::optional<RouterContact>
std::optional<RemoteRC>
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;
for (const auto& entry : entries)
entries.push_back(entry);
@ -150,7 +150,7 @@ namespace llarp
{
if (visit(itr->second.rc))
{
removed.insert(itr->second.rc.pubkey);
removed.insert(itr->second.rc.router_id());
itr = entries.erase(itr);
}
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
void
put_rc_if_newer(RouterContact rc);
put_rc_if_newer(RemoteRC rc);
/// unconditional put of rc into cache
void
put_rc(RouterContact rc);
put_rc(RemoteRC rc);
};
} // namespace llarp

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

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

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

@ -83,7 +83,7 @@ namespace llarp
hop.nonce.Randomize();
// 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());
log::error(path_cat, err);
@ -121,7 +121,7 @@ namespace llarp
outer_nonce.Randomize();
// 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!");
throw std::runtime_error{"DH failed during hop info encryption"};
@ -211,23 +211,25 @@ namespace llarp
return obj;
}
std::optional<RouterContact>
std::optional<RemoteRC>
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) {
const auto& rc = conn.remote_rc;
const auto& rid = rc.router_id();
#ifndef TESTNET
if (router->IsBootstrapNode(rc.pubkey))
if (router->IsBootstrapNode(rid))
return;
#endif
if (exclude.count(rc.pubkey))
if (exclude.count(rid))
return;
if (BuildCooldownHit(rc.pubkey))
if (BuildCooldownHit(rid))
return;
if (router->router_profiling().IsBadForPath(rc.pubkey))
if (router->router_profiling().IsBadForPath(rid))
return;
found = rc;
@ -235,15 +237,15 @@ namespace llarp
return found;
}
std::optional<std::vector<RouterContact>>
std::optional<std::vector<RemoteRC>>
Builder::GetHopsForBuild()
{
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))
{
return GetHopsAlignedToForBuild(maybe->pubkey);
return GetHopsAlignedToForBuild(maybe->router_id());
}
return std::nullopt;
}
@ -308,12 +310,12 @@ namespace llarp
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)
{
const auto pathConfig = router->config()->paths;
std::vector<RouterContact> hops;
std::vector<RemoteRC> hops;
{
const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value())
@ -324,7 +326,7 @@ namespace llarp
hops.emplace_back(*maybe);
};
RouterContact endpointRC;
RemoteRC endpointRC;
if (const auto maybe = router->node_db()->get_rc(endpoint))
{
endpointRC = *maybe;
@ -341,19 +343,21 @@ namespace llarp
else
{
auto filter =
[&hops, r = router, endpointRC, pathConfig, exclude](const auto& rc) -> bool {
if (exclude.count(rc.pubkey))
[&hops, r = router, endpointRC, pathConfig, exclude](const RemoteRC& rc) -> bool {
const auto& rid = rc.router_id();
if (exclude.count(rid))
return false;
std::set<RouterContact> hopsSet;
std::set<RemoteRC> hopsSet;
hopsSet.insert(endpointRC);
hopsSet.insert(hops.begin(), hops.end());
if (r->router_profiling().IsBadForPath(rc.pubkey, 1))
if (r->router_profiling().IsBadForPath(rid, 1))
return false;
for (const auto& hop : hopsSet)
{
if (hop.pubkey == rc.pubkey)
if (hop.router_id() == rid)
return false;
}
@ -362,7 +366,7 @@ namespace llarp
if (not pathConfig.Acceptable(hopsSet))
return false;
#endif
return rc.pubkey != endpointRC.pubkey;
return rc.router_id() != endpointRC.router_id();
};
if (const auto maybe = router->node_db()->GetRandom(filter))
@ -393,7 +397,7 @@ namespace llarp
}
void
Builder::Build(std::vector<RouterContact> hops, PathRole roles)
Builder::Build(std::vector<RemoteRC> hops, PathRole roles)
{
if (IsStopped())
{
@ -402,7 +406,7 @@ namespace llarp
}
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))
{
@ -429,7 +433,8 @@ namespace llarp
{
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);
auto frame_str = PathBuildMessage::serialize(path_hops[i]);
@ -533,7 +538,7 @@ namespace llarp
DoPathBuildBackoff();
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
log::info(path_cat, "Looking up RouterID {} due to path build timeout", target);

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

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

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

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

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

@ -66,7 +66,7 @@ namespace llarp
void
RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup)
{
RouterContact remoteRC;
RemoteRC remoteRC;
if (not forceLookup)
{
@ -101,7 +101,7 @@ namespace llarp
throw;
}
RouterContact result{std::move(payload)};
RemoteRC result{std::move(payload)};
if (callback)
callback(result.router_id(), result, true);
@ -202,24 +202,24 @@ namespace llarp
}
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;
}
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;
}
// 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);
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
RCLookupHandler::periodic_update(llarp_time_t now)
{
@ -278,15 +255,15 @@ namespace llarp
std::unordered_set<RouterID> routersToLookUp;
node_db->VisitInsertedBefore(
[&](const RouterContact& rc) { routersToLookUp.insert(rc.pubkey); },
now - RouterContact::UpdateInterval);
[&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); },
now - RouterContact::REPUBLISH);
for (const auto& router : routersToLookUp)
{
get_rc(router, nullptr, true);
}
node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::StaleInsertionAge);
node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::STALE);
}
void
@ -301,7 +278,8 @@ namespace llarp
{
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
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
@ -336,7 +314,7 @@ namespace llarp
return;
}
// service nodes gossip, not explore
if (contacts->router()->IsServiceNode())
if (contacts->router()->is_service_node())
return;
// explore via every connected peer
@ -367,7 +345,7 @@ namespace llarp
LinkManager* linkManager,
service::Context* hiddenServiceContext,
const std::unordered_set<RouterID>& strictConnectPubkeys,
const std::set<RouterContact>& bootstrapRCList,
const std::set<RemoteRC>& bootstrapRCList,
bool isServiceNode_arg)
{
contacts = c;
@ -383,7 +361,7 @@ namespace llarp
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)
{
if (rc.pubkey == remote)
if (rc.router_id() == remote)
{
return true;
}

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

@ -6,8 +6,6 @@
namespace llarp
{
static auto logcat = log::Cat("route-poker");
void
RoutePoker::add_route(oxen::quic::Address ip)
{
@ -119,7 +117,7 @@ namespace llarp
bool
RoutePoker::is_enabled() const
{
if (router.IsServiceNode())
if (router.is_service_node())
return false;
if (const auto& conf = router.config())
return conf->network.m_EnableRoutePoker;
@ -221,7 +219,7 @@ namespace llarp
// explicit route pokes for first hops
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 default route
@ -240,7 +238,7 @@ namespace llarp
{
// unpoke routes for first hops
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)
{
vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager();

@ -36,8 +36,6 @@ static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 250ms;
namespace llarp
{
static auto logcat = log::Cat("router");
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
: _route_poker{std::make_shared<RoutePoker>(*this)}
, _lmq{std::make_shared<oxenmq::OxenMQ>()}
@ -178,7 +176,7 @@ namespace llarp
util::StatusObject stats{
{"running", true},
{"version", llarp::VERSION_FULL},
{"version", llarp::LOKINET_VERSION_FULL},
{"uptime", to_json(Uptime())},
{"numPathsBuilt", pathsCount},
{"numPeersConnected", peers},
@ -201,7 +199,7 @@ namespace llarp
void
Router::Freeze()
{
if (IsServiceNode())
if (is_service_node())
return;
for_each_connection(
@ -211,13 +209,14 @@ namespace llarp
void
Router::Thaw()
{
if (IsServiceNode())
if (is_service_node())
return;
std::unordered_set<RouterID> peer_pubkeys;
for_each_connection(
[&peer_pubkeys](link::Connection& conn) { peer_pubkeys.emplace(conn.remote_rc.pubkey); });
for_each_connection([&peer_pubkeys](link::Connection& conn) {
peer_pubkeys.emplace(conn.remote_rc.router_id());
});
loop()->call([this, &peer_pubkeys]() {
for (auto& pk : peer_pubkeys)
@ -232,10 +231,10 @@ namespace llarp
}
void
Router::GossipRCIfNeeded(const RouterContact rc)
Router::GossipRCIfNeeded(const LocalRC rc)
{
/// if we are not a service node forget about gossip
if (not IsServiceNode())
if (not is_service_node())
return;
/// wait for random uptime
if (std::chrono::milliseconds{Uptime()} < _randomStartDelay)
@ -246,14 +245,14 @@ namespace llarp
bool
Router::GetRandomGoodRouter(RouterID& router)
{
if (IsServiceNode())
if (is_service_node())
{
return _rc_lookup_handler.get_random_whitelist_router(router);
}
if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; }))
{
router = maybe->pubkey;
router = maybe->router_id();
return true;
}
return false;
@ -272,7 +271,7 @@ namespace llarp
}
void
Router::connect_to(const RouterContact& rc)
Router::connect_to(const RemoteRC& rc)
{
_link_manager.connect_to(rc);
}
@ -315,7 +314,7 @@ namespace llarp
{
_encryption = _key_manager->encryptionKey;
if (IsServiceNode())
if (is_service_node())
{
#if defined(ANDROID) || defined(IOS)
LogError("running a service node on mobile device is not possible.");
@ -397,9 +396,9 @@ namespace llarp
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_client = std::make_shared<rpc::LokidRpcClient>(_lmq, weak_from_this());
@ -418,9 +417,9 @@ namespace llarp
_node_db = std::move(nodedb);
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);
}
@ -439,34 +438,10 @@ namespace llarp
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
Router::IsServiceNode() const
Router::is_service_node() const
{
return is_service_node;
return _is_service_node;
}
bool
@ -508,7 +483,7 @@ namespace llarp
bool
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
@ -564,18 +539,20 @@ namespace llarp
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
Router::update_rc()
{
SecretKey nextOnionKey;
RouterContact nextRC = router_contact;
if (!nextRC.Sign(identity()))
return false;
if (!nextRC.Verify(time_now_ms(), false))
return false;
router_contact = std::move(nextRC);
if (IsServiceNode())
return SaveRC();
router_contact.resign();
if (is_service_node())
save_rc();
return true;
}
@ -585,20 +562,17 @@ namespace llarp
// Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.m_netId);
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;
llarp::LogWarn(
"!!!! you have manually set netid to be '",
netid,
"' which does not equal '",
llarp::DEFAULT_NETID,
llarp::LOKINET_DEFAULT_NETID,
"' you will run as a different network, good luck "
"and don't forget: something something MUH traffic "
"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
@ -628,16 +602,17 @@ namespace llarp
else
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;
/// build a set of strictConnectPubkeys (
/// build a set of strictConnectPubkeys
std::unordered_set<RouterID> strictConnectPubkeys;
if (not networkConfig.m_strictConnect.empty())
{
const auto& val = networkConfig.m_strictConnect;
if (IsServiceNode())
if (is_service_node())
throw std::runtime_error("cannot use strict-connect option as service node");
if (val.size() < 2)
throw std::runtime_error(
@ -666,7 +641,7 @@ namespace llarp
for (const auto& router : configRouters)
{
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)
@ -674,37 +649,10 @@ namespace llarp
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)
{
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)
{
// 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)
LogInfo("we are a seed node");
else
@ -733,10 +699,10 @@ namespace llarp
&_hidden_service_context,
strictConnectPubkeys,
bootstrap_rc_list,
is_service_node);
_is_service_node);
// FIXME: kludge for now, will be part of larger cleanup effort.
if (is_service_node)
if (_is_service_node)
InitInboundLinks();
else
InitOutboundLinks();
@ -765,7 +731,7 @@ namespace llarp
}
// API config
if (not IsServiceNode())
if (not is_service_node())
{
hidden_service_context().AddEndpoint(conf);
}
@ -773,19 +739,13 @@ namespace llarp
return true;
}
bool
Router::CheckRenegotiateValid(RouterContact newrc, RouterContact oldrc)
{
return _rc_lookup_handler.check_renegotiate_valid(newrc, oldrc);
}
bool
Router::IsBootstrapNode(const RouterID r) const
{
return std::count_if(
bootstrap_rc_list.begin(),
bootstrap_rc_list.end(),
[r](const RouterContact& rc) -> bool { return rc.pubkey == r; })
[r](const RemoteRC& rc) -> bool { return rc.router_id() == r; })
> 0;
}
@ -800,17 +760,24 @@ namespace llarp
Router::report_stats()
{
const auto now = llarp::time_now_ms();
LogInfo(node_db()->num_loaded(), " RCs loaded");
LogInfo(bootstrap_rc_list.size(), " bootstrap peers");
LogInfo(NumberOfConnectedRouters(), " router connections");
if (IsServiceNode())
log::info(
logcat,
"{} RCs loaded with {} bootstrap peers and {} router connections!",
node_db()->num_loaded(),
bootstrap_rc_list.size(),
NumberOfConnectedRouters());
if (is_service_node())
{
LogInfo(NumberOfConnectedClients(), " client connections");
LogInfo(ToString(router_contact.Age(now)), " since we last updated our RC");
LogInfo(ToString(router_contact.TimeUntilExpires(now)), " until our RC expires");
log::info(
logcat,
"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)
LogInfo(ToString(now - _last_stats_report), " last reported stats");
log::info(logcat, "Last reported stats time {}", now - _last_stats_report);
_last_stats_report = now;
}
@ -819,8 +786,8 @@ namespace llarp
{
std::string status;
auto out = std::back_inserter(status);
fmt::format_to(out, "v{}", fmt::join(llarp::VERSION, "."));
if (IsServiceNode())
fmt::format_to(out, "v{}", fmt::join(llarp::LOKINET_VERSION, "."));
if (is_service_node())
{
fmt::format_to(
out,
@ -896,12 +863,13 @@ namespace llarp
_rc_lookup_handler.periodic_update(now);
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();
bool should_gossip = appears_funded();
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");
if (update_rc())
@ -921,24 +889,24 @@ namespace llarp
GossipRCIfNeeded(router_contact);
}
// 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
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;
}
// if for some reason we stored an RC that isn't a valid router
// 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;
}
/// 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;
}
// clients have no notion of a whilelist
@ -946,23 +914,23 @@ namespace llarp
// routers that are not whitelisted for first hops
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;
}
// if we don't have the whitelist yet don't remove the entry
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;
}
// if we have no whitelist enabled or we have
// the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not
// 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 false;
@ -971,10 +939,10 @@ namespace llarp
if (not is_snode or not has_whitelist)
{
// 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) {
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))
close_peers.insert(pk);
@ -982,7 +950,7 @@ namespace llarp
// mark peers as de-registered
for (auto& peer : close_peers)
_link_manager.deregister_peer(std::move(peer));
_link_manager.deregister_peer(peer);
}
_link_manager.check_persisting_conns(now);
@ -1051,7 +1019,7 @@ namespace llarp
std::set<dht::Key_t> peer_keys;
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(
[&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();
}
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
Router::GetRandomConnectedRouter(RouterContact& result) const
Router::GetRandomConnectedRouter(RemoteRC& result) const
{
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
Router::set_router_whitelist(
const std::vector<RouterID>& whitelist,
@ -1112,80 +1060,48 @@ namespace llarp
if (is_running || is_stopping)
return false;
// set public signing key
router_contact.pubkey = seckey_topublic(identity());
// set router version if service node
if (IsServiceNode())
{
router_contact.routerVersion = RouterVersion(llarp::VERSION, llarp::constants::proto_version);
}
router_contact = LocalRC::make(identity(), public_ip());
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");
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())
if (not router_contact.is_public_router())
{
LogError("failed to save RC");
log::error(logcat, "Router is configured as relay but has no reachable addresses!");
return false;
}
}
if (IsServiceNode())
{
// initialize as service node
if (!InitServiceNode())
save_rc();
if (not init_service_node())
{
LogError("Failed to initialize service node");
log::error(logcat, "Router failed to initialize service node!");
return false;
}
log::info(logcat, "Router initialized as service node!");
const RouterID us = pubkey();
LogInfo("initalized service node: ", us);
// init gossiper here
_rcGossiper.Init(&_link_manager, us, this);
// relays do not use profiling
router_profiling().Disable();
}
else
{
// we are a client
// regenerate keys and resign rc before everything else
// we are a client, regenerate keys and resign rc before everything else
crypto::identity_keygen(_identity);
crypto::encryption_keygen(_encryption);
router_contact.pubkey = seckey_topublic(identity());
router_contact.enckey = seckey_topublic(encryption());
if (!router_contact.Sign(identity()))
{
LogError("failed to regenerate keys and sign RC");
return false;
}
router_contact.set_router_id(seckey_to_pubkey(identity())); // resigns RC
}
LogInfo("starting hidden service context...");
log::info(logcat, "Starting hidden service context...");
if (!hidden_service_context().StartAll())
{
LogError("Failed to start hidden service context");
log::error(logcat, "Failed to start hidden service context!");
return false;
}
{
LogInfo("Loading nodedb from disk...");
_node_db->load_from_disk();
}
log::info(logcat, "Loading NodeDB from disk...");
_node_db->load_from_disk();
_contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this);
@ -1193,16 +1109,17 @@ namespace llarp
{
node_db()->put_rc(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(); });
_route_poker->start();
is_running.store(true);
_started_at = now();
if (IsServiceNode())
if (is_service_node())
{
// do service node testing if we are in service node whitelist mode
_loop->call_every(consensus::REACHABILITY_TESTING_TIMER_INTERVAL, weak_from_this(), [this] {
@ -1223,13 +1140,16 @@ namespace llarp
{
if (not SessionToRouterAllowed(router))
{
LogDebug(
router,
" is no longer a registered service node so we remove it from the testing list");
log::debug(
logcat,
"{} is no longer a registered service node; dropping from test list",
router);
router_testing.remove_node_from_failing(router);
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
// this will do a dht lookup if needed
_link_manager.connect_to(router);
@ -1406,7 +1326,7 @@ namespace llarp
}
bool
Router::InitServiceNode()
Router::init_service_node()
{
LogInfo("accepting transit traffic");
paths.AllowTransit();
@ -1430,7 +1350,7 @@ namespace llarp
bool
Router::HasClientExit() const
{
if (IsServiceNode())
if (is_service_node())
return false;
const auto& ep = hidden_service_context().GetDefault();
return ep and ep->HasExit();

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

@ -12,407 +12,273 @@
namespace llarp
{
static auto logcat = log::Cat("RC");
NetID&
NetID::DefaultValue()
{
static NetID defaultID(reinterpret_cast<const byte_t*>(llarp::DEFAULT_NETID));
return defaultID;
}
bool RouterContact::BlockBogons = true;
/// 1 day rc lifespan
constexpr auto rc_lifetime = 24h;
/// an RC inserted long enough ago (4 hrs) is considered stale and is removed
constexpr auto rc_stale_age = 4h;
/// window of time in which a router wil try to update their RC before it is marked stale
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;
llarp_time_t RouterContact::Lifetime = rc_lifetime;
llarp_time_t RouterContact::StaleInsertionAge = rc_stale_age;
llarp_time_t RouterContact::UpdateInterval = rc_update_interval;
/// how many rc lifetime intervals should we wait until purging an rc
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;
// RouterContact::RouterContact(std::string buf)
// {
// try
// {
// oxenc::bt_list_consumer btlc{buf};
// // signature.from_string(btlc.consume_string());
// signed_bt_dict = btlc.consume_string();
// // TODO: parse bt dict
// }
// catch (...)
// {
// log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!");
// }
// }
// std::string
// RouterContact::bt_encode() const
// {
// oxenc::bt_dict_producer btdp;
// bt_encode(btdp);
// return std::move(btdp).str();
// }
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());
std::copy(val, val + len, begin());
}
if (int rc_ver = data.require<uint8_t>(""); rc_ver != RC_VERSION)
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
NetID::operator==(const NetID& other) const
{
return ToString() == other.ToString();
}
if (ipv4_port.size() != 6)
throw std::runtime_error{
"Invalid RC address: expected 6-byte IPv4 IP/port, got {}"_format(ipv4_port.size())};
std::string
NetID::ToString() const
{
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;
sockaddr_in s4;
s4.sin_family = AF_INET;
if (strbuf.sz > size())
return false;
std::memcpy(&s4.sin_addr.s_addr, ipv4_port.data(), 4);
std::memcpy(&s4.sin_port, ipv4_port.data() + 4, 2);
std::copy(strbuf.base, strbuf.base + strbuf.sz, begin());
return true;
}
_addr = oxen::quic::Address{&s4};
bool
NetID::BEncode(llarp_buffer_t* buf) const
{
auto term = std::find(begin(), end(), '\0');
return bencode_write_bytestring(buf, data(), std::distance(begin(), term));
}
if (!_addr.is_public())
throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
RouterContact::RouterContact(std::string buf)
{
try
if (auto ipv6_port = data.maybe<std::string_view>("6"))
{
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());
signed_bt_dict = btlc.consume_string();
sockaddr_in6 s6{};
s6.sin6_family = AF_INET6;
// TODO: parse bt dict
}
catch (...)
{
log::warning(llarp_cat, "Error: RouterContact failed to populate bt encoded contents!");
}
}
std::memcpy(&s6.sin6_addr.s6_addr, ipv6_port->data(), 16);
std::memcpy(&s6.sin6_port, ipv6_port->data() + 16, 2);
std::string
RouterContact::bt_encode() const
{
oxenc::bt_list_producer btlp;
try
{
btlp.append(signature.ToView());
btlp.append(signed_bt_dict);
_addr6.emplace(&s6);
if (!_addr6->is_public())
throw std::runtime_error{"Invalid RC: IPv6 address is not a publicly routable IP"};
}
catch (...)
else
{
log::warning(llarp_cat, "Error: RouterContact failed to bt encode contents!");
_addr6.reset();
}
return std::move(btlp).str();
}
void
RouterContact::bt_encode_subdict(oxenc::bt_list_producer& btlp) const
{
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();
}
auto netid = data.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
if (netid != ACTIVE_NETID)
throw std::runtime_error{
"Invalid RC netid: expected {}, got {}; this is an RC for a different network!"_format(
ACTIVE_NETID, netid)};
std::string
RouterContact::bencode_signed_section() const
{
oxenc::bt_dict_producer btdp;
auto pk = data.require<std::string_view>("p");
btdp.append("a", addr.to_string());
btdp.append("i", netID.ToView());
btdp.append("k", pubkey.bt_encode());
btdp.append("p", enckey.ToView());
btdp.append("r", routerVersion->ToString());
if (pk.size() != RouterID::SIZE)
throw std::runtime_error{"Invalid RC: router id has invalid size {}"_format(pk.size())};
if (not srvRecords.empty())
{
auto sublist = btdp.append_list("s");
std::memcpy(_router_id.data(), pk.data(), RouterID::SIZE);
for (auto& s : srvRecords)
sublist.append(s.bt_encode());
}
_timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
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
RouterContact::Clear()
{
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;
for (int i = 0; i < 3; i++)
_router_version[i] = ver[i];
}
bool
RouterContact::BDecode(llarp_buffer_t* buf)
RouterContact::write(const fs::path& fname) const
{
Clear();
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;
}
auto bte = view();
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;
}
util::buffer_to_file(fname, bte.data(), bte.size());
}
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 false;
return true;
}
bool
RouterContact::DecodeVersion_0(llarp_buffer_t* buf)
util::StatusObject
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
RouterContact::DecodeVersion_1(oxenc::bt_list_consumer& btlist)
RouterContact::BDecode(llarp_buffer_t* buf)
{
auto signature_string = btlist.consume_string_view();
signed_bt_dict = btlist.consume_dict_data();
// TODO: unfuck all of this
if (not btlist.is_finished())
{
log::debug(logcat, "RouterContact serialized list too long for specified version.");
return false;
}
(void)buf;
llarp_buffer_t sigbuf(signature_string.data(), signature_string.size());
if (not signature.FromBytestring(&sigbuf))
{
log::debug(logcat, "RouterContact serialized signature had invalid length.");
return false;
}
// clear();
// 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
// {
// 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 bencode_decode_dict(*this, &data_dict_buf);
return false;
}
bool
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
(void)key;
// TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("k", _router_id, read, key, buf))
// return false;
if (key.startswith("r"))
{
RouterVersion r;
if (not r.BDecode(buf))
return false;
routerVersion = r;
return true;
}
// if (key.startswith("r"))
// {
// RouterVersion r;
// if (not r.BDecode(buf))
// return false;
// routerVersion = r;
// return true;
// }
if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
return false;
// if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictInt("u", last_updated, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictInt("u", _timestamp, read, key, buf))
// return false;
if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
// return false;
if (key.startswith("x") and serializeExit)
{
return bencode_discard(buf);
}
// if (key.startswith("x") and serializeExit)
// {
// return bencode_discard(buf);
// }
if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
return false;
// if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
// return false;
return read or bencode_discard(buf);
}
bool
RouterContact::IsPublicRouter() const
RouterContact::is_public_router() const
{
if (not routerVersion)
if (_router_version.empty())
return false;
return addr.is_addressable();
return _addr.is_addressable();
}
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
RouterContact::TimeUntilExpires(llarp_time_t now) const
RouterContact::time_to_expiry(llarp_time_t now) const
{
const auto expiresAt = last_updated + Lifetime;
return now < expiresAt ? expiresAt - now : 0s;
const auto expiry = _timestamp.time_since_epoch() + LIFETIME;
return now < expiry ? expiry - now : 0s;
}
llarp_time_t
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)
RouterContact::age(llarp_time_t now) const
{
pubkey = llarp::seckey_topublic(secretkey);
signature.Zero();
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;
auto delta = now - _timestamp.time_since_epoch();
return delta > 0s ? delta : 0s;
}
bool
RouterContact::VerifySignature() const
RouterContact::expires_within_delta(llarp_time_t now, llarp_time_t dlt) const
{
RouterContact copy;
copy = *this;
copy.signature.Zero();
auto bte = copy.bt_encode();
return crypto::verify(pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), signature);
return time_to_expiry(now) <= dlt;
}
static constexpr std::array obsolete_bootstraps = {
@ -421,66 +287,13 @@ namespace llarp
};
bool
RouterContact::IsObsoleteBootstrap() const
RouterContact::is_obsolete_bootstrap() const
{
for (const auto& k : obsolete_bootstraps)
{
if (pubkey.ToHex() == k)
if (_router_id.ToHex() == k)
return true;
}
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

@ -1,11 +1,11 @@
#pragma once
#include "router_id.hpp"
#include "router_version.hpp"
#include <llarp/constants/version.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/dns/srv_data.hpp>
#include <llarp/net/exit_info.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/bencode.hpp>
#include <llarp/util/status.hpp>
@ -17,128 +17,148 @@
#include <functional>
#include <vector>
#define MAX_RC_SIZE (1024)
namespace oxenc
{
class bt_list_consumer;
} // namespace oxenc
namespace llarp
{
/// NetID
struct NetID final : public AlignedBuffer<8>
static auto logcat = log::Cat("RC");
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&
DefaultValue();
static constexpr uint8_t RC_VERSION = 0;
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;
NetID&
operator=(const NetID& other) = default;
static inline constexpr size_t MAX_RC_SIZE = 1024;
bool
operator==(const NetID& other) const;
/// Timespans for RCs:
bool
operator!=(const NetID& other) const
{
return !(*this == other);
}
/// How long (relative to its timestamp) before an RC becomes stale. Stale records are used
/// (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.
static constexpr auto STALE = 12h;
std::string
ToString() const;
/// How long before an RC becomes invalid (and thus deleted).
static constexpr auto LIFETIME = 30 * 24h;
bool
BDecode(llarp_buffer_t* buf);
/// How long before a relay updates and re-publish its RC to the network. (Relays can
/// 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
BEncode(llarp_buffer_t* buf) const;
};
ustring_view
view() const
{
return _payload;
}
/// RouterContact
struct RouterContact
{
/// for unit tests
static bool BlockBogons;
/// Getters for private attributes
const oxen::quic::Address&
addr() const
{
return _addr;
}
static llarp_time_t Lifetime;
static llarp_time_t UpdateInterval;
static llarp_time_t StaleInsertionAge;
const std::optional<oxen::quic::Address>&
addr6() const
{
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
oxen::quic::Address addr;
// network identifier
NetID netID;
// public encryption public key
llarp::PubKey enckey;
oxen::quic::Address _addr; // refactor all 15 uses to use addr() method
std::optional<oxen::quic::Address> _addr6; // optional ipv6
// public signing public key
llarp::PubKey pubkey;
// signature
llarp::Signature signature;
RouterID _router_id; // refactor all 103 uses to use router_id() method
llarp_time_t last_updated = 0s;
uint64_t version = llarp::constants::proto_version;
std::optional<RouterVersion> routerVersion;
/// should we serialize the exit info?
const static bool serializeExit = true;
rc_time _timestamp{};
// Lokinet version at the time the RC was produced
std::array<uint8_t, 3> _router_version;
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
ExtractStatus() const;
extract_status() const;
RouterID
router_id() const
nlohmann::json
to_json() const
{
return pubkey;
return extract_status();
}
nlohmann::json
ToJson() const
virtual std::string
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
ToString() 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
write(const fs::path& fname) const;
bool
operator==(const RouterContact& other) const
{
return addr == other.addr && enckey == other.enckey && pubkey == other.pubkey
&& signature == other.signature && last_updated == other.last_updated
&& netID == other.netID;
return _router_id == other._router_id and _addr == other._addr and _addr6 == other._addr6
and _timestamp == other._timestamp and _router_version == other._router_version;
}
bool
operator<(const RouterContact& other) const
{
return pubkey < other.pubkey;
return _router_id < other._router_id;
}
bool
@ -147,14 +167,9 @@ namespace llarp
return !(*this == other);
}
void
Clear();
bool
IsExit() const
{
return false;
}
virtual void
clear()
{}
bool
BDecode(llarp_buffer_t* buf);
@ -163,66 +178,182 @@ namespace llarp
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
bool
IsPublicRouter() const;
bool
Verify(llarp_time_t now, bool allowExpired = true) const;
bool
Sign(const llarp::SecretKey& secret);
is_public_router() const;
/// does this RC expire soon? default delta is 1 minute
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
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
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
llarp_time_t
Age(llarp_time_t now) const;
age(llarp_time_t now) const;
bool
OtherIsNewer(const RouterContact& other) const
other_is_newer(const RouterContact& other) const
{
return last_updated < other.last_updated;
return _timestamp < other._timestamp;
}
bool
Read(const fs::path& fname);
is_obsolete_bootstrap() const;
bool
Write(const fs::path& fname) const;
void
bt_load(oxenc::bt_dict_consumer& data);
};
bool
VerifySignature() const;
/// Extension of RouterContact used to store a local "RC," and inserts a RouterContact by
/// 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
bool
FromOurNetwork() const;
private:
ustring _signature;
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
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:
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
DecodeVersion_0(llarp_buffer_t* buf);
verify() const;
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 <>
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 std
@ -233,7 +364,7 @@ namespace std
size_t
operator()(const llarp::RouterContact& r) const
{
return std::hash<llarp::PubKey>{}(r.pubkey);
return std::hash<llarp::PubKey>{}(r.router_id());
}
};
} // 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/status.hpp"
#include <llarp/crypto/types.hpp>
namespace llarp
{
struct RouterID : public AlignedBuffer<32>
struct RouterID : public PubKey
{
static constexpr size_t SIZE = 32;
@ -13,10 +15,10 @@ namespace llarp
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

@ -51,7 +51,7 @@ namespace llarp::rpc
{
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");
}
@ -181,7 +181,7 @@ namespace llarp::rpc
nlohmann::json payload = {
{"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())
payload["error"] = *err;

@ -74,7 +74,7 @@ namespace llarp::rpc
std::shared_ptr<EndpointBase>
GetEndpointByName(Router& r, std::string name)
{
if (r.IsServiceNode())
if (r.is_service_node())
{
return r.exitContext().GetExitEndpoint(name);
}
@ -154,7 +154,7 @@ namespace llarp::rpc
RPCServer::invoke(Version& version)
{
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);
}
@ -309,7 +309,7 @@ namespace llarp::rpc
void
RPCServer::invoke(LookupSnode& lookupsnode)
{
if (not m_Router.IsServiceNode())
if (not m_Router.is_service_node())
{
SetJSONError("Not supported", lookupsnode.response);
return;

@ -206,97 +206,57 @@ namespace llarp::service
std::string service,
std::function<void(std::vector<dns::SRVData>)> resultHandler)
{
// handles when we aligned to a loki address
auto handleGotPathToService = [resultHandler, service, this](auto addr) {
// 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 response back they can send shit to them immediately
const auto& container = _state->remote_sessions;
if (auto itr = container.find(addr); itr != container.end())
{
// parse the stuff we need from this guy
resultHandler(itr->second->GetCurrentIntroSet().GetMatchingSRVRecords(service));
return;
}
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;
}
// A lookup goes through a chain of events:
// - see if the name is ONS, and if so resolve it to a ADDR.loki
// - once we've resolved to ADDR.loki then initiate a path to it
// - once we have a path, consult the remote's introset to pull out the SRV records
// If we fail along the way (e.g. it's a .snode, we can't build a path, or whatever else) then
// we invoke the resultHandler with an empty vector.
lookup_name(
name, [this, resultHandler, service = std::move(service)](oxen::quic::message m) mutable {
if (!m)
return resultHandler({});
if (auto saddr = service::Address(); saddr.FromString(name))
handleGotAddress(saddr);
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 rid = RouterID(); rid.FromString(name))
handleGotAddress(rid);
}
else
{
resultHandler({});
}
});
auto saddr = service::Address();
if (!saddr.FromString(name))
return resultHandler({}); // Not a regular ADDR.loki so doesn't support SRV
// initiate path build
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
@ -754,22 +714,22 @@ namespace llarp::service
return now >= next_pub;
}
std::optional<std::vector<RouterContact>>
std::optional<std::vector<RemoteRC>>
Endpoint::GetHopsForBuild()
{
std::unordered_set<RouterID> exclude;
ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); });
const auto maybe =
router()->node_db()->GetRandom([exclude, r = router()](const auto& rc) -> bool {
return exclude.count(rc.pubkey) == 0
and not r->router_profiling().IsBadForPath(rc.pubkey);
router()->node_db()->GetRandom([exclude, r = router()](const RemoteRC& rc) -> bool {
const auto& rid = rc.router_id();
return exclude.count(rid) == 0 and not r->router_profiling().IsBadForPath(rid);
});
if (not maybe.has_value())
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)
{
return path::Builder::GetHopsAlignedToForBuild(endpoint, SnodeBlacklist());

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

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

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

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

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

@ -256,7 +256,7 @@ namespace llarp::service
// copy
ProtocolFrameMessage frame(self->frame);
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);
self->msg.reset();

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

@ -4,13 +4,10 @@
#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
} // namespace llarp::service

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

@ -17,16 +17,17 @@ namespace llarp
{
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>
bool
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>
bool
BEncodeWriteDictString(const char* k, const Obj_t& str, llarp_buffer_t* buf)
@ -320,23 +321,6 @@ namespace llarp
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>
bool
BEncodeWriteDictList(const char* k, List_t& list, llarp_buffer_t* buf)
@ -365,7 +349,7 @@ namespace llarp
std::string content;
try
{
content = util::slurp_file(fpath);
content = util::file_to_string(fpath);
}
catch (const std::exception&)
{
@ -389,7 +373,7 @@ namespace llarp
tmp.resize(buf.cur - buf.base);
try
{
util::dump_file(fpath, tmp);
util::buffer_to_file(fpath, tmp);
}
catch (const std::exception& e)
{

@ -19,7 +19,7 @@
namespace llarp::util
{
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.open(filename, std::ios::binary | std::ios::in);
@ -30,21 +30,21 @@ namespace llarp::util
}
std::string
slurp_file(const fs::path& filename)
file_to_string(const fs::path& filename)
{
fs::ifstream in;
std::string contents;
auto size = slurp_file_open(filename, in);
auto size = file_reader_impl(filename, in);
contents.resize(size);
in.read(contents.data(), size);
return contents;
}
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;
auto size = slurp_file_open(filename, in);
auto size = file_reader_impl(filename, in);
if (static_cast<size_t>(size) > buffer_size)
throw std::length_error{"file is too large for buffer"};
in.read(buffer, size);
@ -52,7 +52,7 @@ namespace llarp::util
}
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;
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.
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
/// file is bigger than the buffer. Returns the bytes copied on success.
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).
template <
typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
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
/// on error.
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
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
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});
}

Loading…
Cancel
Save