]> 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 4a3c5f612fd41a32071d77da6ccf632440c09006..5ce8d240ddc3167d0a248857f4601be0ed65ebb1 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -29,49 +30,36 @@ 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;
@@ -86,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);
@@ -100,9 +140,6 @@ static int dns_stub_make_reply_packet(
                         return r;
         }
 
-        *ret = p;
-        p = NULL;
-
         return 0;
 }
 
@@ -126,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");
@@ -148,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");
 
@@ -171,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(&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_stub_make_reply_packet(
+                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:
@@ -212,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:
@@ -259,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;
         }
 
@@ -324,7 +376,7 @@ 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;
         }
 
@@ -426,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;
@@ -502,6 +554,14 @@ int manager_dns_stub_start(Manager *m) {
 
         assert(m);
 
+        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);