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 */
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:
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)
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);
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)
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);
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);
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;
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! */
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "hexdecoct.h"
#include "log.h"
#include "resolved-dns-packet.h"
#include "tests.h"
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);
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,