2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-01 06:55:30 +00:00

Merge branch '1835-add-the-ability-to-parse-and-display-extended-dns-error-code-ede' into 'master'

Resolve "Add the ability to parse and display Extended DNS Error code (EDE)."

Closes #1835

See merge request isc-projects/bind9!3515
This commit is contained in:
Mark Andrews
2020-05-12 12:26:50 +00:00
14 changed files with 316 additions and 19 deletions

View File

@@ -1,3 +1,6 @@
5408. [protocol] Print Extended DNS Errors if present in OPT record.
[GL #1835]
5407. [func] The zone timers are now exported to the statistics
channel. Thanks to Paul Frieden, Verizon Media.
[GL #1232]

View File

@@ -1452,6 +1452,7 @@ dig_ednsoptname_t optnames[] = {
{ 12, "PAD" }, /* shorthand */
{ 13, "CHAIN" }, /* RFC 7901 */
{ 14, "KEY-TAG" }, /* RFC 8145 */
{ 15, "EDE" }, /* ietf-dnsop-extended-error-16 */
{ 16, "CLIENT-TAG" }, /* draft-bellis-dnsop-edns-tags */
{ 17, "SERVER-TAG" }, /* draft-bellis-dnsop-edns-tags */
{ 26946, "DEVICEID" }, /* Brian Hartvigsen */

View File

@@ -568,7 +568,8 @@ if [ -x "$DIG" ] ; then
echo_i "checking ednsopt LLQ prints as expected ($n)"
ret=0
dig_with_opts @10.53.0.3 +ednsopt=llq:0001000200001234567812345678fefefefe +qr a.example > dig.out.test$n 2>&1 || ret=1
grep 'LLQ: Version: 1, Opcode: 2, Error: 0, Identifier: 1311768465173141112, Lifetime: 4278124286$' dig.out.test$n > /dev/null || ret=1
pat='LLQ: Version: 1, Opcode: 2, Error: 0, Identifier: 1311768465173141112, Lifetime: 4278124286$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@@ -662,6 +663,51 @@ if [ -x "$DIG" ] ; then
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check that Extended DNS Error 0 is printed correctly ($n)"
# First defined EDE code, additional text "foo".
dig_with_opts @10.53.0.3 +ednsopt=ede:0000666f6f a.example +qr > dig.out.test$n 2>&1 || ret=1
pat='^; EDE: 0 (Other): (foo)$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check that Extended DNS Error 24 is printed correctly ($n)"
# Last defined EDE code, no additional text.
dig_with_opts @10.53.0.3 +ednsopt=ede:0018 a.example +qr > dig.out.test$n 2>&1 || ret=1
pat='^; EDE: 24 (Invalid Data)$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check that Extended DNS Error 25 is printed correctly ($n)"
# First undefined EDE code, additional text "foo".
dig_with_opts @10.53.0.3 +ednsopt=ede:0019666f6f a.example +qr > dig.out.test$n 2>&1 || ret=1
pat='^; EDE: 25: (foo)$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check that invalid Extended DNS Error (length 0) is printed ($n)"
# EDE payload is too short
dig_with_opts @10.53.0.3 +ednsopt=ede a.example +qr > dig.out.test$n 2>&1 || ret=1
pat='^; EDE:$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check that invalid Extended DNS Error (length 1) is printed ($n)"
# EDE payload is too short
dig_with_opts @10.53.0.3 +ednsopt=ede:00 a.example +qr > dig.out.test$n 2>&1 || ret=1
pat='^; EDE: 00 (".")$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /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
@@ -686,7 +732,8 @@ if [ -x "$DIG" ] ; then
echo_i "check that dig -q -m works ($n)"
ret=0
dig_with_opts @10.53.0.3 -q -m > dig.out.test$n 2>&1
grep '^;-m\..*IN.*A$' dig.out.test$n > /dev/null || ret=1
pat='^;-m\..*IN.*A$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
grep "Dump of all outstanding memory allocations" dig.out.test$n > /dev/null && ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@@ -780,7 +827,8 @@ if [ -x "$DIG" ] ; then
echo_i "check that dig +short +expandaaaa works ($n)"
ret=0
dig_with_opts @10.53.0.3 +short +expandaaaa AAAA ns2.example > dig.out.test$n 2>&1 || ret=1
grep '^fd92:7065:0b8e:ffff:0000:0000:0000:0002$' dig.out.test$n > /dev/null || ret=1
pat='^fd92:7065:0b8e:ffff:0000:0000:0000:0002$'
tr -d '\r' < dig.out.test$n | grep "$pat" > /dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))

View File

@@ -96,6 +96,13 @@
Contributed by Paul Frieden, Verizon Media. [GL #1232]
</para>
</listitem>
<listitem>
<para>
<command>dig</command> and other tools can now print the Extended
DNS Error (EDE) option when it appears in a request or response.
[GL #1834]
</para>
</listitem>
</itemizedlist>
</section>

View File

@@ -107,6 +107,7 @@
#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_EDE 15 /*%< Extended DNS Error opt code */
#define DNS_OPT_CLIENT_TAG 16 /*%< Client tag opt code */
#define DNS_OPT_SERVER_TAG 17 /*%< Server tag opt code */

View File

@@ -23,6 +23,7 @@
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/string.h> /* Required for HP/UX (and others?) */
#include <isc/utf8.h>
#include <isc/util.h>
#include <dns/dnssec.h>
@@ -127,6 +128,32 @@ static const char *opcodetext[] = { "QUERY", "IQUERY", "STATUS",
"RESERVED12", "RESERVED13", "RESERVED14",
"RESERVED15" };
static const char *edetext[] = { "Other",
"Unsupported DNSKEY Algorithm",
"Unsupported DS Digest Type",
"Stale Answer",
"Forged Answer",
"DNSSEC Indeterminate",
"DNSSEC Bogus",
"Signature Expired",
"Signature Not Yet Valid",
"DNSKEY Missing",
"RRSIGs Missing",
"No Zone Key Bit Set",
"NSEC Missing",
"Cached Error",
"Not Ready",
"Blocked",
"Censored",
"Filtered",
"Prohibited",
"Stale NXDOMAIN Answer",
"Not Authoritative",
"Not Supported",
"No Reachable Authority",
"Network Error",
"Invalid Data" };
/*%
* "helper" type, which consists of a block of some type, and is linkable.
* For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
@@ -3590,7 +3617,7 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
* Print EDNS info, if any.
*
* WARNING: The option contents may be malformed as
* dig +ednsopt=value:<content> does not validity
* dig +ednsopt=value:<content> does not perform validity
* checking.
*/
dns_rdata_init(&rdata);
@@ -4004,6 +4031,36 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
ADD_STRING(target, "\n");
continue;
}
} else if (optcode == DNS_OPT_EDE) {
ADD_STRING(target, "; EDE:");
if (optlen >= 2U) {
uint16_t ede;
ede = isc_buffer_getuint16(&optbuf);
snprintf(buf, sizeof(buf), " %u", ede);
ADD_STRING(target, buf);
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
if (ede < ARRAY_SIZE(edetext)) {
ADD_STRING(target, " (");
ADD_STRING(target,
edetext[ede]);
ADD_STRING(target, ")");
}
optlen -= 2;
if (optlen != 0) {
ADD_STRING(target, ":");
}
} else if (optlen == 1U) {
/* Malformed */
optdata = isc_buffer_current(&optbuf);
snprintf(buf, sizeof(buf),
" %02x (\"%c\")\n", optdata[0],
isprint(optdata[0])
? optdata[0]
: '.');
isc_buffer_forward(&optbuf, optlen);
ADD_STRING(target, buf);
continue;
}
} else if (optcode == DNS_OPT_CLIENT_TAG) {
uint16_t id;
ADD_STRING(target, "; CLIENT-TAG:");
@@ -4034,23 +4091,31 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
if (optlen != 0) {
int i;
bool utf8ok = false;
ADD_STRING(target, " ");
optdata = isc_buffer_current(&optbuf);
for (i = 0; i < optlen; i++) {
const char *sep;
switch (optcode) {
case DNS_OPT_COOKIE:
sep = "";
break;
default:
sep = " ";
break;
if (optcode == DNS_OPT_EDE) {
utf8ok = isc_utf8_valid(optdata,
optlen);
}
if (!utf8ok) {
for (i = 0; i < optlen; i++) {
const char *sep;
switch (optcode) {
case DNS_OPT_COOKIE:
sep = "";
break;
default:
sep = " ";
break;
}
snprintf(buf, sizeof(buf),
"%02x%s", optdata[i],
sep);
ADD_STRING(target, buf);
}
snprintf(buf, sizeof(buf), "%02x%s",
optdata[i], sep);
ADD_STRING(target, buf);
}
isc_buffer_forward(&optbuf, optlen);
@@ -4087,9 +4152,13 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
/*
* For non-COOKIE options, add a printable
* version
* version.
*/
ADD_STRING(target, "(\"");
if (optcode != DNS_OPT_EDE) {
ADD_STRING(target, "(\"");
} else {
ADD_STRING(target, "(");
}
if (isc_buffer_availablelength(target) < optlen)
{
return (ISC_R_NOSPACE);
@@ -4098,11 +4167,18 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
if (isprint(optdata[i])) {
isc_buffer_putmem(
target, &optdata[i], 1);
} else if (utf8ok && optdata[i] > 127) {
isc_buffer_putmem(
target, &optdata[i], 1);
} else {
isc_buffer_putstr(target, ".");
}
}
ADD_STRING(target, "\")");
if (optcode != DNS_OPT_EDE) {
ADD_STRING(target, "\")");
} else {
ADD_STRING(target, ")");
}
}
ADD_STRING(target, "\n");
}

View File

@@ -18,6 +18,8 @@
(DNS_RDATATYPEATTR_SINGLETON | DNS_RDATATYPEATTR_META | \
DNS_RDATATYPEATTR_NOTQUESTION)
#include <isc/utf8.h>
static inline isc_result_t
fromtext_opt(ARGS_FROMTEXT) {
/*
@@ -206,6 +208,24 @@ fromwire_opt(ARGS_FROMWIRE) {
}
isc_region_consume(&sregion, length);
break;
case DNS_OPT_EDE:
if (length < 2) {
return (DNS_R_OPTERR);
}
/* UTF-8 Byte Order Mark is not permitted. RFC 5198 */
if (isc_utf8_bom(sregion.base + 2, length - 2)) {
return (DNS_R_OPTERR);
}
/*
* The EXTRA-TEXT field is specified as UTF-8, and
* therefore must be validated for correctness
* according to RFC 3269 security considerations.
*/
if (!isc_utf8_valid(sregion.base + 2, length - 2)) {
return (DNS_R_OPTERR);
}
isc_region_consume(&sregion, length);
break;
case DNS_OPT_CLIENT_TAG:
/* FALLTHROUGH */
case DNS_OPT_SERVER_TAG:

View File

@@ -88,6 +88,7 @@ libisc_la_HEADERS = \
include/isc/timer.h \
include/isc/tm.h \
include/isc/types.h \
include/isc/utf8.h \
include/isc/util.h \
pthreads/include/isc/condition.h\
pthreads/include/isc/mutex.h \
@@ -205,6 +206,7 @@ libisc_la_SOURCES = \
taskpool.c \
timer.c \
tm.c \
utf8.c \
pthreads/condition.c \
pthreads/mutex.c \
pthreads/thread.c \

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file isc/utf8.h */
#pragma once
#include <isc/lang.h>
#include <isc/types.h>
ISC_LANG_BEGINDECLS
bool
isc_utf8_bom(const unsigned char *buf, size_t len);
/*<
* Returns 'true' if the string of bytes in 'buf' starts
* with an UTF-8 Byte Order Mark.
*
* Requires:
*\li 'buf' != NULL
*/
bool
isc_utf8_valid(const unsigned char *buf, size_t len);
/*<
* Returns 'true' if the string of bytes in 'buf' is a valid UTF-8
* byte sequence otherwise 'false' is returned.
*
* Requires:
*\li 'buf' != NULL
*/
ISC_LANG_ENDDECLS

86
lib/isc/utf8.c Normal file
View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <string.h>
#include <isc/utf8.h>
#include <isc/util.h>
/*
* UTF-8 is defined in "The Unicode Standard -- Version 4.0"
* Also see RFC 3629.
*
* Char. number range | UTF-8 octet sequence
* (hexadecimal) | (binary)
* --------------------+---------------------------------------------
* 0000 0000-0000 007F | 0xxxxxxx
* 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
* 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
* 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
bool
isc_utf8_valid(const unsigned char *buf, size_t len) {
REQUIRE(buf != NULL);
for (size_t i = 0; i < len; i++) {
if (buf[i] <= 0x7f) {
continue;
}
if ((i + 1) < len && (buf[i] & 0xe0) == 0xc0 &&
(buf[i + 1] & 0xc0) == 0x80) {
unsigned int w;
w = (buf[i] & 0x1f) << 6;
w |= (buf[++i] & 0x3f);
if (w < 0x80) {
return (false);
}
continue;
}
if ((i + 2) < len && (buf[i] & 0xf0) == 0xe0 &&
(buf[i + 1] & 0xc0) == 0x80 && (buf[i + 2] & 0xc0) == 0x80)
{
unsigned int w;
w = (buf[i] & 0x0f) << 12;
w |= (buf[++i] & 0x3f) << 6;
w |= (buf[++i] & 0x3f);
if (w < 0x0800) {
return (false);
}
continue;
}
if ((i + 3) < len && (buf[i] & 0xf8) == 0xf0 &&
(buf[i + 1] & 0xc0) == 0x80 &&
(buf[i + 2] & 0xc0) == 0x80 && (buf[i + 3] & 0xc0) == 0x80)
{
unsigned int w;
w = (buf[i] & 0x07) << 18;
w |= (buf[++i] & 0x3f) << 12;
w |= (buf[++i] & 0x3f) << 6;
w |= (buf[++i] & 0x3f);
if (w < 0x10000 || w > 0x10FFFF) {
return (false);
}
continue;
}
return (false);
}
return (true);
}
bool
isc_utf8_bom(const unsigned char *buf, size_t len) {
REQUIRE(buf != NULL);
if (len >= 3U && !memcmp(buf, "\xef\xbb\xbf", 3)) {
return (true);
}
return (false);
}

View File

@@ -689,6 +689,8 @@ isc_timermgr_destroy
isc_timermgr_poke
isc_tm_timegm
isc_tm_strptime
isc_utf8_bom
isc_utf8_valid
isc_win32os_versioncheck
openlog
@IF PKCS11

View File

@@ -260,6 +260,9 @@
<ClInclude Include="..\include\isc\types.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\utf8.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\util.h">
<Filter>Library Header Files</Filter>
</ClInclude>
@@ -605,6 +608,9 @@
<ClCompile Include="..\tm.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\utf8.c">
<Filter>Library Source Files</Filter>
</ClCompile>
@IF PKCS11
<ClCompile Include="..\pk11.c">
<Filter>Library Source Files</Filter>

View File

@@ -370,6 +370,7 @@ copy InstallFiles ..\Build\Release\
<ClInclude Include="..\include\isc\timer.h" />
<ClInclude Include="..\include\isc\tm.h" />
<ClInclude Include="..\include\isc\types.h" />
<ClInclude Include="..\include\isc\utf8.h" />
<ClInclude Include="..\include\isc\util.h" />
@IF PKCS11
<ClInclude Include="..\include\pk11\constants.h" />
@@ -471,6 +472,7 @@ copy InstallFiles ..\Build\Release\
<ClCompile Include="..\taskpool.c" />
<ClCompile Include="..\timer.c" />
<ClCompile Include="..\tm.c" />
<ClCompile Include="..\utf8.c" />
@IF PKCS11
<ClCompile Include="..\pk11.c" />
<ClCompile Include="..\pk11_result.c" />

View File

@@ -1866,6 +1866,7 @@
./lib/isc/include/isc/timer.h C 1998,1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2018,2019,2020
./lib/isc/include/isc/tm.h C 2014,2016,2018,2019,2020
./lib/isc/include/isc/types.h C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2017,2018,2019,2020
./lib/isc/include/isc/utf8.h C 2020
./lib/isc/include/isc/util.h C 1998,1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2015,2016,2017,2018,2019,2020
./lib/isc/include/pk11/constants.h C 2014,2016,2017,2018,2019,2020
./lib/isc/include/pk11/internal.h C 2014,2016,2018,2019,2020
@@ -1990,6 +1991,7 @@
./lib/isc/unix/stdtime.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020
./lib/isc/unix/syslog.c C 2001,2004,2005,2007,2016,2018,2019,2020
./lib/isc/unix/time.c C 1998,1999,2000,2001,2003,2004,2005,2006,2007,2008,2011,2012,2014,2015,2016,2017,2018,2019,2020
./lib/isc/utf8.c C 2020
./lib/isc/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020
./lib/isc/win32/condition.c C 1998,1999,2000,2001,2004,2006,2007,2016,2018,2019,2020
./lib/isc/win32/dir.c C 1999,2000,2001,2004,2007,2008,2009,2011,2012,2013,2016,2017,2018,2019,2020