]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-packet.c
resolved: enable RFC9460 SVCB and HTTPS records
[thirdparty/systemd.git] / src / resolve / resolved-dns-packet.c
index 6c45b1e5ef8a3e82b6ab602f41431cc6f2411298..29d9dab060974cc78272e88dc78b8cf069ff789b 100644 (file)
@@ -1166,6 +1166,31 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                 r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL);
                 break;
 
+        case DNS_TYPE_SVCB:
+        case DNS_TYPE_HTTPS:
+                r = dns_packet_append_uint16(p, rr->svcb.priority, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_name(p, rr->svcb.target_name, false, false, NULL);
+                if (r < 0)
+                        goto fail;
+
+                LIST_FOREACH(params, i, rr->svcb.params) {
+                        r = dns_packet_append_uint16(p, i->key, NULL);
+                        if (r < 0)
+                                goto fail;
+
+                        r = dns_packet_append_uint16(p, i->length, NULL);
+                        if (r < 0)
+                                goto fail;
+
+                        r = dns_packet_append_blob(p, i->value, i->length, NULL);
+                        if (r < 0)
+                                goto fail;
+                }
+                break;
+
         case DNS_TYPE_CAA:
                 r = dns_packet_append_uint8(p, rr->caa.flags, NULL);
                 if (r < 0)
@@ -1672,6 +1697,41 @@ static bool loc_size_ok(uint8_t size) {
         return m <= 9 && e <= 9 && (m > 0 || e == 0);
 }
 
+static bool dns_svc_param_is_valid(DnsSvcParam *i) {
+        if (!i)
+                return false;
+
+        switch (i->key) {
+        /* RFC 9460, section 7.1.1: alpn-ids must exactly fill SvcParamValue */
+        case DNS_SVC_PARAM_KEY_ALPN: {
+                size_t sz = 0;
+                if (i->length <= 0)
+                        return false;
+                while (sz < i->length)
+                        sz += 1 + i->value[sz]; /* N.B. will not overflow */
+                return sz == i->length;
+        }
+
+        /* RFC 9460, section 7.1.1: value must be empty */
+        case DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN:
+                return i->length == 0;
+
+        /* RFC 9460, section 7.2 */
+        case DNS_SVC_PARAM_KEY_PORT:
+                return i->length == 2;
+
+        /* RFC 9460, section 7.3: addrs must exactly fill SvcParamValue */
+        case DNS_SVC_PARAM_KEY_IPV4HINT:
+                return i->length % (sizeof (struct in_addr)) == 0;
+        case DNS_SVC_PARAM_KEY_IPV6HINT:
+                return i->length % (sizeof (struct in6_addr)) == 0;
+
+        /* Otherwise, permit any value */
+        default:
+                return true;
+        }
+}
+
 int dns_packet_read_rr(
                 DnsPacket *p,
                 DnsResourceRecord **ret,
@@ -2106,6 +2166,52 @@ int dns_packet_read_rr(
 
                 break;
 
+        case DNS_TYPE_SVCB:
+        case DNS_TYPE_HTTPS:
+                r = dns_packet_read_uint16(p, &rr->svcb.priority, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_name(p, &rr->svcb.target_name, false /* uncompressed */, NULL);
+                if (r < 0)
+                        return r;
+
+                DnsSvcParam *last = NULL;
+                while (p->rindex - offset < rdlength) {
+                        _cleanup_free_ DnsSvcParam *i = NULL;
+                        uint16_t svc_param_key;
+                        uint16_t sz;
+
+                        r = dns_packet_read_uint16(p, &svc_param_key, NULL);
+                        if (r < 0)
+                                return r;
+                        /* RFC 9460, section 2.2 says we must consider an RR malformed if SvcParamKeys are
+                         * not in strictly increasing order */
+                        if (last && last->key >= svc_param_key)
+                                return -EBADMSG;
+
+                        r = dns_packet_read_uint16(p, &sz, NULL);
+                        if (r < 0)
+                                return r;
+
+                        i = malloc0(offsetof(DnsSvcParam, value) + sz);
+                        if (!i)
+                                return -ENOMEM;
+
+                        i->key = svc_param_key;
+                        i->length = sz;
+                        r = dns_packet_read_blob(p, &i->value, sz, NULL);
+                        if (r < 0)
+                                return r;
+                        if (!dns_svc_param_is_valid(i))
+                                return -EBADMSG;
+
+                        LIST_INSERT_AFTER(params, rr->svcb.params, last, i);
+                        last = TAKE_PTR(i);
+                }
+
+                break;
+
         case DNS_TYPE_CAA:
                 r = dns_packet_read_uint8(p, &rr->caa.flags, NULL);
                 if (r < 0)