mirror of https://github.com/oxen-io/lokinet
refactor dns subsystem
we want to be able to have multiple locally bound dns sockets in lokinet so i restructured most of the dns subsystem in order to make this easier. specifically, we have a new structure to dns subsystem: * dns::QueryJob_Base base type for holding a dns query and response with virtual methods in charge of sending a reply to whoever requested. * dns::PacketSource_Base base type for reading and writing dns messages to and from wherever they came from * dns::Resolver_Base base type for filtering and handling of dns messages asynchronously. * dns::Server contextualized per endpoint dns object, responsible for all dns related isms. this change hides all impelementation details of all of the dns components. adds some more helper functions for parsing dns and dealing with OwnedBuffer. overall dns becomes less of a pain with this new structure. probably.
This commit is contained in:
parent
bf2488d9e8
commit
74362149eb
|
@ -85,7 +85,7 @@ add_library(liblokinet
|
|||
dns/serialize.cpp
|
||||
dns/server.cpp
|
||||
dns/srv_data.cpp
|
||||
dns/unbound_resolver.cpp
|
||||
dns/resolver.cpp
|
||||
|
||||
consensus/table.cpp
|
||||
consensus/reachability_testing.cpp
|
||||
|
@ -151,7 +151,7 @@ add_library(liblokinet
|
|||
peerstats/types.cpp
|
||||
pow.cpp
|
||||
profiling.cpp
|
||||
|
||||
|
||||
quic/address.cpp
|
||||
quic/client.cpp
|
||||
quic/connection.cpp
|
||||
|
@ -171,7 +171,7 @@ add_library(liblokinet
|
|||
router/rc_gossiper.cpp
|
||||
router/router.cpp
|
||||
router/route_poker.cpp
|
||||
router/systemd_resolved.cpp
|
||||
|
||||
routing/dht_message.cpp
|
||||
routing/message_parser.cpp
|
||||
routing/path_confirm_message.cpp
|
||||
|
|
|
@ -775,18 +775,25 @@ namespace llarp
|
|||
// Most non-linux platforms have loopback as 127.0.0.1/32, but linux uses 127.0.0.1/8 so that we
|
||||
// can bind to other 127.* IPs to avoid conflicting with something else that may be listening on
|
||||
// 127.0.0.1:53.
|
||||
constexpr Default DefaultDNSBind{platform::is_linux ? "127.3.2.1:53" : "127.0.0.1:53"};
|
||||
#ifdef __linux__
|
||||
#ifdef WITH_SYSTEMD
|
||||
// when we have systemd support add a random high port on loopback
|
||||
// see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282
|
||||
constexpr Default DefaultDNSBind{"127.0.0.1:0"};
|
||||
#else
|
||||
constexpr Default DefaultDNSBind{"127.3.2.1:53"};
|
||||
#endif
|
||||
#else
|
||||
constexpr Default DefaultDNSBind{"127.0.0.1:53"};
|
||||
#endif
|
||||
|
||||
// Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it
|
||||
constexpr Default DefaultUpstreamDNS{"9.9.9.10"};
|
||||
constexpr Default DefaultUpstreamDNS{"9.9.9.10:53"};
|
||||
m_upstreamDNS.emplace_back(DefaultUpstreamDNS.val);
|
||||
if (!m_upstreamDNS.back().getPort())
|
||||
m_upstreamDNS.back().setPort(53);
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"dns",
|
||||
"upstream",
|
||||
DefaultUpstreamDNS,
|
||||
MultiValue,
|
||||
Comment{
|
||||
"Upstream resolver(s) to use as fallback for non-loki addresses.",
|
||||
|
@ -798,10 +805,10 @@ namespace llarp
|
|||
m_upstreamDNS.clear();
|
||||
first = false;
|
||||
}
|
||||
if (!arg.empty())
|
||||
if (not arg.empty())
|
||||
{
|
||||
auto& entry = m_upstreamDNS.emplace_back(std::move(arg));
|
||||
if (!entry.getPort())
|
||||
if (not entry.getPort())
|
||||
entry.setPort(53);
|
||||
}
|
||||
});
|
||||
|
@ -814,9 +821,12 @@ namespace llarp
|
|||
"Address to bind to for handling DNS requests.",
|
||||
},
|
||||
[=](std::string arg) {
|
||||
m_bind = SockAddr{std::move(arg)};
|
||||
if (!m_bind.getPort())
|
||||
m_bind.setPort(53);
|
||||
SockAddr addr{arg};
|
||||
// set dns port if no explicit port specified
|
||||
// explicit :0 allowed
|
||||
if (not addr.getPort() and not ends_with(arg, ":0"))
|
||||
addr.setPort(53);
|
||||
m_bind.emplace_back(addr);
|
||||
});
|
||||
|
||||
conf.defineOption<fs::path>(
|
||||
|
@ -843,6 +853,11 @@ namespace llarp
|
|||
"(This is not used directly by lokinet itself, but by the lokinet init scripts",
|
||||
"on systems which use resolveconf)",
|
||||
});
|
||||
|
||||
// forwad the rest to libunbound
|
||||
conf.addUndeclaredHandler("dns", [this](auto, std::string_view key, std::string_view val) {
|
||||
m_ExtraOpts.emplace(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -155,9 +155,10 @@ namespace llarp
|
|||
|
||||
struct DnsConfig
|
||||
{
|
||||
SockAddr m_bind;
|
||||
std::vector<SockAddr> m_bind;
|
||||
std::vector<SockAddr> m_upstreamDNS;
|
||||
std::vector<fs::path> m_hostfiles;
|
||||
std::unordered_multimap<std::string, std::string> m_ExtraOpts;
|
||||
|
||||
void
|
||||
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
|
||||
|
|
|
@ -414,5 +414,17 @@ namespace llarp
|
|||
fmt::format("{}", fmt::join(additional, ",")));
|
||||
}
|
||||
|
||||
std::optional<Message>
|
||||
MaybeParseDNSMessage(llarp_buffer_t buf)
|
||||
{
|
||||
MessageHeader hdr{};
|
||||
if (not hdr.Decode(&buf))
|
||||
return std::nullopt;
|
||||
|
||||
Message msg{hdr};
|
||||
if (not msg.Decode(&buf))
|
||||
return std::nullopt;
|
||||
return msg;
|
||||
}
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
|
|
|
@ -103,6 +103,9 @@ namespace llarp
|
|||
std::vector<ResourceRecord> authorities;
|
||||
std::vector<ResourceRecord> additional;
|
||||
};
|
||||
|
||||
std::optional<Message>
|
||||
MaybeParseDNSMessage(llarp_buffer_t buf);
|
||||
} // namespace dns
|
||||
|
||||
template <>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#include "systemd_resolved.hpp"
|
||||
#include "resolver.hpp"
|
||||
#include <llarp/util/logging.hpp>
|
||||
|
||||
#ifndef WITH_SYSTEMD
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
bool
|
||||
systemd_resolved_set_dns(std::string, llarp::SockAddr, bool)
|
||||
set_resolver(std::string, llarp::SockAddr, bool)
|
||||
{
|
||||
LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS");
|
||||
return false;
|
||||
}
|
||||
} // namespace llarp
|
||||
} // namespace llarp::dns
|
||||
|
||||
#else
|
||||
|
||||
|
@ -25,7 +25,7 @@ extern "C"
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ namespace llarp
|
|||
} // namespace
|
||||
|
||||
bool
|
||||
systemd_resolved_set_dns(std::string ifname, llarp::SockAddr dns, bool global)
|
||||
set_resolver(std::string ifname, llarp::SockAddr dns, bool global)
|
||||
{
|
||||
unsigned int if_ndx = if_nametoindex(ifname.c_str());
|
||||
if (if_ndx == 0)
|
||||
|
@ -204,6 +204,6 @@ namespace llarp
|
|||
return false;
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
} // namespace llarp::dns
|
||||
|
||||
#endif // WITH_SYSTEMD
|
|
@ -1,13 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
/// Attempts to set lokinet as the DNS server for systemd-resolved. Returns true if successful,
|
||||
/// false if unsupported or fails. (When compiled without systemd support this always returns
|
||||
/// false without doing anything).
|
||||
/// Attempts to set lokinet as the DNS server for systemd-resolved.
|
||||
/// Returns true if successful, false if unsupported or fails.
|
||||
///
|
||||
/// If systemd support is enabled it will attempt via dbus call to system-resolved
|
||||
/// When compiled without systemd support this always return false without doing anything.
|
||||
///
|
||||
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0.
|
||||
/// Typically tun_endpoint.GetIfName().
|
||||
|
@ -15,5 +16,6 @@ namespace llarp
|
|||
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
|
||||
/// addresses (false).
|
||||
bool
|
||||
systemd_resolved_set_dns(std::string if_name, llarp::SockAddr dns, bool global);
|
||||
} // namespace llarp
|
||||
set_resolver(std::string if_name, llarp::SockAddr dns, bool global);
|
||||
|
||||
} // namespace llarp::dns
|
|
@ -4,158 +4,539 @@
|
|||
#include <array>
|
||||
#include <utility>
|
||||
#include <llarp/ev/udp_handle.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <unbound.h>
|
||||
#include <uvw.hpp>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
PacketHandler::PacketHandler(EventLoop_ptr loop, IQueryHandler* h)
|
||||
: m_QueryHandler{h}, m_Loop{std::move(loop)}
|
||||
void
|
||||
QueryJob_Base::Cancel() const
|
||||
{
|
||||
Message reply{m_Query};
|
||||
reply.AddServFail();
|
||||
SendReply(reply.ToBuffer());
|
||||
}
|
||||
|
||||
/// sucks up udp packets from a bound socket and feeds it to a server
|
||||
class UDPReader : public PacketSource_Base, public std::enable_shared_from_this<UDPReader>
|
||||
{
|
||||
Server& m_DNS;
|
||||
std::shared_ptr<llarp::UDPHandle> m_udp;
|
||||
SockAddr m_LocalAddr;
|
||||
|
||||
public:
|
||||
explicit UDPReader(Server& dns, const EventLoop_ptr& loop, llarp::SockAddr bindaddr)
|
||||
: m_DNS{dns}
|
||||
{
|
||||
m_udp = loop->make_udp([&](auto&, SockAddr src, llarp::OwnedBuffer buf) {
|
||||
if (src == m_LocalAddr)
|
||||
return;
|
||||
if (not m_DNS.MaybeHandlePacket(weak_from_this(), m_LocalAddr, src, std::move(buf)))
|
||||
{
|
||||
LogWarn("did not handle dns packet from ", src, " to ", m_LocalAddr);
|
||||
}
|
||||
});
|
||||
m_udp->listen(bindaddr);
|
||||
if (auto maybe_addr = BoundOn())
|
||||
{
|
||||
m_LocalAddr = *maybe_addr;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error{"cannot find which address our dns socket is bound on"};
|
||||
}
|
||||
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
return m_udp->LocalAddr();
|
||||
}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr&) const override
|
||||
{
|
||||
return to != m_LocalAddr;
|
||||
}
|
||||
|
||||
void
|
||||
SendTo(const SockAddr& to, const SockAddr&, llarp::OwnedBuffer buf) const override
|
||||
{
|
||||
m_udp->send(to, std::move(buf));
|
||||
}
|
||||
|
||||
void
|
||||
Stop() override
|
||||
{
|
||||
m_udp->close();
|
||||
}
|
||||
};
|
||||
|
||||
namespace libunbound
|
||||
{
|
||||
class Resolver;
|
||||
|
||||
class Query : public QueryJob_Base
|
||||
{
|
||||
std::weak_ptr<Resolver> parent;
|
||||
std::weak_ptr<PacketSource_Base> src;
|
||||
SockAddr resolverAddr;
|
||||
SockAddr askerAddr;
|
||||
|
||||
public:
|
||||
explicit Query(
|
||||
std::weak_ptr<Resolver> parent_,
|
||||
Message query,
|
||||
std::weak_ptr<PacketSource_Base> pktsrc,
|
||||
SockAddr toaddr,
|
||||
SockAddr fromaddr)
|
||||
: QueryJob_Base{std::move(query)}
|
||||
, parent{parent_}
|
||||
, src{pktsrc}
|
||||
, resolverAddr{std::move(toaddr)}
|
||||
, askerAddr{std::move(fromaddr)}
|
||||
{}
|
||||
|
||||
virtual void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const override;
|
||||
};
|
||||
|
||||
/// Resolver_Base that uses libunbound
|
||||
class Resolver : public Resolver_Base, public std::enable_shared_from_this<Resolver>
|
||||
{
|
||||
std::shared_ptr<ub_ctx> m_ctx;
|
||||
std::weak_ptr<EventLoop> m_Loop;
|
||||
#ifdef _WIN32
|
||||
// windows is dumb so we do ub mainloop in a thread
|
||||
std::thread runner;
|
||||
std::atomic<bool> running;
|
||||
#else
|
||||
std::shared_ptr<uvw::PollHandle> m_Poller;
|
||||
#endif
|
||||
|
||||
struct ub_result_deleter
|
||||
{
|
||||
void
|
||||
operator()(ub_result* ptr)
|
||||
{
|
||||
::ub_resolve_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
Callback(void* data, int err, ub_result* _result)
|
||||
{
|
||||
// take ownership of ub_result
|
||||
std::unique_ptr<ub_result, ub_result_deleter> result{_result};
|
||||
// take ownership of our query
|
||||
std::unique_ptr<Query> query{static_cast<Query*>(data)};
|
||||
|
||||
if (err)
|
||||
{
|
||||
// some kind of error from upstream
|
||||
query->Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// rewrite response
|
||||
OwnedBuffer pkt{(const byte_t*)result->answer_packet, (size_t)result->answer_len};
|
||||
llarp_buffer_t buf{pkt};
|
||||
MessageHeader hdr;
|
||||
hdr.Decode(&buf);
|
||||
hdr.id = query->Underlying().hdr_id;
|
||||
buf.cur = buf.base;
|
||||
hdr.Encode(&buf);
|
||||
|
||||
// send reply
|
||||
query->SendReply(std::move(pkt));
|
||||
}
|
||||
|
||||
void
|
||||
SetOpt(std::string key, std::string val)
|
||||
{
|
||||
ub_ctx_set_option(m_ctx.get(), key.c_str(), val.c_str());
|
||||
}
|
||||
|
||||
llarp::DnsConfig m_conf;
|
||||
|
||||
public:
|
||||
explicit Resolver(const EventLoop_ptr& loop, llarp::DnsConfig conf)
|
||||
: m_ctx{::ub_ctx_create(), ::ub_ctx_delete}, m_Loop{loop}, m_conf{std::move(conf)}
|
||||
{
|
||||
Up(m_conf);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
virtual ~Resolver()
|
||||
{
|
||||
running = false;
|
||||
runner.join();
|
||||
}
|
||||
#else
|
||||
virtual ~Resolver() = default;
|
||||
#endif
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "unbound";
|
||||
}
|
||||
|
||||
void
|
||||
Up(const llarp::DnsConfig& conf)
|
||||
{
|
||||
// set libunbound settings
|
||||
for (const auto& [k, v] : conf.m_ExtraOpts)
|
||||
SetOpt(k, v);
|
||||
|
||||
// add host files
|
||||
for (const auto& file : conf.m_hostfiles)
|
||||
{
|
||||
const auto str = file.u8string();
|
||||
if (auto ret = ub_ctx_hosts(m_ctx.get(), str.c_str()))
|
||||
{
|
||||
throw std::runtime_error{
|
||||
fmt::format("Failed to add host file {}: {}", file, ub_strerror(ret))};
|
||||
}
|
||||
}
|
||||
|
||||
// set up forward dns
|
||||
for (const auto& dns : conf.m_upstreamDNS)
|
||||
{
|
||||
std::stringstream ss;
|
||||
auto hoststr = dns.hostString();
|
||||
ss << hoststr;
|
||||
|
||||
if (const auto port = dns.getPort(); port != 53)
|
||||
ss << "@" << port;
|
||||
|
||||
const auto str = ss.str();
|
||||
if (auto err = ub_ctx_set_fwd(m_ctx.get(), str.c_str()))
|
||||
{
|
||||
throw std::runtime_error{
|
||||
fmt::format("cannot use {} as upstream dns: {}", str, ub_strerror(err))};
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
// On Apple, we configure a localhost resolver to trampoline requests through the tunnel
|
||||
// to the actual upstream (because the network extension itself cannot route through the
|
||||
// tunnel using normal sockets but instead we "get" to use Apple's interfaces, hurray).
|
||||
if (hoststr == "127.0.0.1")
|
||||
{
|
||||
// Not at all clear why this is needed but without it we get "send failed: Can't
|
||||
// assign requested address" when unbound tries to connect to the localhost address
|
||||
// using a source address of 0.0.0.0. Yay apple.
|
||||
SetOpt("outgoing-interface:", hoststr.c_str());
|
||||
// The trampoline expects just a single source port (and sends everything back to it)
|
||||
SetOpt("outgoing-range:", "1");
|
||||
SetOpt("outgoing-port-avoid:", "0-65535");
|
||||
SetOpt("outgoing-port-permit:", "1253");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// set async
|
||||
ub_ctx_async(m_ctx.get(), 1);
|
||||
// setup mainloop
|
||||
#ifdef _WIN32
|
||||
running = true;
|
||||
runner = std::thread{[this]() {
|
||||
while (running)
|
||||
{
|
||||
if (m_ctx.get())
|
||||
ub_wait(m_ctx.get());
|
||||
std::this_thread::sleep_for(25ms);
|
||||
}
|
||||
if (m_ctx.get())
|
||||
ub_process(m_ctx.get());
|
||||
}};
|
||||
#else
|
||||
if (auto loop = m_Loop.lock())
|
||||
{
|
||||
if (auto loop_ptr = loop->MaybeGetUVWLoop())
|
||||
{
|
||||
m_Poller = loop_ptr->resource<uvw::PollHandle>(ub_fd(m_ctx.get()));
|
||||
m_Poller->on<uvw::PollEvent>([ptr = std::weak_ptr<ub_ctx>{m_ctx}](auto&, auto&) {
|
||||
if (auto ctx = ptr.lock())
|
||||
ub_process(ctx.get());
|
||||
});
|
||||
m_Poller->start(uvw::PollHandle::Event::READABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error{"no uvw loop"};
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Down()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
running = false;
|
||||
runner.join();
|
||||
#else
|
||||
m_Poller->close();
|
||||
if (auto loop = m_Loop.lock())
|
||||
{
|
||||
if (auto loop_ptr = loop->MaybeGetUVWLoop())
|
||||
{
|
||||
m_Poller = loop_ptr->resource<uvw::PollHandle>(ub_fd(m_ctx.get()));
|
||||
m_Poller->on<uvw::PollEvent>([ptr = std::weak_ptr<ub_ctx>{m_ctx}](auto&, auto&) {
|
||||
if (auto ctx = ptr.lock())
|
||||
ub_process(ctx.get());
|
||||
});
|
||||
m_Poller->start(uvw::PollHandle::Event::READABLE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_ctx.reset();
|
||||
}
|
||||
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
void
|
||||
ResetInternalState() override
|
||||
{
|
||||
Down();
|
||||
Up(m_conf);
|
||||
}
|
||||
|
||||
void
|
||||
CancelPendingQueries() override
|
||||
{
|
||||
Down();
|
||||
}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const override
|
||||
{
|
||||
#if defined(ANDROID)
|
||||
(void)to;
|
||||
(void)from;
|
||||
return false;
|
||||
#else
|
||||
const auto& vec = m_conf.m_upstreamDNS;
|
||||
return std::find(vec.begin(), vec.end(), to) != std::end(vec)
|
||||
or std::find(vec.begin(), vec.end(), from) != std::end(vec);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
void
|
||||
call(Callable&& f)
|
||||
{
|
||||
if (auto loop = m_Loop.lock())
|
||||
loop->call(std::forward<Callable>(f));
|
||||
else
|
||||
LogError("no mainloop?");
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::weak_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override
|
||||
{
|
||||
if (WouldLoop(to, from))
|
||||
return false;
|
||||
// we use this unique ptr to clean up on fail
|
||||
auto tmp = std::make_unique<Query>(weak_from_this(), query, source, to, from);
|
||||
// no questions, send fail
|
||||
if (query.questions.empty())
|
||||
{
|
||||
tmp->Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& q : query.questions)
|
||||
{
|
||||
// dont process .loki or .snode
|
||||
if (q.HasTLD(".loki") or q.HasTLD(".snode"))
|
||||
{
|
||||
tmp->Cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// leak bare pointer and try to do the request
|
||||
auto* pending = tmp.release();
|
||||
const auto& q = query.questions[0];
|
||||
if (auto err = ub_resolve_async(
|
||||
m_ctx.get(),
|
||||
q.Name().c_str(),
|
||||
q.qtype,
|
||||
q.qclass,
|
||||
(void*)pending,
|
||||
&Resolver::Callback,
|
||||
nullptr))
|
||||
{
|
||||
// take back ownership on fail
|
||||
LogWarn("failed to send upstream query with libunbound: ", ub_strerror(err));
|
||||
tmp.reset(pending);
|
||||
tmp->Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
Query::SendReply(llarp::OwnedBuffer replyBuf) const
|
||||
{
|
||||
auto packet_src = src.lock();
|
||||
auto parent_ptr = parent.lock();
|
||||
|
||||
if (packet_src and parent_ptr)
|
||||
{
|
||||
parent_ptr->call([packet_src, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] {
|
||||
packet_src->SendTo(to, from, OwnedBuffer::copy_from(buf));
|
||||
});
|
||||
}
|
||||
else
|
||||
LogError("no source or parent");
|
||||
}
|
||||
} // namespace libunbound
|
||||
|
||||
Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf)
|
||||
: m_Loop{std::move(loop)}, m_Config{std::move(conf)}
|
||||
{}
|
||||
|
||||
Proxy::Proxy(EventLoop_ptr loop, IQueryHandler* h)
|
||||
: PacketHandler{loop, h}, m_Loop(std::move(loop))
|
||||
void
|
||||
Server::Start()
|
||||
{
|
||||
m_Server = m_Loop->make_udp(
|
||||
[this](UDPHandle&, SockAddr a, OwnedBuffer buf) { HandlePacket(a, a, buf); });
|
||||
// set up udp sockets
|
||||
for (const auto& addr : m_Config.m_bind)
|
||||
{
|
||||
if (auto ptr = MakePacketSourceOn(addr, m_Config))
|
||||
AddPacketSource(std::move(ptr));
|
||||
}
|
||||
|
||||
// add default resolver as needed
|
||||
if (auto ptr = MakeDefaultResolver())
|
||||
AddResolver(ptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<PacketSource_Base>
|
||||
Server::MakePacketSourceOn(const llarp::SockAddr& addr, const llarp::DnsConfig&)
|
||||
{
|
||||
return std::make_shared<UDPReader>(*this, m_Loop, addr);
|
||||
}
|
||||
|
||||
std::shared_ptr<Resolver_Base>
|
||||
Server::MakeDefaultResolver()
|
||||
{
|
||||
if (m_Config.m_upstreamDNS.empty())
|
||||
{
|
||||
LogInfo(
|
||||
"explicitly no upstream dns providers specified, we will not resolve anything but .loki "
|
||||
"and .snode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<libunbound::Resolver>(m_Loop, m_Config);
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
Server::BoundPacketSourceAddrs() const
|
||||
{
|
||||
std::vector<SockAddr> addrs;
|
||||
for (const auto& src : m_PacketSources)
|
||||
{
|
||||
if (auto ptr = src.lock())
|
||||
if (auto maybe_addr = ptr->BoundOn())
|
||||
addrs.emplace_back(*maybe_addr);
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
std::optional<SockAddr>
|
||||
Server::FirstBoundPacketSourceAddr() const
|
||||
{
|
||||
for (const auto& src : m_PacketSources)
|
||||
{
|
||||
if (auto ptr = src.lock())
|
||||
if (auto bound = ptr->BoundOn())
|
||||
return bound;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::Stop()
|
||||
Server::AddResolver(std::weak_ptr<Resolver_Base> resolver)
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
m_UnboundResolver->Stop();
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::Start(SockAddr addr, std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
{
|
||||
if (not PacketHandler::Start(addr, std::move(resolvers), std::move(hostfiles)))
|
||||
return false;
|
||||
return m_Server->listen(addr);
|
||||
m_Resolvers.insert(resolver);
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::Restart()
|
||||
Server::AddResolver(std::shared_ptr<Resolver_Base> resolver)
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
{
|
||||
LogInfo("reset libunbound's internal stuff");
|
||||
m_UnboundResolver->Init();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::Start(SockAddr, std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
{
|
||||
return SetupUnboundResolver(std::move(resolvers), std::move(hostfiles));
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::SetupUnboundResolver(
|
||||
std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
{
|
||||
// if we have no resolvers don't set up unbound
|
||||
if (resolvers.empty())
|
||||
return true;
|
||||
|
||||
auto failFunc = [self = weak_from_this()](
|
||||
const SockAddr& to, const SockAddr& from, Message msg) {
|
||||
if (auto this_ptr = self.lock())
|
||||
this_ptr->SendServerMessageBufferTo(to, from, msg.ToBuffer());
|
||||
};
|
||||
|
||||
auto replyFunc = [self = weak_from_this()](auto&&... args) {
|
||||
if (auto this_ptr = self.lock())
|
||||
this_ptr->SendServerMessageBufferTo(std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
|
||||
m_UnboundResolver =
|
||||
std::make_shared<UnboundResolver>(m_Loop, std::move(replyFunc), std::move(failFunc));
|
||||
m_Resolvers.clear();
|
||||
if (not m_UnboundResolver->Init())
|
||||
{
|
||||
llarp::LogError("Failed to initialize upstream DNS resolver.");
|
||||
m_UnboundResolver = nullptr;
|
||||
return false;
|
||||
}
|
||||
for (const auto& resolver : resolvers)
|
||||
{
|
||||
if (not m_UnboundResolver->AddUpstreamResolver(resolver))
|
||||
{
|
||||
llarp::LogError("Failed to add upstream DNS server: ", resolver);
|
||||
m_UnboundResolver = nullptr;
|
||||
return false;
|
||||
}
|
||||
m_Resolvers.emplace(resolver);
|
||||
}
|
||||
for (const auto& path : hostfiles)
|
||||
{
|
||||
m_UnboundResolver->AddHostsFile(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
m_OwnedResolvers.insert(resolver);
|
||||
AddResolver(std::weak_ptr<Resolver_Base>{resolver});
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::SendServerMessageBufferTo(
|
||||
const SockAddr& to, [[maybe_unused]] const SockAddr& from, llarp_buffer_t buf)
|
||||
Server::AddPacketSource(std::weak_ptr<PacketSource_Base> pkt)
|
||||
{
|
||||
if (!m_Server->send(to, buf))
|
||||
llarp::LogError("dns reply failed");
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::IsUpstreamResolver(const SockAddr& to, [[maybe_unused]] const SockAddr& from) const
|
||||
{
|
||||
return m_Resolvers.count(to);
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::ShouldHandlePacket(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const
|
||||
{
|
||||
MessageHeader hdr;
|
||||
if (not hdr.Decode(&buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg{hdr};
|
||||
if (not msg.Decode(&buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_QueryHandler and m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
return true;
|
||||
// If this request is going to an upstream resolver then we want to let it through (i.e. don't
|
||||
// handle it), and so want to return false. If we have something else then we want to
|
||||
// intercept it to route it through our caching libunbound server (which then redirects the
|
||||
// request to the lokinet-configured upstream, if not cached).
|
||||
return !IsUpstreamResolver(to, from);
|
||||
m_PacketSources.push_back(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf)
|
||||
Server::AddPacketSource(std::shared_ptr<PacketSource_Base> pkt)
|
||||
{
|
||||
MessageHeader hdr;
|
||||
if (not hdr.Decode(&buf))
|
||||
m_OwnedPacketSources.push_back(pkt);
|
||||
AddPacketSource(std::weak_ptr<PacketSource_Base>{pkt});
|
||||
}
|
||||
|
||||
void
|
||||
Server::Stop()
|
||||
{
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns header from ", from);
|
||||
return;
|
||||
if (auto ptr = resolver.lock())
|
||||
ptr->CancelPendingQueries();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Server::Reset()
|
||||
{
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
if (auto ptr = resolver.lock())
|
||||
ptr->ResetInternalState();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Server::MaybeHandlePacket(
|
||||
std::weak_ptr<PacketSource_Base> src,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from,
|
||||
llarp::OwnedBuffer buf)
|
||||
{
|
||||
auto ptr = src.lock();
|
||||
if (not ptr)
|
||||
return false;
|
||||
// dont process to prevent feedback loop
|
||||
if (ptr->WouldLoop(to, from))
|
||||
{
|
||||
LogWarn("preventing dns packet replay to=", to, " from=", from);
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg(hdr);
|
||||
if (not msg.Decode(&buf))
|
||||
auto maybe = MaybeParseDNSMessage(buf);
|
||||
if (not maybe)
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns message from ", from);
|
||||
return;
|
||||
LogWarn("invalid dns message format from ", from, " to dns listener on ", to);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& msg = *maybe;
|
||||
// we don't provide a DoH resolver because it requires verified TLS
|
||||
// TLS needs X509/ASN.1-DER and opting into the Root CA Cabal
|
||||
// thankfully mozilla added a backdoor that allows ISPs to turn it off
|
||||
// so we disable DoH for firefox using mozilla's ISP backdoor
|
||||
// see: https://github.com/loki-project/loki-network/issues/832
|
||||
// see: https://github.com/oxen-io/lokinet/issues/832
|
||||
for (const auto& q : msg.questions)
|
||||
{
|
||||
// is this firefox looking for their backdoor record?
|
||||
|
@ -163,32 +544,22 @@ namespace llarp::dns
|
|||
{
|
||||
// yea it is, let's turn off DoH because god is dead.
|
||||
msg.AddNXReply();
|
||||
// press F to pay respects
|
||||
SendServerMessageBufferTo(from, resolver, msg.ToBuffer());
|
||||
return;
|
||||
// press F to pay respects and send it back where it came from
|
||||
ptr->SendTo(from, to, msg.ToBuffer());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
auto reply = [self = shared_from_this(), to = from, resolver](dns::Message msg) {
|
||||
self->SendServerMessageBufferTo(to, resolver, msg.ToBuffer());
|
||||
};
|
||||
if (!m_QueryHandler->HandleHookedDNSMessage(std::move(msg), reply))
|
||||
if (auto res_ptr = resolver.lock())
|
||||
{
|
||||
llarp::LogWarn("failed to handle hooked dns");
|
||||
LogDebug("check resolver ", res_ptr->ResolverName(), " for dns from ", from, " to ", to);
|
||||
if (res_ptr->MaybeHookDNS(src, msg, to, from))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (not m_UnboundResolver)
|
||||
{
|
||||
// no upstream resolvers
|
||||
// let's serv fail it
|
||||
msg.AddServFail();
|
||||
SendServerMessageBufferTo(from, resolver, msg.ToBuffer());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_UnboundResolver->Lookup(resolver, from, std::move(msg));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,99 +1,226 @@
|
|||
#pragma once
|
||||
|
||||
#include "message.hpp"
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include "unbound_resolver.hpp"
|
||||
#include <llarp/util/fs.hpp>
|
||||
#include <set>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
/// a job handling 1 dns query
|
||||
class QueryJob_Base
|
||||
{
|
||||
/// handler of dns query hooking
|
||||
class IQueryHandler
|
||||
protected:
|
||||
/// the original dns query
|
||||
Message m_Query;
|
||||
|
||||
public:
|
||||
explicit QueryJob_Base(Message query) : m_Query{std::move(query)}
|
||||
{}
|
||||
|
||||
virtual ~QueryJob_Base() = default;
|
||||
|
||||
Message&
|
||||
Underlying()
|
||||
{
|
||||
public:
|
||||
virtual ~IQueryHandler() = default;
|
||||
return m_Query;
|
||||
}
|
||||
|
||||
/// return true if we should hook this message
|
||||
virtual bool
|
||||
ShouldHookDNSMessage(const Message& msg) const = 0;
|
||||
|
||||
/// handle a hooked message
|
||||
virtual bool
|
||||
HandleHookedDNSMessage(Message query, std::function<void(Message)> sendReply) = 0;
|
||||
};
|
||||
|
||||
// Base class for DNS lookups
|
||||
class PacketHandler : public std::enable_shared_from_this<PacketHandler>
|
||||
const Message&
|
||||
Underlying() const
|
||||
{
|
||||
public:
|
||||
explicit PacketHandler(EventLoop_ptr loop, IQueryHandler* handler);
|
||||
return m_Query;
|
||||
}
|
||||
|
||||
virtual ~PacketHandler() = default;
|
||||
/// cancel this operation and inform anyone who cares
|
||||
void
|
||||
Cancel() const;
|
||||
|
||||
virtual bool
|
||||
Start(
|
||||
SockAddr localaddr,
|
||||
std::vector<SockAddr> upstreamResolvers,
|
||||
std::vector<fs::path> hostfiles);
|
||||
/// send a raw buffer back to the querier
|
||||
virtual void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const = 0;
|
||||
};
|
||||
|
||||
void
|
||||
Stop();
|
||||
class PacketSource_Base
|
||||
{
|
||||
public:
|
||||
virtual ~PacketSource_Base() = default;
|
||||
|
||||
void
|
||||
Restart();
|
||||
/// return true if traffic with source and dest addresses would cause a
|
||||
/// loop in resolution and thus should not be sent to query handlers
|
||||
virtual bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const = 0;
|
||||
|
||||
void
|
||||
HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf);
|
||||
/// send packet with src and dst address containing buf on this packet source
|
||||
virtual void
|
||||
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const = 0;
|
||||
|
||||
bool
|
||||
ShouldHandlePacket(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const;
|
||||
/// stop reading packets and end operation
|
||||
virtual void
|
||||
Stop() = 0;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
SendServerMessageBufferTo(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) = 0;
|
||||
/// returns the sockaddr we are bound on if applicable
|
||||
virtual std::optional<SockAddr>
|
||||
BoundOn() const = 0;
|
||||
};
|
||||
|
||||
// Returns true if this packet is something that looks like it's going to an upstream
|
||||
// resolver, i.e. matches a configured resolver.
|
||||
virtual bool
|
||||
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const;
|
||||
/// non complex implementation of QueryJob_Base for use in things that
|
||||
/// only ever called on the mainloop thread
|
||||
class QueryJob : public QueryJob_Base, std::enable_shared_from_this<QueryJob>
|
||||
{
|
||||
std::weak_ptr<PacketSource_Base> src;
|
||||
const SockAddr resolver;
|
||||
const SockAddr asker;
|
||||
|
||||
private:
|
||||
void
|
||||
HandleUpstreamFailure(const SockAddr& from, const SockAddr& to, Message msg);
|
||||
public:
|
||||
explicit QueryJob(
|
||||
std::weak_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to_,
|
||||
const SockAddr& from_)
|
||||
: QueryJob_Base{query}, src{source}, resolver{to_}, asker{from_}
|
||||
{}
|
||||
|
||||
bool
|
||||
SetupUnboundResolver(std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles);
|
||||
|
||||
IQueryHandler* const m_QueryHandler;
|
||||
std::set<SockAddr> m_Resolvers;
|
||||
std::shared_ptr<UnboundResolver> m_UnboundResolver;
|
||||
EventLoop_ptr m_Loop;
|
||||
};
|
||||
|
||||
// Proxying DNS handler that listens on a UDP port for proper DNS requests.
|
||||
class Proxy : public PacketHandler
|
||||
void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const override
|
||||
{
|
||||
public:
|
||||
explicit Proxy(EventLoop_ptr loop, IQueryHandler* handler);
|
||||
if (auto ptr = src.lock())
|
||||
ptr->SendTo(asker, resolver, std::move(replyBuf));
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
Start(
|
||||
SockAddr localaddr,
|
||||
std::vector<SockAddr> upstreamResolvers,
|
||||
std::vector<fs::path> hostfiles) override;
|
||||
/// handler of dns query hooking
|
||||
/// intercepts dns for internal processing
|
||||
class Resolver_Base
|
||||
{
|
||||
protected:
|
||||
/// return the sorting order for this resolver
|
||||
/// lower means it will be tried first
|
||||
virtual int
|
||||
Rank() const = 0;
|
||||
|
||||
protected:
|
||||
void
|
||||
SendServerMessageBufferTo(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) override;
|
||||
public:
|
||||
virtual ~Resolver_Base() = default;
|
||||
|
||||
private:
|
||||
std::shared_ptr<UDPHandle> m_Server;
|
||||
EventLoop_ptr m_Loop;
|
||||
/// less than via rank
|
||||
bool
|
||||
operator<(const Resolver_Base& other) const
|
||||
{
|
||||
return Rank() < other.Rank();
|
||||
}
|
||||
|
||||
/// greater than via rank
|
||||
bool
|
||||
operator>(const Resolver_Base& other) const
|
||||
{
|
||||
return Rank() > other.Rank();
|
||||
}
|
||||
|
||||
/// get printable name
|
||||
virtual std::string_view
|
||||
ResolverName() const = 0;
|
||||
|
||||
/// reset state
|
||||
virtual void
|
||||
ResetInternalState(){};
|
||||
|
||||
/// cancel all pending requests and ceace further operation
|
||||
virtual void
|
||||
CancelPendingQueries(){};
|
||||
/// attempt to handle a dns message
|
||||
/// returns true if we consumed this query and it should not be processed again
|
||||
virtual bool
|
||||
MaybeHookDNS(
|
||||
std::weak_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) = 0;
|
||||
|
||||
/// Returns true if a packet with to and from addresses is something that would cause a
|
||||
/// resolution loop and thus should not be used on this resolver
|
||||
virtual bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const
|
||||
{
|
||||
(void)to;
|
||||
(void)from;
|
||||
return false;
|
||||
};
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
};
|
||||
|
||||
// Base class for DNS proxy
|
||||
class Server : public std::enable_shared_from_this<Server>
|
||||
{
|
||||
protected:
|
||||
/// add a packet source to this server, does share ownership
|
||||
void
|
||||
AddPacketSource(std::shared_ptr<PacketSource_Base> resolver);
|
||||
/// add a resolver to this packet handler, does share ownership
|
||||
void
|
||||
AddResolver(std::shared_ptr<Resolver_Base> resolver);
|
||||
|
||||
public:
|
||||
virtual ~Server() = default;
|
||||
explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf);
|
||||
|
||||
/// returns all sockaddr we have from all of our PacketSources
|
||||
std::vector<SockAddr>
|
||||
BoundPacketSourceAddrs() const;
|
||||
|
||||
/// returns the first sockaddr we have on our packet sources if we have one
|
||||
std::optional<SockAddr>
|
||||
FirstBoundPacketSourceAddr() const;
|
||||
|
||||
/// add a resolver to this packet handler, does not share ownership
|
||||
void
|
||||
AddResolver(std::weak_ptr<Resolver_Base> resolver);
|
||||
|
||||
/// add a packet source to this server, does not share ownership
|
||||
void
|
||||
AddPacketSource(std::weak_ptr<PacketSource_Base> resolver);
|
||||
|
||||
/// create a packet source bound on bindaddr but does not add it
|
||||
virtual std::shared_ptr<PacketSource_Base>
|
||||
MakePacketSourceOn(const SockAddr& bindaddr, const llarp::DnsConfig& conf);
|
||||
|
||||
/// sets up all internal binds and such and begins operation
|
||||
virtual void
|
||||
Start();
|
||||
|
||||
/// stops all operation
|
||||
virtual void
|
||||
Stop();
|
||||
|
||||
/// reset the internal state
|
||||
virtual void
|
||||
Reset();
|
||||
|
||||
/// create the default resolver for out config
|
||||
virtual std::shared_ptr<Resolver_Base>
|
||||
MakeDefaultResolver();
|
||||
|
||||
/// feed a packet buffer from a packet source
|
||||
/// returns true if we decided to process the packet and consumed it
|
||||
/// returns false if we dont want to process the packet
|
||||
bool
|
||||
MaybeHandlePacket(
|
||||
std::weak_ptr<PacketSource_Base> pktsource,
|
||||
const SockAddr& resolver,
|
||||
const SockAddr& from,
|
||||
llarp::OwnedBuffer buf);
|
||||
|
||||
protected:
|
||||
EventLoop_ptr m_Loop;
|
||||
llarp::DnsConfig m_Config;
|
||||
|
||||
private:
|
||||
std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>>
|
||||
m_OwnedResolvers;
|
||||
std::set<std::weak_ptr<Resolver_Base>, CompareWeakPtr<Resolver_Base>> m_Resolvers;
|
||||
|
||||
std::vector<std::weak_ptr<PacketSource_Base>> m_PacketSources;
|
||||
std::vector<std::shared_ptr<PacketSource_Base>> m_OwnedPacketSources;
|
||||
};
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -23,6 +23,11 @@ namespace llarp
|
|||
class TunnelManager;
|
||||
}
|
||||
|
||||
namespace dns
|
||||
{
|
||||
class Server;
|
||||
}
|
||||
|
||||
class EndpointBase
|
||||
{
|
||||
std::unordered_set<dns::SRVData> m_SRVRecords;
|
||||
|
@ -72,6 +77,13 @@ namespace llarp
|
|||
void
|
||||
PutSRVRecord(dns::SRVData srv);
|
||||
|
||||
/// get dns serverr if we have on on this endpoint
|
||||
virtual std::shared_ptr<dns::Server>
|
||||
DNS() const
|
||||
{
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
/// called when srv data changes in some way
|
||||
virtual void
|
||||
SRVRecordsChanged() = 0;
|
||||
|
|
|
@ -72,6 +72,13 @@ namespace llarp::uv
|
|||
bool
|
||||
send(const SockAddr& dest, const llarp_buffer_t& buf) override;
|
||||
|
||||
std::optional<SockAddr>
|
||||
LocalAddr() const override
|
||||
{
|
||||
auto addr = handle->sock<uvw::IPv4>();
|
||||
return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
file_descriptor() override
|
||||
{
|
||||
|
|
|
@ -33,6 +33,10 @@ namespace llarp
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// returns the local address we are bound on
|
||||
virtual std::optional<SockAddr>
|
||||
LocalAddr() const = 0;
|
||||
|
||||
// Base class destructor
|
||||
virtual ~UDPHandle() = default;
|
||||
|
||||
|
|
|
@ -18,11 +18,7 @@ namespace llarp
|
|||
namespace handlers
|
||||
{
|
||||
ExitEndpoint::ExitEndpoint(std::string name, AbstractRouter* r)
|
||||
: m_Router(r)
|
||||
, m_Resolver(std::make_shared<dns::Proxy>(r->loop(), this))
|
||||
, m_Name(std::move(name))
|
||||
, m_LocalResolverAddr{"127.0.0.1:53"}
|
||||
, m_QUIC{std::make_shared<quic::TunnelManager>(*this)}
|
||||
: m_Router(r), m_Name(std::move(name)), m_QUIC{std::make_shared<quic::TunnelManager>(*this)}
|
||||
{
|
||||
m_ShouldInitTun = true;
|
||||
m_QUIC = std::make_shared<quic::TunnelManager>(*this);
|
||||
|
@ -211,6 +207,22 @@ namespace llarp
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ExitEndpoint::MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
{
|
||||
if (not ShouldHookDNSMessage(query))
|
||||
return false;
|
||||
|
||||
auto job = std::make_shared<dns::QueryJob>(source, query, to, from);
|
||||
if (not HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); }))
|
||||
job->Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ExitEndpoint::HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)> reply)
|
||||
{
|
||||
|
@ -459,9 +471,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
GetRouter()->loop()->add_ticker([this] { Flush(); });
|
||||
|
||||
llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr);
|
||||
return m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers, {});
|
||||
m_Resolver->Start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -703,8 +713,7 @@ namespace llarp
|
|||
m_ShouldInitTun = false;
|
||||
}
|
||||
|
||||
m_LocalResolverAddr = dnsConfig.m_bind;
|
||||
m_UpstreamResolvers = dnsConfig.m_upstreamDNS;
|
||||
m_Resolver = std::make_shared<dns::Server>(m_Router->loop(), dnsConfig);
|
||||
|
||||
m_OurRange = networkConfig.m_ifaddr;
|
||||
if (!m_OurRange.addr.h)
|
||||
|
|
|
@ -10,8 +10,33 @@ namespace llarp
|
|||
struct AbstractRouter;
|
||||
namespace handlers
|
||||
{
|
||||
struct ExitEndpoint : public dns::IQueryHandler, public EndpointBase
|
||||
struct ExitEndpoint : public dns::Resolver_Base, public EndpointBase
|
||||
{
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "snode";
|
||||
}
|
||||
|
||||
void
|
||||
ResetInternalState() override{};
|
||||
|
||||
void
|
||||
CancelPendingQueries() override{};
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
||||
ExitEndpoint(std::string name, AbstractRouter* r);
|
||||
~ExitEndpoint() override;
|
||||
|
||||
|
@ -66,10 +91,10 @@ namespace llarp
|
|||
SupportsV6() const;
|
||||
|
||||
bool
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const override;
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const;
|
||||
|
||||
bool
|
||||
HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)>) override;
|
||||
HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)>);
|
||||
|
||||
void
|
||||
LookupServiceAsync(
|
||||
|
@ -174,7 +199,7 @@ namespace llarp
|
|||
KickIdentOffExit(const PubKey& pk);
|
||||
|
||||
AbstractRouter* m_Router;
|
||||
std::shared_ptr<dns::Proxy> m_Resolver;
|
||||
std::shared_ptr<dns::Server> m_Resolver;
|
||||
bool m_ShouldInitTun;
|
||||
std::string m_Name;
|
||||
bool m_PermitExit;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/router/systemd_resolved.hpp>
|
||||
#include <llarp/router/route_poker.hpp>
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/service/outbound_context.hpp>
|
||||
#include <llarp/service/endpoint_state.hpp>
|
||||
|
@ -27,26 +27,46 @@
|
|||
#include <llarp/dns/srv_data.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include <oxenc/bt.h>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
bool
|
||||
TunEndpoint::MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
{
|
||||
if (not ShouldHookDNSMessage(query))
|
||||
return false;
|
||||
|
||||
auto job = std::make_shared<dns::QueryJob>(source, query, to, from);
|
||||
if (not HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); }))
|
||||
job->Cancel();
|
||||
return true;
|
||||
}
|
||||
// Intercepts DNS IP packets going to an IP on the tun interface; this is currently used on
|
||||
// Android and macOS where binding to a DNS port (i.e. via llarp::dns::Proxy) isn't possible
|
||||
// Android and macOS where binding to a low port isn't possible
|
||||
// because of OS restrictions, but a tun interface *is* available.
|
||||
class DnsInterceptor : public dns::PacketHandler
|
||||
class DnsInterceptor : public dns::PacketSource_Base
|
||||
{
|
||||
public:
|
||||
TunEndpoint* const m_Endpoint;
|
||||
llarp::DnsConfig m_Config;
|
||||
|
||||
explicit DnsInterceptor(AbstractRouter* router, TunEndpoint* ep)
|
||||
: dns::PacketHandler{router->loop(), ep}, m_Endpoint{ep} {};
|
||||
explicit DnsInterceptor(TunEndpoint* ep, llarp::DnsConfig conf)
|
||||
: m_Endpoint{ep}, m_Config{conf}
|
||||
{}
|
||||
|
||||
virtual ~DnsInterceptor() = default;
|
||||
|
||||
void
|
||||
SendServerMessageBufferTo(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) override
|
||||
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
|
||||
{
|
||||
const auto pkt = net::IPPacket::UDP(
|
||||
from.getIPv4(),
|
||||
|
@ -61,11 +81,20 @@ namespace llarp
|
|||
pkt.ConstBuffer(), net::ExpandV4(from.asIPv4()), net::ExpandV4(to.asIPv4()), 0);
|
||||
}
|
||||
|
||||
void
|
||||
Stop() override{};
|
||||
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
bool
|
||||
IsUpstreamResolver(const SockAddr&, const SockAddr&) const override
|
||||
WouldLoop(const SockAddr&, const SockAddr&) const override
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -77,21 +106,57 @@ namespace llarp
|
|||
// IP for DNS, so we consider anything else to be upstream-bound DNS to let it through the
|
||||
// tunnel.
|
||||
bool
|
||||
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const override
|
||||
WouldLoop(const SockAddr& to, const SockAddr&) const override
|
||||
{
|
||||
return to.asIPv6() != m_Endpoint->GetIfAddr();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(ANDROID) || defined(__APPLE__)
|
||||
class TunDNS : public dns::Server
|
||||
{
|
||||
TunEndpoint* const m_Endpoint;
|
||||
|
||||
public:
|
||||
std::weak_ptr<dns::PacketSource_Base> PacketSource;
|
||||
|
||||
virtual ~TunDNS() = default;
|
||||
explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf)
|
||||
: dns::Server{ep->Router()->loop(), conf}, m_Endpoint{ep}
|
||||
{}
|
||||
|
||||
std::shared_ptr<dns::PacketSource_Base>
|
||||
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override
|
||||
{
|
||||
auto ptr = std::make_shared<DnsInterceptor>(m_Endpoint, conf);
|
||||
PacketSource = ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<dns::Resolver_Base>
|
||||
MakeDefaultResolver() override
|
||||
{
|
||||
// android will not cache dns via unbound it only intercepts .loki
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
|
||||
: service::Endpoint(r, parent)
|
||||
{
|
||||
m_PacketRouter = std::make_unique<vpn::PacketRouter>(
|
||||
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
|
||||
#if defined(ANDROID) || (defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION))
|
||||
m_Resolver = std::make_shared<DnsInterceptor>(r, this);
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [&](net::IPPacket pkt) {
|
||||
}
|
||||
|
||||
void
|
||||
TunEndpoint::SetupDNS()
|
||||
{
|
||||
#if defined(ANDROID) || defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION))
|
||||
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
|
||||
m_DNS = dns;
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) {
|
||||
const size_t ip_header_size = (pkt.Header()->ihl * 4);
|
||||
|
||||
const uint8_t* ptr = pkt.buf + ip_header_size;
|
||||
|
@ -102,14 +167,17 @@ namespace llarp
|
|||
|
||||
OwnedBuffer buf{pkt.sz - (8 + ip_header_size)};
|
||||
std::copy_n(ptr + 8, buf.sz, buf.buf.get());
|
||||
if (m_Resolver->ShouldHandlePacket(raddr, laddr, buf))
|
||||
m_Resolver->HandlePacket(raddr, laddr, buf);
|
||||
else
|
||||
HandleGotUserPacket(std::move(pkt));
|
||||
|
||||
if (dns->MaybeHandlePacket(dns->PacketSource, raddr, laddr, std::move(buf)))
|
||||
return;
|
||||
|
||||
HandleGotUserPacket(std::move(pkt));
|
||||
});
|
||||
#else
|
||||
m_Resolver = std::make_shared<dns::Proxy>(r->loop(), this);
|
||||
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig);
|
||||
#endif
|
||||
m_DNS->AddResolver(weak_from_this());
|
||||
m_DNS->Start();
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
|
@ -118,11 +186,21 @@ namespace llarp
|
|||
auto obj = service::Endpoint::ExtractStatus();
|
||||
obj["ifaddr"] = m_OurRange.ToString();
|
||||
obj["ifname"] = m_IfName;
|
||||
std::vector<std::string> resolvers;
|
||||
for (const auto& addr : m_UpstreamResolvers)
|
||||
resolvers.emplace_back(addr.ToString());
|
||||
obj["ustreamResolvers"] = resolvers;
|
||||
obj["localResolver"] = m_LocalResolverAddr.ToString();
|
||||
|
||||
std::vector<std::string> upstreamRes;
|
||||
for (const auto& ent : m_DnsConfig.m_upstreamDNS)
|
||||
upstreamRes.emplace_back(ent.ToString());
|
||||
obj["ustreamResolvers"] = upstreamRes;
|
||||
|
||||
std::vector<std::string> localRes;
|
||||
for (const auto& ent : m_DnsConfig.m_bind)
|
||||
localRes.emplace_back(ent.ToString());
|
||||
obj["localResolvers"] = localRes;
|
||||
|
||||
// for backwards compat
|
||||
if (not m_DnsConfig.m_bind.empty())
|
||||
obj["localResolver"] = localRes[0];
|
||||
|
||||
util::StatusObject ips{};
|
||||
for (const auto& item : m_IPActivity)
|
||||
{
|
||||
|
@ -147,18 +225,14 @@ namespace llarp
|
|||
void
|
||||
TunEndpoint::Thaw()
|
||||
{
|
||||
if (m_Resolver)
|
||||
m_Resolver->Restart();
|
||||
if (m_DNS)
|
||||
m_DNS->Reset();
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
TunEndpoint::ReconfigureDNS(std::vector<SockAddr> servers)
|
||||
{
|
||||
std::swap(m_UpstreamResolvers, servers);
|
||||
m_Resolver->Stop();
|
||||
if (!m_Resolver->Start(
|
||||
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
|
||||
llarp::LogError(Name(), " failed to reconfigure DNS server");
|
||||
// TODO: implement me
|
||||
return servers;
|
||||
}
|
||||
|
||||
|
@ -199,13 +273,10 @@ namespace llarp
|
|||
m_AuthPolicy = std::move(auth);
|
||||
}
|
||||
|
||||
m_DnsConfig = dnsConf;
|
||||
m_TrafficPolicy = conf.m_TrafficPolicy;
|
||||
m_OwnedRanges = conf.m_OwnedRanges;
|
||||
|
||||
m_LocalResolverAddr = dnsConf.m_bind;
|
||||
m_UpstreamResolvers = dnsConf.m_upstreamDNS;
|
||||
m_hostfiles = dnsConf.m_hostfiles;
|
||||
|
||||
m_BaseV6Address = conf.m_baseV6Address;
|
||||
|
||||
if (conf.m_PathAlignmentTimeout)
|
||||
|
@ -354,7 +425,6 @@ namespace llarp
|
|||
return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}};
|
||||
});
|
||||
}
|
||||
|
||||
return Endpoint::Configure(conf, dnsConf);
|
||||
}
|
||||
|
||||
|
@ -862,11 +932,8 @@ namespace llarp
|
|||
bool
|
||||
TunEndpoint::Start()
|
||||
{
|
||||
if (!Endpoint::Start())
|
||||
{
|
||||
llarp::LogWarn("Couldn't start endpoint");
|
||||
if (not Endpoint::Start())
|
||||
return false;
|
||||
}
|
||||
return SetupNetworking();
|
||||
}
|
||||
|
||||
|
@ -904,7 +971,12 @@ namespace llarp
|
|||
}
|
||||
|
||||
info.ifname = m_IfName;
|
||||
info.dnsaddr.FromString(m_LocalResolverAddr.toHost());
|
||||
|
||||
LogInfo(Name(), " setting up dns...");
|
||||
SetupDNS();
|
||||
|
||||
if (auto maybe_addr = m_DNS->FirstBoundPacketSourceAddr())
|
||||
info.dnsaddr = maybe_addr->asIPv4();
|
||||
|
||||
LogInfo(Name(), " setting up network...");
|
||||
|
||||
|
@ -931,23 +1003,20 @@ namespace llarp
|
|||
LogError(Name(), " failed to add network interface");
|
||||
return false;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
|
||||
m_OurIPv6 = llarp::huint128_t{
|
||||
llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(m_OurRange.addr).h}};
|
||||
#else
|
||||
const auto maybe = m_router->Net().GetInterfaceIPv6Address(m_IfName);
|
||||
if (maybe.has_value())
|
||||
{
|
||||
m_OurIPv6 = *maybe;
|
||||
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Attempt to register DNS on the interface
|
||||
systemd_resolved_set_dns(
|
||||
m_IfName,
|
||||
m_LocalResolverAddr.createSockAddr(),
|
||||
false /* just .loki/.snode DNS initially */);
|
||||
if constexpr (not llarp::platform::is_apple)
|
||||
{
|
||||
if (auto maybe = m_router->Net().GetInterfaceIPv6Address(m_IfName))
|
||||
{
|
||||
m_OurIPv6 = *maybe;
|
||||
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
|
||||
}
|
||||
}
|
||||
|
||||
m_router->routePoker().SetDNSMode(false);
|
||||
|
||||
return HasAddress(ourAddr);
|
||||
}
|
||||
|
@ -970,18 +1039,7 @@ namespace llarp
|
|||
TunEndpoint::SetupNetworking()
|
||||
{
|
||||
llarp::LogInfo("Set Up networking for ", Name());
|
||||
if (!SetupTun())
|
||||
{
|
||||
llarp::LogError(Name(), " failed to set up network interface");
|
||||
return false;
|
||||
}
|
||||
if (!m_Resolver->Start(
|
||||
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
|
||||
{
|
||||
llarp::LogError(Name(), " failed to start DNS server");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return SetupTun();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1016,8 +1074,8 @@ namespace llarp
|
|||
}
|
||||
}
|
||||
#endif
|
||||
if (m_Resolver)
|
||||
m_Resolver->Stop();
|
||||
if (m_DNS)
|
||||
m_DNS->Stop();
|
||||
return llarp::service::Endpoint::Stop();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,12 +23,31 @@ namespace llarp
|
|||
namespace handlers
|
||||
{
|
||||
struct TunEndpoint : public service::Endpoint,
|
||||
public dns::IQueryHandler,
|
||||
public dns::Resolver_Base,
|
||||
public std::enable_shared_from_this<TunEndpoint>
|
||||
{
|
||||
TunEndpoint(AbstractRouter* r, llarp::service::Context* parent);
|
||||
~TunEndpoint() override;
|
||||
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "lokinet";
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
||||
path::PathSet_ptr
|
||||
GetSelf() override
|
||||
{
|
||||
|
@ -71,11 +90,10 @@ namespace llarp
|
|||
SupportsV6() const override;
|
||||
|
||||
bool
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const override;
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const;
|
||||
|
||||
bool
|
||||
HandleHookedDNSMessage(
|
||||
dns::Message query, std::function<void(dns::Message)> sendreply) override;
|
||||
HandleHookedDNSMessage(dns::Message query, std::function<void(dns::Message)> sendreply);
|
||||
|
||||
void
|
||||
TickTun(llarp_time_t now);
|
||||
|
@ -96,6 +114,16 @@ namespace llarp
|
|||
bool
|
||||
SetupTun();
|
||||
|
||||
void
|
||||
SetupDNS();
|
||||
|
||||
/// overrides Endpoint
|
||||
std::shared_ptr<dns::Server>
|
||||
DNS() const override
|
||||
{
|
||||
return m_DNS;
|
||||
};
|
||||
|
||||
/// overrides Endpoint
|
||||
bool
|
||||
SetupNetworking() override;
|
||||
|
@ -249,8 +277,11 @@ namespace llarp
|
|||
query->AddNXReply();
|
||||
reply(*query);
|
||||
}
|
||||
/// our dns resolver
|
||||
std::shared_ptr<dns::PacketHandler> m_Resolver;
|
||||
|
||||
/// dns subsystem for this endpoint
|
||||
std::shared_ptr<dns::Server> m_DNS;
|
||||
|
||||
DnsConfig m_DnsConfig;
|
||||
|
||||
/// maps ip address to timestamp last active
|
||||
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
|
||||
|
@ -265,12 +296,6 @@ namespace llarp
|
|||
huint128_t m_MaxIP;
|
||||
/// our ip range we are using
|
||||
llarp::IPRange m_OurRange;
|
||||
/// upstream dns resolver list
|
||||
std::vector<SockAddr> m_UpstreamResolvers;
|
||||
/// dns host files list
|
||||
std::vector<fs::path> m_hostfiles;
|
||||
/// local dns
|
||||
IpAddress m_LocalResolverAddr;
|
||||
/// list of strict connect addresses for hooks
|
||||
std::vector<IpAddress> m_StrictConnectAddrs;
|
||||
/// use v6?
|
||||
|
|
|
@ -228,7 +228,7 @@ struct lokinet_context
|
|||
[[nodiscard]] std::optional<int>
|
||||
make_udp_handler(
|
||||
const std::shared_ptr<llarp::service::Endpoint>& ep,
|
||||
llarp::huint16_t exposePort,
|
||||
llarp::net::port_t exposePort,
|
||||
lokinet_udp_flow_filter filter,
|
||||
lokinet_udp_flow_recv_func recv,
|
||||
lokinet_udp_flow_timeout_func timeout,
|
||||
|
@ -245,16 +245,16 @@ struct lokinet_context
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::weak_ptr<llarp::service::Endpoint> weak{ep};
|
||||
auto udp = std::make_shared<UDPHandler>(
|
||||
next_socket_id(), llarp::ToNet(exposePort), filter, recv, timeout, user, std::weak_ptr{ep});
|
||||
next_socket_id(), exposePort, filter, recv, timeout, user, weak);
|
||||
auto id = udp->m_SocketID;
|
||||
std::promise<bool> result;
|
||||
|
||||
impl->router->loop()->call([ep, &result, udp, exposePort]() {
|
||||
if (auto pkt = ep->EgresPacketRouter())
|
||||
{
|
||||
pkt->AddUDPHandler(exposePort, [udp](auto from, auto pkt) {
|
||||
pkt->AddUDPHandler(llarp::net::ToHost(exposePort), [udp](auto from, auto pkt) {
|
||||
udp->HandlePacketFrom(std::move(from), std::move(pkt));
|
||||
});
|
||||
result.set_value(true);
|
||||
|
@ -903,8 +903,8 @@ extern "C"
|
|||
auto lock = ctx->acquire();
|
||||
if (auto ep = ctx->endpoint())
|
||||
{
|
||||
if (auto maybe =
|
||||
ctx->make_udp_handler(ep, llarp::huint16_t{exposedPort}, filter, recv, timeout, user))
|
||||
if (auto maybe = ctx->make_udp_handler(
|
||||
ep, llarp::net::port_t::from_host(exposedPort), filter, recv, timeout, user))
|
||||
{
|
||||
result->socket_id = *maybe;
|
||||
return 0;
|
||||
|
@ -934,7 +934,7 @@ extern "C"
|
|||
return EINVAL;
|
||||
std::shared_ptr<llarp::EndpointBase> ep;
|
||||
llarp::nuint16_t srcport{0};
|
||||
llarp::nuint16_t dstport{llarp::ToNet(llarp::huint16_t{remote->remote_port})};
|
||||
auto dstport = llarp::net::port_t::from_host(remote->remote_port);
|
||||
{
|
||||
auto lock = ctx->acquire();
|
||||
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||
|
|
|
@ -16,6 +16,12 @@ namespace llarp
|
|||
{
|
||||
return memcmp(&lh, &rh, sizeof(in6_addr)) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(const in6_addr& lh, const in6_addr& rh)
|
||||
{
|
||||
return memcmp(&lh, &rh, sizeof(in6_addr)) < 0;
|
||||
}
|
||||
/// shared utility functions
|
||||
///
|
||||
|
||||
|
@ -112,7 +118,8 @@ namespace llarp
|
|||
else if (other.sa_family == AF_INET)
|
||||
*this = reinterpret_cast<const sockaddr_in&>(other);
|
||||
else
|
||||
throw std::invalid_argument("Invalid sockaddr (not AF_INET or AF_INET6)");
|
||||
throw std::invalid_argument{
|
||||
fmt::format("Invalid sockaddr (not AF_INET or AF_INET6) was {}", other.sa_family)};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
@ -209,11 +216,8 @@ namespace llarp
|
|||
bool
|
||||
SockAddr::operator<(const SockAddr& other) const
|
||||
{
|
||||
return memcmp(
|
||||
m_addr.sin6_addr.s6_addr,
|
||||
other.m_addr.sin6_addr.s6_addr,
|
||||
sizeof(m_addr.sin6_addr.s6_addr))
|
||||
< 0;
|
||||
return (m_addr.sin6_addr < other.m_addr.sin6_addr)
|
||||
or (m_addr.sin6_port < other.m_addr.sin6_port);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -74,6 +74,12 @@ namespace llarp
|
|||
bool
|
||||
operator==(const SockAddr& other) const;
|
||||
|
||||
bool
|
||||
operator!=(const SockAddr& other) const
|
||||
{
|
||||
return not(*this == other);
|
||||
};
|
||||
|
||||
void
|
||||
fromString(std::string_view str, bool allow_port = true);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "abstractrouter.hpp"
|
||||
#include "net/sock_addr.hpp"
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/dns/resolver.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace llarp
|
||||
|
@ -159,6 +160,27 @@ namespace llarp
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
RoutePoker::SetDNSMode(bool exit_mode_on) const
|
||||
{
|
||||
if (auto dns = m_Router->hiddenServiceContext().GetDefault()->DNS())
|
||||
{
|
||||
if (auto maybe_addr = dns->FirstBoundPacketSourceAddr())
|
||||
{
|
||||
if (dns::set_resolver(
|
||||
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
|
||||
*maybe_addr,
|
||||
exit_mode_on))
|
||||
{
|
||||
LogInfo(
|
||||
"DNS set to ",
|
||||
*maybe_addr,
|
||||
exit_mode_on ? " for all traffic" : " for just lokinet traffic");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RoutePoker::Enable()
|
||||
{
|
||||
|
@ -173,10 +195,7 @@ namespace llarp
|
|||
m_Enabled = true;
|
||||
}
|
||||
|
||||
systemd_resolved_set_dns(
|
||||
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
|
||||
m_Router->GetConfig()->dns.m_bind,
|
||||
true /* route all DNS */);
|
||||
SetDNSMode(true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -188,10 +207,7 @@ namespace llarp
|
|||
DisableAllRoutes();
|
||||
m_Enabled = false;
|
||||
|
||||
systemd_resolved_set_dns(
|
||||
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
|
||||
m_Router->GetConfig()->dns.m_bind,
|
||||
false /* route DNS only for .loki/.snode */);
|
||||
SetDNSMode(false);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <memory>
|
||||
#include <optional>
|
||||
#include <llarp/net/net_int.hpp>
|
||||
#include "systemd_resolved.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -45,6 +44,11 @@ namespace llarp
|
|||
void
|
||||
Down();
|
||||
|
||||
/// set dns resolver
|
||||
/// pass in if we are using exit node mode right now as a bool
|
||||
void
|
||||
SetDNSMode(bool using_exit_mode) const;
|
||||
|
||||
private:
|
||||
void
|
||||
DeleteAllRoutes();
|
||||
|
|
|
@ -1217,12 +1217,6 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
Router::OutboundUDPSocket() const
|
||||
{
|
||||
return m_OutboundUDPSocket;
|
||||
}
|
||||
|
||||
bool
|
||||
Router::Run()
|
||||
{
|
||||
|
|
|
@ -211,9 +211,6 @@ namespace llarp
|
|||
bool
|
||||
ShouldTestOtherRouters() const;
|
||||
|
||||
int
|
||||
OutboundUDPSocket() const override;
|
||||
|
||||
std::optional<SockAddr> _ourAddress;
|
||||
|
||||
EventLoop_ptr _loop;
|
||||
|
|
|
@ -53,6 +53,44 @@ namespace llarp::rpc
|
|||
return obj.dump();
|
||||
}
|
||||
|
||||
/// fake packet source that serializes repsonses back into dns
|
||||
|
||||
class DummyPacketSource : public dns::PacketSource_Base
|
||||
{
|
||||
std::function<void(std::optional<dns::Message>)> func;
|
||||
|
||||
public:
|
||||
SockAddr dumb;
|
||||
|
||||
template <typename Callable>
|
||||
DummyPacketSource(Callable&& f) : func{std::forward<Callable>(f)}
|
||||
{}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr&, const SockAddr&) const override
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/// send packet with src and dst address containing buf on this packet source
|
||||
void
|
||||
SendTo(const SockAddr&, const SockAddr&, OwnedBuffer buf) const override
|
||||
{
|
||||
func(dns::MaybeParseDNSMessage(buf));
|
||||
}
|
||||
|
||||
/// stop reading packets and end operation
|
||||
void
|
||||
Stop() override{};
|
||||
|
||||
/// returns the sockaddr we are bound on if applicable
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/// a function that replies to an rpc request
|
||||
using ReplyFunction_t = std::function<void(std::string)>;
|
||||
|
||||
|
@ -606,19 +644,23 @@ namespace llarp::rpc
|
|||
|
||||
dns::Message msg{dns::Question{qname, qtype}};
|
||||
|
||||
if (auto ep_ptr = (GetEndpointByName(r, endpoint)))
|
||||
if (auto ep_ptr = GetEndpointByName(r, endpoint))
|
||||
{
|
||||
if (auto ep = reinterpret_cast<dns::IQueryHandler*>(ep_ptr.get()))
|
||||
if (auto dns = ep_ptr->DNS())
|
||||
{
|
||||
if (ep->ShouldHookDNSMessage(msg))
|
||||
auto src = std::make_shared<DummyPacketSource>([reply](auto result) {
|
||||
if (result)
|
||||
reply(CreateJSONResponse(result->ToJSON()));
|
||||
else
|
||||
reply(CreateJSONError("no response from dns"));
|
||||
});
|
||||
if (not dns->MaybeHandlePacket(src, src->dumb, src->dumb, msg.ToBuffer()))
|
||||
{
|
||||
ep->HandleHookedDNSMessage(std::move(msg), [reply](dns::Message msg) {
|
||||
reply(CreateJSONResponse(msg.ToJSON()));
|
||||
});
|
||||
return;
|
||||
reply(CreateJSONError("dns query not accepted by endpoint"));
|
||||
}
|
||||
}
|
||||
reply(CreateJSONError("dns query not accepted by endpoint"));
|
||||
else
|
||||
reply(CreateJSONError("endpoint does not have dns"));
|
||||
return;
|
||||
}
|
||||
reply(CreateJSONError("no such endpoint for dns query"));
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "auth.hpp"
|
||||
|
||||
#include <llarp/vpn/egres_packet_router.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
|
||||
// minimum time between introset shifts
|
||||
#ifndef MIN_SHIFT_INTERVAL
|
||||
|
|
|
@ -134,6 +134,16 @@ operator==(const llarp_buffer_t& buff, std::string_view data)
|
|||
}
|
||||
namespace llarp
|
||||
{
|
||||
std::vector<byte_t>
|
||||
OwnedBuffer::copy() const
|
||||
{
|
||||
std::vector<byte_t> ret;
|
||||
ret.resize(sz);
|
||||
const auto* ptr = buf.get();
|
||||
std::copy(ptr, ptr + sz, ret.data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
OwnedBuffer
|
||||
OwnedBuffer::copy_from(const llarp_buffer_t& b)
|
||||
{
|
||||
|
|
|
@ -250,6 +250,12 @@ namespace llarp
|
|||
explicit OwnedBuffer(size_t sz) : OwnedBuffer{std::make_unique<byte_t[]>(sz), sz}
|
||||
{}
|
||||
|
||||
// copy content from existing memory
|
||||
explicit OwnedBuffer(const byte_t* ptr, size_t sz) : OwnedBuffer{sz}
|
||||
{
|
||||
std::copy_n(ptr, sz, buf.get());
|
||||
}
|
||||
|
||||
OwnedBuffer(const OwnedBuffer&) = delete;
|
||||
OwnedBuffer&
|
||||
operator=(const OwnedBuffer&) = delete;
|
||||
|
@ -273,6 +279,10 @@ namespace llarp
|
|||
// cur), for when a llarp_buffer_t is used in write mode.
|
||||
static OwnedBuffer
|
||||
copy_used(const llarp_buffer_t& b);
|
||||
|
||||
/// copy everything in this owned buffer into a vector
|
||||
std::vector<byte_t>
|
||||
copy() const;
|
||||
};
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -16,4 +17,16 @@ namespace llarp
|
|||
return Compare()(left, right);
|
||||
}
|
||||
};
|
||||
|
||||
/// type for comparing weak_ptr by value
|
||||
template <typename Type_t, typename Compare = std::less<>>
|
||||
struct CompareWeakPtr
|
||||
{
|
||||
bool
|
||||
operator()(const std::weak_ptr<Type_t>& left, const std::weak_ptr<Type_t>& right) const
|
||||
{
|
||||
return ComparePtr<std::shared_ptr<Type_t>, Compare>{}(left.lock(), right.lock());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -69,12 +69,12 @@ namespace llarp
|
|||
py::class_<LinksConfig>(mod, "LinksConfig")
|
||||
.def(py::init<>())
|
||||
.def(
|
||||
"setOutboundLink",
|
||||
[](LinksConfig& self, std::string addr) {
|
||||
self.OutboundLinks.emplace_back(std::move(addr));
|
||||
"addOutboundLink",
|
||||
[](LinksConfig& self, std::string _addr) {
|
||||
self.OutboundLinks.emplace_back(std::move(_addr));
|
||||
})
|
||||
.def("addInboundLink", [](LinksConfig& self, std::string addr) {
|
||||
self.InboundListenAddrs.emplace_back(std::move(addr));
|
||||
.def("addInboundLink", [](LinksConfig& self, std::string _addr) {
|
||||
self.InboundLinks.emplace_back(std::move(_addr));
|
||||
});
|
||||
|
||||
py::class_<ApiConfig>(mod, "ApiConfig")
|
||||
|
|
|
@ -12,12 +12,19 @@ namespace mocks
|
|||
class MockUDPHandle : public llarp::UDPHandle
|
||||
{
|
||||
Network* const _net;
|
||||
std::optional<llarp::SockAddr> _addr;
|
||||
|
||||
public:
|
||||
MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv)
|
||||
: llarp::UDPHandle{recv}, _net{net}
|
||||
{}
|
||||
|
||||
std::optional<llarp::SockAddr>
|
||||
LocalAddr() const override
|
||||
{
|
||||
return _addr;
|
||||
}
|
||||
|
||||
bool
|
||||
listen(const llarp::SockAddr& addr) override;
|
||||
|
||||
|
@ -186,7 +193,10 @@ namespace mocks
|
|||
bool
|
||||
MockUDPHandle::listen(const llarp::SockAddr& addr)
|
||||
{
|
||||
return _net->HasInterfaceAddress(addr.getIP());
|
||||
if (not _net->HasInterfaceAddress(addr.getIP()))
|
||||
return false;
|
||||
_addr = addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mocks
|
||||
|
|
Loading…
Reference in New Issue