diff --git a/llarp/endpoint_base.hpp b/llarp/endpoint_base.hpp index ea42a49df..1351777ab 100644 --- a/llarp/endpoint_base.hpp +++ b/llarp/endpoint_base.hpp @@ -14,6 +14,11 @@ namespace llarp { + namespace quic + { + class TunnelManager; + } + class EndpointBase { public: @@ -21,6 +26,9 @@ namespace llarp using AddressVariant_t = std::variant; + virtual quic::TunnelManager* + GetQUICTunnel() = 0; + virtual std::optional GetEndpointWithConvoTag(service::ConvoTag tag) const = 0; diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp index b2a7581e4..ab360ec15 100644 --- a/llarp/exit/context.cpp +++ b/llarp/exit/context.cpp @@ -96,6 +96,16 @@ namespace llarp return false; } + std::shared_ptr + Context::GetExitEndpoint(std::string name) const + { + if (auto itr = m_Exits.find(name); itr != m_Exits.end()) + { + return itr->second; + } + return nullptr; + } + void Context::AddExitEndpoint( const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig) diff --git a/llarp/exit/context.hpp b/llarp/exit/context.hpp index 91398efa9..beb371aee 100644 --- a/llarp/exit/context.hpp +++ b/llarp/exit/context.hpp @@ -44,6 +44,9 @@ namespace llarp void CalculateExitTraffic(TrafficStats& stats); + std::shared_ptr + GetExitEndpoint(std::string name) const; + private: AbstractRouter* m_Router; std::unordered_map> m_Exits; diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index f51144ef4..712c61ba7 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -129,12 +129,15 @@ namespace llarp Flush(); quic::TunnelManager* - GetQUICTunnel(); + GetQUICTunnel() override; - private: huint128_t GetIPForIdent(const PubKey pk); + /// async obtain snode session and call callback when it's ready to send + void + ObtainSNodeSession(const RouterID& router, exit::SessionReadyFunc obtainCb); + private: huint128_t AllocateNewAddress(); @@ -143,10 +146,6 @@ namespace llarp huint128_t ObtainServiceNodeIP(const RouterID& router); - /// async obtain snode session and call callback when it's ready to send - void - ObtainSNodeSession(const RouterID& router, exit::SessionReadyFunc obtainCb); - bool QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from); diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 455f142e7..5a217e70c 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -1022,6 +1022,8 @@ namespace llarp LogWarn("invalid incoming quic packet, dropping"); return false; } + LogInfo("tag active T=", tag); + MarkConvoTagActive(tag); quic->receive_packet(tag, buf); return true; } diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 812cfa955..0eb49772f 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -1248,7 +1248,7 @@ namespace llarp LogInfo("accepting transit traffic"); paths.AllowTransit(); llarp_dht_allow_transit(dht()); - _exitContext.AddExitEndpoint("default-connectivity", m_Config->network, m_Config->dns); + _exitContext.AddExitEndpoint("default", m_Config->network, m_Config->dns); return true; } diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 14c3fce82..c13330912 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -53,6 +55,19 @@ namespace llarp::rpc /// a function that replies to an rpc request using ReplyFunction_t = std::function; + std::shared_ptr + GetEndpointByName(AbstractRouter* r, std::string name) + { + if (r->IsServiceNode()) + { + return r->exitContext().GetExitEndpoint(name); + } + else + { + return r->hiddenServiceContext().GetEndpointByName(name); + } + } + void HandleJSONRequest( oxenmq::Message& msg, std::function handleRequest) @@ -118,6 +133,177 @@ namespace llarp::rpc defer.reply(data); }); }) + .add_request_command( + "quic_connect", + [&](oxenmq::Message& msg) { + HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) { + std::string endpoint = "default"; + if (auto itr = obj.find("endpoint"); itr != obj.end()) + endpoint = itr->get(); + + std::string remoteHost; + if (auto itr = obj.find("host"); itr != obj.end()) + remoteHost = itr->get(); + + uint16_t port = 0; + if (auto itr = obj.find("port"); itr != obj.end()) + port = itr->get(); + + int closeID = 0; + if (auto itr = obj.find("close"); itr != obj.end()) + closeID = itr->get(); + + if (port == 0 and closeID == 0) + { + reply(CreateJSONError("port not provided")); + return; + } + if (remoteHost.empty() and closeID == 0) + { + reply(CreateJSONError("host not provided")); + return; + } + + r->loop()->call([reply, endpoint, r, remoteHost, port, closeID]() { + auto ep = GetEndpointByName(r, endpoint); + if (not ep) + { + reply(CreateJSONError("no such local endpoint")); + return; + } + auto quic = ep->GetQUICTunnel(); + if (not quic) + { + reply(CreateJSONError("local endpoint has no quic tunnel")); + return; + } + if (closeID) + { + quic->close(closeID); + reply(CreateJSONResponse("OK")); + return; + } + + auto status = std::make_shared(); + + auto hook = [status, reply](bool success) { + if (success) + { + reply(CreateJSONResponse(*status)); + } + else + { + reply(CreateJSONError("failed")); + } + }; + auto [addr, id] = quic->open(remoteHost, port, hook); + status->operator[]("addr") = addr.toString(); + status->operator[]("id") = id; + }); + }); + }) + .add_request_command( + "quic_listener", + [&](oxenmq::Message& msg) { + HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) { + std::string endpoint = "default"; + if (auto itr = obj.find("endpoint"); itr != obj.end()) + endpoint = itr->get(); + + uint16_t port = 0; + if (auto itr = obj.find("port"); itr != obj.end()) + port = itr->get(); + + int closeID = 0; + if (auto itr = obj.find("close"); itr != obj.end()) + closeID = itr->get(); + + if (port == 0 and closeID == 0) + { + reply(CreateJSONError("invalid arguments")); + return; + } + r->loop()->call([reply, endpoint, port, r, closeID]() { + auto ep = GetEndpointByName(r, endpoint); + if (not ep) + { + reply(CreateJSONError("no such local endpoint")); + return; + } + auto quic = ep->GetQUICTunnel(); + if (not quic) + { + reply(CreateJSONError("no quic interface available on endpoint " + endpoint)); + return; + } + if (port) + { + int id = 0; + try + { + id = quic->listen(port); + } + catch (std::exception& ex) + { + reply(CreateJSONError(ex.what())); + return; + } + util::StatusObject result{{"id", id}}; + reply(CreateJSONResponse(result)); + } + else if (closeID) + { + quic->forget(closeID); + reply(CreateJSONResponse("OK")); + } + }); + }); + }) + .add_request_command( + "lookup_snode", + [&](oxenmq::Message& msg) { + HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) { + if (not r->IsServiceNode()) + { + reply(CreateJSONError("not supported")); + return; + } + RouterID routerID; + if (auto itr = obj.find("snode"); itr != obj.end()) + { + std::string remote = itr->get(); + if (not routerID.FromString(remote)) + { + reply(CreateJSONError("invalid remote: " + remote)); + return; + } + } + else + { + reply(CreateJSONError("no remote provided")); + return; + } + std::string endpoint = "default"; + r->loop()->call([r, endpoint, routerID, reply]() { + auto ep = r->exitContext().GetExitEndpoint(endpoint); + if (ep == nullptr) + { + reply(CreateJSONError("cannot find local endpoint: " + endpoint)); + return; + } + ep->ObtainSNodeSession(routerID, [routerID, ep, reply](auto session) { + if (session and session->IsReady()) + { + const auto ip = net::TruncateV6(ep->GetIPForIdent(PubKey{routerID})); + util::StatusObject status{{"ip", ip.ToString()}}; + reply(CreateJSONResponse(status)); + } + else + reply(CreateJSONError("failed to obtain snode session")); + }); + }); + }); + }) .add_request_command( "endpoint", [&](oxenmq::Message& msg) { diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 0650c0cd3..6b8a55d2a 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -399,7 +399,7 @@ namespace llarp /// Returns a pointer to the quic::Tunnel object handling quic connections for this endpoint. /// Returns nullptr if quic is not supported. quic::TunnelManager* - GetQUICTunnel(); + GetQUICTunnel() override; protected: /// parent context that owns this endpoint diff --git a/llarp/service/endpoint_util.cpp b/llarp/service/endpoint_util.cpp index f84e00f52..7fe7b1fa7 100644 --- a/llarp/service/endpoint_util.cpp +++ b/llarp/service/endpoint_util.cpp @@ -95,7 +95,7 @@ namespace llarp itr->second->Tick(now); if (itr->second->Pump(now)) { - LogInfo("marking session as dead T=", itr->first); + LogInfo("marking session as dead T=", itr->second->currentConvoTag); itr->second->Stop(); sessions.erase(itr->second->currentConvoTag); deadSessions.emplace(std::move(*itr)); diff --git a/llarp/service/outbound_context.cpp b/llarp/service/outbound_context.cpp index b4262a35c..b6bd270cc 100644 --- a/llarp/service/outbound_context.cpp +++ b/llarp/service/outbound_context.cpp @@ -67,6 +67,8 @@ namespace llarp if (intro.expiresAt > m_NextIntro.expiresAt) m_NextIntro = intro; } + + currentConvoTag.Randomize(); } OutboundContext::~OutboundContext() = default; @@ -182,12 +184,10 @@ namespace llarp void OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t) { - if (not currentConvoTag.IsZero()) - return; if (remoteIntro.router.IsZero()) SwapIntros(); - auto path = m_PathSet->GetNewestPathByRouter(remoteIntro.router); + auto path = m_PathSet->GetPathByRouter(remoteIntro.router); if (path == nullptr) { // try parent as fallback @@ -200,7 +200,6 @@ namespace llarp return; } } - currentConvoTag.Randomize(); auto frame = std::make_shared(); auto ex = std::make_shared( m_Endpoint->Loop(), diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp index 35c36864e..e2cf328fc 100644 --- a/llarp/service/protocol.cpp +++ b/llarp/service/protocol.cpp @@ -412,7 +412,6 @@ namespace llarp msg->handler = handler; if (T.IsZero()) { - LogInfo("Got protocol frame with new convo"); // we need to dh auto dh = std::make_shared( loop, localIdent, handler, msg, *this, recvPath->intro);