]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-stub.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-stub.c
index d263cedcd932509c95cf0b9a99d644dd0b8c22ed..5ce8d240ddc3167d0a248857f4601be0ed65ebb1 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
  * IP and UDP header sizes */
 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
 
+static int manager_dns_stub_udp_fd(Manager *m);
+static int manager_dns_stub_tcp_fd(Manager *m);
+
 static int dns_stub_make_reply_packet(
-                uint16_t id,
-                int rcode,
+                DnsPacket **p,
+                size_t max_size,
                 DnsQuestion *q,
                 DnsAnswer *answer,
-                bool add_opt,   /* add an OPT RR to this packet */
-                bool edns0_do,  /* set the EDNS0 DNSSEC OK bit */
-                bool ad,        /* set the DNSSEC authenticated data bit */
-                DnsPacket **ret) {
+                bool *ret_truncated) {
 
-        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        bool truncated = false;
         DnsResourceRecord *rr;
         unsigned c = 0;
         int r;
 
+        assert(p);
+
         /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
          * roundtrips aren't expensive. */
 
-        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
-        if (r < 0)
-                return r;
-
-        /* If the client didn't do EDNS, clamp the rcode to 4 bit */
-        if (!add_opt && rcode > 0xF)
-                rcode = DNS_RCODE_SERVFAIL;
+        if (!*p) {
+                r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
+                if (r < 0)
+                        return r;
 
-        DNS_PACKET_HEADER(p)->id = id;
-        DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
-                                                              1 /* qr */,
-                                                              0 /* opcode */,
-                                                              0 /* aa */,
-                                                              0 /* tc */,
-                                                              1 /* rd */,
-                                                              1 /* ra */,
-                                                              ad /* ad */,
-                                                              0 /* cd */,
-                                                              rcode));
+                r = dns_packet_append_question(*p, q);
+                if (r < 0)
+                        return r;
 
-        r = dns_packet_append_question(p, q);
-        if (r < 0)
-                return r;
-        DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
+                DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
+        }
 
         DNS_ANSWER_FOREACH(rr, answer) {
+
                 r = dns_question_matches_rr(q, rr, NULL);
                 if (r < 0)
                         return r;
@@ -83,13 +74,65 @@ static int dns_stub_make_reply_packet(
 
                 continue;
         add:
-                r = dns_packet_append_rr(p, rr, NULL, NULL);
+                r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
+                if (r == -EMSGSIZE) {
+                        truncated = true;
+                        break;
+                }
                 if (r < 0)
                         return r;
 
                 c++;
         }
-        DNS_PACKET_HEADER(p)->ancount = htobe16(c);
+
+        if (ret_truncated)
+                *ret_truncated = truncated;
+        else if (truncated)
+                return -EMSGSIZE;
+
+        DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
+
+        return 0;
+}
+
+static int dns_stub_finish_reply_packet(
+                DnsPacket *p,
+                uint16_t id,
+                int rcode,
+                bool tc,        /* set the Truncated bit? */
+                bool add_opt,   /* add an OPT RR to this packet? */
+                bool edns0_do,  /* set the EDNS0 DNSSEC OK bit? */
+                bool ad) {      /* set the DNSSEC authenticated data bit? */
+
+        int r;
+
+        assert(p);
+
+        if (!add_opt) {
+                /* If the client can't to EDNS0, don't do DO either */
+                edns0_do = false;
+
+                /* If the client didn't do EDNS, clamp the rcode to 4 bit */
+                if (rcode > 0xF)
+                        rcode = DNS_RCODE_SERVFAIL;
+        }
+
+        /* Don't set the AD bit unless DO is on, too */
+        if (!edns0_do)
+                ad = false;
+
+        DNS_PACKET_HEADER(p)->id = id;
+
+        DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+                                                              1  /* qr */,
+                                                              0  /* opcode */,
+                                                              0  /* aa */,
+                                                              tc /* tc */,
+                                                              1  /* rd */,
+                                                              1  /* ra */,
+                                                              ad /* ad */,
+                                                              0  /* cd */,
+                                                              rcode));
 
         if (add_opt) {
                 r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
@@ -97,9 +140,6 @@ static int dns_stub_make_reply_packet(
                         return r;
         }
 
-        *ret = p;
-        p = NULL;
-
         return 0;
 }
 
