chiark / gitweb /
resolved: make TXT RR generation and parsing more in-line with RFC 6763, section 6.1
[elogind.git] / src / resolve / resolved-dns-packet.c
index e5d07b3b1ff06666e4be428fd4f78ac31ec62b48..cf5b6189c571daa01de3465d970fa71382fdd9f7 100644 (file)
@@ -547,10 +547,19 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
         case DNS_TYPE_TXT: {
                 char **s;
 
-                STRV_FOREACH(s, rr->txt.strings) {
-                        r = dns_packet_append_string(p, *s, NULL);
+                if (strv_isempty(rr->txt.strings)) {
+                        /* RFC 6763, section 6.1 suggests to generate
+                         * single empty string for an empty array. */
+
+                        r = dns_packet_append_string(p, "", NULL);
                         if (r < 0)
                                 goto fail;
+                } else {
+                        STRV_FOREACH(s, rr->txt.strings) {
+                                r = dns_packet_append_string(p, *s, NULL);
+                                if (r < 0)
+                                        goto fail;
+                        }
                 }
 
                 r = 0;
@@ -860,7 +869,7 @@ fail:
 
 int dns_packet_read_name(DnsPacket *p, char **_ret,
                          bool allow_compression, size_t *start) {
-        size_t saved_rindex, after_rindex = 0;
+        size_t saved_rindex, after_rindex = 0, jump_barrier;
         _cleanup_free_ char *ret = NULL;
         size_t n = 0, allocated = 0;
         bool first = true;
@@ -870,6 +879,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret,
         assert(_ret);
 
         saved_rindex = p->rindex;
+        jump_barrier = p->rindex;
 
         for (;;) {
                 uint8_t c, d;
@@ -916,7 +926,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret,
                                 goto fail;
 
                         ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
-                        if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
+                        if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) {
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -924,9 +934,13 @@ int dns_packet_read_name(DnsPacket *p, char **_ret,
                         if (after_rindex == 0)
                                 after_rindex = p->rindex;
 
+                        /* Jumps are limited to a "prior occurence" (RFC-1035 4.1.4) */
+                        jump_barrier = ptr;
                         p->rindex = ptr;
-                } else
+                } else {
+                        r = -EBADMSG;
                         goto fail;
+                }
         }
 
         if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
@@ -1106,22 +1120,31 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 break;
 
         case DNS_TYPE_SPF: /* exactly the same as TXT */
-        case DNS_TYPE_TXT: {
-                char *s;
+        case DNS_TYPE_TXT:
+                if (rdlength <= 0) {
+                        /* RFC 6763, section 6.1 suggests to treat
+                         * empty TXT RRs as equivalent to a TXT record
+                         * with a single empty string. */
 
-                while (p->rindex < offset + rdlength) {
-                        r = dns_packet_read_string(p, &s, NULL);
+                        r = strv_extend(&rr->txt.strings, "");
                         if (r < 0)
                                 goto fail;
+                } else {
+                        while (p->rindex < offset + rdlength) {
+                                char *s;
 
-                        r = strv_consume(&rr->txt.strings, s);
-                        if (r < 0)
-                                goto fail;
+                                r = dns_packet_read_string(p, &s, NULL);
+                                if (r < 0)
+                                        goto fail;
+
+                                r = strv_consume(&rr->txt.strings, s);
+                                if (r < 0)
+                                        goto fail;
+                        }
                 }
 
                 r = 0;
                 break;
-        }
 
         case DNS_TYPE_A:
                 r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);