}
}
+ obj = NULL;
+ result = named_config_get(maps, "send-report-channel", &obj);
+ if (view->rad != NULL) {
+ dns_name_free(view->rad, view->mctx);
+ isc_mem_put(view->mctx, view->rad, sizeof(*view->rad));
+ }
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ if (strcmp(str, ".") != 0 && strcmp(str, "") != 0) {
+ view->rad = isc_mem_get(mctx, sizeof(*view->rad));
+ dns_name_init(view->rad, NULL);
+ CHECK(dns_name_fromstring(view->rad, str, dns_rootname,
+ 0, mctx));
+ }
+ }
+
obj = NULL;
result = named_config_get(maps, "dnssec-accept-expired", &obj);
INSIST(result == ISC_R_SUCCESS);
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
+rm -f */named.conf
rm -f */named.memstats
rm -f */named.run
-rm -f */named.conf
+rm -f */named.run.prev
rm -f dig.out.test*
+rm -f ns*/managed-keys.bind* ns*/*mkeys*
rm -f ns2/example.com.bk
rm -f ns2/example.net.bk
-rm -f ns*/managed-keys.bind* ns*/*mkeys*
recursion no;
notify yes;
dnssec-validation no;
+ send-report-channel "rad.example.net";
};
view main in {
[ $ret -eq 0 ] || echo_i "failed"
status=$((status + ret))
+n=$((n + 1))
+echo_i "check that a Report-Channel EDNS option is added to responses ($n)"
+ret=0
+$DIG $DIGOPTS @10.53.0.1 example.net >dig.out.test$n
+grep "; Report-Channel: rad.example.net" dig.out.test$n >/dev/null || ret=1
+[ $ret -eq 0 ] || echo_i "failed"
+status=$((status + ret))
+
+n=$((n + 1))
+echo_i "check that error report queries are logged and no Report-Channel option is present in the response ($n)"
+ret=0
+nextpart ns1/named.run >/dev/null
+$DIG $DIGOPTS @10.53.0.1 _er.0.example.1._er.rad.example.net TXT >dig.out.test$n
+nextpart ns1/named.run | grep "dns-reporting-agent '_er.0.example.1._er.rad.example.net/IN'" >/dev/null || ret=1
+grep "; Report-Channel: rad.example.net" dig.out.test$n >/dev/null && ret=1
+[ $ret -eq 0 ] || echo_i "failed"
+status=$((status + ret))
+
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ /* invalid domain name */
+ send-report-channel example..com;
+};
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+view view {
+ send-report-channel example.com;
+};
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ send-report-channel example.com;
+};
+
+zone example.com {
+ type primary;
+ file "example.db";
+};
fi
status=$((status + ret))
+n=$((n + 1))
+echo_i "check that 'send-report-channel' warns if no matching zone exists ($n)"
+ret=0
+$CHECKCONF -z warn-rad.conf >checkconf.out$n 2>&1 || ret=1
+grep -F "send-report-channel 'example.com' is not a primary or secondary zone" checkconf.out$n >/dev/null || ret=1
+if [ $ret != 0 ]; then
+ echo_i "failed"
+ ret=1
+fi
+status=$((status + ret))
+
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ /* nonexistent zone used as agent-domain */
+ send-report-channel example.com;
+};
:rfc:`8749` - W. Mekking and D. Mahoney. *Moving DNSSEC Lookaside Validation
(DLV) to Historic Status.* March 2020.
+:rfc:`9567` - R. Arends and M. Larson. *DNS Error Reporting*
+
Notes
~~~~~
``general``
A catch-all for many things that still are not classified into categories.
+``dns-reporting-agent``
+ Logs reports from clients that the there is an error in our responses.
+
``lame-servers``
Misconfigurations in remote servers, discovered by BIND 9 when trying to query those servers during resolution.
If all supported digest types are disabled, the zones covered by
:any:`disable-ds-digests` are treated as insecure.
+.. namedconf:statement:: send-report-channel
+ :tags: query
+ :short: Sets the Agent Domain value for the EDNS Report-Channel option
+
+ The EDNS Report-Channel option can be added to responses by an
+ authoritative server to inform clients of a domain name to which
+ operational and protocol errors may be reported. This can help
+ operators find out about configuration errors that are causing
+ problems with resolution or validation elsewhere (for example,
+ expired DNSSEC signatures).
+
+ When :any:`send-report-channel` is set in :namedconf:ref:`options`, or
+ :namedconf:ref:`view` :iscman:`named` adds a Report-Channel option to
+ authoritative responses, using the specified domain name as the
+ Agent-Domain. :iscman:`named` also logs any TXT queries received for
+ names matching the prescribed error-reporting format
+ (_er.<type>.<name>.<extended-rcode>._er.<agent-domain>) to the
+ ``dns-reporting-agent`` logging category at level ``info``.
+
+ There should be a zone delegated to respond to these queries with TXT
+ records, to avoid unnecessarily query repetition. For example:
+
+ ::
+
+ e.g.
+ $ORIGIN <agent-domain>
+ @ 600 SOA <namserver1> <contact-email> 0 0 0 0 600
+ @ 600 NS <nameserver1>
+ @ 600 NS <nameserver2>
+ *._er 600 TXT ""
+
+ If :any:`send-report-channel` is not set, or is set to ``.``, then
+ the EDNS option is not added to responses, and error-report queries are
+ not logged.
+
.. namedconf:statement:: dnssec-must-be-secure
:tags: deprecated
:short: Defines hierarchies that must or may not be secure (signed and validated).
rrset-order { [ class <string> ] [ type <string> ] [ name <quoted_string> ] <string> <string>; ... };
secroots-file <quoted_string>;
send-cookie <boolean>;
+ send-report-channel <string>;
serial-query-rate <integer>;
serial-update-method ( date | increment | unixtime );
server-id ( <quoted_string> | none | hostname );
root-key-sentinel <boolean>;
rrset-order { [ class <string> ] [ type <string> ] [ name <quoted_string> ] <string> <string>; ... };
send-cookie <boolean>;
+ send-report-channel <string>;
serial-update-method ( date | increment | unixtime );
server <netprefix> {
bogus <boolean>;
#define DNS_MESSAGEEXTFLAG_DO 0x8000U
/*%< EDNS0 extended OPT codes */
-#define DNS_OPT_LLQ 1 /*%< LLQ opt code */
-#define DNS_OPT_UL 2 /*%< UL opt code */
-#define DNS_OPT_NSID 3 /*%< NSID opt code */
-#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */
-#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */
-#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */
-#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 */
+
+#define DNS_OPT_LLQ 1 /*%< LLQ opt code */
+#define DNS_OPT_UL 2 /*%< UL opt code */
+#define DNS_OPT_NSID 3 /*%< NSID opt code */
+#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */
+#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */
+#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */
+#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 */
+#define DNS_OPT_REPORT_CHANNEL 18 /*%< DNS Reporting Channel */
/*%< Experimental options [65001...65534] as per RFC6891 */
* options we know about. Extended DNS Errors may occur multiple times, but we
* will set only one per message (for now).
*/
-#define DNS_EDNSOPTIONS 8
+#define DNS_EDNSOPTIONS 9
/*%< EDNS0 extended DNS errors */
#define DNS_EDE_OTHER 0 /*%< Other Error */
/*%<
* Return the amount of dynamically allocated memory associated with
* 'name' (which is 0 if 'name' is not dynamic).
+ */
+
+bool
+dns_name_israd(const dns_name_t *name, const dns_name_t *rad);
+/*%<
+ * Determine whether 'name' matches the prescribed format of a
+ * DNS error-reporting name:
+ *
+ * _er.<TYPE>.<QNAME>.<EDE>._er.<AGENT-DOMAIN>.
+ *
+ * AGENT-DOMAIN is specified by the 'rad' parameter.
+ * EDE is a numeric value representing an extended DNS error code.
+ * TYPE and EDE are not currently checked.
*
* Requires:
* \li 'name' to be valid.
uint32_t maxrrperset;
uint32_t maxtypepername;
uint8_t max_restarts;
+ dns_name_t *rad; /* reporting agent domain */
/*
* Configurable data for server use only,
return (result);
}
+static isc_result_t
+render_reportchan(isc_buffer_t *optbuf, isc_buffer_t *target) {
+ dns_decompress_t dctx = DNS_DECOMPRESS_NEVER;
+ dns_fixedname_t fixed;
+ dns_name_t *name = dns_fixedname_initname(&fixed);
+ char namebuf[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+
+ result = dns_name_fromwire(name, optbuf, dctx, NULL);
+ if (result == ISC_R_SUCCESS && isc_buffer_activelength(optbuf) == 0) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ ADD_STRING(target, " ");
+ ADD_STRING(target, namebuf);
+ return (result);
+ }
+ result = ISC_R_FAILURE;
+cleanup:
+ return (result);
+}
+
static isc_result_t
dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
const dns_master_style_t *style,
ADD_STRING(target, buf);
continue;
}
+ } else if (optcode == DNS_OPT_REPORT_CHANNEL) {
+ INDENT(style);
+ ADD_STRING(target, "Report-Channel:");
+ if (optlen > 0U) {
+ isc_buffer_t sb = optbuf;
+ isc_buffer_setactive(&optbuf, optlen);
+ result = render_reportchan(&optbuf,
+ target);
+ if (result == ISC_R_SUCCESS) {
+ continue;
+ }
+ optbuf = sb;
+ }
} else {
INDENT(style);
ADD_STRING(target, "OPT=");
ADD_STRING(target, buf);
continue;
}
+ } else if (optcode == DNS_OPT_REPORT_CHANNEL) {
+ ADD_STRING(target, "; Report-Channel:");
+ if (optlen > 0U) {
+ isc_buffer_t sb = optbuf;
+ isc_buffer_setactive(&optbuf, optlen);
+ result = render_reportchan(&optbuf,
+ target);
+ if (result == ISC_R_SUCCESS) {
+ ADD_STRING(target, "\n");
+ continue;
+ }
+ optbuf = sb;
+ }
} else {
ADD_STRING(target, "; OPT=");
snprintf(buf, sizeof(buf), "%u:", optcode);
return (false);
}
+
+bool
+dns_name_israd(const dns_name_t *name, const dns_name_t *rad) {
+ dns_name_t suffix;
+ dns_offsets_t offsets;
+ char labelbuf[64];
+ unsigned long v, last = ULONG_MAX;
+ char *end, *l;
+
+ REQUIRE(DNS_NAME_VALID(name));
+ REQUIRE(DNS_NAME_VALID(rad));
+
+ if (name->labels < rad->labels + 4U || name->length < 4U) {
+ return (false);
+ }
+
+ if (name->ndata[0] != 3 || name->ndata[1] != '_' ||
+ tolower(name->ndata[2]) != 'e' || tolower(name->ndata[3]) != 'r')
+ {
+ return (false);
+ }
+
+ dns_name_init(&suffix, offsets);
+ dns_name_split(name, rad->labels + 1, NULL, &suffix);
+
+ if (suffix.ndata[0] != 3 || suffix.ndata[1] != '_' ||
+ tolower(suffix.ndata[2]) != 'e' || tolower(suffix.ndata[3]) != 'r')
+ {
+ return (false);
+ }
+
+ /* type list */
+ dns_name_split(name, name->labels - 1, NULL, &suffix);
+ INSIST(*suffix.ndata < sizeof(labelbuf));
+ memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
+ labelbuf[*suffix.ndata] = 0;
+ if (strlen(labelbuf) != *suffix.ndata) {
+ return (false);
+ }
+ l = labelbuf;
+ do {
+ v = strtoul(l, &end, 10);
+ if (v > 0xffff || (*end != 0 && *end != '-') || end == l) {
+ return (false);
+ }
+ if (last != ULONG_MAX && v <= last) {
+ return (false);
+ }
+ last = v;
+ if (*end == '-') {
+ l = end + 1;
+ }
+ } while (*end != 0);
+
+ /* extended error code */
+ dns_name_split(name, rad->labels + 2, NULL, &suffix);
+ INSIST(*suffix.ndata < sizeof(labelbuf));
+ memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
+ labelbuf[*suffix.ndata] = 0;
+ if (strlen(labelbuf) != *suffix.ndata) {
+ return (false);
+ }
+ v = strtoul(labelbuf, &end, 10);
+ if (v > 0xfff || *end != 0) {
+ return (false);
+ }
+
+ return (dns_name_issubdomain(name, rad));
+}
static isc_result_t
fromwire_opt(ARGS_FROMWIRE) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
isc_region_t sregion;
isc_region_t tregion;
- uint16_t opt;
+ isc_result_t result;
uint16_t length;
+ uint16_t opt;
unsigned int total;
REQUIRE(type == dns_rdatatype_opt);
UNUSED(type);
UNUSED(rdclass);
- UNUSED(dctx);
+
+ dctx = dns_decompress_setpermitted(dctx, false);
isc_buffer_activeregion(source, &sregion);
if (sregion.length == 0) {
}
isc_region_consume(&sregion, length);
break;
+ case DNS_OPT_REPORT_CHANNEL:
+ /* A domain name in wire format. RFC 9567 */
+ if (length == 0 || length > DNS_NAME_MAXWIRE) {
+ return (DNS_R_OPTERR);
+ }
+ isc_buffer_init(&b, sregion.base, length);
+ isc_buffer_add(&b, length);
+ name = dns_fixedname_initname(&fixed);
+ result = dns_name_fromwire(name, &b, dctx, NULL);
+ if (result != ISC_R_SUCCESS || name->length != length ||
+ !dns_name_isabsolute(name))
+ {
+ return (DNS_R_OPTERR);
+ }
+ isc_region_consume(&sregion, length);
+ break;
default:
isc_region_consume(&sregion, length);
break;
dns_dns64_unlink(&view->dns64, dns64);
dns_dns64_destroy(&dns64);
}
+ if (view->rad != NULL) {
+ dns_name_free(view->rad, view->mctx);
+ isc_mem_put(view->mctx, view->rad, sizeof(*view->rad));
+ }
if (view->managed_keys != NULL) {
dns_zone_detach(&view->managed_keys);
}
NS_LOGCATEGORY_TAT,
NS_LOGCATEGORY_SERVE_STALE,
NS_LOGCATEGORY_RESPONSES,
+ NS_LOGCATEGORY_DRA,
/* cfg categories */
CFG_LOGCATEGORY_CONFIG,
/* named categories */
[NS_LOGCATEGORY_UPDATE_SECURITY] = "update-security",
[NS_LOGCATEGORY_QUERY_ERRORS] = "query-errors",
[NS_LOGCATEGORY_TAT] = "trust-anchor-telemetry",
+ [NS_LOGCATEGORY_DRA] = "dns-reporting-agent",
[NS_LOGCATEGORY_SERVE_STALE] = "serve-stale",
[NS_LOGCATEGORY_RESPONSES] = "responses",
/* cfg categories */
}
}
+ /*
+ * Check send-report-channel.
+ */
+ obj = NULL;
+ (void)cfg_map_get(options, "send-report-channel", &obj);
+ if (obj != NULL) {
+ str = cfg_obj_asstring(obj);
+ tresult = check_name(str);
+ if (tresult != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ if (result == ISC_R_SUCCESS) {
+ result = tresult;
+ }
+ }
+ }
+
/*
* Check dnssec-must-be-secure.
*/
typedef enum { special_zonetype_rpz, special_zonetype_catz } special_zonetype_t;
+static bool
+iszone(const cfg_obj_t *nameobj, const char *what, const char *forview,
+ const char *viewname, int level, isc_symtab_t *symtab) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ const cfg_obj_t *obj = NULL;
+ const char *zonename = cfg_obj_asstring(nameobj);
+ const char *zonetype = "";
+ dns_fixedname_t fixed;
+ dns_name_t *name = dns_fixedname_initname(&fixed);
+ isc_result_t result;
+ isc_symvalue_t value;
+
+ if (viewname == NULL) {
+ viewname = "";
+ forview = "";
+ }
+
+ result = dns_name_fromstring(name, zonename, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(nameobj, ISC_LOG_ERROR, "bad domain name '%s'",
+ zonename);
+ return (false);
+ }
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ result = isc_symtab_lookup(symtab, namebuf, 3, &value);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_obj_t *zoneobj = value.as_cpointer;
+ if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
+ zoneobj = cfg_tuple_get(zoneobj, "options");
+ }
+ if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
+ (void)cfg_map_get(zoneobj, "type", &obj);
+ }
+ if (obj != NULL) {
+ zonetype = cfg_obj_asstring(obj);
+ }
+ }
+
+ if (strcasecmp(zonetype, "primary") != 0 &&
+ strcasecmp(zonetype, "master") != 0 &&
+ strcasecmp(zonetype, "secondary") != 0 &&
+ strcasecmp(zonetype, "slave") != 0)
+ {
+ cfg_obj_log(nameobj, level,
+ "%s '%s'%s%s is not a primary or secondary zone",
+ what, zonename, forview, viewname);
+ return (false);
+ }
+ return (true);
+}
+
static isc_result_t
check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
const char *viewname, isc_symtab_t *symtab,
special_zonetype_t specialzonetype) {
const cfg_listelt_t *element;
- const cfg_obj_t *obj, *nameobj, *zoneobj;
- const char *zonename, *zonetype;
+ const cfg_obj_t *obj, *nameobj;
const char *forview = " for view ";
- isc_symvalue_t value;
- isc_result_t result, tresult;
- dns_fixedname_t fixed;
- dns_name_t *name;
- char namebuf[DNS_NAME_FORMATSIZE];
+ isc_result_t result = ISC_R_SUCCESS;
unsigned int num_zones = 0;
if (viewname == NULL) {
viewname = "";
forview = "";
}
- result = ISC_R_SUCCESS;
- name = dns_fixedname_initname(&fixed);
obj = cfg_tuple_get(rpz_obj, "zone list");
for (element = cfg_list_first(obj); element != NULL;
{
obj = cfg_listelt_value(element);
nameobj = cfg_tuple_get(obj, "zone name");
- zonename = cfg_obj_asstring(nameobj);
- zonetype = "";
if (specialzonetype == special_zonetype_rpz) {
if (++num_zones > 64) {
cfg_obj_log(nameobj, ISC_LOG_ERROR,
"more than 64 response policy "
- "zones in view '%s'",
- viewname);
+ "zones%s'%s'",
+ forview, viewname);
return (ISC_R_FAILURE);
}
}
- tresult = dns_name_fromstring(name, zonename, dns_rootname, 0,
- NULL);
- if (tresult != ISC_R_SUCCESS) {
- cfg_obj_log(nameobj, ISC_LOG_ERROR,
- "bad domain name '%s'", zonename);
- if (result == ISC_R_SUCCESS) {
- result = tresult;
- }
- continue;
- }
- dns_name_format(name, namebuf, sizeof(namebuf));
- tresult = isc_symtab_lookup(symtab, namebuf, 3, &value);
- if (tresult == ISC_R_SUCCESS) {
- obj = NULL;
- zoneobj = value.as_cpointer;
- if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
- zoneobj = cfg_tuple_get(zoneobj, "options");
- }
- if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
- (void)cfg_map_get(zoneobj, "type", &obj);
- }
- if (obj != NULL) {
- zonetype = cfg_obj_asstring(obj);
- }
- }
- if (strcasecmp(zonetype, "primary") != 0 &&
- strcasecmp(zonetype, "master") != 0 &&
- strcasecmp(zonetype, "secondary") != 0 &&
- strcasecmp(zonetype, "slave") != 0)
+ if (!iszone(nameobj, rpz_catz, forview, viewname, ISC_LOG_ERROR,
+ symtab))
{
- cfg_obj_log(nameobj, ISC_LOG_ERROR,
- "%s '%s'%s%s is not a primary or secondary "
- "zone",
- rpz_catz, zonename, forview, viewname);
- if (result == ISC_R_SUCCESS) {
- result = ISC_R_FAILURE;
- }
+ result = ISC_R_FAILURE;
}
}
return (result);
/*
* Check that the response-policy and catalog-zones options
- * refer to zones that exist.
+ * refer to zones that exist. Also warn if send-report-channel
+ * is not a zone.
*/
if (opts != NULL) {
obj = NULL;
{
result = ISC_R_FAILURE;
}
+
+ obj = NULL;
+ if ((cfg_map_get(opts, "send-report-channel", &obj) ==
+ ISC_R_SUCCESS))
+ {
+ (void)iszone(obj, "send-report-channel", " for view ",
+ viewname, ISC_LOG_WARNING, symtab);
+ }
}
/*
{ "queryport-pool-updateinterval", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "rate-limit", &cfg_type_rrl, 0 },
{ "recursion", &cfg_type_boolean, 0 },
+ { "send-report-channel", &cfg_type_astring, 0 },
{ "request-nsid", &cfg_type_boolean, 0 },
{ "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "require-server-cookie", &cfg_type_boolean, 0 },
#define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
-#define TCPBUFFERS_FILLCOUNT 1U
-#define TCPBUFFERS_FREEMAX 8U
-
-#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
+#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
+#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
-#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
+#define WANTRC(x) (((x)->attributes & NS_CLIENTATTR_WANTRC) != 0)
#define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
count++;
}
+ if (WANTRC(client) && view != NULL && view->rad != NULL &&
+ !dns_name_equal(view->rad, dns_rootname))
+ {
+ INSIST(count < DNS_EDNSOPTIONS);
+ ednsopts[count].code = DNS_OPT_REPORT_CHANNEL;
+ ednsopts[count].length = view->rad->length;
+ ednsopts[count].value = view->rad->ndata;
+ count++;
+ }
+
/* Padding must be added last */
if ((view != NULL) && (view->padding > 0) && WANTPAD(client) &&
(TCP_CLIENT(client) ||
#define NS_CLIENTATTR_WANTNSID 0x00020 /*%< include nameserver ID */
#define NS_CLIENTATTR_BADCOOKIE \
0x00040 /*%< Presented cookie is bad/out-of-date */
-/* Obsolete: NS_CLIENTATTR_FILTER_AAAA_RC 0x00080 */
+#define NS_CLIENTATTR_WANTRC 0x00080 /*%< include Report-Channel */
#define NS_CLIENTATTR_WANTAD 0x00100 /*%< want AD in response if possible */
#define NS_CLIENTATTR_WANTCOOKIE 0x00200 /*%< return a COOKIE */
#define NS_CLIENTATTR_HAVECOOKIE 0x00400 /*%< has a valid COOKIE */
#define NS_CLIENTATTR_HAVEECS 0x04000 /*%< received an ECS option */
#define NS_CLIENTATTR_WANTPAD 0x08000 /*%< pad reply */
#define NS_CLIENTATTR_USEKEEPALIVE 0x10000 /*%< use TCP keepalive */
-
-#define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */
+#define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */
+#define NS_CLIENTATTR_NEEDTCP 0x40000 /*%< send TC=1 */
/*
* Flag to use with the SERVFAIL cache to indicate
(((c)->query.attributes & NS_QUERYATTR_WANTRECURSION) != 0)
/*% Is TCP? */
#define TCP(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
+/*% This query needs to have been sent over TCP. Return TC=1. */
+#define NEEDTCP(c) (((c)->attributes & NS_CLIENTATTR_NEEDTCP) != 0)
/*% Want DNSSEC? */
#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD;
qctx->client->message->rcode = dns_rcode_badcookie;
+ qctx->client->attributes &= ~NS_CLIENTATTR_WANTRC;
+ return (ns_query_done(qctx));
+ }
+
+ /*
+ * Respond with TC=1 if we need TCP for this request.
+ */
+ if (!TCP(qctx->client) && NEEDTCP(qctx->client)) {
+ qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
+ qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD;
+ qctx->client->message->flags |= DNS_MESSAGEFLAG_TC;
return (ns_query_done(qctx));
}
~DNS_MESSAGEFLAG_AD;
qctx->client->message->rcode =
dns_rcode_badcookie;
+ qctx->client->attributes &=
+ ~NS_CLIENTATTR_WANTRC;
} else {
qctx->client->message->flags |=
DNS_MESSAGEFLAG_TC;
*/
if (qctx->client->query.restarts == 0 && !qctx->authoritative) {
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
+ qctx->client->attributes &= ~NS_CLIENTATTR_WANTRC;
}
/*
return (result);
}
+static void
+log_reportchannel(ns_client_t *client) {
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ client->attributes |= NS_CLIENTATTR_WANTRC;
+
+ if (client->view->rad != NULL &&
+ dns_name_issubdomain(client->query.qname, client->view->rad))
+ {
+ /*
+ * Don't add Report-Channel to responses at or below the
+ * reporting agent domain to prevent infinite loops.
+ */
+ client->attributes &= ~NS_CLIENTATTR_WANTRC;
+ }
+
+ if (client->query.qtype != dns_rdatatype_txt ||
+ client->view->rad == NULL ||
+ !dns_name_israd(client->query.qname, client->view->rad))
+ {
+ return;
+ }
+
+ /*
+ * Check for TCP or a good server cookie. If neither send
+ * back BADCOOKIE or TC=1.
+ */
+ if (!TCP(client) && !HAVECOOKIE(client)) {
+ if (WANTCOOKIE(client)) {
+ client->attributes |= NS_CLIENTATTR_BADCOOKIE;
+ } else {
+ client->attributes |= NS_CLIENTATTR_NEEDTCP;
+ }
+ }
+
+ if (!isc_log_wouldlog(ISC_LOG_INFO)) {
+ return;
+ }
+
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+
+ isc_log_write(NS_LOGCATEGORY_DRA, NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "dns-reporting-agent '%s/%s'", namebuf, classbuf);
+}
+
static void
log_tat(ns_client_t *client) {
char namebuf[DNS_NAME_FORMATSIZE];
dns_rdatatypestats_increment(client->manager->sctx->rcvquerystats,
qtype);
+ log_reportchannel(client);
log_tat(client);
if (dns_rdatatype_ismeta(qtype)) {
dns_rdatatype_opt, sizeof(dns_rdata_opt_t));
}
+ISC_RUN_TEST_IMPL(edns_rad) {
+ wire_ok_t wire_ok[] = {
+ /*
+ * Option code with no content.
+ */
+ WIRE_INVALID(0x00, 0x12, 0x00, 0x00),
+ /*
+ * Agent Domain = "."
+ */
+ WIRE_VALID(0x00, 0x12, 0x00, 0x01, 0x00),
+ /*
+ * Data after name.
+ */
+ WIRE_INVALID(0x00, 0x12, 0x00, 0x02, 0x00, 0x00),
+ /*
+ * Agent Domain = "example.com."
+ */
+ WIRE_VALID(0x00, 0x12, 0x00, 13, 7, 'e', 'x', 'a', 'm', 'p',
+ 'l', 'e', 3, 'c', 'o', 'm', 0x00),
+ /*
+ * No root label at end.
+ */
+ WIRE_INVALID(0x00, 0x12, 0x00, 12, 7, 'e', 'x', 'a', 'm', 'p',
+ 'l', 'e', 3, 'c', 'o', 'm'),
+ /*
+ * Truncated label.
+ */
+ WIRE_INVALID(0x00, 0x12, 0x00, 11, 7, 'e', 'x', 'a', 'm', 'p',
+ 'l', 'e', 3, 'c', 'o'),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ check_rdata(NULL, wire_ok, NULL, true, dns_rdataclass_in,
+ dns_rdatatype_opt, sizeof(dns_rdata_opt_t));
+}
+
/*
* http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt
*
/* other tests */
ISC_TEST_ENTRY(edns_client_subnet)
+ISC_TEST_ENTRY(edns_rad)
ISC_TEST_ENTRY(atcname)
ISC_TEST_ENTRY(atparent)
ISC_TEST_ENTRY(iszonecutauth)
| grep -E "^[[:space:]]+[^[:space:]]+_LOGCATEGORY_[^[:space:]]+([[:space:]]+=[[:space:]]+[-0-9]+)?," \
| grep -Ev "ISC_LOGCATEGORY_(MAX|INVALID)" \
| sed -e 's/.*LOGCATEGORY_\([A-Z_]*\).*/\1/' -e 's/^RRL$/rate-limit/' \
+ -e 's/DRA/dns-reporting-agent/' \
| tr 'A-Z' 'a-z' \
| tr _ - \
| sed 's/^tat$/trust-anchor-telemetry/' \