]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-packet.c
Merge pull request #2147 from vcaputo/sd-event-measure-latencies
[thirdparty/systemd.git] / src / resolve / resolved-dns-packet.c
index b0739fcf12ab87f6d3d6872f14838c66276d612a..a8a8632491908108183a1ce7df53ec1960c3b984 100644 (file)
@@ -58,6 +58,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
         p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
         p->allocated = a;
         p->protocol = protocol;
+        p->opt_start = p->opt_size = (size_t) -1;
         p->n_ref = 1;
 
         *ret = p;
@@ -438,10 +439,15 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
         return 0;
 }
 
-int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
+int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonical_candidate, size_t *start) {
         uint8_t *w;
         int r;
 
+        /* Append a label to a packet. Optionally, does this in DNSSEC
+         * canonical form, if this label is marked as a candidate for
+         * it, and the canonical form logic is enabled for the
+         * packet */
+
         assert(p);
         assert(d);
 
@@ -454,18 +460,14 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start
 
         *(w++) = (uint8_t) l;
 
-        if (p->canonical_form) {
+        if (p->canonical_form && canonical_candidate) {
                 size_t i;
 
                 /* Generate in canonical form, as defined by DNSSEC
                  * RFC 4034, Section 6.2, i.e. all lower-case. */
 
-                for (i = 0; i < l; i++) {
-                        if (d[i] >= 'A' && d[i] <= 'Z')
-                                w[i] = (uint8_t) (d[i] - 'A' + 'a');
-                        else
-                                w[i] = (uint8_t) d[i];
-                }
+                for (i = 0; i < l; i++)
+                        w[i] = (uint8_t) ascii_tolower(d[i]);
         } else
                 /* Otherwise, just copy the string unaltered. This is
                  * essential for DNS-SD, where the casing of labels
@@ -479,6 +481,7 @@ int dns_packet_append_name(
                 DnsPacket *p,
                 const char *name,
                 bool allow_compression,
+                bool canonical_candidate,
                 size_t *start) {
 
         size_t saved_size;
@@ -492,8 +495,8 @@ int dns_packet_append_name(
 
         saved_size = p->size;
 
-        while (*name) {
-                _cleanup_free_ char *s = NULL;
+        while (!dns_name_is_root(name)) {
+                const char *z = name;
                 char label[DNS_LABEL_MAX];
                 size_t n = 0;
                 int k;
@@ -512,12 +515,6 @@ int dns_packet_append_name(
                         }
                 }
 
-                s = strdup(name);
-                if (!s) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
                 r = dns_label_unescape(&name, label, sizeof(label));
                 if (r < 0)
                         goto fail;
@@ -533,11 +530,19 @@ int dns_packet_append_name(
                 if (k > 0)
                         r = k;
 
-                r = dns_packet_append_label(p, label, r, &n);
+                r = dns_packet_append_label(p, label, r, canonical_candidate, &n);
                 if (r < 0)
                         goto fail;
 
                 if (allow_compression) {
+                        _cleanup_free_ char *s = NULL;
+
+                        s = strdup(z);
+                        if (!s) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
                         r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops);
                         if (r < 0)
                                 goto fail;
@@ -574,7 +579,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)
 
         saved_size = p->size;
 
-        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL);
+        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL);
         if (r < 0)
                 goto fail;
 
@@ -596,7 +601,7 @@ fail:
         return r;
 }
 
-static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) {
+static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, const uint8_t *types, size_t *start) {
         size_t saved_size;
         int r;
 
@@ -637,7 +642,6 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
         int r;
 
         assert(p);
-        assert(types);
 
         saved_size = p->size;
 
@@ -653,15 +657,16 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
                 }
 
                 window = n >> 8;
-
                 entry = n & 255;
 
                 bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
         }
 
-        r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
-        if (r < 0)
-                goto fail;
+        if (bitmaps[entry / 8] != 0) {
+                r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
+                if (r < 0)
+                        goto fail;
+        }
 
         if (start)
                 *start = saved_size;
@@ -673,7 +678,7 @@ fail:
 }
 
 /* Append the OPT pseudo-RR described in RFC6891 */
-int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
+int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
         size_t saved_size;
         int r;
 
@@ -681,6 +686,11 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
         /* we must never advertise supported packet size smaller than the legacy max */
         assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
 
+        if (p->opt_start != (size_t) -1)
+                return -EBUSY;
+
+        assert(p->opt_size == (size_t) -1);
+
         saved_size = p->size;
 
         /* empty name */
@@ -709,10 +719,48 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
                 goto fail;
 
         /* RDLENGTH */
-        r = dns_packet_append_uint16(p, 0, NULL);
+
+        if (edns0_do) {
+                /* If DO is on, also append RFC6975 Algorithm data */
+
+                static const uint8_t rfc6975[] = {
+
+                        0, 5, /* OPTION_CODE: DAU */
+                        0, 6, /* LIST_LENGTH */
+                        DNSSEC_ALGORITHM_RSASHA1,
+                        DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
+                        DNSSEC_ALGORITHM_RSASHA256,
+                        DNSSEC_ALGORITHM_RSASHA512,
+                        DNSSEC_ALGORITHM_ECDSAP256SHA256,
+                        DNSSEC_ALGORITHM_ECDSAP384SHA384,
+
+                        0, 6, /* OPTION_CODE: DHU */
+                        0, 3, /* LIST_LENGTH */
+                        DNSSEC_DIGEST_SHA1,
+                        DNSSEC_DIGEST_SHA256,
+                        DNSSEC_DIGEST_SHA384,
+
+                        0, 7, /* OPTION_CODE: N3U */
+                        0, 1, /* LIST_LENGTH */
+                        NSEC3_ALGORITHM_SHA1,
+                };
+
+                r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
+        } else
+                r = dns_packet_append_uint16(p, 0, NULL);
+
         if (r < 0)
                 goto fail;
 
+        DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) + 1);
+
+        p->opt_start = saved_size;
+        p->opt_size = p->size - saved_size;
+
         if (start)
                 *start = saved_size;
 
