]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: properly decode NAPTR RRs 31580/head
authorLennart Poettering <lennart@poettering.net>
Fri, 1 Mar 2024 22:36:51 +0000 (23:36 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 5 Mar 2024 14:46:31 +0000 (15:46 +0100)
Fixes: #18126
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/test-resolved-packet.c
src/shared/varlink-io.systemd.Resolve.Monitor.c

index a7d04449b2f78bb33bd4bb36d8268fdbac79a4b9..e6267400532d1ac4b9d67624648402fe94a63b99 100644 (file)
@@ -1220,6 +1220,30 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                 r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL);
                 break;
 
+        case DNS_TYPE_NAPTR:
+                r = dns_packet_append_uint16(p, rr->naptr.order, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint16(p, rr->naptr.preference, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_string(p, rr->naptr.flags, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_string(p, rr->naptr.services, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_string(p, rr->naptr.regexp, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_name(p, rr->naptr.replacement, /* allow_compression= */ false, /* canonical_candidate= */ true, NULL);
+                break;
+
         case DNS_TYPE_OPT:
         case DNS_TYPE_OPENPGPKEY:
         case _DNS_TYPE_INVALID: /* unparsable */
@@ -2247,6 +2271,30 @@ int dns_packet_read_rr(
 
                 break;
 
+        case DNS_TYPE_NAPTR:
+                r = dns_packet_read_uint16(p, &rr->naptr.order, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_uint16(p, &rr->naptr.preference, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_string(p, &rr->naptr.flags, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_string(p, &rr->naptr.services, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_string(p, &rr->naptr.regexp, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dns_packet_read_name(p, &rr->naptr.replacement, /* allow_compressed= */ false, NULL);
+                break;
+
         case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
         case DNS_TYPE_OPENPGPKEY:
         default:
index 7d824ee806526f6338609fdfe7b69b795d60922f..ca30508ff4c456611e87a0c3f42d37cda4c46313 100644 (file)
@@ -481,6 +481,13 @@ static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
                         free(rr->caa.value);
                         break;
 
+                case DNS_TYPE_NAPTR:
+                        free(rr->naptr.flags);
+                        free(rr->naptr.services);
+                        free(rr->naptr.regexp);
+                        free(rr->naptr.replacement);
+                        break;
+
                 case DNS_TYPE_OPENPGPKEY:
                 default:
                         if (!rr->unparsable)
@@ -694,6 +701,17 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
                        streq(a->caa.tag, b->caa.tag) &&
                        FIELD_EQUAL(a->caa, b->caa, value);
 
+        case DNS_TYPE_NAPTR:
+                r = dns_name_equal(a->naptr.replacement, b->naptr.replacement);
+                if (r <= 0)
+                        return r;
+
+                return a->naptr.order == b->naptr.order &&
+                        a->naptr.preference == b->naptr.preference &&
+                        streq(a->naptr.flags, b->naptr.flags) &&
+                        streq(a->naptr.services, b->naptr.services) &&
+                        streq(a->naptr.regexp, b->naptr.regexp);
+
         case DNS_TYPE_OPENPGPKEY:
         default:
                 return FIELD_EQUAL(a->generic, b->generic, data);
@@ -1263,6 +1281,31 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
                         return NULL;
                 break;
 
+        case DNS_TYPE_NAPTR: {
+                _cleanup_free_ char *tt = NULL, *ttt = NULL;
+
+                t = octescape(rr->naptr.flags, SIZE_MAX);
+                if (!t)
+                        return NULL;
+
+                tt = octescape(rr->naptr.services, SIZE_MAX);
+                if (!tt)
+                        return NULL;
+
+                ttt = octescape(rr->naptr.regexp, SIZE_MAX);
+                if (!ttt)
+                        return NULL;
+
+                if (asprintf(&s, "%" PRIu16 " %" PRIu16 " \"%s\" \"%s\" \"%s\" %s.",
+                             rr->naptr.order,
+                             rr->naptr.preference,
+                             t,
+                             tt,
+                             ttt,
+                             rr->naptr.replacement) < 0)
+                        return NULL;
+                break;
+        }
         default:
                 /* Format as documented in RFC 3597, Section 5 */
                 if (rr->generic.data_size == 0)
@@ -1588,6 +1631,15 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *
                 siphash24_compress_safe(rr->caa.value, rr->caa.value_size, state);
                 break;
 
+        case DNS_TYPE_NAPTR:
+                siphash24_compress_typesafe(rr->naptr.order, state);
+                siphash24_compress_typesafe(rr->naptr.preference, state);
+                string_hash_func(rr->naptr.flags, state);
+                string_hash_func(rr->naptr.services, state);
+                string_hash_func(rr->naptr.regexp, state);
+                dns_name_hash_func(rr->naptr.replacement, state);
+                break;
+
         case DNS_TYPE_OPENPGPKEY:
         default:
                 siphash24_compress_safe(rr->generic.data, rr->generic.data_size, state);
@@ -1806,6 +1858,23 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
                         return NULL;
                 break;
 
+        case DNS_TYPE_NAPTR:
+                copy->naptr.order = rr->naptr.order;
+                copy->naptr.preference = rr->naptr.preference;
+                copy->naptr.flags = strdup(rr->naptr.flags);
+                if (!copy->naptr.flags)
+                        return NULL;
+                copy->naptr.services = strdup(rr->naptr.services);
+                if (!copy->naptr.services)
+                        return NULL;
+                copy->naptr.regexp = strdup(rr->naptr.regexp);
+                if (!copy->naptr.regexp)
+                        return NULL;
+                copy->naptr.replacement = strdup(rr->naptr.replacement);
+                if (!copy->naptr.replacement)
+                        return NULL;
+                break;
+
         case DNS_TYPE_OPT:
         default:
                 copy->generic.data = memdup(rr->generic.data, rr->generic.data_size);
@@ -2352,6 +2421,18 @@ int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
                                                   JSON_BUILD_PAIR("tag", JSON_BUILD_STRING(rr->caa.tag)),
                                                   JSON_BUILD_PAIR("value", JSON_BUILD_OCTESCAPE(rr->caa.value, rr->caa.value_size))));
 
+        case DNS_TYPE_NAPTR:
+                return json_build(ret,
+                                  JSON_BUILD_OBJECT(
+                                                  JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
+                                                  JSON_BUILD_PAIR("order", JSON_BUILD_UNSIGNED(rr->naptr.order)),
+                                                  JSON_BUILD_PAIR("preference", JSON_BUILD_UNSIGNED(rr->naptr.preference)),
+                                                  /* NB: we name this flags field here naptrFlags, because there's already another "flags" field (for example in CAA) which has a different type */
+                                                  JSON_BUILD_PAIR("naptrFlags", JSON_BUILD_STRING(rr->naptr.flags)),
+                                                  JSON_BUILD_PAIR("services", JSON_BUILD_STRING(rr->naptr.services)),
+                                                  JSON_BUILD_PAIR("regexp", JSON_BUILD_STRING(rr->naptr.regexp)),
+                                                  JSON_BUILD_PAIR("replacement", JSON_BUILD_STRING(rr->naptr.replacement))));
+
         default:
                 /* Can't provide broken-down format */
                 *ret = NULL;
index 961d3c785265df1897e50a850789665b211ac482..8ad4009ebf73683c16849f57fe0766c69520f4fa 100644 (file)
@@ -270,6 +270,16 @@ struct DnsResourceRecord {
 
                         uint8_t flags;
                 } caa;
+
+                /* https://datatracker.ietf.org/doc/html/rfc2915 */
+                struct {
+                        uint16_t order;
+                        uint16_t preference;
+                        char *flags;
+                        char *services;
+                        char *regexp;
+                        char *replacement;
+                } naptr;
         };
 
         /* Note: fields should be ordered to minimize alignment gaps. Use pahole! */
index dd8c969b14b55fc1166b73cb350d6e3cd082d952..8a65ea0f5e78446107776ff35fb5f959a3799f6e 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "hexdecoct.h"
 #include "log.h"
 #include "resolved-dns-packet.h"
 #include "tests.h"
@@ -23,4 +24,190 @@ TEST(dns_packet_new) {
         assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1, DNS_PACKET_SIZE_MAX) == -EFBIG);
 }
 
+TEST(naptr) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
+        static const char twilio_reply[] =
+                "Sq+BgAABAAkAAAABBnR3aWxpbwNjb20AACMAAcAMACMAAQAABwgAMgAUAAoBUwdTSVArRDJUAARf"
+                "c2lwBF90Y3AEcHN0bgdpZTEtdG54BnR3aWxpbwNjb20AwAwAIwABAAAHCAAyAAoACgFTB1NJUCtE"
+                "MlUABF9zaXAEX3VkcARwc3RuB3VzMi10bngGdHdpbGlvA2NvbQDADAAjAAEAAAcIADQAFAAKAVMI"
+                "U0lQUytEMlQABV9zaXBzBF90Y3AEcHN0bgd1czEtdG54BnR3aWxpbwNjb20AwAwAIwABAAAHCAAy"
+                "AAoACgFTB1NJUCtEMlUABF9zaXAEX3VkcARwc3RuB2llMS10bngGdHdpbGlvA2NvbQDADAAjAAEA"
+                "AAcIADIAFAAKAVMHU0lQK0QyVAAEX3NpcARfdGNwBHBzdG4HdXMyLXRueAZ0d2lsaW8DY29tAMAM"
+                "ACMAAQAABwgANAAUAAoBUwhTSVBTK0QyVAAFX3NpcHMEX3RjcARwc3RuB3VzMi10bngGdHdpbGlv"
+                "A2NvbQDADAAjAAEAAAcIADQAFAAKAVMIU0lQUytEMlQABV9zaXBzBF90Y3AEcHN0bgdpZTEtdG54"
+                "BnR3aWxpbwNjb20AwAwAIwABAAAHCAAyAAoACgFTB1NJUCtEMlUABF9zaXAEX3VkcARwc3RuB3Vz"
+                "MS10bngGdHdpbGlvA2NvbQDADAAjAAEAAAcIADIAFAAKAVMHU0lQK0QyVAAEX3NpcARfdGNwBHBz"
+                "dG4HdXMxLXRueAZ0d2lsaW8DY29tAAAAKQIAAAAAAAAA";
+
+        static const char twilio_reply_string[] =
+                "20 10 \"S\" \"SIP+D2T\" \"\" _sip._tcp.pstn.ie1-tnx.twilio.com.\n"
+                "10 10 \"S\" \"SIP+D2U\" \"\" _sip._udp.pstn.us2-tnx.twilio.com.\n"
+                "20 10 \"S\" \"SIPS+D2T\" \"\" _sips._tcp.pstn.us1-tnx.twilio.com.\n"
+                "10 10 \"S\" \"SIP+D2U\" \"\" _sip._udp.pstn.ie1-tnx.twilio.com.\n"
+                "20 10 \"S\" \"SIP+D2T\" \"\" _sip._tcp.pstn.us2-tnx.twilio.com.\n"
+                "20 10 \"S\" \"SIPS+D2T\" \"\" _sips._tcp.pstn.us2-tnx.twilio.com.\n"
+                "20 10 \"S\" \"SIPS+D2T\" \"\" _sips._tcp.pstn.ie1-tnx.twilio.com.\n"
+                "10 10 \"S\" \"SIP+D2U\" \"\" _sip._udp.pstn.us1-tnx.twilio.com.\n"
+                "20 10 \"S\" \"SIP+D2T\" \"\" _sip._tcp.pstn.us1-tnx.twilio.com.\n";
+
+        static const char twilio_reply_json[] =
+                "[\n"
+                "        {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._tcp.pstn.ie1-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 10,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2U\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._udp.pstn.us2-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIPS+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sips._tcp.pstn.us1-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 10,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2U\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._udp.pstn.ie1-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._tcp.pstn.us2-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIPS+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sips._tcp.pstn.us2-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIPS+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sips._tcp.pstn.ie1-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 10,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2U\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._udp.pstn.us1-tnx.twilio.com\"\n"
+                "       },\n"
+                "       {\n"
+                "               \"key\" : {\n"
+                "                       \"class\" : 1,\n"
+                "                       \"type\" : 35,\n"
+                "                       \"name\" : \"twilio.com\"\n"
+                "               },\n"
+                "               \"order\" : 20,\n"
+                "               \"preference\" : 10,\n"
+                "               \"naptrFlags\" : \"S\",\n"
+                "               \"services\" : \"SIP+D2T\",\n"
+                "               \"regexp\" : \"\",\n"
+                "               \"replacement\" : \"_sip._tcp.pstn.us1-tnx.twilio.com\"\n"
+                "       }\n"
+                "]\n";
+
+        _cleanup_free_ void *buf = NULL;
+        size_t sz = 0;
+
+        assert_se(unbase64mem(twilio_reply, &buf, &sz) >= 0);
+
+        assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, sz, DNS_PACKET_SIZE_MAX) == 0);
+        assert_se(p->allocated >= sz);
+
+        memcpy(DNS_PACKET_DATA(p), buf, sz);
+        p->size = sz;
+
+        assert_se(dns_packet_extract(p) >= 0);
+
+        _cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
+        _cleanup_free_ char *joined = NULL;
+        DnsResourceRecord *rr;
+        DNS_ANSWER_FOREACH(rr, p->answer) {
+                const char *s;
+
+                s = ASSERT_PTR(dns_resource_record_to_string(rr));
+                printf("%s\n", s);
+
+                assert_se(strextend(&joined, s, "\n"));
+
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+                assert_se(dns_resource_record_to_json(rr, &v) >= 0);
+
+                assert_se(json_variant_append_array(&a, v) >= 0);
+        }
+
+        assert(streq(joined, twilio_reply_string));
+
+        _cleanup_(json_variant_unrefp) JsonVariant *parsed = NULL;
+        assert_se(json_parse(twilio_reply_json, /* flags= */ 0, &parsed, /* ret_line= */ NULL, /* ret_column= */ NULL) >= 0);
+
+        assert_se(json_variant_equal(parsed, a));
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index ea21b437fa78f9fcc991fee206e9862795f86649..3ea97432db3431db7ac365b5c09c405853d60db9 100644 (file)
@@ -61,7 +61,13 @@ static VARLINK_DEFINE_STRUCT_TYPE(
                 VARLINK_DEFINE_FIELD(tag, VARLINK_STRING, VARLINK_NULLABLE),
                 VARLINK_DEFINE_FIELD(value, VARLINK_STRING, VARLINK_NULLABLE),
                 VARLINK_DEFINE_FIELD(target, VARLINK_STRING, VARLINK_NULLABLE),
-                VARLINK_DEFINE_FIELD(params, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY));
+                VARLINK_DEFINE_FIELD(params, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY),
+                VARLINK_DEFINE_FIELD(order, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(preference, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(naptrFlags, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(services, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(regexp, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(replacement, VARLINK_STRING, VARLINK_NULLABLE));
 
 static VARLINK_DEFINE_STRUCT_TYPE(
                 ResourceRecordArray,