mirror of https://github.com/oxen-io/lokinet
Merge pull request #1615 from jagerman/lokinet-systemd-resolved
Lokinet systemd-resolved supportpull/1617/head
commit
8a3e3a0c39
@ -1,3 +0,0 @@
|
|||||||
[Resolve]
|
|
||||||
DNS=127.3.2.1
|
|
||||||
Domains=~loki ~snode
|
|
@ -0,0 +1,4 @@
|
|||||||
|
[Allow lokinet to set DNS settings]
|
||||||
|
Identity=unix-user:_lokinet
|
||||||
|
Action=org.freedesktop.resolve1.set-dns-servers;org.freedesktop.resolve1.set-domains
|
||||||
|
ResultAny=yes
|
@ -0,0 +1,9 @@
|
|||||||
|
/* Allow lokinet to set DNS settings */
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if ((action.id == "org.freedesktop.resolve1.set-dns-servers" ||
|
||||||
|
action.id == "org.freedesktop.resolve1.set-domains") &&
|
||||||
|
subject.user == "_lokinet") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,171 @@
|
|||||||
|
#include "systemd_resolved.hpp"
|
||||||
|
#include <llarp/util/logging/logger.hpp>
|
||||||
|
|
||||||
|
#ifndef WITH_SYSTEMD
|
||||||
|
|
||||||
|
namespace llarp
|
||||||
|
{
|
||||||
|
bool
|
||||||
|
systemd_resolved_set_dns(std::string, llarp::SockAddr, bool)
|
||||||
|
{
|
||||||
|
LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace llarp
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace llarp
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename... T>
|
||||||
|
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
|
||||||
|
{
|
||||||
|
void
|
||||||
|
operator()(sd_bus* ptr) const
|
||||||
|
{
|
||||||
|
sd_bus_unref(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool
|
||||||
|
systemd_resolved_set_dns(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<sd_bus, sd_bus_deleter> 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.
|
||||||
|
if (dns.isIPv6())
|
||||||
|
{
|
||||||
|
auto ipv6 = dns.getIPv6();
|
||||||
|
static_assert(sizeof(ipv6) == 16);
|
||||||
|
auto* a = reinterpret_cast<const uint8_t*>(&ipv6);
|
||||||
|
resolved_call(
|
||||||
|
bus,
|
||||||
|
"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<const uint8_t*>(&ipv4);
|
||||||
|
resolved_call(
|
||||||
|
bus,
|
||||||
|
"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)
|
||||||
|
resolved_call(
|
||||||
|
bus,
|
||||||
|
"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).
|
||||||
|
resolved_call(
|
||||||
|
bus,
|
||||||
|
"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
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
LogWarn("Failed to set DNS via systemd-resolved: ", e.what());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace llarp
|
||||||
|
|
||||||
|
#endif // WITH_SYSTEMD
|
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <llarp/net/sock_addr.hpp>
|
||||||
|
|
||||||
|
namespace llarp
|
||||||
|
{
|
||||||
|
/// 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).
|
||||||
|
///
|
||||||
|
/// \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
|
||||||
|
systemd_resolved_set_dns(std::string if_name, llarp::SockAddr dns, bool global);
|
||||||
|
} // namespace llarp
|
Loading…
Reference in New Issue