diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 05e4122dc..40067810f 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -128,6 +128,9 @@ add_library(liblokinet dht/recursiverouterlookup.cpp dht/serviceaddresslookup.cpp dht/taglookup.cpp + + endpoint_base.cpp + exit/context.cpp exit/endpoint.cpp exit/exit_messages.cpp diff --git a/llarp/dns/srv_data.cpp b/llarp/dns/srv_data.cpp index 0801bfc69..4acc55766 100644 --- a/llarp/dns/srv_data.cpp +++ b/llarp/dns/srv_data.cpp @@ -4,6 +4,9 @@ #include +#include +#include "llarp/util/bencode.h" + namespace llarp::dns { bool @@ -99,4 +102,42 @@ namespace llarp::dns return IsValid(); } + bool + SRVData::BEncode(llarp_buffer_t* buf) const + { + const std::string data = oxenmq::bt_serialize(toTuple()); + return buf->write(data.begin(), data.end()); + } + + bool + SRVData::BDecode(llarp_buffer_t* buf) + { + byte_t* begin = buf->cur; + if (not bencode_discard(buf)) + return false; + byte_t* end = buf->cur; + std::string_view srvString{reinterpret_cast(begin), end - begin}; + try + { + SRVTuple tuple{}; + oxenmq::bt_deserialize(srvString, tuple); + *this = fromTuple(std::move(tuple)); + return true; + } + catch (const oxenmq::bt_deserialize_invalid&) + { + return false; + }; + } + + util::StatusObject + SRVData::ExtractStatus() const + { + return util::StatusObject{ + {"proto", service_proto}, + {"priority", priority}, + {"weight", weight}, + {"port", port}, + {"target", target}}; + } } // namespace llarp::dns diff --git a/llarp/dns/srv_data.hpp b/llarp/dns/srv_data.hpp index b7547bc8c..f61606322 100644 --- a/llarp/dns/srv_data.hpp +++ b/llarp/dns/srv_data.hpp @@ -6,6 +6,8 @@ #include #include +#include "llarp/util/status.hpp" + namespace llarp::dns { typedef std::tuple SRVTuple; @@ -36,6 +38,30 @@ namespace llarp::dns SRVTuple toTuple() const; + /// so we can put SRVData in a std::set + bool + operator<(const SRVData& other) const + { + return service_proto < other.service_proto or priority < other.priority + or weight < other.weight or port < other.port or target < other.target; + } + + bool + operator==(const SRVData& other) const + { + return service_proto == other.service_proto and priority == other.priority + and weight == other.weight and port == other.port and target == other.target; + } + + bool + BEncode(llarp_buffer_t*) const; + + bool + BDecode(llarp_buffer_t*); + + util::StatusObject + ExtractStatus() const; + static SRVData fromTuple(SRVTuple tuple); @@ -60,3 +86,19 @@ namespace llarp::dns }; } // namespace llarp::dns + +namespace std +{ + template <> + struct hash + { + size_t + operator()(const llarp::dns::SRVData& data) const + { + const std::hash h_str{}; + const std::hash h_port{}; + return h_str(data.service_proto) ^ (h_str(data.target) << 3) ^ (h_port(data.priority) << 5) + ^ (h_port(data.weight) << 7) ^ (h_port(data.port) << 9); + } + }; +} // namespace std diff --git a/llarp/endpoint_base.hpp b/llarp/endpoint_base.hpp index c20ca6cea..b0ce0e465 100644 --- a/llarp/endpoint_base.hpp +++ b/llarp/endpoint_base.hpp @@ -4,13 +4,16 @@ #include "llarp/service/convotag.hpp" #include "llarp/service/protocol_type.hpp" #include "router_id.hpp" -#include "ev/ev.hpp" +#include "llarp/ev/ev.hpp" +#include "llarp/dns/srv_data.hpp" #include #include #include #include #include +#include +#include #include "oxenmq/variant.h" namespace llarp @@ -22,6 +25,8 @@ namespace llarp class EndpointBase { + std::unordered_set m_SRVRecords; + public: virtual ~EndpointBase() = default; @@ -43,10 +48,51 @@ namespace llarp Duration_t lastRecvAt; }; - /// get statistics about how much traffic we sent and recv'd via remote endpoints we are talking - /// to - virtual std::unordered_map - GetStatistics() const = 0; + /// info about a quic mapping + struct QUICMappingInfo + { + /// srv data if it was provided + std::optional srv; + /// address we are bound on + SockAddr localAddr; + /// the remote's lns name if we have one + std::optional remoteName; + /// the remote's address + AddressVariant_t remoteAddr; + /// the remote's port we are connecting to + uint16_t remotePort; + }; + + /// maybe get quic mapping info given its stream id + /// returns std::nullopt if we have no stream given that id + std::optional + GetQUICMappingInfoByID(int stream_id) const; + + /// add an srv record to this endpoint's descriptor + void + PutSRVRecord(dns::SRVData srv); + + /// called when srv data changes in some way + virtual void + SRVRecordsChanged() = 0; + + /// remove srv records from this endpoint that match a filter + /// for each srv record call it with filter, remove if filter returns true + /// return if we removed any srv records + bool + DelSRVRecordIf(std::function filter); + + /// get copy of all srv records + std::set + SRVRecords() const; + + /// get statistics about how much traffic we sent and recv'd to a remote endpoint + virtual std::optional + GetStatFor(AddressVariant_t remote) const = 0; + + /// list all remote endpoint addresses we have that are mapped + virtual std::unordered_set + AllRemoteEndpoints() const = 0; /// get our local address virtual AddressVariant_t diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index bdbf7ab90..4f105a3de 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -563,10 +563,49 @@ namespace llarp return RouterID{m_Router->pubkey()}; } - std::unordered_map - ExitEndpoint::GetStatistics() const + void + ExitEndpoint::SRVRecordsChanged() { - return {}; + m_Router->ModifyOurRC( + [srvRecords = SRVRecords()](RouterContact rc) -> std::optional { + // 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 version to 1 because we have srv records + rc.version = 1; + return rc; + }); + } + + std::optional ExitEndpoint::GetStatFor(AddressVariant_t) const + { + /// TODO: implement me + return std::nullopt; + } + + std::unordered_set + ExitEndpoint::AllRemoteEndpoints() const + { + std::unordered_set remote; + for (auto itr = m_Paths.begin(); itr != m_Paths.end(); ++itr) + { + remote.insert(RouterID{itr->second}); + } + return remote; } bool diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index 9bdcdf29b..4a8e87c3f 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -35,6 +35,12 @@ namespace llarp const EventLoop_ptr& Loop() override; + std::unordered_set + AllRemoteEndpoints() const override; + + void + SRVRecordsChanged() override; + bool SendToOrQueue( service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) override; @@ -112,8 +118,8 @@ namespace llarp AddressVariant_t LocalAddress() const override; - std::unordered_map - GetStatistics() const override; + std::optional + GetStatFor(AddressVariant_t remote) const override; /// sets up networking and starts traffic bool diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index f4f7a1b8c..388be3825 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -13,6 +13,8 @@ #include #include +#include + #ifdef LOKINET_HIVE #include #endif @@ -114,6 +116,12 @@ namespace llarp virtual const RouterContact& rc() const = 0; + /// modify our rc + /// modify returns nullopt if unmodified otherwise it returns the new rc to be sigend and + /// published out + virtual void + ModifyOurRC(std::function(RouterContact)> modify) = 0; + virtual exit::Context& exitContext() = 0; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index f62a27c3f..063138cf0 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -946,6 +946,17 @@ namespace llarp _outboundSessionMaker.OnConnectTimeout(session); } + void + Router::ModifyOurRC(std::function(RouterContact)> modify) + { + if (auto maybe = modify(rc())) + { + _rc = *maybe; + UpdateOurRC(); + _rcGossiper.GossipRC(rc()); + } + } + bool Router::ConnectionEstablished(ILinkSession* session, bool inbound) { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 3300eb597..662b9bab7 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -119,6 +119,9 @@ namespace llarp return _rc; } + void + ModifyOurRC(std::function(RouterContact)> modify) override; + void SetRouterWhitelist(const std::vector routers) override; diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index 60e288c90..a4c7ff1a9 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -163,12 +163,20 @@ namespace llarp return false; if (!enckey.BEncode(buf)) return false; + // write router version if present if (routerVersion) { if (not BEncodeWriteDictEntry("r", *routerVersion, buf)) return false; } + + if (version > 0) + { + // srv records if present + if (not BEncodeWriteDictList("s", srvRecords, buf)) + return false; + } /* write last updated */ if (!bencode_write_bytestring(buf, "u", 1)) return false; @@ -212,6 +220,8 @@ namespace llarp pubkey.Zero(); routerVersion = std::optional{}; last_updated = 0s; + srvRecords.clear(); + version = LLARP_PROTO_VERSION; } util::StatusObject @@ -231,6 +241,12 @@ namespace llarp { obj["routerVersion"] = routerVersion->ToString(); } + std::vector srv; + for (const auto& record : srvRecords) + { + srv.emplace_back(record.ExtractStatus()); + } + obj["srvRecords"] = srv; return obj; } @@ -345,6 +361,9 @@ namespace llarp return true; } + if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf)) + return false; + if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf)) return false; @@ -362,7 +381,7 @@ namespace llarp if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf)) return false; - return read; + return read or bencode_discard(buf); } bool @@ -503,7 +522,7 @@ namespace llarp /* else */ if (version == 1) { - llarp_buffer_t buf(signed_bt_dict); + llarp_buffer_t buf{signed_bt_dict}; return CryptoManager::instance()->verify(pubkey, buf, signature); } diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index 48442a202..347c0e405 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -1,14 +1,16 @@ #pragma once -#include "constants/version.hpp" -#include "crypto/types.hpp" -#include "net/address_info.hpp" -#include "net/exit_info.hpp" -#include "util/aligned.hpp" -#include "util/bencode.hpp" -#include "util/status.hpp" +#include "llarp/constants/version.hpp" +#include "llarp/crypto/types.hpp" +#include "llarp/net/address_info.hpp" +#include "llarp/net/exit_info.hpp" +#include "llarp/util/aligned.hpp" +#include "llarp/util/bencode.hpp" +#include "llarp/util/status.hpp" #include "router_version.hpp" +#include "llarp/dns/srv_data.hpp" + #include #include #include @@ -104,6 +106,8 @@ namespace llarp std::string signed_bt_dict; + std::vector srvRecords; + util::StatusObject ExtractStatus() const; diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 3f56cb759..9cc244fb2 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -226,12 +226,16 @@ namespace llarp::rpc if (auto itr = obj.find("close"); itr != obj.end()) closeID = itr->get(); + std::string srvProto; + if (auto itr = obj.find("srv-proto"); itr != obj.end()) + srvProto = itr->get(); + if (port == 0 and closeID == 0) { reply(CreateJSONError("invalid arguments")); return; } - r->loop()->call([reply, endpoint, remote, port, r, closeID]() { + r->loop()->call([reply, endpoint, remote, port, r, closeID, srvProto]() { auto ep = GetEndpointByName(r, endpoint); if (not ep) { @@ -263,6 +267,12 @@ namespace llarp::rpc var::visit( [&](auto&& addr) { localAddress = addr.ToString(); }, ep->LocalAddress()); result["addr"] = localAddress + ":" + std::to_string(port); + if (not srvProto.empty()) + { + auto srvData = + dns::SRVData::fromTuple(std::make_tuple(srvProto, 1, 1, port, "")); + ep->PutSRVRecord(std::move(srvData)); + } reply(CreateJSONResponse(result)); } else if (closeID) diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 2b2bb5553..d758f4be3 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -1030,20 +1030,25 @@ namespace llarp const std::shared_ptr& session, EndpointBase::SendStat& stats) {} - std::unordered_map - Endpoint::GetStatistics() const + std::optional Endpoint::GetStatFor(AddressVariant_t) const { - std::unordered_map stats; + // TODO: implement me + return std::nullopt; + } + + std::unordered_set + Endpoint::AllRemoteEndpoints() const + { + std::unordered_set remote; for (const auto& item : Sessions()) { - Address addr = item.second.remote.Addr(); - AccumulateStats(item.second, stats[addr]); + remote.insert(item.second.remote.Addr()); } for (const auto& item : m_state->m_SNodeSessions) { - AccumulateStats(item.second.first, stats[item.first]); + remote.insert(item.first); } - return stats; + return remote; } bool @@ -1329,6 +1334,17 @@ namespace llarp return hookAdded; } + void + Endpoint::SRVRecordsChanged() + { + auto& introset = introSet(); + introset.SRVs.clear(); + for (const auto& srv : SRVRecords()) + introset.SRVs.emplace_back(srv.toTuple()); + + RegenAndPublishIntroSet(); + } + bool Endpoint::EnsurePathToSNode(const RouterID snode, SNodeEnsureHook h) { diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index eee335ee7..d3cdb58f0 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -139,12 +139,18 @@ namespace llarp AddressVariant_t LocalAddress() const override; - std::unordered_map - GetStatistics() const override; + std::optional + GetStatFor(AddressVariant_t remote) const override; + + std::unordered_set + AllRemoteEndpoints() const override; bool ShouldPublishDescriptors(llarp_time_t now) const override; + void + SRVRecordsChanged() override; + void HandlePathDied(path::Path_ptr p) override; diff --git a/llarp/service/intro_set.cpp b/llarp/service/intro_set.cpp index 4edb1b306..fded044b2 100644 --- a/llarp/service/intro_set.cpp +++ b/llarp/service/intro_set.cpp @@ -177,7 +177,7 @@ namespace llarp::service byte_t* end = buf->cur; - std::string_view srvString(reinterpret_cast(begin), end - begin); + std::string_view srvString{reinterpret_cast(begin), end - begin}; try {