From 253d22db4f631c8060100d814037073127c39da1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 23 May 2022 12:45:06 -0400 Subject: [PATCH] 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