From 74362149eb2958b9f939d171f4b5cf6a740dcfaf Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 7 Apr 2022 16:44:23 -0400 Subject: [PATCH 01/71] 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. --- llarp/CMakeLists.txt | 6 +- llarp/config/config.cpp | 35 +- llarp/config/config.hpp | 3 +- llarp/dns/message.cpp | 12 + llarp/dns/message.hpp | 3 + .../systemd_resolved.cpp => dns/resolver.cpp} | 14 +- .../systemd_resolved.hpp => dns/resolver.hpp} | 16 +- llarp/dns/server.cpp | 607 ++++++++++++++---- llarp/dns/server.hpp | 287 ++++++--- llarp/endpoint_base.hpp | 12 + llarp/ev/ev_libuv.cpp | 7 + llarp/ev/udp_handle.hpp | 4 + llarp/handlers/exit.cpp | 29 +- llarp/handlers/exit.hpp | 33 +- llarp/handlers/tun.cpp | 190 ++++-- llarp/handlers/tun.hpp | 49 +- llarp/lokinet_shared.cpp | 14 +- llarp/net/sock_addr.cpp | 16 +- llarp/net/sock_addr.hpp | 6 + llarp/router/route_poker.cpp | 32 +- llarp/router/route_poker.hpp | 6 +- llarp/router/router.cpp | 6 - llarp/router/router.hpp | 3 - llarp/rpc/rpc_server.cpp | 58 +- llarp/service/endpoint.hpp | 1 + llarp/util/buffer.cpp | 10 + llarp/util/buffer.hpp | 10 + llarp/util/compare_ptr.hpp | 13 + pybind/llarp/config.cpp | 10 +- test/mocks/mock_network.hpp | 12 +- 30 files changed, 1141 insertions(+), 363 deletions(-) rename llarp/{router/systemd_resolved.cpp => dns/resolver.cpp} (95%) rename llarp/{router/systemd_resolved.hpp => dns/resolver.hpp} (55%) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index f95543089..9635d984d 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -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 diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 7fc78746d..c62cb42a8 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.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( "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( @@ -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 diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 0d7a6856d..33c38aef7 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -155,9 +155,10 @@ namespace llarp struct DnsConfig { - SockAddr m_bind; + std::vector m_bind; std::vector m_upstreamDNS; std::vector m_hostfiles; + std::unordered_multimap m_ExtraOpts; void defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); diff --git a/llarp/dns/message.cpp b/llarp/dns/message.cpp index 97bc3707e..556f3d6d8 100644 --- a/llarp/dns/message.cpp +++ b/llarp/dns/message.cpp @@ -414,5 +414,17 @@ namespace llarp fmt::format("{}", fmt::join(additional, ","))); } + std::optional + 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 diff --git a/llarp/dns/message.hpp b/llarp/dns/message.hpp index 5d10e2901..6140d307c 100644 --- a/llarp/dns/message.hpp +++ b/llarp/dns/message.hpp @@ -103,6 +103,9 @@ namespace llarp std::vector authorities; std::vector additional; }; + + std::optional + MaybeParseDNSMessage(llarp_buffer_t buf); } // namespace dns template <> diff --git a/llarp/router/systemd_resolved.cpp b/llarp/dns/resolver.cpp similarity index 95% rename from llarp/router/systemd_resolved.cpp rename to llarp/dns/resolver.cpp index 61d9ef930..cb64f9e6f 100644 --- a/llarp/router/systemd_resolved.cpp +++ b/llarp/dns/resolver.cpp @@ -1,17 +1,17 @@ -#include "systemd_resolved.hpp" +#include "resolver.hpp" #include #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 diff --git a/llarp/router/systemd_resolved.hpp b/llarp/dns/resolver.hpp similarity index 55% rename from llarp/router/systemd_resolved.hpp rename to llarp/dns/resolver.hpp index 8fc8c20af..62c5e6111 100644 --- a/llarp/router/systemd_resolved.hpp +++ b/llarp/dns/resolver.hpp @@ -1,13 +1,14 @@ #pragma once - #include #include -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 diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index d5b3b6b4b..3d91eab3d 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -4,158 +4,539 @@ #include #include #include +#include +#include +#include +#include namespace llarp::dns { - PacketHandler::PacketHandler(EventLoop_ptr loop, IQueryHandler* h) - : m_QueryHandler{h}, m_Loop{std::move(loop)} - {} - - Proxy::Proxy(EventLoop_ptr loop, IQueryHandler* h) - : PacketHandler{loop, h}, m_Loop(std::move(loop)) - { - m_Server = m_Loop->make_udp( - [this](UDPHandle&, SockAddr a, OwnedBuffer buf) { HandlePacket(a, a, buf); }); - } - void - PacketHandler::Stop() + QueryJob_Base::Cancel() const { - if (m_UnboundResolver) - m_UnboundResolver->Stop(); + Message reply{m_Query}; + reply.AddServFail(); + SendReply(reply.ToBuffer()); } - bool - Proxy::Start(SockAddr addr, std::vector resolvers, std::vector hostfiles) + /// 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 { - if (not PacketHandler::Start(addr, std::move(resolvers), std::move(hostfiles))) - return false; - return m_Server->listen(addr); - } + Server& m_DNS; + std::shared_ptr m_udp; + SockAddr m_LocalAddr; - void - PacketHandler::Restart() - { - if (m_UnboundResolver) + public: + explicit UDPReader(Server& dns, const EventLoop_ptr& loop, llarp::SockAddr bindaddr) + : m_DNS{dns} { - LogInfo("reset libunbound's internal stuff"); - m_UnboundResolver->Init(); + 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"}; } - } - bool - PacketHandler::Start(SockAddr, std::vector resolvers, std::vector hostfiles) - { - return SetupUnboundResolver(std::move(resolvers), std::move(hostfiles)); - } + std::optional + BoundOn() const override + { + return m_udp->LocalAddr(); + } - bool - PacketHandler::SetupUnboundResolver( - std::vector resolvers, std::vector 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()); - }; + bool + WouldLoop(const SockAddr& to, const SockAddr&) const override + { + return to != m_LocalAddr; + } - auto replyFunc = [self = weak_from_this()](auto&&... args) { - if (auto this_ptr = self.lock()) - this_ptr->SendServerMessageBufferTo(std::forward(args)...); - }; + void + SendTo(const SockAddr& to, const SockAddr&, llarp::OwnedBuffer buf) const override + { + m_udp->send(to, std::move(buf)); + } - m_UnboundResolver = - std::make_shared(m_Loop, std::move(replyFunc), std::move(failFunc)); - m_Resolvers.clear(); - if (not m_UnboundResolver->Init()) + void + Stop() override { - llarp::LogError("Failed to initialize upstream DNS resolver."); - m_UnboundResolver = nullptr; - return false; + m_udp->close(); } - for (const auto& resolver : resolvers) + }; + + namespace libunbound + { + class Resolver; + + class Query : public QueryJob_Base + { + std::weak_ptr parent; + std::weak_ptr src; + SockAddr resolverAddr; + SockAddr askerAddr; + + public: + explicit Query( + std::weak_ptr parent_, + Message query, + std::weak_ptr 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 { - if (not m_UnboundResolver->AddUpstreamResolver(resolver)) + std::shared_ptr m_ctx; + std::weak_ptr m_Loop; +#ifdef _WIN32 + // windows is dumb so we do ub mainloop in a thread + std::thread runner; + std::atomic running; +#else + std::shared_ptr 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 result{_result}; + // take ownership of our query + std::unique_ptr query{static_cast(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(ub_fd(m_ctx.get())); + m_Poller->on([ptr = std::weak_ptr{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(ub_fd(m_ctx.get())); + m_Poller->on([ptr = std::weak_ptr{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 { - llarp::LogError("Failed to add upstream DNS server: ", resolver); - m_UnboundResolver = nullptr; + 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 + void + call(Callable&& f) + { + if (auto loop = m_Loop.lock()) + loop->call(std::forward(f)); + else + LogError("no mainloop?"); + } + + bool + MaybeHookDNS( + std::weak_ptr 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(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; } - m_Resolvers.emplace(resolver); + }; + + 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"); } - for (const auto& path : hostfiles) + } // namespace libunbound + + Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf) + : m_Loop{std::move(loop)}, m_Config{std::move(conf)} + {} + + void + Server::Start() + { + // set up udp sockets + for (const auto& addr : m_Config.m_bind) { - m_UnboundResolver->AddHostsFile(path); + if (auto ptr = MakePacketSourceOn(addr, m_Config)) + AddPacketSource(std::move(ptr)); } - return true; + // add default resolver as needed + if (auto ptr = MakeDefaultResolver()) + AddResolver(ptr); } - void - Proxy::SendServerMessageBufferTo( - const SockAddr& to, [[maybe_unused]] const SockAddr& from, llarp_buffer_t buf) + std::shared_ptr + Server::MakePacketSourceOn(const llarp::SockAddr& addr, const llarp::DnsConfig&) { - if (!m_Server->send(to, buf)) - llarp::LogError("dns reply failed"); + return std::make_shared(*this, m_Loop, addr); } - bool - PacketHandler::IsUpstreamResolver(const SockAddr& to, [[maybe_unused]] const SockAddr& from) const + std::shared_ptr + Server::MakeDefaultResolver() { - return m_Resolvers.count(to); + 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(m_Loop, m_Config); } - bool - PacketHandler::ShouldHandlePacket( - const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const + std::vector + Server::BoundPacketSourceAddrs() const { - MessageHeader hdr; - if (not hdr.Decode(&buf)) + std::vector addrs; + for (const auto& src : m_PacketSources) { - return false; + if (auto ptr = src.lock()) + if (auto maybe_addr = ptr->BoundOn()) + addrs.emplace_back(*maybe_addr); } + return addrs; + } - Message msg{hdr}; - if (not msg.Decode(&buf)) + std::optional + Server::FirstBoundPacketSourceAddr() const + { + for (const auto& src : m_PacketSources) { - return false; + if (auto ptr = src.lock()) + if (auto bound = ptr->BoundOn()) + return bound; } + return std::nullopt; + } + + void + Server::AddResolver(std::weak_ptr resolver) + { + m_Resolvers.insert(resolver); + } + + void + Server::AddResolver(std::shared_ptr resolver) + { + m_OwnedResolvers.insert(resolver); + AddResolver(std::weak_ptr{resolver}); + } - 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); + void + Server::AddPacketSource(std::weak_ptr pkt) + { + m_PacketSources.push_back(pkt); } void - PacketHandler::HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf) + Server::AddPacketSource(std::shared_ptr pkt) { - MessageHeader hdr; - if (not hdr.Decode(&buf)) + m_OwnedPacketSources.push_back(pkt); + AddPacketSource(std::weak_ptr{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(); } + } - Message msg(hdr); - if (not msg.Decode(&buf)) + void + Server::Reset() + { + for (const auto& resolver : m_Resolvers) { - llarp::LogWarn("failed to parse dns message from ", from); - return; + if (auto ptr = resolver.lock()) + ptr->ResetInternalState(); } + } + bool + Server::MaybeHandlePacket( + std::weak_ptr 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; + } + + auto maybe = MaybeParseDNSMessage(buf); + if (not maybe) + { + 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 diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index 025ec8ef6..c101581af 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -1,99 +1,226 @@ #pragma once #include "message.hpp" +#include #include #include -#include "unbound_resolver.hpp" +#include +#include -#include - -namespace llarp +namespace llarp::dns { - namespace dns + /// a job handling 1 dns query + class QueryJob_Base { - /// handler of dns query hooking - class IQueryHandler - { - public: - virtual ~IQueryHandler() = default; + protected: + /// the original dns query + Message m_Query; - /// return true if we should hook this message - virtual bool - ShouldHookDNSMessage(const Message& msg) const = 0; + public: + explicit QueryJob_Base(Message query) : m_Query{std::move(query)} + {} - /// handle a hooked message - virtual bool - HandleHookedDNSMessage(Message query, std::function sendReply) = 0; - }; + virtual ~QueryJob_Base() = default; - // Base class for DNS lookups - class PacketHandler : public std::enable_shared_from_this + Message& + Underlying() { - public: - explicit PacketHandler(EventLoop_ptr loop, IQueryHandler* handler); - - virtual ~PacketHandler() = default; - - virtual bool - Start( - SockAddr localaddr, - std::vector upstreamResolvers, - std::vector hostfiles); - - void - Stop(); - - void - Restart(); - - void - HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf); - - bool - ShouldHandlePacket(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const; - - protected: - virtual void - SendServerMessageBufferTo(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) = 0; + return m_Query; + } - // 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; + const Message& + Underlying() const + { + return m_Query; + } - private: - void - HandleUpstreamFailure(const SockAddr& from, const SockAddr& to, Message msg); + /// cancel this operation and inform anyone who cares + void + Cancel() const; - bool - SetupUnboundResolver(std::vector resolvers, std::vector hostfiles); + /// send a raw buffer back to the querier + virtual void + SendReply(llarp::OwnedBuffer replyBuf) const = 0; + }; - IQueryHandler* const m_QueryHandler; - std::set m_Resolvers; - std::shared_ptr m_UnboundResolver; - EventLoop_ptr m_Loop; - }; + class PacketSource_Base + { + public: + virtual ~PacketSource_Base() = default; + + /// 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; + + /// 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; + + /// stop reading packets and end operation + virtual void + Stop() = 0; + + /// returns the sockaddr we are bound on if applicable + virtual std::optional + BoundOn() const = 0; + }; + + /// 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 + { + std::weak_ptr src; + const SockAddr resolver; + const SockAddr asker; + + public: + explicit QueryJob( + std::weak_ptr source, + const Message& query, + const SockAddr& to_, + const SockAddr& from_) + : QueryJob_Base{query}, src{source}, resolver{to_}, asker{from_} + {} + + void + SendReply(llarp::OwnedBuffer replyBuf) const override + { + if (auto ptr = src.lock()) + ptr->SendTo(asker, resolver, std::move(replyBuf)); + } + }; + + /// 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; + + public: + virtual ~Resolver_Base() = default; + + /// less than via rank + bool + operator<(const Resolver_Base& other) const + { + return Rank() < other.Rank(); + } - // Proxying DNS handler that listens on a UDP port for proper DNS requests. - class Proxy : public PacketHandler + /// 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 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 { - public: - explicit Proxy(EventLoop_ptr loop, IQueryHandler* handler); - - bool - Start( - SockAddr localaddr, - std::vector upstreamResolvers, - std::vector hostfiles) override; - - protected: - void - SendServerMessageBufferTo( - const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) override; - - private: - std::shared_ptr m_Server; - EventLoop_ptr m_Loop; + (void)to; + (void)from; + return false; }; - } // namespace dns -} // namespace llarp + }; + + // Base class for DNS proxy + class Server : public std::enable_shared_from_this + { + protected: + /// add a packet source to this server, does share ownership + void + AddPacketSource(std::shared_ptr resolver); + /// add a resolver to this packet handler, does share ownership + void + AddResolver(std::shared_ptr 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 + BoundPacketSourceAddrs() const; + + /// returns the first sockaddr we have on our packet sources if we have one + std::optional + FirstBoundPacketSourceAddr() const; + + /// add a resolver to this packet handler, does not share ownership + void + AddResolver(std::weak_ptr resolver); + + /// add a packet source to this server, does not share ownership + void + AddPacketSource(std::weak_ptr resolver); + + /// create a packet source bound on bindaddr but does not add it + virtual std::shared_ptr + 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 + 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 pktsource, + const SockAddr& resolver, + const SockAddr& from, + llarp::OwnedBuffer buf); + + protected: + EventLoop_ptr m_Loop; + llarp::DnsConfig m_Config; + + private: + std::set, ComparePtr>> + m_OwnedResolvers; + std::set, CompareWeakPtr> m_Resolvers; + + std::vector> m_PacketSources; + std::vector> m_OwnedPacketSources; + }; + +} // namespace llarp::dns diff --git a/llarp/endpoint_base.hpp b/llarp/endpoint_base.hpp index e924ab523..6bbd97bce 100644 --- a/llarp/endpoint_base.hpp +++ b/llarp/endpoint_base.hpp @@ -23,6 +23,11 @@ namespace llarp class TunnelManager; } + namespace dns + { + class Server; + } + class EndpointBase { std::unordered_set 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() const + { + return nullptr; + }; + /// called when srv data changes in some way virtual void SRVRecordsChanged() = 0; diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp index d5f009f16..3f4784b94 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/ev_libuv.cpp @@ -72,6 +72,13 @@ namespace llarp::uv bool send(const SockAddr& dest, const llarp_buffer_t& buf) override; + std::optional + LocalAddr() const override + { + auto addr = handle->sock(); + return SockAddr{addr.ip, huint16_t{static_cast(addr.port)}}; + } + std::optional file_descriptor() override { diff --git a/llarp/ev/udp_handle.hpp b/llarp/ev/udp_handle.hpp index 8169bd8a1..9e8425cd5 100644 --- a/llarp/ev/udp_handle.hpp +++ b/llarp/ev/udp_handle.hpp @@ -33,6 +33,10 @@ namespace llarp return std::nullopt; } + /// returns the local address we are bound on + virtual std::optional + LocalAddr() const = 0; + // Base class destructor virtual ~UDPHandle() = default; diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 00f1f8679..972dfeecc 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -18,11 +18,7 @@ namespace llarp namespace handlers { ExitEndpoint::ExitEndpoint(std::string name, AbstractRouter* r) - : m_Router(r) - , m_Resolver(std::make_shared(r->loop(), this)) - , m_Name(std::move(name)) - , m_LocalResolverAddr{"127.0.0.1:53"} - , m_QUIC{std::make_shared(*this)} + : m_Router(r), m_Name(std::move(name)), m_QUIC{std::make_shared(*this)} { m_ShouldInitTun = true; m_QUIC = std::make_shared(*this); @@ -211,6 +207,22 @@ namespace llarp return false; } + bool + ExitEndpoint::MaybeHookDNS( + std::weak_ptr source, + const dns::Message& query, + const SockAddr& to, + const SockAddr& from) + { + if (not ShouldHookDNSMessage(query)) + return false; + + auto job = std::make_shared(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 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(m_Router->loop(), dnsConfig); m_OurRange = networkConfig.m_ifaddr; if (!m_OurRange.addr.h) diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index 5c2f3ec41..9984a3431 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -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 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) override; + HandleHookedDNSMessage(dns::Message msg, std::function); void LookupServiceAsync( @@ -174,7 +199,7 @@ namespace llarp KickIdentOffExit(const PubKey& pk); AbstractRouter* m_Router; - std::shared_ptr m_Resolver; + std::shared_ptr m_Resolver; bool m_ShouldInitTun; std::string m_Name; bool m_PermitExit; diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 776b2edb1..4b5259146 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -27,26 +27,46 @@ #include #include +#include + #include namespace llarp { namespace handlers { + bool + TunEndpoint::MaybeHookDNS( + std::weak_ptr source, + const dns::Message& query, + const SockAddr& to, + const SockAddr& from) + { + if (not ShouldHookDNSMessage(query)) + return false; + + auto job = std::make_shared(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 + 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 PacketSource; + + virtual ~TunDNS() = default; + explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf) + : dns::Server{ep->Router()->loop(), conf}, m_Endpoint{ep} + {} + + std::shared_ptr + MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override + { + auto ptr = std::make_shared(m_Endpoint, conf); + PacketSource = ptr; + return ptr; + } + + std::shared_ptr + 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( [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }); -#if defined(ANDROID) || (defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION)) - m_Resolver = std::make_shared(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(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(r->loop(), this); + m_DNS = std::make_shared(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 resolvers; - for (const auto& addr : m_UpstreamResolvers) - resolvers.emplace_back(addr.ToString()); - obj["ustreamResolvers"] = resolvers; - obj["localResolver"] = m_LocalResolverAddr.ToString(); + + std::vector upstreamRes; + for (const auto& ent : m_DnsConfig.m_upstreamDNS) + upstreamRes.emplace_back(ent.ToString()); + obj["ustreamResolvers"] = upstreamRes; + + std::vector 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 TunEndpoint::ReconfigureDNS(std::vector 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()) + + if constexpr (not llarp::platform::is_apple) { - m_OurIPv6 = *maybe; - LogInfo(Name(), " has ipv6 address ", m_OurIPv6); + if (auto maybe = m_router->Net().GetInterfaceIPv6Address(m_IfName)) + { + 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 */); + 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(); } diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index 74161754b..e8e4ad22a 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -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(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 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 sendreply) override; + HandleHookedDNSMessage(dns::Message query, std::function sendreply); void TickTun(llarp_time_t now); @@ -96,6 +114,16 @@ namespace llarp bool SetupTun(); + void + SetupDNS(); + + /// overrides Endpoint + std::shared_ptr + 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 m_Resolver; + + /// dns subsystem for this endpoint + std::shared_ptr m_DNS; + + DnsConfig m_DnsConfig; /// maps ip address to timestamp last active std::unordered_map 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 m_UpstreamResolvers; - /// dns host files list - std::vector m_hostfiles; - /// local dns - IpAddress m_LocalResolverAddr; /// list of strict connect addresses for hooks std::vector m_StrictConnectAddrs; /// use v6? diff --git a/llarp/lokinet_shared.cpp b/llarp/lokinet_shared.cpp index f2177d3de..de8f48528 100644 --- a/llarp/lokinet_shared.cpp +++ b/llarp/lokinet_shared.cpp @@ -228,7 +228,7 @@ struct lokinet_context [[nodiscard]] std::optional make_udp_handler( const std::shared_ptr& 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 weak{ep}; auto udp = std::make_shared( - 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 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 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()) diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index 1a2d9c046..7f66e0aff 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -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(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 diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 447c4771e..82ea367c7 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -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); diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index b24b88476..c92aa741f 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -2,6 +2,7 @@ #include "abstractrouter.hpp" #include "net/sock_addr.hpp" #include +#include #include 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 diff --git a/llarp/router/route_poker.hpp b/llarp/router/route_poker.hpp index f494fada8..17969a2b5 100644 --- a/llarp/router/route_poker.hpp +++ b/llarp/router/route_poker.hpp @@ -5,7 +5,6 @@ #include #include #include -#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(); diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 62a42049d..62f17e9e5 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -1217,12 +1217,6 @@ namespace llarp return true; } - int - Router::OutboundUDPSocket() const - { - return m_OutboundUDPSocket; - } - bool Router::Run() { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 3500ed9cf..dc4b901a1 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -211,9 +211,6 @@ namespace llarp bool ShouldTestOtherRouters() const; - int - OutboundUDPSocket() const override; - std::optional _ourAddress; EventLoop_ptr _loop; diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 957bba6db..175ccdf91 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -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)> func; + + public: + SockAddr dumb; + + template + DummyPacketSource(Callable&& f) : func{std::forward(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 + BoundOn() const override + { + return std::nullopt; + } + }; + /// a function that replies to an rpc request using ReplyFunction_t = std::function; @@ -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(ep_ptr.get())) + if (auto dns = ep_ptr->DNS()) { - if (ep->ShouldHookDNSMessage(msg)) + auto src = std::make_shared([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")); diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index a90752961..64b8b68ab 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -26,6 +26,7 @@ #include "auth.hpp" #include +#include // minimum time between introset shifts #ifndef MIN_SHIFT_INTERVAL diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index f02eae652..15cfca265 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -134,6 +134,16 @@ operator==(const llarp_buffer_t& buff, std::string_view data) } namespace llarp { + std::vector + OwnedBuffer::copy() const + { + std::vector 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) { diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 841945d03..08010616f 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -250,6 +250,12 @@ namespace llarp explicit OwnedBuffer(size_t sz) : OwnedBuffer{std::make_unique(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 + copy() const; }; } // namespace llarp diff --git a/llarp/util/compare_ptr.hpp b/llarp/util/compare_ptr.hpp index e203688b9..4c3ec7cc7 100644 --- a/llarp/util/compare_ptr.hpp +++ b/llarp/util/compare_ptr.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace llarp { @@ -16,4 +17,16 @@ namespace llarp return Compare()(left, right); } }; + + /// type for comparing weak_ptr by value + template > + struct CompareWeakPtr + { + bool + operator()(const std::weak_ptr& left, const std::weak_ptr& right) const + { + return ComparePtr, Compare>{}(left.lock(), right.lock()); + } + }; + } // namespace llarp diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 9b705bc5a..4ec4c0131 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -69,12 +69,12 @@ namespace llarp py::class_(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_(mod, "ApiConfig") diff --git a/test/mocks/mock_network.hpp b/test/mocks/mock_network.hpp index f5a47548e..54e92a44a 100644 --- a/test/mocks/mock_network.hpp +++ b/test/mocks/mock_network.hpp @@ -12,12 +12,19 @@ namespace mocks class MockUDPHandle : public llarp::UDPHandle { Network* const _net; + std::optional _addr; public: MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv) : llarp::UDPHandle{recv}, _net{net} {} + std::optional + 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 From 2d586145eef8ff19ba6311b82609925130ae2032 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 16 Apr 2022 12:41:34 -0400 Subject: [PATCH 02/71] wire up dns srv records --- llarp/handlers/tun.cpp | 27 ++++++---- llarp/service/endpoint.cpp | 102 ++++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 4b5259146..d25c8d361 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -833,20 +833,29 @@ namespace llarp } else if (msg.questions[0].qtype == dns::qTypeSRV) { - llarp::service::Address addr; - + auto srv_for = msg.questions[0].Subdomains(); + auto name = msg.questions[0].qname; if (is_localhost_loki(msg)) { - msg.AddSRVReply(introSet().GetMatchingSRVRecords(msg.questions[0].Subdomains())); + msg.AddSRVReply(introSet().GetMatchingSRVRecords(srv_for)); reply(msg); return true; } - else if (addr.FromString(qname, ".loki")) - { - llarp::LogDebug("SRV request for: ", qname); - - return ReplyToLokiSRVWhenReady(addr, std::make_shared(msg)); - } + LookupServiceAsync( + name, + srv_for, + [reply, msg = std::make_shared(std::move(msg))](auto records) { + if (records.empty()) + { + msg->AddNXReply(); + } + else + { + msg->AddSRVReply(records); + } + reply(*msg); + }); + return true; } else { diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 84c10f2e8..47599397a 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -222,39 +222,81 @@ namespace llarp std::string service, std::function)> resultHandler) { - auto fail = [resultHandler]() { resultHandler({}); }; - - auto lookupByAddress = [service, fail, resultHandler](auto address) { - // TODO: remove me after implementing the rest - fail(); - if (auto* ptr = std::get_if(&address)) - {} - else if (auto* ptr = std::get_if
(&address)) - {} + // handles when we aligned to a loki address + auto handleGotPathToService = [resultHandler, service, this](auto addr) { + // we can probably get this info before we have a path to them but we do this after we + // have a path so when we send the response back they can send shit to them immediately + const auto& container = m_state->m_RemoteSessions; + if (auto itr = container.find(addr); itr != container.end()) + { + // parse the stuff we need from this guy + resultHandler(itr->second->GetCurrentIntroSet().GetMatchingSRVRecords(service)); + return; + } + resultHandler({}); + }; + + // handles when we resolved a .snode + auto handleResolvedSNodeName = [resultHandler, nodedb = Router()->nodedb()](auto router_id) { + std::vector result{}; + if (auto maybe_rc = nodedb->Get(router_id)) + { + result = maybe_rc->srvRecords; + } + resultHandler(std::move(result)); + }; + + // handles when we got a path to a remote thing + auto handleGotPathTo = [handleGotPathToService, handleResolvedSNodeName, resultHandler]( + auto maybe_tag, auto address) { + if (not maybe_tag) + { + resultHandler({}); + return; + } + + if (auto* addr = std::get_if
(&address)) + { + // .loki case + handleGotPathToService(*addr); + } + else if (auto* router_id = std::get_if(&address)) + { + // .snode case + handleResolvedSNodeName(*router_id); + } else { - fail(); + // fallback case + // XXX: never should happen but we'll handle it anyways + resultHandler({}); } }; - if (auto maybe = ParseAddress(name)) - { - lookupByAddress(*maybe); - } - else if (NameIsValid(name)) - { - LookupNameAsync(name, [lookupByAddress, fail](auto maybe) { - if (maybe) - { - lookupByAddress(*maybe); - } - else - { - fail(); - } - }); - } - else - fail(); + + // handles when we know a long address of a remote resource + auto handleGotAddress = [resultHandler, handleGotPathTo, this](auto address) { + // we will attempt a build to whatever we looked up + const auto result = EnsurePathTo( + address, + [address, handleGotPathTo](auto maybe_tag) { handleGotPathTo(maybe_tag, address); }, + PathAlignmentTimeout()); + + // on path build start fail short circuit + if (not result) + resultHandler({}); + }; + + // look up this name async and start the entire chain of events + LookupNameAsync(name, [handleGotAddress, resultHandler](auto maybe_addr) { + if (maybe_addr) + { + handleGotAddress(*maybe_addr); + } + else + { + resultHandler({}); + } + }); } bool @@ -912,7 +954,7 @@ namespace llarp { if (not NameIsValid(name)) { - handler(std::nullopt); + handler(ParseAddress(name)); return; } auto& cache = m_state->nameCache; From 253d22db4f631c8060100d814037073127c39da1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 23 May 2022 12:45:06 -0400 Subject: [PATCH 03/71] restucture dbus parts * move dbus into llarp/linux/dbus.hpp and llarp/linux/dbus.cpp * provide platform abstraction for setting dns in preparation for network manager --- llarp/CMakeLists.txt | 5 +- llarp/constants/platform.hpp | 8 ++ llarp/dns/resolver.cpp | 185 +++++++++++++++++++---------------- llarp/dns/resolver.hpp | 64 +++++++++--- llarp/linux/dbus.cpp | 26 +++++ llarp/linux/dbus.hpp | 69 +++++++++++++ 6 files changed, 253 insertions(+), 104 deletions(-) create mode 100644 llarp/linux/dbus.cpp create mode 100644 llarp/linux/dbus.hpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 9635d984d..b98cd4c1f 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -53,10 +53,7 @@ if (ANDROID) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") - if(NON_PC_TARGET) - add_import_library(rt) - target_link_libraries(lokinet-platform PUBLIC rt) - endif() + target_sources(lokinet-platform PRIVATE linux/dbus.cpp) endif() if (WIN32) diff --git a/llarp/constants/platform.hpp b/llarp/constants/platform.hpp index feca36fb6..6ca98f655 100644 --- a/llarp/constants/platform.hpp +++ b/llarp/constants/platform.hpp @@ -9,6 +9,14 @@ namespace llarp::platform true #else false +#endif + ; + /// we have systemd ? + inline constexpr bool has_systemd = +#ifdef WITH_SYSTEMD + true +#else + false #endif ; diff --git a/llarp/dns/resolver.cpp b/llarp/dns/resolver.cpp index cb64f9e6f..f3e2f22a2 100644 --- a/llarp/dns/resolver.cpp +++ b/llarp/dns/resolver.cpp @@ -1,92 +1,50 @@ #include "resolver.hpp" #include - -#ifndef WITH_SYSTEMD +#include namespace llarp::dns { - bool - set_resolver(std::string, llarp::SockAddr, bool) + class Null_SystemSettings : public I_SystemSettings { - LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS"); - return false; - } + void + set_resolver(std::string, llarp::SockAddr, bool) override + { + LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS"); + } + }; } // namespace llarp::dns -#else - -#include - +#ifdef WITH_SYSTEMD extern "C" { -#include #include } +#include + using namespace std::literals; namespace llarp::dns { - namespace + class SD_SystemSettings : public I_SystemSettings { - template + public: void - resolved_call(sd_bus* bus, const char* method, const char* arg_format, T... args) - { - sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus_message* msg = nullptr; - int r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - method, - &error, - &msg, - arg_format, - args...); - - if (r < 0) - throw std::runtime_error{"sdbus resolved "s + method + " failed: " + strerror(-r)}; - - sd_bus_message_unref(msg); - sd_bus_error_free(&error); - } - - struct sd_bus_deleter + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override { - void - operator()(sd_bus* ptr) const + unsigned int if_ndx = if_nametoindex(ifname.c_str()); + if (if_ndx == 0) { - sd_bus_unref(ptr); + throw std::runtime_error{"No such interface '" + ifname + "'"}; } - }; - } // namespace - - bool - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) - { - unsigned int if_ndx = if_nametoindex(ifname.c_str()); - if (if_ndx == 0) - { - LogWarn("No such interface '", ifname, "'"); - return false; - } - - // Connect to the system bus - sd_bus* bus = nullptr; - int r = sd_bus_open_system(&bus); - if (r < 0) - { - LogWarn("Failed to connect to system bus to set DNS: ", strerror(-r)); - return false; - } - std::unique_ptr bus_ptr{bus}; - try - { - // This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but the - // alternative is to build up a bunch of crap with va_args, which is slightly more gross. + linux::DBUS _dbus{ + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager"}; + // This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but + // the alternative is to build up a bunch of crap with va_args, which is slightly more + // gross. const bool isStandardDNSPort = dns.getPort() == 53; if (dns.isIPv6()) { @@ -95,8 +53,7 @@ namespace llarp::dns auto* a = reinterpret_cast(&ipv6); if (isStandardDNSPort) { - resolved_call( - bus, + _dbus( "SetLinkDNS", "ia(iay)", (int32_t)if_ndx, @@ -111,8 +68,7 @@ namespace llarp::dns } else { - resolved_call( - bus, + _dbus( "SetLinkDNSEx", "ia(iayqs)", (int32_t)if_ndx, @@ -135,8 +91,7 @@ namespace llarp::dns auto* a = reinterpret_cast(&ipv4); if (isStandardDNSPort) { - resolved_call( - bus, + _dbus( "SetLinkDNS", "ia(iay)", (int32_t)if_ndx, @@ -150,8 +105,7 @@ namespace llarp::dns } else { - resolved_call( - bus, + _dbus( "SetLinkDNSEx", "ia(iayqs)", (int32_t)if_ndx, @@ -171,8 +125,7 @@ namespace llarp::dns // Setting "." as a routing domain gives this DNS server higher priority in resolution // compared to dns servers that are set without a domain (e.g. the default for a // DHCP-configured DNS server) - resolved_call( - bus, + _dbus( "SetLinkDomains", "ia(sb)", (int32_t)if_ndx, @@ -180,11 +133,10 @@ namespace llarp::dns "." // global DNS root ); else - // Only resolve .loki and .snode through lokinet (so you keep using your local DNS server - // for everything else, which is nicer than forcing everything though lokinet's upstream - // DNS). - resolved_call( - bus, + // Only resolve .loki and .snode through lokinet (so you keep using your local DNS + // server for everything else, which is nicer than forcing everything though lokinet's + // upstream DNS). + _dbus( "SetLinkDomains", "ia(sb)", (int32_t)if_ndx, @@ -194,16 +146,75 @@ namespace llarp::dns "snode", // domain (int)1 // routing domain = true ); - - return true; } - catch (const std::exception& e) + }; + + /// network manager dns setter + class NM_SystemSettings : public I_SystemSettings + { + public: + void + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override { - LogWarn("Failed to set DNS via systemd-resolved: ", e.what()); + unsigned int if_ndx = if_nametoindex(ifname.c_str()); + if (if_ndx == 0) + { + throw std::runtime_error{"No such interface '" + ifname + "'"}; + } + (void)dns; + (void)global; + // TODO: implement network manager shit } - return false; - } + }; } // namespace llarp::dns #endif // WITH_SYSTEMD + +namespace llarp::dns +{ + class MultiSettings : public I_SystemSettings + { + std::vector> m_Impls; + + public: + void + add_impl(std::unique_ptr impl) + { + m_Impls.emplace_back(std::move(impl)); + } + + void + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override + { + size_t fails{0}; + for (const auto& ptr : m_Impls) + { + try + { + ptr->set_resolver(ifname, dns, global); + } + catch (std::exception& ex) + { + LogWarn(ex.what()); + fails++; + } + } + if (fails == m_Impls.size()) + throw std::runtime_error{"tried all ways to set resolver and failed"}; + } + }; + + std::shared_ptr + MakeSystemSettings() + { + auto settings = std::make_shared(); + settings->add_impl(std::make_unique()); + if constexpr (llarp::platform::has_systemd) + { + settings->add_impl(std::make_unique()); + settings->add_impl(std::make_unique()); + } + return settings; + } +} // namespace llarp::dns diff --git a/llarp/dns/resolver.hpp b/llarp/dns/resolver.hpp index 62c5e6111..7f71ef66c 100644 --- a/llarp/dns/resolver.hpp +++ b/llarp/dns/resolver.hpp @@ -1,21 +1,59 @@ #pragma once #include +#include #include +#include + +#include + +namespace llarp +{ + struct AbstractRouter; +} namespace llarp::dns { - /// 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(). - /// \param dns -- the listening address of the lokinet DNS server - /// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode - /// addresses (false). - bool - set_resolver(std::string if_name, llarp::SockAddr dns, bool global); + /// sets dns settings in a platform dependant way + class I_SystemSettings + { + public: + virtual ~I_SystemSettings() = default; + + /// Attempts to set lokinet as the DNS server. + /// throws if unsupported or fails. + /// + /// + /// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0. + /// Typically tun_endpoint.GetIfName(). + /// \param dns -- the listening address of the lokinet DNS server + /// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode + /// addresses (false). + virtual void + set_resolver(std::string if_name, llarp::SockAddr dns, bool global) = 0; + }; + + /// creates for the current platform + std::shared_ptr + MakeSystemSettings(); + + /// compat wrapper + inline bool + set_resolver(std::string if_name, llarp::SockAddr dns, bool global) + { + try + { + if (auto settings = MakeSystemSettings()) + { + settings->set_resolver(std::move(if_name), std::move(dns), global); + return true; + } + } + catch (std::exception& ex) + { + LogError("failed to set DNS: ", ex.what()); + } + LogWarn("did not set dns"); + return false; + } } // namespace llarp::dns diff --git a/llarp/linux/dbus.cpp b/llarp/linux/dbus.cpp new file mode 100644 index 000000000..801250a9f --- /dev/null +++ b/llarp/linux/dbus.cpp @@ -0,0 +1,26 @@ +#ifdef WITH_SYSTEMD +#include "dbus.hpp" + +namespace llarp::linux +{ + system_bus_exception::system_bus_exception(int err) + : std::runtime_error{"cannot connect to system bus: " + std::string{strerror(-err)}} + {} + + dbus_call_exception::dbus_call_exception(std::string meth, int err) + : std::runtime_error{ + "failed to call dbus function '" + meth + "': " + std::string{strerror(-err)}} + {} + + DBUS::DBUS(std::string _interface, std::string _instance, std::string _call) + : m_interface{std::move(_interface)} + , m_instance{std::move(_instance)} + , m_call{std::move(_call)} + { + sd_bus* bus{nullptr}; + if (auto err = sd_bus_open_system(&bus); err < 0) + throw system_bus_exception{err}; + m_ptr.reset(bus); + } +} // namespace llarp::linux +#endif diff --git a/llarp/linux/dbus.hpp b/llarp/linux/dbus.hpp new file mode 100644 index 000000000..fe7198c79 --- /dev/null +++ b/llarp/linux/dbus.hpp @@ -0,0 +1,69 @@ +#pragma once + +extern "C" +{ +#include +} +#include +#include +#include + +namespace llarp::linux +{ + /// exception for connecting to system bus + class system_bus_exception : public std::runtime_error + { + public: + explicit system_bus_exception(int err); + }; + + /// exception for a failed calling of a dbus method + class dbus_call_exception : public std::runtime_error + { + public: + explicit dbus_call_exception(std::string meth, int err); + }; + + class DBUS + { + struct sd_bus_deleter + { + void + operator()(sd_bus* ptr) const + { + sd_bus_unref(ptr); + } + }; + std::unique_ptr m_ptr; + const std::string m_interface; + const std::string m_instance; + const std::string m_call; + + public: + DBUS(std::string _interface, std::string _instance, std::string _call); + + template + void + operator()(std::string method, const char* arg_format, T... args) + { + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message* msg = nullptr; + auto r = sd_bus_call_method( + m_ptr.get(), + m_interface.c_str(), + m_instance.c_str(), + m_call.c_str(), + method.c_str(), + &error, + &msg, + arg_format, + args...); + + if (r < 0) + throw dbus_call_exception{std::move(method), r}; + + sd_bus_message_unref(msg); + sd_bus_error_free(&error); + } + }; +} // namespace llarp::linux From baddad95645cc1f72faf1eaab5f368656346921a Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 22 Jun 2022 12:14:33 -0400 Subject: [PATCH 04/71] remove compat wrapper --- jni/lokinet_daemon.cpp | 5 +- llarp/CMakeLists.txt | 4 +- llarp/dns/multi_platform.cpp | 30 ++++ llarp/dns/multi_platform.hpp | 22 +++ llarp/dns/nm_platform.cpp | 24 +++ llarp/dns/nm_platform.hpp | 24 +++ llarp/dns/null_platform.hpp | 14 ++ llarp/dns/{resolver.hpp => platform.hpp} | 33 +--- llarp/dns/resolver.cpp | 220 ----------------------- llarp/dns/sd_platform.cpp | 134 ++++++++++++++ llarp/dns/sd_platform.hpp | 24 +++ llarp/dns/server.cpp | 30 +++- llarp/dns/server.hpp | 14 +- llarp/handlers/exit.cpp | 5 +- llarp/handlers/tun.cpp | 6 +- llarp/router/abstractrouter.hpp | 10 +- llarp/router/route_poker.cpp | 20 +-- llarp/router/router.hpp | 6 + 18 files changed, 342 insertions(+), 283 deletions(-) create mode 100644 llarp/dns/multi_platform.cpp create mode 100644 llarp/dns/multi_platform.hpp create mode 100644 llarp/dns/nm_platform.cpp create mode 100644 llarp/dns/nm_platform.hpp create mode 100644 llarp/dns/null_platform.hpp rename llarp/dns/{resolver.hpp => platform.hpp} (55%) delete mode 100644 llarp/dns/resolver.cpp create mode 100644 llarp/dns/sd_platform.cpp create mode 100644 llarp/dns/sd_platform.hpp diff --git a/jni/lokinet_daemon.cpp b/jni/lokinet_daemon.cpp index 4d4fa1638..8bd90f510 100644 --- a/jni/lokinet_daemon.cpp +++ b/jni/lokinet_daemon.cpp @@ -84,9 +84,8 @@ extern "C" JNIEXPORT jint JNICALL Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self) { - auto ptr = GetImpl(env, self); - if (const auto& router = ptr->router; ptr and ptr->router) - return router->OutboundUDPSocket(); + if (auto ptr = GetImpl(env, self); ptr and ptr->router) + return ptr->router->OutboundUDPSocket(); return -1; } diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index b98cd4c1f..f80bdb77c 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -77,12 +77,14 @@ add_library(liblokinet dns/message.cpp dns/name.cpp + dns/multi_platform.cpp + dns/nm_platform.cpp + dns/sd_platform.cpp dns/question.cpp dns/rr.cpp dns/serialize.cpp dns/server.cpp dns/srv_data.cpp - dns/resolver.cpp consensus/table.cpp consensus/reachability_testing.cpp diff --git a/llarp/dns/multi_platform.cpp b/llarp/dns/multi_platform.cpp new file mode 100644 index 000000000..bf4b0f887 --- /dev/null +++ b/llarp/dns/multi_platform.cpp @@ -0,0 +1,30 @@ +#include "multi_platform.hpp" + +namespace llarp::dns +{ + void + Multi_Platform::add_impl(std::unique_ptr impl) + { + m_Impls.emplace_back(std::move(impl)); + } + + void + Multi_Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + { + size_t fails{0}; + for (const auto& ptr : m_Impls) + { + try + { + ptr->set_resolver(ifname, dns, global); + } + catch (std::exception& ex) + { + LogWarn(ex.what()); + fails++; + } + } + if (fails == m_Impls.size()) + throw std::runtime_error{"tried all ways to set resolver and failed"}; + } +} // namespace llarp::dns diff --git a/llarp/dns/multi_platform.hpp b/llarp/dns/multi_platform.hpp new file mode 100644 index 000000000..8f05d7ed7 --- /dev/null +++ b/llarp/dns/multi_platform.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "platform.hpp" +#include + +namespace llarp::dns +{ + /// a collection of dns platforms that are tried in order when setting dns + class Multi_Platform : public I_Platform + { + std::vector> m_Impls; + + public: + /// add a platform to be owned + void + add_impl(std::unique_ptr impl); + + /// try all owned platforms to set the resolver, throws if none of them work + void + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + }; +} // namespace llarp::dns diff --git a/llarp/dns/nm_platform.cpp b/llarp/dns/nm_platform.cpp new file mode 100644 index 000000000..d4315a049 --- /dev/null +++ b/llarp/dns/nm_platform.cpp @@ -0,0 +1,24 @@ +#include "nm_platform.hpp" +#ifdef WITH_SYSTEMD + +extern "C" +{ +#include +} + +#include + +using namespace std::literals; + +namespace llarp::dns::nm +{ + Platform::Platform() + {} + + void + Platform::set_resolver(std::string, llarp::SockAddr, bool) + { + // todo: implement me eventually + } +} // namespace llarp::dns::nm +#endif diff --git a/llarp/dns/nm_platform.hpp b/llarp/dns/nm_platform.hpp new file mode 100644 index 000000000..e41e858cb --- /dev/null +++ b/llarp/dns/nm_platform.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "platform.hpp" +#include "null_platform.hpp" + +#include +#include +#include + +namespace llarp::dns +{ + namespace nm + { + // a dns platform that sets dns via network manager + class Platform : public I_Platform + { + public: + virtual ~Platform() = default; + + void + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + }; + }; // namespace nm + using NM_Platform_t = std::conditional_t; +} // namespace llarp::dns diff --git a/llarp/dns/null_platform.hpp b/llarp/dns/null_platform.hpp new file mode 100644 index 000000000..2b31ee8a2 --- /dev/null +++ b/llarp/dns/null_platform.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "platform.hpp" + +namespace llarp::dns +{ + /// a dns platform does silently does nothing, successfully + class Null_Platform : public I_Platform + { + public: + void + set_resolver(std::string, llarp::SockAddr, bool) override + {} + }; +} // namespace llarp::dns diff --git a/llarp/dns/resolver.hpp b/llarp/dns/platform.hpp similarity index 55% rename from llarp/dns/resolver.hpp rename to llarp/dns/platform.hpp index 7f71ef66c..25402db26 100644 --- a/llarp/dns/resolver.hpp +++ b/llarp/dns/platform.hpp @@ -6,18 +6,13 @@ #include -namespace llarp -{ - struct AbstractRouter; -} - namespace llarp::dns { /// sets dns settings in a platform dependant way - class I_SystemSettings + class I_Platform { public: - virtual ~I_SystemSettings() = default; + virtual ~I_Platform() = default; /// Attempts to set lokinet as the DNS server. /// throws if unsupported or fails. @@ -32,28 +27,4 @@ namespace llarp::dns set_resolver(std::string if_name, llarp::SockAddr dns, bool global) = 0; }; - /// creates for the current platform - std::shared_ptr - MakeSystemSettings(); - - /// compat wrapper - inline bool - set_resolver(std::string if_name, llarp::SockAddr dns, bool global) - { - try - { - if (auto settings = MakeSystemSettings()) - { - settings->set_resolver(std::move(if_name), std::move(dns), global); - return true; - } - } - catch (std::exception& ex) - { - LogError("failed to set DNS: ", ex.what()); - } - LogWarn("did not set dns"); - return false; - } - } // namespace llarp::dns diff --git a/llarp/dns/resolver.cpp b/llarp/dns/resolver.cpp deleted file mode 100644 index f3e2f22a2..000000000 --- a/llarp/dns/resolver.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include "resolver.hpp" -#include -#include - -namespace llarp::dns -{ - class Null_SystemSettings : public I_SystemSettings - { - void - set_resolver(std::string, llarp::SockAddr, bool) override - { - LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS"); - } - }; -} // namespace llarp::dns - -#ifdef WITH_SYSTEMD -extern "C" -{ -#include -} - -#include - -using namespace std::literals; - -namespace llarp::dns -{ - class SD_SystemSettings : public I_SystemSettings - { - public: - void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override - { - unsigned int if_ndx = if_nametoindex(ifname.c_str()); - if (if_ndx == 0) - { - throw std::runtime_error{"No such interface '" + ifname + "'"}; - } - - linux::DBUS _dbus{ - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager"}; - // This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but - // the alternative is to build up a bunch of crap with va_args, which is slightly more - // gross. - const bool isStandardDNSPort = dns.getPort() == 53; - if (dns.isIPv6()) - { - auto ipv6 = dns.getIPv6(); - static_assert(sizeof(ipv6) == 16); - auto* a = reinterpret_cast(&ipv6); - if (isStandardDNSPort) - { - _dbus( - "SetLinkDNS", - "ia(iay)", - (int32_t)if_ndx, - (int)1, // number of "iayqs"s we are passing - (int32_t)AF_INET6, // network address type - (int)16, // network addr byte size - // clang-format off - a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], - a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15] // yuck - // clang-format on - ); - } - else - { - _dbus( - "SetLinkDNSEx", - "ia(iayqs)", - (int32_t)if_ndx, - (int)1, // number of "iayqs"s we are passing - (int32_t)AF_INET6, // network address type - (int)16, // network addr byte size - // clang-format off - a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], - a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck - // clang-format on - (uint16_t)dns.getPort(), - nullptr // dns server name (for TLS SNI which we don't care about) - ); - } - } - else - { - auto ipv4 = dns.getIPv4(); - static_assert(sizeof(ipv4) == 4); - auto* a = reinterpret_cast(&ipv4); - if (isStandardDNSPort) - { - _dbus( - "SetLinkDNS", - "ia(iay)", - (int32_t)if_ndx, - (int)1, // number of "iayqs"s we are passing - (int32_t)AF_INET, // network address type - (int)4, // network addr byte size - // clang-format off - a[0], a[1], a[2], a[3] // yuck - // clang-format on - ); - } - else - { - _dbus( - "SetLinkDNSEx", - "ia(iayqs)", - (int32_t)if_ndx, - (int)1, // number of "iayqs"s we are passing - (int32_t)AF_INET, // network address type - (int)4, // network addr byte size - // clang-format off - a[0], a[1], a[2], a[3], // yuck - // clang-format on - (uint16_t)dns.getPort(), - nullptr // dns server name (for TLS SNI which we don't care about) - ); - } - } - - if (global) - // Setting "." as a routing domain gives this DNS server higher priority in resolution - // compared to dns servers that are set without a domain (e.g. the default for a - // DHCP-configured DNS server) - _dbus( - "SetLinkDomains", - "ia(sb)", - (int32_t)if_ndx, - (int)1, // array size - "." // global DNS root - ); - else - // Only resolve .loki and .snode through lokinet (so you keep using your local DNS - // server for everything else, which is nicer than forcing everything though lokinet's - // upstream DNS). - _dbus( - "SetLinkDomains", - "ia(sb)", - (int32_t)if_ndx, - (int)2, // array size - "loki", // domain - (int)1, // routing domain = true - "snode", // domain - (int)1 // routing domain = true - ); - } - }; - - /// network manager dns setter - class NM_SystemSettings : public I_SystemSettings - { - public: - void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override - { - unsigned int if_ndx = if_nametoindex(ifname.c_str()); - if (if_ndx == 0) - { - throw std::runtime_error{"No such interface '" + ifname + "'"}; - } - (void)dns; - (void)global; - // TODO: implement network manager shit - } - }; - -} // namespace llarp::dns - -#endif // WITH_SYSTEMD - -namespace llarp::dns -{ - class MultiSettings : public I_SystemSettings - { - std::vector> m_Impls; - - public: - void - add_impl(std::unique_ptr impl) - { - m_Impls.emplace_back(std::move(impl)); - } - - void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override - { - size_t fails{0}; - for (const auto& ptr : m_Impls) - { - try - { - ptr->set_resolver(ifname, dns, global); - } - catch (std::exception& ex) - { - LogWarn(ex.what()); - fails++; - } - } - if (fails == m_Impls.size()) - throw std::runtime_error{"tried all ways to set resolver and failed"}; - } - }; - - std::shared_ptr - MakeSystemSettings() - { - auto settings = std::make_shared(); - settings->add_impl(std::make_unique()); - if constexpr (llarp::platform::has_systemd) - { - settings->add_impl(std::make_unique()); - settings->add_impl(std::make_unique()); - } - return settings; - } -} // namespace llarp::dns diff --git a/llarp/dns/sd_platform.cpp b/llarp/dns/sd_platform.cpp new file mode 100644 index 000000000..f867441f1 --- /dev/null +++ b/llarp/dns/sd_platform.cpp @@ -0,0 +1,134 @@ +#ifdef WITH_SYSTEMD +#include "sd_platform.hpp" + +extern "C" +{ +#include +} + +#include + +using namespace std::literals; + +namespace llarp::dns::sd +{ + void + Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + { + unsigned int if_ndx = if_nametoindex(ifname.c_str()); + if (if_ndx == 0) + { + throw std::runtime_error{"No such interface '" + ifname + "'"}; + } + + linux::DBUS _dbus{ + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager"}; + // This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but + // the alternative is to build up a bunch of crap with va_args, which is slightly more + // gross. + const bool isStandardDNSPort = dns.getPort() == 53; + if (dns.isIPv6()) + { + auto ipv6 = dns.getIPv6(); + static_assert(sizeof(ipv6) == 16); + auto* a = reinterpret_cast(&ipv6); + if (isStandardDNSPort) + { + _dbus( + "SetLinkDNS", + "ia(iay)", + (int32_t)if_ndx, + (int)1, // number of "iayqs"s we are passing + (int32_t)AF_INET6, // network address type + (int)16, // network addr byte size + // clang-format off + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15] // yuck + // clang-format on + ); + } + else + { + _dbus( + "SetLinkDNSEx", + "ia(iayqs)", + (int32_t)if_ndx, + (int)1, // number of "iayqs"s we are passing + (int32_t)AF_INET6, // network address type + (int)16, // network addr byte size + // clang-format off + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck + // clang-format on + (uint16_t)dns.getPort(), + nullptr // dns server name (for TLS SNI which we don't care about) + ); + } + } + else + { + auto ipv4 = dns.getIPv4(); + static_assert(sizeof(ipv4) == 4); + auto* a = reinterpret_cast(&ipv4); + if (isStandardDNSPort) + { + _dbus( + "SetLinkDNS", + "ia(iay)", + (int32_t)if_ndx, + (int)1, // number of "iayqs"s we are passing + (int32_t)AF_INET, // network address type + (int)4, // network addr byte size + // clang-format off + a[0], a[1], a[2], a[3] // yuck + // clang-format on + ); + } + else + { + _dbus( + "SetLinkDNSEx", + "ia(iayqs)", + (int32_t)if_ndx, + (int)1, // number of "iayqs"s we are passing + (int32_t)AF_INET, // network address type + (int)4, // network addr byte size + // clang-format off + a[0], a[1], a[2], a[3], // yuck + // clang-format on + (uint16_t)dns.getPort(), + nullptr // dns server name (for TLS SNI which we don't care about) + ); + } + } + + if (global) + // Setting "." as a routing domain gives this DNS server higher priority in resolution + // compared to dns servers that are set without a domain (e.g. the default for a + // DHCP-configured DNS server) + _dbus( + "SetLinkDomains", + "ia(sb)", + (int32_t)if_ndx, + (int)1, // array size + "." // global DNS root + ); + else + // Only resolve .loki and .snode through lokinet (so you keep using your local DNS + // server for everything else, which is nicer than forcing everything though lokinet's + // upstream DNS). + _dbus( + "SetLinkDomains", + "ia(sb)", + (int32_t)if_ndx, + (int)2, // array size + "loki", // domain + (int)1, // routing domain = true + "snode", // domain + (int)1 // routing domain = true + ); + } +} // namespace llarp::dns::sd +#endif diff --git a/llarp/dns/sd_platform.hpp b/llarp/dns/sd_platform.hpp new file mode 100644 index 000000000..0c83c2557 --- /dev/null +++ b/llarp/dns/sd_platform.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "platform.hpp" +#include "null_platform.hpp" + +#include +#include + +namespace llarp::dns +{ + namespace sd + { + /// a dns platform that sets dns via systemd resolved + class Platform : public I_Platform + { + public: + virtual ~Platform() = default; + + void + set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + }; + } // namespace sd + using SD_Platform_t = + std::conditional_t; +} // namespace llarp::dns diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index 3d91eab3d..0d652074f 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -9,6 +9,10 @@ #include #include +#include "multi_platform.hpp" +#include "sd_platform.hpp" +#include "nm_platform.hpp" + namespace llarp::dns { void @@ -398,8 +402,11 @@ namespace llarp::dns } } // namespace libunbound - Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf) - : m_Loop{std::move(loop)}, m_Config{std::move(conf)} + Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif) + : m_Loop{std::move(loop)} + , m_Config{std::move(conf)} + , m_Platform{CreatePlatform()} + , m_NetifName{std::move(netif)} {} void @@ -417,6 +424,18 @@ namespace llarp::dns AddResolver(ptr); } + std::shared_ptr + Server::CreatePlatform() const + { + auto plat = std::make_shared(); + if constexpr (llarp::platform::has_systemd) + { + plat->add_impl(std::make_unique()); + plat->add_impl(std::make_unique()); + } + return plat; + } + std::shared_ptr Server::MakePacketSourceOn(const llarp::SockAddr& addr, const llarp::DnsConfig&) { @@ -508,6 +527,13 @@ namespace llarp::dns } } + void + Server::SetDNSMode(bool all_queries) + { + if (auto maybe_addr = FirstBoundPacketSourceAddr()) + m_Platform->set_resolver(m_NetifName, *maybe_addr, all_queries); + } + bool Server::MaybeHandlePacket( std::weak_ptr src, diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index c101581af..88ca86385 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -1,6 +1,7 @@ #pragma once #include "message.hpp" +#include "platform.hpp" #include #include #include @@ -160,9 +161,13 @@ namespace llarp::dns void AddResolver(std::shared_ptr resolver); + /// create the platform dependant dns stuff + virtual std::shared_ptr + CreatePlatform() const; + public: virtual ~Server() = default; - explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf); + explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif_name); /// returns all sockaddr we have from all of our PacketSources std::vector @@ -210,11 +215,18 @@ namespace llarp::dns const SockAddr& from, llarp::OwnedBuffer buf); + /// set which dns mode we are in. + /// true for intercepting all queries. false for just .loki and .snode + void + SetDNSMode(bool all_queries); + protected: EventLoop_ptr m_Loop; llarp::DnsConfig m_Config; + std::shared_ptr m_Platform; private: + const std::string m_NetifName; std::set, ComparePtr>> m_OwnedResolvers; std::set, CompareWeakPtr> m_Resolvers; diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 972dfeecc..c14245bbc 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -713,8 +713,6 @@ namespace llarp m_ShouldInitTun = false; } - m_Resolver = std::make_shared(m_Router->loop(), dnsConfig); - m_OurRange = networkConfig.m_ifaddr; if (!m_OurRange.addr.h) { @@ -745,8 +743,7 @@ namespace llarp return llarp::SockAddr{ifaddr, huint16_t{port}}; }); } - // TODO: "exit-whitelist" and "exit-blacklist" - // (which weren't originally implemented) + m_Resolver = std::make_shared(m_Router->loop(), dnsConfig, m_ifname); } huint128_t diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index d25c8d361..2d1ab4b8f 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -123,7 +123,7 @@ namespace llarp virtual ~TunDNS() = default; explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf) - : dns::Server{ep->Router()->loop(), conf}, m_Endpoint{ep} + : dns::Server{ep->Router()->loop(), conf, ep->GetIfName()}, m_Endpoint{ep} {} std::shared_ptr @@ -144,7 +144,7 @@ namespace llarp #endif TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent) - : service::Endpoint(r, parent) + : service::Endpoint{r, parent} { m_PacketRouter = std::make_unique( [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }); @@ -174,7 +174,7 @@ namespace llarp HandleGotUserPacket(std::move(pkt)); }); #else - m_DNS = std::make_shared(Loop(), m_DnsConfig); + m_DNS = std::make_shared(Loop(), m_DnsConfig, GetIfName()); #endif m_DNS->AddResolver(weak_from_this()); m_DNS->Start(); diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 243f0a47b..69b4ccf4a 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -45,6 +45,11 @@ namespace llarp struct I_RCLookupHandler; struct RoutePoker; + namespace dns + { + class I_SystemSettings; + } + namespace net { class Platform; @@ -355,7 +360,10 @@ namespace llarp } virtual int - OutboundUDPSocket() const = 0; + OutboundUDPSocket() const + { + return -1; + } protected: /// Virtual function to handle RouterEvent. HiveRouter overrides this in diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index c92aa741f..352ac1625 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -2,7 +2,7 @@ #include "abstractrouter.hpp" #include "net/sock_addr.hpp" #include -#include +#include #include namespace llarp @@ -163,22 +163,8 @@ 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"); - } - } - } + if (auto dns_server = m_Router->hiddenServiceContext().GetDefault()->DNS()) + dns_server->SetDNSMode(exit_mode_on); } void diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index dc4b901a1..2b280a4c2 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -355,6 +355,12 @@ namespace llarp return m_peerDb; } + inline int + OutboundUDPSocket() const override + { + return m_OutboundUDPSocket; + } + void GossipRCIfNeeded(const RouterContact rc) override; From d846bab0e184a7ba7ce6fd69dbd1b39516758b61 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 27 Jun 2022 13:14:03 -0400 Subject: [PATCH 05/71] unbreak android config loading --- llarp/config/config.cpp | 72 ++++++++++++++++++++++----------------- llarp/config/config.hpp | 6 +++- llarp/config/ini.cpp | 2 +- llarp/config/ini.hpp | 6 ++++ llarp/dns/nm_platform.cpp | 3 -- 5 files changed, 53 insertions(+), 36 deletions(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index c62cb42a8..787fcdd83 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -1336,15 +1336,25 @@ namespace llarp } void - Config::LoadOverrides() + Config::LoadOverrides(ConfigDefinition& conf) const { + ConfigParser parser; const auto overridesDir = GetOverridesDir(m_DataDir); if (fs::exists(overridesDir)) { util::IterDir(overridesDir, [&](const fs::path& overrideFile) { if (overrideFile.extension() == ".ini") { - m_Parser.LoadFile(overrideFile); + ConfigParser parser; + if (not parser.LoadFile(overrideFile)) + throw std::runtime_error{"cannot load '" + overrideFile.u8string() + "'"}; + + parser.IterAll([&](std::string_view section, const SectionValues_t& values) { + for (const auto& pair : values) + { + conf.addConfigValue(section, pair.first, pair.second); + } + }); } return true; }); @@ -1358,7 +1368,7 @@ namespace llarp } bool - Config::LoadString(std::string_view ini, bool isRelay) + Config::LoadConfigData(std::string_view ini, std::optional filename, bool isRelay) { auto params = MakeGenParams(); params->isRelay = isRelay; @@ -1366,7 +1376,18 @@ namespace llarp ConfigDefinition conf{isRelay}; initializeConfig(conf, *params); + for (const auto& item : m_Additional) + { + conf.addConfigValue(item[0], item[1], item[2]); + } + m_Parser.Clear(); + + if (filename) + m_Parser.Filename(*filename); + else + m_Parser.Filename(fs::path{}); + if (not m_Parser.LoadFromStr(ini)) return false; @@ -1377,6 +1398,8 @@ namespace llarp } }); + LoadOverrides(conf); + conf.process(); return true; @@ -1385,37 +1408,24 @@ namespace llarp bool Config::Load(std::optional fname, bool isRelay) { - if (not fname.has_value()) - return LoadDefault(isRelay); - try + std::vector ini{}; + if (fname) { - auto params = MakeGenParams(); - params->isRelay = isRelay; - params->defaultDataDir = m_DataDir; - - ConfigDefinition conf{isRelay}; - initializeConfig(conf, *params); - m_Parser.Clear(); - if (!m_Parser.LoadFile(*fname)) - { + if (not fs::exists(*fname)) return false; - } - LoadOverrides(); - - m_Parser.IterAll([&](std::string_view section, const SectionValues_t& values) { - for (const auto& pair : values) - { - conf.addConfigValue(section, pair.first, pair.second); - } - }); - conf.process(); - return true; - } - catch (const std::exception& e) - { - LogError("Error trying to init and parse config from file: ", e.what()); - return false; + fs::ifstream inf{*fname, std::ios::in | std::ios::binary}; + auto sz = inf.seekg(0, std::ios::end).tellg(); + inf.seekg(0, std::ios::beg); + ini.resize(sz); + inf.read(ini.data(), ini.size()); } + return LoadConfigData(std::string_view{ini.data(), ini.size()}, fname, isRelay); + } + + bool + Config::LoadString(std::string_view ini, bool isRelay) + { + return LoadConfigData(ini, std::nullopt, isRelay); } bool diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 33c38aef7..198599dab 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -295,8 +295,12 @@ namespace llarp bool LoadDefault(bool isRelay); + bool + LoadConfigData( + std::string_view ini, std::optional fname = std::nullopt, bool isRelay = false); + void - LoadOverrides(); + LoadOverrides(ConfigDefinition& conf) const; std::vector> m_Additional; ConfigParser m_Parser; diff --git a/llarp/config/ini.cpp b/llarp/config/ini.cpp index dbacc412b..0769112ba 100644 --- a/llarp/config/ini.cpp +++ b/llarp/config/ini.cpp @@ -17,7 +17,7 @@ namespace llarp { { std::ifstream f(fname, std::ios::in | std::ios::binary); - if (!f.is_open()) + if (not f.is_open()) return false; f.seekg(0, std::ios::end); m_Data.resize(f.tellg()); diff --git a/llarp/config/ini.hpp b/llarp/config/ini.hpp index 9fc36567b..9106e30b1 100644 --- a/llarp/config/ini.hpp +++ b/llarp/config/ini.hpp @@ -47,6 +47,12 @@ namespace llarp void Save(); + inline void + Filename(fs::path f) + { + m_FileName = f; + }; + private: bool Parse(); diff --git a/llarp/dns/nm_platform.cpp b/llarp/dns/nm_platform.cpp index d4315a049..79d2a0fc3 100644 --- a/llarp/dns/nm_platform.cpp +++ b/llarp/dns/nm_platform.cpp @@ -12,9 +12,6 @@ using namespace std::literals; namespace llarp::dns::nm { - Platform::Platform() - {} - void Platform::set_resolver(std::string, llarp::SockAddr, bool) { From e981c9f89983a12cc75d691fc439366703d5bfff Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 16 Aug 2022 15:49:08 -0400 Subject: [PATCH 06/71] tweaks for wine and yarn for gui * allow specifying a custom yarn binary for building the gui using -DYARN= cmake option * unset DISPLAY when calling wine because i hate popups * do not rebuild gui when building for windows * by setting the magical undocumented env var USE_SYSTEM_7ZA to 'true' we can have the pile of npm bullshit code use our system's local 7z binary instead of the probably not backdoored binary from npm, yes for real. i hate nodejs so god damn much you have no fucking idea * allow providing a custom gui from a zip file via -DGUI_ZIP_FILE cmake option --- CMakeLists.txt | 3 +-- cmake/gui.cmake | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f726a720..8498e480e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -317,7 +317,6 @@ if(NOT TARGET uninstall) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() - -if(BUILD_PACKAGE AND NOT APPLE) +if(BUILD_PACKAGE) include(cmake/installer.cmake) endif() diff --git a/cmake/gui.cmake b/cmake/gui.cmake index 32dc93116..be27a9688 100644 --- a/cmake/gui.cmake +++ b/cmake/gui.cmake @@ -1,4 +1,3 @@ - set(default_build_gui OFF) set(default_gui_target pack) if(APPLE) @@ -15,12 +14,13 @@ set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn bui if (BUILD_GUI) message(STATUS "Building lokinet-gui") - - find_program(YARN NAMES yarn yarnpkg REQUIRED) + if(NOT YARN) + find_program(YARN NAMES yarn yarnpkg REQUIRED) + endif() message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}") set(wine_env) if(WIN32) - set(wine_env WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix") + set(wine_env USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix") endif() add_custom_target(lokinet-gui @@ -48,17 +48,25 @@ if (BUILD_GUI) elseif(WIN32) file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui") - add_custom_target(copy_gui ALL - DEPENDS lokinet lokinet-gui - # FIXME: we really shouldn't be building inside the source directory but this is npm... - COMMAND ${CMAKE_COMMAND} -E copy_if_different + option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF) + if(GUI_ZIP_FILE) + message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}") + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + else() + add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" + DEPENDS lokinet lokinet-gui + # FIXME: we really shouldn't be building inside the source directory but this is npm... + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" - ) + ) + add_custom_target(assemble_gui ALL + DEPENDS ${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe) + endif() else() message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform") endif() - else() - message(STATUS "Not building lokinet-gui") + message(STATUS "not building gui") endif() From 871c3e3281d15ce876fcd85a02228b4e70453bac Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 28 Jul 2022 12:07:38 -0400 Subject: [PATCH 07/71] changeset for windows port * wintun vpn platform for windows * bundle config snippets into nsis installer for exit node, keyfile persisting, reduced hops mode. * use wintun for vpn platform * isolate all windows platform specific code into their own compilation units and libraries * split up internal libraries into more specific components * rename liblokinet.a target to liblokinet-amalgum.a to elimiate ambiguity with liblokinet.so * DNS platform for win32 * rename llarp/ev/ev_libuv.{c,h}pp to llarp/ev/libuv.{c,h}pp as the old name was idiotic * split up net platform into win32 and posix specific compilation units * rename lokinet_init.c to easter_eggs.cpp as that is what they are for and it does not need to be a c compilation target * add cmake option STRIP_SYMBOLS for seperating out debug symbols for windows builds * intercept dns traffic on all interfaces on windows using windivert and feed it into lokinet --- .drone.jsonnet | 2 +- CMakeLists.txt | 32 +- cmake/enable_lto.cmake | 3 + cmake/gui.cmake | 54 +- cmake/installer.cmake | 35 +- cmake/win32.cmake | 60 +- cmake/win32_installer_deps.cmake | 29 +- contrib/android-configure.sh | 1 + contrib/ci/drone-static-upload.sh | 11 +- contrib/configs/00-debug-log.ini | 2 + contrib/configs/00-exit.ini | 5 + contrib/configs/00-keyfile.ini | 5 + contrib/windows.sh | 9 +- daemon/CMakeLists.txt | 24 +- daemon/lokinet.cpp | 12 +- jni/CMakeLists.txt | 2 +- llarp/CMakeLists.txt | 99 ++- llarp/apple/CMakeLists.txt | 12 +- llarp/apple/vpn_interface.hpp | 2 +- llarp/config/config.cpp | 29 +- llarp/config/config.hpp | 3 + llarp/constants/net.hpp | 7 + llarp/constants/platform.hpp | 31 +- llarp/dns/multi_platform.hpp | 21 - llarp/dns/nm_platform.cpp | 2 +- llarp/dns/nm_platform.hpp | 2 +- llarp/dns/null_platform.hpp | 13 - .../dns/{multi_platform.cpp => platform.cpp} | 6 +- llarp/dns/platform.hpp | 37 +- llarp/dns/sd_platform.cpp | 8 +- llarp/dns/sd_platform.hpp | 2 +- llarp/dns/server.cpp | 104 ++- llarp/dns/server.hpp | 74 +- llarp/dns/win32_platform.cpp | 51 ++ llarp/dns/win32_platform.hpp | 8 + llarp/ev/ev.cpp | 11 +- llarp/ev/ev.hpp | 9 +- llarp/ev/{ev_libuv.cpp => libuv.cpp} | 16 +- llarp/ev/{ev_libuv.hpp => libuv.hpp} | 0 llarp/exit/endpoint.cpp | 45 +- llarp/exit/endpoint.hpp | 6 +- llarp/exit/session.cpp | 27 +- llarp/handlers/exit.cpp | 63 +- llarp/handlers/exit.hpp | 3 +- llarp/handlers/null.hpp | 2 +- llarp/handlers/tun.cpp | 241 +++--- llarp/handlers/tun.hpp | 21 +- llarp/lokinet_shared.cpp | 2 +- llarp/net/address_info.hpp | 12 +- llarp/net/bogon_ranges.hpp | 36 + llarp/net/interface_info.hpp | 21 + llarp/net/ip_address.cpp | 2 +- llarp/net/ip_address.hpp | 60 +- llarp/net/ip_packet.cpp | 216 +++--- llarp/net/ip_packet.hpp | 144 +++- llarp/net/ip_range.cpp | 11 + llarp/net/ip_range.hpp | 37 +- llarp/net/net.hpp | 178 ++++- llarp/net/net_bits.hpp | 8 - llarp/net/{net.cpp => posix.cpp} | 256 ++----- llarp/net/sock_addr.cpp | 26 +- llarp/net/sock_addr.hpp | 13 +- llarp/net/win32.cpp | 216 ++++++ llarp/path/path.cpp | 4 +- llarp/path/transit_hop.cpp | 8 +- llarp/quic/client.cpp | 2 +- llarp/quic/connection.cpp | 8 +- llarp/quic/endpoint.cpp | 2 +- llarp/quic/tunnel.cpp | 2 +- llarp/router/abstractrouter.hpp | 8 +- llarp/router/route_poker.cpp | 264 +++---- llarp/router/route_poker.hpp | 44 +- llarp/router/router.cpp | 76 +- llarp/router/router.hpp | 23 +- llarp/router_contact.cpp | 4 +- llarp/rpc/rpc_server.cpp | 8 +- llarp/service/endpoint.cpp | 9 +- llarp/service/endpoint.hpp | 9 + llarp/service/pendingbuffer.hpp | 34 +- llarp/service/sendcontext.cpp | 1 + llarp/util/buffer.cpp | 8 + llarp/util/buffer.hpp | 143 ++-- llarp/util/easter_eggs.cpp | 39 + llarp/util/lokinet_init.c | 35 - llarp/util/lokinet_init.h | 9 - llarp/util/str.cpp | 24 + llarp/util/str.hpp | 5 +- llarp/vpn/android.hpp | 42 +- llarp/vpn/i_packet_io.hpp | 35 + llarp/vpn/linux.hpp | 138 ++-- llarp/vpn/packet_intercept.hpp | 27 + llarp/vpn/packet_router.cpp | 33 +- llarp/vpn/packet_router.hpp | 30 +- llarp/vpn/platform.cpp | 4 +- llarp/{ev/vpn.hpp => vpn/platform.hpp} | 93 ++- llarp/vpn/win32.hpp | 687 +++--------------- llarp/win32/dll.cpp | 22 + llarp/win32/dll.hpp | 30 + llarp/win32/exception.cpp | 39 + llarp/win32/exception.hpp | 25 +- llarp/win32/exec.cpp | 57 ++ llarp/win32/exec.hpp | 46 ++ llarp/win32/guid.hpp | 27 + llarp/win32/handle.hpp | 19 + llarp/win32/windivert.cpp | 194 +++++ llarp/win32/windivert.hpp | 30 + llarp/win32/wintun.cpp | 411 +++++++++++ llarp/win32/wintun.hpp | 34 + pybind/CMakeLists.txt | 2 +- pybind/llarp/config.cpp | 2 +- test/CMakeLists.txt | 11 +- test/mocks/mock_network.hpp | 54 +- test/mocks/mock_vpn.hpp | 20 +- test/net/test_llarp_net.cpp | 26 +- win32-setup/extra_install.nsis | 12 +- win32-setup/extra_preinstall.nsis | 4 +- win32-setup/extra_uninstall.nsis | 12 +- 117 files changed, 3338 insertions(+), 2076 deletions(-) create mode 100644 contrib/configs/00-debug-log.ini create mode 100644 contrib/configs/00-exit.ini create mode 100644 contrib/configs/00-keyfile.ini create mode 100644 llarp/constants/net.hpp rename llarp/dns/{multi_platform.cpp => platform.cpp} (78%) create mode 100644 llarp/dns/win32_platform.cpp create mode 100644 llarp/dns/win32_platform.hpp rename llarp/ev/{ev_libuv.cpp => libuv.cpp} (97%) rename llarp/ev/{ev_libuv.hpp => libuv.hpp} (100%) create mode 100644 llarp/net/bogon_ranges.hpp create mode 100644 llarp/net/interface_info.hpp rename llarp/net/{net.cpp => posix.cpp} (67%) create mode 100644 llarp/net/win32.cpp create mode 100644 llarp/util/easter_eggs.cpp delete mode 100644 llarp/util/lokinet_init.c create mode 100644 llarp/vpn/i_packet_io.hpp create mode 100644 llarp/vpn/packet_intercept.hpp rename llarp/{ev/vpn.hpp => vpn/platform.hpp} (58%) create mode 100644 llarp/win32/dll.cpp create mode 100644 llarp/win32/dll.hpp create mode 100644 llarp/win32/exception.cpp create mode 100644 llarp/win32/exec.cpp create mode 100644 llarp/win32/exec.hpp create mode 100644 llarp/win32/guid.hpp create mode 100644 llarp/win32/handle.hpp create mode 100644 llarp/win32/windivert.cpp create mode 100644 llarp/win32/windivert.hpp create mode 100644 llarp/win32/wintun.cpp create mode 100644 llarp/win32/wintun.hpp diff --git a/.drone.jsonnet b/.drone.jsonnet index 2ec9ac4aa..d03f1baec 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -146,7 +146,7 @@ local windows_cross_pipeline(name, 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', - 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh ' + ci_mirror_opts, + 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, ] + extra_cmds, }, ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 8498e480e..1496b2a25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ option(WITH_HIVE "build simulation stubs" OFF) option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF) option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP}) option(WITH_PEERSTATS "build with experimental peerstats db support" OFF) +option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF) + include(cmake/enable_lto.cmake) @@ -81,6 +83,12 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() +set(debug OFF) +if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") + set(debug ON) + add_definitions(-DLOKINET_DEBUG) +endif() + if(BUILD_STATIC_DEPS AND STATIC_LINK) message(STATUS "we are building static deps so we won't build shared libs") set(BUILD_SHARED_LIBS OFF CACHE BOOL "") @@ -171,11 +179,18 @@ if(NOT TARGET sodium) export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake) endif() -if(NOT APPLE) - add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla) +if(APPLE) + add_compile_options(-Wno-deprecated-declarations) +else() + add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-unknown-warning-option) endif() + if(debug) + add_compile_options(-Wdeprecated-declarations) + else() + add_compile_options(-Wno-deprecated-declarations) + endif() endif() if(XSAN) @@ -186,11 +201,6 @@ if(XSAN) message(STATUS "Doing a ${XSAN} sanitizer build") endif() -if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") - add_definitions(-DLOKINET_DEBUG) -endif() - - include(cmake/coverage.cmake) # these vars are set by the cmake toolchain spec @@ -317,6 +327,10 @@ if(NOT TARGET uninstall) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() -if(BUILD_PACKAGE) - include(cmake/installer.cmake) +if(BUILD_PACKAGE AND NOT APPLE) + include(cmake/installer.cmake) +endif() + +if(TARGET package) + add_dependencies(package assemble_gui) endif() diff --git a/cmake/enable_lto.cmake b/cmake/enable_lto.cmake index c315c6cff..dd81906bb 100644 --- a/cmake/enable_lto.cmake +++ b/cmake/enable_lto.cmake @@ -2,6 +2,9 @@ include(CheckIPOSupported) option(WITH_LTO "enable lto on compile time" ON) if(WITH_LTO) + if(WIN32) + message(FATAL_ERROR "LTO not supported on win32 targets, please set -DWITH_LTO=OFF") + endif() check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error) if(IPO_ENABLED) message(STATUS "LTO enabled") diff --git a/cmake/gui.cmake b/cmake/gui.cmake index be27a9688..fcb81b2fd 100644 --- a/cmake/gui.cmake +++ b/cmake/gui.cmake @@ -8,25 +8,30 @@ elseif(WIN32) set(default_gui_target win32) endif() +if(WIN32) + option(GUI_EXE "path to an externally built lokinet gui.exe" OFF) +endif() + option(BUILD_GUI "build electron gui from 'gui' submodule source" ${default_build_gui}) set(GUI_YARN_TARGET "${default_gui_target}" CACHE STRING "yarn target for building the GUI") set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command") + if (BUILD_GUI) message(STATUS "Building lokinet-gui") + # allow manually specifying yarn with -DYARN= if(NOT YARN) - find_program(YARN NAMES yarn yarnpkg REQUIRED) + find_program(YARN NAMES yarnpkg yarn REQUIRED) endif() message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}") - set(wine_env) - if(WIN32) - set(wine_env USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix") - endif() - add_custom_target(lokinet-gui - COMMAND ${YARN} install --frozen-lockfile && - ${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") + + if(NOT WIN32) + add_custom_target(lokinet-gui + COMMAND ${YARN} install --frozen-lockfile && + ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") + endif() if(APPLE) add_custom_target(assemble_gui ALL @@ -38,14 +43,13 @@ if (BUILD_GUI) COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns" COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/" COMMAND /usr/libexec/PlistBuddy - -c "Delete :CFBundleDisplayName" - -c "Add :LSHasLocalizedDisplayName bool true" - -c "Add :CFBundleDevelopmentRegion string en" - -c "Set :CFBundleShortVersionString ${lokinet_VERSION}" - -c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}" - "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist" + -c "Delete :CFBundleDisplayName" + -c "Add :LSHasLocalizedDisplayName bool true" + -c "Add :CFBundleDevelopmentRegion string en" + -c "Set :CFBundleShortVersionString ${lokinet_VERSION}" + -c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}" + "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist" ) - elseif(WIN32) file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui") option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF) @@ -53,20 +57,28 @@ if (BUILD_GUI) message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}") execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE} WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true") + elseif(GUI_EXE) + message(STATUS "using custom lokinet gui executable: ${GUI_EXE}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GUI_EXE}" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe") + add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true") else() add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" - DEPENDS lokinet lokinet-gui - # FIXME: we really shouldn't be building inside the source directory but this is npm... + COMMAND ${YARN} install --frozen-lockfile && + USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all WINEPREFIX="${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" - ) - add_custom_target(assemble_gui ALL - DEPENDS ${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe) + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") endif() + add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe") else() message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform") endif() else() message(STATUS "not building gui") endif() + +if(NOT TARGET assemble_gui) + add_custom_target(assemble_gui COMMAND "true") +endif() diff --git a/cmake/installer.cmake b/cmake/installer.cmake index 60e39f69b..503fd15dd 100644 --- a/cmake/installer.cmake +++ b/cmake/installer.cmake @@ -5,11 +5,42 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") if(WIN32) include(cmake/win32_installer_deps.cmake) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-exit.ini DESTINATION share/conf.d COMPONENT exit_configs) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-keyfile.ini DESTINATION share/conf.d COMPONENT keyfile_configs) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-debug-log.ini DESTINATION share/conf.d COMPONENT debug_configs) + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) + list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified" "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs") + list(APPEND CPACK_COMPONENTS_ALL "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs") elseif(APPLE) set(CPACK_GENERATOR DragNDrop;ZIP) + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) + list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") endif() - -# This must always be last! include(CPack) +if(WIN32) + cpack_add_component(lokinet + DISPLAY_NAME "lokinet" + DESCRIPTION "core required lokinet files" + REQUIRED) + + cpack_add_component(gui + DISPLAY_NAME "lokinet gui" + DESCRIPTION "electron based control panel for lokinet") + + cpack_add_component(exit_configs + DISPLAY_NAME "auto-enable exit" + DESCRIPTION "automatically enable usage of exit.loki as an exit node\n" + DISABLED) + + cpack_add_component(keyfile_configs + DISPLAY_NAME "persist address" + DESCRIPTION "persist .loki address across restarts of lokinet\nnot recommended when enabling exit nodes" + DISABLED) + + cpack_add_component(debug_configs + DISPLAY_NAME "debug logging" + DESCRIPTION "enable debug spew log level by default" + DISABLED) +endif() diff --git a/cmake/win32.cmake b/cmake/win32.cmake index 73cda29b3..ae3bdb6d2 100644 --- a/cmake/win32.cmake +++ b/cmake/win32.cmake @@ -1,32 +1,52 @@ if(NOT WIN32) return() endif() +if (NOT STATIC_LINK) + message(FATAL_ERROR "windows requires static builds (thanks balmer)") +endif() enable_language(RC) -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -if(NOT MSVC_VERSION) - add_compile_options($<$:-Wno-bad-function-cast>) - add_compile_options($<$:-Wno-cast-function-type>) - add_compile_options($<$:-fpermissive>) - # unlike unix where you get a *single* compiler ID string in .comment - # GNU ld sees fit to merge *all* the .ident sections in object files - # to .r[o]data section one after the other! - add_compile_options(-fno-ident -Wa,-mbig-obj) - link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv) - # the minimum windows version, set to 6 rn because supporting older windows is hell - set(_winver 0x0600) - add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver}) -endif() +option(WITH_WINDOWS_32 "build 32 bit windows" OFF) + +# unlike unix where you get a *single* compiler ID string in .comment +# GNU ld sees fit to merge *all* the .ident sections in object files +# to .r[o]data section one after the other! +add_compile_options(-fno-ident -Wa,-mbig-obj) +# the minimum windows version, set to 6 rn because supporting older windows is hell +set(_winver 0x0600) +add_definitions(-D_WIN32_WINNT=${_winver}) if(EMBEDDED_CFG) link_libatomic() endif() -add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32) +set(WINTUN_VERSION 0.14.1 CACHE STRING "wintun version") +set(WINTUN_MIRROR https://www.wintun.net/builds + CACHE STRING "wintun mirror(s)") +set(WINTUN_SOURCE wintun-${WINTUN_VERSION}.zip) +set(WINTUN_HASH SHA256=07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51 + CACHE STRING "wintun source hash") -if (NOT STATIC_LINK AND NOT MSVC) - message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll") - message("for release builds, turn on STATIC_LINK in cmake options") -endif() +set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version") +set(WINDIVERT_MIRROR https://reqrypt.org/download + CACHE STRING "windivert mirror(s)") +set(WINDIVERT_SOURCE WinDivert-${WINDIVERT_VERSION}.zip) +set(WINDIVERT_HASH SHA256=2a7630aac0914746fbc565ac862fa096e3e54233883ac52d17c83107496b7a7f + CACHE STRING "windivert source hash") + +set(WINTUN_URL ${WINTUN_MIRROR}/${WINTUN_SOURCE} + CACHE STRING "wintun download url") +set(WINDIVERT_URL ${WINDIVERT_MIRROR}/${WINDIVERT_SOURCE} + CACHE STRING "windivert download url") + +message(STATUS "Downloading wintun from ${WINTUN_URL}") +file(DOWNLOAD ${WINTUN_URL} ${CMAKE_BINARY_DIR}/wintun.zip EXPECTED_HASH ${WINTUN_HASH}) +message(STATUS "Downloading windivert from ${WINDIVERT_URL}") +file(DOWNLOAD ${WINDIVERT_URL} ${CMAKE_BINARY_DIR}/windivert.zip EXPECTED_HASH ${WINDIVERT_HASH}) + +execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/wintun.zip + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/windivert.zip + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cmake/win32_installer_deps.cmake b/cmake/win32_installer_deps.cmake index f20034212..4461c5ec4 100644 --- a/cmake/win32_installer_deps.cmake +++ b/cmake/win32_installer_deps.cmake @@ -1,11 +1,3 @@ -set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe") -set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe") -set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed") - -file(DOWNLOAD - ${TUNTAP_URL} - ${TUNTAP_EXE}) - if(NOT BUILD_GUI) if(NOT GUI_ZIP_URL) set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip") @@ -25,9 +17,19 @@ if(NOT BUILD_GUI) message(FATAL_ERROR "Downloaded gui archive from ${GUI_ZIP_URL} does not contain gui/lokinet-gui.exe!") endif() endif() - install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui) -install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap) + +if(WITH_WINDOWS_32) + install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet) +else() + install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet) +endif() + +set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed") install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed) set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") @@ -35,7 +37,6 @@ set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico") set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin") set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) - function(read_nsis_file filename outvar) file(STRINGS "${filename}" _outvar) list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\") @@ -51,9 +52,9 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_ set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}") -set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") +set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}") set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}") +set(CPACK_NSIS_MODIFY_PATH ON) -get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) -list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") +set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") diff --git a/contrib/android-configure.sh b/contrib/android-configure.sh index 2d669cae2..637bbe7ba 100755 --- a/contrib/android-configure.sh +++ b/contrib/android-configure.sh @@ -34,6 +34,7 @@ for abi in $build_abis; do -DBUILD_TESTING=OFF \ -DBUILD_LIBLOKINET=OFF \ -DWITH_TESTS=OFF \ + -DWITH_BOOTSTRAP=OFF \ -DNATIVE_BUILD=OFF \ -DSTATIC_LINK=ON \ -DWITH_SYSTEMD=OFF \ diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index 6180b516f..80a87d5a9 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -34,8 +34,11 @@ else fi mkdir -v "$base" -if [ -e build-windows ]; then - cp -av build-windows/lokinet-*.exe "$base" +if [ -e build/win32 ]; then + # save debug symbols + cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz" + # save installer + cp -av build/win32/*.exe "$base" # zipit up yo archive="$base.zip" zip -r "$archive" "$base" @@ -77,6 +80,10 @@ $mkdirs put $archive $upload_to SFTP +if [ -e "$base-debug-symbols.tar.xz" ] ; then + sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<<"put $base-debug-symbols.tar.xz $upload_to" +fi + set +o xtrace echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n" diff --git a/contrib/configs/00-debug-log.ini b/contrib/configs/00-debug-log.ini new file mode 100644 index 000000000..73dd5bee6 --- /dev/null +++ b/contrib/configs/00-debug-log.ini @@ -0,0 +1,2 @@ +[logging] +level=debug \ No newline at end of file diff --git a/contrib/configs/00-exit.ini b/contrib/configs/00-exit.ini new file mode 100644 index 000000000..f9f2b8550 --- /dev/null +++ b/contrib/configs/00-exit.ini @@ -0,0 +1,5 @@ +# +# "suggested" default exit node config +# +[network] +exit-node=exit.loki \ No newline at end of file diff --git a/contrib/configs/00-keyfile.ini b/contrib/configs/00-keyfile.ini new file mode 100644 index 000000000..cfdf515e2 --- /dev/null +++ b/contrib/configs/00-keyfile.ini @@ -0,0 +1,5 @@ +# +# persist .loki address in a private key file in the data dir +# +[network] +keyfile=lokinet-addr.privkey \ No newline at end of file diff --git a/contrib/windows.sh b/contrib/windows.sh index ff380aebd..d06adab84 100755 --- a/contrib/windows.sh +++ b/contrib/windows.sh @@ -6,9 +6,8 @@ set -e set +x - - root="$(readlink -f $(dirname $0)/../)" -cd "$root" -./contrib/windows-configure.sh . build-windows "$@" -make package -j${JOBS:-$(nproc)} -C build-windows +mkdir -p $root/build/win32 +$root/contrib/windows-configure.sh $root $root/build/win32 "$@" +make package -j${JOBS:-$(nproc)} -C $root/build/win32 + diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index db864b57e..5372fe46d 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -46,15 +46,27 @@ if(WITH_BOOTSTRAP) endif() endif() +# cmake interface library for bunch of cmake hacks to fix final link order +add_library(hax_and_shims_for_cmake INTERFACE) +if(WIN32) + target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv) +endif() + foreach(exe ${exetargets}) - if(WIN32 AND NOT MSVC_VERSION) + if(WIN32) target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) + target_compile_options(${exe} PRIVATE $<$:-fpermissive>) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) - target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_link_directories(${exe} PRIVATE /usr/local/lib) endif() - target_link_libraries(${exe} PUBLIC liblokinet) + target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake) + if(STRIP_SYMBOLS) + add_custom_command(TARGET ${exe} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $ $.debug + COMMAND ${CMAKE_STRIP} ARGS --strip-all $) + endif() target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}") if(should_install) if(APPLE) @@ -71,3 +83,9 @@ endforeach() if(SETCAP) install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)") endif() + +if(STRIP_SYMBOLS) + add_custom_target(symbols ALL + COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $.debug + DEPENDS lokinet) +endif() diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 53655c860..8ff06fdfd 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -101,7 +101,7 @@ install_win32_daemon() // Create the service schService = CreateService( schSCManager, // SCM database - "lokinet", // name of service + strdup("lokinet"), // name of service "Lokinet for Windows", // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type @@ -134,10 +134,10 @@ insert_description() SC_HANDLE schSCManager; SC_HANDLE schService; SERVICE_DESCRIPTION sd; - LPTSTR szDesc = + LPTSTR szDesc = strdup( "LokiNET is a free, open source, private, " "decentralized, \"market based sybil resistant\" " - "and IP based onion routing network"; + "and IP based onion routing network"); // Get a handle to the SCM database. schSCManager = OpenSCManager( NULL, // local computer @@ -270,7 +270,7 @@ run_main_context(std::optional confFile, const llarp::RuntimeOptions o } catch (std::exception& ex) { - llarp::LogError("failed to start up lokinet: {}", ex.what()); + llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what())); exit_code.set_value(1); return; } @@ -367,7 +367,7 @@ main(int argc, char* argv[]) return lokinet_main(argc, argv); #else SERVICE_TABLE_ENTRY DispatchTable[] = { - {"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; + {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; if (lstrcmpi(argv[1], "--win32-daemon") == 0) { start_as_daemon = true; @@ -680,7 +680,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv) ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); // SCM clobbers startup args, regenerate them here argc = 2; - argv[1] = "c:/programdata/lokinet/lokinet.ini"; + argv[1] = strdup("c:/programdata/lokinet/lokinet.ini"); argv[2] = nullptr; lokinet_main(argc, argv); } diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt index 000e25aa0..ef24506a3 100644 --- a/jni/CMakeLists.txt +++ b/jni/CMakeLists.txt @@ -2,4 +2,4 @@ add_library(lokinet-android SHARED lokinet_config.cpp lokinet_daemon.cpp) -target_link_libraries(lokinet-android liblokinet) +target_link_libraries(lokinet-android lokinet-amalgum) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index f80bdb77c..f033721d5 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -8,7 +8,7 @@ add_library(lokinet-util util/fs.cpp util/json.cpp util/logging/buffer.cpp - util/lokinet_init.c + util/easter_eggs.cpp util/mem.cpp util/str.cpp util/thread/queue_manager.cpp @@ -32,12 +32,11 @@ add_library(lokinet-platform STATIC # for networking ev/ev.cpp - ev/ev_libuv.cpp + ev/libuv.cpp net/ip.cpp net/ip_address.cpp net/ip_packet.cpp net/ip_range.cpp - net/net.cpp net/net_int.cpp net/sock_addr.cpp vpn/packet_router.cpp @@ -58,34 +57,66 @@ endif() if (WIN32) target_sources(lokinet-platform PRIVATE - win32/win32_inet.c - win32/win32_intrnl.c) - - target_link_libraries(lokinet-platform PUBLIC iphlpapi) + net/win32.cpp + win32/exec.cpp) + add_library(lokinet-win32 STATIC + win32/dll.cpp + win32/exception.cpp) + add_library(lokinet-wintun STATIC + win32/wintun.cpp) + add_library(lokinet-windivert STATIC + win32/windivert.cpp) + + # wintun and windivert are privated linked by lokinet-platform + # this is so their details do not leak out to deps of lokinet-platform + # wintun and windivert still need things from lokinet-platform + target_compile_options(lokinet-wintun PUBLIC -I${CMAKE_BINARY_DIR}/wintun/include/) + target_compile_options(lokinet-windivert PUBLIC -I${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/include/) + target_include_directories(lokinet-windivert PUBLIC ${PROJECT_SOURCE_DIR}) + target_link_libraries(lokinet-wintun PUBLIC lokinet-platform lokinet-util lokinet-config) + target_link_libraries(lokinet-win32 PUBLIC lokinet-util) + target_link_libraries(lokinet-windivert PUBLIC oxen-logging) + target_link_libraries(lokinet-windivert PRIVATE lokinet-win32) + target_link_libraries(lokinet-platform PRIVATE lokinet-win32 lokinet-wintun lokinet-windivert) +else() + target_sources(lokinet-platform PRIVATE + net/posix.cpp) endif() + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include) endif() -add_library(liblokinet +add_library(lokinet-dns STATIC - config/config.cpp - config/definition.cpp - config/ini.cpp - config/key_manager.cpp - dns/message.cpp dns/name.cpp - dns/multi_platform.cpp - dns/nm_platform.cpp - dns/sd_platform.cpp + dns/platform.cpp dns/question.cpp dns/rr.cpp dns/serialize.cpp dns/server.cpp - dns/srv_data.cpp + dns/srv_data.cpp) + +if(WITH_SYSTEMD) + target_sources(lokinet-dns PRIVATE dns/nm_platform.cpp dns/sd_platform.cpp) +endif() + +target_link_libraries(lokinet-dns PUBLIC lokinet-platform uvw) +target_link_libraries(lokinet-dns PRIVATE libunbound lokinet-config) + +add_library(lokinet-config + STATIC + config/config.cpp + config/definition.cpp + config/ini.cpp + config/key_manager.cpp) +target_link_libraries(lokinet-config PUBLIC lokinet-dns lokinet-platform oxenmq::oxenmq) + +add_library(lokinet-amalgum + STATIC consensus/table.cpp consensus/reachability_testing.cpp @@ -115,7 +146,7 @@ add_library(liblokinet dht/taglookup.cpp endpoint_base.cpp - + exit/context.cpp exit/endpoint.cpp exit/exit_messages.cpp @@ -170,7 +201,7 @@ add_library(liblokinet router/rc_gossiper.cpp router/router.cpp router/route_poker.cpp - + routing/dht_message.cpp routing/message_parser.cpp routing/path_confirm_message.cpp @@ -204,42 +235,42 @@ add_library(liblokinet service/tag.cpp ) -if(WITH_PEERSTATS_BACKEND) - target_compile_definitions(liblokinet PRIVATE -DLOKINET_PEERSTATS_BACKEND) - target_link_libraries(liblokinet PUBLIC sqlite_orm) -endif() - -set_target_properties(liblokinet PROPERTIES OUTPUT_NAME lokinet) -enable_lto(lokinet-util lokinet-platform liblokinet) - -if(TRACY_ROOT) - target_sources(liblokinet PRIVATE ${TRACY_ROOT}/TracyClient.cpp) +if(WITH_PEERSTATS_BACKEND) + target_compile_definitions(lokinet-amalgum PRIVATE -DLOKINET_PEERSTATS_BACKEND) + target_link_libraries(lokinet-amalgum PUBLIC sqlite_orm) endif() if(WITH_HIVE) - target_sources(liblokinet PRIVATE + target_sources(lokinet-amalgum PRIVATE tooling/router_hive.cpp tooling/hive_router.cpp tooling/hive_context.cpp ) endif() -target_link_libraries(liblokinet PUBLIC +# TODO: make libunbound hidden behind a feature flag like sqlite for embedded lokinet +target_link_libraries(lokinet-amalgum PRIVATE libunbound) + +target_link_libraries(lokinet-amalgum PUBLIC cxxopts oxenc::oxenc lokinet-platform + lokinet-config + lokinet-dns lokinet-util lokinet-cryptography ngtcp2_static oxenmq::oxenmq) -target_link_libraries(liblokinet PRIVATE libunbound) + +enable_lto(lokinet-util lokinet-platform lokinet-dns lokinet-config lokinet-amalgum) + pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET) if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING) add_definitions(-DHAVE_CRYPT) add_library(libcrypt INTERFACE) target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT) - target_link_libraries(liblokinet PRIVATE libcrypt) + target_link_libraries(lokinet-amalgum PRIVATE libcrypt) message(STATUS "using libcrypt ${CRYPT_VERSION}") endif() @@ -247,7 +278,7 @@ endif() if(BUILD_LIBLOKINET) include(GNUInstallDirs) add_library(lokinet-shared SHARED lokinet_shared.cpp) - target_link_libraries(lokinet-shared PUBLIC liblokinet) + target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum) if(WIN32) set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "") endif() diff --git a/llarp/apple/CMakeLists.txt b/llarp/apple/CMakeLists.txt index 71d6c6651..8dd561ef7 100644 --- a/llarp/apple/CMakeLists.txt +++ b/llarp/apple/CMakeLists.txt @@ -18,12 +18,9 @@ target_sources(lokinet-platform PRIVATE vpn_platform.cpp vpn_interface.cpp route add_executable(lokinet-extension MACOSX_BUNDLE PacketTunnelProvider.m DNSTrampoline.m - ) +) + enable_lto(lokinet-extension) -target_link_libraries(lokinet-extension PRIVATE - liblokinet - ${COREFOUNDATION} - ${NETEXT}) # -fobjc-arc enables automatic reference counting for objective-C code # -e _NSExtensionMain because the appex has that instead of a `main` function entry point, of course. @@ -43,6 +40,11 @@ else() set(product_type com.apple.product-type.app-extension) endif() +target_link_libraries(lokinet-extension PRIVATE + lokinet-amalgum + ${COREFOUNDATION} + ${NETEXT}) + set_target_properties(lokinet-extension PROPERTIES BUNDLE TRUE BUNDLE_EXTENSION ${bundle_ext} diff --git a/llarp/apple/vpn_interface.hpp b/llarp/apple/vpn_interface.hpp index 762227f91..07b4de5ab 100644 --- a/llarp/apple/vpn_interface.hpp +++ b/llarp/apple/vpn_interface.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 787fcdd83..195b96f4d 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -140,14 +140,14 @@ namespace llarp "this setting specifies the public IP at which this router is reachable. When", "provided the public-port option must also be specified.", }, - [this](std::string arg) { + [this, net = params.Net_ptr()](std::string arg) { if (arg.empty()) return; nuint32_t addr{}; if (not addr.FromString(arg)) throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", arg)}; - if (IsIPv4Bogon(addr)) + if (net->IsBogonIP(addr)) throw std::invalid_argument{ fmt::format("{} is not a publicly routable ip address", addr)}; @@ -648,6 +648,7 @@ namespace llarp throw std::invalid_argument{ fmt::format("[network]:ip6-range invalid value: '{}'", arg)}; }); + // TODO: could be useful for snodes in the future, but currently only implemented for clients: conf.defineOption( "network", @@ -813,6 +814,30 @@ namespace llarp } }); + conf.defineOption( + "dns", + "l3-intercept", + Default{ + platform::is_windows or platform::is_android + or (platform::is_macos and not platform::is_apple_sysex)}, + Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface " + "instead of binding a local udp socket"}, + AssignmentAcceptor(m_raw_dns)); + + conf.defineOption( + "dns", + "query-bind", +#ifdef __APPLE__ + Default{"127.0.0.1:1253"}, +#endif +#ifdef _WIN32 + Default{"0.0.0.0:0"}, +#endif + Comment{ + "Address to bind to for sending upstream DNS requests.", + }, + [this](std::string arg) { m_QueryBind = SockAddr{arg}; }); + conf.defineOption( "dns", "bind", diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 198599dab..54dbcc443 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -155,9 +155,12 @@ namespace llarp struct DnsConfig { + bool m_raw_dns; std::vector m_bind; std::vector m_upstreamDNS; std::vector m_hostfiles; + std::optional m_QueryBind; + std::unordered_multimap m_ExtraOpts; void diff --git a/llarp/constants/net.hpp b/llarp/constants/net.hpp new file mode 100644 index 000000000..9b6c6c4b3 --- /dev/null +++ b/llarp/constants/net.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace llarp::constants +{ + constexpr auto udp_header_bytes = 8; + constexpr auto ip_header_min_bytes = 20; +} // namespace llarp::constants diff --git a/llarp/constants/platform.hpp b/llarp/constants/platform.hpp index 6ca98f655..64b48c67e 100644 --- a/llarp/constants/platform.hpp +++ b/llarp/constants/platform.hpp @@ -11,8 +11,9 @@ namespace llarp::platform false #endif ; - /// we have systemd ? - inline constexpr bool has_systemd = + + /// building with systemd enabled ? + inline constexpr bool with_systemd = #ifdef WITH_SYSTEMD true #else @@ -47,6 +48,15 @@ namespace llarp::platform #endif ; + /// are we building as an apple system extension + inline constexpr bool is_apple_sysex = +#ifdef MACOS_SYSTEM_EXTENSION + true +#else + false +#endif + ; + /// are we an android platform ? inline constexpr bool is_android = #ifdef ANDROID @@ -65,9 +75,26 @@ namespace llarp::platform #endif ; + /// are we running with pybind simulation mode enabled? + inline constexpr bool is_simulation = +#ifdef LOKINET_HIVE + true +#else + false +#endif + ; + /// do we have systemd support ? + // on cross compiles sometimes weird permutations of target and host make this value not correct, + // this ensures it always is + inline constexpr bool has_systemd = is_linux and with_systemd and not(is_android or is_windows); + + /// are we using macos ? + inline constexpr bool is_macos = is_apple and not is_iphone; + /// are we a mobile phone ? inline constexpr bool is_mobile = is_android or is_iphone; /// does this platform support native ipv6 ? + // TODO: make windows support ipv6 inline constexpr bool supports_ipv6 = not is_windows; } // namespace llarp::platform diff --git a/llarp/dns/multi_platform.hpp b/llarp/dns/multi_platform.hpp index 8f05d7ed7..8b1378917 100644 --- a/llarp/dns/multi_platform.hpp +++ b/llarp/dns/multi_platform.hpp @@ -1,22 +1 @@ -#pragma once -#include "platform.hpp" -#include - -namespace llarp::dns -{ - /// a collection of dns platforms that are tried in order when setting dns - class Multi_Platform : public I_Platform - { - std::vector> m_Impls; - - public: - /// add a platform to be owned - void - add_impl(std::unique_ptr impl); - - /// try all owned platforms to set the resolver, throws if none of them work - void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; - }; -} // namespace llarp::dns diff --git a/llarp/dns/nm_platform.cpp b/llarp/dns/nm_platform.cpp index 79d2a0fc3..30fa981d8 100644 --- a/llarp/dns/nm_platform.cpp +++ b/llarp/dns/nm_platform.cpp @@ -13,7 +13,7 @@ using namespace std::literals; namespace llarp::dns::nm { void - Platform::set_resolver(std::string, llarp::SockAddr, bool) + Platform::set_resolver(unsigned int, llarp::SockAddr, bool) { // todo: implement me eventually } diff --git a/llarp/dns/nm_platform.hpp b/llarp/dns/nm_platform.hpp index e41e858cb..742b050c6 100644 --- a/llarp/dns/nm_platform.hpp +++ b/llarp/dns/nm_platform.hpp @@ -17,7 +17,7 @@ namespace llarp::dns virtual ~Platform() = default; void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + set_resolver(unsigned int index, llarp::SockAddr dns, bool global) override; }; }; // namespace nm using NM_Platform_t = std::conditional_t; diff --git a/llarp/dns/null_platform.hpp b/llarp/dns/null_platform.hpp index 2b31ee8a2..8b1378917 100644 --- a/llarp/dns/null_platform.hpp +++ b/llarp/dns/null_platform.hpp @@ -1,14 +1 @@ -#pragma once -#include "platform.hpp" -namespace llarp::dns -{ - /// a dns platform does silently does nothing, successfully - class Null_Platform : public I_Platform - { - public: - void - set_resolver(std::string, llarp::SockAddr, bool) override - {} - }; -} // namespace llarp::dns diff --git a/llarp/dns/multi_platform.cpp b/llarp/dns/platform.cpp similarity index 78% rename from llarp/dns/multi_platform.cpp rename to llarp/dns/platform.cpp index bf4b0f887..c63e42683 100644 --- a/llarp/dns/multi_platform.cpp +++ b/llarp/dns/platform.cpp @@ -1,4 +1,4 @@ -#include "multi_platform.hpp" +#include "platform.hpp" namespace llarp::dns { @@ -9,14 +9,14 @@ namespace llarp::dns } void - Multi_Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + Multi_Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool global) { size_t fails{0}; for (const auto& ptr : m_Impls) { try { - ptr->set_resolver(ifname, dns, global); + ptr->set_resolver(index, dns, global); } catch (std::exception& ex) { diff --git a/llarp/dns/platform.hpp b/llarp/dns/platform.hpp index 25402db26..46fea1c13 100644 --- a/llarp/dns/platform.hpp +++ b/llarp/dns/platform.hpp @@ -3,9 +3,12 @@ #include #include #include - #include +#ifndef _WIN32 +#include +#endif + namespace llarp::dns { /// sets dns settings in a platform dependant way @@ -18,13 +21,39 @@ namespace llarp::dns /// throws if unsupported or fails. /// /// - /// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0. - /// Typically tun_endpoint.GetIfName(). + /// \param if_index -- the interface index to which we add the DNS servers, this can be gotten + /// from the interface name e.g. lokitun0 (Typically tun_endpoint.GetIfName().) and then put + /// through if_nametoindex(). /// \param dns -- the listening address of the lokinet DNS server /// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode /// addresses (false). virtual void - set_resolver(std::string if_name, llarp::SockAddr dns, bool global) = 0; + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) = 0; + }; + + /// a dns platform does silently does nothing, successfully + class Null_Platform : public I_Platform + { + public: + ~Null_Platform() override = default; + void + set_resolver(unsigned int, llarp::SockAddr, bool) override + {} }; + /// a collection of dns platforms that are tried in order when setting dns + class Multi_Platform : public I_Platform + { + std::vector> m_Impls; + + public: + ~Multi_Platform() override = default; + /// add a platform to be owned + void + add_impl(std::unique_ptr impl); + + /// try all owned platforms to set the resolver, throws if none of them work + void + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override; + }; } // namespace llarp::dns diff --git a/llarp/dns/sd_platform.cpp b/llarp/dns/sd_platform.cpp index f867441f1..1e4ae3d2b 100644 --- a/llarp/dns/sd_platform.cpp +++ b/llarp/dns/sd_platform.cpp @@ -13,14 +13,8 @@ using namespace std::literals; namespace llarp::dns::sd { void - Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + Platform::set_resolver(unsigned int if_ndx, llarp::SockAddr dns, bool global) { - unsigned int if_ndx = if_nametoindex(ifname.c_str()); - if (if_ndx == 0) - { - throw std::runtime_error{"No such interface '" + ifname + "'"}; - } - linux::DBUS _dbus{ "org.freedesktop.resolve1", "/org/freedesktop/resolve1", diff --git a/llarp/dns/sd_platform.hpp b/llarp/dns/sd_platform.hpp index 0c83c2557..532e73a15 100644 --- a/llarp/dns/sd_platform.hpp +++ b/llarp/dns/sd_platform.hpp @@ -16,7 +16,7 @@ namespace llarp::dns virtual ~Platform() = default; void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override; }; } // namespace sd using SD_Platform_t = diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index 0d652074f..41632e6e4 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -9,9 +9,9 @@ #include #include -#include "multi_platform.hpp" #include "sd_platform.hpp" #include "nm_platform.hpp" +#include "win32_platform.hpp" namespace llarp::dns { @@ -37,7 +37,7 @@ namespace llarp::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))) + if (not m_DNS.MaybeHandlePacket(shared_from_this(), m_LocalAddr, src, std::move(buf))) { LogWarn("did not handle dns packet from ", src, " to ", m_LocalAddr); } @@ -83,7 +83,7 @@ namespace llarp::dns class Query : public QueryJob_Base { std::weak_ptr parent; - std::weak_ptr src; + std::shared_ptr src; SockAddr resolverAddr; SockAddr askerAddr; @@ -91,7 +91,7 @@ namespace llarp::dns explicit Query( std::weak_ptr parent_, Message query, - std::weak_ptr pktsrc, + std::shared_ptr pktsrc, SockAddr toaddr, SockAddr fromaddr) : QueryJob_Base{std::move(query)} @@ -118,6 +118,8 @@ namespace llarp::dns std::shared_ptr m_Poller; #endif + std::optional m_LocalAddr; + struct ub_result_deleter { void @@ -127,6 +129,12 @@ namespace llarp::dns } }; + const net::Platform* + Net_ptr() const + { + return m_Loop.lock()->Net_ptr(); + } + static void Callback(void* data, int err, ub_result* _result) { @@ -186,6 +194,12 @@ namespace llarp::dns return "unbound"; } + virtual std::optional + GetLocalAddr() const override + { + return m_LocalAddr; + } + void Up(const llarp::DnsConfig& conf) { @@ -220,23 +234,39 @@ namespace llarp::dns 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") + } + + if (auto maybe_addr = conf.m_QueryBind) + { + SockAddr addr{*maybe_addr}; + std::string host{addr.hostString()}; + + if (addr.getPort() == 0) { - // 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"); + // unbound manages their own sockets because of COURSE it does. so we find an open port + // on our system and use it so we KNOW what it is before giving it to unbound to + // explicitly bind to JUST that port. + + addrinfo hints{}; + addrinfo* result{nullptr}; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_INET; + if (auto err = getaddrinfo(host.c_str(), nullptr, &hints, &result)) + throw std::invalid_argument{strerror(err)}; + addr.setPort(net::port_t{reinterpret_cast(result->ai_addr)->sin_port}); + freeaddrinfo(result); } -#endif + m_LocalAddr = addr; + + LogInfo(fmt::format("sening dns queries from {}:{}", host, addr.getPort())); + // set up query bind port if needed + SetOpt("outgoing-interface:", host); + SetOpt("outgoing-range:", "1"); + SetOpt("outgoing-port-avoid:", "0-65535"); + SetOpt("outgoing-port-permit:", std::to_string(addr.getPort())); } + // set async ub_ctx_async(m_ctx.get(), 1); // setup mainloop @@ -339,7 +369,7 @@ namespace llarp::dns bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to, const SockAddr& from) override @@ -388,13 +418,10 @@ namespace llarp::dns void Query::SendReply(llarp::OwnedBuffer replyBuf) const { - auto packet_src = src.lock(); - auto parent_ptr = parent.lock(); - - if (packet_src and parent_ptr) + if (auto ptr = parent.lock()) { - parent_ptr->call([packet_src, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] { - packet_src->SendTo(to, from, OwnedBuffer::copy_from(buf)); + ptr->call([this, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] { + src->SendTo(to, from, OwnedBuffer::copy_from(buf)); }); } else @@ -402,13 +429,22 @@ namespace llarp::dns } } // namespace libunbound - Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif) + Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif) : m_Loop{std::move(loop)} , m_Config{std::move(conf)} , m_Platform{CreatePlatform()} - , m_NetifName{std::move(netif)} + , m_NetIfIndex{std::move(netif)} {} + std::vector> + Server::GetAllResolvers() const + { + std::vector> all; + for (const auto& res : m_Resolvers) + all.push_back(res); + return all; + } + void Server::Start() { @@ -433,6 +469,10 @@ namespace llarp::dns plat->add_impl(std::make_unique()); plat->add_impl(std::make_unique()); } + if constexpr (llarp::platform::is_windows) + { + plat->add_impl(std::make_unique()); + } return plat; } @@ -531,19 +571,16 @@ namespace llarp::dns Server::SetDNSMode(bool all_queries) { if (auto maybe_addr = FirstBoundPacketSourceAddr()) - m_Platform->set_resolver(m_NetifName, *maybe_addr, all_queries); + m_Platform->set_resolver(m_NetIfIndex, *maybe_addr, all_queries); } bool Server::MaybeHandlePacket( - std::weak_ptr src, + std::shared_ptr ptr, 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)) { @@ -557,6 +594,7 @@ namespace llarp::dns 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 @@ -581,7 +619,7 @@ namespace llarp::dns if (auto res_ptr = resolver.lock()) { LogDebug("check resolver ", res_ptr->ResolverName(), " for dns from ", from, " to ", to); - if (res_ptr->MaybeHookDNS(src, msg, to, from)) + if (res_ptr->MaybeHookDNS(ptr, msg, to, from)) return true; } } diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index 88ca86385..3202d02af 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -67,17 +67,62 @@ namespace llarp::dns BoundOn() const = 0; }; + /// a packet source which will override the sendto function of an wrapped packet source to + /// construct a raw ip packet as a reply + class PacketSource_Wrapper : public PacketSource_Base + { + std::weak_ptr m_Wrapped; + std::function m_WritePacket; + + public: + explicit PacketSource_Wrapper( + std::weak_ptr wrapped, std::function write_packet) + : m_Wrapped{wrapped}, m_WritePacket{write_packet} + {} + + bool + WouldLoop(const SockAddr& to, const SockAddr& from) const override + { + if (auto ptr = m_Wrapped.lock()) + return ptr->WouldLoop(to, from); + return true; + } + + void + SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override + { + m_WritePacket(net::IPPacket::make_udp(to, from, std::move(buf))); + } + + /// stop reading packets and end operation + void + Stop() override + { + if (auto ptr = m_Wrapped.lock()) + ptr->Stop(); + } + + /// returns the sockaddr we are bound on if applicable + std::optional + BoundOn() const override + { + if (auto ptr = m_Wrapped.lock()) + return ptr->BoundOn(); + return std::nullopt; + } + }; + /// 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 { - std::weak_ptr src; + std::shared_ptr src; const SockAddr resolver; const SockAddr asker; public: explicit QueryJob( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to_, const SockAddr& from_) @@ -87,8 +132,7 @@ namespace llarp::dns void SendReply(llarp::OwnedBuffer replyBuf) const override { - if (auto ptr = src.lock()) - ptr->SendTo(asker, resolver, std::move(replyBuf)); + src->SendTo(asker, resolver, std::move(replyBuf)); } }; @@ -119,6 +163,13 @@ namespace llarp::dns return Rank() > other.Rank(); } + /// get local socket address that queries are sent from + virtual std::optional + GetLocalAddr() const + { + return std::nullopt; + } + /// get printable name virtual std::string_view ResolverName() const = 0; @@ -134,7 +185,7 @@ namespace llarp::dns /// returns true if we consumed this query and it should not be processed again virtual bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to, const SockAddr& from) = 0; @@ -167,7 +218,8 @@ namespace llarp::dns public: virtual ~Server() = default; - explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif_name); + + explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index); /// returns all sockaddr we have from all of our PacketSources std::vector @@ -205,16 +257,18 @@ namespace llarp::dns virtual std::shared_ptr MakeDefaultResolver(); - /// feed a packet buffer from a packet source + std::vector> + GetAllResolvers() const; + + /// 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 pktsource, + std::shared_ptr pktsource, const SockAddr& resolver, const SockAddr& from, llarp::OwnedBuffer buf); - /// set which dns mode we are in. /// true for intercepting all queries. false for just .loki and .snode void @@ -226,7 +280,7 @@ namespace llarp::dns std::shared_ptr m_Platform; private: - const std::string m_NetifName; + const unsigned int m_NetIfIndex; std::set, ComparePtr>> m_OwnedResolvers; std::set, CompareWeakPtr> m_Resolvers; diff --git a/llarp/dns/win32_platform.cpp b/llarp/dns/win32_platform.cpp new file mode 100644 index 000000000..6c268b05c --- /dev/null +++ b/llarp/dns/win32_platform.cpp @@ -0,0 +1,51 @@ +#include "win32_platform.hpp" +#include + +namespace llarp::dns::win32 +{ + void + Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool) + { +#ifdef _WIN32 + + // clear any previous dns settings + m_UndoDNS.clear(); + + auto interfaces = m_Loop->Net_ptr()->AllNetworkInterfaces(); + // remove dns + { + std::vector jobs; + for (const auto& ent : interfaces) + { + if (ent.index == index) + continue; + jobs.emplace_back( + "netsh.exe", fmt::format("interface ipv4 delete dns \"{}\" all", ent.name)); + jobs.emplace_back( + "netsh.exe", fmt::format("interface ipv6 delete dns \"{}\" all", ent.name)); + } + } + // add new dns + { + std::vector jobs; + for (const auto& ent : interfaces) + { + if (ent.index == index) + continue; + jobs.emplace_back( + "netsh.exe", + fmt::format("interface ipv4 add dns \"{}\" {} validate=no", ent.name, dns.asIPv4())); + jobs.emplace_back( + "netsh.exe", + fmt::format("interface ipv6 add dns \"{}\" {} validate=no", ent.name, dns.asIPv6())); + m_UndoDNS.emplace_back("netsh.exe", fmt::format("", index)); + } + m_UndoDNS.emplace_back("netsh.exe", "winsock reset"); + } + // flush dns + llarp::win32::Exec("ipconfig.exe", "/flushdns"); + +#endif + } + +} // namespace llarp::dns::win32 diff --git a/llarp/dns/win32_platform.hpp b/llarp/dns/win32_platform.hpp new file mode 100644 index 000000000..cb57a206c --- /dev/null +++ b/llarp/dns/win32_platform.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "platform.hpp" + +namespace llarp::dns +{ + // TODO: implement me + using Win32_Platform_t = Null_Platform; +} // namespace llarp::dns diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index 9c86b8cdd..8170c8a82 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -6,8 +6,8 @@ #include #include -// We libuv now -#include "ev_libuv.hpp" +#include "libuv.hpp" +#include namespace llarp { @@ -16,4 +16,11 @@ namespace llarp { return std::make_shared(queueLength); } + + const net::Platform* + EventLoop::Net_ptr() const + { + return net::Platform::Default_ptr(); + } + } // namespace llarp diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index 898d526a3..54e9046f5 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -4,7 +4,7 @@ #include #include #include - +#include #include #include #include @@ -28,8 +28,10 @@ namespace llarp namespace net { + class Platform; + struct IPPacket; - } + } // namespace net /// distinct event loop waker upper; used to idempotently schedule a task on the next event loop /// @@ -184,6 +186,9 @@ namespace llarp virtual ~EventLoop() = default; + virtual const net::Platform* + Net_ptr() const; + using UDPReceiveFunc = std::function; // Constructs a UDP socket that can be used for sending and/or receiving diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/libuv.cpp similarity index 97% rename from llarp/ev/ev_libuv.cpp rename to llarp/ev/libuv.cpp index 3f4784b94..75e370522 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/libuv.cpp @@ -1,12 +1,12 @@ -#include "ev_libuv.hpp" -#include "vpn.hpp" +#include "libuv.hpp" #include #include #include +#include + #include #include -#include -#include "ev.hpp" +#include #include @@ -251,19 +251,21 @@ namespace llarp::uv using event_t = uvw::PrepareEvent; auto handle = m_Impl->resource(); #endif + if (!handle) return false; handle->on([netif = std::move(netif), handler = std::move(handler)]( const event_t&, [[maybe_unused]] auto& handle) { - for (auto pkt = netif->ReadNextPacket(); pkt.sz > 0; pkt = netif->ReadNextPacket()) + for (auto pkt = netif->ReadNextPacket(); true; pkt = netif->ReadNextPacket()) { - LogDebug("got packet ", pkt.sz); + if (pkt.empty()) + return; if (handler) handler(std::move(pkt)); // on windows/apple, vpn packet io does not happen as an io action that wakes up the event // loop thus, we must manually wake up the event loop when we get a packet on our interface. - // on linux this is a nop + // on linux/android this is a nop netif->MaybeWakeUpperLayers(); } }); diff --git a/llarp/ev/ev_libuv.hpp b/llarp/ev/libuv.hpp similarity index 100% rename from llarp/ev/ev_libuv.hpp rename to llarp/ev/libuv.hpp diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 919a4c705..4bbad541c 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -110,7 +110,7 @@ namespace llarp bool Endpoint::QueueOutboundTraffic( - PathID_t path, ManagedBuffer buf, uint64_t counter, service::ProtocolType t) + PathID_t path, std::vector buf, uint64_t counter, service::ProtocolType t) { const service::ConvoTag tag{path.as_array()}; if (t == service::ProtocolType::QUIC) @@ -118,18 +118,19 @@ namespace llarp auto quic = m_Parent->GetQUICTunnel(); if (not quic) return false; - quic->receive_packet(tag, buf.underlying); + m_TxRate += buf.size(); + quic->receive_packet(tag, std::move(buf)); m_LastActive = m_Parent->Now(); - m_TxRate += buf.underlying.sz; return true; } // queue overflow if (m_UpstreamQueue.size() > MaxUpstreamQueueSize) return false; - llarp::net::IPPacket pkt; - if (!pkt.Load(buf.underlying)) + llarp::net::IPPacket pkt{std::move(buf)}; + if (pkt.empty()) return false; + if (pkt.IsV6() && m_Parent->SupportsV6()) { huint128_t dst; @@ -152,24 +153,19 @@ namespace llarp { return false; } - m_UpstreamQueue.emplace(pkt, counter); - m_TxRate += buf.underlying.sz; + m_TxRate += pkt.size(); + m_UpstreamQueue.emplace(std::move(pkt), counter); m_LastActive = m_Parent->Now(); return true; } bool - Endpoint::QueueInboundTraffic(ManagedBuffer buf, service::ProtocolType type) + Endpoint::QueueInboundTraffic(std::vector buf, service::ProtocolType type) { - llarp::net::IPPacket pkt{}; - if (type == service::ProtocolType::QUIC) + if (type != service::ProtocolType::QUIC) { - pkt.sz = std::min(buf.underlying.sz, sizeof(pkt.buf)); - std::copy_n(buf.underlying.base, pkt.sz, pkt.buf); - } - else - { - if (!pkt.Load(buf.underlying)) + llarp::net::IPPacket pkt{std::move(buf)}; + if (pkt.empty()) return false; huint128_t src; @@ -181,11 +177,11 @@ namespace llarp pkt.UpdateIPv6Address(src, m_IP); else pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP))); + + buf = pkt.steal(); } - const auto _pktbuf = pkt.ConstBuffer(); - auto& pktbuf = _pktbuf.underlying; - const uint8_t queue_idx = pktbuf.sz / llarp::routing::ExitPadSize; + const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize; if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end()) m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{}); auto& queue = m_DownstreamQueues.at(queue_idx); @@ -193,17 +189,17 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = type; - return queue.back().PutBuffer(pktbuf, m_Counter++); + return queue.back().PutBuffer(std::move(buf), m_Counter++); } auto& msg = queue.back(); - if (msg.Size() + pktbuf.sz > llarp::routing::ExitPadSize) + if (msg.Size() + buf.size() > llarp::routing::ExitPadSize) { queue.emplace_back(); queue.back().protocol = type; - return queue.back().PutBuffer(pktbuf, m_Counter++); + return queue.back().PutBuffer(std::move(buf), m_Counter++); } msg.protocol = type; - return msg.PutBuffer(pktbuf, m_Counter++); + return msg.PutBuffer(std::move(buf), m_Counter++); } bool @@ -212,7 +208,8 @@ namespace llarp // flush upstream queue while (m_UpstreamQueue.size()) { - m_Parent->QueueOutboundTraffic(m_UpstreamQueue.top().pkt); + m_Parent->QueueOutboundTraffic( + const_cast(m_UpstreamQueue.top().pkt).steal()); m_UpstreamQueue.pop(); } // flush downstream queue diff --git a/llarp/exit/endpoint.hpp b/llarp/exit/endpoint.hpp index 50b1d138b..b6fc4b13e 100644 --- a/llarp/exit/endpoint.hpp +++ b/llarp/exit/endpoint.hpp @@ -58,7 +58,7 @@ namespace llarp /// queue traffic from service node / internet to be transmitted bool - QueueInboundTraffic(ManagedBuffer buff, service::ProtocolType t); + QueueInboundTraffic(std::vector data, service::ProtocolType t); /// flush inbound and outbound traffic queues bool @@ -68,7 +68,7 @@ namespace llarp /// does ip rewrite here bool QueueOutboundTraffic( - PathID_t txid, ManagedBuffer pkt, uint64_t counter, service::ProtocolType t); + PathID_t txid, std::vector data, uint64_t counter, service::ProtocolType t); /// update local path id and cascade information to parent /// return true if success @@ -122,7 +122,7 @@ namespace llarp struct UpstreamBuffer { - UpstreamBuffer(const llarp::net::IPPacket& p, uint64_t c) : pkt(p), counter(c) + UpstreamBuffer(llarp::net::IPPacket p, uint64_t c) : pkt{std::move(p)}, counter(c) {} llarp::net::IPPacket pkt; diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index b2370002a..1ae5870c3 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -213,8 +213,8 @@ namespace llarp if (m_WritePacket) { - llarp::net::IPPacket pkt; - if (!pkt.Load(buf)) + llarp::net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return false; m_LastUse = m_router->Now(); m_Downstream.emplace(counter, pkt); @@ -235,9 +235,7 @@ namespace llarp BaseSession::QueueUpstreamTraffic( llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t) { - const auto pktbuf = pkt.ConstBuffer(); - const llarp_buffer_t& buf = pktbuf; - auto& queue = m_Upstream[buf.sz / N]; + auto& queue = m_Upstream[pkt.size() / N]; // queue overflow if (queue.size() >= MaxUpstreamQueueLength) return false; @@ -245,18 +243,18 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(buf, m_Counter++); + return queue.back().PutBuffer(std::move(pkt), m_Counter++); } auto& back = queue.back(); // pack to nearest N - if (back.Size() + buf.sz > N) + if (back.Size() + pkt.size() > N) { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(buf, m_Counter++); + return queue.back().PutBuffer(std::move(pkt), m_Counter++); } back.protocol = t; - return back.PutBuffer(buf, m_Counter++); + return back.PutBuffer(std::move(pkt), m_Counter++); } bool @@ -333,7 +331,7 @@ namespace llarp while (m_Downstream.size()) { if (m_WritePacket) - m_WritePacket(m_Downstream.top().second.ConstBuffer()); + m_WritePacket(const_cast(m_Downstream.top().second).steal()); m_Downstream.pop(); } } @@ -369,8 +367,8 @@ namespace llarp void SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt; - if (not pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return; pkt.ZeroAddresses(); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); @@ -379,9 +377,10 @@ namespace llarp void ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt; - if (not pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return; + pkt.ZeroSourceAddress(); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); } diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index c14245bbc..df508141c 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -109,7 +109,7 @@ namespace llarp { if (not itr->second->LooksDead(Now())) { - if (itr->second->QueueInboundTraffic(ManagedBuffer{payload}, type)) + if (itr->second->QueueInboundTraffic(payload.copy(), type)) return true; } } @@ -117,10 +117,10 @@ namespace llarp if (not m_Router->PathToRouterAllowed(*rid)) return false; - ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) { + ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable { if (session and session->IsReady()) { - session->SendPacketToRemote(data, type); + session->SendPacketToRemote(std::move(pkt), type); } }); } @@ -209,7 +209,7 @@ namespace llarp bool ExitEndpoint::MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) @@ -374,20 +374,24 @@ namespace llarp { while (not m_InetToNetwork.empty()) { - net::IPPacket pkt{m_InetToNetwork.top()}; - m_InetToNetwork.pop(); + auto& top = m_InetToNetwork.top(); - PubKey pk; + // get a session by public key + std::optional maybe_pk; { - auto itr = m_IPToKey.find(pkt.dstv6()); - if (itr == m_IPToKey.end()) - { - // drop - LogWarn(Name(), " dropping packet, has no session at ", pkt.dstv6()); - continue; - } - pk = itr->second; + auto itr = m_IPToKey.find(top.dstv6()); + if (itr != m_IPToKey.end()) + maybe_pk = itr->second; } + + auto buf = const_cast(top).steal(); + m_InetToNetwork.pop(); + // we have no session for public key so drop + if (not maybe_pk) + continue; // we are in a while loop + + const auto& pk = *maybe_pk; + // check if this key is a service node if (m_SNodeKeys.count(pk)) { @@ -397,13 +401,14 @@ namespace llarp auto itr = m_SNodeSessions.find(pk); if (itr != m_SNodeSessions.end()) { - itr->second->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::TrafficV4); + itr->second->SendPacketToRemote(std::move(buf), service::ProtocolType::TrafficV4); + // we are in a while loop continue; } } - auto tryFlushingTraffic = [&](exit::Endpoint* const ep) -> bool { - if (!ep->QueueInboundTraffic( - ManagedBuffer{pkt.Buffer()}, service::ProtocolType::TrafficV4)) + auto tryFlushingTraffic = + [this, buf = std::move(buf), pk](exit::Endpoint* const ep) -> bool { + if (!ep->QueueInboundTraffic(buf, service::ProtocolType::TrafficV4)) { LogWarn( Name(), @@ -451,13 +456,14 @@ namespace llarp m_IPToKey[ip] = us; m_IPActivity[ip] = std::numeric_limits::max(); m_SNodeKeys.insert(us); + if (m_ShouldInitTun) { vpn::InterfaceInfo info; info.ifname = m_ifname; - info.addrs.emplace(m_OurRange); + info.addrs.emplace_back(m_OurRange); - m_NetIf = GetRouter()->GetVPNPlatform()->ObtainInterface(std::move(info), m_Router); + m_NetIf = GetRouter()->GetVPNPlatform()->CreateInterface(std::move(info), m_Router); if (not m_NetIf) { llarp::LogError("Could not create interface"); @@ -471,7 +477,12 @@ namespace llarp } GetRouter()->loop()->add_ticker([this] { Flush(); }); +#ifndef _WIN32 + m_Resolver = std::make_shared( + m_Router->loop(), m_DNSConf, if_nametoindex(m_ifname.c_str())); m_Resolver->Start(); + +#endif } return true; } @@ -596,7 +607,7 @@ namespace llarp rc.srvRecords.clear(); for (auto& record : srvRecords) rc.srvRecords.emplace_back(record); - // set the version to 1 because we have srv records + // set the verssion to 1 because we have srv records rc.version = 1; return rc; }); @@ -651,8 +662,8 @@ namespace llarp bool ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from) { - net::IPPacket pkt; - if (!pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return false; // rewrite ip if (m_UseV6) @@ -708,6 +719,9 @@ namespace llarp return true; } */ + + m_DNSConf = dnsConfig; + if (networkConfig.m_endpointType == "null") { m_ShouldInitTun = false; @@ -743,7 +757,6 @@ namespace llarp return llarp::SockAddr{ifaddr, huint16_t{port}}; }); } - m_Resolver = std::make_shared(m_Router->loop(), dnsConfig, m_ifname); } huint128_t diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index 9984a3431..66bb09784 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -32,7 +32,7 @@ namespace llarp bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) override; @@ -245,6 +245,7 @@ namespace llarp /// internet to llarp packet queue PacketQueue_t m_InetToNetwork; bool m_UseV6; + DnsConfig m_DNSConf; }; } // namespace handlers } // namespace llarp diff --git a/llarp/handlers/null.hpp b/llarp/handlers/null.hpp index e0464b041..cd97fa82d 100644 --- a/llarp/handlers/null.hpp +++ b/llarp/handlers/null.hpp @@ -17,7 +17,7 @@ namespace llarp::handlers , m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) { var::visit( [&pkt](auto&& from) { - LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes"); + LogError("unhandled traffic from: ", from, " of ", pkt.size(), " bytes"); }, from); }}} diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 2d1ab4b8f..cf0ae5dc0 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -25,8 +25,7 @@ #include #include #include -#include - +#include #include #include @@ -37,7 +36,7 @@ namespace llarp { bool TunEndpoint::MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) @@ -46,21 +45,25 @@ namespace llarp return false; auto job = std::make_shared(source, query, to, from); - if (not HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); })) + if (HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); })) + Router()->TriggerPump(); + else 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 low port isn't possible - // because of OS restrictions, but a tun interface *is* available. + + /// Intercepts DNS IP packets on platforms where binding to a low port isn't viable. + /// (windows/macos/ios/android ... aka everything that is not linux... funny that) class DnsInterceptor : public dns::PacketSource_Base { - public: - TunEndpoint* const m_Endpoint; + std::function m_Reply; + net::ipaddr_t m_OurIP; llarp::DnsConfig m_Config; - explicit DnsInterceptor(TunEndpoint* ep, llarp::DnsConfig conf) - : m_Endpoint{ep}, m_Config{conf} + public: + explicit DnsInterceptor( + std::function reply, net::ipaddr_t our_ip, llarp::DnsConfig conf) + : m_Reply{std::move(reply)}, m_OurIP{std::move(our_ip)}, m_Config{std::move(conf)} {} virtual ~DnsInterceptor() = default; @@ -68,17 +71,11 @@ namespace llarp void SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override { - const auto pkt = net::IPPacket::UDP( - from.getIPv4(), - ToNet(huint16_t{from.getPort()}), - to.getIPv4(), - ToNet(huint16_t{to.getPort()}), - buf); - - if (pkt.sz == 0) + auto pkt = net::IPPacket::make_udp(from, to, std::move(buf)); + + if (pkt.empty()) return; - m_Endpoint->HandleWriteIPPacket( - pkt.ConstBuffer(), net::ExpandV4(from.asIPv4()), net::ExpandV4(to.asIPv4()), 0); + m_Reply(std::move(pkt)); } void @@ -90,92 +87,131 @@ namespace llarp return std::nullopt; } -#ifdef ANDROID bool - WouldLoop(const SockAddr&, const SockAddr&) const override + WouldLoop(const SockAddr& to, const SockAddr& from) const override { +#ifdef __APPLE__ + (void)from; + // DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data + // through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual + // upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver + // won't work for us. However when active the mac also only queries the main tunnel IP for + // DNS, so we consider anything else to be upstream-bound DNS to let it through the tunnel. + return to.asIPv6() != m_OurIP(); +#else + if (auto maybe_addr = m_Config.m_QueryBind) + { + const auto& addr = *maybe_addr; + // omit traffic to and from our dns socket + return addr == to or addr == from; + } return false; - } #endif - -#ifdef __APPLE__ - // DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data - // through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual - // upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver, - // above, won't work for us). However when active the mac also only queries the main tunnel - // IP for DNS, so we consider anything else to be upstream-bound DNS to let it through the - // tunnel. - bool - 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 { + std::optional m_QueryBind; + net::ipaddr_t m_OurIP; TunEndpoint* const m_Endpoint; public: - std::weak_ptr PacketSource; + std::shared_ptr PacketSource; virtual ~TunDNS() = default; + explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf) - : dns::Server{ep->Router()->loop(), conf, ep->GetIfName()}, m_Endpoint{ep} + : dns::Server{ep->Router()->loop(), conf, 0} + , m_QueryBind{conf.m_QueryBind} + , m_OurIP{ToNet(ep->GetIfAddr())} + , m_Endpoint{ep} {} std::shared_ptr MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override { - auto ptr = std::make_shared(m_Endpoint, conf); + auto ptr = std::make_shared( + [ep = m_Endpoint](auto pkt) { + ep->HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0); + }, + m_OurIP, + conf); PacketSource = ptr; return ptr; } - - std::shared_ptr - 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( + m_PacketRouter = std::make_shared( [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }); } void TunEndpoint::SetupDNS() { -#if defined(ANDROID) || defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION)) - auto dns = std::make_shared(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; - const auto dst = ToNet(pkt.dstv4()); - const auto src = ToNet(pkt.srcv4()); - const SockAddr laddr{src, nuint16_t{*reinterpret_cast(ptr)}}; - const SockAddr raddr{dst, nuint16_t{*reinterpret_cast(ptr + 2)}}; - - OwnedBuffer buf{pkt.sz - (8 + ip_header_size)}; - std::copy_n(ptr + 8, buf.sz, buf.buf.get()); - - if (dns->MaybeHandlePacket(dns->PacketSource, raddr, laddr, std::move(buf))) - return; + const auto& info = GetVPNInterface()->Info(); + if (m_DnsConfig.m_raw_dns) + { + auto dns = std::make_shared(this, m_DnsConfig); + if (auto vpn = Router()->GetVPNPlatform()) + { + // get the first local address we know of + std::optional localaddr; + for (auto res : dns->GetAllResolvers()) + { + if (localaddr) + continue; + if (auto ptr = res.lock()) + localaddr = ptr->GetLocalAddr(); + } + if (platform::is_windows) + { + auto dns_io = vpn->create_packet_io(0); + LogInfo("doing dns queries from ", *localaddr); + Router()->loop()->add_ticker( + [r = Router(), dns_io, handler = m_PacketRouter, src = localaddr]() { + net::IPPacket pkt = dns_io->ReadNextPacket(); + while (not pkt.empty()) + { + // reinject if for upstream dns + if (src and pkt.src() == *src) + { + LogInfo("reinject dns"); + std::function reply{std::move(pkt.reply)}; + reply(std::move(pkt)); + } + else + { + LogInfo("got dns packet from ", pkt.src(), " of size ", pkt.size(), "B"); + handler->HandleIPPacket(std::move(pkt)); + } + pkt = dns_io->ReadNextPacket(); + } + }); + m_RawDNS = dns_io; + } + } + m_DNS = dns; - HandleGotUserPacket(std::move(pkt)); - }); -#else - m_DNS = std::make_shared(Loop(), m_DnsConfig, GetIfName()); -#endif + m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) { + auto dns_pkt_src = dns->PacketSource; + if (const auto& reply = pkt.reply) + dns_pkt_src = std::make_shared(dns_pkt_src, reply); + if (dns->MaybeHandlePacket( + std::move(dns_pkt_src), pkt.dst(), pkt.src(), *pkt.L4OwnedBuffer())) + return; + + HandleGotUserPacket(std::move(pkt)); + }); + } + else + m_DNS = std::make_shared(Loop(), m_DnsConfig, info.index); + + if (m_RawDNS) + m_RawDNS->Start(); m_DNS->AddResolver(weak_from_this()); m_DNS->Start(); } @@ -969,45 +1005,39 @@ namespace llarp } vpn::InterfaceInfo info; - info.addrs.emplace(m_OurRange); + info.addrs.emplace_back(m_OurRange); if (m_BaseV6Address) { IPRange v6range = m_OurRange; v6range.addr = (*m_BaseV6Address) | m_OurRange.addr; LogInfo(Name(), " using v6 range: ", v6range); - info.addrs.emplace(v6range, AF_INET6); + info.addrs.emplace_back(v6range, AF_INET6); } info.ifname = m_IfName; - LogInfo(Name(), " setting up dns..."); - SetupDNS(); - - if (auto maybe_addr = m_DNS->FirstBoundPacketSourceAddr()) - info.dnsaddr = maybe_addr->asIPv4(); - LogInfo(Name(), " setting up network..."); try { - m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info), Router()); + m_NetIf = Router()->GetVPNPlatform()->CreateInterface(std::move(info), Router()); } catch (std::exception& ex) { LogError(Name(), " failed to set up network interface: ", ex.what()); - } - if (not m_NetIf) - { - LogError(Name(), " failed to obtain network interface"); return false; } - m_IfName = m_NetIf->IfName(); + + m_IfName = m_NetIf->Info().ifname; LogInfo(Name(), " got network interface ", m_IfName); - if (not Router()->loop()->add_network_interface(m_NetIf, [this](net::IPPacket pkt) { - m_PacketRouter->HandleIPPacket(std::move(pkt)); - })) + auto handle_packet = [netif = m_NetIf, pkt_router = m_PacketRouter](auto pkt) { + pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); }; + pkt_router->HandleIPPacket(std::move(pkt)); + }; + + if (not Router()->loop()->add_network_interface(m_NetIf, std::move(handle_packet))) { LogError(Name(), " failed to add network interface"); return false; @@ -1025,8 +1055,9 @@ namespace llarp } } - m_router->routePoker().SetDNSMode(false); - + LogInfo(Name(), " setting up dns..."); + SetupDNS(); + Loop()->call_soon([this]() { m_router->routePoker()->SetDNSMode(false); }); return HasAddress(ourAddr); } @@ -1060,9 +1091,13 @@ namespace llarp bool TunEndpoint::Stop() { + // stop vpn tunnel + if (m_NetIf) + m_NetIf->Stop(); + if (m_RawDNS) + m_RawDNS->Stop(); // save address map if applicable -#ifndef ANDROID - if (m_PersistAddrMapFile) + if (m_PersistAddrMapFile and not platform::is_android) { const auto& file = *m_PersistAddrMapFile; LogInfo(Name(), " saving address map to ", file); @@ -1082,7 +1117,6 @@ namespace llarp maybe->write(data.data(), data.size()); } } -#endif if (m_DNS) m_DNS->Stop(); return llarp::service::Endpoint::Stop(); @@ -1096,12 +1130,18 @@ namespace llarp // is it already mapped? return the mapping if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end()) return itr->second; + + const auto& net = m_router->Net(); + const bool is_bogon = net.IsBogonIP(ip); // build up our candidates to choose + std::unordered_set candidates; for (const auto& entry : m_ExitMap.FindAllEntries(ip)) { - // make sure it is allowed by the range if the ip is a bogon - if (not IsBogon(ip) or entry.first.BogonContains(ip)) + // in the event the exit's range is a bogon range, make sure the ip is located in that range + // to allow it + if ((is_bogon and net.IsBogonRange(entry.first) and entry.first.Contains(ip)) + or entry.first.Contains(ip)) candidates.emplace(entry.second); } // no candidates? bail. @@ -1163,13 +1203,20 @@ namespace llarp return; } + std::function extra_cb; + if (not HasFlowToService(addr)) + { + extra_cb = [poker = Router()->routePoker()]() { poker->Up(); }; + } pkt.ZeroSourceAddress(); MarkAddressOutbound(addr); EnsurePathToService( addr, - [pkt, this](service::Address addr, service::OutboundContext* ctx) { + [pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) { if (ctx) { + if (extra_cb) + extra_cb(); ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit); Router()->TriggerPump(); return; diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index e8e4ad22a..3c254a9e3 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -2,22 +2,20 @@ #include #include -#include #include #include #include #include +#include +#include #include #include +#include #include - #include #include -#include -#include - namespace llarp { namespace handlers @@ -29,6 +27,12 @@ namespace llarp TunEndpoint(AbstractRouter* r, llarp::service::Context* parent); ~TunEndpoint() override; + vpn::NetworkInterface* + GetVPNInterface() override + { + return m_NetIf.get(); + } + int Rank() const override { @@ -43,7 +47,7 @@ namespace llarp bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) override; @@ -306,7 +310,7 @@ namespace llarp std::shared_ptr m_NetIf; - std::unique_ptr m_PacketRouter; + std::shared_ptr m_PacketRouter; std::optional m_TrafficPolicy; /// ranges we advetise as reachable @@ -316,6 +320,9 @@ namespace llarp /// a file to load / store the ephemeral address map to std::optional m_PersistAddrMapFile; + + /// for raw packet dns + std::shared_ptr m_RawDNS; }; } // namespace handlers diff --git a/llarp/lokinet_shared.cpp b/llarp/lokinet_shared.cpp index de8f48528..7d4a0f085 100644 --- a/llarp/lokinet_shared.cpp +++ b/llarp/lokinet_shared.cpp @@ -954,7 +954,7 @@ extern "C" dstport, llarp_buffer_t{reinterpret_cast(ptr), len}); - if (pkt.sz == 0) + if (pkt.empty()) return EINVAL; std::promise ret; ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() { diff --git a/llarp/net/address_info.hpp b/llarp/net/address_info.hpp index cd5698886..73d41939e 100644 --- a/llarp/net/address_info.hpp +++ b/llarp/net/address_info.hpp @@ -50,9 +50,19 @@ namespace llarp fromSockAddr(const SockAddr& address); /// get this as an explicit v4 or explicit v6 - std::variant + net::ipaddr_t IP() const; + /// get this as an v4 or throw if it is not one + inline net::ipv4addr_t + IPv4() const + { + auto ip = IP(); + if (auto* ptr = std::get_if(&ip)) + return *ptr; + throw std::runtime_error{"no ipv4 address found in address info"}; + } + std::string ToString() const; }; diff --git a/llarp/net/bogon_ranges.hpp b/llarp/net/bogon_ranges.hpp new file mode 100644 index 000000000..d11512672 --- /dev/null +++ b/llarp/net/bogon_ranges.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "ip_range.hpp" + +namespace llarp +{ + static constexpr std::array bogonRanges_v6 = { + // zero + IPRange{huint128_t{0}, netmask_ipv6_bits(128)}, + // loopback + IPRange{huint128_t{1}, netmask_ipv6_bits(128)}, + // yggdrasil + IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)}, + // multicast + IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, + // local + IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, + // local + IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}}; + + static constexpr std::array bogonRanges_v4 = { + IPRange::FromIPv4(0, 0, 0, 0, 8), + IPRange::FromIPv4(10, 0, 0, 0, 8), + IPRange::FromIPv4(100, 64, 0, 0, 10), + IPRange::FromIPv4(127, 0, 0, 0, 8), + IPRange::FromIPv4(169, 254, 0, 0, 16), + IPRange::FromIPv4(172, 16, 0, 0, 12), + IPRange::FromIPv4(192, 0, 0, 0, 24), + IPRange::FromIPv4(192, 0, 2, 0, 24), + IPRange::FromIPv4(192, 88, 99, 0, 24), + IPRange::FromIPv4(192, 168, 0, 0, 16), + IPRange::FromIPv4(198, 18, 0, 0, 15), + IPRange::FromIPv4(198, 51, 100, 0, 24), + IPRange::FromIPv4(203, 0, 113, 0, 24), + IPRange::FromIPv4(224, 0, 0, 0, 4), + IPRange::FromIPv4(240, 0, 0, 0, 4)}; +} // namespace llarp diff --git a/llarp/net/interface_info.hpp b/llarp/net/interface_info.hpp new file mode 100644 index 000000000..c5d897264 --- /dev/null +++ b/llarp/net/interface_info.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "ip_range.hpp" + +namespace llarp::net +{ + /// info about a network interface lokinet does not own + struct InterfaceInfo + { + /// human readable name of interface + std::string name; + /// interface's index + int index; + /// the addresses owned by this interface + std::vector addrs; + /// a gateway we can use if it exists + std::optional gateway; + }; +} // namespace llarp::net diff --git a/llarp/net/ip_address.cpp b/llarp/net/ip_address.cpp index 818df7ac8..3503ff7c7 100644 --- a/llarp/net/ip_address.cpp +++ b/llarp/net/ip_address.cpp @@ -127,7 +127,7 @@ namespace llarp SockAddr addr(m_ipAddress); const auto* addr6 = static_cast(addr); const uint8_t* raw = addr6->sin6_addr.s6_addr; - return IsIPv4Bogon(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15])); + return IPRange::V4MappedRange().Contains(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15])); } std::string diff --git a/llarp/net/ip_address.hpp b/llarp/net/ip_address.hpp index d86c77314..5be44071e 100644 --- a/llarp/net/ip_address.hpp +++ b/llarp/net/ip_address.hpp @@ -19,14 +19,12 @@ namespace llarp /// As a convenience, it can produce a SockAddr for dealing with network libraries which depend /// sockaddr structs. However, it does not keep this as a member variable and isn't responsible /// for its lifetime/memory/etc. - /// - /// TODO: IPv6 is not currently supported. - struct IpAddress + struct [[deprecated("use llarp::SockAddr instead")]] IpAddress { /// Empty constructor. IpAddress() = default; /// move construtor - IpAddress(IpAddress&&) = default; + IpAddress(IpAddress &&) = default; /// copy construct IpAddress(const IpAddress&); @@ -56,80 +54,64 @@ namespace llarp /// @param addr is an SockAddr to initialize from. IpAddress(const SockAddr& addr); - IpAddress& - operator=(const sockaddr& other); + IpAddress& operator=(const sockaddr& other); /// move assignment - IpAddress& - operator=(IpAddress&& other); + IpAddress& operator=(IpAddress&& other); /// copy assignment - IpAddress& - operator=(const IpAddress& other); + IpAddress& operator=(const IpAddress& other); /// Return the port. Returns -1 if no port has been provided. /// /// @return the port, if present - std::optional - getPort() const; + std::optional getPort() const; /// Return true if we have a port set otherwise return false - bool - hasPort() const; + bool hasPort() const; /// Set the port. /// /// @param port - void - setPort(std::optional port); + void setPort(std::optional port); /// Set the IP address. Follows the same logic as the constructor with the same signature, but /// doesn't overwrite the port if the port isn't present in the string. - void - setAddress(std::string_view str); - void - setAddress(std::string_view str, std::optional port); + void setAddress(std::string_view str); + void setAddress(std::string_view str, std::optional port); /// Returns true if this is an IPv4 address (or an IPv6 address representing an IPv4 address) /// /// TODO: could return an int (e.g. 4 or 6) or an enum /// /// @return true if this is an IPv4 address, false otherwise - bool - isIPv4(); + bool isIPv4(); /// Returns true if this represents a valid IpAddress, false otherwise. /// /// @return whether or not this IpAddress is empty - bool - isEmpty() const; + bool isEmpty() const; /// Creates an instance of SockAddr representing this IpAddress. /// /// @return an instance of a SockAddr created from this IpAddress - SockAddr - createSockAddr() const; + SockAddr createSockAddr() const; /// Returns true if this IpAddress is a bogon, false otherwise /// /// @return whether or not this IpAddress is a bogon - bool - isBogon() const; + bool isBogon() const; /// Returns a string representing this IpAddress /// /// @return string representation of this IpAddress - std::string - ToString() const; + std::string ToString() const; - std::string - toHost() const; + std::string toHost() const; - huint32_t - toIP() const; + huint32_t toIP() const; - huint128_t - toIP6() const; + huint128_t toIP6() const; // TODO: other utility functions left over from Addr which may be useful // IsBogon() const; @@ -137,11 +119,9 @@ namespace llarp // std::hash // to string / stream / etc - bool - operator<(const IpAddress& other) const; + bool operator<(const IpAddress& other) const; - bool - operator==(const IpAddress& other) const; + bool operator==(const IpAddress& other) const; private: bool m_empty = true; diff --git a/llarp/net/ip_packet.cpp b/llarp/net/ip_packet.cpp index d6e339ee5..9f822b818 100644 --- a/llarp/net/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -1,6 +1,6 @@ #include "ip_packet.hpp" #include "ip.hpp" - +#include #include #include #include @@ -75,7 +75,6 @@ namespace llarp::net } throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; } - inline static uint32_t* in6_uint32_ptr(in6_addr& addr) { @@ -106,30 +105,54 @@ namespace llarp::net return ExpandV4(dstv4()); } - bool - IPPacket::Load(const llarp_buffer_t& pkt) + IPPacket::IPPacket(byte_view_t view) + { + if (view.size() < MinSize) + { + _buf.resize(0); + return; + } + _buf.resize(view.size()); + std::copy_n(view.data(), size(), data()); + } + + IPPacket::IPPacket(size_t sz) + { + if (sz and sz < MinSize) + throw std::invalid_argument{"buffer size is too small to hold an ip packet"}; + _buf.resize(sz); + } + + SockAddr + IPPacket::src() const + { + auto port = SrcPort(); + if (IsV4()) + return SockAddr{ToNet(srcv4()), *port}; + else + return SockAddr{ToNet(srcv6()), *port}; + } + + SockAddr + IPPacket::dst() const { - if (pkt.sz > sizeof(buf) or pkt.sz == 0) - return false; - sz = pkt.sz; - std::copy_n(pkt.base, sz, buf); - return true; + auto port = DstPort(); + if (IsV4()) + return SockAddr{ToNet(dstv4()), *port}; + else + return SockAddr{ToNet(dstv6()), *port}; } - ManagedBuffer - IPPacket::ConstBuffer() const + IPPacket::IPPacket(std::vector&& stolen) : _buf{stolen} { - const byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); + if (size() < MinSize) + _buf.resize(0); } - ManagedBuffer - IPPacket::Buffer() + byte_view_t + IPPacket::view() const { - byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); + return byte_view_t{data(), size()}; } std::optional @@ -139,7 +162,7 @@ namespace llarp::net { case IPProtocol::TCP: case IPProtocol::UDP: - return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4) + 2)}; + return nuint16_t{*reinterpret_cast(data() + (Header()->ihl * 4) + 2)}; default: return std::nullopt; } @@ -152,7 +175,7 @@ namespace llarp::net { case IPProtocol::TCP: case IPProtocol::UDP: - return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4))}; + return nuint16_t{*reinterpret_cast(data() + (Header()->ihl * 4))}; default: return std::nullopt; } @@ -397,7 +420,8 @@ namespace llarp::net auto oSrcIP = nuint32_t{hdr->saddr}; auto oDstIP = nuint32_t{hdr->daddr}; - + auto* buf = data(); + auto sz = size(); // L4 checksum auto ihs = size_t(hdr->ihl * 4); if (ihs <= sz) @@ -435,7 +459,7 @@ namespace llarp::net IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional flowlabel) { const size_t ihs = 4 + 4 + 16 + 16; - + const auto sz = size(); // XXX should've been checked at upper level? if (sz <= ihs) return; @@ -459,11 +483,11 @@ namespace llarp::net const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr); // TODO IPv6 header options - auto pld = buf + ihs; + auto* pld = data() + ihs; auto psz = sz - ihs; size_t fragoff = 0; - auto nextproto = hdr->proto; + auto nextproto = hdr->protocol; for (;;) { switch (nextproto) @@ -554,31 +578,27 @@ namespace llarp::net if (IsV4()) { constexpr auto icmp_Header_size = 8; - constexpr auto ip_Header_size = 20; - net::IPPacket pkt{}; - auto* pkt_Header = pkt.Header(); + auto ip_Header_size = Header()->ihl * 4; + auto pkt_size = (icmp_Header_size + ip_Header_size) * 2; + net::IPPacket pkt{static_cast(pkt_size)}; + auto* pkt_Header = pkt.Header(); pkt_Header->version = 4; pkt_Header->ihl = 0x05; pkt_Header->tos = 0; pkt_Header->check = 0; - pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size); + pkt_Header->tot_len = ntohs(pkt_size); pkt_Header->saddr = Header()->daddr; pkt_Header->daddr = Header()->saddr; pkt_Header->protocol = 1; // ICMP - pkt_Header->ttl = 1; + pkt_Header->ttl = Header()->ttl; pkt_Header->frag_off = htons(0b0100000000000000); - // size pf ip header - const size_t l3_HeaderSize = Header()->ihl * 4; - // size of l4 packet to reflect back - const size_t l4_PacketSize = 8; - pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize); uint16_t* checksum; - uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4); + uint8_t* itr = pkt.data() + ip_Header_size; uint8_t* icmp_begin = itr; // type 'destination unreachable' *itr++ = 3; - // code 'Destination host unknown error' + // code 'Destination host unknown error' *itr++ = 7; // checksum + unused oxenc::write_host_as_big(0, itr); @@ -588,14 +608,13 @@ namespace llarp::net oxenc::write_host_as_big(1500, itr); itr += 2; // copy ip header and first 8 bytes of datagram for icmp rject - std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr); - itr += l4_PacketSize + l3_HeaderSize; + std::copy_n(data(), ip_Header_size + icmp_Header_size, itr); + itr += ip_Header_size + icmp_Header_size; // calculate checksum of ip header - pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4); + pkt_Header->check = ipchksum(pkt.data(), ip_Header_size); const auto icmp_size = std::distance(icmp_begin, itr); // calculate icmp checksum *checksum = ipchksum(icmp_begin, icmp_size); - pkt.sz = ntohs(pkt_Header->tot_len); return pkt; } return std::nullopt; @@ -614,56 +633,85 @@ namespace llarp::net return std::nullopt; // check for invalid size - if (sz < (hdr->ihl * 4) + l4_HeaderSize) + if (size() < (hdr->ihl * 4) + l4_HeaderSize) return std::nullopt; - const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize); - return std::make_pair(reinterpret_cast(ptr), std::distance(ptr, buf + sz)); + const uint8_t* ptr = data() + ((hdr->ihl * 4) + l4_HeaderSize); + return std::make_pair(reinterpret_cast(ptr), std::distance(ptr, data() + size())); } - IPPacket - IPPacket::UDP( - nuint32_t srcaddr, - nuint16_t srcport, - nuint32_t dstaddr, - nuint16_t dstport, - const llarp_buffer_t& buf) + namespace { - net::IPPacket pkt; - - if (buf.sz + 28 > sizeof(pkt.buf)) + IPPacket + make_ip4_udp( + net::ipv4addr_t srcaddr, + net::port_t srcport, + net::ipv4addr_t dstaddr, + net::port_t dstport, + std::vector udp_data) { - pkt.sz = 0; + constexpr auto pkt_overhead = constants::udp_header_bytes + constants::ip_header_min_bytes; + net::IPPacket pkt{udp_data.size() + pkt_overhead}; + + auto* hdr = pkt.Header(); + pkt.data()[1] = 0; + hdr->version = 4; + hdr->ihl = 5; + hdr->tot_len = htons(pkt_overhead + udp_data.size()); + hdr->protocol = 0x11; // udp + hdr->ttl = 64; + hdr->frag_off = htons(0b0100000000000000); + + hdr->saddr = srcaddr.n; + hdr->daddr = dstaddr.n; + + // make udp packet + uint8_t* ptr = pkt.data() + constants::ip_header_min_bytes; + std::memcpy(ptr, &srcport.n, 2); + ptr += 2; + std::memcpy(ptr, &dstport.n, 2); + ptr += 2; + oxenc::write_host_as_big( + static_cast(udp_data.size() + constants::udp_header_bytes), ptr); + ptr += 2; + oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum + ptr += 2; + std::copy_n(udp_data.data(), udp_data.size(), ptr); + + hdr->check = 0; + hdr->check = net::ipchksum(pkt.data(), 20); return pkt; } - auto* hdr = pkt.Header(); - pkt.buf[1] = 0; - hdr->version = 4; - hdr->ihl = 5; - hdr->tot_len = htons(buf.sz + 28); - hdr->protocol = 0x11; // udp - hdr->ttl = 64; - hdr->frag_off = htons(0b0100000000000000); - - hdr->saddr = srcaddr.n; - hdr->daddr = dstaddr.n; - - // make udp packet - uint8_t* ptr = pkt.buf + 20; - std::memcpy(ptr, &srcport.n, 2); - ptr += 2; - std::memcpy(ptr, &dstport.n, 2); - ptr += 2; - oxenc::write_host_as_big(static_cast(buf.sz + 8), ptr); - ptr += 2; - oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum - ptr += 2; - std::copy_n(buf.base, buf.sz, ptr); - - hdr->check = 0; - hdr->check = net::ipchksum(pkt.buf, 20); - pkt.sz = 28 + buf.sz; - return pkt; + } // namespace + IPPacket + IPPacket::make_udp( + net::ipaddr_t srcaddr, + net::port_t srcport, + net::ipaddr_t dstaddr, + net::port_t dstport, + std::vector udp_data) + { + auto getfam = [](auto&& v) { + if (std::holds_alternative(v)) + return AF_INET; + else if (std::holds_alternative(v)) + return AF_INET6; + else + return AF_UNSPEC; + }; + auto fam = getfam(srcaddr); + if (fam != getfam(dstaddr)) + return net::IPPacket{size_t{}}; + if (fam == AF_INET) + { + return make_ip4_udp( + *std::get_if(&srcaddr), + srcport, + *std::get_if(&dstaddr), + dstport, + std::move(udp_data)); + } + // TODO: ipv6 + return net::IPPacket{size_t{}}; } - } // namespace llarp::net diff --git a/llarp/net/ip_packet.hpp b/llarp/net/ip_packet.hpp index a9db47904..14418d05f 100644 --- a/llarp/net/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -69,7 +69,7 @@ namespace llarp::net } preamble; uint16_t payload_len; - uint8_t proto; + uint8_t protocol; uint8_t hoplimit; in6_addr srcaddr; in6_addr dstaddr; @@ -109,34 +109,119 @@ namespace llarp::net /// an Packet struct IPPacket { - static constexpr size_t MaxSize = 1500; + static constexpr size_t _max_size = 1500; llarp_time_t timestamp; - size_t sz; + std::vector _buf; - alignas(ip_header) byte_t buf[MaxSize]; + public: + IPPacket() : IPPacket{size_t{}} + {} + /// create an ip packet buffer of all zeros of size sz + explicit IPPacket(size_t sz); + /// create an ip packet from a view + explicit IPPacket(byte_view_t); + /// create an ip packet from a vector we then own + IPPacket(std::vector&&); - static IPPacket + ~IPPacket() = default; + + static constexpr size_t MaxSize = _max_size; + static constexpr size_t MinSize = 20; + + [[deprecated("deprecated because of llarp_buffer_t")]] static IPPacket UDP(nuint32_t srcaddr, nuint16_t srcport, nuint32_t dstaddr, nuint16_t dstport, - const llarp_buffer_t& data); + const llarp_buffer_t& data) + { + return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy()); + } + + static IPPacket + make_udp( + net::ipaddr_t srcaddr, + net::port_t srcport, + net::ipaddr_t dstaddr, + net::port_t dstport, + std::vector udp_body); + + static inline IPPacket + make_udp(SockAddr src, SockAddr dst, std::variant> udp_body) + { + if (auto* vec = std::get_if>(&udp_body)) + return make_udp(src.getIP(), src.port(), dst.getIP(), dst.port(), std::move(*vec)); + else if (auto* buf = std::get_if(&udp_body)) + return make_udp(src, dst, buf->copy()); + else + return net::IPPacket{size_t{}}; + } + + [[deprecated("deprecated because of llarp_buffer_t")]] inline bool + Load(const llarp_buffer_t& buf) + { + _buf = buf.copy(); + if (size() >= MinSize) + return true; + _buf.resize(0); + return false; + } - ManagedBuffer - Buffer(); + [[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t + ConstBuffer() const + { + return llarp_buffer_t{_buf}; + } - ManagedBuffer - ConstBuffer() const; + /// steal the underlying vector + inline std::vector + steal() + { + std::vector buf; + buf.resize(0); + std::swap(_buf, buf); + return buf; + } - bool - Load(const llarp_buffer_t& buf); + inline byte_t* + data() + { + return _buf.data(); + } + + inline const byte_t* + data() const + { + return _buf.data(); + } + + constexpr size_t + capacity() const + { + return _max_size; + } + + inline size_t + size() const + { + return _buf.size(); + } + + inline bool + empty() const + { + return _buf.empty(); + } + + byte_view_t + view() const; struct CompareSize { bool operator()(const IPPacket& left, const IPPacket& right) { - return left.sz < right.sz; + return left.size() < right.size(); } }; @@ -152,25 +237,25 @@ namespace llarp::net inline ip_header* Header() { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline const ip_header* Header() const { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline ipv6_header* HeaderV6() { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline const ipv6_header* HeaderV6() const { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline int @@ -179,6 +264,15 @@ namespace llarp::net return Header()->version; } + inline byte_t + protocol() const + { + if (IsV4()) + return Header()->protocol; + else + return HeaderV6()->protocol; + } + inline bool IsV4() const { @@ -226,6 +320,12 @@ namespace llarp::net huint128_t dst4to6Lan() const; + SockAddr + src() const; + + SockAddr + dst() const; + /// get destination port if applicable std::optional DstPort() const; @@ -238,6 +338,14 @@ namespace llarp::net std::optional> L4Data() const; + inline std::optional + L4OwnedBuffer() const + { + if (auto data = L4Data()) + return OwnedBuffer{reinterpret_cast(data->first), data->second}; + return std::nullopt; + } + void UpdateIPv4Address(nuint32_t src, nuint32_t dst); @@ -256,6 +364,8 @@ namespace llarp::net /// make an icmp unreachable reply packet based of this ip packet std::optional MakeICMPUnreachable() const; + + std::function reply; }; /// generate ip checksum diff --git a/llarp/net/ip_range.cpp b/llarp/net/ip_range.cpp index 08d234584..ea3fe858e 100644 --- a/llarp/net/ip_range.cpp +++ b/llarp/net/ip_range.cpp @@ -90,4 +90,15 @@ namespace llarp return addr.ToString(); } + std::string + IPRange::NetmaskString() const + { + if (IsV4()) + { + const huint32_t mask = net::TruncateV6(netmask_bits); + return mask.ToString(); + } + return netmask_bits.ToString(); + } + } // namespace llarp diff --git a/llarp/net/ip_range.hpp b/llarp/net/ip_range.hpp index e89e54865..201473852 100644 --- a/llarp/net/ip_range.hpp +++ b/llarp/net/ip_range.hpp @@ -9,10 +9,6 @@ namespace llarp { - /// forward declare - bool - IsBogon(huint128_t ip); - struct IPRange { using Addr_t = huint128_t; @@ -25,6 +21,12 @@ namespace llarp : addr{std::move(address)}, netmask_bits{std::move(netmask)} {} + static constexpr IPRange + V4MappedRange() + { + return IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)}; + } + static constexpr IPRange FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask) { @@ -42,8 +44,7 @@ namespace llarp constexpr bool IsV4() const { - constexpr auto ipv4_map = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)}; - return ipv4_map.Contains(addr); + return V4MappedRange().Contains(addr); } /// get address family @@ -55,27 +56,6 @@ namespace llarp return AF_INET6; } - /// return true if we intersect with a bogon range - bool - BogonRange() const - { - // special case for 0.0.0.0/0 - if (IsV4() and netmask_bits == netmask_ipv6_bits(96)) - return false; - // special case for ::/0 - if (netmask_bits == huint128_t{0}) - return false; - return IsBogon(addr) or IsBogon(HighestAddr()); - } - - /// return true if we intersect with a bogon range *and* we contain the given address - template - bool - BogonContains(Addr&& addr) const - { - return BogonRange() and Contains(std::forward(addr)); - } - /// return the number of bits set in the hostmask constexpr int HostmaskBits() const @@ -147,6 +127,9 @@ namespace llarp std::string BaseAddressString() const; + std::string + NetmaskString() const; + bool FromString(std::string str); diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index d8bd77aaa..e54a7ee8e 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -9,6 +9,8 @@ #include #include +#include "interface_info.hpp" + #include #include // for itoa #include @@ -22,50 +24,73 @@ #include #include #include -#define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif #ifndef _WIN32 #include #endif -bool -operator==(const sockaddr& a, const sockaddr& b); - -bool -operator==(const sockaddr_in& a, const sockaddr_in& b); +#include "bogon_ranges.hpp" -bool -operator==(const sockaddr_in6& a, const sockaddr_in6& b); - -bool -operator<(const sockaddr_in6& a, const sockaddr_in6& b); +namespace llarp +{ + inline bool + operator==(const in_addr& a, const in_addr& b) + { + return memcmp(&a, &b, sizeof(in_addr)) == 0; + } -bool -operator<(const in6_addr& a, const in6_addr& b); + inline bool + operator==(const in6_addr& a, const in6_addr& b) + { + return memcmp(&a, &b, sizeof(in6_addr)) == 0; + } -bool -operator==(const in6_addr& a, const in6_addr& b); + inline bool + operator==(const sockaddr_in& a, const sockaddr_in& b) + { + return a.sin_port == b.sin_port and a.sin_addr.s_addr == b.sin_addr.s_addr; + } -namespace llarp -{ - bool - IsIPv4Bogon(const huint32_t& addr); + inline bool + operator==(const sockaddr_in6& a, const sockaddr_in6& b) + { + return a.sin6_port == b.sin6_port and a.sin6_addr == b.sin6_addr; + } inline bool - IsIPv4Bogon(const nuint32_t& addr) + operator==(const sockaddr& a, const sockaddr& b) { - return IsIPv4Bogon(ToHost(addr)); + if (a.sa_family != b.sa_family) + return false; + switch (a.sa_family) + { + case AF_INET: + return reinterpret_cast(a) == reinterpret_cast(b); + case AF_INET6: + return reinterpret_cast(a) == reinterpret_cast(b); + default: + return false; + } } - bool - IsBogon(const in6_addr& addr); + inline bool + operator<(const in_addr& a, const in_addr& b) + { + return memcmp(&a, &b, sizeof(in_addr)) < 0; + } - bool - IsBogon(const huint128_t addr); + inline bool + operator<(const in6_addr& a, const in6_addr& b) + { + return memcmp(&a, &b, sizeof(in6_addr)) < 0; + } - bool - IsBogonRange(const in6_addr& host, const in6_addr& mask); + inline bool + operator<(const sockaddr_in6& a, const sockaddr_in6& b) + { + return a.sin6_addr < b.sin6_addr or a.sin6_port < b.sin6_port; + } namespace net { @@ -86,8 +111,27 @@ namespace llarp virtual std::optional AllInterfaces(SockAddr pubaddr) const = 0; - virtual SockAddr - Wildcard(int af = AF_INET) const = 0; + inline SockAddr + Wildcard(int af = AF_INET) const + { + if (af == AF_INET) + { + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + return SockAddr{addr}; + } + if (af == AF_INET6) + { + sockaddr_in6 addr6{}; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(0); + addr6.sin6_addr = IN6ADDR_ANY_INIT; + return SockAddr{addr6}; + } + throw std::invalid_argument{fmt::format("{} is not a valid address family")}; + } inline SockAddr WildcardWithPort(port_t port, int af = AF_INET) const @@ -104,12 +148,24 @@ namespace llarp HasInterfaceAddress(ipaddr_t ip) const = 0; /// return true if ip is considered a loopback address - virtual bool - IsLoopbackAddress(ipaddr_t ip) const = 0; + inline bool + IsLoopbackAddress(ipaddr_t ip) const + { + return var::visit( + [loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)}, + loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) { + const auto h_ip = ToHost(ip); + return loopback4.Contains(h_ip) or loopback6.Contains(h_ip); + }, + ip); + } /// return true if ip is considered a wildcard address - virtual bool - IsWildcardAddress(ipaddr_t ip) const = 0; + inline bool + IsWildcardAddress(ipaddr_t ip) const + { + return var::visit([](auto&& ip) { return not ip.n; }, ip); + } virtual std::optional GetBestNetIF(int af = AF_INET) const = 0; @@ -143,11 +199,63 @@ namespace llarp return std::nullopt; } - virtual bool - IsBogon(const SockAddr& addr) const = 0; + inline bool + IsBogon(const SockAddr& addr) const + { + return IsBogonIP(addr.asIPv6()); + } + + inline bool + IsBogonRange(const IPRange& range) const + { + // special case for 0.0.0.0/0 + if (range.IsV4() and range.netmask_bits == netmask_ipv6_bits(96)) + return false; + // special case for ::/0 + if (IsWildcardAddress(ToNet(range.netmask_bits))) + return false; + return IsBogonIP(range.addr) or IsBogonIP(range.HighestAddr()); + } + + inline bool + IsBogonIP(const net::ipaddr_t& addr) const + { + return IsBogonIP(var::visit( + [](auto&& ip) { + if constexpr (std::is_same_v>) + return ExpandV4(ToHost(ip)); + else + return ToHost(ip); + }, + addr)); + } + inline bool + IsBogonIP(const huint128_t& addr) const + { + if (not IPRange::V4MappedRange().Contains(addr)) + { + for (const auto& v6_range : bogonRanges_v6) + { + if (v6_range.Contains(addr)) + return true; + } + return false; + } + const auto v4_addr = net::TruncateV6(addr); + for (const auto& v4_range : bogonRanges_v4) + { + if (v4_range.Contains(v4_addr)) + return true; + } + return false; + } virtual std::optional GetInterfaceIndex(ipaddr_t ip) const = 0; + + /// returns a vector holding all of our network interfaces + virtual std::vector + AllNetworkInterfaces() const = 0; }; } // namespace net diff --git a/llarp/net/net_bits.hpp b/llarp/net/net_bits.hpp index c9c2c9c97..d36297593 100644 --- a/llarp/net/net_bits.hpp +++ b/llarp/net/net_bits.hpp @@ -40,14 +40,6 @@ namespace llarp // IPv4 mapped address live at ::ffff:0:0/96 constexpr std::array ipv4_map_prefix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; - constexpr bool - ipv6_is_mapped_ipv4(const in6_addr& addr) - { - for (size_t i = 0; i < ipv4_map_prefix.size(); i++) - if (addr.s6_addr[i] != ipv4_map_prefix[i]) - return false; - return true; - } namespace net { inline auto diff --git a/llarp/net/net.cpp b/llarp/net/posix.cpp similarity index 67% rename from llarp/net/net.cpp rename to llarp/net/posix.cpp index 419a718fd..aaff8ece0 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/posix.cpp @@ -1,19 +1,9 @@ #include "net.hpp" - #include "net_if.hpp" #include #include -#ifdef ANDROID -#include -#endif - -#ifndef _WIN32 #include -#ifndef ANDROID -#include -#endif -#endif #include "ip.hpp" #include "ip_range.hpp" @@ -23,116 +13,15 @@ #ifdef ANDROID #include #else -#ifdef _WIN32 -#include -#include -#else #include #endif -#endif #include #include #include -bool -operator==(const sockaddr& a, const sockaddr& b) -{ - if (a.sa_family != b.sa_family) - return false; - switch (a.sa_family) - { - case AF_INET: - return *((const sockaddr_in*)&a) == *((const sockaddr_in*)&b); - case AF_INET6: - return *((const sockaddr_in6*)&a) == *((const sockaddr_in6*)&b); - default: - return false; - } -} - -bool -operator<(const sockaddr_in6& a, const sockaddr_in6& b) -{ - return a.sin6_addr < b.sin6_addr || a.sin6_port < b.sin6_port; -} - -bool -operator<(const in6_addr& a, const in6_addr& b) -{ - return memcmp(&a, &b, sizeof(in6_addr)) < 0; -} - -bool -operator==(const in6_addr& a, const in6_addr& b) -{ - return memcmp(&a, &b, sizeof(in6_addr)) == 0; -} - -bool -operator==(const sockaddr_in& a, const sockaddr_in& b) -{ - return a.sin_port == b.sin_port && a.sin_addr.s_addr == b.sin_addr.s_addr; -} - -bool -operator==(const sockaddr_in6& a, const sockaddr_in6& b) -{ - return a.sin6_port == b.sin6_port && a.sin6_addr == b.sin6_addr; -} - namespace llarp::net { - class Platform_Base : public llarp::net::Platform - { - public: - bool - IsLoopbackAddress(ipaddr_t ip) const override - { - return var::visit( - [loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)}, - loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) { - const auto h_ip = ToHost(ip); - return loopback4.Contains(h_ip) or loopback6.Contains(h_ip); - }, - ip); - } - - SockAddr - Wildcard(int af) const override - { - if (af == AF_INET) - { - sockaddr_in addr{}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - return SockAddr{addr}; - } - if (af == AF_INET6) - { - sockaddr_in6 addr6{}; - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(0); - addr6.sin6_addr = IN6ADDR_ANY_INIT; - return SockAddr{addr6}; - } - throw std::invalid_argument{fmt::format("{} is not a valid address family")}; - } - - bool - IsBogon(const llarp::SockAddr& addr) const override - { - return llarp::IsBogon(addr.asIPv6()); - } - - bool - IsWildcardAddress(ipaddr_t ip) const override - { - return var::visit([](auto&& ip) { return not ip.n; }, ip); - } - }; - #ifdef _WIN32 class Platform_Impl : public Platform_Base { @@ -293,11 +182,30 @@ namespace llarp::net { return GetInterfaceIndex(ip) != std::nullopt; } + + std::vector + AllNetworkInterfaces() const override + { + std::vector all; + iter_adapters([&all](auto* a) { + auto& cur = all.emplace_back(); + cur.index = a->IfIndex; + cur.name = a->AdapterName; + for (auto* addr = a->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + cur.addrs.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + return all; + } }; #else - class Platform_Impl : public Platform_Base + class Platform_Impl : public Platform { template void @@ -469,6 +377,33 @@ namespace llarp::net }); return found; } + std::vector + AllNetworkInterfaces() const override + { + std::unordered_map ifmap; + iter_all([&ifmap](auto* i) { + if (i == nullptr or i->ifa_addr == nullptr) + return; + + const auto fam = i->ifa_addr->sa_family; + if (fam != AF_INET and fam != AF_INET6) + return; + + auto& ent = ifmap[i->ifa_name]; + if (ent.name.empty()) + { + ent.name = i->ifa_name; + ent.index = if_nametoindex(i->ifa_name); + } + SockAddr addr{*i->ifa_addr}; + SockAddr mask{*i->ifa_netmask}; + ent.addrs.emplace_back(addr.asIPv6(), mask.asIPv6()); + }); + std::vector all; + for (auto& [name, ent] : ifmap) + all.emplace_back(std::move(ent)); + return all; + } }; #endif @@ -480,100 +415,3 @@ namespace llarp::net return &g_plat; } } // namespace llarp::net - -namespace llarp -{ -#if !defined(TESTNET) - static constexpr std::array bogonRanges_v6 = { - // zero - IPRange{huint128_t{0}, netmask_ipv6_bits(128)}, - // loopback - IPRange{huint128_t{1}, netmask_ipv6_bits(128)}, - // yggdrasil - IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)}, - // multicast - IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, - // local - IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, - // local - IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}}; - - static constexpr std::array bogonRanges_v4 = { - IPRange::FromIPv4(0, 0, 0, 0, 8), - IPRange::FromIPv4(10, 0, 0, 0, 8), - IPRange::FromIPv4(100, 64, 0, 0, 10), - IPRange::FromIPv4(127, 0, 0, 0, 8), - IPRange::FromIPv4(169, 254, 0, 0, 16), - IPRange::FromIPv4(172, 16, 0, 0, 12), - IPRange::FromIPv4(192, 0, 0, 0, 24), - IPRange::FromIPv4(192, 0, 2, 0, 24), - IPRange::FromIPv4(192, 88, 99, 0, 24), - IPRange::FromIPv4(192, 168, 0, 0, 16), - IPRange::FromIPv4(198, 18, 0, 0, 15), - IPRange::FromIPv4(198, 51, 100, 0, 24), - IPRange::FromIPv4(203, 0, 113, 0, 24), - IPRange::FromIPv4(224, 0, 0, 0, 4), - IPRange::FromIPv4(240, 0, 0, 0, 4)}; - -#endif - - bool - IsBogon(const in6_addr& addr) - { -#if defined(TESTNET) - (void)addr; - return false; -#else - if (not ipv6_is_mapped_ipv4(addr)) - { - const auto ip = net::In6ToHUInt(addr); - for (const auto& range : bogonRanges_v6) - { - if (range.Contains(ip)) - return true; - } - return false; - } - return IsIPv4Bogon( - ipaddr_ipv4_bits(addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15])); -#endif - } - - bool - IsBogon(const huint128_t ip) - { - const nuint128_t netIP{ntoh128(ip.h)}; - in6_addr addr{}; - std::copy_n((const uint8_t*)&netIP.n, 16, &addr.s6_addr[0]); - return IsBogon(addr); - } - - bool - IsBogonRange(const in6_addr& host, const in6_addr&) - { - // TODO: implement me - return IsBogon(host); - } - -#if !defined(TESTNET) - bool - IsIPv4Bogon(const huint32_t& addr) - { - for (const auto& bogon : bogonRanges_v4) - { - if (bogon.Contains(addr)) - { - return true; - } - } - return false; - } -#else - bool - IsIPv4Bogon(const huint32_t&) - { - return false; - } -#endif - -} // namespace llarp diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index 7f66e0aff..2e9595bc8 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -1,7 +1,9 @@ #include "sock_addr.hpp" +#include "ip_range.hpp" #include "address_info.hpp" #include "ip.hpp" #include "net_bits.hpp" +#include "net.hpp" #include #include #include @@ -11,17 +13,6 @@ namespace llarp { - bool - operator==(const in6_addr& lh, const in6_addr& rh) - { - 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 /// @@ -156,7 +147,7 @@ namespace llarp init(); memcpy(&m_addr, &other, sizeof(sockaddr_in6)); - if (ipv6_is_mapped_ipv4(other.sin6_addr)) + if (IPRange::V4MappedRange().Contains(asIPv6())) { setIPv4( other.sin6_addr.s6_addr[12], @@ -179,9 +170,8 @@ namespace llarp SockAddr::operator=(const in6_addr& other) { init(); - memcpy(&m_addr.sin6_addr.s6_addr, &other.s6_addr, sizeof(m_addr.sin6_addr.s6_addr)); - if (ipv6_is_mapped_ipv4(other)) + if (IPRange::V4MappedRange().Contains(asIPv6())) { setIPv4(other.s6_addr[12], other.s6_addr[13], other.s6_addr[14], other.s6_addr[15]); m_addr4.sin_port = m_addr.sin6_port; @@ -335,7 +325,7 @@ namespace llarp bool SockAddr::isIPv4() const { - return ipv6_is_mapped_ipv4(m_addr.sin6_addr); + return IPRange::V4MappedRange().Contains(asIPv6()); } bool SockAddr::isIPv6() const @@ -438,10 +428,10 @@ namespace llarp setPort(ToNet(port)); } - uint16_t - SockAddr::getPort() const + net::port_t + SockAddr::port() const { - return ntohs(m_addr.sin6_port); + return net::port_t{m_addr.sin6_port}; } } // namespace llarp diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 82ea367c7..7634c6e23 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -141,9 +141,16 @@ namespace llarp setPort(huint16_t{port}); } - /// port is always returned in native (host) order - uint16_t - getPort() const; + /// get the port of this sockaddr in network order + net::port_t + port() const; + + /// port is always returned in host order + inline uint16_t + getPort() const + { + return ToHost(port()).h; + } /// True if this stores an IPv6 address, false if IPv4. bool diff --git a/llarp/net/win32.cpp b/llarp/net/win32.cpp new file mode 100644 index 000000000..4f166fb84 --- /dev/null +++ b/llarp/net/win32.cpp @@ -0,0 +1,216 @@ +#include "net.hpp" + +#include "net_if.hpp" +#include +#include + +#include "ip.hpp" +#include "ip_range.hpp" +#include +#include + +#include +#include + +#include +#include +#include + +namespace llarp::net +{ + class Platform_Impl : public Platform + { + /// visit all adapters (not addresses). windows serves net info per adapter unlink posix which + /// gives a list of all distinct addresses. + template + void + iter_adapters(Visit_t&& visit, int af = AF_UNSPEC) const + { + ULONG sz{}; + GetAdaptersAddresses(af, 0, nullptr, nullptr, &sz); + auto ptr = std::make_unique(sz); + auto* addrs = reinterpret_cast(ptr.get()); + + if (auto err = GetAdaptersAddresses(af, 0, nullptr, addrs, &sz); err != ERROR_SUCCESS) + throw llarp::win32::error{err, "GetAdaptersAddresses()"}; + + for (auto* addr = addrs; addr->Next; addr = addr->Next) + visit(addr); + } + + template + bool + adapter_has_ip(adapter_t* a, ipaddr_t ip) const + { + for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + LogDebug(fmt::format("'{}' has address '{}'", a->AdapterName, saddr)); + if (saddr.getIP() == ip) + return true; + } + return false; + } + + template + bool + adapter_has_fam(adapter_t* a, int af) const + { + for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + if (saddr.Family() == af) + return true; + } + return false; + } + + public: + std::optional + GetInterfaceIndex(ipaddr_t ip) const override + { + std::optional found; + int af{AF_INET}; + if (std::holds_alternative(ip)) + af = AF_INET6; + iter_adapters( + [&found, ip, this](auto* adapter) { + if (found) + return; + + LogDebug(fmt::format( + "visit adapter looking for '{}': '{}' idx={}", + ip, + adapter->AdapterName, + adapter->IfIndex)); + if (adapter_has_ip(adapter, ip)) + { + found = adapter->IfIndex; + } + }, + af); + return found; + } + + std::optional + GetInterfaceAddr(std::string_view name, int af) const override + { + std::optional found; + iter_adapters([name = std::string{name}, af, &found, this](auto* a) { + if (found) + return; + if (std::string{a->AdapterName} != name) + return; + + if (adapter_has_fam(a, af)) + found = SockAddr{*a->FirstUnicastAddress->Address.lpSockaddr}; + }); + return found; + } + + std::optional + AllInterfaces(SockAddr fallback) const override + { + // windows seems to not give a shit about source address + return fallback.isIPv6() ? SockAddr{"[::]"} : SockAddr{"0.0.0.0"}; + } + + std::optional + FindFreeTun() const override + { + return "lokitun0"; + } + + std::optional + GetBestNetIF(int) const override + { + // TODO: implement me ? + return std::nullopt; + } + + std::optional + FindFreeRange() const override + { + std::list currentRanges; + iter_adapters([¤tRanges](auto* i) { + for (auto* addr = i->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + currentRanges.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + + auto ownsRange = [¤tRanges](const IPRange& range) -> bool { + for (const auto& ownRange : currentRanges) + { + if (ownRange * range) + return true; + } + return false; + }; + // generate possible ranges to in order of attempts + std::list possibleRanges; + for (byte_t oct = 16; oct < 32; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(172, oct, 0, 1, 16)); + } + for (byte_t oct = 0; oct < 255; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(10, oct, 0, 1, 16)); + } + for (byte_t oct = 0; oct < 255; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(192, 168, oct, 1, 24)); + } + // for each possible range pick the first one we don't own + for (const auto& range : possibleRanges) + { + if (not ownsRange(range)) + return range; + } + return std::nullopt; + } + + std::string + LoopbackInterfaceName() const override + { + // todo: implement me? does windows even have a loopback? + return ""; + } + + bool + HasInterfaceAddress(ipaddr_t ip) const override + { + return GetInterfaceIndex(ip) != std::nullopt; + } + + std::vector + AllNetworkInterfaces() const override + { + std::vector all; + iter_adapters([&all](auto* a) { + auto& cur = all.emplace_back(); + cur.index = a->IfIndex; + cur.name = a->AdapterName; + for (auto* addr = a->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + cur.addrs.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + return all; + } + }; + + const Platform_Impl g_plat{}; + + const Platform* + Platform::Default_ptr() + { + return &g_plat; + } +} // namespace llarp::net diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index 516a6d02d..0790c1238 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -877,7 +877,7 @@ namespace llarp auto self = shared_from_this(); bool result = true; for (const auto& hook : m_ObtainedExitHooks) - result &= hook(self, B); + result = hook(self, B) and result; m_ObtainedExitHooks.clear(); return result; } @@ -897,7 +897,7 @@ namespace llarp for (const auto& pkt : msg.X) { if (pkt.size() <= 8) - return false; + continue; auto counter = oxenc::load_big_to_host(pkt.data()); if (m_ExitTrafficHandler( self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol)) diff --git a/llarp/path/transit_hop.cpp b/llarp/path/transit_hop.cpp index 903326260..4efe3540f 100644 --- a/llarp/path/transit_hop.cpp +++ b/llarp/path/transit_hop.cpp @@ -397,11 +397,9 @@ namespace llarp if (pkt.size() <= 8) continue; auto counter = oxenc::load_big_to_host(pkt.data()); - sent &= endpoint->QueueOutboundTraffic( - info.rxID, - ManagedBuffer(llarp_buffer_t(pkt.data() + 8, pkt.size() - 8)), - counter, - msg.protocol); + llarp_buffer_t buf{pkt.data() + 8, pkt.size() - 8}; + sent = + endpoint->QueueOutboundTraffic(info.rxID, buf.copy(), counter, msg.protocol) and sent; } return sent; } diff --git a/llarp/quic/client.cpp b/llarp/quic/client.cpp index 8cf9d7f80..007e8e8b6 100644 --- a/llarp/quic/client.cpp +++ b/llarp/quic/client.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include diff --git a/llarp/quic/connection.cpp b/llarp/quic/connection.cpp index 71b84cfd4..f9567f4ac 100644 --- a/llarp/quic/connection.cpp +++ b/llarp/quic/connection.cpp @@ -333,13 +333,11 @@ namespace llarp::quic extern "C" inline void ngtcp_trace_logger([[maybe_unused]] void* user_data, const char* fmt, ...) { + std::array buf{}; va_list ap; va_start(ap, fmt); - if (char* msg; vasprintf(&msg, fmt, ap) >= 0) - { - LogTrace{msg}; - std::free(msg); - } + if (vsnprintf(buf.data(), buf.size(), fmt, ap) >= 0) + LogTrace(fmt::format("{}", buf.data())); va_end(ap); } #endif diff --git a/llarp/quic/endpoint.cpp b/llarp/quic/endpoint.cpp index 037011883..4fa35434a 100644 --- a/llarp/quic/endpoint.cpp +++ b/llarp/quic/endpoint.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include diff --git a/llarp/quic/tunnel.cpp b/llarp/quic/tunnel.cpp index 480d14177..9b09e5e2f 100644 --- a/llarp/quic/tunnel.cpp +++ b/llarp/quic/tunnel.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 69b4ccf4a..4826947a6 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -181,8 +181,8 @@ namespace llarp virtual ILinkManager& linkManager() = 0; - virtual RoutePoker& - routePoker() = 0; + virtual const std::shared_ptr& + routePoker() const = 0; virtual I_RCLookupHandler& rcLookupHandler() = 0; @@ -215,6 +215,10 @@ namespace llarp virtual void Stop() = 0; + /// indicate we are about to sleep for a while + virtual void + Freeze() = 0; + /// thaw from long sleep or network changed event virtual void Thaw() = 0; diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 352ac1625..1c55e38ad 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -7,83 +7,93 @@ namespace llarp { + namespace + { + auto logcat = log::Cat("route-poker"); + } + void - RoutePoker::AddRoute(huint32_t ip) + RoutePoker::AddRoute(net::ipv4addr_t ip) { - m_PokedRoutes[ip] = m_CurrentGateway; - if (m_CurrentGateway.h == 0) - { - llarp::LogDebug("RoutePoker::AddRoute no current gateway, cannot enable route."); - } - else if (m_Enabled or m_Enabling) + bool has_existing = m_PokedRoutes.count(ip); + // set up route and apply as needed + auto& gw = m_PokedRoutes[ip]; + if (m_CurrentGateway) { - llarp::LogInfo( - "RoutePoker::AddRoute enabled, enabling route to ", ip, " via ", m_CurrentGateway); - EnableRoute(ip, m_CurrentGateway); + // remove existing mapping as needed + if (has_existing) + DisableRoute(ip, gw); + // update and add new mapping + gw = *m_CurrentGateway; + EnableRoute(ip, gw); } else - { - llarp::LogDebug("RoutePoker::AddRoute disabled, not enabling route."); - } + gw = net::ipv4addr_t{}; } void - RoutePoker::DisableRoute(huint32_t ip, huint32_t gateway) + RoutePoker::DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway) { - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.DelRoute(ip, gateway); + if (ip.n and gateway.n and IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.DelRoute(ip, gateway); + } } void - RoutePoker::EnableRoute(huint32_t ip, huint32_t gateway) + RoutePoker::EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway) { - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.AddRoute(ip, gateway); + if (ip.n and gateway.n and IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.AddRoute(ip, gateway); + } } void - RoutePoker::DelRoute(huint32_t ip) + RoutePoker::DelRoute(net::ipv4addr_t ip) { const auto itr = m_PokedRoutes.find(ip); if (itr == m_PokedRoutes.end()) return; - if (m_Enabled) - DisableRoute(itr->first, itr->second); + DisableRoute(itr->first, itr->second); m_PokedRoutes.erase(itr); } void - RoutePoker::Init(AbstractRouter* router, bool enable) + RoutePoker::Start(AbstractRouter* router) { m_Router = router; - m_Enabled = enable; - m_CurrentGateway = {0}; + if (m_Router->IsServiceNode()) + return; + + m_Router->loop()->call_every(100ms, weak_from_this(), [this]() { Update(); }); } void RoutePoker::DeleteAllRoutes() { // DelRoute will check enabled, so no need here - for (const auto& [ip, gateway] : m_PokedRoutes) - DelRoute(ip); + for (const auto& item : m_PokedRoutes) + DelRoute(item.first); } void RoutePoker::DisableAllRoutes() { for (const auto& [ip, gateway] : m_PokedRoutes) + { DisableRoute(ip, gateway); + } } void - RoutePoker::EnableAllRoutes() + RoutePoker::RefreshAllRoutes() { - for (auto& [ip, gateway] : m_PokedRoutes) - { - gateway = m_CurrentGateway; - EnableRoute(ip, m_CurrentGateway); - } + for (const auto& item : m_PokedRoutes) + AddRoute(item.first); } RoutePoker::~RoutePoker() @@ -91,149 +101,145 @@ namespace llarp if (not m_Router or not m_Router->GetVPNPlatform()) return; - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + auto& route = m_Router->GetVPNPlatform()->RouteManager(); for (const auto& [ip, gateway] : m_PokedRoutes) { - if (gateway.h) + if (gateway.n and ip.n) route.DelRoute(ip, gateway); } route.DelBlackhole(); } - std::optional - RoutePoker::GetDefaultGateway() const + bool + RoutePoker::IsEnabled() const { if (not m_Router) - throw std::runtime_error("Attempting to use RoutePoker before calling Init"); + throw std::runtime_error{"Attempting to use RoutePoker before calling Init"}; + if (m_Router->IsServiceNode()) + return false; + if (const auto& conf = m_Router->GetConfig()) + return conf->network.m_EnableRoutePoker; - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - const auto gateways = route.GetGatewaysNotOnInterface(ep->GetIfName()); - if (gateways.empty()) - { - return std::nullopt; - } - if (auto* ptr = std::get_if(&gateways[0])) - { - return huint32_t{*ptr}; - } - return std::nullopt; + throw std::runtime_error{"Attempting to use RoutePoker with router with no config set"}; } void RoutePoker::Update() { if (not m_Router) - throw std::runtime_error("Attempting to use RoutePoker before calling Init"); + throw std::runtime_error{"Attempting to use RoutePoker before calling Init"}; - // check for network - const auto maybe = GetDefaultGateway(); - if (not maybe.has_value()) - { -#ifndef ANDROID - LogError("Network is down"); -#endif - // mark network lost - m_HasNetwork = false; + // ensure we have an endpoint + auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (ep == nullptr) + return; + // ensure we have a vpn platform + auto* platform = m_Router->GetVPNPlatform(); + if (platform == nullptr) + return; + // ensure we have a vpn interface + auto* vpn = ep->GetVPNInterface(); + if (vpn == nullptr) return; + + auto& route = platform->RouteManager(); + + // find current gateways + auto gateways = route.GetGatewaysNotOnInterface(*vpn); + std::optional next_gw; + for (auto& gateway : gateways) + { + if (auto* gw_ptr = std::get_if(&gateway)) + next_gw = *gw_ptr; } - const huint32_t gateway = *maybe; - const bool gatewayChanged = m_CurrentGateway.h != 0 and m_CurrentGateway != gateway; + auto is_equal = [](auto lhs, auto rhs) { + if (lhs == std::nullopt and rhs == std::nullopt) + return true; + if (lhs and rhs) + return *lhs == *rhs; + return false; + }; - if (m_CurrentGateway != gateway) + // update current gateway and apply state chnages as needed + if (not is_equal(m_CurrentGateway, next_gw)) { - LogInfo("found default gateway: ", gateway); - m_CurrentGateway = gateway; - if (m_Enabling) + if (next_gw and m_CurrentGateway) { - EnableAllRoutes(); - Up(); + log::info(logcat, "default gateway changed from {} to {}", *m_CurrentGateway, *next_gw); + m_CurrentGateway = next_gw; + m_Router->Thaw(); + if (m_Router->HasClientExit()) + Up(); + else + RefreshAllRoutes(); + } + else if (m_CurrentGateway) + { + log::warning(logcat, "default gateway {} has gone away", *m_CurrentGateway); + m_CurrentGateway = next_gw; + m_Router->Freeze(); + } + else if (next_gw) + { + log::info(logcat, "default gateway found at {}", *next_gw); + m_CurrentGateway = next_gw; } - } - // revive network connectitivity on gateway change or network wakeup - if (gatewayChanged or not m_HasNetwork) - { - LogInfo("our network changed, thawing router state"); - m_Router->Thaw(); - m_HasNetwork = true; } } void RoutePoker::SetDNSMode(bool exit_mode_on) const { - if (auto dns_server = m_Router->hiddenServiceContext().GetDefault()->DNS()) + auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (not ep) + return; + if (auto dns_server = ep->DNS()) dns_server->SetDNSMode(exit_mode_on); } void - RoutePoker::Enable() + RoutePoker::Up() { - if (m_Enabled) - return; - - if (m_Router->GetConfig()->network.m_EnableRoutePoker) + if (IsEnabled()) { - m_Enabling = true; - Update(); - m_Enabling = false; - m_Enabled = true; + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + + // black hole all routes if enabled + if (m_Router->GetConfig()->network.m_BlackholeRoutes) + route.AddBlackhole(); + + // explicit route pokes for first hops + m_Router->ForEachPeer( + [this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); }, false); + // add default route + const auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (auto* vpn = ep->GetVPNInterface()) + route.AddDefaultRouteViaInterface(*vpn); } - SetDNSMode(true); } - void - RoutePoker::Disable() - { - if (not m_Enabled) - return; - - DisableAllRoutes(); - m_Enabled = false; - - SetDNSMode(false); - } - - void - RoutePoker::Up() - { - if (not m_Router->GetConfig()->network.m_EnableRoutePoker) - return; - - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - - // black hole all routes if enabled - if (m_Router->GetConfig()->network.m_BlackholeRoutes) - route.AddBlackhole(); - - // explicit route pokes for first hops - m_Router->ForEachPeer( - [&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); }, - false); - // add default route - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - route.AddDefaultRouteViaInterface(ep->GetIfName()); - } - void RoutePoker::Down() { - if (not m_Router->GetConfig()->network.m_EnableRoutePoker) - return; - // unpoke routes for first hops m_Router->ForEachPeer( - [&](auto session, auto) mutable { DelRoute(session->GetRemoteEndpoint().asIPv4()); }, - false); + [this](auto session, auto) { DelRoute(session->GetRemoteEndpoint().getIPv4()); }, false); + // remove default route - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.DelDefaultRouteViaInterface(ep->GetIfName()); - // delete route blackhole - route.DelBlackhole(); + if (IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + const auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (auto* vpn = ep->GetVPNInterface()) + route.DelDefaultRouteViaInterface(*vpn); + + // delete route blackhole + route.DelBlackhole(); + } + SetDNSMode(false); } } // namespace llarp diff --git a/llarp/router/route_poker.hpp b/llarp/router/route_poker.hpp index 17969a2b5..7473617e1 100644 --- a/llarp/router/route_poker.hpp +++ b/llarp/router/route_poker.hpp @@ -10,32 +10,19 @@ namespace llarp { struct AbstractRouter; - struct RoutePoker + struct RoutePoker : public std::enable_shared_from_this { void - AddRoute(huint32_t ip); + AddRoute(net::ipv4addr_t ip); void - DelRoute(huint32_t ip); + DelRoute(net::ipv4addr_t ip); void - Init(AbstractRouter* router, bool enable = false); + Start(AbstractRouter* router); ~RoutePoker(); - void - Update(); - - // sets stored routes and causes AddRoute to actually - // set routes rather than just storing them - void - Enable(); - - // unsets stored routes, and causes AddRoute to simply - // remember the desired routes rather than setting them. - void - Disable(); - /// explicitly put routes up void Up(); @@ -50,6 +37,12 @@ namespace llarp SetDNSMode(bool using_exit_mode) const; private: + void + Update(); + + bool + IsEnabled() const; + void DeleteAllRoutes(); @@ -57,25 +50,18 @@ namespace llarp DisableAllRoutes(); void - EnableAllRoutes(); + RefreshAllRoutes(); void - EnableRoute(huint32_t ip, huint32_t gateway); + EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway); void - DisableRoute(huint32_t ip, huint32_t gateway); - - std::optional - GetDefaultGateway() const; + DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway); - std::unordered_map m_PokedRoutes; - huint32_t m_CurrentGateway; + std::unordered_map m_PokedRoutes; - bool m_Enabled = false; - bool m_Enabling = false; + std::optional m_CurrentGateway; AbstractRouter* m_Router = nullptr; - - bool m_HasNetwork = true; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 62f17e9e5..bb9e0f514 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -48,22 +48,21 @@ namespace llarp static auto logcat = log::Cat("router"); Router::Router(EventLoop_ptr loop, std::shared_ptr vpnPlatform) - : ready(false) - , m_lmq(std::make_shared()) - , _loop(std::move(loop)) - , _vpnPlatform(std::move(vpnPlatform)) - , paths(this) - , _exitContext(this) - , _dht(llarp_dht_context_new(this)) - , m_DiskThread(m_lmq->add_tagged_thread("disk")) - , inbound_link_msg_parser(this) - , _hiddenServiceContext(this) - , m_RPCServer(new rpc::RpcServer(m_lmq, this)) -#ifdef LOKINET_HIVE - , _randomStartDelay(std::chrono::milliseconds((llarp::randint() % 1250) + 2000)) -#else - , _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) -#endif + : ready{false} + , m_lmq{std::make_shared()} + , _loop{std::move(loop)} + , _vpnPlatform{std::move(vpnPlatform)} + , paths{this} + , _exitContext{this} + , _dht{llarp_dht_context_new(this)} + , m_DiskThread{m_lmq->add_tagged_thread("disk")} + , inbound_link_msg_parser{this} + , _hiddenServiceContext{this} + , m_RoutePoker{std::make_shared()} + , m_RPCServer{new rpc::RpcServer{m_lmq, this}} + , _randomStartDelay{ + platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000} + : 0s} { m_keyManager = std::make_shared(); // for lokid, so we don't close the connection when syncing the whitelist @@ -219,10 +218,22 @@ namespace llarp } return inbound_link_msg_parser.ProcessFrom(session, buf); } + void + Router::Freeze() + { + if (IsServiceNode()) + return; + linkManager().ForEachPeer([](auto peer) { + if (peer) + peer->Close(); + }); + } void Router::Thaw() { + if (IsServiceNode()) + return; // get pubkeys we are connected to std::unordered_set peerPubkeys; linkManager().ForEachPeer([&peerPubkeys](auto peer) { @@ -420,8 +431,6 @@ namespace llarp if (not EnsureIdentity()) throw std::runtime_error("EnsureIdentity() failed"); - - m_RoutePoker.Init(this); return true; } @@ -1003,19 +1012,6 @@ namespace llarp _linkManager.CheckPersistingSessions(now); - if (not isSvcNode) - { - if (HasClientExit()) - { - m_RoutePoker.Enable(); - } - else - { - m_RoutePoker.Disable(); - } - m_RoutePoker.Update(); - } - size_t connected = NumberOfConnectedRouters(); if (not isSvcNode) { @@ -1123,7 +1119,7 @@ namespace llarp if (const auto maybe = nodedb()->Get(remote); maybe.has_value()) { for (const auto& addr : maybe->addrs) - m_RoutePoker.DelRoute(addr.toIpAddress().toIP()); + m_RoutePoker->DelRoute(addr.IPv4()); } } @@ -1242,7 +1238,7 @@ namespace llarp throw std::runtime_error{"cannot override public ip, it is already set"}; ai.fromSockAddr(*_ourAddress); } - if (RouterContact::BlockBogons && IsBogon(ai.ip)) + if (RouterContact::BlockBogons && Net().IsBogon(ai.ip)) throw std::runtime_error{var::visit( [](auto&& ip) { return "cannot use " + ip.ToString() @@ -1254,12 +1250,6 @@ namespace llarp } }); - if (ExitEnabled() and IsServiceNode()) - { - LogError("exit mode not supported while service node"); - return false; - } - if (IsServiceNode() and not _rc.IsPublicRouter()) { LogError("we are configured as relay but have no reachable addresses"); @@ -1345,6 +1335,7 @@ namespace llarp LogInfo("have ", _nodedb->NumLoaded(), " routers"); _loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); }); + m_RoutePoker->Start(this); _running.store(true); _startedAt = Now(); #if defined(WITH_SYSTEMD) @@ -1677,11 +1668,8 @@ namespace llarp [this](llarp::RouterContact rc) { if (IsServiceNode()) return; - llarp::LogTrace( - "Before connect, outbound link adding route to (", - rc.addrs[0].toIpAddress().toIP(), - ") via gateway."); - m_RoutePoker.AddRoute(rc.addrs[0].toIpAddress().toIP()); + for (const auto& addr : rc.addrs) + m_RoutePoker->AddRoute(addr.IPv4()); }, util::memFn(&Router::ConnectionEstablished, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this), diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 2b280a4c2..8f56024ea 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -274,27 +274,13 @@ namespace llarp /// bootstrap RCs BootstrapList bootstrapRCList; - bool - ExitEnabled() const - { - return false; // FIXME - have to fix the FIXME because FIXME - throw std::runtime_error("FIXME: this needs to be derived from config"); - /* - // TODO: use equal_range ? - auto itr = netConfig.find("exit"); - if (itr == netConfig.end()) - return false; - return IsTrueValue(itr->second.c_str()); - */ - } - - RoutePoker& - routePoker() override + const std::shared_ptr& + routePoker() const override { return m_RoutePoker; } - RoutePoker m_RoutePoker; + std::shared_ptr m_RoutePoker; void TriggerPump() override; @@ -401,6 +387,9 @@ namespace llarp bool StartRpcServer() override; + void + Freeze() override; + void Thaw() override; diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index b40339a59..f565255d2 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -485,9 +485,11 @@ namespace llarp if (IsExpired(now) and not allowExpired) return false; + // TODO: make net* overridable + const auto* net = net::Platform::Default_ptr(); for (const auto& a : addrs) { - if (IsBogon(a.ip) && BlockBogons) + if (net->IsBogon(a.ip) && BlockBogons) { llarp::LogError("invalid address info: ", a); return false; diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 175ccdf91..f688dfa46 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -516,8 +516,8 @@ namespace llarp::rpc { auto mapExit = [=](service::Address addr) mutable { ep->MapExitRange(range, addr); - r->routePoker().Enable(); - r->routePoker().Up(); + + r->routePoker()->Up(); bool shouldSendAuth = false; if (token.has_value()) { @@ -531,7 +531,7 @@ namespace llarp::rpc reply(CreateJSONError("we dont have an exit?")); }; auto onBadResult = [r, reply, ep, range](std::string reason) { - r->routePoker().Down(); + r->routePoker()->Down(); ep->UnmapExitRange(range); reply(CreateJSONError(reason)); }; @@ -614,7 +614,7 @@ namespace llarp::rpc } else if (not map) { - r->routePoker().Down(); + r->routePoker()->Down(); ep->UnmapExitRange(range); reply(CreateJSONResponse("OK")); } diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 47599397a..1395dadd1 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -124,8 +123,7 @@ namespace llarp // add supported ethertypes if (HasIfAddr()) { - const auto ourIP = net::HUIntToIn6(GetIfAddr()); - if (ipv6_is_mapped_ipv4(ourIP)) + if (IPRange::V4MappedRange().Contains(GetIfAddr())) { introSet().supportedProtocols.push_back(ProtocolType::TrafficV4); } @@ -2074,6 +2072,11 @@ namespace llarp LogInfo(Name(), " map ", range, " to exit at ", exit); m_ExitMap.Insert(range, exit); } + bool + Endpoint::HasFlowToService(Address addr) const + { + return HasOutboundConvo(addr) or HasInboundConvo(addr); + } void Endpoint::UnmapExitRange(IPRange range) diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 64b8b68ab..00a2a3c68 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -174,6 +174,12 @@ namespace llarp return nullptr; }; + virtual vpn::NetworkInterface* + GetVPNInterface() + { + return nullptr; + } + bool PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override; @@ -355,6 +361,9 @@ namespace llarp bool HasPathToSNode(const RouterID remote) const; + bool + HasFlowToService(const Address remote) const; + void PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound) override; diff --git a/llarp/service/pendingbuffer.hpp b/llarp/service/pendingbuffer.hpp index ed9a71522..827b2fab1 100644 --- a/llarp/service/pendingbuffer.hpp +++ b/llarp/service/pendingbuffer.hpp @@ -2,31 +2,23 @@ #include "protocol.hpp" #include - -#include -#include #include -namespace llarp +namespace llarp::service { - namespace service + struct PendingBuffer { - struct PendingBuffer - { - std::vector payload; - ProtocolType protocol; + std::vector payload; + ProtocolType protocol; - PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload(buf.sz), protocol(t) - { - std::copy(buf.base, buf.base + buf.sz, std::back_inserter(payload)); - } + inline llarp_buffer_t + Buffer() + { + return llarp_buffer_t{payload}; + } - ManagedBuffer - Buffer() - { - return ManagedBuffer{llarp_buffer_t(payload)}; - } - }; - } // namespace service + PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload{buf.copy()}, protocol{t} + {} + }; -} // namespace llarp +} // namespace llarp::service diff --git a/llarp/service/sendcontext.cpp b/llarp/service/sendcontext.cpp index 16b524c16..20fb744a0 100644 --- a/llarp/service/sendcontext.cpp +++ b/llarp/service/sendcontext.cpp @@ -5,6 +5,7 @@ #include "endpoint.hpp" #include #include +#include namespace llarp { diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 15cfca265..05c4162ea 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -132,6 +132,14 @@ operator==(const llarp_buffer_t& buff, std::string_view data) { return std::string_view{reinterpret_cast(buff.cur), buff.size_left()} == data; } + +llarp::byte_view_t +llarp_buffer_t::view() const +{ + return {base, sz}; + +} + namespace llarp { std::vector diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 08010616f..5d5899209 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -15,59 +15,17 @@ #include #include #include +#include -/** - * buffer.h - * - * generic memory buffer - * - * TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a - * fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains - * both start and current position, plus has some value reading/writing methods). - */ - -/** - llarp_buffer_t represents a region of memory that is ONLY - valid in the current scope. - - make sure to follow the rules: - - ALWAYS copy the contents of the buffer if that data is to be used outside the - current scope. - - ALWAYS pass a llarp_buffer_t * if you plan on modifying the data associated - with the buffer - - ALWAYS pass a llarp_buffer_t * if you plan on advancing the stream position - - ALWAYS pass a const llarp_buffer_t & if you are doing a read only operation - that does not modify the buffer - - ALWAYS pass a const llarp_buffer_t & if you don't want to advance the stream - position - - ALWAYS bail out of the current operation if you run out of space in a buffer - - ALWAYS assume the pointers in the buffer are stack allocated memory - (yes even if you know they are not) - - NEVER malloc() the pointers in the buffer when using it - - NEVER realloc() the pointers in the buffer when using it - - NEVER free() the pointers in the buffer when using it - - NEVER use llarp_buffer_t ** (double pointers) - - NEVER use llarp_buffer_t ** (double pointers) - - ABSOLUTELY NEVER USE DOUBLE POINTERS. - - */ - -struct ManagedBuffer; +namespace llarp +{ + using byte_view_t = std::basic_string_view; +} -struct llarp_buffer_t +/// TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a +/// fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains +/// both start and current position, plus has some value reading/writing methods). +struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t { /// starting memory address byte_t* base{nullptr}; @@ -76,26 +34,22 @@ struct llarp_buffer_t /// max size of buffer size_t sz{0}; - byte_t - operator[](size_t x) + byte_t operator[](size_t x) { return *(this->base + x); } llarp_buffer_t() = default; - llarp_buffer_t(byte_t* b, byte_t* c, size_t s) : base(b), cur(c), sz(s) + llarp_buffer_t(byte_t * b, byte_t * c, size_t s) : base(b), cur(c), sz(s) {} - llarp_buffer_t(const ManagedBuffer&) = delete; - llarp_buffer_t(ManagedBuffer&&) = delete; - /// Construct referencing some 1-byte, trivially copyable (e.g. char, unsigned char, byte_t) /// pointer type and a buffer size. template < typename T, typename = std::enable_if_t>> - llarp_buffer_t(T* buf, size_t _sz) + llarp_buffer_t(T * buf, size_t _sz) : base(reinterpret_cast(const_cast*>(buf))) , cur(base) , sz(_sz) @@ -105,82 +59,73 @@ struct llarp_buffer_t template < typename T, typename = std::void_t().data() + std::declval().size())>> - llarp_buffer_t(T&& t) : llarp_buffer_t{t.data(), t.size()} + llarp_buffer_t(T && t) : llarp_buffer_t{t.data(), t.size()} {} - byte_t* - begin() + byte_t* begin() { return base; } - byte_t* - begin() const + byte_t* begin() const { return base; } - byte_t* - end() + byte_t* end() { return base + sz; } - byte_t* - end() const + byte_t* end() const { return base + sz; } - size_t - size_left() const; + size_t size_left() const; template - bool - read_into(OutputIt begin, OutputIt end); + bool read_into(OutputIt begin, OutputIt end); template - bool - write(InputIt begin, InputIt end); + bool write(InputIt begin, InputIt end); #ifndef _WIN32 - bool - writef(const char* fmt, ...) __attribute__((format(printf, 2, 3))); + bool writef(const char* fmt, ...) __attribute__((format(printf, 2, 3))); #elif defined(__MINGW64__) || defined(__MINGW32__) - bool - writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3))); + bool writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3))); #else - bool - writef(const char* fmt, ...); + bool writef(const char* fmt, ...); #endif - bool - put_uint16(uint16_t i); - bool - put_uint32(uint32_t i); + bool put_uint16(uint16_t i); + bool put_uint32(uint32_t i); - bool - put_uint64(uint64_t i); + bool put_uint64(uint64_t i); - bool - read_uint16(uint16_t& i); - bool - read_uint32(uint32_t& i); + bool read_uint16(uint16_t & i); + bool read_uint32(uint32_t & i); - bool - read_uint64(uint64_t& i); + bool read_uint64(uint64_t & i); - size_t - read_until(char delim, byte_t* result, size_t resultlen); + size_t read_until(char delim, byte_t* result, size_t resultlen); /// make a copy of this buffer - std::vector - copy() const; + std::vector copy() const; + + /// get a read only view over the entire region + llarp::byte_view_t view() const; + + bool operator==(std::string_view data) const + { + return std::string_view{reinterpret_cast(base), sz} == data; + } private: friend struct ManagedBuffer; llarp_buffer_t(const llarp_buffer_t&) = default; - llarp_buffer_t(llarp_buffer_t&&) = default; + llarp_buffer_t(llarp_buffer_t &&) = default; }; + bool operator==(const llarp_buffer_t& buff, std::string_view data); @@ -214,7 +159,7 @@ llarp_buffer_t::write(InputIt begin, InputIt end) /** Provide a copyable/moveable wrapper around `llarp_buffer_t`. */ -struct ManagedBuffer +struct [[deprecated("deprecated along with llarp_buffer_t")]] ManagedBuffer { llarp_buffer_t underlying; @@ -223,7 +168,7 @@ struct ManagedBuffer explicit ManagedBuffer(const llarp_buffer_t& b) : underlying(b) {} - ManagedBuffer(ManagedBuffer&&) = default; + ManagedBuffer(ManagedBuffer &&) = default; ManagedBuffer(const ManagedBuffer&) = default; operator const llarp_buffer_t&() const @@ -234,6 +179,8 @@ struct ManagedBuffer namespace llarp { + using byte_view_t = std::basic_string_view; + // Wrapper around a std::unique_ptr that owns its own memory and is also implicitly // convertible to a llarp_buffer_t. struct OwnedBuffer diff --git a/llarp/util/easter_eggs.cpp b/llarp/util/easter_eggs.cpp new file mode 100644 index 000000000..c3a658db9 --- /dev/null +++ b/llarp/util/easter_eggs.cpp @@ -0,0 +1,39 @@ +#include "lokinet_init.h" + +#if defined(_WIN32) +#include +#include +#include + +#define WineKiller \ + DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit + +struct WineKiller +{ + WineKiller() + { + if (auto hntdll = GetModuleHandle("ntdll.dll")) + { + if (GetProcAddress(hntdll, "wine_get_version")) + { + static const char* text = + "dont run lokinet in wine like wtf man we support linux and pretty " + "much every flavour of BSD, and even some flavours of unix system " + "5.x.\nThis Program Will now crash lmao."; + static const char* title = "srsly fam wtf"; + MessageBoxA(NULL, text, title, MB_ICONHAND); + abort(); + } + } + } +}; + +// i heckin love static initalization +WineKiller lmao{}; +#endif + +extern "C" int +Lokinet_INIT(void) +{ + return 0; +} diff --git a/llarp/util/lokinet_init.c b/llarp/util/lokinet_init.c deleted file mode 100644 index 62fb3f7b2..000000000 --- a/llarp/util/lokinet_init.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "lokinet_init.h" -#if defined(_WIN32) -#include -#include -#include - -int -Lokinet_INIT(void) -{ - static const char*(CDECL * pwine_get_version)(void); - HMODULE hntdll = GetModuleHandle("ntdll.dll"); - if (hntdll) - { - pwine_get_version = (void*)GetProcAddress(hntdll, "wine_get_version"); - if (pwine_get_version) - { - static const char* text = - "dont run lokinet in wine like wtf man we support linux and pretty " - "much every flavour of BSD, and even some flavours of unix system " - "5.x.\nThis Program Will now crash lmao."; - static const char* title = "srsly fam wtf"; - MessageBoxA(NULL, text, title, MB_ICONHAND); - abort(); - } - } - return 0; -} -#else -int -Lokinet_INIT(void) -{ - return 0; -} - -#endif diff --git a/llarp/util/lokinet_init.h b/llarp/util/lokinet_init.h index 42154facc..5fd0a73de 100644 --- a/llarp/util/lokinet_init.h +++ b/llarp/util/lokinet_init.h @@ -3,15 +3,6 @@ #ifdef __cplusplus extern "C" { -#endif - -#ifndef Lokinet_INIT -#if defined(_WIN32) -#define Lokinet_INIT \ - DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit -#else -#define Lokinet_INIT _lokinet_non_shit_platform_INIT -#endif #endif int diff --git a/llarp/util/str.cpp b/llarp/util/str.cpp index 3ffa8eeda..56ca034cf 100644 --- a/llarp/util/str.cpp +++ b/llarp/util/str.cpp @@ -7,6 +7,12 @@ #include #include +#ifdef _WIN32 +#include +#include +#include +#endif + namespace llarp { bool @@ -168,4 +174,22 @@ namespace llarp dur.count()); } + std::wstring + to_wide(std::string data) + { + std::wstring buf; + buf.resize(data.size()); +#ifdef _WIN32 + // win32 specific codepath because balmer made windows so that man may suffer + if (MultiByteToWideChar(CP_UTF8, 0, data.c_str(), data.size(), buf.data(), buf.size()) == 0) + throw win32::error{GetLastError(), "failed to convert string to wide string"}; + +#else + // this dumb but probably works (i guess?) + std::transform( + data.begin(), data.end(), buf.begin(), [](const auto& ch) -> wchar_t { return ch; }); +#endif + return buf; + } + } // namespace llarp diff --git a/llarp/util/str.hpp b/llarp/util/str.hpp index cb31b8412..eed00482e 100644 --- a/llarp/util/str.hpp +++ b/llarp/util/str.hpp @@ -5,7 +5,6 @@ #include #include #include - #include namespace llarp @@ -141,4 +140,8 @@ namespace llarp std::string friendly_duration(std::chrono::nanoseconds dur); + /// convert a "normal" string into a wide string + std::wstring + to_wide(std::string data); + } // namespace llarp diff --git a/llarp/vpn/android.hpp b/llarp/vpn/android.hpp index 043249b0d..3364e75fa 100644 --- a/llarp/vpn/android.hpp +++ b/llarp/vpn/android.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include "platform.hpp" #include "common.hpp" #include @@ -12,10 +12,9 @@ namespace llarp::vpn class AndroidInterface : public NetworkInterface { const int m_fd; - const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now public: - AndroidInterface(InterfaceInfo info, int fd) : m_fd(fd), m_Info(info) + AndroidInterface(InterfaceInfo info, int fd) : NetworkInterface{std::move(info)}, m_fd{fd} { if (m_fd == -1) throw std::runtime_error( @@ -37,38 +36,34 @@ namespace llarp::vpn net::IPPacket ReadNextPacket() override { - net::IPPacket pkt{}; - const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); - if (sz >= 0) - pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); - return pkt; + std::vector pkt; + pkt.reserve(net::IPPacket::MaxSize); + const auto n = read(m_fd, pkt.data(), pkt.capacity()); + pkt.resize(std::min(std::max(ssize_t{}, n), static_cast(pkt.capacity()))); + return net::IPPacket{std::move(pkt)}; } bool WritePacket(net::IPPacket pkt) override { - const auto sz = write(m_fd, pkt.buf, pkt.sz); + const auto sz = write(m_fd, pkt.data(), pkt.size()); if (sz <= 0) return false; - return sz == static_cast(pkt.sz); - } - - std::string - IfName() const override - { - return m_Info.ifname; + return sz == static_cast(pkt.size()); } }; class AndroidRouteManager : public IRouteManager { - void AddRoute(IPVariant_t, IPVariant_t) override{}; + void AddRoute(net::ipaddr_t, net::ipaddr_t) override{}; - void DelRoute(IPVariant_t, IPVariant_t) override{}; + void DelRoute(net::ipaddr_t, net::ipaddr_t) override{}; - void AddDefaultRouteViaInterface(std::string) override{}; + void + AddDefaultRouteViaInterface(NetworkInterface&) override{}; - void DelDefaultRouteViaInterface(std::string) override{}; + void + DelDefaultRouteViaInterface(NetworkInterface&) override{}; void AddRouteViaInterface(NetworkInterface&, IPRange) override{}; @@ -76,9 +71,10 @@ namespace llarp::vpn void DelRouteViaInterface(NetworkInterface&, IPRange) override{}; - std::vector GetGatewaysNotOnInterface(std::string) override + std::vector + GetGatewaysNotOnInterface(NetworkInterface&) override { - return std::vector{}; + return std::vector{}; }; }; @@ -88,7 +84,7 @@ namespace llarp::vpn AndroidRouteManager _routeManager{}; public: - AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD) + AndroidPlatform(llarp::Context* ctx) : fd{ctx->androidFD} {} std::shared_ptr diff --git a/llarp/vpn/i_packet_io.hpp b/llarp/vpn/i_packet_io.hpp new file mode 100644 index 000000000..5789b193c --- /dev/null +++ b/llarp/vpn/i_packet_io.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include + +namespace llarp::vpn +{ + class I_Packet_IO + { + public: + virtual ~I_Packet_IO() = default; + + /// start any platform specific operations before running + virtual void + Start(){}; + + /// stop operation and tear down anything that Start() set up. + virtual void + Stop(){}; + + /// read next ip packet, return an empty packet if there are none ready. + virtual net::IPPacket + ReadNextPacket() = 0; + + /// write a packet to the interface + /// returns false if we dropped it + virtual bool + WritePacket(net::IPPacket pkt) = 0; + + /// get pollable fd for reading + virtual int + PollFD() const = 0; + }; + +} // namespace llarp::vpn diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 44b5ae220..5fc06d674 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "platform.hpp" #include #include #include @@ -36,11 +36,10 @@ namespace llarp::vpn class LinuxInterface : public NetworkInterface { const int m_fd; - const InterfaceInfo m_Info; public: LinuxInterface(InterfaceInfo info) - : NetworkInterface{}, m_fd{::open("/dev/net/tun", O_RDWR)}, m_Info{std::move(info)} + : NetworkInterface{std::move(info)}, m_fd{::open("/dev/net/tun", O_RDWR)} { if (m_fd == -1) @@ -60,7 +59,7 @@ namespace llarp::vpn control.ioctl(SIOCGIFFLAGS, &ifr); const int flags = ifr.ifr_flags; control.ioctl(SIOCGIFINDEX, &ifr); - const int ifindex = ifr.ifr_ifindex; + m_Info.index = ifr.ifr_ifindex; for (const auto& ifaddr : m_Info.addrs) { @@ -79,7 +78,7 @@ namespace llarp::vpn { ifr6.addr = net::HUIntToIn6(ifaddr.range.addr); ifr6.prefixlen = llarp::bits::count_bits(ifaddr.range.netmask_bits); - ifr6.ifindex = ifindex; + ifr6.ifindex = m_Info.index; try { IOCTL{AF_INET6}.ioctl(SIOCSIFADDR, &ifr6); @@ -108,30 +107,29 @@ namespace llarp::vpn net::IPPacket ReadNextPacket() override { - net::IPPacket pkt; - const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); - if (sz >= 0) - pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); - else if (errno == EAGAIN || errno == EWOULDBLOCK) - pkt.sz = 0; - else + std::vector pkt; + pkt.resize(net::IPPacket::MaxSize); + const auto sz = read(m_fd, pkt.data(), pkt.capacity()); + if (errno) + { + if (errno == EAGAIN or errno == EWOULDBLOCK) + { + errno = 0; + return net::IPPacket{}; + } throw std::error_code{errno, std::system_category()}; + } + pkt.resize(sz); return pkt; } bool WritePacket(net::IPPacket pkt) override { - const auto sz = write(m_fd, pkt.buf, pkt.sz); + const auto sz = write(m_fd, pkt.data(), pkt.size()); if (sz <= 0) return false; - return sz == static_cast(pkt.sz); - } - - std::string - IfName() const override - { - return m_Info.ifname; + return sz == static_cast(pkt.size()); } }; @@ -182,19 +180,18 @@ namespace llarp::vpn unsigned char bitlen; unsigned char data[sizeof(struct in6_addr)]; - _inet_addr(huint32_t addr, size_t bits = 32) + _inet_addr(net::ipv4addr_t addr, size_t bits = 32) { family = AF_INET; bitlen = bits; - oxenc::write_host_as_big(addr.h, data); + std::memcpy(data, &addr.n, 4); } - _inet_addr(huint128_t addr, size_t bits = 128) + _inet_addr(net::ipv6addr_t addr, size_t bits = 128) { family = AF_INET6; bitlen = bits; - const nuint128_t net = ToNet(addr); - std::memcpy(data, &net, 16); + std::memcpy(data, &addr.n, 16); } }; @@ -290,76 +287,68 @@ namespace llarp::vpn } void - DefaultRouteViaInterface(std::string ifname, int cmd, int flags) + DefaultRouteViaInterface(NetworkInterface& vpn, int cmd, int flags) { - int if_idx = if_nametoindex(ifname.c_str()); - const auto maybe = Net().GetInterfaceAddr(ifname); + const auto& info = vpn.Info(); + + const auto maybe = Net().GetInterfaceAddr(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{maybe->asIPv4()}; - const _inet_addr lower{ipaddr_ipv4_bits(0, 0, 0, 0), 1}; - const _inet_addr upper{ipaddr_ipv4_bits(128, 0, 0, 0), 1}; + const _inet_addr gateway{maybe->getIPv4()}; + const _inet_addr lower{ToNet(ipaddr_ipv4_bits(0, 0, 0, 0)), 1}; + const _inet_addr upper{ToNet(ipaddr_ipv4_bits(128, 0, 0, 0)), 1}; - Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, if_idx); - Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, if_idx); + Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, info.index); + Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, info.index); - if (const auto maybe6 = Net().GetInterfaceIPv6Address(ifname)) + if (const auto maybe6 = Net().GetInterfaceIPv6Address(info.ifname)) { - const _inet_addr gateway6{*maybe6, 128}; + const _inet_addr gateway6{ToNet(*maybe6), 128}; for (const std::string str : {"::", "4000::", "8000::", "c000::"}) { - huint128_t _hole{}; - _hole.FromString(str); - const _inet_addr hole6{_hole, 2}; - Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, if_idx); + const _inet_addr hole6{net::ipv6addr_t::from_string(str), 2}; + Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, info.index); } } } void - RouteViaInterface(int cmd, int flags, std::string ifname, IPRange range) + RouteViaInterface(int cmd, int flags, NetworkInterface& vpn, IPRange range) { - int if_idx = if_nametoindex(ifname.c_str()); + const auto& info = vpn.Info(); if (range.IsV4()) { - const auto maybe = Net().GetInterfaceAddr(ifname); + const auto maybe = Net().GetInterfaceAddr(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{maybe->asIPv4()}; + const auto gateway = var::visit([](auto&& ip) { return _inet_addr{ip}; }, maybe->getIP()); const _inet_addr addr{ - net::TruncateV6(range.addr), bits::count_bits(net::TruncateV6(range.netmask_bits))}; + ToNet(net::TruncateV6(range.addr)), + bits::count_bits(net::TruncateV6(range.netmask_bits))}; - Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index); } else { - const auto maybe = Net().GetInterfaceIPv6Address(ifname); + const auto maybe = Net().GetInterfaceIPv6Address(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{*maybe, 128}; - const _inet_addr addr{range.addr, bits::count_bits(range.netmask_bits)}; - Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + const _inet_addr gateway{ToNet(*maybe), 128}; + const _inet_addr addr{ToNet(range.addr), bits::count_bits(range.netmask_bits)}; + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index); } } void - Route(int cmd, int flags, IPVariant_t ip, IPVariant_t gateway) + Route(int cmd, int flags, net::ipaddr_t ip, net::ipaddr_t gateway) { - // do bullshit double std::visit because lol variants - std::visit( - [gateway, cmd, flags, this](auto&& ip) { - const _inet_addr toAddr{ip}; - std::visit( - [toAddr, cmd, flags, this](auto&& gateway) { - const _inet_addr gwAddr{gateway}; - Route(cmd, flags, toAddr, gwAddr, GatewayMode::eFirstHop, 0); - }, - gateway); - }, - ip); + auto _ip = var::visit([](auto&& i) { return _inet_addr{i}; }, ip); + auto _gw = var::visit([](auto&& i) { return _inet_addr{i}; }, gateway); + + Route(cmd, flags, _ip, _gw, GatewayMode::eFirstHop, 0); } public: @@ -375,45 +364,46 @@ namespace llarp::vpn } void - AddRoute(IPVariant_t ip, IPVariant_t gateway) override + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { Route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ip, gateway); } void - DelRoute(IPVariant_t ip, IPVariant_t gateway) override + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { Route(RTM_DELROUTE, 0, ip, gateway); } void - AddDefaultRouteViaInterface(std::string ifname) override + AddDefaultRouteViaInterface(NetworkInterface& vpn) override { - DefaultRouteViaInterface(ifname, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL); + DefaultRouteViaInterface(vpn, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL); } void - DelDefaultRouteViaInterface(std::string ifname) override + DelDefaultRouteViaInterface(NetworkInterface& vpn) override { - DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0); + DefaultRouteViaInterface(vpn, RTM_DELROUTE, 0); } void AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn.IfName(), range); + RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn, range); } void DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_DELROUTE, 0, vpn.IfName(), range); + RouteViaInterface(RTM_DELROUTE, 0, vpn, range); } - std::vector - GetGatewaysNotOnInterface(std::string ifname) override + std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) override { - std::vector gateways{}; + const auto& ifname = vpn.Info().ifname; + std::vector gateways{}; std::ifstream inf{"/proc/net/route"}; for (std::string line; std::getline(inf, line);) { @@ -425,7 +415,7 @@ namespace llarp::vpn { huint32_t x{}; oxenc::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); - gateways.emplace_back(x); + gateways.emplace_back(net::ipv4addr_t::from_host(x.h)); } } } diff --git a/llarp/vpn/packet_intercept.hpp b/llarp/vpn/packet_intercept.hpp new file mode 100644 index 000000000..7150ae54a --- /dev/null +++ b/llarp/vpn/packet_intercept.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include + +namespace llarp::vpn +{ + using PacketSendFunc_t = std::function)>; + using PacketInterceptFunc_t = std::function, PacketSendFunc_t)>; + + class I_PacketIntercepter + { + public: + virtual ~I_PacketIntercepter() = default; + + /// start intercepting packets and call a callback for each one we get + /// the callback passes in an ip packet and a function that we can use to send an ip packet to + /// its origin + virtual void + start(PacketInterceptFunc_t f) = 0; + + /// stop intercepting packets + virtual void + stop() = 0; + }; + +} // namespace llarp::vpn diff --git a/llarp/vpn/packet_router.cpp b/llarp/vpn/packet_router.cpp index 54c1839e9..7a618022c 100644 --- a/llarp/vpn/packet_router.cpp +++ b/llarp/vpn/packet_router.cpp @@ -4,14 +4,15 @@ namespace llarp::vpn { struct UDPPacketHandler : public Layer4Handler { - PacketHandlerFunc m_BaseHandler; - std::unordered_map m_LocalPorts; + PacketHandlerFunc_t m_BaseHandler; + std::unordered_map m_LocalPorts; - explicit UDPPacketHandler(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)} + explicit UDPPacketHandler(PacketHandlerFunc_t baseHandler) + : m_BaseHandler{std::move(baseHandler)} {} void - AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override + AddSubHandler(nuint16_t localport, PacketHandlerFunc_t handler) override { m_LocalPorts.emplace(localport, std::move(handler)); } @@ -19,12 +20,15 @@ namespace llarp::vpn void HandleIPPacket(llarp::net::IPPacket pkt) override { - const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2; - const nuint16_t dstPort{*reinterpret_cast(ptr)}; - if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end()) + auto dstport = pkt.DstPort(); + if (not dstport) { - itr->second(std::move(pkt)); + m_BaseHandler(std::move(pkt)); + return; } + + if (auto itr = m_LocalPorts.find(*dstport); itr != m_LocalPorts.end()) + itr->second(std::move(pkt)); else m_BaseHandler(std::move(pkt)); } @@ -32,9 +36,9 @@ namespace llarp::vpn struct GenericLayer4Handler : public Layer4Handler { - PacketHandlerFunc m_BaseHandler; + PacketHandlerFunc_t m_BaseHandler; - explicit GenericLayer4Handler(PacketHandlerFunc baseHandler) + explicit GenericLayer4Handler(PacketHandlerFunc_t baseHandler) : m_BaseHandler{std::move(baseHandler)} {} @@ -45,7 +49,8 @@ namespace llarp::vpn } }; - PacketRouter::PacketRouter(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)} + PacketRouter::PacketRouter(PacketHandlerFunc_t baseHandler) + : m_BaseHandler{std::move(baseHandler)} {} void @@ -53,15 +58,13 @@ namespace llarp::vpn { const auto proto = pkt.Header()->protocol; if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end()) - { itr->second->HandleIPPacket(std::move(pkt)); - } else m_BaseHandler(std::move(pkt)); } void - PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func) + PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func) { constexpr byte_t udp_proto = 0x11; @@ -73,7 +76,7 @@ namespace llarp::vpn } void - PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func) + PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func) { m_IPProtoHandler[proto] = std::make_unique(std::move(func)); } diff --git a/llarp/vpn/packet_router.hpp b/llarp/vpn/packet_router.hpp index ee0721a05..635df7925 100644 --- a/llarp/vpn/packet_router.hpp +++ b/llarp/vpn/packet_router.hpp @@ -6,25 +6,18 @@ namespace llarp::vpn { - using PacketHandlerFunc = std::function; + using PacketHandlerFunc_t = std::function; - struct Layer4Handler - { - virtual ~Layer4Handler() = default; + struct Layer4Handler; - virtual void - HandleIPPacket(llarp::net::IPPacket pkt) = 0; - - virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){}; - }; class PacketRouter { - PacketHandlerFunc m_BaseHandler; + PacketHandlerFunc_t m_BaseHandler; std::unordered_map> m_IPProtoHandler; public: /// baseHandler will be called if no other handlers matches a packet - explicit PacketRouter(PacketHandlerFunc baseHandler); + explicit PacketRouter(PacketHandlerFunc_t baseHandler); /// feed in an ip packet for handling void @@ -32,14 +25,25 @@ namespace llarp::vpn /// add a non udp packet handler using ip protocol proto void - AddIProtoHandler(uint8_t proto, PacketHandlerFunc func); + AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func); /// helper that adds a udp packet handler for UDP destinted for localport void - AddUDPHandler(huint16_t localport, PacketHandlerFunc func); + AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func); /// remove a udp handler that is already set up by bound port void RemoveUDPHandler(huint16_t localport); }; + + struct Layer4Handler + { + virtual ~Layer4Handler() = default; + + virtual void + HandleIPPacket(llarp::net::IPPacket pkt) = 0; + + virtual void AddSubHandler(nuint16_t, PacketHandlerFunc_t){}; + }; + } // namespace llarp::vpn diff --git a/llarp/vpn/platform.cpp b/llarp/vpn/platform.cpp index ef5a30295..757894b61 100644 --- a/llarp/vpn/platform.cpp +++ b/llarp/vpn/platform.cpp @@ -1,5 +1,5 @@ -#include +#include "platform.hpp" #ifdef _WIN32 #include "win32.hpp" @@ -28,7 +28,7 @@ namespace llarp::vpn (void)ctx; std::shared_ptr plat; #ifdef _WIN32 - plat = std::make_shared(); + plat = std::make_shared(ctx); #endif #ifdef __linux__ #ifdef ANDROID diff --git a/llarp/ev/vpn.hpp b/llarp/vpn/platform.hpp similarity index 58% rename from llarp/ev/vpn.hpp rename to llarp/vpn/platform.hpp index 4488010eb..a29841138 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/vpn/platform.hpp @@ -2,10 +2,12 @@ #include #include -#include - #include +#include "i_packet_io.hpp" + +#include + namespace llarp { struct Context; @@ -30,36 +32,40 @@ namespace llarp::vpn struct InterfaceInfo { std::string ifname; + unsigned int index; huint32_t dnsaddr; - std::set addrs; + std::vector addrs; + + /// get address number N + inline net::ipaddr_t + operator[](size_t idx) const + { + const auto& range = addrs[idx].range; + if (range.IsV4()) + return ToNet(net::TruncateV6(range.addr)); + return ToNet(range.addr); + } }; /// a vpn network interface - class NetworkInterface + class NetworkInterface : public I_Packet_IO { + protected: + InterfaceInfo m_Info; + public: - NetworkInterface() = default; + NetworkInterface(InterfaceInfo info) : m_Info{std::move(info)} + {} NetworkInterface(const NetworkInterface&) = delete; NetworkInterface(NetworkInterface&&) = delete; virtual ~NetworkInterface() = default; - /// get pollable fd for reading - virtual int - PollFD() const = 0; - - /// the interface's name - virtual std::string - IfName() const = 0; - - /// read next ip packet, return an empty packet if there are none ready. - virtual net::IPPacket - ReadNextPacket() = 0; - - /// write a packet to the interface - /// returns false if we dropped it - virtual bool - WritePacket(net::IPPacket pkt) = 0; + const InterfaceInfo& + Info() const + { + return m_Info; + } /// idempotently wake up the upper layers as needed (platform dependant) virtual void @@ -69,8 +75,6 @@ namespace llarp::vpn class IRouteManager { public: - using IPVariant_t = std::variant; - IRouteManager() = default; IRouteManager(const IRouteManager&) = delete; IRouteManager(IRouteManager&&) = delete; @@ -86,16 +90,16 @@ namespace llarp::vpn } virtual void - AddRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0; virtual void - DelRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0; virtual void - AddDefaultRouteViaInterface(std::string ifname) = 0; + AddDefaultRouteViaInterface(NetworkInterface& vpn) = 0; virtual void - DelDefaultRouteViaInterface(std::string ifname) = 0; + DelDefaultRouteViaInterface(NetworkInterface& vpn) = 0; virtual void AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; @@ -103,8 +107,8 @@ namespace llarp::vpn virtual void DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; - virtual std::vector - GetGatewaysNotOnInterface(std::string ifname) = 0; + virtual std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) = 0; virtual void AddBlackhole(){}; @@ -117,20 +121,43 @@ namespace llarp::vpn /// responsible for obtaining vpn interfaces class Platform { + protected: + /// get a new network interface fully configured given the interface info + /// blocks until ready, throws on error + virtual std::shared_ptr + ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; + public: Platform() = default; Platform(const Platform&) = delete; Platform(Platform&&) = delete; virtual ~Platform() = default; - /// get a new network interface fully configured given the interface info - /// blocks until ready, throws on error - virtual std::shared_ptr - ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; + /// create and start a network interface + inline std::shared_ptr + CreateInterface(InterfaceInfo info, AbstractRouter* router) + { + if (auto netif = ObtainInterface(std::move(info), router)) + { + netif->Start(); + return netif; + } + return nullptr; + } /// get owned ip route manager for managing routing table virtual IRouteManager& RouteManager() = 0; + + /// create a packet io that will read (and optionally write) packets on a network interface the + /// lokinet process does not own + /// @param index the interface index of the network interface to use or 0 for all + /// interfaces on the system + virtual std::shared_ptr + create_packet_io(unsigned int) + { + throw std::runtime_error{"raw packet io is unimplemented"}; + } }; /// create native vpn platform diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index 89771353f..a08e28813 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -5,662 +5,191 @@ #include #include #include -#include #include - +#include +#include +#include +#include #include -// DDK macros -#define CTL_CODE(DeviceType, Function, Method, Access) \ - (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) -#define FILE_DEVICE_UNKNOWN 0x00000022 -#define FILE_ANY_ACCESS 0x00000000 -#define METHOD_BUFFERED 0 - -/* From OpenVPN tap driver, common.h */ -#define TAP_CONTROL_CODE(request, method) \ - CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) -#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED) -#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED) -#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE(3, METHOD_BUFFERED) -#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE(4, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED) -#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE(7, METHOD_BUFFERED) -#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE(8, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE(9, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED) - -/* Windows registry crap */ -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 -#define NETWORK_ADAPTERS \ - "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-" \ - "08002BE10318}" +#include "platform.hpp" -typedef unsigned long IPADDR; - -namespace llarp::vpn +namespace llarp::win32 { - static char* - reg_query(char* key_name) - { - HKEY adapters, adapter; - DWORD i, ret, len; - char* deviceid = nullptr; - DWORD sub_keys = 0; - - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(key_name), 0, KEY_READ, &adapters); - if (ret != ERROR_SUCCESS) - { - return nullptr; - } - - ret = RegQueryInfoKey( - adapters, NULL, NULL, NULL, &sub_keys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) - { - return nullptr; - } - - if (sub_keys <= 0) - { - return nullptr; - } - - /* Walk througt all adapters */ - for (i = 0; i < sub_keys; i++) - { - char new_key[MAX_KEY_LENGTH]; - char data[256]; - TCHAR key[MAX_KEY_LENGTH]; - DWORD keylen = MAX_KEY_LENGTH; - - /* Get the adapter key name */ - ret = RegEnumKeyEx(adapters, i, key, &keylen, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) - { - continue; - } - - /* Append it to NETWORK_ADAPTERS and open it */ - snprintf(new_key, sizeof new_key, "%s\\%s", key_name, key); - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(new_key), 0, KEY_READ, &adapter); - if (ret != ERROR_SUCCESS) - { - continue; - } - - /* Check its values */ - len = sizeof data; - ret = RegQueryValueEx(adapter, "ComponentId", NULL, NULL, (LPBYTE)data, &len); - if (ret != ERROR_SUCCESS) - { - /* This value doesn't exist in this adaptater tree */ - goto clean; - } - /* If its a tap adapter, its all good */ - if (strncmp(data, "tap0901", 7) == 0) - { - DWORD type; - - len = sizeof data; - ret = RegQueryValueEx(adapter, "NetCfgInstanceId", NULL, &type, (LPBYTE)data, &len); - if (ret != ERROR_SUCCESS) - { - goto clean; - } - deviceid = strdup(data); - break; - } - clean: - RegCloseKey(adapter); - } - RegCloseKey(adapters); - return deviceid; - } - - template - void - ForEachWIN32Interface(Visit visit) - { -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) - MIB_IPFORWARDTABLE* pIpForwardTable; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); - if (pIpForwardTable == nullptr) - return; - - if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) - { - FREE(pIpForwardTable); - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); - if (pIpForwardTable == nullptr) - { - return; - } - } - - if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) - { - for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) - { - visit(&pIpForwardTable->table[i]); - } - } - FREE(pIpForwardTable); -#undef MALLOC -#undef FREE - } - - std::optional - GetInterfaceIndex(huint32_t ip) - { - std::optional ret = std::nullopt; - ForEachWIN32Interface([&ret, n = ToNet(ip)](auto* iface) { - if (ret.has_value()) - return; - if (iface->dwForwardNextHop == n.n) - { - ret = iface->dwForwardIfIndex; - } - }); - return ret; - } - namespace { - std::wstring - get_win_sys_path() + template + std::string + ip_to_string(T ip) { - wchar_t win_sys_path[MAX_PATH] = {0}; - const wchar_t* default_sys_path = L"C:\\Windows\\system32"; - - if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path))) - { - wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path)); - win_sys_path[_countof(win_sys_path) - 1] = L'\0'; - } - return win_sys_path; + return var::visit([](auto&& ip) { return ip.ToString(); }, ip); } } // namespace - class Win32Interface final : public NetworkInterface + using namespace llarp::vpn; + class VPNPlatform : public Platform, public IRouteManager { - std::atomic m_Run; - HANDLE m_Device, m_IOCP; - std::vector m_Threads; - thread::Queue m_ReadQueue; - - InterfaceInfo m_Info; + llarp::Context* const _ctx; + const int m_Metric{2}; - AbstractRouter* const _router; - - static std::string - NetSHCommand() + const auto& + Net() const { - std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); + return _ctx->router->Net(); } - static void - NetSH(std::string commands) + void + Route(std::string ip, std::string gw, std::string cmd) { - commands = NetSHCommand() + " interface IPv6 " + commands; - LogInfo("exec: ", commands); - ::system(commands.c_str()); + llarp::win32::Exec( + "route.exe", + fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric)); } - public: - static std::string - RouteCommand() + void + DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd) { - std::wstring wcmd = get_win_sys_path() + L"\\route.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); + // route hole for loopback bacause god is dead on windows + llarp::win32::Exec( + "route.exe", fmt::format("{} 127.0.0.0 MASK 255.0.0.0 0.0.0.0 METRIC {}", cmd, m_Metric)); + // set up ipv4 routes + auto lower = RouteViaInterface(vpn, "0.0.0.0", "128.0.0.0", cmd); + auto upper = RouteViaInterface(vpn, "128.0.0.0", "128.0.0.0", cmd); } - Win32Interface(InterfaceInfo info, AbstractRouter* router) - : m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router} + OneShotExec + RouteViaInterface(NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd) { - DWORD len; - - const auto device_id = reg_query(NETWORK_ADAPTERS); - if (device_id == nullptr) - { - LogError("cannot query registry"); - throw std::invalid_argument{"cannot query registery"}; - } - const auto fname = fmt::format(R"(\\.\Global\{}.tap)", device_id); - m_Device = CreateFile( - fname.c_str(), - GENERIC_WRITE | GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0); - if (m_Device == INVALID_HANDLE_VALUE) + const auto& info = vpn.Info(); + auto index = info.index; + if (index == 0) { - LogError("failed to open device"); - throw std::invalid_argument{"cannot open " + fname}; + if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0])) + index = *maybe_idx; } - LogInfo("putting interface up..."); - ULONG flag = 1; - // put the interface up - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_SET_MEDIA_STATUS, - &flag, - sizeof(flag), - &flag, - sizeof(flag), - &len, - nullptr)) + auto ifaddr = ip_to_string(info[0]); + // this changes the last 1 to a 0 so that it routes over the interface + // this is required because windows is idiotic af + ifaddr.back()--; + if (index) { - LogError("cannot up interface up"); - throw std::invalid_argument{"cannot put interface up"}; + return OneShotExec{ + "route.exe", + fmt::format( + "{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)}; } - - LogInfo("setting addresses"); - huint32_t ip{}; - // set ipv4 addresses - for (const auto& ifaddr : m_Info.addrs) - { - if (ifaddr.fam == AF_INET) - { - IPADDR sock[3]{}; - const nuint32_t addr = xhtonl(net::TruncateV6(ifaddr.range.addr)); - ip = net::TruncateV6(ifaddr.range.addr); - m_Info.ifname = ip.ToString(); - const nuint32_t mask = xhtonl(net::TruncateV6(ifaddr.range.netmask_bits)); - LogInfo("address ", addr, " netmask ", mask); - sock[0] = addr.n; - sock[1] = addr.n & mask.n; - sock[2] = mask.n; - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_TUN, - &sock, - sizeof(sock), - &sock, - sizeof(sock), - &len, - nullptr)) - { - LogError("cannot set address"); - throw std::invalid_argument{"cannot configure tun interface address"}; - } - - IPADDR ep[4]{}; - - ep[0] = addr.n; - ep[1] = mask.n; - ep[2] = (addr.n | ~mask.n) - htonl(1); - ep[3] = 31536000; - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_MASQ, - ep, - sizeof(ep), - ep, - sizeof(ep), - &len, - nullptr)) - { - LogError("cannot set dhcp masq"); - throw std::invalid_argument{"Cannot configure tun interface dhcp"}; - } -#pragma pack(push) -#pragma pack(1) - struct opt - { - uint8_t dhcp_opt; - uint8_t length; - uint32_t value; - } dns, gateway; -#pragma pack(pop) - - const nuint32_t dnsaddr{xhtonl(m_Info.dnsaddr)}; - dns.dhcp_opt = 6; - dns.length = 4; - dns.value = dnsaddr.n; - - gateway.dhcp_opt = 3; - gateway.length = 4; - gateway.value = addr.n + htonl(1); - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_SET_OPT, - &gateway, - sizeof(gateway), - &gateway, - sizeof(gateway), - &len, - nullptr)) - { - LogError("cannot set gateway"); - throw std::invalid_argument{"cannot set tun gateway"}; - } - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_SET_OPT, - &dns, - sizeof(dns), - &dns, - sizeof(dns), - &len, - nullptr)) - { - LogError("cannot set dns"); - throw std::invalid_argument{"cannot set tun dns"}; - } - } - } - // set ipv6 addresses - for (const auto& ifaddr : m_Info.addrs) + else { - if (ifaddr.fam == AF_INET6) - { - const auto maybe = GetInterfaceIndex(ip); - if (maybe.has_value()) - { - NetSH( - "add address interface=" + std::to_string(*maybe) + " " + ifaddr.range.ToString()); - } - } + return OneShotExec{ + "route.exe", + fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)}; } } - ~Win32Interface() - { - ULONG flag = 0; - DWORD len; - // put the interface down - DeviceIoControl( - m_Device, - TAP_IOCTL_SET_MEDIA_STATUS, - &flag, - sizeof(flag), - &flag, - sizeof(flag), - &len, - nullptr); - m_Run = false; - CloseHandle(m_IOCP); - // close the handle - CloseHandle(m_Device); - // close the reader threads - for (auto& thread : m_Threads) - thread.join(); - } - - virtual void - MaybeWakeUpperLayers() const override - { - _router->TriggerPump(); - } - - int - PollFD() const override - { - return -1; - } - - std::string - IfName() const override - { - return m_Info.ifname; - } - - void - Start() - { - m_Run = true; - const auto numThreads = std::thread::hardware_concurrency(); - // allocate packets - for (size_t idx = 0; idx < numThreads; ++idx) - m_Packets.emplace_back(new asio_evt_pkt{true}); - - // create completion port - m_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0); - // attach the handle or some shit - CreateIoCompletionPort(m_Device, m_IOCP, 0, 0); - // spawn reader threads - for (size_t idx = 0; idx < numThreads; ++idx) - m_Threads.emplace_back([this, idx]() { ReadLoop(idx); }); - } - - net::IPPacket - ReadNextPacket() override - { - if (m_ReadQueue.empty()) - return net::IPPacket{}; - - return m_ReadQueue.popFront(); - } - - struct asio_evt_pkt - { - explicit asio_evt_pkt(bool _read) : read{_read} - {} + std::shared_ptr _wintun; - OVERLAPPED hdr = {0, 0, 0, 0, nullptr}; // must be first, since this is part of the IO call - bool read; - net::IPPacket pkt; + WinDivert_API m_WinDivert{}; - void - Read(HANDLE dev) - { - ReadFile(dev, pkt.buf, sizeof(pkt.buf), nullptr, &hdr); - } - }; + public: + VPNPlatform(const VPNPlatform&) = delete; + VPNPlatform(VPNPlatform&&) = delete; - std::vector> m_Packets; + VPNPlatform(llarp::Context* ctx) : Platform{}, _ctx{ctx}, _wintun{WintunContext_new()} + {} - bool - WritePacket(net::IPPacket pkt) - { - LogDebug("write packet ", pkt.sz); - asio_evt_pkt* ev = new asio_evt_pkt{false}; - std::copy_n(pkt.buf, pkt.sz, ev->pkt.buf); - ev->pkt.sz = pkt.sz; - WriteFile(m_Device, ev->pkt.buf, ev->pkt.sz, nullptr, &ev->hdr); - return true; - } + virtual ~VPNPlatform() = default; void - ReadLoop(size_t packetIndex) + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { - auto& ev = m_Packets[packetIndex]; - ev->Read(m_Device); - while (m_Run) - { - DWORD size; - ULONG_PTR user; - OVERLAPPED* ovl = nullptr; - if (not GetQueuedCompletionStatus(m_IOCP, &size, &user, &ovl, 1000)) - continue; - asio_evt_pkt* pkt = (asio_evt_pkt*)ovl; - LogDebug("got iocp event size=", size, " read=", pkt->read); - if (pkt->read) - { - pkt->pkt.sz = size; - m_ReadQueue.pushBack(pkt->pkt); - pkt->Read(m_Device); - } - else - delete pkt; - } + Route(ip_to_string(ip), ip_to_string(gateway), "ADD"); } - }; - class Win32RouteManager : public IRouteManager - { void - Execute(std::string cmd) const + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { - llarp::LogInfo("exec: ", cmd); - ::system(cmd.c_str()); - } - - static std::string - PowerShell() - { - std::wstring wcmd = - get_win_sys_path() + L"\\WindowsPowerShell\\v1.0\\powershell.exe -Command "; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); - } - - static std::string - RouteCommand() - { - return Win32Interface::RouteCommand(); + Route(ip_to_string(ip), ip_to_string(gateway), "DELETE"); } void - Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd) + AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - Execute(fmt::format( - "{} {} {} MASK 255.255.255.255 {} METRIC 2", RouteCommand(), cmd, ip, gateway)); + RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD"); } void - DefaultRouteViaInterface(std::string ifname, std::string cmd) + DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - // poke hole for loopback bacause god is dead on windows - Execute(RouteCommand() + " " + cmd + " 127.0.0.0 MASK 255.0.0.0 0.0.0.0"); - - huint32_t ip{}; - ip.FromString(ifname); - const auto ipv6 = net::ExpandV4Lan(ip); - - Execute(RouteCommand() + " " + cmd + " ::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " 4000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " 8000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " c000::/2 " + ipv6.ToString()); - - ifname.back()++; - Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); - Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); + RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE"); } - void - RouteViaInterface(std::string ifname, IPRange range, std::string cmd) + std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) override { - if (range.IsV4()) - { - const huint32_t addr4 = net::TruncateV6(range.addr); - const huint32_t mask4 = net::TruncateV6(range.netmask_bits); - Execute( - RouteCommand() + " " + cmd + " " + addr4.ToString() + " MASK " + mask4.ToString() + " " - + ifname); - } - else + std::vector gateways; + + auto idx = vpn.Info().index; + using UInt_t = decltype(idx); + for (const auto& iface : Net().AllNetworkInterfaces()) { - Execute( - RouteCommand() + " " + cmd + range.addr.ToString() + " MASK " - + range.netmask_bits.ToString() + " " + ifname); + if (static_cast(iface.index) == idx) + continue; + if (iface.gateway) + gateways.emplace_back(*iface.gateway); } - } - - public: - void - AddRoute(IPVariant_t ip, IPVariant_t gateway) override - { - Route(ip, gateway, "ADD"); + return gateways; } void - DelRoute(IPVariant_t ip, IPVariant_t gateway) override + AddDefaultRouteViaInterface(NetworkInterface& vpn) override { - Route(ip, gateway, "DELETE"); - } + // kill ipv6 + llarp::win32::Exec( + "WindowsPowerShell\\v1.0\\powershell.exe", + "-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)"); - void - AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override - { - RouteViaInterface(vpn.IfName(), range, "ADD"); + DefaultRouteViaInterface(vpn, "ADD"); + llarp::win32::Exec("ipconfig.exe", "/flushdns"); } void - DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override + DelDefaultRouteViaInterface(NetworkInterface& vpn) override { - RouteViaInterface(vpn.IfName(), range, "DELETE"); - } + // restore ipv6 + llarp::win32::Exec( + "WindowsPowerShell\\v1.0\\powershell.exe", + "-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)"); - std::vector - GetGatewaysNotOnInterface(std::string ifname) override - { - std::vector gateways; - ForEachWIN32Interface([&](auto w32interface) { - struct in_addr gateway, interface_addr; - gateway.S_un.S_addr = (u_long)w32interface->dwForwardDest; - interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop; - std::string interface_name{inet_ntoa(interface_addr)}; - if ((!gateway.S_un.S_addr) and interface_name != ifname) - { - llarp::LogTrace( - "Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways."); - huint32_t x{}; - if (x.FromString(interface_name)) - gateways.push_back(x); - } - }); - return gateways; + DefaultRouteViaInterface(vpn, "DELETE"); + llarp::win32::Exec("netsh.exe", "winsock reset"); + llarp::win32::Exec("ipconfig.exe", "/flushdns"); } - void - AddDefaultRouteViaInterface(std::string ifname) override + std::shared_ptr + ObtainInterface(InterfaceInfo info, AbstractRouter* router) override { - // kill ipv6 - Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); - DefaultRouteViaInterface(ifname, "ADD"); + return WintunInterface_new(_wintun, std::move(info), router); } - void - DelDefaultRouteViaInterface(std::string ifname) override + std::shared_ptr + create_packet_io(unsigned int ifindex) override { - // restore ipv6 - Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); - DefaultRouteViaInterface(ifname, "DELETE"); + // we only want do this on all interfaes with windivert + if (ifindex) + throw std::invalid_argument{ + "cannot create packet io on explicitly specified interface, not currently supported on " + "windows (yet)"}; + return m_WinDivert.make_intercepter( + "outbound and ( udp.DstPort == 53 or tcp.DstPort == 53 )", + [router = _ctx->router] { router->TriggerPump(); }); } - }; - - class Win32Platform : public Platform - { - Win32RouteManager _routeManager{}; - - public: - std::shared_ptr - ObtainInterface(InterfaceInfo info, AbstractRouter* router) override - { - auto netif = std::make_shared(std::move(info), router); - netif->Start(); - return netif; - }; IRouteManager& RouteManager() override { - return _routeManager; + return *this; } }; -} // namespace llarp::vpn +} // namespace llarp::win32 diff --git a/llarp/win32/dll.cpp b/llarp/win32/dll.cpp new file mode 100644 index 000000000..650617a52 --- /dev/null +++ b/llarp/win32/dll.cpp @@ -0,0 +1,22 @@ +#include "dll.hpp" +#include +#include + +namespace llarp::win32 +{ + namespace + { + auto cat = log::Cat("win32-dll"); + } + DLL::DLL(std::string dll) : m_Handle{LoadLibraryA(dll.c_str())} + { + if (not m_Handle) + throw win32::error{fmt::format("failed to load '{}'", dll)}; + log::info(cat, "loaded '{}'", dll); + } + + DLL::~DLL() + { + FreeLibrary(m_Handle); + } +} // namespace llarp::win32 diff --git a/llarp/win32/dll.hpp b/llarp/win32/dll.hpp new file mode 100644 index 000000000..ab7360aa0 --- /dev/null +++ b/llarp/win32/dll.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include "exception.hpp" +#include + +namespace llarp::win32 +{ + class DLL + { + const HMODULE m_Handle; + + protected: + /// given a name of a function pointer find it and put it into `func` + /// throws if the function does not exist in the DLL we openned. + template + void + init(std::string name, Func_t& func) + { + auto ptr = GetProcAddress(m_Handle, name.c_str()); + if (not ptr) + throw win32::error{fmt::format("function '{}' not found", name)}; + func = reinterpret_cast(ptr); + } + + public: + DLL(std::string dll); + + virtual ~DLL(); + }; +} // namespace llarp::win32 diff --git a/llarp/win32/exception.cpp b/llarp/win32/exception.cpp new file mode 100644 index 000000000..4b74e0584 --- /dev/null +++ b/llarp/win32/exception.cpp @@ -0,0 +1,39 @@ +#include "windows.h" +#include "exception.hpp" +#include +#include + +namespace llarp::win32 + +{ + error::error(std::string msg) : error{GetLastError(), std::move(msg)} + {} + error::error(DWORD err, std::string msg) + : std::runtime_error{fmt::format("{}: {} (code={})", msg, error_to_string(err), err)} + {} + std::string + error_to_string(DWORD err) + { + // mostly yoinked from https://stackoverflow.com/a/45565001 + LPTSTR psz{nullptr}; + const DWORD cchMsg = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + nullptr, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&psz), + 0, + nullptr); + + if (cchMsg <= 0) + { + // cannot get message for error, reset the last error here so it does not propagate + ::SetLastError(0); + return "unknown error"; + } + + auto deleter = [](void* p) { ::LocalFree(p); }; + std::unique_ptr ptrBuffer{psz, deleter}; + return std::string{ptrBuffer.get(), cchMsg}; + } +} // namespace llarp::win32 diff --git a/llarp/win32/exception.hpp b/llarp/win32/exception.hpp index 893fcc741..38663061c 100644 --- a/llarp/win32/exception.hpp +++ b/llarp/win32/exception.hpp @@ -1,31 +1,18 @@ #pragma once - -#include -#include #include +#include #include -#include - namespace llarp::win32 { - namespace - { - inline std::string - error_to_string(DWORD err) - { - std::array buffer{}; - - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, &err, 0, 0, buffer.data(), buffer.size(), nullptr); - return std::string{buffer.data()}; - } - } // namespace + std::string + error_to_string(DWORD err); class error : public std::runtime_error { public: - error(DWORD err, std::string msg) - : std::runtime_error{fmt::format("{}: {}", msg, error_to_string(err))} - {} + error(std::string msg); + virtual ~error() = default; + explicit error(DWORD err, std::string msg); }; } // namespace llarp::win32 diff --git a/llarp/win32/exec.cpp b/llarp/win32/exec.cpp new file mode 100644 index 000000000..bce7f02d7 --- /dev/null +++ b/llarp/win32/exec.cpp @@ -0,0 +1,57 @@ +#include "exec.hpp" +#include "exception.hpp" +#include + +#include + +namespace llarp::win32 +{ + namespace + { + auto logcat = log::Cat("win32:exec"); + + /// get the directory for system32 which contains all the executables we use + std::string + SystemExeDir() + { + std::array path{}; + + if (GetSystemDirectoryA(path.data(), path.size())) + return path.data(); + + return "C:\\Windows\\system32"; + } + + } // namespace + + void + Exec(std::string exe, std::string args) + { + OneShotExec{exe, args}; + } + + OneShotExec::OneShotExec(std::string cmd, std::chrono::milliseconds timeout) + : _si{}, _pi{}, _timeout{timeout.count()} + { + log::info(logcat, "exec: {}", cmd); + if (not CreateProcessA( + nullptr, cmd.data(), nullptr, nullptr, false, 0, nullptr, nullptr, &_si, &_pi)) + throw win32::error(GetLastError(), "failed to execute subprocess"); + } + + OneShotExec::~OneShotExec() + { + WaitForSingleObject(_pi.hProcess, _timeout); + CloseHandle(_pi.hProcess); + CloseHandle(_pi.hThread); + } + + OneShotExec::OneShotExec(std::string cmd, std::string args, std::chrono::milliseconds timeout) + : OneShotExec{fmt::format("{}\\{} {}", SystemExeDir(), cmd, args), timeout} + {} + + DeferExec::~DeferExec() + { + OneShotExec{std::move(_exe), std::move(_args), std::move(_timeout)}; + } +} // namespace llarp::win32 diff --git a/llarp/win32/exec.hpp b/llarp/win32/exec.hpp new file mode 100644 index 000000000..f77e009df --- /dev/null +++ b/llarp/win32/exec.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +namespace llarp::win32 +{ + /// RAII wrapper for calling a subprocess in win32 for parallel executuion in the same scope + /// destructor blocks until timeout has been hit of the execution of the subprocses finished + class OneShotExec + { + STARTUPINFO _si; + PROCESS_INFORMATION _pi; + const DWORD _timeout; + + OneShotExec(std::string cmd, std::chrono::milliseconds timeout); + + public: + /// construct a call to an exe in system32 with args, will resolve the full path of the exe to + /// prevent path injection + explicit OneShotExec(std::string exe, std::string args, std::chrono::milliseconds timeout = 5s); + + ~OneShotExec(); + }; + + /// a wrapper for OneShotExec that calls the thing we want on destruction + class DeferExec + { + std::string _exe; + std::string _args; + std::chrono::milliseconds _timeout; + + public: + explicit DeferExec(std::string exe, std::string args, std::chrono::milliseconds timeout = 5s) + : _exe{std::move(exe)}, _args{std::move(args)}, _timeout{std::move(timeout)} + {} + + ~DeferExec(); + }; + + /// simple wrapper to run a single command in a blocking way + void + Exec(std::string exe, std::string args); + +} // namespace llarp::win32 diff --git a/llarp/win32/guid.hpp b/llarp/win32/guid.hpp new file mode 100644 index 000000000..83c926523 --- /dev/null +++ b/llarp/win32/guid.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace llarp::win32 +{ + /// @brief given a container of data hash it and make it into a GUID so we have a way to + /// deterministically generate GUIDs + template + inline GUID + MakeDeterministicGUID(Data data) + { + ShortHash h{}; + auto hash = [&h](auto data) { CryptoManager::instance()->shorthash(h, data); }; + + if constexpr (std::is_same_v) + hash(llarp_buffer_t{reinterpret_cast(data.data()), data.size()}); + else + hash(llarp_buffer_t{data}); + GUID guid{}; + std::copy_n( + h.begin(), std::min(sizeof(GUID), sizeof(ShortHash)), reinterpret_cast(&guid)); + return guid; + } +} // namespace llarp::win32 diff --git a/llarp/win32/handle.hpp b/llarp/win32/handle.hpp new file mode 100644 index 000000000..d97caf7b6 --- /dev/null +++ b/llarp/win32/handle.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "exception.hpp" + +namespace llarp::win32 +{ + inline void + ensure_handle_is_valid(HANDLE h) + { + BY_HANDLE_FILE_INFORMATION info{}; + if (GetFileInformationByHandle(h, &info)) + return; + if (auto err = GetLastError()) + { + SetLastError(0); + throw llarp::win32::error{err, "handle validity check failed"}; + } + } +} // namespace llarp::win32 diff --git a/llarp/win32/windivert.cpp b/llarp/win32/windivert.cpp new file mode 100644 index 000000000..4e4e4d3f4 --- /dev/null +++ b/llarp/win32/windivert.cpp @@ -0,0 +1,194 @@ +#include +#include +#include "windivert.hpp" +#include "dll.hpp" +#include "handle.hpp" +#include +#include +#include +extern "C" +{ +#include +} +namespace L = llarp::log; + +namespace llarp::win32 +{ + namespace + { + auto cat = L::Cat("windivert"); + } + + using WD_Open_Func_t = decltype(&::WinDivertOpen); + using WD_Close_Func_t = decltype(&::WinDivertClose); + using WD_Shutdown_Func_t = decltype(&::WinDivertShutdown); + using WD_Send_Func_t = decltype(&::WinDivertSend); + using WD_Recv_Func_t = decltype(&::WinDivertRecv); + using WD_IP4_Format_Func_t = decltype(&::WinDivertHelperFormatIPv4Address); + using WD_IP6_Format_Func_t = decltype(&::WinDivertHelperFormatIPv6Address); + + struct WinDivertDLL : DLL + { + WD_Open_Func_t open; + WD_Close_Func_t close; + WD_Shutdown_Func_t shutdown; + WD_Send_Func_t send; + WD_Recv_Func_t recv; + WD_IP4_Format_Func_t format_ip4; + WD_IP6_Format_Func_t format_ip6; + + WinDivertDLL() : DLL{"WinDivert.dll"} + { + init("WinDivertOpen", open); + init("WinDivertClose", close); + init("WinDivertShutdown", shutdown); + init("WinDivertSend", send); + init("WinDivertRecv", recv); + init("WinDivertHelperFormatIPv4Address", format_ip4); + init("WinDivertHelperFormatIPv6Address", format_ip6); + L::debug(cat, "loaded windivert functions"); + } + + virtual ~WinDivertDLL() = default; + }; + + struct WD_Packet + { + std::vector pkt; + WINDIVERT_ADDRESS addr; + }; + + class WinDivert_IO : public llarp::vpn::I_Packet_IO + { + const std::shared_ptr m_WinDivert; + std::function m_Wake; + + HANDLE m_Handle; + std::thread m_Runner; + thread::Queue m_RecvQueue; + + public: + WinDivert_IO( + std::shared_ptr api, std::string filter_spec, std::function wake) + : m_WinDivert{api}, m_Wake{wake}, m_RecvQueue{recv_queue_size} + { + L::info(cat, "load windivert with filterspec: '{}'", filter_spec); + + m_Handle = m_WinDivert->open(filter_spec.c_str(), WINDIVERT_LAYER_NETWORK, 0, 0); + if (auto err = GetLastError()) + throw win32::error{err, "cannot open windivert handle"}; + } + + ~WinDivert_IO() + { + m_WinDivert->close(m_Handle); + } + + std::optional + recv_packet() const + { + WINDIVERT_ADDRESS addr{}; + std::vector pkt; + pkt.resize(1500); // net::IPPacket::MaxSize + UINT sz{}; + if (not m_WinDivert->recv(m_Handle, pkt.data(), pkt.size(), &sz, &addr)) + { + auto err = GetLastError(); + if (err and err != ERROR_BROKEN_PIPE) + throw win32::error{ + err, fmt::format("failed to receive packet from windivert (code={})", err)}; + else if (err) + SetLastError(0); + return std::nullopt; + } + L::info(cat, "got packet of size {}B", sz); + pkt.resize(sz); + return WD_Packet{std::move(pkt), std::move(addr)}; + } + + void + send_packet(const WD_Packet& w_pkt) const + { + const auto& pkt = w_pkt.pkt; + const auto* addr = &w_pkt.addr; + L::info(cat, "send dns packet of size {}B", pkt.size()); + UINT sz{}; + if (m_WinDivert->send(m_Handle, pkt.data(), pkt.size(), &sz, addr)) + return; + throw win32::error{"windivert send failed"}; + } + + virtual int + PollFD() const + { + return -1; + } + + virtual bool WritePacket(net::IPPacket) override + { + return false; + } + + virtual net::IPPacket + ReadNextPacket() override + { + auto w_pkt = recv_packet(); + if (not w_pkt) + return net::IPPacket{}; + net::IPPacket pkt{std::move(w_pkt->pkt)}; + pkt.reply = [this, addr = std::move(w_pkt->addr)](auto pkt) { + send_packet(WD_Packet{pkt.steal(), addr}); + }; + return pkt; + } + + virtual void + Start() override + { + L::info(cat, "starting windivert"); + if (m_Runner.joinable()) + throw std::runtime_error{"windivert thread is already running"}; + + auto read_loop = [this]() { + log::debug(cat, "windivert read loop start"); + while (true) + { + // in the read loop, read packets until they stop coming in + // each packet is sent off + if (auto maybe_pkt = recv_packet()) + m_RecvQueue.pushBack(std::move(*maybe_pkt)); + else // leave loop on read fail + break; + } + log::debug(cat, "windivert read loop end"); + }; + + m_Runner = std::thread{std::move(read_loop)}; + } + + virtual void + Stop() override + { + L::info(cat, "stopping windivert"); + m_WinDivert->shutdown(m_Handle, WINDIVERT_SHUTDOWN_BOTH); + m_Runner.join(); + } + }; + + WinDivert_API::WinDivert_API() : m_Impl{std::make_shared()} + {} + + std::string + WinDivert_API::format_ip(uint32_t ip) const + { + std::array buf{}; + m_Impl->format_ip4(ip, buf.data(), buf.size()); + return buf.data(); + } + + std::shared_ptr + WinDivert_API::make_intercepter(std::string filter_spec, std::function wake) const + { + return std::make_shared(m_Impl, filter_spec, wake); + } +} // namespace llarp::win32 diff --git a/llarp/win32/windivert.hpp b/llarp/win32/windivert.hpp new file mode 100644 index 000000000..36fbaffea --- /dev/null +++ b/llarp/win32/windivert.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include +#include + +namespace llarp::win32 +{ + struct WinDivertDLL; + + class WinDivert_API + { + std::shared_ptr m_Impl; + + public: + WinDivert_API(); + + /// format an ipv4 in host order to string such that a windivert filter spec can understand it + std::string + format_ip(uint32_t ip) const; + + /// create a packet intercepter that uses windivert. + /// filter_spec describes the kind of traffic we wish to intercept. + /// pass in a callable that wakes up the main event loop. + /// we hide all implementation details from other compilation units to prevent issues with + /// linkage that may arrise. + std::shared_ptr + make_intercepter(std::string filter_spec, std::function wakeup) const; + }; +} // namespace llarp::win32 diff --git a/llarp/win32/wintun.cpp b/llarp/win32/wintun.cpp new file mode 100644 index 000000000..bf8554891 --- /dev/null +++ b/llarp/win32/wintun.cpp @@ -0,0 +1,411 @@ +extern "C" +{ +#include +} + +#include +#include "wintun.hpp" +#include "exception.hpp" +#include "dll.hpp" +#include "guid.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace llarp::win32 +{ + namespace + { + auto logcat = log::Cat("wintun"); + constexpr auto PoolName = "lokinet"; + } // namespace + + using Adapter_ptr = std::shared_ptr<_WINTUN_ADAPTER>; + + struct PacketWrapper + { + BYTE* data; + DWORD size; + WINTUN_SESSION_HANDLE session; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* release; + /// copy our data into an ip packet struct + net::IPPacket + copy() const + { + net::IPPacket pkt{size}; + std::copy_n(data, size, pkt.data()); + return pkt; + } + + ~PacketWrapper() + { + release(session, data); + } + }; + + class WintunDLL : public DLL + { + public: + WINTUN_CREATE_ADAPTER_FUNC* create_adapter; + WINTUN_OPEN_ADAPTER_FUNC* open_adapter; + WINTUN_CLOSE_ADAPTER_FUNC* close_adapter; + + WINTUN_START_SESSION_FUNC* start_session; + WINTUN_END_SESSION_FUNC* end_session; + + WINTUN_GET_ADAPTER_LUID_FUNC* get_adapter_LUID; + WINTUN_GET_READ_WAIT_EVENT_FUNC* get_adapter_handle; + + WINTUN_RECEIVE_PACKET_FUNC* read_packet; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* release_read; + + WINTUN_ALLOCATE_SEND_PACKET_FUNC* alloc_write; + WINTUN_SEND_PACKET_FUNC* write_packet; + + WINTUN_SET_LOGGER_FUNC* set_logger; + WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC* get_version; + /// read out all the wintun function pointers from a library handle + WintunDLL() : DLL{"wintun.dll"} + { + init("WintunGetRunningDriverVersion", get_version); + init("WintunCreateAdapter", create_adapter); + init("WintunOpenAdapter", open_adapter); + init("WintunCloseAdapter", close_adapter); + init("WintunStartSession", start_session); + init("WintunEndSession", end_session); + init("WintunGetAdapterLUID", get_adapter_LUID); + init("WintunReceivePacket", read_packet); + init("WintunReleaseReceivePacket", release_read); + init("WintunSendPacket", write_packet); + init("WintunAllocateSendPacket", alloc_write); + init("WintunSetLogger", set_logger); + init("WintunGetReadWaitEvent", get_adapter_handle); + + log::info(logcat, fmt::format("wintun version {0:x} loaded", get_version())); + } + + /// autovivify a wintun adapter handle + [[nodiscard]] auto + make_adapter(std::string adapter_name, std::string tunnel_name) const + { + auto adapter_name_wide = to_wide(adapter_name); + if (auto _impl = open_adapter(adapter_name_wide.c_str())) + { + log::info(logcat, "opened existing adapter: '{}'", adapter_name); + return _impl; + } + if (auto err = GetLastError()) + { + log::info( + logcat, "did not open existing adapter '{}': {}", adapter_name, error_to_string(err)); + SetLastError(0); + } + const auto guid = + llarp::win32::MakeDeterministicGUID(fmt::format("{}|{}", adapter_name, tunnel_name)); + log::info(logcat, "creating adapter: '{}' on pool '{}'", adapter_name, tunnel_name); + auto tunnel_name_wide = to_wide(tunnel_name); + if (auto _impl = create_adapter(adapter_name_wide.c_str(), tunnel_name_wide.c_str(), &guid)) + return _impl; + throw win32::error{"failed to create wintun adapter"}; + } + }; + + class WintunAdapter + { + WINTUN_CLOSE_ADAPTER_FUNC* _close_adapter; + WINTUN_GET_ADAPTER_LUID_FUNC* _get_adapter_LUID; + + WINTUN_GET_READ_WAIT_EVENT_FUNC* _get_handle; + WINTUN_START_SESSION_FUNC* _start_session; + WINTUN_END_SESSION_FUNC* _end_session; + + WINTUN_ADAPTER_HANDLE _handle; + + [[nodiscard]] auto + get_adapter_LUID() const + { + NET_LUID _uid{}; + _get_adapter_LUID(_handle, &_uid); + return _uid; + } + + public: + WintunAdapter(const WintunDLL& dll, std::string name) + : _close_adapter{dll.close_adapter} + , _get_adapter_LUID{dll.get_adapter_LUID} + , _get_handle{dll.get_adapter_handle} + , _start_session{dll.start_session} + , _end_session{dll.end_session} + { + _handle = dll.make_adapter(std::move(name), PoolName); + if (_handle == nullptr) + throw std::runtime_error{"failed to create wintun adapter"}; + } + + /// put adapter up + void + Up(const vpn::InterfaceInfo& info) const + { + const auto luid = get_adapter_LUID(); + for (const auto& addr : info.addrs) + { + // TODO: implement ipv6 + if (addr.fam != AF_INET) + continue; + MIB_UNICASTIPADDRESS_ROW AddressRow; + InitializeUnicastIpAddressEntry(&AddressRow); + AddressRow.InterfaceLuid = luid; + + AddressRow.Address.Ipv4.sin_family = AF_INET; + AddressRow.Address.Ipv4.sin_addr.S_un.S_addr = ToNet(net::TruncateV6(addr.range.addr)).n; + AddressRow.OnLinkPrefixLength = addr.range.HostmaskBits(); + AddressRow.DadState = IpDadStatePreferred; + + if (auto err = CreateUnicastIpAddressEntry(&AddressRow); err != ERROR_SUCCESS) + throw error{err, fmt::format("cannot set address '{}'", addr.range)}; + LogDebug(fmt::format("added address: '{}'", addr.range)); + } + } + + /// put adapter down and close it + void + Down() const + { + _close_adapter(_handle); + } + + /// auto vivify a wintun session handle and read handle off of our adapter + [[nodiscard]] std::pair + session() const + { + if (auto impl = _start_session(_handle, WINTUN_MAX_RING_CAPACITY)) + { + if (auto handle = _get_handle(impl)) + return {impl, handle}; + _end_session(impl); + } + return {nullptr, nullptr}; + } + }; + + class WintunSession + { + WINTUN_END_SESSION_FUNC* _end_session; + WINTUN_RECEIVE_PACKET_FUNC* _recv_pkt; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* _release_pkt; + WINTUN_ALLOCATE_SEND_PACKET_FUNC* _alloc_write; + WINTUN_SEND_PACKET_FUNC* _write_pkt; + WINTUN_SESSION_HANDLE _impl; + HANDLE _handle; + + public: + WintunSession(const WintunDLL& dll) + : _end_session{dll.end_session} + , _recv_pkt{dll.read_packet} + , _release_pkt{dll.release_read} + , _alloc_write{dll.alloc_write} + , _write_pkt{dll.write_packet} + , _impl{nullptr} + , _handle{nullptr} + {} + + void + Start(const std::shared_ptr& adapter) + { + if (auto [impl, handle] = adapter->session(); impl and handle) + { + _impl = impl; + _handle = handle; + return; + } + throw error{GetLastError(), "could not create wintun session"}; + } + + void + Stop() const + { + _end_session(_impl); + } + + void + WaitFor(std::chrono::milliseconds dur) + { + WaitForSingleObject(_handle, dur.count()); + } + + /// read a unique pointer holding a packet read from wintun, returns the packet if we read one + /// and a bool, set to true if our adapter is now closed + [[nodiscard]] std::pair, bool> + ReadPacket() const + { + // typedef so the return statement fits on 1 line :^D + using Pkt_ptr = std::unique_ptr; + DWORD sz; + if (auto* ptr = _recv_pkt(_impl, &sz)) + return {Pkt_ptr{new PacketWrapper{ptr, sz, _impl, _release_pkt}}, false}; + const auto err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS or err == ERROR_HANDLE_EOF) + { + SetLastError(0); + return {nullptr, err == ERROR_HANDLE_EOF}; + } + throw error{err, "failed to read packet"}; + } + + /// write an ip packet to the interface, return 2 bools, first is did we write the packet, + /// second if we are terminating + std::pair + WritePacket(net::IPPacket pkt) const + { + if (auto* buf = _alloc_write(_impl, pkt.size())) + { + std::copy_n(pkt.data(), pkt.size(), buf); + _write_pkt(_impl, buf); + return {true, false}; + } + const auto err = GetLastError(); + if (err == ERROR_BUFFER_OVERFLOW or err == ERROR_HANDLE_EOF) + { + SetLastError(0); + return {err != ERROR_BUFFER_OVERFLOW, err == ERROR_HANDLE_EOF}; + } + throw error{err, "failed to write packet"}; + } + }; + + class WintunInterface : public vpn::NetworkInterface + { + AbstractRouter* const _router; + std::shared_ptr _adapter; + std::shared_ptr _session; + thread::Queue _recv_queue; + thread::Queue _send_queue; + std::thread _recv_thread; + std::thread _send_thread; + + static inline constexpr size_t packet_queue_length = 1024; + + public: + WintunInterface(const WintunDLL& dll, vpn::InterfaceInfo info, AbstractRouter* router) + : vpn::NetworkInterface{std::move(info)} + , _router{router} + , _adapter{std::make_shared(dll, m_Info.ifname)} + , _session{std::make_shared(dll)} + , _recv_queue{packet_queue_length} + , _send_queue{packet_queue_length} + {} + + void + Start() override + { + m_Info.index = 0; + // put the adapter and set addresses + _adapter->Up(m_Info); + // start up io session + _session->Start(_adapter); + + // start read packet loop + _recv_thread = std::thread{[session = _session, this]() { + do + { + // read all our packets this iteration + bool more{true}; + do + { + auto [pkt, done] = session->ReadPacket(); + // bail if we are closing + if (done) + return; + if (pkt) + _recv_queue.pushBack(pkt->copy()); + else + more = false; + } while (more); + // wait for more packets + session->WaitFor(5s); + } while (true); + }}; + // start write packet loop + _send_thread = std::thread{[this, session = _session]() { + do + { + if (auto maybe = _send_queue.popFrontWithTimeout(100ms)) + { + auto [written, done] = session->WritePacket(std::move(*maybe)); + if (done) + return; + } + } while (_send_queue.enabled()); + }}; + } + + void + Stop() override + { + // end writing packets + _send_queue.disable(); + _send_thread.join(); + // end reading packets + _session->Stop(); + _recv_thread.join(); + // close session + _session.reset(); + // put adapter down + _adapter->Down(); + _adapter.reset(); + } + + net::IPPacket + ReadNextPacket() override + { + net::IPPacket pkt{}; + if (auto maybe_pkt = _recv_queue.tryPopFront()) + pkt = std::move(*maybe_pkt); + return pkt; + } + + bool + WritePacket(net::IPPacket pkt) override + { + return _send_queue.tryPushBack(std::move(pkt)) == thread::QueueReturn::Success; + } + + int + PollFD() const override + { + return -1; + } + + void + MaybeWakeUpperLayers() const override + { + _router->TriggerPump(); + } + }; + + struct WintunContext + { + WintunDLL dll{}; + }; + + std::shared_ptr + WintunContext_new() + { + return std::make_shared(); + } + + std::shared_ptr + WintunInterface_new( + std::shared_ptr const& ctx, + const llarp::vpn::InterfaceInfo& info, + llarp::AbstractRouter* r) + { + return std::static_pointer_cast( + std::make_shared(ctx->dll, info, r)); + } + +} // namespace llarp::win32 diff --git a/llarp/win32/wintun.hpp b/llarp/win32/wintun.hpp new file mode 100644 index 000000000..9bc5c0f52 --- /dev/null +++ b/llarp/win32/wintun.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +namespace llarp +{ + struct AbstractRouter; +} + +namespace llarp::vpn +{ + struct InterfaceInfo; + class NetworkInterface; +} // namespace llarp::vpn + +namespace llarp::win32 +{ + /// holds all wintun implementation, including function pointers we fetch out of the wintun + /// library forward declared to hide wintun from other compilation units + class WintunContext; + + std::shared_ptr + WintunContext_new(); + + /// makes a new vpn interface with a wintun context given info and a router pointer + std::shared_ptr + WintunInterface_new( + const std::shared_ptr&, + const vpn::InterfaceInfo& info, + AbstractRouter* router); + +} // namespace llarp::win32 diff --git a/pybind/CMakeLists.txt b/pybind/CMakeLists.txt index c8444599a..471e92319 100644 --- a/pybind/CMakeLists.txt +++ b/pybind/CMakeLists.txt @@ -16,5 +16,5 @@ pybind11_add_module(pyllarp MODULE llarp/tooling/router_event.cpp llarp/service/address.cpp ) -target_link_libraries(pyllarp PUBLIC liblokinet) +target_link_libraries(pyllarp PUBLIC lokinet-amalgum) target_include_directories(pyllarp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 4ec4c0131..8b686959d 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -74,7 +74,7 @@ namespace llarp self.OutboundLinks.emplace_back(std::move(_addr)); }) .def("addInboundLink", [](LinksConfig& self, std::string _addr) { - self.InboundLinks.emplace_back(std::move(_addr)); + self.InboundListenAddrs.emplace_back(std::move(_addr)); }); py::class_(mod, "ApiConfig") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cac731775..c11104139 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ if (WITH_HIVE) - add_custom_target(hive_build DEPENDS liblokinet pyllarp) + add_custom_target(hive_build DEPENDS lokinet-amalgum pyllarp) add_custom_target(hive ${CMAKE_COMMAND} -E env PYTHONPATH="$ENV{PYTHONPATH}:${CMAKE_BINARY_DIR}/pybind" ${PYTHON_EXECUTABLE} -m pytest @@ -9,7 +9,6 @@ if (WITH_HIVE) endif() add_subdirectory(Catch2) - add_executable(testAll # helpers check_main.cpp @@ -19,7 +18,7 @@ add_executable(testAll config/test_llarp_config_ini.cpp config/test_llarp_config_output.cpp config/test_llarp_config_values.cpp - crypto/test_llarp_crypto_types.cpp + crypto/test_llarp_crypto_types.cpp crypto/test_llarp_crypto.cpp crypto/test_llarp_key_manager.cpp dns/test_llarp_dns_dns.cpp @@ -46,14 +45,16 @@ add_executable(testAll test_llarp_encrypted_frame.cpp test_llarp_router_contact.cpp) -if(WITH_PEERSTATS) + +if(WITH_PEERSTATS_BACKEND) target_sources(testAll PRIVATE peerstats/test_peer_db.cpp peerstats/test_peer_types.cpp) endif() -target_link_libraries(testAll PUBLIC liblokinet Catch2::Catch2) +target_link_libraries(testAll PUBLIC lokinet-amalgum Catch2::Catch2) target_include_directories(testAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + if(WIN32) target_sources(testAll PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/win32/test.rc") target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi) diff --git a/test/mocks/mock_network.hpp b/test/mocks/mock_network.hpp index 54e92a44a..4653e053b 100644 --- a/test/mocks/mock_network.hpp +++ b/test/mocks/mock_network.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace mocks @@ -15,7 +15,7 @@ namespace mocks std::optional _addr; public: - MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv) + MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv) : llarp::UDPHandle{recv}, _net{net} {} @@ -54,6 +54,12 @@ namespace mocks , _snode{snode} {} + const llarp::net::Platform* + Net_ptr() const override + { + return this; + } + void run() override { @@ -80,7 +86,7 @@ namespace mocks GetBestNetIF(int af) const override { for (const auto& [k, range] : _network_interfaces) - if (range.Family() == af and not range.BogonRange()) + if (range.Family() == af and not IsBogonRange(range)) return k; return std::nullopt; } @@ -116,29 +122,6 @@ namespace mocks return m_Default->AllInterfaces(fallback); } - llarp::SockAddr - Wildcard(int af) const override - { - return m_Default->Wildcard(af); - } - - bool - IsBogon(const llarp::SockAddr& addr) const override - { - return m_Default->IsBogon(addr); - } - bool - IsLoopbackAddress(llarp::net::ipaddr_t ip) const override - { - return m_Default->IsLoopbackAddress(ip); - } - - bool - IsWildcardAddress(llarp::net::ipaddr_t ip) const override - { - return m_Default->IsWildcardAddress(ip); - } - std::optional GetInterfaceIndex(llarp::net::ipaddr_t ip) const override { @@ -188,6 +171,25 @@ namespace mocks return name; throw std::runtime_error{"no loopback interface?"}; } + + std::vector + AllNetworkInterfaces() const override + { + std::map _addrs; + for(const auto & [ifname, range] : _network_interfaces) + { + auto & ent = _addrs[ifname]; + ent.name = ifname; + ent.addrs.emplace_back(range); + } + std::vector infos; + for(const auto & [name, info] : _addrs) + { + infos.emplace_back(info); + infos.back().index = infos.size(); + } + return infos; + } }; bool diff --git a/test/mocks/mock_vpn.hpp b/test/mocks/mock_vpn.hpp index 538102226..6841bdd7d 100644 --- a/test/mocks/mock_vpn.hpp +++ b/test/mocks/mock_vpn.hpp @@ -9,7 +9,7 @@ namespace mocks int _pipes[2]; public: - MockInterface(llarp::vpn::InterfaceInfo) : llarp::vpn::NetworkInterface{} + MockInterface(llarp::vpn::InterfaceInfo info) : llarp::vpn::NetworkInterface{std::move(info)} { if (pipe(_pipes)) throw std::runtime_error{strerror(errno)}; @@ -26,12 +26,6 @@ namespace mocks return _pipes[0]; }; - std::string - IfName() const override - { - return "ligma"; - }; - llarp::net::IPPacket ReadNextPacket() override { @@ -64,13 +58,13 @@ namespace mocks return &_net; }; - void AddRoute(IPVariant_t, IPVariant_t) override{}; + void AddRoute(llarp::net::ipaddr_t, llarp::net::ipaddr_t) override{}; - void DelRoute(IPVariant_t, IPVariant_t) override{}; + void DelRoute(llarp::net::ipaddr_t, llarp::net::ipaddr_t) override{}; - void AddDefaultRouteViaInterface(std::string) override{}; + void AddDefaultRouteViaInterface(llarp::vpn::NetworkInterface&) override{}; - void DelDefaultRouteViaInterface(std::string) override{}; + void DelDefaultRouteViaInterface(llarp::vpn::NetworkInterface&) override{}; void AddRouteViaInterface(llarp::vpn::NetworkInterface&, llarp::IPRange) override{}; @@ -78,9 +72,9 @@ namespace mocks void DelRouteViaInterface(llarp::vpn::NetworkInterface&, llarp::IPRange) override{}; - std::vector GetGatewaysNotOnInterface(std::string) override + std::vector GetGatewaysNotOnInterface(llarp::vpn::NetworkInterface&) override { - return std::vector{}; + return std::vector{}; }; /// get owned ip route manager for managing routing table diff --git a/test/net/test_llarp_net.cpp b/test/net/test_llarp_net.cpp index c383684c1..b65f8ce73 100644 --- a/test/net/test_llarp_net.cpp +++ b/test/net/test_llarp_net.cpp @@ -6,6 +6,16 @@ #include +namespace +{ + template + bool IsBogon(T ip) + { + return llarp::net::Platform::Default_ptr()->IsBogon(ip); + } +} + + TEST_CASE("In6Addr") { llarp::huint128_t ip; @@ -83,30 +93,30 @@ TEST_CASE("Bogon") { SECTION("Bogon_10_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6))); } SECTION("Bogon_192_168_16") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111))); } SECTION("Bogon_127_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); } SECTION("Bogon_0_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0))); } SECTION("Non-bogon") { - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4))); } } diff --git a/win32-setup/extra_install.nsis b/win32-setup/extra_install.nsis index ec07fb540..a00e002d3 100644 --- a/win32-setup/extra_install.nsis +++ b/win32-setup/extra_install.nsis @@ -1,7 +1,13 @@ -ifFileExists $INSTDIR\bin\tuntap-install.exe 0 +2 -ExecWait '$INSTDIR\bin\tuntap-install.exe /S' ExecWait '$INSTDIR\bin\lokinet.exe --install' -ExecWait 'sc failure lokinet reset= 60 actions= restart/1000' ExecWait '$INSTDIR\bin\lokinet.exe -g C:\ProgramData\lokinet\lokinet.ini' CopyFiles '$INSTDIR\share\bootstrap.signed' C:\ProgramData\lokinet\bootstrap.signed +ifFileExists $INSTDIR\share\conf.d 0 +3 +CreateDirectory C:\ProgramData\lokinet\conf.d +CopyFiles '$INSTDIR\share\conf.d\*.ini' C:\ProgramData\lokinet\conf.d + +IfFileExists $INSTDIR\bin\WinDivert64.sys +2 0 +CopyFiles '$INSTDIR\lib\WinDivert64.sys' '$INSTDIR\bin\WinDivert64.sys' + +IfFileExists $INSTDIR\bin\WinDivert.sys +2 0 +CopyFiles '$INSTDIR\lib\WinDivert.sys' '$INSTDIR\bin\WinDivert.sys' \ No newline at end of file diff --git a/win32-setup/extra_preinstall.nsis b/win32-setup/extra_preinstall.nsis index de3904f6f..1e6637d3d 100644 --- a/win32-setup/extra_preinstall.nsis +++ b/win32-setup/extra_preinstall.nsis @@ -1,6 +1,6 @@ +ExecWait 'taskkill /f /t /im lokinet-gui.exe' + IfFileExists $INSTDIR\bin\lokinet.exe 0 +3 ExecWait 'net stop lokinet' ExecWait '$INSTDIR\bin\lokinet.exe --remove' -IfFileExists $INSTDIR\share\gui\lokinet.exe 0 +2 -ExecWait 'taskkill /f /t /im lokinet-gui.exe' diff --git a/win32-setup/extra_uninstall.nsis b/win32-setup/extra_uninstall.nsis index ea8664219..5f24cc341 100644 --- a/win32-setup/extra_uninstall.nsis +++ b/win32-setup/extra_uninstall.nsis @@ -1,5 +1,13 @@ -ExecWait 'net stop lokinet' ExecWait 'taskkill /f /t /im lokinet-gui.exe' +ExecWait 'net stop lokinet' +ExecWait 'sc stop windivert' ExecWait '$INSTDIR\bin\lokinet.exe --remove' -RMDir /r /REBOOTOK C:\ProgramData\lokinet +IfFileExists '$INSTDIR\bin\WinDivert64.sys' 0 +2 +Delete /REBOOTOK '$INSTDIR\bin\WinDivert64.sys' + +IfFileExists '$INSTDIR\bin\WinDivert.sys' 0 +2 +Delete /REBOOTOK '$INSTDIR\bin\WinDivert.sys' + +RMDir /r /REBOOTOK C:\ProgramData\lokinet +RMDir /r /REBOOTOK '$INSTDIR\share\conf.d' From 7f27760c97da11dcca0b7aa54f869989a65fb7da Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 29 Aug 2022 10:24:04 -0400 Subject: [PATCH 08/71] disable lokinet-bootstrap on windows builds --- contrib/windows-configure.sh | 1 + daemon/CMakeLists.txt | 1 - daemon/lokinet.cpp | 2 +- llarp/constants/files.hpp | 23 +++++++++++++---------- llarp/dns/multi_platform.hpp | 1 - llarp/dns/nm_platform.hpp | 1 - llarp/dns/null_platform.hpp | 1 - llarp/dns/sd_platform.hpp | 1 - llarp/ev/ev.hpp | 1 - llarp/win32/windivert.cpp | 12 +++++++++--- 10 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 llarp/dns/multi_platform.hpp delete mode 100644 llarp/dns/null_platform.hpp diff --git a/contrib/windows-configure.sh b/contrib/windows-configure.sh index c4e193fe3..49668c7e7 100755 --- a/contrib/windows-configure.sh +++ b/contrib/windows-configure.sh @@ -21,6 +21,7 @@ cmake \ -DBUILD_TESTING=OFF \ -DBUILD_LIBLOKINET=OFF \ -DWITH_TESTS=OFF \ + -DWITH_BOOTSTRAP=OFF \ -DNATIVE_BUILD=OFF \ -DSTATIC_LINK=ON \ -DWITH_SYSTEMD=OFF \ diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 5372fe46d..c9ff4aec6 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -55,7 +55,6 @@ endif() foreach(exe ${exetargets}) if(WIN32) target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) - target_compile_options(${exe} PRIVATE $<$:-fpermissive>) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_link_directories(${exe} PRIVATE /usr/local/lib) diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 8ff06fdfd..ed8bfc09b 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -680,7 +680,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv) ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); // SCM clobbers startup args, regenerate them here argc = 2; - argv[1] = strdup("c:/programdata/lokinet/lokinet.ini"); + argv[1] = strdup("c:\\programdata\\lokinet\\lokinet.ini"); argv[2] = nullptr; lokinet_main(argc, argv); } diff --git a/llarp/constants/files.hpp b/llarp/constants/files.hpp index 27d7dd69a..6e0085121 100644 --- a/llarp/constants/files.hpp +++ b/llarp/constants/files.hpp @@ -1,4 +1,5 @@ #pragma once +#include "platform.hpp" #include @@ -21,20 +22,22 @@ namespace llarp inline fs::path GetDefaultDataDir() { -#ifdef _WIN32 - return "C:/programdata/lokinet"; -#else - fs::path datadir{"/var/lib/lokinet"}; - - if (auto uid = ::geteuid()) + if constexpr (not platform::is_windows) { - if (auto* pw = getpwuid(uid)) + fs::path datadir{"/var/lib/lokinet"}; +#ifndef _WIN32 + if (auto uid = geteuid()) { - datadir = fs::path{pw->pw_dir} / ".lokinet"; + if (auto* pw = getpwuid(uid)) + { + datadir = fs::path{pw->pw_dir} / ".lokinet"; + } } - } - return datadir; #endif + return datadir; + } + else + return "C:\\ProgramData\\Lokinet"; } inline fs::path diff --git a/llarp/dns/multi_platform.hpp b/llarp/dns/multi_platform.hpp deleted file mode 100644 index 8b1378917..000000000 --- a/llarp/dns/multi_platform.hpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/llarp/dns/nm_platform.hpp b/llarp/dns/nm_platform.hpp index 742b050c6..593cfaa28 100644 --- a/llarp/dns/nm_platform.hpp +++ b/llarp/dns/nm_platform.hpp @@ -1,6 +1,5 @@ #pragma once #include "platform.hpp" -#include "null_platform.hpp" #include #include diff --git a/llarp/dns/null_platform.hpp b/llarp/dns/null_platform.hpp deleted file mode 100644 index 8b1378917..000000000 --- a/llarp/dns/null_platform.hpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/llarp/dns/sd_platform.hpp b/llarp/dns/sd_platform.hpp index 532e73a15..27db6d6fa 100644 --- a/llarp/dns/sd_platform.hpp +++ b/llarp/dns/sd_platform.hpp @@ -1,6 +1,5 @@ #pragma once #include "platform.hpp" -#include "null_platform.hpp" #include #include diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index 54e9046f5..6addbf3d5 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -223,7 +223,6 @@ namespace llarp return nullptr; } - protected: // Triggers an event loop wakeup; use when something has been done that requires the event loop // to wake up (e.g. adding to queues). This is called implicitly by call() and call_soon(). // Idempotent and thread-safe. diff --git a/llarp/win32/windivert.cpp b/llarp/win32/windivert.cpp index 4e4e4d3f4..359d0f7b2 100644 --- a/llarp/win32/windivert.cpp +++ b/llarp/win32/windivert.cpp @@ -66,6 +66,8 @@ namespace llarp::win32 HANDLE m_Handle; std::thread m_Runner; thread::Queue m_RecvQueue; + // dns packet queue size + static constexpr size_t recv_queue_size = 64; public: WinDivert_IO( @@ -132,7 +134,7 @@ namespace llarp::win32 virtual net::IPPacket ReadNextPacket() override { - auto w_pkt = recv_packet(); + auto w_pkt = m_RecvQueue.tryPopFront(); if (not w_pkt) return net::IPPacket{}; net::IPPacket pkt{std::move(w_pkt->pkt)}; @@ -150,17 +152,21 @@ namespace llarp::win32 throw std::runtime_error{"windivert thread is already running"}; auto read_loop = [this]() { - log::debug(cat, "windivert read loop start"); + log::info(cat, "windivert read loop start"); while (true) { // in the read loop, read packets until they stop coming in // each packet is sent off if (auto maybe_pkt = recv_packet()) + { m_RecvQueue.pushBack(std::move(*maybe_pkt)); + // wake up event loop + m_Wake(); + } else // leave loop on read fail break; } - log::debug(cat, "windivert read loop end"); + log::info(cat, "windivert read loop end"); }; m_Runner = std::thread{std::move(read_loop)}; From a02679b87a4538134f44fde7c9b22a81125c7eb1 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 1 Sep 2022 10:08:15 -0400 Subject: [PATCH 09/71] revise ./contrib/format.sh --- contrib/format.sh | 4 +- llarp/apple/DNSTrampoline.m | 142 ++++++++------ llarp/apple/PacketTunnelProvider.m | 289 +++++++++++++++++------------ 3 files changed, 256 insertions(+), 179 deletions(-) diff --git a/contrib/format.sh b/contrib/format.sh index 956f5db92..6f4e8af36 100755 --- a/contrib/format.sh +++ b/contrib/format.sh @@ -21,11 +21,11 @@ fi cd "$(dirname $0)/../" if [ "$1" = "verify" ] ; then - if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm?)$' | grep -v '\#') | grep '' | wc -l) -ne 0 ] ; then + if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '\#') | grep '' | wc -l) -ne 0 ] ; then exit 2 fi else - $binary -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm)$' | grep -v '\#') &> /dev/null + $binary -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '\#') &> /dev/null fi swift_format=$(command -v swiftformat 2>/dev/null) diff --git a/llarp/apple/DNSTrampoline.m b/llarp/apple/DNSTrampoline.m index cbbe211a3..c30cabc25 100644 --- a/llarp/apple/DNSTrampoline.m +++ b/llarp/apple/DNSTrampoline.m @@ -3,23 +3,31 @@ NSString* error_domain = @"org.lokinet"; - // Receiving an incoming packet, presumably from libunbound. NB: this is called from the libuv // event loop. -static void on_request(uv_udp_t* socket, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { - if (nread < 0) { +static void +on_request( + uv_udp_t* socket, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) +{ + if (nread < 0) + { NSLog(@"Read error: %s", uv_strerror(nread)); free(buf->base); return; } - if (nread == 0 || !addr) { + if (nread == 0 || !addr) + { if (buf) free(buf->base); return; } - LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*) socket->data; + LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)socket->data; // We configure libunbound to use just one single port so we'll just send replies to the last port // to talk to us. (And we're only listening on localhost in the first place). @@ -31,61 +39,67 @@ static void on_request(uv_udp_t* socket, ssize_t nread, const uv_buf_t* buf, con [t flushWrites]; } -static void on_sent(uv_udp_send_t* req, int status) { - NSArray* datagrams = (__bridge_transfer NSArray*) req->data; +static void +on_sent(uv_udp_send_t* req, int status) +{ + NSArray* datagrams = (__bridge_transfer NSArray*)req->data; free(req); } // NB: called from the libuv event loop (so we don't have to worry about the above and this one // running at once from different threads). -static void write_flusher(uv_async_t* async) { - LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*) async->data; +static void +write_flusher(uv_async_t* async) +{ + LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)async->data; if (t->pending_writes.count == 0) return; NSArray* data = [NSArray arrayWithArray:t->pending_writes]; [t->pending_writes removeAllObjects]; __weak LLARPDNSTrampoline* weakSelf = t; - [t->upstream writeMultipleDatagrams:data completionHandler: ^(NSError* error) - { - if (error) - NSLog(@"Failed to send request to upstream DNS: %@", error); - - // Trigger another flush in case anything built up while Apple was doing its things. Just - // call it unconditionally (rather than checking the queue) because this handler is probably - // running in some other thread. - [weakSelf flushWrites]; - } - ]; + [t->upstream writeMultipleDatagrams:data + completionHandler:^(NSError* error) { + if (error) + NSLog(@"Failed to send request to upstream DNS: %@", error); + + // Trigger another flush in case anything built up while Apple was doing its + // things. Just call it unconditionally (rather than checking the queue) + // because this handler is probably running in some other thread. + [weakSelf flushWrites]; + }]; } - -static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { +static void +alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) +{ buf->base = malloc(suggested_size); buf->len = suggested_size; } @implementation LLARPDNSTrampoline -- (void)startWithUpstreamDns:(NWUDPSession*) dns - listenIp:(NSString*) listenIp - listenPort:(uint16_t) listenPort - uvLoop:(uv_loop_t*) loop +- (void)startWithUpstreamDns:(NWUDPSession*)dns + listenIp:(NSString*)listenIp + listenPort:(uint16_t)listenPort + uvLoop:(uv_loop_t*)loop completionHandler:(void (^)(NSError* error))completionHandler { NSLog(@"Setting up trampoline"); pending_writes = [[NSMutableArray alloc] init]; - write_trigger.data = (__bridge void*) self; + write_trigger.data = (__bridge void*)self; uv_async_init(loop, &write_trigger, write_flusher); - request_socket.data = (__bridge void*) self; + request_socket.data = (__bridge void*)self; uv_udp_init(loop, &request_socket); struct sockaddr_in recv_addr; uv_ip4_addr(listenIp.UTF8String, listenPort, &recv_addr); - int ret = uv_udp_bind(&request_socket, (const struct sockaddr*) &recv_addr, UV_UDP_REUSEADDR); - if (ret < 0) { - NSString* errstr = [NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)]; - NSError *err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}]; + int ret = uv_udp_bind(&request_socket, (const struct sockaddr*)&recv_addr, UV_UDP_REUSEADDR); + if (ret < 0) + { + NSString* errstr = + [NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)]; + NSError* err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}]; NSLog(@"%@", err); return completionHandler(err); } @@ -95,30 +109,40 @@ static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* b upstream = dns; __weak LLARPDNSTrampoline* weakSelf = self; - [upstream setReadHandler: ^(NSArray* datagrams, NSError* error) { - // Reading a reply back from the UDP socket used to talk to upstream - if (error) { - NSLog(@"Reader handler failed: %@", error); - return; - } - LLARPDNSTrampoline* strongSelf = weakSelf; - if (!strongSelf || datagrams.count == 0) - return; - - uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t)); - size_t buf_count = 0; - for (NSData* packet in datagrams) { - buffers[buf_count].base = (void*) packet.bytes; - buffers[buf_count].len = packet.length; - buf_count++; - } - uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t)); - uvsend->data = (__bridge_retained void*) datagrams; - int ret = uv_udp_send(uvsend, &strongSelf->request_socket, buffers, buf_count, &strongSelf->reply_addr, on_sent); - free(buffers); - if (ret < 0) - NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret)); - } maxDatagrams:NSUIntegerMax]; + [upstream + setReadHandler:^(NSArray* datagrams, NSError* error) { + // Reading a reply back from the UDP socket used to talk to upstream + if (error) + { + NSLog(@"Reader handler failed: %@", error); + return; + } + LLARPDNSTrampoline* strongSelf = weakSelf; + if (!strongSelf || datagrams.count == 0) + return; + + uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t)); + size_t buf_count = 0; + for (NSData* packet in datagrams) + { + buffers[buf_count].base = (void*)packet.bytes; + buffers[buf_count].len = packet.length; + buf_count++; + } + uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t)); + uvsend->data = (__bridge_retained void*)datagrams; + int ret = uv_udp_send( + uvsend, + &strongSelf->request_socket, + buffers, + buf_count, + &strongSelf->reply_addr, + on_sent); + free(buffers); + if (ret < 0) + NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret)); + } + maxDatagrams:NSUIntegerMax]; completionHandler(nil); } @@ -128,11 +152,11 @@ static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* b uv_async_send(&write_trigger); } -- (void) dealloc +- (void)dealloc { NSLog(@"Stopping DNS trampoline"); - uv_close((uv_handle_t*) &request_socket, NULL); - uv_close((uv_handle_t*) &write_trigger, NULL); + uv_close((uv_handle_t*)&request_socket, NULL); + uv_close((uv_handle_t*)&write_trigger, NULL); } @end diff --git a/llarp/apple/PacketTunnelProvider.m b/llarp/apple/PacketTunnelProvider.m index 17e76d4f1..2cf8c2917 100644 --- a/llarp/apple/PacketTunnelProvider.m +++ b/llarp/apple/PacketTunnelProvider.m @@ -9,9 +9,12 @@ { void* lokinet; llarp_incoming_packet packet_buf[LLARP_APPLE_PACKET_BUF_SIZE]; - @public NEPacketTunnelNetworkSettings* settings; - @public NEIPv4Route* tun_route4; - @public NEIPv6Route* tun_route6; + @public + NEPacketTunnelNetworkSettings* settings; + @public + NEIPv4Route* tun_route4; + @public + NEIPv6Route* tun_route6; LLARPDNSTrampoline* dns_tramp; } @@ -30,107 +33,133 @@ @end -static void nslogger(const char* msg) { NSLog(@"%s", msg); } +static void +nslogger(const char* msg) +{ + NSLog(@"%s", msg); +} -static void packet_writer(int af, const void* data, size_t size, void* ctx) { +static void +packet_writer(int af, const void* data, size_t size, void* ctx) +{ if (ctx == nil || data == nil) return; NSData* buf = [NSData dataWithBytesNoCopy:(void*)data length:size freeWhenDone:NO]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; - [t.packetFlow writePackets:@[buf] - withProtocols:@[[NSNumber numberWithInt:af]]]; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + [t.packetFlow writePackets:@[buf] withProtocols:@[[NSNumber numberWithInt:af]]]; } -static void start_packet_reader(void* ctx) { +static void +start_packet_reader(void* ctx) +{ if (ctx == nil) return; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; [t readPackets]; } -static void add_ipv4_route(const char* addr, const char* netmask, void* ctx) { +static void +add_ipv4_route(const char* addr, const char* netmask, void* ctx) +{ NSLog(@"Adding IPv4 route %s:%s to packet tunnel", addr, netmask); - NEIPv4Route* route = [[NEIPv4Route alloc] - initWithDestinationAddress: [NSString stringWithUTF8String:addr] - subnetMask: [NSString stringWithUTF8String:netmask]]; + NEIPv4Route* route = + [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + subnetMask:[NSString stringWithUTF8String:netmask]]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; for (NEIPv4Route* r in t->settings.IPv4Settings.includedRoutes) if ([r.destinationAddress isEqualToString:route.destinationAddress] && - [r.destinationSubnetMask isEqualToString:route.destinationSubnetMask]) - return; // Already in the settings, nothing to add. + [r.destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + return; // Already in the settings, nothing to add. t->settings.IPv4Settings.includedRoutes = - [t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route]; + [t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route]; [t updateNetworkSettings]; } -static void del_ipv4_route(const char* addr, const char* netmask, void* ctx) { +static void +del_ipv4_route(const char* addr, const char* netmask, void* ctx) +{ NSLog(@"Removing IPv4 route %s:%s to packet tunnel", addr, netmask); - NEIPv4Route* route = [[NEIPv4Route alloc] - initWithDestinationAddress: [NSString stringWithUTF8String:addr] - subnetMask: [NSString stringWithUTF8String:netmask]]; - - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; - NSMutableArray* routes = [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes]; - for (int i = 0; i < routes.count; i++) { + NEIPv4Route* route = + [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + subnetMask:[NSString stringWithUTF8String:netmask]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSMutableArray* routes = + [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes]; + for (int i = 0; i < routes.count; i++) + { if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && - [routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) { + [routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + { [routes removeObjectAtIndex:i]; i--; } } - if (routes.count != t->settings.IPv4Settings.includedRoutes.count) { + if (routes.count != t->settings.IPv4Settings.includedRoutes.count) + { t->settings.IPv4Settings.includedRoutes = routes; [t updateNetworkSettings]; } } -static void add_ipv6_route(const char* addr, int prefix, void* ctx) { - NEIPv6Route* route = [[NEIPv6Route alloc] - initWithDestinationAddress: [NSString stringWithUTF8String:addr] - networkPrefixLength: [NSNumber numberWithInt:prefix]]; +static void +add_ipv6_route(const char* addr, int prefix, void* ctx) +{ + NEIPv6Route* route = + [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + networkPrefixLength:[NSNumber numberWithInt:prefix]]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; for (NEIPv6Route* r in t->settings.IPv6Settings.includedRoutes) if ([r.destinationAddress isEqualToString:route.destinationAddress] && - [r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) - return; // Already in the settings, nothing to add. + [r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) + return; // Already in the settings, nothing to add. t->settings.IPv6Settings.includedRoutes = - [t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route]; + [t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route]; [t updateNetworkSettings]; } -static void del_ipv6_route(const char* addr, int prefix, void* ctx) { - NEIPv6Route* route = [[NEIPv6Route alloc] - initWithDestinationAddress: [NSString stringWithUTF8String:addr] - networkPrefixLength: [NSNumber numberWithInt:prefix]]; - - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; - NSMutableArray* routes = [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes]; - for (int i = 0; i < routes.count; i++) { +static void +del_ipv6_route(const char* addr, int prefix, void* ctx) +{ + NEIPv6Route* route = + [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + networkPrefixLength:[NSNumber numberWithInt:prefix]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSMutableArray* routes = + [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes]; + for (int i = 0; i < routes.count; i++) + { if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && - [routes[i].destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) { + [routes[i].destinationNetworkPrefixLength + isEqualToNumber:route.destinationNetworkPrefixLength]) + { [routes removeObjectAtIndex:i]; i--; } } - if (routes.count != t->settings.IPv6Settings.includedRoutes.count) { + if (routes.count != t->settings.IPv6Settings.includedRoutes.count) + { t->settings.IPv6Settings.includedRoutes = routes; [t updateNetworkSettings]; } } -static void add_default_route(void* ctx) { +static void +add_default_route(void* ctx) +{ NSLog(@"Making the tunnel the default route"); - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; t->settings.IPv4Settings.includedRoutes = @[NEIPv4Route.defaultRoute]; t->settings.IPv6Settings.includedRoutes = @[NEIPv6Route.defaultRoute]; @@ -138,9 +167,11 @@ static void add_default_route(void* ctx) { [t updateNetworkSettings]; } -static void del_default_route(void* ctx) { +static void +del_default_route(void* ctx) +{ NSLog(@"Removing default route from tunnel"); - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; t->settings.IPv4Settings.includedRoutes = @[t->tun_route4]; t->settings.IPv6Settings.includedRoutes = @[t->tun_route6]; @@ -152,12 +183,13 @@ static void del_default_route(void* ctx) { - (void)readPackets { - [self.packetFlow readPacketObjectsWithCompletionHandler: ^(NSArray* packets) { + [self.packetFlow readPacketObjectsWithCompletionHandler:^(NSArray* packets) { if (lokinet == nil) return; size_t size = 0; - for (NEPacket* p in packets) { + for (NEPacket* p in packets) + { packet_buf[size].bytes = p.data.bytes; packet_buf[size].size = p.data.length; size++; @@ -186,19 +218,21 @@ static void del_default_route(void* ctx) { .ns_logger = nslogger, .packet_writer = packet_writer, .start_reading = start_packet_reader, - .route_callbacks = { - .add_ipv4_route = add_ipv4_route, - .del_ipv4_route = del_ipv4_route, - .add_ipv6_route = add_ipv6_route, - .del_ipv6_route = del_ipv6_route, - .add_default_route = add_default_route, - .del_default_route = del_default_route - }, + .route_callbacks = + {.add_ipv4_route = add_ipv4_route, + .del_ipv4_route = del_ipv4_route, + .add_ipv6_route = add_ipv6_route, + .del_ipv6_route = del_ipv6_route, + .add_default_route = add_default_route, + .del_default_route = del_default_route}, }; lokinet = llarp_apple_init(&conf); - if (!lokinet) { - NSError *init_failure = [NSError errorWithDomain:error_domain code:500 userInfo:@{@"Error": @"Failed to initialize lokinet"}]; + if (!lokinet) + { + NSError* init_failure = [NSError errorWithDomain:error_domain + code:500 + userInfo:@{@"Error": @"Failed to initialize lokinet"}]; NSLog(@"%@", [init_failure localizedDescription]); return completionHandler(init_failure); } @@ -237,11 +271,12 @@ static void del_default_route(void* ctx) { NWHostEndpoint* upstreamdns_ep; if (strlen(conf.upstream_dns)) - upstreamdns_ep = [NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns] port:@(conf.upstream_dns_port).stringValue]; + upstreamdns_ep = + [NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns] + port:@(conf.upstream_dns_port).stringValue]; - NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] - subnetMasks:@[mask]]; - tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask: mask]; + NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] subnetMasks:@[mask]]; + tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask:mask]; ipv4.includedRoutes = @[tun_route4]; settings.IPv4Settings = ipv4; @@ -249,50 +284,62 @@ static void del_default_route(void* ctx) { NSNumber* ip6_prefix = [NSNumber numberWithUnsignedInt:conf.tunnel_ipv6_prefix]; NEIPv6Settings* ipv6 = [[NEIPv6Settings alloc] initWithAddresses:@[ip6] networkPrefixLengths:@[ip6_prefix]]; - tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6 - networkPrefixLength:ip6_prefix]; + tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6 networkPrefixLength:ip6_prefix]; ipv6.includedRoutes = @[tun_route6]; settings.IPv6Settings = ipv6; __weak LLARPPacketTunnel* weakSelf = self; - [self setTunnelNetworkSettings:settings completionHandler:^(NSError* err) { - if (err) { - NSLog(@"Failed to configure lokinet tunnel: %@", err); - return completionHandler(err); - } - LLARPPacketTunnel* strongSelf = weakSelf; - if (!strongSelf) - return completionHandler(nil); - - int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*) strongSelf); - if (start_ret != 0) { - NSError *start_failure = [NSError errorWithDomain:error_domain code:start_ret userInfo:@{@"Error": @"Failed to start lokinet"}]; - NSLog(@"%@", start_failure); - lokinet = nil; - return completionHandler(start_failure); - } - - NSString* dns_tramp_ip = @"127.0.0.1"; - NSLog(@"Starting DNS exit mode trampoline to %@ on %@:%d", upstreamdns_ep, dns_tramp_ip, dns_trampoline_port); - NWUDPSession* upstreamdns = [strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep fromEndpoint:nil]; - strongSelf->dns_tramp = [LLARPDNSTrampoline alloc]; - [strongSelf->dns_tramp - startWithUpstreamDns:upstreamdns - listenIp:dns_tramp_ip - listenPort:dns_trampoline_port - uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet) - completionHandler:^(NSError* error) { - if (error) - NSLog(@"Error starting dns trampoline: %@", error); - return completionHandler(error); - }]; - }]; + [self setTunnelNetworkSettings:settings + completionHandler:^(NSError* err) { + if (err) + { + NSLog(@"Failed to configure lokinet tunnel: %@", err); + return completionHandler(err); + } + LLARPPacketTunnel* strongSelf = weakSelf; + if (!strongSelf) + return completionHandler(nil); + + int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*)strongSelf); + if (start_ret != 0) + { + NSError* start_failure = + [NSError errorWithDomain:error_domain + code:start_ret + userInfo:@{@"Error": @"Failed to start lokinet"}]; + NSLog(@"%@", start_failure); + lokinet = nil; + return completionHandler(start_failure); + } + + NSString* dns_tramp_ip = @"127.0.0.1"; + NSLog( + @"Starting DNS exit mode trampoline to %@ on %@:%d", + upstreamdns_ep, + dns_tramp_ip, + dns_trampoline_port); + NWUDPSession* upstreamdns = + [strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep + fromEndpoint:nil]; + strongSelf->dns_tramp = [LLARPDNSTrampoline alloc]; + [strongSelf->dns_tramp + startWithUpstreamDns:upstreamdns + listenIp:dns_tramp_ip + listenPort:dns_trampoline_port + uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet) + completionHandler:^(NSError* error) { + if (error) + NSLog(@"Error starting dns trampoline: %@", error); + return completionHandler(error); + }]; + }]; } - (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler:(void (^)(void))completionHandler { - if (lokinet) { + if (lokinet) + { llarp_apple_shutdown(lokinet); lokinet = nil; } @@ -319,29 +366,35 @@ static void del_default_route(void* ctx) { // // Thanks for the accurate documentation, Apple. // - [self setTunnelNetworkSettings:nil completionHandler:^(NSError* err) { - if (err) - NSLog(@"Failed to clear lokinet tunnel settings: %@", err); - LLARPPacketTunnel* strongSelf = weakSelf; - if (strongSelf) { - [weakSelf setTunnelNetworkSettings:strongSelf->settings completionHandler:^(NSError* err) { - LLARPPacketTunnel* strongSelf = weakSelf; - if (strongSelf) - strongSelf.reasserting = NO; - if (err) - NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err); - }]; - } - }]; + [self setTunnelNetworkSettings:nil + completionHandler:^(NSError* err) { + if (err) + NSLog(@"Failed to clear lokinet tunnel settings: %@", err); + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) + { + [weakSelf + setTunnelNetworkSettings:strongSelf->settings + completionHandler:^(NSError* err) { + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) + strongSelf.reasserting = NO; + if (err) + NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err); + }]; + } + }]; } @end #ifdef MACOS_SYSTEM_EXTENSION -int main() { - [NEProvider startSystemExtensionMode]; - dispatch_main(); +int +main() +{ + [NEProvider startSystemExtensionMode]; + dispatch_main(); } #endif From 49b97f47ccc5e3ca91b959d98726f73c65a81ab1 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 1 Sep 2022 15:15:34 -0300 Subject: [PATCH 10/71] Make windows-configure.sh runnable without root/build For when you want to set up a windows build dir, without doing the build yet. --- contrib/windows-configure.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/contrib/windows-configure.sh b/contrib/windows-configure.sh index 49668c7e7..f41ef3af8 100755 --- a/contrib/windows-configure.sh +++ b/contrib/windows-configure.sh @@ -2,12 +2,25 @@ set -e set -x -root=$(readlink -f "$1") -shift -mkdir -p "$1" -build=$(readlink -f "$1") -shift -cd "$build" +# Usage: windows-configure.sh [rootdir [builddir]] -DWHATEVER=BLAH ... + +if [ $# -ge 1 ] && [[ "$1" != -* ]]; then + root="$1" + shift +else + root="$(dirname $0)"/.. +fi +root="$(readlink -f "$root")" + +if [ $# -ge 1 ] && [[ "$1" != -* ]]; then + build="$(readlink -f "$1")" + shift +else + build="$root/build/win32" + echo "Setting up build in $build" +fi + +mkdir -p "$build" cmake \ -S "$root" -B "$build" \ -G 'Unix Makefiles' \ @@ -31,4 +44,4 @@ cmake \ -DFORCE_SPDLOG_SUBMODULE=ON \ -DFORCE_NLOHMANN_SUBMODULE=ON \ -DWITH_LTO=OFF \ - $@ + "$@" From c9d928950a456cbd3f7b71e22d853be77121b606 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 1 Sep 2022 15:37:13 -0300 Subject: [PATCH 11/71] C-cast to work around gross enum==int assumption in windows API --- daemon/lokinet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index ed8bfc09b..ab89e790a 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -329,8 +329,9 @@ class WindowsServiceStopped LONG GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) { - const DWORD flags = MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData - | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo; + const auto flags = (MINIDUMP_TYPE)( + MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData + | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo); std::stringstream ss; ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp"; From 4490fdcf4643dcd3407b933f5ffb39bacccd8978 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 3 Sep 2022 11:43:40 -0400 Subject: [PATCH 12/71] fix up CI add jason's suggested changes for artifact upload use lokinet-ci-nodejs-lts as base image so we can build the installer update ci pipeline for windows to have building gui toggle-able by default we will build the gui from this repo, but this allows it to easily run using a custom gui asset if needed --- .drone.jsonnet | 14 +++++++------- CMakeLists.txt | 4 +++- contrib/ci/drone-static-upload.sh | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index d03f1baec..15cc443f6 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -76,6 +76,7 @@ local debian_pipeline(name, 'mkdir build', 'cd build', 'cmake .. -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE=' + build_type + ' ' + + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + '-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') + @@ -121,7 +122,7 @@ local windows_cross_pipeline(name, lto=false, werror=false, cmake_extra='', - toolchain='32', + gui_zip_url='', extra_cmds=[], jobs=6, allow_fail=false) = { @@ -137,16 +138,16 @@ local windows_cross_pipeline(name, image: image, pull: 'always', [if allow_fail then 'failure']: 'ignore', - environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, WINDOWS_BUILD_NAME: toolchain + 'bit' }, + environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, WINDOWS_BUILD_NAME: '64bit' }, commands: [ 'echo "Building on ${DRONE_STAGE_MACHINE}"', 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections', apt_get_quiet + ' update', apt_get_quiet + ' install -y eatmydata', - 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', + 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y p7zip-full build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', - 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, + 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh ' + (if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') + ' -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, ] + extra_cmds, }, ], @@ -277,7 +278,7 @@ local mac_builder(name, // basic system headers. WTF apple: 'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"', 'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk - './contrib/mac.sh ' + ci_mirror_opts + ' ' + codesign, + './contrib/mac.sh ' + ci_mirror_opts + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + codesign, ] + extra_cmds, }, ], @@ -358,8 +359,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = { // Windows builds (x64) windows_cross_pipeline('Windows (amd64)', - docker_base + 'debian-win32-cross', - toolchain='64', + docker_base + 'nodejs-lts', extra_cmds=[ './contrib/ci/drone-static-upload.sh', ]), diff --git a/CMakeLists.txt b/CMakeLists.txt index 1496b2a25..d8ad1a3c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,8 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") add_definitions(-DLOKINET_DEBUG) endif() +option(WARN_DEPRECATED "show deprecation warnings" ${debug}) + if(BUILD_STATIC_DEPS AND STATIC_LINK) message(STATUS "we are building static deps so we won't build shared libs") set(BUILD_SHARED_LIBS OFF CACHE BOOL "") @@ -186,7 +188,7 @@ else() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-unknown-warning-option) endif() - if(debug) + if(WARN_DEPRECATED) add_compile_options(-Wdeprecated-declarations) else() add_compile_options(-Wno-deprecated-declarations) diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index 80a87d5a9..4eb4921b5 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -67,6 +67,7 @@ upload_to="oxen.rocks/${DRONE_REPO// /_}/${DRONE_BRANCH// /_}" # -mkdir a/, -mkdir a/b/, -mkdir a/b/c/, ... commands. The leading `-` allows the command to fail # without error. upload_dirs=(${upload_to//\// }) +put_debug= mkdirs= dir_tmp="" for p in "${upload_dirs[@]}"; do @@ -74,16 +75,15 @@ for p in "${upload_dirs[@]}"; do mkdirs="$mkdirs -mkdir $dir_tmp" done - +if [ -e "$base-debug-symbols.tar.xz" ] ; then + put_debug="put $base-debug-symbols.tar.xz $upload_to" +fi sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks < Date: Mon, 5 Sep 2022 10:32:09 -0400 Subject: [PATCH 13/71] unit tests --- test/mocks/mock_vpn.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/mock_vpn.hpp b/test/mocks/mock_vpn.hpp index 6841bdd7d..9e7a3f374 100644 --- a/test/mocks/mock_vpn.hpp +++ b/test/mocks/mock_vpn.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include "mock_network.hpp" namespace mocks From 15443568dbe5b19ae480ae2adfd0f6c6fa5c950c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 9 Sep 2022 16:09:28 -0300 Subject: [PATCH 14/71] Apply some lipstick to llarp_buffer_t This class is cursed, but also broken under gcc-12. Apply some lipstick to get it moving again (but we really need to refactor this because it is a mess). --- llarp/CMakeLists.txt | 1 - llarp/consensus/table.cpp | 17 -------- llarp/consensus/table.hpp | 17 -------- llarp/exit/session.cpp | 6 +-- llarp/quic/endpoint.cpp | 3 +- llarp/service/outbound_context.cpp | 7 ++-- llarp/util/buffer.cpp | 6 --- llarp/util/buffer.hpp | 62 +++++++++++++++++++++++------- 8 files changed, 57 insertions(+), 62 deletions(-) delete mode 100644 llarp/consensus/table.cpp delete mode 100644 llarp/consensus/table.hpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index f033721d5..b860822d7 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -117,7 +117,6 @@ target_link_libraries(lokinet-config PUBLIC lokinet-dns lokinet-platform oxenmq: add_library(lokinet-amalgum STATIC - consensus/table.cpp consensus/reachability_testing.cpp bootstrap.cpp diff --git a/llarp/consensus/table.cpp b/llarp/consensus/table.cpp deleted file mode 100644 index 6645bc97c..000000000 --- a/llarp/consensus/table.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "table.hpp" -#include - -namespace llarp -{ - namespace consensus - { - ShortHash - Table::CalculateHash() const - { - ShortHash h; - const llarp_buffer_t buf(begin()->data(), size()); - CryptoManager::instance()->shorthash(h, buf); - return h; - } - } // namespace consensus -} // namespace llarp diff --git a/llarp/consensus/table.hpp b/llarp/consensus/table.hpp deleted file mode 100644 index 64327255b..000000000 --- a/llarp/consensus/table.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -namespace llarp -{ - namespace consensus - { - /// consensus table - struct Table : public std::vector - { - ShortHash - CalculateHash() const; - }; - } // namespace consensus -} // namespace llarp diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 1ae5870c3..9a873fb55 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -243,7 +243,7 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(std::move(pkt), m_Counter++); + return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++); } auto& back = queue.back(); // pack to nearest N @@ -251,10 +251,10 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(std::move(pkt), m_Counter++); + return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++); } back.protocol = t; - return back.PutBuffer(std::move(pkt), m_Counter++); + return back.PutBuffer(llarp_buffer_t{pkt}, m_Counter++); } bool diff --git a/llarp/quic/endpoint.cpp b/llarp/quic/endpoint.cpp index 4fa35434a..55fc9262c 100644 --- a/llarp/quic/endpoint.cpp +++ b/llarp/quic/endpoint.cpp @@ -185,7 +185,8 @@ namespace llarp::quic std::memcpy(&buf_[header_size], data.data(), data.size()); bstring_view outgoing{buf_.data(), outgoing_len}; - if (service_endpoint.SendToOrQueue(to, outgoing, service::ProtocolType::QUIC)) + if (service_endpoint.SendToOrQueue( + to, llarp_buffer_t{outgoing.data(), outgoing.size()}, service::ProtocolType::QUIC)) { LogTrace("[", to, "]: sent ", buffer_printer{outgoing}); } diff --git a/llarp/service/outbound_context.cpp b/llarp/service/outbound_context.cpp index a67b70fe7..3494bceee 100644 --- a/llarp/service/outbound_context.cpp +++ b/llarp/service/outbound_context.cpp @@ -334,9 +334,10 @@ namespace llarp void OutboundContext::KeepAlive() { - Encrypted<64> tmp; - tmp.Randomize(); - SendPacketToRemote(tmp, ProtocolType::Control); + std::array tmp; + llarp_buffer_t buf{tmp}; + CryptoManager::instance()->randomize(buf); + SendPacketToRemote(buf, ProtocolType::Control); m_LastKeepAliveAt = Now(); } diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 05c4162ea..2373ceadc 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -127,12 +127,6 @@ llarp_buffer_t::copy() const return copy; } -bool -operator==(const llarp_buffer_t& buff, std::string_view data) -{ - return std::string_view{reinterpret_cast(buff.cur), buff.size_left()} == data; -} - llarp::byte_view_t llarp_buffer_t::view() const { diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 5d5899209..1bfef5db4 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -22,6 +22,8 @@ namespace llarp using byte_view_t = std::basic_string_view; } +struct ManagedBuffer; + /// TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a /// fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains /// both start and current position, plus has some value reading/writing methods). @@ -44,29 +46,65 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t llarp_buffer_t(byte_t * b, byte_t * c, size_t s) : base(b), cur(c), sz(s) {} + llarp_buffer_t(const ManagedBuffer&) = delete; + llarp_buffer_t(ManagedBuffer &&) = delete; + + template + static constexpr bool is_basic_byte = sizeof(Byte) == 1 and std::is_trivially_copyable_v; + /// Construct referencing some 1-byte, trivially copyable (e.g. char, unsigned char, byte_t) /// pointer type and a buffer size. template < - typename T, - typename = std::enable_if_t>> - llarp_buffer_t(T * buf, size_t _sz) - : base(reinterpret_cast(const_cast*>(buf))) - , cur(base) - , sz(_sz) + typename Byte, + typename = std::enable_if_t && is_basic_byte>> + llarp_buffer_t(Byte * buf, size_t sz) : base{reinterpret_cast(buf)}, cur{base}, sz{sz} + {} + + /// initialize llarp_buffer_t from vector or array of byte-like values + template < + typename Byte, + typename = std::enable_if_t && is_basic_byte>> + llarp_buffer_t(std::vector & b) : llarp_buffer_t{b.data(), b.size()} {} - /// initialize llarp_buffer_t from containers supporting .data() and .size() + template < + typename Byte, + size_t N, + typename = std::enable_if_t && is_basic_byte>> + llarp_buffer_t(std::array & b) : llarp_buffer_t{b.data(), b.size()} + {} + + // These overloads, const_casting away the const, are not just gross but downright dangerous: + template >> + [[deprecated("dangerous constructor that casts away constness, be very careful")]] llarp_buffer_t( + const Byte* buf, size_t sz) + : llarp_buffer_t{const_cast(buf), sz} + {} + + template >> + [[deprecated("dangerous constructor that casts away constness, be very careful")]] llarp_buffer_t( + const std::vector& b) + : llarp_buffer_t{const_cast(b.data()), b.size()} + {} + + template >> + [[deprecated("dangerous constructor that casts away constness, be very careful")]] llarp_buffer_t( + const std::array& b) + : llarp_buffer_t{const_cast(b.data()), b.size()} + {} + + /// Explicitly construct a llarp_buffer_t from anything with a `.data()` and a `.size()`. Cursed. template < typename T, typename = std::void_t().data() + std::declval().size())>> - llarp_buffer_t(T && t) : llarp_buffer_t{t.data(), t.size()} + explicit llarp_buffer_t(T && t) : llarp_buffer_t{t.data(), t.size()} {} byte_t* begin() { return base; } - byte_t* begin() const + const byte_t* begin() const { return base; } @@ -74,7 +112,7 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t { return base + sz; } - byte_t* end() const + const byte_t* end() const { return base + sz; } @@ -125,10 +163,6 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t llarp_buffer_t(llarp_buffer_t &&) = default; }; - -bool -operator==(const llarp_buffer_t& buff, std::string_view data); - template bool llarp_buffer_t::read_into(OutputIt begin, OutputIt end) From b9c9ee1ca79765b944269c4ad6a4bb172fc23851 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 9 Sep 2022 17:17:36 -0300 Subject: [PATCH 15/71] Fix read problem in linux errno is only set if read returns < 0 and won't be set to 0 if read succeeds, so we were bailing here frequently on successful reads (whenever errno happened to be non-0). --- llarp/vpn/linux.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 5fc06d674..9dcf9b761 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -110,7 +110,7 @@ namespace llarp::vpn std::vector pkt; pkt.resize(net::IPPacket::MaxSize); const auto sz = read(m_fd, pkt.data(), pkt.capacity()); - if (errno) + if (sz < 0) { if (errno == EAGAIN or errno == EWOULDBLOCK) { From f168b7cf720a95ea66ed46540058eaba1bb7a180 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 9 Sep 2022 18:48:38 -0300 Subject: [PATCH 16/71] llarp_buffer_t: rename badly named operator== It didn't do equality, it did "does the remaining space start with the argument" (and so the replacement in the previous commit was broken). This renames it to avoid the confusion and restores to what it was doing on dev. --- llarp/dht/message.cpp | 2 +- llarp/dht/messages/findname.cpp | 4 ++-- llarp/dht/messages/findrouter.cpp | 10 ++++----- llarp/dht/messages/gotintro.cpp | 4 ++-- llarp/dht/messages/gotname.cpp | 6 +++--- llarp/dht/messages/gotrouter.cpp | 8 ++++---- llarp/exit/session.cpp | 6 +++--- llarp/handlers/exit.cpp | 2 +- llarp/messages/dht_immediate.cpp | 4 ++-- llarp/messages/discard.hpp | 2 +- llarp/messages/link_intro.cpp | 12 +++++------ llarp/messages/link_message_parser.cpp | 2 +- llarp/messages/relay_commit.cpp | 6 +++--- llarp/messages/relay_status.cpp | 8 ++++---- llarp/net/address_info.cpp | 12 +++++------ llarp/net/exit_info.cpp | 4 ++-- llarp/net/traffic_policy.cpp | 4 ++-- llarp/router_contact.cpp | 6 +++--- llarp/routing/dht_message.cpp | 6 +++--- llarp/routing/message_parser.cpp | 2 +- llarp/service/intro_set.cpp | 12 +++++------ llarp/service/protocol.cpp | 4 ++-- llarp/util/bencode.hpp | 8 ++++---- llarp/util/buffer.cpp | 18 ----------------- llarp/util/buffer.hpp | 28 +++++++++++++++++++++----- 25 files changed, 90 insertions(+), 90 deletions(-) diff --git a/llarp/dht/message.cpp b/llarp/dht/message.cpp index 7435cc3cb..dfaabfea3 100644 --- a/llarp/dht/message.cpp +++ b/llarp/dht/message.cpp @@ -35,7 +35,7 @@ namespace llarp // first key if (firstKey) { - if (!(*key == "A")) + if (!(key->startswith("A"))) return false; if (!bencode_read_string(buffer, &strbuf)) return false; diff --git a/llarp/dht/messages/findname.cpp b/llarp/dht/messages/findname.cpp index 049cc0724..975ee2a09 100644 --- a/llarp/dht/messages/findname.cpp +++ b/llarp/dht/messages/findname.cpp @@ -26,11 +26,11 @@ namespace llarp::dht bool FindNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) { - if (key == "H") + if (key.startswith("H")) { return NameHash.BDecode(val); } - if (key == "T") + if (key.startswith("T")) { return bencode_read_integer(val, &TxID); } diff --git a/llarp/dht/messages/findrouter.cpp b/llarp/dht/messages/findrouter.cpp index 9ce1f4d40..35978122d 100644 --- a/llarp/dht/messages/findrouter.cpp +++ b/llarp/dht/messages/findrouter.cpp @@ -105,7 +105,7 @@ namespace llarp { llarp_buffer_t strbuf; - if (key == "E") + if (key.startswith("E")) { uint64_t result; if (!bencode_read_integer(val, &result)) @@ -115,7 +115,7 @@ namespace llarp return true; } - if (key == "I") + if (key.startswith("I")) { uint64_t result; if (!bencode_read_integer(val, &result)) @@ -124,7 +124,7 @@ namespace llarp iterative = result != 0; return true; } - if (key == "K") + if (key.startswith("K")) { if (!bencode_read_string(val, &strbuf)) return false; @@ -134,11 +134,11 @@ namespace llarp std::copy(strbuf.base, strbuf.base + targetKey.SIZE, targetKey.begin()); return true; } - if (key == "T") + if (key.startswith("T")) { return bencode_read_integer(val, &txid); } - if (key == "V") + if (key.startswith("V")) { return bencode_read_integer(val, &version); } diff --git a/llarp/dht/messages/gotintro.cpp b/llarp/dht/messages/gotintro.cpp index 0480a521a..f96a9cd4b 100644 --- a/llarp/dht/messages/gotintro.cpp +++ b/llarp/dht/messages/gotintro.cpp @@ -79,11 +79,11 @@ namespace llarp bool GotIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == "I") + if (key.startswith("I")) { return BEncodeReadList(found, buf); } - if (key == "K") + if (key.startswith("K")) { if (closer) // duplicate key? return false; diff --git a/llarp/dht/messages/gotname.cpp b/llarp/dht/messages/gotname.cpp index 2f1720938..78c0f2e74 100644 --- a/llarp/dht/messages/gotname.cpp +++ b/llarp/dht/messages/gotname.cpp @@ -27,7 +27,7 @@ namespace llarp::dht bool GotNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) { - if (key == "D") + if (key.startswith("D")) { llarp_buffer_t str{}; if (not bencode_read_string(val, &str)) @@ -38,11 +38,11 @@ namespace llarp::dht std::copy_n(str.cur, str.sz, result.ciphertext.data()); return true; } - if (key == "N") + if (key.startswith("N")) { return result.nonce.BDecode(val); } - if (key == "T") + if (key.startswith("T")) { return bencode_read_integer(val, &TxID); } diff --git a/llarp/dht/messages/gotrouter.cpp b/llarp/dht/messages/gotrouter.cpp index 9fcb56c31..46bb89303 100644 --- a/llarp/dht/messages/gotrouter.cpp +++ b/llarp/dht/messages/gotrouter.cpp @@ -53,22 +53,22 @@ namespace llarp bool GotRouterMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) { - if (key == "K") + if (key.startswith("K")) { if (closerTarget) // duplicate key? return false; closerTarget = std::make_unique(); return closerTarget->BDecode(val); } - if (key == "N") + if (key.startswith("N")) { return BEncodeReadList(nearKeys, val); } - if (key == "R") + if (key.startswith("R")) { return BEncodeReadList(foundRCs, val); } - if (key == "T") + if (key.startswith("T")) { return bencode_read_integer(val, &txid); } diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 9a873fb55..8c8553922 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -213,7 +213,7 @@ namespace llarp if (m_WritePacket) { - llarp::net::IPPacket pkt{buf.view()}; + llarp::net::IPPacket pkt{buf.view_all()}; if (pkt.empty()) return false; m_LastUse = m_router->Now(); @@ -367,7 +367,7 @@ namespace llarp void SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt{buf.view()}; + net::IPPacket pkt{buf.view_all()}; if (pkt.empty()) return; pkt.ZeroAddresses(); @@ -377,7 +377,7 @@ namespace llarp void ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt{buf.view()}; + net::IPPacket pkt{buf.view_all()}; if (pkt.empty()) return; diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index df508141c..11c9d35c2 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -662,7 +662,7 @@ namespace llarp bool ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from) { - net::IPPacket pkt{buf.view()}; + net::IPPacket pkt{buf.view_all()}; if (pkt.empty()) return false; // rewrite ip diff --git a/llarp/messages/dht_immediate.cpp b/llarp/messages/dht_immediate.cpp index 2ab9f13e3..479932979 100644 --- a/llarp/messages/dht_immediate.cpp +++ b/llarp/messages/dht_immediate.cpp @@ -14,9 +14,9 @@ namespace llarp bool DHTImmediateMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == "m") + if (key.startswith("m")) return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()), buf, msgs); - if (key == "v") + if (key.startswith("v")) { if (!bencode_read_integer(buf, &version)) return false; diff --git a/llarp/messages/discard.hpp b/llarp/messages/discard.hpp index 46d24d429..5a3458a59 100644 --- a/llarp/messages/discard.hpp +++ b/llarp/messages/discard.hpp @@ -39,7 +39,7 @@ namespace llarp bool DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override { - if (key == "a") + if (key.startswith("a")) { llarp_buffer_t strbuf; if (!bencode_read_string(buf, &strbuf)) diff --git a/llarp/messages/link_intro.cpp b/llarp/messages/link_intro.cpp index 88d1af2d1..2ffab6f3c 100644 --- a/llarp/messages/link_intro.cpp +++ b/llarp/messages/link_intro.cpp @@ -11,7 +11,7 @@ namespace llarp bool LinkIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == "a") + if (key.startswith("a")) { llarp_buffer_t strbuf; if (!bencode_read_string(buf, &strbuf)) @@ -20,18 +20,18 @@ namespace llarp return false; return *strbuf.cur == 'i'; } - if (key == "n") + if (key.startswith("n")) { if (N.BDecode(buf)) return true; llarp::LogWarn("failed to decode nonce in LIM"); return false; } - if (key == "p") + if (key.startswith("p")) { return bencode_read_integer(buf, &P); } - if (key == "r") + if (key.startswith("r")) { if (rc.BDecode(buf)) return true; @@ -39,7 +39,7 @@ namespace llarp llarp::DumpBuffer(*buf); return false; } - if (key == "v") + if (key.startswith("v")) { if (!bencode_read_integer(buf, &version)) return false; @@ -52,7 +52,7 @@ namespace llarp llarp::LogDebug("LIM version ", version); return true; } - if (key == "z") + if (key.startswith("z")) { return Z.BDecode(buf); } diff --git a/llarp/messages/link_message_parser.cpp b/llarp/messages/link_message_parser.cpp index 971af9a6d..814b7d0d6 100644 --- a/llarp/messages/link_message_parser.cpp +++ b/llarp/messages/link_message_parser.cpp @@ -45,7 +45,7 @@ namespace llarp if (!key) return false; // we are expecting the first key to be 'a' - if (!(*key == "a")) + if (!key->startswith("a")) { llarp::LogWarn("message has no message type"); return false; diff --git a/llarp/messages/relay_commit.cpp b/llarp/messages/relay_commit.cpp index 74b5ecc35..4cffe912f 100644 --- a/llarp/messages/relay_commit.cpp +++ b/llarp/messages/relay_commit.cpp @@ -22,7 +22,7 @@ namespace llarp bool LR_CommitMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == "c") + if (key.startswith("c")) { /// so we dont put it into the shitty queue pathid.Fill('c'); @@ -131,7 +131,7 @@ namespace llarp return false; if (!BEncodeMaybeReadDictEntry("t", txid, read, *key, buffer)) return false; - if (*key == "u") + if (key->startswith("u")) { nextRC = std::make_unique(); return nextRC->BDecode(buffer); @@ -139,7 +139,7 @@ namespace llarp if (!BEncodeMaybeVerifyVersion( "v", version, llarp::constants::proto_version, read, *key, buffer)) return false; - if (*key == "w") + if (key->startswith("w")) { // check for duplicate if (work) diff --git a/llarp/messages/relay_status.cpp b/llarp/messages/relay_status.cpp index 8d0732059..0f5fdc607 100644 --- a/llarp/messages/relay_status.cpp +++ b/llarp/messages/relay_status.cpp @@ -60,25 +60,25 @@ namespace llarp LR_StatusMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { bool read = false; - if (key == "c") + if (key.startswith("c")) { return BEncodeReadArray(frames, buf); } - if (key == "p") + if (key.startswith("p")) { if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf)) { return false; } } - else if (key == "s") + else if (key.startswith("s")) { if (!BEncodeMaybeReadDictInt("s", status, read, key, buf)) { return false; } } - else if (key == "v") + else if (key.startswith("v")) { if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf)) { diff --git a/llarp/net/address_info.cpp b/llarp/net/address_info.cpp index 70788b258..fd5424632 100644 --- a/llarp/net/address_info.cpp +++ b/llarp/net/address_info.cpp @@ -41,7 +41,7 @@ namespace llarp llarp_buffer_t strbuf; // rank - if (key == "c") + if (key.startswith("c")) { if (!bencode_read_integer(buf, &i)) return false; @@ -54,7 +54,7 @@ namespace llarp } // dialect - if (key == "d") + if (key.startswith("d")) { if (!bencode_read_string(buf, &strbuf)) return false; @@ -67,13 +67,13 @@ namespace llarp } // encryption public key - if (key == "e") + if (key.startswith("e")) { return pubkey.BDecode(buf); } // ip address - if (key == "i") + if (key.startswith("i")) { if (!bencode_read_string(buf, &strbuf)) return false; @@ -87,7 +87,7 @@ namespace llarp } // port - if (key == "p") + if (key.startswith("p")) { if (!bencode_read_integer(buf, &i)) return false; @@ -100,7 +100,7 @@ namespace llarp } // version - if (key == "v") + if (key.startswith("v")) { if (!bencode_read_integer(buf, &i)) return false; diff --git a/llarp/net/exit_info.cpp b/llarp/net/exit_info.cpp index 2af7c707f..ef91471d3 100644 --- a/llarp/net/exit_info.cpp +++ b/llarp/net/exit_info.cpp @@ -69,7 +69,7 @@ namespace llarp return false; if (!BEncodeMaybeReadDictInt("v", version, read, k, buf)) return false; - if (k == "a") + if (k.startswith("a")) { in6_addr tmp; if (not bdecode_ip_string(buf, tmp)) @@ -79,7 +79,7 @@ namespace llarp ipAddress = IpAddress(addr); return true; } - if (k == "b") + if (k.startswith("b")) { in6_addr tmp; if (not bdecode_ip_string(buf, tmp)) diff --git a/llarp/net/traffic_policy.cpp b/llarp/net/traffic_policy.cpp index f8d00d80d..e94cb94a7 100644 --- a/llarp/net/traffic_policy.cpp +++ b/llarp/net/traffic_policy.cpp @@ -167,11 +167,11 @@ namespace llarp::net [&](llarp_buffer_t* buffer, llarp_buffer_t* key) -> bool { if (key == nullptr) return true; - if (*key == "p") + if (key->startswith("p")) { return BEncodeReadSet(protocols, buffer); } - if (*key == "r") + if (key->startswith("r")) { return BEncodeReadSet(ranges, buffer); } diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index f565255d2..e06c8101f 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -344,7 +344,7 @@ namespace llarp if (!BEncodeMaybeReadDictEntry("k", pubkey, read, key, buf)) return false; - if (key == "r") + if (key.startswith("r")) { RouterVersion r; if (not r.BDecode(buf)) @@ -353,7 +353,7 @@ namespace llarp return true; } - if (key == "n") + if (key.startswith("n")) { llarp_buffer_t strbuf; if (!bencode_read_string(buf, &strbuf)) @@ -381,7 +381,7 @@ namespace llarp if (!BEncodeMaybeReadDictInt("v", version, read, key, buf)) return false; - if (key == "x" and serializeExit) + if (key.startswith("x") and serializeExit) { return bencode_discard(buf); } diff --git a/llarp/routing/dht_message.cpp b/llarp/routing/dht_message.cpp index 510754049..72ffe6bb4 100644 --- a/llarp/routing/dht_message.cpp +++ b/llarp/routing/dht_message.cpp @@ -10,17 +10,17 @@ namespace llarp bool DHTMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) { - if (key == "M") + if (key.startswith("M")) { llarp::dht::Key_t fromKey; fromKey.Zero(); return llarp::dht::DecodeMesssageList(fromKey, val, M, true); } - if (key == "S") + if (key.startswith("S")) { return bencode_read_integer(val, &S); } - if (key == "V") + if (key.startswith("V")) { return bencode_read_integer(val, &V); } diff --git a/llarp/routing/message_parser.cpp b/llarp/routing/message_parser.cpp index 68e015dc9..16f77ae87 100644 --- a/llarp/routing/message_parser.cpp +++ b/llarp/routing/message_parser.cpp @@ -48,7 +48,7 @@ namespace llarp if (firstKey) { llarp_buffer_t strbuf; - if (!(*key == "A")) + if (!(key->startswith("A"))) return false; if (!bencode_read_string(buffer, &strbuf)) return false; diff --git a/llarp/service/intro_set.cpp b/llarp/service/intro_set.cpp index 5141256db..07c39497f 100644 --- a/llarp/service/intro_set.cpp +++ b/llarp/service/intro_set.cpp @@ -38,7 +38,7 @@ namespace llarp::service EncryptedIntroSet::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { bool read = false; - if (key == "x") + if (key.startswith("x")) { llarp_buffer_t strbuf; if (not bencode_read_string(buf, &strbuf)) @@ -178,7 +178,7 @@ namespace llarp::service if (!BEncodeMaybeReadDictEntry("a", addressKeys, read, key, buf)) return false; - if (key == "e") + if (key.startswith("e")) { net::TrafficPolicy policy; if (not policy.BDecode(buf)) @@ -187,7 +187,7 @@ namespace llarp::service return true; } - if (key == "i") + if (key.startswith("i")) { return BEncodeReadList(intros, buf); } @@ -197,7 +197,7 @@ namespace llarp::service if (!BEncodeMaybeReadDictEntry("n", topic, read, key, buf)) return false; - if (key == "p") + if (key.startswith("p")) { return bencode_read_list( [&](llarp_buffer_t* buf, bool more) { @@ -213,12 +213,12 @@ namespace llarp::service buf); } - if (key == "r") + if (key.startswith("r")) { return BEncodeReadSet(ownedRanges, buf); } - if (key == "s") + if (key.startswith("s")) { byte_t* begin = buf->cur; if (not bencode_discard(buf)) diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp index 0b9939c59..b838c44cd 100644 --- a/llarp/service/protocol.cpp +++ b/llarp/service/protocol.cpp @@ -43,7 +43,7 @@ namespace llarp bool read = false; if (!BEncodeMaybeReadDictInt("a", proto, read, k, buf)) return false; - if (k == "d") + if (k.startswith("d")) { llarp_buffer_t strbuf; if (!bencode_read_string(buf, &strbuf)) @@ -168,7 +168,7 @@ namespace llarp ProtocolFrame::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) { bool read = false; - if (key == "A") + if (key.startswith("A")) { llarp_buffer_t strbuf; if (!bencode_read_string(val, &strbuf)) diff --git a/llarp/util/bencode.hpp b/llarp/util/bencode.hpp index c02453f58..24f6a520b 100644 --- a/llarp/util/bencode.hpp +++ b/llarp/util/bencode.hpp @@ -55,7 +55,7 @@ namespace llarp BEncodeMaybeReadDictList( const char* k, List_t& item, bool& read, const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == k) + if (key.startswith(k)) { if (!BEncodeReadList(item, buf)) { @@ -71,7 +71,7 @@ namespace llarp BEncodeMaybeReadDictEntry( const char* k, Item_t& item, bool& read, const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == k) + if (key.startswith(k)) { if (!item.BDecode(buf)) { @@ -89,7 +89,7 @@ namespace llarp BEncodeMaybeReadDictInt( const char* k, Int_t& i, bool& read, const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == k) + if (key.startswith(k)) { uint64_t read_i; if (!bencode_read_integer(buf, &read_i)) @@ -116,7 +116,7 @@ namespace llarp const llarp_buffer_t& key, llarp_buffer_t* buf) { - if (key == k) + if (key.startswith(k)) { if (!bencode_read_integer(buf, &item)) return false; diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 2373ceadc..bc495dacd 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -4,18 +4,6 @@ #include #include -size_t -llarp_buffer_t::size_left() const -{ - size_t diff = cur - base; - if (diff > sz) - { - return 0; - } - - return sz - diff; -} - bool llarp_buffer_t::writef(const char* fmt, ...) { @@ -127,12 +115,6 @@ llarp_buffer_t::copy() const return copy; } -llarp::byte_view_t -llarp_buffer_t::view() const -{ - return {base, sz}; - -} namespace llarp { diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 1bfef5db4..343b2f466 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -117,7 +117,14 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t return base + sz; } - size_t size_left() const; + size_t size_left() const + { + size_t diff = cur - base; + assert(diff <= sz); + if (diff > sz) + return 0; + return sz - diff; + } template bool read_into(OutputIt begin, OutputIt end); @@ -149,12 +156,23 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t /// make a copy of this buffer std::vector copy() const; - /// get a read only view over the entire region - llarp::byte_view_t view() const; + /// get a read-only view over the entire region + llarp::byte_view_t view_all() const + { + return {base, sz}; + } + + /// get a read-only view over the remaining/unused region + llarp::byte_view_t view_remaining() const + { + return {cur, size_left()}; + } - bool operator==(std::string_view data) const + /// Part of the curse. Returns true if the remaining buffer space starts with the given string view. + bool startswith(std::string_view prefix_str) const { - return std::string_view{reinterpret_cast(base), sz} == data; + llarp::byte_view_t prefix{reinterpret_cast(prefix_str.data()), prefix_str.size()}; + return view_remaining().substr(0, prefix.size()) == prefix; } private: From cfd80f6a17525e03a4ec933dd7beb38c9227f6d0 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 9 Sep 2022 21:51:34 -0300 Subject: [PATCH 17/71] Fix buffer_printer overflow --- llarp/util/logging/buffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/util/logging/buffer.cpp b/llarp/util/logging/buffer.cpp index a3dfd347c..fe055b9f7 100644 --- a/llarp/util/logging/buffer.cpp +++ b/llarp/util/logging/buffer.cpp @@ -22,7 +22,7 @@ namespace llarp auto k = i + j; if (j % 4 == 0) out.push_back(' '); - if (j >= stop) + if (k >= stop) out.append(" "); else fmt::format_to(ins, "{:02x}", std::to_integer(b[k])); From beb07bf46fbc0e3d7b91eb16320ddaa55ffc998c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 9 Sep 2022 21:33:47 -0400 Subject: [PATCH 18/71] small optimizations and fixes - Ensure ip header struct is packed - Use fmt - add missing header --- llarp/handlers/tun.cpp | 1 + llarp/net/ip_packet.cpp | 16 +++++++++------- llarp/net/ip_packet.hpp | 2 ++ llarp/net/sock_addr.cpp | 5 +---- llarp/util/buffer.cpp | 1 - llarp/util/buffer.hpp | 6 ++++-- llarp/win32/windivert.cpp | 1 + 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index cf0ae5dc0..3ace345ed 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/llarp/net/ip_packet.cpp b/llarp/net/ip_packet.cpp index 9f822b818..3bda060d0 100644 --- a/llarp/net/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -126,21 +126,22 @@ namespace llarp::net SockAddr IPPacket::src() const { - auto port = SrcPort(); + const auto port = SrcPort().value_or(net::port_t{}); + if (IsV4()) - return SockAddr{ToNet(srcv4()), *port}; + return SockAddr{ToNet(srcv4()), port}; else - return SockAddr{ToNet(srcv6()), *port}; + return SockAddr{ToNet(srcv6()), port}; } SockAddr IPPacket::dst() const { - auto port = DstPort(); + auto port = *DstPort(); if (IsV4()) - return SockAddr{ToNet(dstv4()), *port}; + return SockAddr{ToNet(dstv4()), port}; else - return SockAddr{ToNet(dstv6()), *port}; + return SockAddr{ToNet(dstv6()), port}; } IPPacket::IPPacket(std::vector&& stolen) : _buf{stolen} @@ -171,7 +172,8 @@ namespace llarp::net std::optional IPPacket::SrcPort() const { - switch (IPProtocol{Header()->protocol}) + IPProtocol proto{Header()->protocol}; + switch (proto) { case IPProtocol::TCP: case IPProtocol::UDP: diff --git a/llarp/net/ip_packet.hpp b/llarp/net/ip_packet.hpp index 14418d05f..5d297ff92 100644 --- a/llarp/net/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -10,6 +10,7 @@ namespace llarp::net { +#pragma pack(push, 1) template struct ip_header_le { @@ -41,6 +42,7 @@ namespace llarp::net uint32_t saddr; uint32_t daddr; }; +#pragma pack(pop) using ip_header = ip_header_le; diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index 2e9595bc8..f42feb928 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -288,10 +288,7 @@ namespace llarp // TODO: review if (isEmpty()) return ""; - std::string str = hostString(); - str.append(1, ':'); - str.append(std::to_string(getPort())); - return str; + return fmt::format("{}:{}", hostString(), port()); } std::string diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index bc495dacd..7dc3d747c 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -115,7 +115,6 @@ llarp_buffer_t::copy() const return copy; } - namespace llarp { std::vector diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 343b2f466..abb0cffa0 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -168,10 +168,12 @@ struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t return {cur, size_left()}; } - /// Part of the curse. Returns true if the remaining buffer space starts with the given string view. + /// Part of the curse. Returns true if the remaining buffer space starts with the given string + /// view. bool startswith(std::string_view prefix_str) const { - llarp::byte_view_t prefix{reinterpret_cast(prefix_str.data()), prefix_str.size()}; + llarp::byte_view_t prefix{ + reinterpret_cast(prefix_str.data()), prefix_str.size()}; return view_remaining().substr(0, prefix.size()) == prefix; } diff --git a/llarp/win32/windivert.cpp b/llarp/win32/windivert.cpp index 359d0f7b2..34bea2956 100644 --- a/llarp/win32/windivert.cpp +++ b/llarp/win32/windivert.cpp @@ -5,6 +5,7 @@ #include "handle.hpp" #include #include +#include #include extern "C" { From dd16158081777c0402412912d7da71cad202982b Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 9 Sep 2022 23:44:21 -0300 Subject: [PATCH 19/71] DNS: default to 127.3.2.1 & high port on Linux Lots of tools struggle with non-default DNS port, so keep a listener on 127.3.2.1:53 (by default). This required various changes to the config handling to hold a vector (instead of an optional) of defaults and values, and now allows passing in an array of defaults instead of just a single default. --- llarp/config/config.cpp | 19 +++-- llarp/config/definition.cpp | 30 +++---- llarp/config/definition.hpp | 159 +++++++++++++++++++----------------- 3 files changed, 110 insertions(+), 98 deletions(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 195b96f4d..9826e98f6 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -776,17 +776,18 @@ 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 std::array DefaultDNSBind{ #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"}; + // when we have systemd support add a random high port on loopback as well + // see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282 + Default{"127.0.0.1:0"}, #endif + Default{"127.3.2.1:53"}, #else - constexpr Default DefaultDNSBind{"127.0.0.1:53"}; + Default{"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:53"}; @@ -829,9 +830,10 @@ namespace llarp "query-bind", #ifdef __APPLE__ Default{"127.0.0.1:1253"}, -#endif -#ifdef _WIN32 +#elif defined(_WIN32) Default{"0.0.0.0:0"}, +#else + Hidden, #endif Comment{ "Address to bind to for sending upstream DNS requests.", @@ -842,6 +844,7 @@ namespace llarp "dns", "bind", DefaultDNSBind, + MultiValue, Comment{ "Address to bind to for handling DNS requests.", }, diff --git a/llarp/config/definition.cpp b/llarp/config/definition.cpp index ccf885c15..aa199ebea 100644 --- a/llarp/config/definition.cpp +++ b/llarp/config/definition.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -176,12 +175,14 @@ namespace llarp std::string ConfigDefinition::generateINIConfig(bool useValues) { - std::ostringstream oss; + std::string ini; + auto ini_append = std::back_inserter(ini); int sectionsVisited = 0; visitSections([&](const std::string& section, const DefinitionMap&) { - std::ostringstream sect_out; + std::string sect_str; + auto sect_append = std::back_inserter(sect_str); visitDefinitions(section, [&](const std::string& name, const OptionDefinition_ptr& def) { bool has_comment = false; @@ -190,44 +191,43 @@ namespace llarp // (i.e. those handled by UndeclaredValueHandler's) for (const std::string& comment : m_definitionComments[section][name]) { - sect_out << "\n# " << comment; + fmt::format_to(sect_append, "\n# {}", comment); has_comment = true; } if (useValues and def->getNumberFound() > 0) { - sect_out << "\n" << name << "=" << def->valueAsString(false) << "\n"; + for (const auto& val : def->valuesAsString()) + fmt::format_to(sect_append, "\n{}={}\n", name, val); } else if (not(def->hidden and not has_comment)) { - sect_out << "\n"; - if (not def->required) - sect_out << "#"; - sect_out << name << "=" << def->defaultValueAsString() << "\n"; + for (const auto& val : def->defaultValuesAsString()) + fmt::format_to(sect_append, "\n{}{}={}\n", def->required ? "" : "#", name, val); } }); - auto sect_str = sect_out.str(); if (sect_str.empty()) return; // Skip sections with no options if (sectionsVisited > 0) - oss << "\n\n"; + ini += "\n\n"; - oss << "[" << section << "]\n"; + fmt::format_to(ini_append, "[{}]\n", section); // TODO: this will create empty objects as a side effect of map's operator[] // TODO: this also won't handle sections which have no definition for (const std::string& comment : m_sectionComments[section]) { - oss << "# " << comment << "\n"; + fmt::format_to(ini_append, "# {}\n", comment); } - oss << "\n" << sect_str; + ini += "\n"; + ini += sect_str; sectionsVisited++; }); - return oss.str(); + return ini; } const OptionDefinition_ptr& diff --git a/llarp/config/definition.hpp b/llarp/config/definition.hpp index b3f9cd0ab..8f9225c28 100644 --- a/llarp/config/definition.hpp +++ b/llarp/config/definition.hpp @@ -97,12 +97,19 @@ namespace llarp template constexpr bool is_default = is_default>; + template + constexpr bool is_default_array = false; + template + constexpr bool is_default_array, N>> = true; + template + constexpr bool is_default_array = is_default_array>; + template constexpr bool is_option = std::is_base_of_v< option_flag, remove_cvref_t< - Option>> or std::is_same_v or is_default