]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-manager.c
sd-netlink: add functions which manage sd_netlink_slot object
[thirdparty/systemd.git] / src / resolve / resolved-manager.c
index 4292383d21d16dcdc72075ff6ed190cf541bf57c..05244499c0011aa1603861161a58a08963a8cc1f 100644 (file)
@@ -1,25 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  This file is part of systemd.
-
-  Copyright 2014 Tom Gundersen <teg@jklm.no>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
 
 #include <netinet/in.h>
 #include <poll.h>
+#include <stdio_ext.h>
 #include <sys/ioctl.h>
 
 #if HAVE_LIBIDN2
@@ -41,6 +24,7 @@
 #include "random-util.h"
 #include "resolved-bus.h"
 #include "resolved-conf.h"
+#include "resolved-dnssd.h"
 #include "resolved-dns-stub.h"
 #include "resolved-etc-hosts.h"
 #include "resolved-llmnr.h"
@@ -216,19 +200,19 @@ static int manager_rtnl_listen(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m);
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, manager_process_link, NULL, m);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m);
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, manager_process_link, NULL, m);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m);
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, manager_process_address, NULL, m);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m);
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELADDR, manager_process_address, NULL, m);
         if (r < 0)
                 return r;
 
@@ -394,11 +378,8 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
         if (r < 0)
                 return log_error_errno(r, "Failed to determine mDNS hostname: %m");
 
-        *llmnr_hostname = n;
-        n = NULL;
-
-        *full_hostname = h;
-        h = NULL;
+        *llmnr_hostname = TAKE_PTR(n);
+        *full_hostname = TAKE_PTR(h);
 
         return 0;
 }
@@ -443,11 +424,8 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname,
         if (!h)
                 return log_oom();
 
-        *llmnr_hostname = n;
-        n = NULL;
-
-        *mdns_hostname = m;
-        m = NULL;
+        *llmnr_hostname = TAKE_PTR(n);
+        *mdns_hostname = TAKE_PTR(m);
 
         *full_hostname = h;
 
