{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)
pull/2218/head
dr7ana 7 months ago
parent 07271f9ae7
commit fa4471f566

@ -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.bt_encode());
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;
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

@ -64,9 +64,9 @@ namespace llarp
+ "' 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);
});
@ -1322,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;
@ -1530,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

@ -51,7 +51,7 @@ 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;
RemoteRC rc;
bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent)
{
@ -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.verify_signature());
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

@ -19,8 +19,6 @@
namespace llarp
{
static auto logcat = llarp::log::Cat("llarp-context");
bool
Context::CallSafe(std::function<void(void)> f)
{
@ -163,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();

@ -67,8 +67,7 @@ namespace llarp
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 verify(ustring_view, ustring_view, ustring_view);
bool
verify(uint8_t*, uint8_t*, size_t, uint8_t*);

@ -57,12 +57,6 @@ namespace llarp
return lhs.as_array() == rhs.as_array();
}
bool
operator==(const RouterID& lhs, const PubKey& rhs)
{
return lhs.as_array() == rhs.as_array();
}
bool
SecretKey::LoadFromFile(const fs::path& fname)
{
@ -124,17 +118,13 @@ 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);
util::buffer_to_file(fname, bte);
}
catch (const std::exception&)
catch (const std::exception& e)
{
return false;
}

@ -50,9 +50,6 @@ namespace llarp
bool
operator==(const PubKey& lhs, const RouterID& rhs);
bool
operator==(const RouterID& lhs, const PubKey& rhs);
struct PrivateKey;
/// Stores a sodium "secret key" value, which is actually the seed

@ -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,33 +578,7 @@ namespace llarp::handlers
void
ExitEndpoint::SRVRecordsChanged()
{
router->modify_rc(
[srvRecords = SRVRecords()](RouterContact rc) -> std::optional<RouterContact> {
// TODO: update this RouterContact handling
// 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

@ -18,7 +18,7 @@ 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.router_id()); itr != conns.end())
return itr->second;
@ -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,7 +301,7 @@ 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.router_id()); conn)
{
@ -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,7 +487,9 @@ 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))
{
@ -654,7 +656,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)
@ -720,7 +722,7 @@ namespace llarp
if (m)
{
_router.node_db()->put_rc_if_newer(RouterContact{payload});
_router.node_db()->put_rc_if_newer(RemoteRC{payload});
}
else
{

@ -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
{

@ -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);

@ -14,7 +14,7 @@ static const std::string RC_FILE_EXT = ".signed";
namespace llarp
{
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
@ -70,9 +70,11 @@ 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)]() {
@ -119,7 +121,7 @@ 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))
{
@ -137,7 +139,7 @@ namespace llarp
// validate signature and purge entries with invalid signatures
// load ones with valid signatures
if (rc.verify_signature()) // TODO: fix this after RouterContact -> RemoteRC
if (rc.verify())
entries.emplace(rc.router_id(), rc);
else
purge.emplace(f);
@ -175,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())
@ -219,11 +221,12 @@ namespace llarp
}
void
NodeDB::put_rc(RouterContact rc)
NodeDB::put_rc(RemoteRC rc)
{
router.loop()->call([this, rc]() {
entries.erase(rc.router_id());
entries.emplace(rc.router_id(), rc);
const auto& rid = rc.router_id();
entries.erase(rid);
entries.emplace(rid, rc);
});
}
@ -234,7 +237,7 @@ 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.router_id());
@ -267,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.router_id().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.router_id().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)
@ -306,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);
@ -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._router_id;
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._router_id;
return hops[hops.size() - 1].rc.router_id();
}
PubKey
Path::EndpointPubKey() const
{
return hops[hops.size() - 1].rc._router_id;
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._router_id == 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._router_id;
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._router_id).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._router_id.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

