]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-packet.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / resolve / resolved-dns-packet.c
index b37f57fe676a7ed3708a9dfc18aca669f9cc14a8..991a1be458453766aa55fbfa10e914dc0d522a80 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #if HAVE_GCRYPT
-#include <gcrypt.h>
+#  include <gcrypt.h>
 #endif
 
 #include "alloc-util.h"
@@ -9,11 +9,11 @@
 #include "memory-util.h"
 #include "resolved-dns-packet.h"
 #include "set.h"
+#include "stdio-util.h"
 #include "string-table.h"
 #include "strv.h"
 #include "unaligned.h"
 #include "utf8.h"
-#include "util.h"
 
 #define EDNS0_OPT_DO (1<<15)
 
@@ -29,8 +29,11 @@ static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
                 dns_packet_rewind(rewinder->packet, rewinder->saved_rindex);
 }
 
-#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0)
-#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0)
+#define REWINDER_INIT(p) {                              \
+                .packet = (p),                          \
+                .saved_rindex = (p)->rindex,            \
+        }
+#define CANCEL_REWINDER(rewinder) do { (rewinder).packet = NULL; } while (0)
 
 int dns_packet_new(
                 DnsPacket **ret,
@@ -99,7 +102,7 @@ void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool trun
 
         h = DNS_PACKET_HEADER(p);
 
-        switch(p->protocol) {
+        switch (p->protocol) {
         case DNS_PROTOCOL_LLMNR:
                 assert(!truncated);
 
@@ -304,13 +307,13 @@ int dns_packet_validate_query(DnsPacket *p) {
         if (DNS_PACKET_OPCODE(p) != 0)
                 return -EBADMSG;
 
-        if (DNS_PACKET_TC(p))
-                return -EBADMSG;
-
         switch (p->protocol) {
 
         case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_DNS:
+                if (DNS_PACKET_TC(p)) /* mDNS query may have truncation flag. */
+                        return -EBADMSG;
+
                 /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
                 if (DNS_PACKET_QDCOUNT(p) != 1)
                         return -EBADMSG;
@@ -515,15 +518,12 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonica
 
         *(w++) = (uint8_t) l;
 
-        if (p->canonical_form && canonical_candidate) {
-                size_t i;
-
+        if (p->canonical_form && canonical_candidate)
                 /* Generate in canonical form, as defined by DNSSEC
                  * RFC 4034, Section 6.2, i.e. all lower-case. */
-
-                for (i = 0; i < l; i++)
+                for (size_t i = 0; i < l; i++)
                         w[i] = (uint8_t) ascii_tolower(d[i]);
-        else
+        else
                 /* Otherwise, just copy the string unaltered. This is
                  * essential for DNS-SD, where the casing of labels
                  * matters and needs to be retained. */
@@ -552,7 +552,7 @@ int dns_packet_append_name(
 
         while (!dns_name_is_root(name)) {
                 const char *z = name;
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
                 size_t n = 0;
 
                 if (allow_compression)
@@ -776,7 +776,7 @@ int dns_packet_append_opt(
                 static const uint8_t rfc6975[] = {
 
                         0, 5, /* OPTION_CODE: DAU */
-#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600
+#if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600)
                         0, 7, /* LIST_LENGTH */
 #else
                         0, 6, /* LIST_LENGTH */
@@ -787,7 +787,7 @@ int dns_packet_append_opt(
                         DNSSEC_ALGORITHM_RSASHA512,
                         DNSSEC_ALGORITHM_ECDSAP256SHA256,
                         DNSSEC_ALGORITHM_ECDSAP384SHA384,
-#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600
+#if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600)
                         DNSSEC_ALGORITHM_ED25519,
 #endif
 
@@ -911,9 +911,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                 if (r < 0)
                         goto fail;
 
-                /* RFC 2782 states "Unless and until permitted by future standards
-                 * action, name compression is not to be used for this field." */
-                r = dns_packet_append_name(p, rr->srv.name, false, true, NULL);
+                /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+                 * is not to be used for this field." Hence we turn off compression here. */
+                r = dns_packet_append_name(p, rr->srv.name, /* allow_compression= */ false, /* canonical_candidate= */ true, NULL);
                 break;
 
         case DNS_TYPE_PTR:
@@ -941,15 +941,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                         r = dns_packet_append_raw_string(p, NULL, 0, NULL);
                         if (r < 0)
                                 goto fail;
-                } else {
-                        DnsTxtItem *i;
-
+                } else
                         LIST_FOREACH(items, i, rr->txt.items) {
                                 r = dns_packet_append_raw_string(p, i->data, i->length, NULL);
                                 if (r < 0)
                                         goto fail;
                         }
-                }
 
                 r = 0;
                 break;
@@ -1254,8 +1251,9 @@ int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a, unsigned *completed) {
 
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
+        assert(p->rindex <= p->size);
 
-        if (p->rindex + sz > p->size)
+        if (sz > p->size - p->rindex)
                 return -EMSGSIZE;
 
         if (ret)
@@ -1373,14 +1371,13 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
 }
 
 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        _cleanup_free_ char *t = NULL;
         const void *d;
-        char *t;
         uint8_t c;
         int r;
 
         assert(p);
-        INIT_REWINDER(rewinder, p);
 
         r = dns_packet_read_uint8(p, &c, NULL);
         if (r < 0)
@@ -1390,19 +1387,14 @@ int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
         if (r < 0)
                 return r;
 
-        if (memchr(d, 0, c))
-                return -EBADMSG;
-
-        t = strndup(d, c);
-        if (!t)
-                return -ENOMEM;
+        r = make_cstring(d, c, MAKE_CSTRING_REFUSE_TRAILING_NUL, &t);
+        if (r < 0)
+                return r;
 
-        if (!utf8_is_valid(t)) {
-                free(t);
+        if (!utf8_is_valid(t))
                 return -EBADMSG;
-        }
 
-        *ret = t;
+        *ret = TAKE_PTR(t);
 
         if (start)
                 *start = rewinder.saved_rindex;
@@ -1412,13 +1404,12 @@ int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
 }
 
 int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+        assert(p);
+
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         uint8_t c;
         int r;
 
-        assert(p);
-        INIT_REWINDER(rewinder, p);
-
         r = dns_packet_read_uint8(p, &c, NULL);
         if (r < 0)
                 return r;
@@ -1442,18 +1433,15 @@ int dns_packet_read_name(
                 bool allow_compression,
                 size_t *ret_start) {
 
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
-        size_t after_rindex = 0, jump_barrier;
+        assert(p);
+
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        size_t after_rindex = 0, jump_barrier = p->rindex;
         _cleanup_free_ char *name = NULL;
         bool first = true;
         size_t n = 0;
         int r;
 
-        assert(p);
-
-        INIT_REWINDER(rewinder, p);
-        jump_barrier = p->rindex;
-
         if (p->refuse_compression)
                 allow_compression = false;
 
@@ -1530,19 +1518,16 @@ int dns_packet_read_name(
 }
 
 static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
-        uint8_t window;
-        uint8_t length;
+        assert(p);
+        assert(types);
+
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        uint8_t window, length;
         const uint8_t *bitmap;
         uint8_t bit = 0;
-        unsigned i;
         bool found = false;
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
         int r;
 
-        assert(p);
-        assert(types);
-        INIT_REWINDER(rewinder, p);
-
         r = bitmap_ensure_allocated(types);
         if (r < 0)
                 return r;
@@ -1562,7 +1547,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
         if (r < 0)
                 return r;
 
-        for (i = 0; i < length; i++) {
+        for (uint8_t i = 0; i < length; i++) {
                 uint8_t bitmask = 1 << 7;
 
                 if (!bitmap[i]) {
@@ -1600,22 +1585,22 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
 }
 
 static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         int r;
 
-        INIT_REWINDER(rewinder, p);
-
-        while (p->rindex < rewinder.saved_rindex + size) {
+        while (p->rindex - rewinder.saved_rindex < size) {
                 r = dns_packet_read_type_window(p, types, NULL);
                 if (r < 0)
                         return r;
 
+                assert(p->rindex >= rewinder.saved_rindex);
+
                 /* don't read past end of current RR */
-                if (p->rindex > rewinder.saved_rindex + size)
+                if (p->rindex - rewinder.saved_rindex > size)
                         return -EBADMSG;
         }
 
-        if (p->rindex != rewinder.saved_rindex + size)
+        if (p->rindex - rewinder.saved_rindex != size)
                 return -EBADMSG;
 
         if (start)
@@ -1631,15 +1616,14 @@ int dns_packet_read_key(
                 bool *ret_cache_flush_or_qu,
                 size_t *ret_start) {
 
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+        assert(p);
+
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         _cleanup_free_ char *name = NULL;
         bool cache_flush_or_qu = false;
         uint16_t class, type;
         int r;
 
-        assert(p);
-        INIT_REWINDER(rewinder, p);
-
         r = dns_packet_read_name(p, &name, true, NULL);
         if (r < 0)
                 return r;
@@ -1693,18 +1677,16 @@ int dns_packet_read_rr(
                 bool *ret_cache_flush,
                 size_t *ret_start) {
 
+        assert(p);
+
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
         size_t offset;
         uint16_t rdlength;
         bool cache_flush;
         int r;
 
-        assert(p);
-
-        INIT_REWINDER(rewinder, p);
-
         r = dns_packet_read_key(p, &key, &cache_flush, NULL);
         if (r < 0)
                 return r;
@@ -1729,7 +1711,7 @@ int dns_packet_read_rr(
         if (r < 0)
                 return r;
 
-        if (p->rindex + rdlength > p->size)
+        if (rdlength > p->size - p->rindex)
                 return -EBADMSG;
 
         offset = p->rindex;
@@ -1746,7 +1728,13 @@ int dns_packet_read_rr(
                 r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
                 if (r < 0)
                         return r;
-                r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
+
+                /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+                 * is not to be used for this field." Nonetheless, we support it here, in the interest of
+                 * increasing compatibility with implementations that do not implement this correctly. After
+                 * all we didn't do this right once upon a time ourselves (see
+                 * https://github.com/systemd/systemd/issues/9793). */
+                r = dns_packet_read_name(p, &rr->srv.name, /* allow_compression= */ true, NULL);
                 break;
 
         case DNS_TYPE_PTR:
@@ -1773,7 +1761,7 @@ int dns_packet_read_rr(
                 } else {
                         DnsTxtItem *last = NULL;
 
-                        while (p->rindex < offset + rdlength) {
+                        while (p->rindex - offset < rdlength) {
                                 DnsTxtItem *i;
                                 const void *data;
                                 size_t sz;
@@ -2005,7 +1993,7 @@ int dns_packet_read_rr(
                 if (r < 0)
                         return r;
 
-                if (rdlength + offset < p->rindex)
+                if (rdlength < p->rindex - offset)
                         return -EBADMSG;
 
                 r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
@@ -2032,6 +2020,9 @@ int dns_packet_read_rr(
                 if (r < 0)
                         return r;
 
+                if (rdlength < p->rindex - offset)
+                        return -EBADMSG;
+
                 r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
 
                 /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
@@ -2077,6 +2068,9 @@ int dns_packet_read_rr(
                 if (r < 0)
                         return r;
 
+                if (rdlength < p->rindex - offset)
+                        return -EBADMSG;
+
                 r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
 
                 /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
@@ -2120,7 +2114,7 @@ int dns_packet_read_rr(
                 if (r < 0)
                         return r;
 
-                if (rdlength + offset < p->rindex)
+                if (rdlength < p->rindex - offset)
                         return -EBADMSG;
 
                 r = dns_packet_read_memdup(p,
@@ -2139,7 +2133,7 @@ int dns_packet_read_rr(
         }
         if (r < 0)
                 return r;
-        if (p->rindex != offset + rdlength)
+        if (p->rindex - offset != rdlength)
                 return -EBADMSG;
 
         if (ret)
@@ -2199,7 +2193,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
 
 static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) {
         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
-        unsigned n, i;
+        unsigned n;
         int r;
 
         n = DNS_PACKET_QDCOUNT(p);
@@ -2219,7 +2213,7 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
                 if (r < 0)
                         return r;
 
-                for (i = 0; i < n; i++) {
+                for (unsigned i = 0; i < n; i++) {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
                         bool qu;
 
@@ -2250,7 +2244,7 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
 
 static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned n, i;
+        unsigned n;
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
         bool bad_opt = false;
         int r;
@@ -2263,7 +2257,7 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
         if (!answer)
                 return -ENOMEM;
 
-        for (i = 0; i < n; i++) {
+        for (unsigned i = 0; i < n; i++) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
                 bool cache_flush = false;
                 size_t start;
@@ -2354,18 +2348,25 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
                 } else {
                         DnsAnswerFlags flags = 0;
 
-                        if (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush)
-                                flags |= DNS_ANSWER_SHARED_OWNER;
+                        if (p->protocol == DNS_PROTOCOL_MDNS) {
+                                flags |= DNS_ANSWER_REFUSE_TTL_NO_MATCH;
+                                if (!cache_flush)
+                                        flags |= DNS_ANSWER_SHARED_OWNER;
+                        }
 
                         /* 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. */
+                         * the Additional or Authority sections.
+                         * This restriction does not apply to mDNS records (RFC 6762). */
                         if (i < DNS_PACKET_ANCOUNT(p))
                                 flags |= DNS_ANSWER_CACHEABLE|DNS_ANSWER_SECTION_ANSWER;
                         else if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p))
                                 flags |= DNS_ANSWER_SECTION_AUTHORITY;
-                        else
+                        else {
                                 flags |= DNS_ANSWER_SECTION_ADDITIONAL;
+                                if (p->protocol == DNS_PROTOCOL_MDNS)
+                                        flags |= DNS_ANSWER_CACHEABLE;
+                        }
 
                         r = dns_answer_add(answer, rr, p->ifindex, flags, NULL);
                         if (r < 0)
@@ -2375,8 +2376,7 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
                 /* Remember this RR, so that we can potentially merge its ->key object with the
                  * next RR. Note that we only do this if we actually decided to keep the RR around.
                  */
-                dns_resource_record_unref(previous);
-                previous = dns_resource_record_ref(rr);
+                DNS_RR_REPLACE(previous, dns_resource_record_ref(rr));
         }
 
         if (bad_opt) {
@@ -2390,15 +2390,16 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
 }
 
 int dns_packet_extract(DnsPacket *p) {
-        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
-        int r;
+        assert(p);
 
         if (p->extracted)
                 return 0;
 
-        INIT_REWINDER(rewinder, p);
+        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        _unused_ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        int r;
+
         dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
 
         r = dns_packet_extract_question(p, &question);
@@ -2417,7 +2418,6 @@ int dns_packet_extract(DnsPacket *p) {
 
         p->question = TAKE_PTR(question);
         p->answer = TAKE_PTR(answer);
-
         p->extracted = true;
 
         /* no CANCEL, always rewind */
@@ -2466,14 +2466,12 @@ int dns_packet_patch_max_udp_size(DnsPacket *p, uint16_t max_udp_size) {
 }
 
 static int patch_rr(DnsPacket *p, usec_t age) {
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         size_t ttl_index;
         uint32_t ttl;
         uint16_t type, rdlength;
         int r;
 
-        INIT_REWINDER(rewinder, p);
-
         /* Patches the RR at the current rindex, subtracts the specified time from the TTL */
 
         r = dns_packet_read_name(p, NULL, true, NULL);
@@ -2510,33 +2508,31 @@ static int patch_rr(DnsPacket *p, usec_t age) {
 }
 
 int dns_packet_patch_ttls(DnsPacket *p, usec_t timestamp) {
-        _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
-        unsigned i, n;
-        usec_t k;
-        int r;
-
         assert(p);
         assert(timestamp_is_set(timestamp));
 
         /* Adjusts all TTLs in the packet by subtracting the time difference between now and the specified timestamp */
 
-        k = now(clock_boottime_or_monotonic());
+        _unused_ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        unsigned n;
+        usec_t k;
+        int r;
+
+        k = now(CLOCK_BOOTTIME);
         assert(k >= timestamp);
         k -= timestamp;
 
-        INIT_REWINDER(rewinder, p);
-
         dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
 
         n = DNS_PACKET_QDCOUNT(p);
-        for (i = 0; i < n; i++) {
+        for (unsigned i = 0; i < n; i++) {
                 r = dns_packet_read_key(p, NULL, NULL, NULL);
                 if (r < 0)
                         return r;
         }
 
         n = DNS_PACKET_RRCOUNT(p);
-        for (i = 0; i < n; i++) {
+        for (unsigned i = 0; i < n; i++) {
 
                 /* DNS servers suck, hence the RR count is in many servers off. If we reached the end
                  * prematurely, accept that, exit early */
@@ -2657,6 +2653,14 @@ static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
 };
 DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);
 
+const char *format_dns_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) {
+        const char *p = dns_rcode_to_string(i);
+        if (p)
+                return p;
+
+        return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i);
+}
+
 static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
         [DNS_PROTOCOL_DNS]   = "dns",
         [DNS_PROTOCOL_MDNS]  = "mdns",