lokinet/llarp/service/endpoint.hpp
Jeff Becker e5efe793ca
llarp::service::Endpoint::ReadyToDoLookup()
previously we had a checking style function that passes in an optional
defaulting to nullopt as a micro optimzation, this makes the code
unnessarily obtuse.

simplify this by splitting up into 2 functions,
one for getting the unique endpoints and one for checking if the
number of them is above the minimum.

add overload for ReadyToDoLookup() that checks against constant but
can do more in the future if desired to reduce the burden on future contributors.
2022-11-03 10:00:44 -04:00

584 lines
16 KiB
C++

#pragma once
#include <llarp/dht/messages/gotrouter.hpp>
#include <llarp/ev/ev.hpp>
#include <llarp/exit/session.hpp>
#include <llarp/net/ip_range_map.hpp>
#include <llarp/net/net.hpp>
#include <llarp/path/path.hpp>
#include <llarp/path/pathbuilder.hpp>
#include "address.hpp"
#include "handler.hpp"
#include "identity.hpp"
#include "pendingbuffer.hpp"
#include "protocol.hpp"
#include "sendcontext.hpp"
#include "service/protocol_type.hpp"
#include "session.hpp"
#include "lookup.hpp"
#include <llarp/util/compare_ptr.hpp>
#include <optional>
#include <unordered_map>
#include <variant>
#include <oxenc/variant.h>
#include "endpoint_types.hpp"
#include "llarp/endpoint_base.hpp"
#include "auth.hpp"
#include <llarp/vpn/egres_packet_router.hpp>
#include <llarp/dns/server.hpp>
// minimum time between introset shifts
#ifndef MIN_SHIFT_INTERVAL
#define MIN_SHIFT_INTERVAL 5s
#endif
namespace llarp
{
namespace quic
{
class TunnelManager;
}
namespace service
{
struct AsyncKeyExchange;
struct Context;
struct EndpointState;
struct OutboundContext;
/// minimum interval for publishing introsets
inline constexpr auto IntrosetPublishInterval = path::intro_path_spread / 2;
/// how agressively should we retry publishing introset on failure
inline constexpr auto IntrosetPublishRetryCooldown = 1s;
/// how aggressively should we retry looking up introsets
inline constexpr auto IntrosetLookupCooldown = 250ms;
/// number of unique snodes we want to talk to do to ons lookups
inline constexpr size_t MIN_ENDPOINTS_FOR_LNS_LOOKUP = 2;
struct Endpoint : public path::Builder,
public ILookupHolder,
public IDataHandler,
public EndpointBase
{
Endpoint(AbstractRouter* r, Context* parent);
~Endpoint() override;
/// return true if we are ready to recv packets from the void.
/// really should be ReadyForInboundTraffic() but the diff is HUGE and we need to rewrite this
/// component anyways.
bool
IsReady() const;
void
QueueRecvData(RecvDataEvent ev) override;
/// return true if our introset has expired intros
bool
IntrosetIsStale() const;
/// construct parameters for notify hooks
virtual std::unordered_map<std::string, std::string>
NotifyParams() const;
virtual util::StatusObject
ExtractStatus() const;
void
SetHandler(IDataHandler* h);
virtual bool
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf);
void
Tick(llarp_time_t now) override;
/// return true if we have a resolvable ip address
virtual bool
HasIfAddr() const
{
return false;
}
virtual std::string
GetIfName() const = 0;
std::optional<ConvoTag>
GetBestConvoTagFor(std::variant<Address, RouterID> addr) const override;
/// get our ifaddr if it is set
virtual huint128_t
GetIfAddr() const
{
return {0};
}
/// get the exit policy for our exit if we have one
/// override me
virtual std::optional<net::TrafficPolicy>
GetExitPolicy() const
{
return std::nullopt;
};
/// get the ip ranges we claim to own
/// override me
virtual std::set<IPRange>
GetOwnedRanges() const
{
return {};
};
virtual void
Thaw(){};
void
ResetInternalState() override;
/// loop (via router)
/// use when sending any data on a path
const EventLoop_ptr&
Loop() override;
AbstractRouter*
Router();
virtual bool
LoadKeyFile();
virtual bool
Start();
std::string
Name() const override;
AddressVariant_t
LocalAddress() const override;
std::optional<SendStat>
GetStatFor(AddressVariant_t remote) const override;
std::unordered_set<AddressVariant_t>
AllRemoteEndpoints() const override;
bool
ShouldPublishDescriptors(llarp_time_t now) const override;
void
SRVRecordsChanged() override;
void
HandlePathDied(path::Path_ptr p) override;
virtual vpn::EgresPacketRouter*
EgresPacketRouter()
{
return nullptr;
};
virtual vpn::NetworkInterface*
GetVPNInterface()
{
return nullptr;
}
bool
PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override;
bool
PublishIntroSetVia(
const EncryptedIntroSet& i, AbstractRouter* r, path::Path_ptr p, uint64_t relayOrder);
bool
HandleGotIntroMessage(std::shared_ptr<const dht::GotIntroMessage> msg) override;
bool
HandleGotRouterMessage(std::shared_ptr<const dht::GotRouterMessage> msg) override;
bool
HandleGotNameMessage(std::shared_ptr<const dht::GotNameMessage> msg) override;
bool
HandleHiddenServiceFrame(path::Path_ptr p, const service::ProtocolFrame& msg);
void
SetEndpointAuth(std::shared_ptr<IAuthPolicy> policy);
/// sets how we authenticate with remote address
void
SetAuthInfoForEndpoint(Address remote, AuthInfo info);
virtual huint128_t ObtainIPForAddr(std::variant<Address, RouterID>) = 0;
/// get a key for ip address
virtual std::optional<std::variant<service::Address, RouterID>>
ObtainAddrForIP(huint128_t ip) const = 0;
// virtual bool
// HasServiceAddress(const AlignedBuffer< 32 >& addr) const = 0;
/// return true if we have a pending job to build to a hidden service but
/// it's not done yet
bool
HasPendingPathToService(const Address& remote) const;
bool
HandleDataMessage(
path::Path_ptr path, const PathID_t from, std::shared_ptr<ProtocolMessage> msg) override;
/// handle packet io from service node or hidden service to frontend
virtual bool
HandleInboundPacket(
const ConvoTag tag, const llarp_buffer_t& pkt, ProtocolType t, uint64_t seqno) = 0;
// virtual bool
// HandleWriteIPPacket(const llarp_buffer_t& pkt,
// std::function< huint128_t(void) > getFromIP) = 0;
bool
ProcessDataMessage(std::shared_ptr<ProtocolMessage> msg);
/// ensure that we know a router, looks up if it doesn't
void
EnsureRouterIsKnown(const RouterID& router);
/// lookup a router via closest path
bool
LookupRouterAnon(RouterID router, RouterLookupHandler handler);
void
LookupNameAsync(
std::string name,
std::function<void(std::optional<std::variant<Address, RouterID>>)> resultHandler)
override;
void
LookupServiceAsync(
std::string name,
std::string service,
std::function<void(std::vector<dns::SRVData>)> resultHandler) override;
/// called on event loop pump
virtual void
Pump(llarp_time_t now);
/// stop this endpoint
bool
Stop() override;
const Identity&
GetIdentity() const
{
return m_Identity;
}
void
MapExitRange(IPRange range, service::Address exit);
void
UnmapExitRange(IPRange range);
void
PutLookup(IServiceLookup* lookup, uint64_t txid) override;
void
HandlePathBuilt(path::Path_ptr path) override;
bool
HandleDataDrop(path::Path_ptr p, const PathID_t& dst, uint64_t s);
bool
CheckPathIsDead(path::Path_ptr p, llarp_time_t latency);
using PendingBufferQueue = std::deque<PendingBuffer>;
size_t
RemoveAllConvoTagsFor(service::Address remote);
bool
WantsOutboundSession(const Address&) const override;
/// this MUST be called if you want to call EnsurePathTo on the given address
void MarkAddressOutbound(AddressVariant_t) override;
bool
ShouldBundleRC() const override
{
return false;
}
void
BlacklistSNode(const RouterID snode) override;
/// maybe get an endpoint variant given its convo tag
std::optional<std::variant<Address, RouterID>>
GetEndpointWithConvoTag(ConvoTag t) const override;
bool
HasConvoTag(const ConvoTag& t) const override;
bool
ShouldBuildMore(llarp_time_t now) const override;
virtual llarp_time_t
PathAlignmentTimeout() const
{
constexpr auto DefaultPathAlignmentTimeout = 30s;
return DefaultPathAlignmentTimeout;
}
bool
EnsurePathTo(
std::variant<Address, RouterID> addr,
std::function<void(std::optional<ConvoTag>)> hook,
llarp_time_t timeout) override;
// passed a sendto context when we have a path established otherwise
// nullptr if the path was not made before the timeout
using PathEnsureHook = std::function<void(Address, OutboundContext*)>;
static constexpr auto DefaultPathEnsureTimeout = 2s;
/// return false if we have already called this function before for this
/// address
bool
EnsurePathToService(
const Address remote,
PathEnsureHook h,
llarp_time_t timeoutMS = DefaultPathEnsureTimeout);
using SNodeEnsureHook = std::function<void(const RouterID, exit::BaseSession_ptr, ConvoTag)>;
void
InformPathToService(const Address remote, OutboundContext* ctx);
/// ensure a path to a service node by public key
bool
EnsurePathToSNode(const RouterID remote, SNodeEnsureHook h);
/// return true if this endpoint is trying to lookup this router right now
bool
HasPendingRouterLookup(const RouterID remote) const;
bool
HasPathToSNode(const RouterID remote) const;
bool
HasFlowToService(const Address remote) const;
void
PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound) override;
bool
HasInboundConvo(const Address& addr) const override;
bool
HasOutboundConvo(const Address& addr) const override;
bool
GetCachedSessionKeyFor(const ConvoTag& remote, SharedSecret& secret) const override;
void
PutCachedSessionKeyFor(const ConvoTag& remote, const SharedSecret& secret) override;
bool
GetSenderFor(const ConvoTag& remote, ServiceInfo& si) const override;
void
PutIntroFor(const ConvoTag& remote, const Introduction& intro) override;
bool
GetIntroFor(const ConvoTag& remote, Introduction& intro) const override;
void
RemoveConvoTag(const ConvoTag& remote) override;
void
ConvoTagTX(const ConvoTag& remote) override;
void
ConvoTagRX(const ConvoTag& remote) override;
void
PutReplyIntroFor(const ConvoTag& remote, const Introduction& intro) override;
bool
GetReplyIntroFor(const ConvoTag& remote, Introduction& intro) const override;
bool
GetConvoTagsForService(const Address& si, std::set<ConvoTag>& tag) const override;
void
PutNewOutboundContext(const IntroSet& introset, llarp_time_t timeLeftToAlign);
std::optional<uint64_t>
GetSeqNoForConvo(const ConvoTag& tag);
/// count unique endpoints we are talking to
size_t
UniqueEndpoints() const;
bool
HasExit() const;
std::optional<std::vector<RouterContact>>
GetHopsForBuild() override;
std::optional<std::vector<RouterContact>>
GetHopsForBuildWithEndpoint(RouterID endpoint);
virtual void
PathBuildStarted(path::Path_ptr path) override;
virtual void
IntroSetPublishFail();
virtual void
IntroSetPublished();
void
AsyncProcessAuthMessage(
std::shared_ptr<ProtocolMessage> msg, std::function<void(AuthResult)> hook);
void
SendAuthResult(path::Path_ptr path, PathID_t replyPath, ConvoTag tag, AuthResult st);
uint64_t
GenTXID();
void
ResetConvoTag(ConvoTag tag, path::Path_ptr path, PathID_t from);
const std::set<RouterID>&
SnodeBlacklist() const;
// Looks up the ConvoTag and, if it exists, calls SendToOrQueue to send it to a remote client
// or a snode (or nothing, if the convo tag is unknown).
bool
SendToOrQueue(ConvoTag tag, const llarp_buffer_t& payload, ProtocolType t) override;
// Send a to (or queues for sending) to either an address or router id
bool
SendToOrQueue(
const std::variant<Address, RouterID>& addr,
const llarp_buffer_t& payload,
ProtocolType t);
// Sends to (or queues for sending) to a remote client
bool
SendToOrQueue(const Address& addr, const llarp_buffer_t& payload, ProtocolType t);
// Sends to (or queues for sending) to a router
bool
SendToOrQueue(const RouterID& addr, const llarp_buffer_t& payload, ProtocolType t);
std::optional<AuthInfo>
MaybeGetAuthInfoForEndpoint(service::Address addr);
/// Returns a pointer to the quic::Tunnel object handling quic connections for this endpoint.
/// Returns nullptr if quic is not supported.
quic::TunnelManager*
GetQUICTunnel() override;
protected:
/// parent context that owns this endpoint
Context* const context;
virtual bool
SupportsV6() const = 0;
void
RegenAndPublishIntroSet();
IServiceLookup*
GenerateLookupByTag(const Tag& tag);
void
PrefetchServicesByTag(const Tag& tag);
private:
void
HandleVerifyGotRouter(dht::GotRouterMessage_constptr msg, RouterID id, bool valid);
bool
OnLookup(
const service::Address& addr,
std::optional<IntroSet> i,
const RouterID& endpoint,
llarp_time_t timeLeft,
uint64_t relayOrder);
bool
DoNetworkIsolation(bool failed);
virtual bool
SetupNetworking()
{
// XXX: override me
return true;
}
virtual bool
IsolationFailed()
{
// XXX: override me
return false;
}
/// return true if we are ready to do outbound and inbound traffic
bool
ReadyForNetwork() const;
protected:
bool
ReadyToDoLookup(size_t num_paths) const;
path::Path::UniqueEndpointSet_t
GetUniqueEndpointsForLookup() const;
IDataHandler* m_DataHandler = nullptr;
Identity m_Identity;
net::IPRangeMap<service::Address> m_ExitMap;
bool m_PublishIntroSet = true;
std::unique_ptr<EndpointState> m_state;
std::shared_ptr<IAuthPolicy> m_AuthPolicy;
std::unordered_map<Address, AuthInfo> m_RemoteAuthInfos;
std::unique_ptr<quic::TunnelManager> m_quic;
/// (lns name, optional exit range, optional auth info) for looking up on startup
std::unordered_map<std::string, std::pair<std::optional<IPRange>, std::optional<AuthInfo>>>
m_StartupLNSMappings;
RecvPacketQueue_t m_InboundTrafficQueue;
public:
SendMessageQueue_t m_SendQueue;
private:
llarp_time_t m_LastIntrosetRegenAttempt = 0s;
protected:
void
FlushRecvData();
friend struct EndpointUtil;
// clang-format off
const IntroSet& introSet() const;
IntroSet& introSet();
using ConvoMap = std::unordered_map<ConvoTag, Session>;
const ConvoMap& Sessions() const;
ConvoMap& Sessions();
// clang-format on
thread::Queue<RecvDataEvent> m_RecvQueue;
/// for rate limiting introset lookups
util::DecayingHashSet<Address> m_IntrosetLookupFilter;
};
using Endpoint_ptr = std::shared_ptr<Endpoint>;
} // namespace service
} // namespace llarp