mirror of
https://github.com/systemd/systemd.git
synced 2024-11-26 19:53:45 +08:00
Merge pull request #31580 from poettering/resolved-naptr
resolved: properly decode NAPTR RRs
This commit is contained in:
commit
5e575e4d92
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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! */
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user