@@ -723,6 +771,27 @@ fail:
         return r;
 }
 
+int dns_packet_truncate_opt(DnsPacket *p) {
+        assert(p);
+
+        if (p->opt_start == (size_t) -1) {
+                assert(p->opt_size == (size_t) -1);
+                return 0;
+        }
+
+        assert(p->opt_size != (size_t) -1);
+        assert(DNS_PACKET_ARCOUNT(p) > 0);
+
+        if (p->opt_start + p->opt_size != p->size)
+                return -EBUSY;
+
+        dns_packet_truncate(p, p->opt_start);
+        DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) - 1);
+        p->opt_start = p->opt_size = (size_t) -1;
+
+        return 1;
+}
+
 int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
         size_t saved_size, rdlength_offset, end, rdlength, rds;
         int r;
@@ -762,14 +831,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->srv.name, true, NULL);
+                r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);
                 break;
 
         case DNS_TYPE_PTR:
         case DNS_TYPE_NS:
         case DNS_TYPE_CNAME:
         case DNS_TYPE_DNAME:
-                r = dns_packet_append_name(p, rr->ptr.name, true, NULL);
+                r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);
                 break;
 
         case DNS_TYPE_HINFO:
@@ -812,11 +881,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 break;
 
         case DNS_TYPE_SOA:
-                r = dns_packet_append_name(p, rr->soa.mname, true, NULL);
+                r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->soa.rname, true, NULL);
+                r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -844,7 +913,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->mx.exchange, true, NULL);
+                r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);
                 break;
 
         case DNS_TYPE_LOC:
@@ -948,7 +1017,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL);
+                r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -956,7 +1025,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 break;
 
         case DNS_TYPE_NSEC:
-                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL);
+                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -1038,7 +1107,6 @@ fail:
         return r;
 }
 
-
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
 
@@ -1448,9 +1516,9 @@ fail:
         return r;
 }
 
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_free_ char *name = NULL;
-        bool cache_flush = true;
+        bool cache_flush = false;
         uint16_t class, type;
         DnsResourceKey *key;
         size_t saved_rindex;