@@ -486,7 +464,8 @@ static int manager_watch_hostname(Manager *m) {
 
         assert(m);
 
-        m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY);
+        m->hostname_fd = open("/proc/sys/kernel/hostname",
+                              O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
         if (m->hostname_fd < 0) {
                 log_warning_errno(errno, "Failed to watch hostname: %m");
                 return 0;
@@ -534,6 +513,8 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si
         if (!f)
                 return log_oom();
 
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
         LIST_FOREACH(scopes, scope, m->dns_scopes)
                 dns_scope_dump(scope, f);
 
@@ -581,24 +562,33 @@ int manager_new(Manager **ret) {
 
         assert(ret);
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
-        m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
-        m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
-        m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
-        m->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1;
-        m->hostname_fd = -1;
-
-        m->llmnr_support = RESOLVE_SUPPORT_YES;
-        m->mdns_support = RESOLVE_SUPPORT_YES;
-        m->dnssec_mode = DEFAULT_DNSSEC_MODE;
-        m->enable_cache = true;
-        m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
-        m->read_resolv_conf = true;
-        m->need_builtin_fallbacks = true;
-        m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
+        *m = (Manager) {
+                .llmnr_ipv4_udp_fd = -1,
+                .llmnr_ipv6_udp_fd = -1,
+                .llmnr_ipv4_tcp_fd = -1,
+                .llmnr_ipv6_tcp_fd = -1,
+                .mdns_ipv4_fd = -1,
+                .mdns_ipv6_fd = -1,
+                .dns_stub_udp_fd = -1,
+                .dns_stub_tcp_fd = -1,
+                .hostname_fd = -1,
+
+                .llmnr_support = RESOLVE_SUPPORT_YES,
+                .mdns_support = RESOLVE_SUPPORT_YES,
+                .dnssec_mode = DEFAULT_DNSSEC_MODE,
+                .dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE,
+                .enable_cache = true,
+                .dns_stub_listener_mode = DNS_STUB_LISTENER_UDP,
+                .read_resolv_conf = true,
+                .need_builtin_fallbacks = true,
+                .etc_hosts_last = USEC_INFINITY,
+                .etc_hosts_mtime = USEC_INFINITY,
+                .read_etc_hosts = true,
+        };
 
         r = dns_trust_anchor_load(&m->trust_anchor);
         if (r < 0)
@@ -612,15 +602,19 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        sd_event_add_signal(m->event, NULL, SIGTERM, NULL,  NULL);
-        sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+        (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL,  NULL);
+        (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
 
-        sd_event_set_watchdog(m->event, true);
+        (void) sd_event_set_watchdog(m->event, true);
 
         r = manager_watch_hostname(m);
         if (r < 0)
                 return r;
 
+        r = dnssd_load(m);
+        if (r < 0)
+                log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
+
         r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
         if (r < 0)
                 return r;
@@ -643,8 +637,7 @@ int manager_new(Manager **ret) {
 
         manager_cleanup_saved_user(m);
 
-        *ret = m;
-        m = NULL;
+        *ret = TAKE_PTR(m);
 
         return 0;
 }
@@ -663,6 +656,7 @@ int manager_start(Manager *m) {
 
 Manager *manager_free(Manager *m) {
         Link *l;
+        DnssdService *s;
 
         if (!m)
                 return NULL;
@@ -697,8 +691,6 @@ Manager *manager_free(Manager *m) {
         manager_mdns_stop(m);
         manager_dns_stub_stop(m);
 
-        sd_bus_slot_unref(m->prepare_for_sleep_slot);
-        sd_event_source_unref(m->bus_retry_event_source);
         sd_bus_unref(m->bus);
 
         sd_event_source_unref(m->sigusr1_event_source);
@@ -719,6 +711,10 @@ Manager *manager_free(Manager *m) {
         free(m->llmnr_hostname);
         free(m->mdns_hostname);
 
+        while ((s = hashmap_first(m->dnssd_services)))
+               dnssd_service_free(s);
+        hashmap_free(m->dnssd_services);
+
         dns_trust_anchor_flush(&m->trust_anchor);
         manager_etc_hosts_flush(m);
 
@@ -734,9 +730,16 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
                                + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
         } control;
         union sockaddr_union sa;
-        struct msghdr mh = {};
-        struct cmsghdr *cmsg;
         struct iovec iov;
+        struct msghdr mh = {
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
         ssize_t ms, l;
         int r;
 
@@ -752,15 +755,10 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         if (r < 0)
                 return r;
 
-        iov.iov_base = DNS_PACKET_DATA(p);
-        iov.iov_len = p->allocated;
-
-        mh.msg_name = &sa.sa;
-        mh.msg_namelen = sizeof(sa);
-        mh.msg_iov = &iov;
-        mh.msg_iovlen = 1;
-        mh.msg_control = &control;
-        mh.msg_controllen = sizeof(control);
+        iov = (struct iovec) {
+                .iov_base = DNS_PACKET_DATA(p),
+                iov.iov_len = p->allocated,
+        };
 
         l = recvmsg(fd, &mh, 0);
         if (l == 0)
@@ -847,8 +845,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
                         p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
         }
 
-        *ret = p;
-        p = NULL;
+        *ret = TAKE_PTR(p);
 
         return 1;
 }
@@ -921,15 +918,18 @@ static int manager_ipv4_send(
                 uint16_t port,
                 const struct in_addr *source,
                 DnsPacket *p) {
-        union sockaddr_union sa = {
-                .in.sin_family = AF_INET,
-        };
         union {
                 struct cmsghdr header; /* For alignment */
                 uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
-        } control;
-        struct msghdr mh = {};
+        } control = {};
+        union sockaddr_union sa;
         struct iovec iov;
+        struct msghdr mh = {
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa.in),
+        };
 
         assert(m);
         assert(fd >= 0);
@@ -937,23 +937,21 @@ static int manager_ipv4_send(
         assert(port > 0);
         assert(p);
 
-        iov.iov_base = DNS_PACKET_DATA(p);
-        iov.iov_len = p->size;
-
-        sa.in.sin_addr = *destination;
-        sa.in.sin_port = htobe16(port),
+        iov = (struct iovec) {
+                .iov_base = DNS_PACKET_DATA(p),
+                .iov_len = p->size,
+        };
 
-        mh.msg_iov = &iov;
-        mh.msg_iovlen = 1;
-        mh.msg_name = &sa.sa;
-        mh.msg_namelen = sizeof(sa.in);
+        sa = (union sockaddr_union) {
+                .in.sin_family = AF_INET,
+                .in.sin_addr = *destination,
+                .in.sin_port = htobe16(port),
+        };
 
         if (ifindex > 0) {
                 struct cmsghdr *cmsg;
                 struct in_pktinfo *pi;
 
-                zero(control);
-
                 mh.msg_control = &control;
                 mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
 
@@ -981,15 +979,18 @@ static int manager_ipv6_send(
                 const struct in6_addr *source,
                 DnsPacket *p) {
 
-        union sockaddr_union sa = {
-                .in6.sin6_family = AF_INET6,
-        };
         union {
                 struct cmsghdr header; /* For alignment */
                 uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-        } control;
-        struct msghdr mh = {};
+        } control = {};
+        union sockaddr_union sa;
         struct iovec iov;
+        struct msghdr mh = {
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa.in6),
+        };
 
         assert(m);
         assert(fd >= 0);
@@ -997,24 +998,22 @@ static int manager_ipv6_send(
         assert(port > 0);
         assert(p);
 
-        iov.iov_base = DNS_PACKET_DATA(p);
-        iov.iov_len = p->size;
-
-        sa.in6.sin6_addr = *destination;
-        sa.in6.sin6_port = htobe16(port),
-        sa.in6.sin6_scope_id = ifindex;
+        iov = (struct iovec) {
+                .iov_base = DNS_PACKET_DATA(p),
+                .iov_len = p->size,
+        };
 
-        mh.msg_iov = &iov;
-        mh.msg_iovlen = 1;
-        mh.msg_name = &sa.sa;
-        mh.msg_namelen = sizeof(sa.in6);
+        sa = (union sockaddr_union) {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_addr = *destination,
+                .in6.sin6_port = htobe16(port),
+                .in6.sin6_scope_id = ifindex,
+        };
 
         if (ifindex > 0) {
                 struct cmsghdr *cmsg;
                 struct in6_pktinfo *pi;
 
-                zero(control);
-
                 mh.msg_control = &control;
                 mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
 
@@ -1052,9 +1051,9 @@ int manager_send(
         log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
 
         if (family == AF_INET)
-                return manager_ipv4_send(m, fd, ifindex, &destination->in, port, &source->in, p);
+                return manager_ipv4_send(m, fd, ifindex, &destination->in, port, source ? &source->in : NULL, p);
         if (family == AF_INET6)
-                return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p);
+                return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, source ? &source->in6 : NULL, p);
 
         return -EAFNOSUPPORT;
 }
@@ -1094,6 +1093,7 @@ int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_a
 void manager_refresh_rrs(Manager *m) {
         Iterator i;
         Link *l;
+        DnssdService *s;
 
         assert(m);
 
@@ -1102,25 +1102,27 @@ void manager_refresh_rrs(Manager *m) {
         m->mdns_host_ipv4_key = dns_resource_key_unref(m->mdns_host_ipv4_key);
         m->mdns_host_ipv6_key = dns_resource_key_unref(m->mdns_host_ipv6_key);
 
+        if (m->mdns_support == RESOLVE_SUPPORT_YES)
+                HASHMAP_FOREACH(s, m->dnssd_services, i)
+                        if (dnssd_update_rrs(s) < 0)
+                                log_warning("Failed to refresh DNS-SD service '%s'", s->name);
+
         HASHMAP_FOREACH(l, m->links, i) {
                 link_add_rrs(l, true);
                 link_add_rrs(l, false);
         }
 }
 
-int manager_next_hostname(Manager *m) {
+static int manager_next_random_name(const char *old, char **ret_new) {
         const char *p;
         uint64_t u, a;
-        char *h, *k;
-        int r;
-
-        assert(m);
+        char *n;
 
-        p = strchr(m->llmnr_hostname, 0);
+        p = strchr(old, 0);
         assert(p);
 
-        while (p > m->llmnr_hostname) {
-                if (!strchr("0123456789", p[-1]))
+        while (p > old) {
+                if (!strchr(DIGITS, p[-1]))
                         break;
 
                 p--;
@@ -1139,22 +1141,32 @@ int manager_next_hostname(Manager *m) {
         random_bytes(&a, sizeof(a));
         u += 1 + a % 10;
 
-        if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0)
+        if (asprintf(&n, "%.*s%" PRIu64, (int) (p - old), old, u) < 0)
                 return -ENOMEM;
 
+        *ret_new = n;
+
+        return 0;
+}
+
+int manager_next_hostname(Manager *m) {
+        _cleanup_free_ char *h = NULL, *k = NULL;
+        int r;
+
+        assert(m);
+
+        r = manager_next_random_name(m->llmnr_hostname, &h);
+        if (r < 0)
+                return r;
+
         r = dns_name_concat(h, "local", &k);
-        if (r < 0) {
-                free(h);
+        if (r < 0)
                 return r;
-        }
 
         log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h);
 
-        free(m->llmnr_hostname);
-        m->llmnr_hostname = h;
-
-        free(m->mdns_hostname);
-        m->mdns_hostname = k;
+        free_and_replace(m->llmnr_hostname, h);
+        free_and_replace(m->mdns_hostname, k);
 
         manager_refresh_rrs(m);
 
@@ -1379,12 +1391,21 @@ bool manager_dnssec_supported(Manager *m) {
         return true;
 }
 
+DnsOverTlsMode manager_get_dns_over_tls_mode(Manager *m) {
+        assert(m);
+
+        if (m->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+                return m->dns_over_tls_mode;
+
+        return DNS_OVER_TLS_NO;
+}
+
 void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
 
         assert(verdict >= 0);
         assert(verdict < _DNSSEC_VERDICT_MAX);
 
-        if (log_get_max_level() >= LOG_DEBUG) {
+        if (DEBUG_LOGGING) {
                 char s[DNS_RESOURCE_KEY_STRING_MAX];
 
                 log_debug("Found verdict for lookup %s: %s",
@@ -1488,3 +1509,36 @@ void manager_cleanup_saved_user(Manager *m) {
                 (void) unlink(p);
         }
 }
+
+bool manager_next_dnssd_names(Manager *m) {
+        Iterator i;
+        DnssdService *s;
+        bool tried = false;
+        int r;
+
+        assert(m);
+
+        HASHMAP_FOREACH(s, m->dnssd_services, i) {
+                _cleanup_free_ char * new_name = NULL;
+
+                if (!s->withdrawn)
+                        continue;
+
+                r = manager_next_random_name(s->name_template, &new_name);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to get new name for service '%s': %m", s->name);
+                        continue;
+                }
+
+                free_and_replace(s->name_template, new_name);
+
+                s->withdrawn = false;
+
+                tried = true;
+        }
+
+        if (tried)
+                manager_refresh_rrs(m);
+
+        return tried;
+}