Merge pull request #31580 from poettering/resolved-naptr

resolved: properly decode NAPTR RRs
This commit is contained in:
Luca Boccassi 2024-03-06 14:14:11 +00:00 committed by GitHub
commit 5e575e4d92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 339 additions and 1 deletions

View File

@ -451,6 +451,12 @@ char* octescape(const char *s, size_t len) {
assert(s || len == 0);
if (len == SIZE_MAX)
len = strlen(s);
if (len > (SIZE_MAX)-1/4)
return NULL;
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;

View 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:

View 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;

View 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! */

View 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);

View 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,