From 90ec6fa88e0bea7e1ad30cd0895ed25113969a65 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 30 Dec 2024 18:47:02 +0100 Subject: [PATCH] DSYNC RR type --- include/zone.h | 2 ++ scripts/hash.c | 1 + src/generic/type.h | 2 +- src/generic/types.h | 76 +++++++++++++++++++++++++++++++++++++++++++-- src/westmere/type.h | 2 +- tests/semantics.c | 59 +++++++++++++++++++++++++++++++++++ tests/types.c | 27 ++++++++++++++++ 7 files changed, 165 insertions(+), 4 deletions(-) diff --git a/include/zone.h b/include/zone.h index 35ee6ec..af39c2a 100644 --- a/include/zone.h +++ b/include/zone.h @@ -176,6 +176,8 @@ extern "C" { /** Service binding @rfc{9460} */ #define ZONE_TYPE_HTTPS (65u) /** Sender Policy Framework @rfc{7208} */ +#define ZONE_TYPE_DSYNC (66u) +/** Endpoint discovery for delegation synchronization @draft{ietf, dnsop-generalized-notify} */ #define ZONE_TYPE_SPF (99u) /** Node Identifier @rfc{6742} */ #define ZONE_TYPE_NID (104u) diff --git a/scripts/hash.c b/scripts/hash.c index 46c254c..a4343c9 100644 --- a/scripts/hash.c +++ b/scripts/hash.c @@ -83,6 +83,7 @@ static const tuple_t types_and_classes[] = { { "ZONEMD", 63, true }, { "SVCB", 64, true }, { "HTTPS", 65, true }, + { "DSYNC", 66, true }, { "SPF", 99, true }, { "NID", 104, true }, { "L32", 105, true }, diff --git a/src/generic/type.h b/src/generic/type.h index bab3a00..89f8304 100644 --- a/src/generic/type.h +++ b/src/generic/type.h @@ -25,7 +25,7 @@ static const struct { V(0), T(16), V(0), V(0), T(56), T(14), T(22), V(0), V(0), T(13), V(0), T(47), T(21), V(0), T(65), T(27), V(0), V(0), V(0), V(0), V(0), T(1), T(62), V(0), - V(0), C(1), V(0), T(44), V(0), V(0), T(33), V(0), + V(0), C(1), V(0), T(44), T(66), V(0), T(33), V(0), V(0), V(0), V(0), V(0), T(63), V(0), T(266), V(0), C(3), T(99), T(37), V(0), V(0), V(0), C(2), T(43), V(0), T(50), C(4), T(51), V(0), V(0), V(0), T(2), diff --git a/src/generic/types.h b/src/generic/types.h index 1e388df..d13940f 100644 --- a/src/generic/types.h +++ b/src/generic/types.h @@ -2189,6 +2189,71 @@ static int32_t parse_https_rdata( return accept_rr(parser, type, rdata); } +nonnull_all +static int32_t check_dsync_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_name(parser, type, &f[3], o+c, n-c)))) + return r; + + const uint8_t dsync_scheme = o[2]; + uint16_t dsync_type; + memcpy(&dsync_type, o, sizeof(dsync_type)); + dsync_type = be16toh(dsync_type); + if (dsync_scheme == 1 && dsync_type != ZONE_TYPE_CDS + && dsync_type != ZONE_TYPE_CDNSKEY) + SEMANTIC_ERROR(parser, "Wrong type for scheme 1 in %s", NAME(type)); + + if (c > n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_dsync_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_type(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[3], rdata, token)) < 0) + return code; + take(parser, token); + + const uint8_t dsync_scheme = parser->rdata->octets[2]; + uint16_t dsync_type; + memcpy(&dsync_type, parser->rdata->octets, sizeof(dsync_type)); + dsync_type = be16toh(dsync_type); + if (dsync_scheme == 1 && dsync_type != ZONE_TYPE_CDS + && dsync_type != ZONE_TYPE_CDNSKEY) + SEMANTIC_ERROR(parser, "Wrong type for scheme 1 in %s", NAME(type)); + + return accept_rr(parser, type, rdata); +} + nonnull_all static int32_t check_nid_rr( parser_t *parser, const type_info_t *type, const rdata_t *rdata) @@ -2847,6 +2912,13 @@ static const rdata_info_t https_rdata_fields[] = { FIELD("params") }; +static const rdata_info_t dsync_rdata_fields[] = { + FIELD("rrtype"), + FIELD("scheme"), + FIELD("port"), + FIELD("target") +}; + static const rdata_info_t spf_rdata_fields[] = { FIELD("text") }; @@ -3066,8 +3138,8 @@ static const type_info_t types[] = { check_svcb_rr, parse_svcb_rdata), TYPE("HTTPS", ZONE_TYPE_HTTPS, ZONE_CLASS_IN, FIELDS(https_rdata_fields), check_https_rr, parse_https_rdata), - - UNKNOWN_TYPE(66), + TYPE("DSYNC", ZONE_TYPE_DSYNC, ZONE_CLASS_ANY, FIELDS(dsync_rdata_fields), + check_dsync_rr, parse_dsync_rdata), UNKNOWN_TYPE(67), UNKNOWN_TYPE(68), UNKNOWN_TYPE(69), diff --git a/src/westmere/type.h b/src/westmere/type.h index b3403e9..efbe615 100644 --- a/src/westmere/type.h +++ b/src/westmere/type.h @@ -25,7 +25,7 @@ static const struct { V(0), T(16), V(0), V(0), T(56), T(14), T(22), V(0), V(0), T(13), V(0), T(47), T(21), V(0), T(65), T(27), V(0), V(0), V(0), V(0), V(0), T(1), T(62), V(0), - V(0), C(1), V(0), T(44), V(0), V(0), T(33), V(0), + V(0), C(1), V(0), T(44), T(66), V(0), T(33), V(0), V(0), V(0), V(0), V(0), T(63), V(0), T(266), V(0), C(3), T(99), T(37), V(0), V(0), V(0), C(2), T(43), V(0), T(50), C(4), T(51), V(0), V(0), V(0), T(2), diff --git a/tests/semantics.c b/tests/semantics.c index 8bd6d57..c0d1cc2 100644 --- a/tests/semantics.c +++ b/tests/semantics.c @@ -177,3 +177,62 @@ void zonemd_digest_lengths(void **state) assert_int_equal(code, tests[i].code); } } + +/*!cmocka */ +void dsync_scheme_types(void **state) +{ + static const char fmt[] = + "example.com. 86400 IN DSYNC %s %d 5359 ( type-scanner.example.net. )"; + static const char hex_fmt[] = + "example.com. 86400 CLASS1 TYPE66 \\# 31 %.4x %.2x 14ef " + "( 0c747970652d7363616e6e6572076578616d706c65036e657400 )"; + + static const struct { + const char* dsync_type_str; + uint16_t dsync_type; + uint8_t dsync_scheme; + int32_t code; + } tests[] = { + // Schema 0: Reserved + { "CDS" , ZONE_TYPE_CDS , 0, ZONE_SUCCESS }, + { "TYPE59" , 59 /* CDS */ , 0, ZONE_SUCCESS }, + { "CDNSKEY", ZONE_TYPE_CDNSKEY, 0, ZONE_SUCCESS }, + { "TYPE60" , 60 /* CDNSKEY */ , 0, ZONE_SUCCESS }, + { "TXT" , ZONE_TYPE_TXT , 0, ZONE_SUCCESS }, + { "TYPE16" , 16 /* TXT */ , 0, ZONE_SUCCESS }, + // Scheme 1: only CDS and CDNSKEY + { "CDS" , ZONE_TYPE_CDS , 1, ZONE_SUCCESS }, + { "TYPE59" , 59 /* CDS */ , 1, ZONE_SUCCESS }, + { "CDNSKEY", ZONE_TYPE_CDNSKEY, 1, ZONE_SUCCESS }, + { "TYPE60" , 60 /* CDNSKEY */ , 1, ZONE_SUCCESS }, + { "TXT" , ZONE_TYPE_TXT , 1, ZONE_SEMANTIC_ERROR }, + { "TYPE16" , 16 /* TXT */ , 1, ZONE_SEMANTIC_ERROR }, + // Other schemes, anything goes + { "CDS" , ZONE_TYPE_CDS , 2, ZONE_SUCCESS }, + { "TYPE59" , 59 /* CDS */ , 2, ZONE_SUCCESS }, + { "CDNSKEY", ZONE_TYPE_CDNSKEY, 2, ZONE_SUCCESS }, + { "TYPE60" , 60 /* CDNSKEY */ , 2, ZONE_SUCCESS }, + { "TXT" , ZONE_TYPE_TXT , 2, ZONE_SUCCESS }, + { "TYPE16" , 16 /* TXT */ , 2, ZONE_SUCCESS } + }; + + (void)state; + + int32_t code; + + for (size_t i=0, n = sizeof(tests)/sizeof(tests[0]); i < n; i++) { + char buf[512]; + const char *dsync_type_str = tests[i].dsync_type_str; + const uint16_t dsync_type = tests[i].dsync_type; + const uint8_t dsync_scheme = tests[i].dsync_scheme; + + snprintf(buf, sizeof(buf), fmt, dsync_type_str, (int)dsync_scheme); + code = parse_digest(buf); + assert_int_equal(code, tests[i].code); + + /* No need to htobe16(dsync_type), since %.4x is "written" big endian */ + snprintf(buf, sizeof(buf), hex_fmt, (int)dsync_type, (int)dsync_scheme); + code = parse_digest(buf); + assert_int_equal(code, tests[i].code); + } +} diff --git a/tests/types.c b/tests/types.c index e292cc5..61a6a04 100644 --- a/tests/types.c +++ b/tests/types.c @@ -790,6 +790,31 @@ static const rdata_t svcb_rdata = 3, 'f', 'o', 'o', 0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00); +static const char dsync_text[] = + PAD("foo. DSYNC CDS 1 5359 cds-scanner.example.net."); +static const char dsync_generic_text[] = + PAD("foo. DSYNC \\# 30 " + /* type */ + "003b" + /* scheme */ + "01" + /* port */ + "14ef" + /* target */ + "0b6364732d7363616e6e6572 076578616d706c65 036e6574 00"); +static const rdata_t dsync_rdata = + RDATA(/* type */ + 0x00, 0x3b, + /* scheme */ + 0x01, + /* port */ + 0x14, 0xef, + /* target */ + 11, 'c', 'd', 's', '-', 's', 'c', 'a', 'n', 'n', 'e', 'r', + 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 3, 'n', 'e', 't', + 0); + static const char spf_text[] = PAD("foo. SPF \"v=spf1 +all\""); static const char spf_generic_text[] = @@ -977,6 +1002,8 @@ static const test_t tests[] = { { ZONE_TYPE_ZONEMD, zonemd_text, &zonemd_rdata }, { ZONE_TYPE_ZONEMD, zonemd_generic_text, &zonemd_rdata }, { ZONE_TYPE_SVCB, svcb_text, &svcb_rdata }, + { ZONE_TYPE_DSYNC, dsync_text, &dsync_rdata }, + { ZONE_TYPE_DSYNC, dsync_generic_text, &dsync_rdata }, { ZONE_TYPE_SPF, spf_text, &spf_rdata }, { ZONE_TYPE_SPF, spf_generic_text, &spf_rdata }, { ZONE_TYPE_NID, nid_text, &nid_rdata },