@@ -1476,10 +1544,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         if (p->protocol == DNS_PROTOCOL_MDNS) {
                 /* See RFC6762, Section 10.2 */
 
-                if (class & MDNS_RR_CACHE_FLUSH)
+                if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
                         class &= ~MDNS_RR_CACHE_FLUSH;
-                else
-                        cache_flush = false;
+                        cache_flush = true;
+                }
         }
 
         key = dns_resource_key_new_consume(class, type, name);
@@ -1488,11 +1556,11 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
                 goto fail;
         }
 
-        key->cache_flush = cache_flush;
-
         name = NULL;
         *ret = key;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1508,11 +1576,12 @@ static bool loc_size_ok(uint8_t size) {
         return m <= 9 && e <= 9 && (m > 0 || e == 0);
 }
 
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         size_t saved_rindex, offset;
         uint16_t rdlength;
+        bool cache_flush;
         int r;
 
         assert(p);
@@ -1520,11 +1589,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
 
         saved_rindex = p->rindex;
 
-        r = dns_packet_read_key(p, &key, NULL);
+        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
         if (r < 0)
                 goto fail;
 
-        if (key->class == DNS_CLASS_ANY ||
+        if (!dns_class_is_valid_rr(key->class)||
             !dns_type_is_valid_rr(key->type)) {
                 r = -EBADMSG;
                 goto fail;
@@ -1540,6 +1609,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         if (r < 0)
                 goto fail;
 
+        /* RFC 2181, Section 8, suggests to
+         * treat a TTL with the MSB set as a zero TTL. */
+        if (rr->ttl & UINT32_C(0x80000000))
+                rr->ttl = 0;
+
         r = dns_packet_read_uint16(p, &rdlength, NULL);
         if (r < 0)
                 goto fail;
@@ -1932,6 +2006,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         *ret = rr;
         rr = NULL;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1964,11 +2040,17 @@ int dns_packet_extract(DnsPacket *p) {
 
                 for (i = 0; i < n; i++) {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+                        bool cache_flush;
 
-                        r = dns_packet_read_key(p, &key, NULL);
+                        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
                         if (r < 0)
                                 goto finish;
 
+                        if (cache_flush) {
+                                r = -EBADMSG;
+                                goto finish;
+                        }
+
                         if (!dns_type_is_valid_query(key->type)) {
                                 r = -EBADMSG;
                                 goto finish;
@@ -1990,19 +2072,26 @@ int dns_packet_extract(DnsPacket *p) {
 
                 for (i = 0; i < n; i++) {
                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+                        bool cache_flush;
 
-                        r = dns_packet_read_rr(p, &rr, NULL);
+                        r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
                         if (r < 0)
                                 goto finish;
 
                         if (rr->key->type == DNS_TYPE_OPT) {
 
-                                /* The OPT RR is only valid in the Additional section */
-                                if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
+                                if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) {
                                         r = -EBADMSG;
                                         goto finish;
                                 }
 
+                                /* Note that we accept the OPT RR in
+                                 * any section, not just in the
+                                 * additional section, as some routers
+                                 * (Belkin!)  blindly copy the OPT RR
+                                 * from the query to the reply packet,
+                                 * and don't get the section right. */
+
                                 /* Two OPT RRs? */
                                 if (p->opt) {
                                         r = -EBADMSG;
@@ -2011,7 +2100,18 @@ int dns_packet_extract(DnsPacket *p) {
 
                                 p->opt = dns_resource_record_ref(rr);
                         } else {
-                                r = dns_answer_add(answer, rr, p->ifindex);
+
+                                /* According to RFC 4795, section
+                                 * 2.9. only the RRs from the Answer
+                                 * section shall be cached. Hence mark
+                                 * only those RRs as cacheable by
+                                 * default, but not the ones from the
+                                 * Additional or Authority
+                                 * sections. */
+
+                                r = dns_answer_add(answer, rr, p->ifindex,
+                                                   (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
+                                                   (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
                                 if (r < 0)
                                         goto finish;
                         }