@@ -123,12 +163,6 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
         else {
                 int fd;
 
-                /* Truncate the message to the right size */
-                if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) {
-                        dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX);
-                        DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC);
-                }
-
                 fd = manager_dns_stub_udp_fd(m);
                 if (fd < 0)
                         return log_debug_errno(fd, "Failed to get reply socket: %m");
@@ -145,14 +179,18 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
         return 0;
 }
 
-static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
+static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
         int r;
 
         assert(m);
         assert(p);
 
-        r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply);
+        r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to make failure packet: %m");
+
+        r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated);
         if (r < 0)
                 return log_debug_errno(r, "Failed to build failure packet: %m");
 
@@ -168,32 +206,49 @@ static void dns_stub_query_complete(DnsQuery *q) {
         switch (q->state) {
 
         case DNS_TRANSACTION_SUCCESS: {
-                _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+                bool truncated;
 
-                r = dns_stub_make_reply_packet(
+                r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to build reply packet: %m");
+                        break;
+                }
+
+                r = dns_query_process_cname(q);
+                if (r == -ELOOP) {
+                        (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
+                        break;
+                }
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to process CNAME: %m");
+                        break;
+                }
+                if (r == DNS_QUERY_RESTARTED)
+                        return;
+
+                r = dns_stub_finish_reply_packet(
+                                q->reply_dns_packet,
                                 DNS_PACKET_ID(q->request_dns_packet),
                                 q->answer_rcode,
-                                q->question_idna,
-                                q->answer,
+                                truncated,
                                 !!q->request_dns_packet->opt,
                                 DNS_PACKET_DO(q->request_dns_packet),
-                                DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
-                                &reply);
+                                dns_query_fully_authenticated(q));
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to build reply packet: %m");
+                        log_debug_errno(r, "Failed to finish reply packet: %m");
                         break;
                 }
 
-                (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply);
+                (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
                 break;
         }
 
         case DNS_TRANSACTION_RCODE_FAILURE:
-                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
+                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
                 break;
 
         case DNS_TRANSACTION_NOT_FOUND:
-                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
+                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
                 break;
 
         case DNS_TRANSACTION_TIMEOUT:
@@ -209,7 +264,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
         case DNS_TRANSACTION_NO_TRUST_ANCHOR:
         case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
         case DNS_TRANSACTION_NETWORK_DOWN:
-                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+                (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
                 break;
 
         case DNS_TRANSACTION_NULL:
@@ -256,52 +311,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
         if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
             in_addr_is_localhost(p->family, &p->destination) <= 0) {
                 log_error("Got packet on unexpected IP range, refusing.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
                 goto fail;
         }
 
         r = dns_packet_extract(p);
         if (r < 0) {
                 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
                 goto fail;
         }
 
         if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
                 log_debug("Got EDNS OPT field with unsupported version number.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
                 goto fail;
         }
 
         if (dns_type_is_obsolete(p->question->keys[0]->type)) {
                 log_debug("Got message with obsolete key type, refusing.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
                 goto fail;
         }
 
         if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
                 log_debug("Got request for zone transfer, refusing.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
                 goto fail;
         }
 
         if (!DNS_PACKET_RD(p))  {
                 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
                 log_debug("Got request with recursion disabled, refusing.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
                 goto fail;
         }
 
         if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
                 log_debug("Got request with DNSSEC CD bit set, refusing.");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
                 goto fail;
         }
 
-        r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME);
+        r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
         if (r < 0) {
                 log_error_errno(r, "Failed to generate query object: %m");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
                 goto fail;
         }
 
@@ -321,11 +376,11 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
         r = dns_query_go(q);
         if (r < 0) {
                 log_error_errno(r, "Failed to start query: %m");
-                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+                dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
                 goto fail;
         }
 
-        log_info("Processing query...");
+        log_debug("Processing query...");
         return;
 
 fail:
@@ -354,66 +409,48 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
         return 0;
 }
 
-int manager_dns_stub_udp_fd(Manager *m) {
+static int manager_dns_stub_udp_fd(Manager *m) {
         static const int one = 1;
-
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(53),
                 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
         };
-
+        _cleanup_close_ int fd = -1;
         int r;
 
         if (m->dns_stub_udp_fd >= 0)
                 return m->dns_stub_udp_fd;
 
-        m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->dns_stub_udp_fd < 0)
+        fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (fd < 0)
                 return -errno;
 
-        r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+                return -errno;
 
-        r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+                return -errno;
 
-        r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+                return -errno;
 
         /* Make sure no traffic from outside the local host can leak to onto this socket */
-        r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+                return -errno;
 
-        r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+                return -errno;
 
-        r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m);
+        r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
         if (r < 0)
