From e7ef4e41ebd55ed1cfb2b2652b66a0f026aea1c7 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 30 Oct 2024 10:41:21 +1100 Subject: [PATCH 01/12] Collapse common switch cases when emitting EDNS options The CHAIN and REPORT-CHANNEL EDNS options are both domain names, so they can be combined. THE CLIENT-TAG and SERVER-TAG EDNS options are both 16 bit integers, so they can be combined. --- lib/dns/message.c | 44 ++------------------------------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/lib/dns/message.c b/lib/dns/message.c index dd9921eb95..3c80e45c55 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3766,6 +3766,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, } break; case DNS_OPT_CHAIN: + case DNS_OPT_REPORT_CHANNEL: if (optlen > 0U) { isc_buffer_t sb = optbuf; isc_buffer_setactive(&optbuf, optlen); @@ -3822,14 +3823,6 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, } break; case DNS_OPT_CLIENT_TAG: - if (optlen == 2U) { - uint16_t id = - isc_buffer_getuint16(&optbuf); - snprintf(buf, sizeof(buf), " %u\n", id); - ADD_STRING(target, buf); - continue; - } - break; case DNS_OPT_SERVER_TAG: if (optlen == 2U) { uint16_t id = @@ -3839,19 +3832,6 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, continue; } break; - case DNS_OPT_REPORT_CHANNEL: - if (optlen > 0U) { - isc_buffer_t sb = optbuf; - isc_buffer_setactive(&optbuf, optlen); - result = render_nameopt(&optbuf, - target); - if (result == ISC_R_SUCCESS) { - ADD_STRING(target, "\n"); - continue; - } - optbuf = sb; - } - break; case DNS_OPT_ZONEVERSION: if (optlen >= 2U) { isc_buffer_t zonebuf = optbuf; @@ -4195,6 +4175,7 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, ADD_STRING(target, "\n"); continue; case DNS_OPT_CHAIN: + case DNS_OPT_REPORT_CHANNEL: if (optlen > 0U) { isc_buffer_t sb = optbuf; isc_buffer_setactive(&optbuf, optlen); @@ -4255,14 +4236,6 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, } break; case DNS_OPT_CLIENT_TAG: - if (optlen == 2U) { - uint16_t id = - isc_buffer_getuint16(&optbuf); - snprintf(buf, sizeof(buf), " %u\n", id); - ADD_STRING(target, buf); - continue; - } - break; case DNS_OPT_SERVER_TAG: if (optlen == 2U) { uint16_t id = @@ -4272,19 +4245,6 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, continue; } break; - case DNS_OPT_REPORT_CHANNEL: - if (optlen > 0U) { - isc_buffer_t sb = optbuf; - isc_buffer_setactive(&optbuf, optlen); - result = render_nameopt(&optbuf, - target); - if (result == ISC_R_SUCCESS) { - ADD_STRING(target, "\n"); - continue; - } - optbuf = sb; - } - break; case DNS_OPT_ZONEVERSION: if (optlen >= 2U) { isc_buffer_t zonebuf = optbuf; From 280e9b7cf4b28bffa6a281bb64b43e35829645ec Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 29 Oct 2024 16:45:41 +1100 Subject: [PATCH 02/12] Add YAML escaping where needed When rendering text, such as domain names or the EXTRA-TEXT field of the EDE option, backslashes and quotation marks must be escaped to ensure that the emitted message is valid YAML. --- bin/tests/system/digdelv/tests.sh | 33 ++++++++++ lib/dns/message.c | 104 ++++++++++++++++-------------- 2 files changed, 87 insertions(+), 50 deletions(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 60f5e1f022..55de3f12a5 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -679,6 +679,26 @@ 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=chain:02002200 ($n)" + ret=0 + dig_with_opts @10.53.0.3 +ednsopt=chain:02002200 'a.\000"' +qr >dig.out.test$n 2>&1 || ret=1 + grep '; CHAIN: "\\000\\""' dig.out.test$n >/dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status + ret)) + + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "check that dig processes +ednsopt=chain:02002200 +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.3 +yaml +ednsopt=chain:02002200 'a.\000"' +qr >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS CHAIN >yamlget.out.test$n 2>&1 || ret=1 + read -r value dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS EDE EXTRA-TEXT >yamlget.out.test$n 2>&1 || ret=1 + read -r value 127)) { + if (namebuf[i] == '\\' || namebuf[i] == '"') { + ADD_STRING(target, "\\"); + } + if (isc_buffer_availablelength(target) < 1) { + return ISC_R_NOSPACE; + } + isc_buffer_putmem(target, &namebuf[i], 1); + } else { + ADD_STRING(target, "."); + } + } +cleanup: + return result; +} + +static isc_result_t +render_nameopt(isc_buffer_t *optbuf, bool yaml, isc_buffer_t *target) { dns_decompress_t dctx = DNS_DECOMPRESS_NEVER; dns_fixedname_t fixed; dns_name_t *name = dns_fixedname_initname(&fixed); @@ -3435,7 +3457,15 @@ render_nameopt(isc_buffer_t *optbuf, isc_buffer_t *target) { if (result == ISC_R_SUCCESS && isc_buffer_activelength(optbuf) == 0) { dns_name_format(name, namebuf, sizeof(namebuf)); ADD_STRING(target, " \""); - ADD_STRING(target, namebuf); + if (yaml) { + result = put_yamlstr(target, (unsigned char *)namebuf, + strlen(namebuf), false); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } else { + ADD_STRING(target, namebuf); + } ADD_STRING(target, "\""); return result; } @@ -3501,19 +3531,11 @@ render_zoneversion(dns_message_t *msg, isc_buffer_t *optbuf, INDENT(style); ADD_STRING(target, "ZONE: "); if (yaml) { - char *s = namebuf; ADD_STRING(target, "\""); - while (*s != 0) { - if (*s == '\\' || *s == '"') { - ADD_STRING(target, "\\"); - } - if (isc_buffer_availablelength(target) < 1) { - result = ISC_R_NOSPACE; - goto cleanup; - } - isc_buffer_putmem(target, (unsigned char *)s, - 1); - s++; + put_yamlstr(target, (unsigned char *)namebuf, + strlen(namebuf), false); + if (result != ISC_R_SUCCESS) { + goto cleanup; } ADD_STRING(target, "\""); } else { @@ -3546,27 +3568,23 @@ render_zoneversion(dns_message_t *msg, isc_buffer_t *optbuf, ADD_STRING(target, sep2); INDENT(style); ADD_STRING(target, "PVALUE: \""); - } else { - ADD_STRING(target, " (\""); - } - for (size_t i = 0; i < len; i++) { - if (isprint(data[i])) { - if (yaml && (data[i] == '\\' || data[i] == '"')) - { - ADD_STRING(target, "\\"); - } - if (isc_buffer_availablelength(target) < 1) { - result = ISC_R_NOSPACE; - goto cleanup; - } - isc_buffer_putmem(target, &data[i], 1); - } else { - ADD_STRING(target, "."); - } - } - if (yaml) { + put_yamlstr(target, data, len, false); ADD_STRING(target, "\""); } else { + ADD_STRING(target, " (\""); + for (size_t i = 0; i < len; i++) { + if (isprint(data[i])) { + if (isc_buffer_availablelength(target) < + 1) + { + result = ISC_R_NOSPACE; + goto cleanup; + } + isc_buffer_putmem(target, &data[i], 1); + } else { + ADD_STRING(target, "."); + } + } ADD_STRING(target, "\")"); } isc_buffer_forward(optbuf, len); @@ -3770,7 +3788,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, if (optlen > 0U) { isc_buffer_t sb = optbuf; isc_buffer_setactive(&optbuf, optlen); - result = render_nameopt(&optbuf, + result = render_nameopt(&optbuf, true, target); if (result == ISC_R_SUCCESS) { ADD_STRING(target, "\n"); @@ -3920,21 +3938,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, } else { ADD_STRING(target, "\""); } - if (isc_buffer_availablelength(target) < optlen) - { - result = ISC_R_NOSPACE; - goto cleanup; - } - for (i = 0; i < optlen; i++) { - if (isprint(optdata[i]) || - (utf8ok && optdata[i] > 127)) - { - isc_buffer_putmem( - target, &optdata[i], 1); - } else { - isc_buffer_putstr(target, "."); - } - } + put_yamlstr(target, optdata, optlen, utf8ok); if (!extra_text) { ADD_STRING(target, "\")"); } else { @@ -4179,7 +4183,7 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, if (optlen > 0U) { isc_buffer_t sb = optbuf; isc_buffer_setactive(&optbuf, optlen); - result = render_nameopt(&optbuf, + result = render_nameopt(&optbuf, false, target); if (result == ISC_R_SUCCESS) { ADD_STRING(target, "\n"); From 68cdc4774c53fb439ebb0dca4e6fc98ba69577d5 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 29 Oct 2024 14:32:54 +1100 Subject: [PATCH 03/12] Change the name and YAML format of EDNS UL The offical EDNS option name for "UL" is "UPDATE-LEASE". We now emit "UPDATE-LEASE" instead of "UL", when printing messages, but "UL" has been retained as an alias on the command line. Update leases consist of 1 or 2 values, LEASE and KEY-LEASE. These components are now emitted separately so they can be easily extracted from YAML output. Tests have been added to check YAML correctness. --- bin/dig/dighost.c | 1 + bin/tests/system/digdelv/tests.sh | 40 +++++++++++++++++++++++++------ lib/dns/message.c | 32 +++++++++++++++++-------- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index e9696ab1d2..05693cdfe2 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1363,6 +1363,7 @@ typedef struct dig_ednsoptname { dig_ednsoptname_t optnames[] = { { 1, "LLQ" }, /* draft-sekar-dns-llq */ + { 2, "UPDATE-LEASE" }, /* draft-ietf-dnssd-update-lease */ { 2, "UL" }, /* draft-ietf-dnssd-update-lease */ { 3, "NSID" }, /* RFC 5001 */ { 5, "DAU" }, /* RFC 6975 */ diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 55de3f12a5..207e407c5a 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -562,24 +562,50 @@ if [ -x "$DIG" ]; then status=$((status + ret)) n=$((n + 1)) - echo_i "checking ednsopt UL prints as expected (single lease) ($n)" + echo_i "checking ednsopt UPDATE-LEASE prints as expected (single lease) ($n)" ret=0 - dig_with_opts @10.53.0.3 +ednsopt=UL:00000e10 +qr a.example >dig.out.test$n 2>&1 || ret=1 - pat='UL: 3600 (1 hour)' + dig_with_opts @10.53.0.3 +ednsopt=UPDATE-LEASE:00000e10 +qr a.example >dig.out.test$n 2>&1 || ret=1 + pat='UPDATE-LEASE: 3600 (1 hour)' grep "$pat" dig.out.test$n >/dev/null || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) n=$((n + 1)) + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "checking ednsopt UPDATE-LEASE prints as expected (single lease) +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.3 +yaml +ednsopt=UPDATE-LEASE:00000e10 +qr a.example >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS UPDATE-LEASE LEASE >yamlget.out.test$n 2>&1 || ret=1 + read -r value dig.out.test$n 2>&1 || ret=1 - pat='UL: 3600/1209600 (1 hour/2 weeks)' + dig_with_opts @10.53.0.3 +ednsopt=UPDATE-LEASE:00000e1000127500 +qr a.example >dig.out.test$n 2>&1 || ret=1 + pat='UPDATE-LEASE: 3600/1209600 (1 hour/2 weeks)' grep "$pat" dig.out.test$n >/dev/null || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) - n=$((n + 1)) + + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "checking ednsopt UPDATE-LEASE prints as expected (split lease) +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.3 +yaml +ednsopt=UPDATE-LEASE:00000e1000127500 +qr a.example >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS UPDATE-LEASE LEASE >yamlget.out.test$n 2>&1 || ret=1 + read -r value yamlget.out.test$n 2>&1 || ret=1 + read -r value indent.count++; + secs = isc_buffer_getuint32(&optbuf); + ADD_STRING(target, "\n"); + INDENT(style); + ADD_STRING(target, "LEASE:"); snprintf(buf, sizeof(buf), " %u", secs); ADD_STRING(target, buf); - if (optlen == 8U) { - key = isc_buffer_getuint32( - &optbuf); - snprintf(buf, sizeof(buf), - "/%u", key); - ADD_STRING(target, buf); - } + ADD_STRING(target, " ("); result = dns_ttl_totext(secs, true, true, target); if (result != ISC_R_SUCCESS) { goto cleanup; } + ADD_STRING(target, ")"); + if (optlen == 8U) { - ADD_STRING(target, "/"); + key = isc_buffer_getuint32( + &optbuf); + ADD_STRING(target, "\n"); + INDENT(style); + ADD_STRING(target, + "KEY-LEASE:"); + snprintf(buf, sizeof(buf), + " %u", key); + ADD_STRING(target, buf); + + ADD_STRING(target, " ("); result = dns_ttl_totext( key, true, true, target); if (result != ISC_R_SUCCESS) { goto cleanup; } + ADD_STRING(target, ")"); } - ADD_STRING(target, ")\n"); + ADD_STRING(target, "\n"); continue; } break; From 378bc7cfa69c5f1d58551896e8aeb95e5acccd0c Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 31 Oct 2024 16:52:11 +1100 Subject: [PATCH 04/12] Use YAML comments for durations rather than parentheses This will allow the values to be parsed using standard yaml processing tools, and still provide the value in a human friendly form. --- bin/tests/system/digdelv/tests.sh | 9 ++++++--- lib/dns/message.c | 14 ++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 207e407c5a..65bf8f50f8 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -578,7 +578,8 @@ if [ -x "$DIG" ]; then dig_with_opts @10.53.0.3 +yaml +ednsopt=UPDATE-LEASE:00000e10 +qr a.example >dig.out.test$n 2>&1 || ret=1 $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS UPDATE-LEASE LEASE >yamlget.out.test$n 2>&1 || ret=1 read -r value /dev/null || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) fi @@ -599,10 +600,12 @@ if [ -x "$DIG" ]; then dig_with_opts @10.53.0.3 +yaml +ednsopt=UPDATE-LEASE:00000e1000127500 +qr a.example >dig.out.test$n 2>&1 || ret=1 $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS UPDATE-LEASE LEASE >yamlget.out.test$n 2>&1 || ret=1 read -r value /dev/null || ret=1 $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS UPDATE-LEASE KEY-LEASE >yamlget.out.test$n 2>&1 || ret=1 read -r value /dev/null || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) fi diff --git a/lib/dns/message.c b/lib/dns/message.c index 6762e2e397..0f046f78be 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3720,18 +3720,17 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, snprintf(buf, sizeof(buf), " %u", secs); ADD_STRING(target, buf); - ADD_STRING(target, " ("); + ADD_STRING(target, " # "); result = dns_ttl_totext(secs, true, true, target); if (result != ISC_R_SUCCESS) { goto cleanup; } - ADD_STRING(target, ")"); + ADD_STRING(target, "\n"); if (optlen == 8U) { key = isc_buffer_getuint32( &optbuf); - ADD_STRING(target, "\n"); INDENT(style); ADD_STRING(target, "KEY-LEASE:"); @@ -3739,16 +3738,15 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, " %u", key); ADD_STRING(target, buf); - ADD_STRING(target, " ("); + ADD_STRING(target, " # "); result = dns_ttl_totext( key, true, true, target); if (result != ISC_R_SUCCESS) { goto cleanup; } - ADD_STRING(target, ")"); + ADD_STRING(target, "\n"); } - ADD_STRING(target, "\n"); continue; } break; @@ -3774,13 +3772,13 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, secs = isc_buffer_getuint32(&optbuf); snprintf(buf, sizeof(buf), " %u", secs); ADD_STRING(target, buf); - ADD_STRING(target, " ("); + ADD_STRING(target, " # "); result = dns_ttl_totext(secs, true, true, target); if (result != ISC_R_SUCCESS) { goto cleanup; } - ADD_STRING(target, ")\n"); + ADD_STRING(target, "\n"); continue; } break; From 27e8732c1726aebc43d41e39b115d0c7eda5bb77 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 29 Oct 2024 16:01:58 +1100 Subject: [PATCH 05/12] Change the EDNS KEY-TAG YAML output format When using YAML, print the EDNS KEY-TAG as an array of integers for easier machine parsing. Check the validity of the YAML output. --- bin/tests/system/digdelv/tests.sh | 12 ++++++++++++ lib/dns/message.c | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 65bf8f50f8..249c9989d4 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -645,6 +645,18 @@ if [ -x "$DIG" ]; then if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + ret=0 + echo_i "check that dig processes +ednsopt=key-tag: +yaml ($n)" + dig_with_opts @10.53.0.3 +yaml +ednsopt=key-tag:00010002 a.example +qr >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS KEY-TAG >yamlget.out.test$n 2>&1 || ret=1 + read -r value and FORMERR is returned ($n)" ret=0 diff --git a/lib/dns/message.c b/lib/dns/message.c index 0f046f78be..8673dc7121 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3809,7 +3809,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, break; case DNS_OPT_KEY_TAG: if (optlen > 0U && (optlen % 2U) == 0U) { - const char *sep = ""; + const char *sep = " ["; while (optlen > 0U) { uint16_t id = isc_buffer_getuint16( @@ -3820,7 +3820,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, sep = ","; optlen -= 2; } - ADD_STRING(target, "\n"); + ADD_STRING(target, " ]\n"); continue; } break; From 81334113c39d419528e540dcf94f0046544f6edb Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 29 Oct 2024 14:06:57 +1100 Subject: [PATCH 06/12] Fix EDNS LLQ option YAML output The EDNS LLQ option was not being emitted as valid YAML. Correct the output to be valid YAML with each field of the LLQ being individually selectable. --- bin/tests/system/digdelv/tests.sh | 24 +++++++++++++ lib/dns/message.c | 60 ++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 249c9989d4..52e15e1b90 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -618,6 +618,30 @@ if [ -x "$DIG" ]; then if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "checking ednsopt LLQ prints as expected +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.3 +yaml +ednsopt=llq:0001000200001234567812345678fefefefe +qr a.example >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS LLQ LLQ-VERSION >yamlget.out.test$n 2>&1 || ret=1 + read -r value yamlget.out.test$n 2>&1 || ret=1 + read -r value yamlget.out.test$n 2>&1 || ret=1 + read -r value yamlget.out.test$n 2>&1 || ret=1 + read -r value yamlget.out.test$n 2>&1 || ret=1 + read -r value indent.count; + bool yaml = false; + + if ((dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) != 0) { + sep1 = sep2 = "\n"; + msg->indent.count++; + yaml = true; + } u = isc_buffer_getuint16(optbuf); - ADD_STRING(target, " Version: "); + ADD_STRING(target, sep1); + INDENT(style); + if (yaml) { + ADD_STRING(target, "LLQ-VERSION: "); + } else { + ADD_STRING(target, "Version: "); + } snprintf(buf, sizeof(buf), "%u", u); ADD_STRING(target, buf); u = isc_buffer_getuint16(optbuf); - ADD_STRING(target, ", Opcode: "); + ADD_STRING(target, sep2); + INDENT(style); + if (yaml) { + ADD_STRING(target, "LLQ-OPCODE: "); + } else { + ADD_STRING(target, "Opcode: "); + } snprintf(buf, sizeof(buf), "%u", u); ADD_STRING(target, buf); u = isc_buffer_getuint16(optbuf); - ADD_STRING(target, ", Error: "); + ADD_STRING(target, sep2); + INDENT(style); + if (yaml) { + ADD_STRING(target, "LLQ-ERROR: "); + } else { + ADD_STRING(target, "Error: "); + } snprintf(buf, sizeof(buf), "%u", u); ADD_STRING(target, buf); q = isc_buffer_getuint32(optbuf); q <<= 32; q |= isc_buffer_getuint32(optbuf); - ADD_STRING(target, ", Identifier: "); + ADD_STRING(target, sep2); + INDENT(style); + if (yaml) { + ADD_STRING(target, "LLQ-ID: "); + } else { + ADD_STRING(target, "Identifier: "); + } snprintf(buf, sizeof(buf), "%" PRIu64, q); ADD_STRING(target, buf); u = isc_buffer_getuint32(optbuf); - ADD_STRING(target, ", Lifetime: "); + ADD_STRING(target, sep2); + INDENT(style); + if (yaml) { + ADD_STRING(target, "LLQ-LEASE: "); + } else { + ADD_STRING(target, "Lifetime: "); + } snprintf(buf, sizeof(buf), "%u", u); ADD_STRING(target, buf); + cleanup: + msg->indent.count = count; return result; } @@ -3700,7 +3742,8 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, switch (optcode) { case DNS_OPT_LLQ: if (optlen == 18U) { - result = render_llq(&optbuf, target); + result = render_llq(&optbuf, msg, style, + target); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -4097,7 +4140,8 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section, switch (optcode) { case DNS_OPT_LLQ: if (optlen == 18U) { - result = render_llq(&optbuf, target); + result = render_llq(&optbuf, msg, style, + target); if (result != ISC_R_SUCCESS) { return result; } From 07c28652a3aaaf9ca413655375ec0cc3d6cc1c32 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 30 Oct 2024 13:35:58 +1100 Subject: [PATCH 07/12] Fix EDNS TCP-KEEPALIVE option YAML output There was missing white space between the option name and its value. --- bin/tests/system/digdelv/tests.sh | 21 +++++++++++++++++++++ lib/dns/message.c | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 52e15e1b90..4050f18ec6 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -764,6 +764,27 @@ if [ -x "$DIG" ]; then status=$((status + ret)) fi + n=$((n + 1)) + echo_i "check that dig processes +expire ($n)" + ret=0 + dig_with_opts @10.53.0.1 +expire . soa >dig.out.test$n 2>&1 || ret=1 + grep '; EXPIRE: 1200 (20 minutes)' dig.out.test$n >/dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status + ret)) + + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "check that dig processes +expire +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.1 +yaml +expire . soa >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message response_message_data OPT_PSEUDOSECTION EDNS EXPIRE >yamlget.out.test$n 2>&1 || ret=1 + read -r value /dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status + ret)) + fi + n=$((n + 1)) echo_i "check that Extended DNS Error 0 is printed correctly ($n)" ret=0 diff --git a/lib/dns/message.c b/lib/dns/message.c index 96f487469e..f1e7079aae 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3829,7 +3829,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, if (optlen == 2) { unsigned int dsecs; dsecs = isc_buffer_getuint16(&optbuf); - snprintf(buf, sizeof(buf), "%u.%u", + snprintf(buf, sizeof(buf), " %u.%u", dsecs / 10U, dsecs % 10U); ADD_STRING(target, buf); ADD_STRING(target, " secs\n"); From 5eeb31f0b9f630fc130d3876aad3eaa4c4ef8dee Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 30 Oct 2024 14:37:32 +1100 Subject: [PATCH 08/12] Split EDNS COOKIE YAML into separate parts Split the YAML display of the EDNS COOKIE option into CLIENT and SERVER parts. The STATUS of the EDNS COOKIE in the reply is now a YAML element rather than a comment. --- lib/dns/message.c | 91 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/lib/dns/message.c b/lib/dns/message.c index f1e7079aae..17e3e83e3a 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3917,6 +3917,68 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, continue; } break; + case DNS_OPT_COOKIE: + if (optlen == 8 || + (optlen >= 16 && optlen < 40)) + { + size_t i; + + msg->indent.count++; + optdata = isc_buffer_current(&optbuf); + + ADD_STRING(target, "\n"); + INDENT(style); + ADD_STRING(target, "CLIENT: "); + for (i = 0; i < 8; i++) { + snprintf(buf, sizeof(buf), + "%02x", optdata[i]); + ADD_STRING(target, buf); + } + ADD_STRING(target, "\n"); + + if (optlen >= 16) { + INDENT(style); + ADD_STRING(target, "SERVER: "); + for (; i < optlen; i++) { + snprintf(buf, + sizeof(buf), + "%02x", + optdata[i]); + ADD_STRING(target, buf); + } + ADD_STRING(target, "\n"); + } + + /* + * Valid server cookie? + */ + if (msg->cc_ok && optlen >= 16) { + INDENT(style); + ADD_STRING(target, + "STATUS: good\n"); + } + /* + * Server cookie is not valid but + * we had our cookie echoed back. + */ + if (msg->cc_ok && optlen < 16) { + INDENT(style); + ADD_STRING(target, + "STATUS: echoed\n"); + } + /* + * We didn't get our cookie echoed + * back. + */ + if (msg->cc_bad) { + INDENT(style); + ADD_STRING(target, + "STATUS: bad\n)"); + } + isc_buffer_forward(&optbuf, optlen); + continue; + } + break; default: break; } @@ -3952,32 +4014,9 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section, isc_buffer_forward(&optbuf, optlen); - if (optcode == DNS_OPT_COOKIE) { - /* - * Valid server cookie? - */ - if (msg->cc_ok && optlen >= 16) { - ADD_STRING(target, " (good)"); - } - /* - * Server cookie is not valid but - * we had our cookie echoed back. - */ - if (msg->cc_ok && optlen < 16) { - ADD_STRING(target, " (echoed)"); - } - /* - * We didn't get our cookie echoed - * back. - */ - if (msg->cc_bad) { - ADD_STRING(target, " (bad)"); - } - ADD_STRING(target, "\n"); - continue; - } - - if (optcode == DNS_OPT_CLIENT_SUBNET) { + if (optcode == DNS_OPT_COOKIE || + optcode == DNS_OPT_CLIENT_SUBNET) + { ADD_STRING(target, "\n"); continue; } From 641ca9044f7f05ed28704f43cefedd933b1c00ff Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 30 Oct 2024 10:30:44 +1100 Subject: [PATCH 09/12] Check EDNS CLIENT-SUBNET option is emitted using valid YAML Check that when there is an EDNS CLIENT-SUBNET option in the message, the emitted YAML is valid. --- bin/tests/system/digdelv/tests.sh | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 4050f18ec6..0d6bb0c02c 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -482,6 +482,18 @@ if [ -x "$DIG" ]; then if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "checking dig +subnet=0 +yaml ($n)" + ret=0 + dig_with_opts +yaml +tcp @10.53.0.2 +subnet=0 A a.example >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message response_message_data OPT_PSEUDOSECTION EDNS CLIENT-SUBNET >yamlget.out.test$n 2>&1 || ret=1 + read -r value dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message response_message_data OPT_PSEUDOSECTION EDNS CLIENT-SUBNET >yamlget.out.test$n 2>&1 || ret=1 + read -r value dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS CLIENT-SUBNET >yamlget.out.test$n 2>&1 || ret=1 + read -r value Date: Wed, 30 Oct 2024 13:11:06 +1100 Subject: [PATCH 10/12] Check EDNS EXPIRE option is emitted using valid YAML Check that when an EDNS EXPIRE option is present in the message, the emitted YAML is valid. --- bin/tests/system/digdelv/tests.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index 0d6bb0c02c..aed37b6fa5 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -819,6 +819,26 @@ if [ -x "$DIG" ]; then status=$((status + ret)) fi + n=$((n + 1)) + echo_i "check that dig processes +keepalive ($n)" + ret=0 + dig_with_opts @10.53.0.1 +keepalive . soa +tcp >dig.out.test$n 2>&1 || ret=1 + grep '; TCP-KEEPALIVE: 30.0 secs' dig.out.test$n >/dev/null || ret=1 + if [ $ret -ne 0 ]; then echo_i "failed"; fi + status=$((status + ret)) + + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "check that dig processes +keepalive +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.1 +yaml +keepalive . soa +tcp >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message response_message_data OPT_PSEUDOSECTION EDNS TCP-KEEPALIVE >yamlget.out.test$n 2>&1 || ret=1 + read -r value Date: Wed, 30 Oct 2024 13:18:49 +1100 Subject: [PATCH 11/12] Check EDNS CLIENT-TAG and SERVER-TAG are emitted using valid YAML Check that when an EDNS CLIENT-TAG or EDNS SERVER-TAG option is present in the message, the emitted YAML is valid. --- bin/tests/system/digdelv/tests.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index aed37b6fa5..bd0afa9301 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -733,6 +733,18 @@ if [ -x "$DIG" ]; then if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) + if [ $HAS_PYYAML -ne 0 ]; then + n=$((n + 1)) + echo_i "check that dig processes +ednsopt=client-tag:value +yaml ($n)" + ret=0 + dig_with_opts @10.53.0.3 +yaml +ednsopt=client-tag:0001 a.example +qr >dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS CLIENT-TAG >yamlget.out.test$n 2>&1 || ret=1 + read -r value dig.out.test$n 2>&1 || ret=1 + $PYTHON yamlget.py dig.out.test$n 0 message query_message_data OPT_PSEUDOSECTION EDNS SERVER-TAG >yamlget.out.test$n 2>&1 || ret=1 + read -r value Date: Mon, 16 Dec 2024 10:04:10 +1100 Subject: [PATCH 12/12] Fix a typo in a test description The test description "checking delv -c CH is ignored, and treated like IN" in digdelv was garbled. --- bin/tests/system/digdelv/tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh index bd0afa9301..4807826fff 100644 --- a/bin/tests/system/digdelv/tests.sh +++ b/bin/tests/system/digdelv/tests.sh @@ -1657,7 +1657,7 @@ if [ -x "$DELV" ]; then status=$((status + ret)) n=$((n + 1)) - echo_i "checking delv H is ignored, and treated like IN ($n)" + echo_i "checking delv -c CH is ignored, and treated like IN ($n)" ret=0 delv_with_opts @10.53.0.3 -c CH -t a a.example >delv.out.test$n || ret=1 grep "a.example." /dev/null || ret=1