diff --git a/CHANGES b/CHANGES index 8878e399b5..1ee12a8191 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5231. [protocol] Add support for displaying CLIENT-TAG and SERVER-TAG. + [GL #960] + 5229. [protocol] Enforce known SSHFP fingerprint lengths. [GL #852] 5228. [cleanup] If trusted-keys and managed-keys are configured diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 9249ebd021..531240b53f 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1411,6 +1411,8 @@ dig_ednsoptname_t optnames[] = { { 12, "PAD" }, /* shorthand */ { 13, "CHAIN" }, /* RFC 7901 */ { 14, "KEY-TAG" }, /* RFC 8145 */ + { 16, "CLIENT-TAG" }, /* draft-bellis-dnsop-edns-tags */ + { 17, "SERVER-TAG" }, /* draft-bellis-dnsop-edns-tags */ { 26946, "DEVICEID" }, /* Brian Hartvigsen */ }; diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index aecc104a6f..22ab4f66c2 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -548,6 +548,54 @@ if [ -x "$DIG" ] ; then if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status+ret)) + n=$((n+1)) + echo_i "check that dig processes +ednsopt=client-tag:value ($n)" + dig_with_opts @10.53.0.3 +ednsopt=client-tag:0001 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; CLIENT-TAG: 1$" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null && ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + + n=$((n+1)) + echo_i "check that FORMERR is returned for a too short client-tag ($n)" + dig_with_opts @10.53.0.3 +ednsopt=client-tag:01 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; CLIENT-TAG" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + + n=$((n+1)) + echo_i "check that FORMERR is returned for a too long client-tag ($n)" + dig_with_opts @10.53.0.3 +ednsopt=client-tag:000001 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; CLIENT-TAG" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + + n=$((n+1)) + echo_i "check that dig processes +ednsopt=server-tag:value ($n)" + dig_with_opts @10.53.0.3 +ednsopt=server-tag:0001 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; SERVER-TAG: 1$" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null && ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + + n=$((n+1)) + echo_i "check that FORMERR is returned for a too short server-tag ($n)" + dig_with_opts @10.53.0.3 +ednsopt=server-tag:01 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; SERVER-TAG" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + + n=$((n+1)) + echo_i "check that FORMERR is returned for a too long server-tag ($n)" + dig_with_opts @10.53.0.3 +ednsopt=server-tag:000001 a.example +qr > dig.out.test$n 2>&1 || ret=1 + grep "; SERVER-TAG" dig.out.test$n > /dev/null || ret=1 + grep "status: FORMERR" dig.out.test$n > /dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status+ret)) + n=$((n+1)) echo_i "check that dig handles malformed option '+ednsopt=:' gracefully ($n)" ret=0 diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index b933a57c91..1f793b4809 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -106,6 +106,8 @@ #define DNS_OPT_TCP_KEEPALIVE 11 /*%< TCP keepalive opt code */ #define DNS_OPT_PAD 12 /*%< PAD opt code */ #define DNS_OPT_KEY_TAG 14 /*%< Key tag opt code */ +#define DNS_OPT_CLIENT_TAG 16 /*%< Client tag opt code */ +#define DNS_OPT_SERVER_TAG 17 /*%< Server tag opt code */ /*%< Experimental options [65001...65534] as per RFC6891 */ diff --git a/lib/dns/message.c b/lib/dns/message.c index 4856537381..b806aed05d 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3584,6 +3584,30 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, ADD_STRING(target, "\n"); continue; } + } else if (optcode == DNS_OPT_CLIENT_TAG) { + uint16_t id; + INDENT(style); + ADD_STRING(target, "CLIENT-TAG"); + if (optlen == 2U) { + id = isc_buffer_getuint16(&optbuf); + snprintf(buf, sizeof(buf), ": %u\n", + id); + ADD_STRING(target, buf); + optlen -= 2; + continue; + } + } else if (optcode == DNS_OPT_SERVER_TAG) { + uint16_t id; + INDENT(style); + ADD_STRING(target, "SERVER-TAG"); + if (optlen == 2U) { + id = isc_buffer_getuint16(&optbuf); + snprintf(buf, sizeof(buf), ": %u\n", + id); + ADD_STRING(target, buf); + optlen -= 2; + continue; + } } else { INDENT(style); ADD_STRING(target, "OPT: "); @@ -3853,6 +3877,28 @@ dns_message_pseudosectiontotext(dns_message_t *msg, ADD_STRING(target, "\n"); continue; } + } else if (optcode == DNS_OPT_CLIENT_TAG) { + uint16_t id; + ADD_STRING(target, "; CLIENT-TAG"); + if (optlen == 2U) { + id = isc_buffer_getuint16(&optbuf); + snprintf(buf, sizeof(buf), ": %u\n", + id); + ADD_STRING(target, buf); + optlen -= 2; + continue; + } + } else if (optcode == DNS_OPT_SERVER_TAG) { + uint16_t id; + ADD_STRING(target, "; SERVER-TAG"); + if (optlen == 2U) { + id = isc_buffer_getuint16(&optbuf); + snprintf(buf, sizeof(buf), ": %u\n", + id); + ADD_STRING(target, buf); + optlen -= 2; + continue; + } } else { ADD_STRING(target, "; OPT="); snprintf(buf, sizeof(buf), "%u", optcode); diff --git a/lib/dns/rdata/generic/opt_41.c b/lib/dns/rdata/generic/opt_41.c index b102cf2f42..b8b7f53656 100644 --- a/lib/dns/rdata/generic/opt_41.c +++ b/lib/dns/rdata/generic/opt_41.c @@ -99,19 +99,22 @@ fromwire_opt(ARGS_FROMWIRE) { UNUSED(options); isc_buffer_activeregion(source, &sregion); - if (sregion.length == 0) + if (sregion.length == 0) { return (ISC_R_SUCCESS); + } total = 0; while (sregion.length != 0) { - if (sregion.length < 4) + if (sregion.length < 4) { return (ISC_R_UNEXPECTEDEND); + } opt = uint16_fromregion(&sregion); isc_region_consume(&sregion, 2); length = uint16_fromregion(&sregion); isc_region_consume(&sregion, 2); total += 4; - if (sregion.length < length) + if (sregion.length < length) { return (ISC_R_UNEXPECTEDEND); + } switch (opt) { case DNS_OPT_CLIENT_SUBNET: { uint16_t family; @@ -119,8 +122,9 @@ fromwire_opt(ARGS_FROMWIRE) { uint8_t scope; uint8_t addrbytes; - if (length < 4) + if (length < 4) { return (DNS_R_OPTERR); + } family = uint16_fromregion(&sregion); isc_region_consume(&sregion, 2); addrlen = uint8_fromregion(&sregion); @@ -139,29 +143,34 @@ fromwire_opt(ARGS_FROMWIRE) { * lengths don't make sense because the * family is unknown. */ - if (addrlen != 0U || scope != 0U) + if (addrlen != 0U || scope != 0U) { return (DNS_R_OPTERR); + } break; case 1: - if (addrlen > 32U || scope > 32U) + if (addrlen > 32U || scope > 32U) { return (DNS_R_OPTERR); + } break; case 2: - if (addrlen > 128U || scope > 128U) + if (addrlen > 128U || scope > 128U) { return (DNS_R_OPTERR); + } break; default: return (DNS_R_OPTERR); } addrbytes = (addrlen + 7) / 8; - if (addrbytes + 4 != length) + if (addrbytes + 4 != length) { return (DNS_R_OPTERR); + } if (addrbytes != 0U && (addrlen % 8) != 0) { uint8_t bits = ~0U << (8 - (addrlen % 8)); bits &= sregion.base[addrbytes - 1]; - if (bits != sregion.base[addrbytes - 1]) + if (bits != sregion.base[addrbytes - 1]) { return (DNS_R_OPTERR); + } } isc_region_consume(&sregion, addrbytes); break; @@ -170,18 +179,29 @@ fromwire_opt(ARGS_FROMWIRE) { /* * Request has zero length. Response is 32 bits. */ - if (length != 0 && length != 4) + if (length != 0 && length != 4) { return (DNS_R_OPTERR); + } isc_region_consume(&sregion, length); break; case DNS_OPT_COOKIE: - if (length != 8 && (length < 16 || length > 40)) + if (length != 8 && (length < 16 || length > 40)) { return (DNS_R_OPTERR); + } isc_region_consume(&sregion, length); break; case DNS_OPT_KEY_TAG: - if (length == 0 || (length % 2) != 0) + if (length == 0 || (length % 2) != 0) { return (DNS_R_OPTERR); + } + isc_region_consume(&sregion, length); + break; + case DNS_OPT_CLIENT_TAG: + /* FALLTHROUGH */ + case DNS_OPT_SERVER_TAG: + if (length != 2) { + return (DNS_R_OPTERR); + } isc_region_consume(&sregion, length); break; default: @@ -193,8 +213,9 @@ fromwire_opt(ARGS_FROMWIRE) { isc_buffer_activeregion(source, &sregion); isc_buffer_availableregion(target, &tregion); - if (tregion.length < total) + if (tregion.length < total) { return (ISC_R_NOSPACE); + } memmove(tregion.base, sregion.base, total); isc_buffer_forward(source, total); isc_buffer_add(target, total);