mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-11 07:10:36 +00:00
87ae0686d0
- new btdc method used to ensure no junk at the end of our bt data - DRYed out the RC code - check inbound bootstraps against all registered routers, not just whitelist - libquic vbump
204 lines
5.6 KiB
C++
204 lines
5.6 KiB
C++
#include "router_contact.hpp"
|
|
|
|
#include "constants/version.hpp"
|
|
#include "crypto/crypto.hpp"
|
|
#include "net/net.hpp"
|
|
#include "util/bencode.hpp"
|
|
#include "util/buffer.hpp"
|
|
#include "util/file.hpp"
|
|
|
|
#include <oxenc/bt_serialize.h>
|
|
|
|
namespace llarp
|
|
{
|
|
void
|
|
RouterContact::bt_verify(oxenc::bt_dict_consumer& btdc, bool reject_expired) const
|
|
{
|
|
btdc.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 (reject_expired and is_expired(time_now_ms()))
|
|
throw std::runtime_error{"Rejecting 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 address!";
|
|
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 signature"};
|
|
});
|
|
|
|
if (not btdc.is_finished())
|
|
throw std::runtime_error{"RouterContact has some fucked up shit at the end"};
|
|
|
|
btdc.finish();
|
|
}
|
|
|
|
void
|
|
RouterContact::bt_load(oxenc::bt_dict_consumer& btdc)
|
|
{
|
|
if (int rc_ver = btdc.require<uint8_t>(""); rc_ver != RC_VERSION)
|
|
throw std::runtime_error{"Invalid RC: do not know how to parse v{} RCs"_format(rc_ver)};
|
|
|
|
auto ipv4_port = btdc.require<std::string_view>("4");
|
|
|
|
if (ipv4_port.size() != 6)
|
|
throw std::runtime_error{
|
|
"Invalid RC address: expected 6-byte IPv4 IP/port, got {}"_format(ipv4_port.size())};
|
|
|
|
sockaddr_in s4;
|
|
s4.sin_family = AF_INET;
|
|
|
|
std::memcpy(&s4.sin_addr.s_addr, ipv4_port.data(), 4);
|
|
std::memcpy(&s4.sin_port, ipv4_port.data() + 4, 2);
|
|
|
|
_addr = oxen::quic::Address{&s4};
|
|
|
|
if (!_addr.is_public())
|
|
throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
|
|
|
|
if (auto ipv6_port = btdc.maybe<std::string_view>("6"))
|
|
{
|
|
if (ipv6_port->size() != 18)
|
|
throw std::runtime_error{
|
|
"Invalid RC address: expected 18-byte IPv6 IP/port, got {}"_format(ipv6_port->size())};
|
|
|
|
sockaddr_in6 s6{};
|
|
s6.sin6_family = AF_INET6;
|
|
|
|
std::memcpy(&s6.sin6_addr.s6_addr, ipv6_port->data(), 16);
|
|
std::memcpy(&s6.sin6_port, ipv6_port->data() + 16, 2);
|
|
|
|
_addr6.emplace(&s6);
|
|
if (!_addr6->is_public())
|
|
throw std::runtime_error{"Invalid RC: IPv6 address is not a publicly routable IP"};
|
|
}
|
|
else
|
|
{
|
|
_addr6.reset();
|
|
}
|
|
|
|
auto netid = btdc.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)};
|
|
|
|
auto pubkey = btdc.require<std::string_view>("p");
|
|
if (pubkey.size() != 32)
|
|
throw std::runtime_error{
|
|
"Invalid RC pubkey: expected 32 bytes, got {}"_format(pubkey.size())};
|
|
std::memcpy(_router_id.data(), pubkey.data(), 32);
|
|
|
|
_timestamp = rc_time{std::chrono::seconds{btdc.require<uint64_t>("t")}};
|
|
|
|
auto ver = btdc.require<ustring_view>("v");
|
|
|
|
if (ver.size() != 3)
|
|
throw std::runtime_error{
|
|
"Invalid RC router version: received {} bytes (!= 3)"_format(ver.size())};
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
_router_version[i] = ver[i];
|
|
}
|
|
|
|
bool
|
|
RouterContact::write(const fs::path& fname) const
|
|
{
|
|
auto bte = view();
|
|
|
|
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;
|
|
}
|
|
|
|
util::StatusObject
|
|
RouterContact::extract_status() const
|
|
{
|
|
util::StatusObject obj{
|
|
{"lastUpdated", _timestamp.time_since_epoch().count()},
|
|
{"publicRouter", is_public_addressable()},
|
|
{"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::is_public_addressable() const
|
|
{
|
|
if (_router_version.empty())
|
|
return false;
|
|
|
|
return _addr.is_addressable();
|
|
}
|
|
|
|
bool
|
|
RouterContact::is_expired(llarp_time_t now) const
|
|
{
|
|
return age(now) >= _timestamp.time_since_epoch() + LIFETIME;
|
|
}
|
|
|
|
llarp_time_t
|
|
RouterContact::time_to_expiry(llarp_time_t now) const
|
|
{
|
|
const auto expiry = _timestamp.time_since_epoch() + LIFETIME;
|
|
return now < expiry ? expiry - now : 0s;
|
|
}
|
|
|
|
llarp_time_t
|
|
RouterContact::age(llarp_time_t now) const
|
|
{
|
|
auto delta = now - _timestamp.time_since_epoch();
|
|
return delta > 0s ? delta : 0s;
|
|
}
|
|
|
|
bool
|
|
RouterContact::expires_within_delta(llarp_time_t now, llarp_time_t dlt) const
|
|
{
|
|
return time_to_expiry(now) <= dlt;
|
|
}
|
|
|
|
static constexpr std::array obsolete_bootstraps = {
|
|
"7a16ac0b85290bcf69b2f3b52456d7e989ac8913b4afbb980614e249a3723218"sv,
|
|
"e6b3a6fe5e32c379b64212c72232d65b0b88ddf9bbaed4997409d329f8519e0b"sv,
|
|
};
|
|
|
|
bool
|
|
RouterContact::is_obsolete_bootstrap() const
|
|
{
|
|
for (const auto& k : obsolete_bootstraps)
|
|
{
|
|
if (_router_id.ToHex() == k)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
} // namespace llarp
|