]> 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 50785a682389e40b853180057655419d273b91e1..991a1be458453766aa55fbfa10e914dc0d522a80 100644 (file)
@@ -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)
 
@@ -307,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;
@@ -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)
@@ -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:
@@ -1251,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)
@@ -1370,14 +1371,14 @@ 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) {
-        assert(p);
-
         _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);
+
         r = dns_packet_read_uint8(p, &c, NULL);
         if (r < 0)
                 return r;
@@ -1386,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;
@@ -1592,17 +1588,19 @@ static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t siz
         _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
         int r;
 
-        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)
@@ -1713,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;
@@ -1730,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:
@@ -1757,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;
@@ -1989,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,
@@ -2016,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
@@ -2061,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 */
@@ -2104,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,
@@ -2123,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)
@@ -2338,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)
@@ -2359,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) {
@@ -2637,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",