]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: follow CNAMES for DNS stub replies
authorLennart Poettering <lennart@poettering.net>
Wed, 8 Feb 2017 18:12:55 +0000 (19:12 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 9 Feb 2017 15:13:07 +0000 (16:13 +0100)
Clients expect us to follow CNAMEs for them, hence do so. On the first
iteration start putting together a packet, and then keep adding data we
acquire through CNAMEs to it, until we finally send it off.

Fixes: #3826
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-stub.c

index e03db4d003ee3630ec9a6d1fbd9a96a7ef27c5cb..39fb213cbccdf009e47e7f862bc8e4379075b5c8 100644 (file)
@@ -403,6 +403,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
         sd_bus_track_unref(q->bus_track);
 
         dns_packet_unref(q->request_dns_packet);
+        dns_packet_unref(q->reply_dns_packet);
 
         if (q->request_dns_stream) {
                 /* Detach the stream from our query, in case something else keeps a reference to it. */
index 49a35b846bdac6265a64e01437e67c6ec13bb757..8f378999d659cd6357695e9a79a4fea210a6f683 100644 (file)
@@ -71,7 +71,6 @@ struct DnsQuery {
          * family */
         bool suppress_unroutable_family;
 
-
         /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
         bool clamp_ttl;
 
@@ -102,6 +101,7 @@ struct DnsQuery {
         /* DNS stub information */
         DnsPacket *request_dns_packet;
         DnsStream *request_dns_stream;
+        DnsPacket *reply_dns_packet;
 
         /* Completion callback */
         void (*complete)(DnsQuery* q);
index 4a3c5f612fd41a32071d77da6ccf632440c09006..77ee7e97dbbfe68dbeda89afe8f2f58bfcc61e34 100644 (file)
@@ -29,49 +29,33 @@ 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,
                 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) {
+                DnsAnswer *answer) {
 
-        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         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);
+                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 +70,46 @@ 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, NULL, NULL);
                 if (r < 0)
                         return r;
 
                 c++;
         }
-        DNS_PACKET_HEADER(p)->ancount = htobe16(c);
+
+        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 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 the client didn't do EDNS, clamp the rcode to 4 bit */
+        if (!add_opt && rcode > 0xF)
+                rcode = DNS_RCODE_SERVFAIL;
+
+        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));
 
         if (add_opt) {
                 r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
@@ -100,9 +117,6 @@ static int dns_stub_make_reply_packet(
                         return r;
         }
 
-        *ret = p;
-        p = NULL;
-
         return 0;
 }
 
@@ -155,7 +169,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
         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, p->question, 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, !!p->opt, DNS_PACKET_DO(p), false);
         if (r < 0)
                 return log_debug_errno(r, "Failed to build failure packet: %m");
 
@@ -170,26 +188,40 @@ static void dns_stub_query_complete(DnsQuery *q) {
 
         switch (q->state) {
 
-        case DNS_TRANSACTION_SUCCESS: {
-                _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+        case DNS_TRANSACTION_SUCCESS:
+
+                r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer);
+                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);
+                        break;
+                }
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to process CNAME: %m");
+                        break;
+                }
+                if (r == DNS_QUERY_RESTARTED)
+                        return;
 
-                r = dns_stub_make_reply_packet(
+                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,
                                 !!q->request_dns_packet->opt,
                                 DNS_PACKET_DO(q->request_dns_packet),
-                                DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
-                                &reply);
+                                DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated);
                 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);
@@ -301,7 +333,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
                 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);