-                goto fail;
+                return r;
 
         (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
+        m->dns_stub_udp_fd = fd;
+        fd = -1;
 
         return m->dns_stub_udp_fd;
-
-fail:
-        m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
-        return r;
 }
 
 static int on_dns_stub_stream_packet(DnsStream *s) {
@@ -441,7 +478,7 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
 
         cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
         if (cfd < 0) {
-                if (errno == EAGAIN || errno == EINTR)
+                if (IN_SET(errno, EAGAIN, EINTR))
                         return 0;
 
                 return -errno;
@@ -461,102 +498,91 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
         return 0;
 }
 
-int manager_dns_stub_tcp_fd(Manager *m) {
+static int manager_dns_stub_tcp_fd(Manager *m) {
         static const int one = 1;
-
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
                 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
                 .in.sin_port = htobe16(53),
         };
-
+        _cleanup_close_ int fd = -1;
         int r;
 
         if (m->dns_stub_tcp_fd >= 0)
                 return m->dns_stub_tcp_fd;
 
-        m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->dns_stub_tcp_fd < 0)
+        fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (fd < 0)
                 return -errno;
 
-        r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
+                return -errno;
 
-        r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+                return -errno;
 
-        r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+                return -errno;
 
-        r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+                return -errno;
 
         /* Make sure no traffic from outside the local host can leak to onto this socket */
-        r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+                return -errno;
 
-        r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+                return -errno;
 
-        r = listen(m->dns_stub_tcp_fd, SOMAXCONN);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (listen(fd, SOMAXCONN) < 0)
+                return -errno;
 
-        r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m);
+        r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
         if (r < 0)
-                goto fail;
+                return r;
 
         (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
+        m->dns_stub_tcp_fd = fd;
+        fd = -1;
 
         return m->dns_stub_tcp_fd;
-
-fail:
-        m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
-        return r;
 }
 
 int manager_dns_stub_start(Manager *m) {
-        int r;
+        const char *t = "UDP";
+        int r = 0;
 
         assert(m);
 
-        r = manager_dns_stub_udp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
-
-        r = manager_dns_stub_tcp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
-
-        return 0;
+        if (m->dns_stub_listener_mode == DNS_STUB_LISTENER_NO)
+                log_debug("Not creating stub listener.");
+        else
+                log_debug("Creating stub listener using %s.",
+                          m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
+                          m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
+                          "UDP/TCP");
+
+        if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
+                r = manager_dns_stub_udp_fd(m);
+
+        if (r >= 0 &&
+            IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
+                t = "TCP";
+                r = manager_dns_stub_tcp_fd(m);
+        }
 
-eaddrinuse:
-        log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support.");
-        manager_dns_stub_stop(m);
+        if (IN_SET(r, -EADDRINUSE, -EPERM)) {
+                if (r == -EADDRINUSE)
+                        log_warning_errno(r,
+                                          "Another process is already listening on %s socket 127.0.0.53:53.\n"
+                                          "Turning off local DNS stub support.", t);
+                else
+                        log_warning_errno(r,
+                                          "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
+                                          "Turning off local DNS stub support.", t);
+                manager_dns_stub_stop(m);
+        } else if (r < 0)
+                return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
 
         return 0;
 }