@ -260,7 +260,7 @@ namespace llarp
try
{
util::dump_file(fpath, buf);
util::buffer_to_file(fpath, buf);
}
catch (const std::exception& e)
{

@ -28,7 +28,7 @@ namespace llarp
}
bool
RCGossiper::IsOurRC(const RouterContact& rc) const
RCGossiper::IsOurRC(const LocalRC& rc) const
{
return rc.router_id() == rid;
}
@ -64,7 +64,7 @@ namespace llarp
}
bool
RCGossiper::GossipRC(const RouterContact& rc)
RCGossiper::GossipRC(const LocalRC& rc)
{
// only distribute public routers
if (not rc.is_public_router())

@ -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,8 +101,7 @@ namespace llarp
throw;
}
// TODO: replace this with construction of RemoteRC
RouterContact result{std::move(payload)};
RemoteRC result{std::move(payload)};
if (callback)
callback(result.router_id(), result, true);
@ -203,7 +202,7 @@ namespace llarp
}
bool
RCLookupHandler::check_rc(const RouterContact& rc) const
RCLookupHandler::check_rc(const RemoteRC& rc) const
{
if (not is_session_allowed(rc.router_id()))
{
@ -211,16 +210,16 @@ namespace llarp
return false;
}
if (not rc.verify(llarp::time_now_ms())) // TODO: fix this call after RouterContact -> RemoteRC
if (not rc.verify())
{
LogWarn("RC for ", RouterID(rc.router_id()), " is invalid");
log::info(link_cat, "Invalid RC (rid: {})", rc.router_id());
return false;
}
// update nodedb if required
if (rc.is_public_router())
{
LogDebug("Adding or updating RC for ", RouterID(rc.router_id()), " 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);
}
@ -249,29 +248,6 @@ namespace llarp
});
}
bool
RCLookupHandler::check_renegotiate_valid(RouterContact newrc, RouterContact oldrc)
{
// mismatch of identity ?
if (newrc.router_id() != oldrc.router_id())
return false;
if (!is_session_allowed(newrc.router_id()))
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.router_id()}))
{
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)
{
@ -337,7 +313,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
@ -368,7 +344,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;

@ -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

@ -117,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;

@ -199,7 +199,7 @@ namespace llarp
void
Router::Freeze()
{
if (IsServiceNode())
if (is_service_node())
return;
for_each_connection(
@ -209,7 +209,7 @@ namespace llarp
void
Router::Thaw()
{
if (IsServiceNode())
if (is_service_node())
return;
std::unordered_set<RouterID> peer_pubkeys;
@ -231,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)
@ -245,7 +245,7 @@ namespace llarp
bool
Router::GetRandomGoodRouter(RouterID& router)
{
if (IsServiceNode())
if (is_service_node())
{
return _rc_lookup_handler.get_random_whitelist_router(router);
}
@ -271,7 +271,7 @@ namespace llarp
}
void
Router::connect_to(const RouterContact& rc)
Router::connect_to(const RemoteRC& rc)
{
_link_manager.connect_to(rc);
}
@ -314,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.");
@ -396,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());
@ -417,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);
}
@ -438,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()
Router::is_service_node() const
{
LogDebug("verify RC signature");
if (!router_contact.verify(now())) // TODO: RouterContact -> RemoteRC
{
Dump<RouterContact::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
{
return is_service_node;
return _is_service_node;
}
bool
@ -507,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
@ -566,13 +542,13 @@ namespace llarp
bool
Router::update_rc()
{
SecretKey nextOnionKey;
RouterContact nextRC = router_contact;
if (!nextRC.sign(identity())) // TODO: RouterContact -> LocalRC
return false;
router_contact = std::move(nextRC);
if (IsServiceNode())
return SaveRC();
router_contact.resign();
if (is_service_node())
{
_node_db->put_rc(router_contact.view());
queue_disk_io([&]() { router_contact.write(our_rc_file); });
}
return true;
}
@ -626,12 +602,13 @@ namespace llarp
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(
@ -660,7 +637,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)
@ -668,37 +645,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->is_obsolete_bootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", RouterID{it->router_id()});
else if (not it->verify(now())) // TODO: RouterContact -> RemoteRC
log::warning(logcat, "ignoring invalid bootstrap RC: {}", RouterID{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);
}
};
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
@ -711,6 +661,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
@ -727,10 +695,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();
@ -759,7 +727,7 @@ namespace llarp
}
// API config
if (not IsServiceNode())
if (not is_service_node())
{
hidden_service_context().AddEndpoint(conf);
}
@ -767,19 +735,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.router_id() == r; })
[r](const RemoteRC& rc) -> bool { return rc.router_id() == r; })
> 0;
}
@ -797,7 +759,7 @@ namespace llarp
LogInfo(node_db()->num_loaded(), " RCs loaded");
LogInfo(bootstrap_rc_list.size(), " bootstrap peers");
LogInfo(NumberOfConnectedRouters(), " router connections");
if (IsServiceNode())
if (is_service_node())
{
LogInfo(NumberOfConnectedClients(), " client connections");
LogInfo(ToString(router_contact.age(now)), " since we last updated our RC");
@ -814,7 +776,7 @@ namespace llarp
std::string status;
auto out = std::back_inserter(status);
fmt::format_to(out, "v{}", fmt::join(llarp::LOKINET_VERSION, "."));
if (IsServiceNode())
if (is_service_node())
{
fmt::format_to(
out,
@ -890,7 +852,7 @@ 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();
@ -916,7 +878,7 @@ 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.router_id()))
{
@ -1057,32 +1019,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,
@ -1200,7 +1142,7 @@ namespace llarp
_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] {
@ -1428,7 +1370,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;
@ -167,7 +167,7 @@ namespace llarp
connect_to(const RouterID& rid);
void
connect_to(const RouterContact& rc);
connect_to(const RemoteRC& rc);
Contacts*
contacts() const
@ -274,7 +274,7 @@ namespace llarp
return paths;
}
const RouterContact&
const LocalRC&
rc() const
{
return router_contact;
@ -295,9 +295,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 +378,7 @@ namespace llarp
status_line();
void
GossipRCIfNeeded(const RouterContact rc);
GossipRCIfNeeded(const LocalRC rc);
void
InitInboundLinks();
@ -402,7 +399,7 @@ namespace llarp
/// 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 +449,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;
@ -489,13 +480,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 +509,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;

@ -30,13 +30,13 @@ namespace llarp
// }
// }
std::string
RouterContact::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bt_encode(btdp);
return std::move(btdp).str();
}
// std::string
// RouterContact::bt_encode() const
// {
// oxenc::bt_dict_producer btdp;
// bt_encode(btdp);
// return std::move(btdp).str();
// }
void
RouterContact::bt_load(oxenc::bt_dict_consumer& data)
@ -107,6 +107,23 @@ namespace llarp
_router_version[i] = ver[i];
}
bool
RouterContact::write(const fs::path& fname) const
{
auto bte = bt_encode();
try
{
util::buffer_to_file(fname, bte.data(), bte.size());
}
catch (const std::exception& e)
{
log::error(logcat, "Failed to write RC to {}: {}", fname, e.what());
return false;
}
return true;
}
// std::string
// RouterContact::bencode_signed_section() const
// {
@ -208,7 +225,7 @@ namespace llarp
(void)key;
// TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false;
@ -299,42 +316,4 @@ namespace llarp
}
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::file_to_buffer(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);
}
} // namespace llarp

