mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-17 15:25:35 +00:00
371 lines
9.9 KiB
C++
371 lines
9.9 KiB
C++
#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/util/aligned.hpp>
|
|
#include <llarp/util/bencode.hpp>
|
|
#include <llarp/util/status.hpp>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <oxenc/bt_producer.h>
|
|
#include <quic.hpp>
|
|
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
namespace llarp
|
|
{
|
|
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 constexpr uint8_t RC_VERSION = 0;
|
|
|
|
/// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected.
|
|
static inline bool BLOCK_BOGONS = true;
|
|
|
|
static inline std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
|
|
|
|
static inline constexpr size_t MAX_RC_SIZE = 1024;
|
|
|
|
/// Timespans for RCs:
|
|
|
|
/// 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;
|
|
|
|
/// How long before an RC becomes invalid (and thus deleted).
|
|
static constexpr auto LIFETIME = 30 * 24h;
|
|
|
|
/// 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;
|
|
|
|
ustring_view
|
|
view() const
|
|
{
|
|
return _payload;
|
|
}
|
|
|
|
/// Getters for private attributes
|
|
const oxen::quic::Address&
|
|
addr() const
|
|
{
|
|
return _addr;
|
|
}
|
|
|
|
const std::optional<oxen::quic::Address>&
|
|
addr6() const
|
|
{
|
|
return _addr6;
|
|
}
|
|
|
|
const RouterID&
|
|
router_id() const
|
|
{
|
|
return _router_id;
|
|
}
|
|
|
|
const rc_time&
|
|
timestamp() const
|
|
{
|
|
return _timestamp;
|
|
}
|
|
|
|
protected:
|
|
// advertised addresses
|
|
oxen::quic::Address _addr; // refactor all 15 uses to use addr() method
|
|
std::optional<oxen::quic::Address> _addr6; // optional ipv6
|
|
// public signing public key
|
|
RouterID _router_id; // refactor all 103 uses to use router_id() method
|
|
|
|
rc_time _timestamp{};
|
|
|
|
// 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;
|
|
|
|
util::StatusObject
|
|
extract_status() const;
|
|
|
|
nlohmann::json
|
|
to_json() const
|
|
{
|
|
return extract_status();
|
|
}
|
|
|
|
virtual std::string
|
|
to_string() const
|
|
{
|
|
return fmt::format(
|
|
"[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;
|
|
|
|
bool
|
|
operator==(const RouterContact& 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;
|
|
}
|
|
|
|
bool
|
|
operator<(const RouterContact& other) const
|
|
{
|
|
return _router_id < other._router_id;
|
|
}
|
|
|
|
bool
|
|
operator!=(const RouterContact& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
virtual void
|
|
clear()
|
|
{}
|
|
|
|
bool
|
|
BDecode(llarp_buffer_t* buf);
|
|
|
|
bool
|
|
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
|
|
|
|
bool
|
|
is_public_router() const;
|
|
|
|
/// does this RC expire soon? default delta is 1 minute
|
|
bool
|
|
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
|
|
is_expired(llarp_time_t now) const;
|
|
|
|
/// returns time in ms until we expire or 0 if we have expired
|
|
llarp_time_t
|
|
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;
|
|
|
|
bool
|
|
other_is_newer(const RouterContact& other) const
|
|
{
|
|
return _timestamp < other._timestamp;
|
|
}
|
|
|
|
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
|
|
///
|
|
/// 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);
|
|
|
|
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 = oxen::quic::Address{};
|
|
_addr6.reset();
|
|
_router_id.Zero();
|
|
_timestamp = {};
|
|
_router_version.fill(0);
|
|
_signature.clear();
|
|
}
|
|
|
|
bool
|
|
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
|
|
verify() const;
|
|
|
|
bool
|
|
read(const fs::path& fname);
|
|
|
|
void
|
|
clear() override
|
|
{
|
|
_addr = oxen::quic::Address{};
|
|
_addr6.reset();
|
|
_router_id.Zero();
|
|
_timestamp = {};
|
|
_router_version.fill(0);
|
|
}
|
|
};
|
|
|
|
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<RemoteRC>&)>;
|
|
} // namespace llarp
|
|
|
|
namespace std
|
|
{
|
|
template <>
|
|
struct hash<llarp::RouterContact>
|
|
{
|
|
size_t
|
|
operator()(const llarp::RouterContact& r) const
|
|
{
|
|
return std::hash<llarp::PubKey>{}(r.router_id());
|
|
}
|
|
};
|
|
} // namespace std
|