@ -18,10 +18,10 @@
#include <functional>
#include <vector>
namespace oxenc
{
class bt_list_consumer;
} // namespace oxenc
// namespace oxenc
// {
// class bt_list_consumer;
// } // namespace oxenc
/*
- figure out where we do bt_encoding of RC's
@ -42,17 +42,20 @@ namespace llarp
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
static inline constexpr size_t NETID_SIZE{8};
/// RouterContact
struct RouterContact
{
static constexpr uint8_t RC_VERSION = 0;
/// Constructs an empty RC
RouterContact() = default;
RouterContact(std::string)
{
log::error(logcat, "ERROR: SUPPLANT THIS CONSTRUCTOR");
}
// RouterContact() = default;
// RouterContact(std::string)
// {
// log::error(logcat, "ERROR: SUPPLANT THIS CONSTRUCTOR");
// }
// RouterContact(std::string buf);
@ -119,12 +122,19 @@ namespace llarp
// Lokinet version at the time the RC was produced
std::array<uint8_t, 3> _router_version;
// 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;
public:
/// should we serialize the exit info?
const static bool serializeExit = true;
ustring _signed_payload;
util::StatusObject
extract_status() const;
@ -138,13 +148,16 @@ namespace llarp
to_string() const
{
return fmt::format(
"[RC k={} updated={} v={} addr={}]",
_router_id,
_timestamp,
RC_VERSION,
_addr.to_string());
"[RC k={} updated={} v={} addr={}]",
_router_id.ToView(),
_timestamp.time_since_epoch().count(),
RC_VERSION,
_addr.to_string());
}
bool
write(const fs::path& fname) const;
/// 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
@ -161,13 +174,11 @@ namespace llarp
/// "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"
std::string
bt_encode() const;
virtual void
bt_encode(oxenc::bt_dict_producer& btdp) const
virtual ustring_view
bt_encode() const
{
(void)btdp;
log::warning(logcat, "ERROR: SUPPLANT THIS METHOD");
return {};
}
bool
@ -190,7 +201,7 @@ namespace llarp
}
virtual void
clear()
clear()
{}
bool
@ -224,21 +235,13 @@ namespace llarp
return _timestamp < other._timestamp;
}
bool
read(const fs::path& fname);
bool
write(const fs::path& fname) const;
bool
is_obsolete_bootstrap() const;
void
bt_load(oxenc::bt_dict_consumer& data);
};
/// 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
@ -246,24 +249,35 @@ namespace llarp
/// 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 : public RouterContact
struct LocalRC final : public RouterContact
{
private:
ustring _signature;
const SecretKey _secret_key;
// TODO: fix these parameters
void
bt_sign(oxenc::bt_dict_consumer& btdc);
bt_sign(oxenc::bt_dict_producer& btdp);
void
resign();
bt_encode(oxenc::bt_dict_producer& btdp) const;
public:
LocalRC(std::string payload, const SecretKey sk);
LocalRC() = default;
explicit LocalRC(std::string payload, const SecretKey sk);
~LocalRC() = default;
void
bt_encode(oxenc::bt_dict_producer& btdp) const override;
resign();
ustring_view
bt_encode() const override;
ustring_view
view() const
{
return _payload;
}
void
clear() override
@ -289,22 +303,22 @@ namespace llarp
void
set_addr(oxen::quic::Address new_addr)
{
resign();
_addr = std::move(new_addr);
resign();
}
void
set_addr6(oxen::quic::Address new_addr)
{
resign();
_addr6 = std::move(new_addr);
resign();
}
void
set_router_id(RouterID rid)
{
resign();
_router_id = std::move(rid);
resign();
}
void
@ -328,24 +342,44 @@ namespace llarp
}
};
/// 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 : public RouterContact
struct RemoteRC final : public RouterContact
{
private:
//
// TODO: fix these parameters
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false);
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
public:
RemoteRC(std::string payload);
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;
ustring_view
bt_encode() const override
{
return _payload;
}
std::string_view
view() const
{
return {reinterpret_cast<const char*>(_payload.data()), _payload.size()};
}
bool
verify() const;
bool
read(const fs::path& fname);
// TODO: this method could use oxenc's append_encoded
void
bt_encode(oxenc::bt_dict_producer& btdp) const override;
void
clear() override
{
@ -355,7 +389,6 @@ namespace llarp
_timestamp = {};
_router_version.fill(0);
}
};
template <>
@ -365,7 +398,7 @@ namespace llarp
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

@ -11,8 +11,7 @@
namespace llarp
{
LocalRC::LocalRC(std::string payload, const SecretKey sk) :
_secret_key{std::move(sk)}
LocalRC::LocalRC(std::string payload, const SecretKey sk) : _secret_key{std::move(sk)}
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
@ -20,7 +19,27 @@ namespace llarp
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
bt_sign(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)
{
@ -30,17 +49,29 @@ namespace llarp
}
void
LocalRC::bt_sign(oxenc::bt_dict_consumer& btdc)
LocalRC::bt_sign(oxenc::bt_dict_producer& btdp)
{
_signature.clear();
btdc.require_signature("~", [&](ustring_view msg, ustring_view s) {
if (!crypto::sign(const_cast<unsigned char*>(s.data()), _secret_key, msg))
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 = s;
_signed_payload = msg;
_signature = {sig.data(), sig.size()};
return sig;
});
_payload = btdp.view<unsigned char>();
}
ustring_view
LocalRC::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bt_encode(btdp);
return btdp.view<unsigned char>();
}
void
@ -86,12 +117,12 @@ namespace llarp
btdp.append(
"v", std::string_view{reinterpret_cast<const char*>(llarp::LOKINET_VERSION.data()), 3});
btdp.append_signature("~", [&](ustring_view to_sign) {
btdp.append_signature("~", [this](ustring_view to_sign) {
std::array<unsigned char, 64> sig;
if (!crypto::sign(sig.data(), _secret_key, to_sign))
throw std::runtime_error{"Failed to sign LocalRC"};
return sig;
});
}
@ -99,10 +130,9 @@ namespace llarp
void
LocalRC::resign()
{
oxenc::bt_dict_consumer btdc{_signed_payload};
bt_sign(btdc);
// DISCUSS: should we also update the timestamp when we re-sign?
// -> Is the timestamp referring to signing time or time the RC was originally created?
set_systime_timestamp();
oxenc::bt_dict_producer btdp;
bt_encode(btdp);
bt_sign(btdp);
}
} // namespace llarp

@ -11,13 +11,32 @@
namespace llarp
{
RemoteRC::RemoteRC(std::string payload)
RemoteRC::RemoteRC(oxenc::bt_dict_consumer btdc)
{
try
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
bt_verify(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)
{
@ -27,21 +46,7 @@ namespace llarp
}
void
RemoteRC::bt_encode(oxenc::bt_dict_producer& btdp) const
{
(void)btdp;
// TODO: implement append_encoded in oxenc so we can finish this implementation.
// It is almost identical to the implementation of LocalRC::bt_encode, except the
// call to append_signature is append_encoded.
//
// When that is done, we can take the common logic and move it into the base class
// ::bt_encode, and then have each derived class call into a different virtual method
// that calls append_signature in the LocalRC and append_encoded in the RemoteRC
}
void
RemoteRC::bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired)
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)
@ -62,8 +67,40 @@ namespace llarp
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
_signed_payload = msg;
});
}
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

@ -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");
}

@ -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);
}
@ -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;

@ -200,6 +200,7 @@ namespace llarp::service
});
}
// TODO: revisit once SRVRecords are straightened out
void
Endpoint::LookupServiceAsync(
std::string name,
@ -226,9 +227,9 @@ namespace llarp::service
if (auto maybe_rc = nodedb->get_rc(router_id))
{
result = maybe_rc->srvRecords; // TODO: RouterContact has no SRV records
// result = maybe_rc->srvRecords; // TODO: RouterContact has no SRV records
}
resultHandler(std::move(result));
};
@ -756,22 +757,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->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

@ -97,7 +97,7 @@ namespace llarp::service
// write
try
{
util::dump_file(fname, tmp.data(), sz);
util::buffer_to_file(fname, tmp.data(), sz);
}
catch (const std::exception& e)
{

@ -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

@ -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

@ -389,7 +389,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)
{

@ -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);

@ -34,14 +34,14 @@ namespace llarp::util
/// 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