diff --git a/CHANGES b/CHANGES
index ad52987e2f..105907004c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+6255. [func] Expose data about incoming zone transfers in progress
+ using statistics channel. [GL #3883]
+
6254. [cleanup] Add semantic patch to do an explicit cast from char
to unsigned char in ctype.h class of functions.
[GL #4327]
diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
index 91f59440d2..badb3a1e9a 100644
--- a/bin/named/bind9.xsl
+++ b/bin/named/bind9.xsl
@@ -303,6 +303,7 @@
Status,
Server,
Zones,
+ Incoming Zone Transfers,
Network,
Memory and
Traffic Size
@@ -907,6 +908,59 @@
+
+
+ Incoming Zone Transfers for View
+
+
+
+ | Zone Name |
+ Zone Type |
+ Local Serial |
+ Remote Serial |
+ IXFR |
+ State |
+ Additional Refresh Queued |
+ Local Address |
+ Remote Address |
+ Transport |
+ TSIG Key Name |
+ Duration (s) |
+ Messages Received |
+ Records Received |
+ Bytes Received |
+
+
+
+
+
+
+ even
+ odd
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
Memory Usage Summary
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
index 7edb92e2e6..191006ede0 100644
--- a/bin/named/statschannel.c
+++ b/bin/named/statschannel.c
@@ -33,7 +33,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
@@ -1264,6 +1266,7 @@ cleanup:
#define STATS_XML_STATUS 0x00 /* display only common statistics */
#define STATS_XML_SERVER 0x01
#define STATS_XML_ZONES 0x02
+#define STATS_XML_XFRINS 0x04
#define STATS_XML_NET 0x08
#define STATS_XML_MEM 0x10
#define STATS_XML_TRAFFIC 0x20
@@ -1449,6 +1452,221 @@ cleanup:
return (ISC_R_FAILURE);
}
+static isc_result_t
+xfrin_xmlrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ dns_rdataclass_t rdclass;
+ const char *ztype;
+ uint32_t serial;
+ const isc_sockaddr_t *addrp = NULL;
+ char addr_buf[ISC_SOCKADDR_FORMATSIZE];
+ const dns_transport_t *transport = NULL;
+ xmlTextWriterPtr writer = arg;
+ dns_zonestat_level_t statlevel;
+ int xmlrc;
+ dns_xfrin_t *xfr = NULL;
+ bool is_running, is_deferred, is_pending;
+ bool needs_refresh;
+ bool is_first_data_received, is_ixfr;
+ unsigned int nmsg = 0;
+ unsigned int nrecs = 0;
+ uint64_t nbytes = 0;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none) {
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_zone_getxfr(zone, &xfr, &is_running, &is_deferred,
+ &is_pending, &needs_refresh);
+ if (result != ISC_R_SUCCESS) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if (!is_running && !is_deferred && !is_pending && !needs_refresh) {
+ /* No ongoing/queued transfer. */
+ goto cleanup;
+ }
+
+ if (is_running && xfr == NULL) {
+ /* The transfer is finished, and it's shutting down. */
+ goto cleanup;
+ }
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "xfrin"));
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR buf));
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "class",
+ ISC_XMLCHAR buf));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
+ ztype = user_zonetype(zone);
+ if (ztype != NULL) {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
+ if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) {
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteserial"));
+ if (is_running) {
+ serial = dns_xfrin_getendserial(xfr);
+ if (serial != 0) {
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u",
+ serial));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state"));
+ if (is_running) {
+ const char *xfr_state = NULL;
+
+ dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received,
+ &is_ixfr);
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR xfr_state));
+ } else if (is_deferred) {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Deferred"));
+ } else if (is_pending) {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Pending"));
+ } else if (needs_refresh) {
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR "Needs Refresh"));
+ } else {
+ UNREACHABLE();
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refreshqueued"));
+ TRY0(xmlTextWriterWriteString(
+ writer, ISC_XMLCHAR(needs_refresh ? "Yes" : "No")));
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "localaddr"));
+ if (is_running) {
+ addrp = dns_xfrin_getsourceaddr(xfr);
+ isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteaddr"));
+ if (is_running) {
+ addrp = dns_xfrin_getprimaryaddr(xfr);
+ isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "transport"));
+ if (is_running) {
+ transport = dns_xfrin_gettransport(xfr);
+ if (transport == NULL ||
+ dns_transport_get_type(transport) == DNS_TRANSPORT_TCP)
+ {
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR "TCP"));
+ } else if (dns_transport_get_type(transport) ==
+ DNS_TRANSPORT_TLS)
+ {
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR "TLS"));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tsigkeyname"));
+ if (is_running) {
+ const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr);
+ char tsigkeyname_buf[DNS_NAME_FORMATSIZE];
+
+ if (tsigkeyname != NULL) {
+ dns_name_format(tsigkeyname, tsigkeyname_buf,
+ sizeof(tsigkeyname_buf));
+ TRY0(xmlTextWriterWriteString(
+ writer, ISC_XMLCHAR tsigkeyname_buf));
+ }
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "duration"));
+ if (is_running) {
+ isc_time_t start = dns_xfrin_getstarttime(xfr);
+ isc_time_t now = isc_time_now();
+ isc_time_t diff;
+ uint32_t sec;
+
+ isc_time_subtract(&now, &start, &diff);
+ sec = isc_time_seconds(&diff);
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu32, sec));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "0"));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ if (is_running) {
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ }
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nmsg"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", nmsg));
+ TRY0(xmlTextWriterEndElement(writer));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nrecs"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", nrecs));
+ TRY0(xmlTextWriterEndElement(writer));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nbytes"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, nbytes));
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ixfr"));
+ if (is_running && is_first_data_received) {
+ TRY0(xmlTextWriterWriteString(
+ writer, ISC_XMLCHAR(is_ixfr ? "Yes" : "No")));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ""));
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* xfrin */
+
+cleanup:
+ if (xfr != NULL) {
+ dns_xfrin_detach(&xfr);
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Failed at xfrin_xmlrender()");
+ }
+ return (result);
+}
+
static isc_result_t
generatexml(named_server_t *server, uint32_t flags, int *buflen,
xmlChar **buf) {
@@ -1727,8 +1945,8 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen,
*/
view = ISC_LIST_HEAD(server->viewlist);
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
- while (view != NULL &&
- ((flags & (STATS_XML_SERVER | STATS_XML_ZONES)) != 0))
+ while (view != NULL && ((flags & (STATS_XML_SERVER | STATS_XML_ZONES |
+ STATS_XML_XFRINS)) != 0))
{
isc_stats_t *istats = NULL;
dns_stats_t *dstats = NULL;
@@ -1746,6 +1964,14 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen,
TRY0(xmlTextWriterEndElement(writer)); /* /zones */
}
+ if ((flags & STATS_XML_XFRINS) != 0) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "xfrins"));
+ CHECK(dns_zt_apply(view->zonetable, true, NULL,
+ xfrin_xmlrender, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /xfrins */
+ }
+
if ((flags & STATS_XML_SERVER) == 0) {
TRY0(xmlTextWriterEndElement(writer)); /* /view */
view = ISC_LIST_NEXT(view, link);
@@ -1934,6 +2160,17 @@ render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
freecb, freecb_args));
}
+static isc_result_t
+render_xml_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_XFRINS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
static isc_result_t
render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
@@ -1976,6 +2213,7 @@ render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
#define STATS_JSON_STATUS 0x00 /* display only common statistics */
#define STATS_JSON_SERVER 0x01
#define STATS_JSON_ZONES 0x02
+#define STATS_JSON_XFRINS 0x04
#define STATS_JSON_NET 0x08
#define STATS_JSON_MEM 0x10
#define STATS_JSON_TRAFFIC 0x20
@@ -2226,6 +2464,211 @@ cleanup:
return (result);
}
+static isc_result_t
+xfrin_jsonrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ char classbuf[64]; /* sufficiently large for class */
+ char *zone_name_only = NULL;
+ char *class_only = NULL;
+ dns_rdataclass_t rdclass;
+ uint32_t serial;
+ json_object *xfrinarray = (json_object *)arg;
+ json_object *xfrinobj = NULL;
+ const isc_sockaddr_t *addrp = NULL;
+ char addr_buf[ISC_SOCKADDR_FORMATSIZE];
+ const dns_transport_t *transport = NULL;
+ dns_zonestat_level_t statlevel;
+ dns_xfrin_t *xfr = NULL;
+ bool is_running, is_deferred, is_pending;
+ bool needs_refresh;
+ bool is_first_data_received, is_ixfr;
+ unsigned int nmsg = 0;
+ unsigned int nrecs = 0;
+ uint64_t nbytes = 0;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none) {
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ zone_name_only = buf;
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ class_only = classbuf;
+
+ if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) {
+ xfrinobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), 0, false);
+ } else {
+ xfrinobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), serial, true);
+ }
+
+ if (xfrinobj == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dns_zone_getxfr(zone, &xfr, &is_running, &is_deferred,
+ &is_pending, &needs_refresh);
+ if (result != ISC_R_SUCCESS) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if (!is_running && !is_deferred && !is_pending && !needs_refresh) {
+ /* No ongoing/queued transfer. */
+ goto cleanup;
+ }
+
+ if (is_running && xfr == NULL) {
+ /* The transfer is finished, and it's shutting down. */
+ goto cleanup;
+ }
+
+ if (is_running) {
+ serial = dns_xfrin_getendserial(xfr);
+ if (serial != 0) {
+ json_object_object_add(xfrinobj, "remoteserial",
+ json_object_new_int64(serial));
+ }
+ }
+
+ if (is_running) {
+ const char *xfr_state = NULL;
+
+ dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received,
+ &is_ixfr);
+ json_object_object_add(xfrinobj, "state",
+ json_object_new_string(xfr_state));
+ } else if (is_deferred) {
+ json_object_object_add(xfrinobj, "state",
+ json_object_new_string("Deferred"));
+ } else if (is_pending) {
+ json_object_object_add(xfrinobj, "state",
+ json_object_new_string("Pending"));
+ } else if (needs_refresh) {
+ json_object_object_add(xfrinobj, "state",
+ json_object_new_string("Needs Refresh"));
+ } else {
+ UNREACHABLE();
+ }
+
+ json_object_object_add(
+ xfrinobj, "refreshqueued",
+ json_object_new_string(needs_refresh ? "Yes" : "No"));
+
+ if (is_running) {
+ addrp = dns_xfrin_getsourceaddr(xfr);
+ isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
+ json_object_object_add(xfrinobj, "localaddr",
+ json_object_new_string(addr_buf));
+ } else {
+ json_object_object_add(xfrinobj, "localaddr",
+ json_object_new_string("-"));
+ }
+
+ if (is_running) {
+ addrp = dns_xfrin_getprimaryaddr(xfr);
+ isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
+ json_object_object_add(xfrinobj, "remoteaddr",
+ json_object_new_string(addr_buf));
+ } else {
+ json_object_object_add(xfrinobj, "remoteaddr",
+ json_object_new_string("-"));
+ }
+
+ if (is_running) {
+ transport = dns_xfrin_gettransport(xfr);
+ if (transport == NULL ||
+ dns_transport_get_type(transport) == DNS_TRANSPORT_TCP)
+ {
+ json_object_object_add(xfrinobj, "transport",
+ json_object_new_string("TCP"));
+ } else if (dns_transport_get_type(transport) ==
+ DNS_TRANSPORT_TLS)
+ {
+ json_object_object_add(xfrinobj, "transport",
+ json_object_new_string("TLS"));
+ } else {
+ json_object_object_add(xfrinobj, "transport",
+ json_object_new_string("-"));
+ }
+ } else {
+ json_object_object_add(xfrinobj, "transport",
+ json_object_new_string("-"));
+ }
+
+ if (is_running) {
+ const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr);
+ char tsigkeyname_buf[DNS_NAME_FORMATSIZE];
+
+ if (tsigkeyname != NULL) {
+ dns_name_format(tsigkeyname, tsigkeyname_buf,
+ sizeof(tsigkeyname_buf));
+ json_object_object_add(
+ xfrinobj, "tsigkeyname",
+ json_object_new_string(tsigkeyname_buf));
+ } else {
+ json_object_object_add(xfrinobj, "tsigkeyname", NULL);
+ }
+ } else {
+ json_object_object_add(xfrinobj, "tsigkeyname", NULL);
+ }
+
+ if (is_running) {
+ isc_time_t start = dns_xfrin_getstarttime(xfr);
+ isc_time_t now = isc_time_now();
+ isc_time_t diff;
+ uint32_t sec;
+
+ isc_time_subtract(&now, &start, &diff);
+ sec = isc_time_seconds(&diff);
+ json_object_object_add(xfrinobj, "duration",
+ json_object_new_int64((int64_t)sec));
+ } else {
+ json_object_object_add(xfrinobj, "duration",
+ json_object_new_int64(0));
+ }
+
+ if (is_running) {
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ }
+ json_object_object_add(xfrinobj, "nmsg",
+ json_object_new_int64((int64_t)nmsg));
+ json_object_object_add(xfrinobj, "nrecs",
+ json_object_new_int64((int64_t)nrecs));
+ json_object_object_add(
+ xfrinobj, "nbytes",
+ json_object_new_int64(nbytes > INT64_MAX ? INT64_MAX
+ : (int64_t)nbytes));
+
+ if (is_running && is_first_data_received) {
+ json_object_object_add(
+ xfrinobj, "ixfr",
+ json_object_new_string(is_ixfr ? "Yes" : "No"));
+ } else {
+ json_object_object_add(xfrinobj, "ixfr",
+ json_object_new_string(""));
+ }
+
+ json_object_array_add(xfrinarray, xfrinobj);
+ xfrinobj = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (xfr != NULL) {
+ dns_xfrin_detach(&xfr);
+ }
+ if (xfrinobj != NULL) {
+ json_object_put(xfrinobj);
+ }
+ return (result);
+}
+
static isc_result_t
generatejson(named_server_t *server, size_t *msglen, const char **msg,
json_object **rootp, uint32_t flags) {
@@ -2444,7 +2887,9 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg,
#endif /* ifdef HAVE_DNSTAP */
}
- if ((flags & (STATS_JSON_ZONES | STATS_JSON_SERVER)) != 0) {
+ if ((flags &
+ (STATS_JSON_SERVER | STATS_JSON_ZONES | STATS_JSON_XFRINS)) != 0)
+ {
viewlist = json_object_new_object();
CHECKMEM(viewlist);
@@ -2452,7 +2897,7 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg,
view = ISC_LIST_HEAD(server->viewlist);
while (view != NULL) {
- json_object *za, *v = json_object_new_object();
+ json_object *za, *xa, *v = json_object_new_object();
dns_adb_t *adb = NULL;
CHECKMEM(v);
@@ -2472,6 +2917,20 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg,
json_object_put(za);
}
+ xa = json_object_new_array();
+ CHECKMEM(xa);
+
+ if ((flags & STATS_JSON_XFRINS) != 0) {
+ CHECK(dns_zt_apply(view->zonetable, true, NULL,
+ xfrin_jsonrender, xa));
+ }
+
+ if (json_object_array_length(xa) != 0) {
+ json_object_object_add(v, "xfrins", xa);
+ } else {
+ json_object_put(xa);
+ }
+
if ((flags & STATS_JSON_SERVER) != 0) {
json_object *res = NULL;
dns_stats_t *dstats = NULL;
@@ -2855,6 +3314,17 @@ render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
freecb, freecb_args));
}
+static isc_result_t
+render_json_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_XFRINS, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
+}
+
static isc_result_t
render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
@@ -3060,6 +3530,9 @@ add_listener(named_server_t *server, named_statschannel_t **listenerp,
isc_httpdmgr_addurl(listener->httpdmgr,
"/xml/v" STATS_XML_VERSION_MAJOR "/zones", false,
render_xml_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/xfrins", false,
+ render_xml_xfrins, server);
isc_httpdmgr_addurl(listener->httpdmgr,
"/xml/v" STATS_XML_VERSION_MAJOR "/net", false,
render_xml_net, server);
@@ -3085,6 +3558,9 @@ add_listener(named_server_t *server, named_statschannel_t **listenerp,
isc_httpdmgr_addurl(listener->httpdmgr,
"/json/v" STATS_JSON_VERSION_MAJOR "/zones", false,
render_json_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/xfrins", false,
+ render_json_xfrins, server);
isc_httpdmgr_addurl(listener->httpdmgr,
"/json/v" STATS_JSON_VERSION_MAJOR "/net", false,
render_json_net, server);
diff --git a/bin/tests/system/statschannel/clean.sh b/bin/tests/system/statschannel/clean.sh
index b0ab571b24..d1c381e629 100644
--- a/bin/tests/system/statschannel/clean.sh
+++ b/bin/tests/system/statschannel/clean.sh
@@ -32,4 +32,5 @@ rm -f traffic traffic.out.* traffic.json.* traffic.xml.*
rm -f xml.*mem json.*mem
rm -f xml.*stats json.*stats
rm -f zones zones.out.* zones.json.* zones.xml.* zones.expect.*
+rm -f xfrins xfrins.json.* xfrins.xml.*
rm -rf ./__pycache__
diff --git a/bin/tests/system/statschannel/ns1/named.conf.in b/bin/tests/system/statschannel/ns1/named.conf.in
index 52fd9fd855..52e8a7339f 100644
--- a/bin/tests/system/statschannel/ns1/named.conf.in
+++ b/bin/tests/system/statschannel/ns1/named.conf.in
@@ -37,6 +37,10 @@ controls {
inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
+server 10.53.0.3 {
+ transfer-format one-answer;
+};
+
zone "example" {
type primary;
file "example.db";
diff --git a/bin/tests/system/statschannel/tests.sh b/bin/tests/system/statschannel/tests.sh
index db8bcc263d..6772ac8d1a 100644
--- a/bin/tests/system/statschannel/tests.sh
+++ b/bin/tests/system/statschannel/tests.sh
@@ -18,6 +18,7 @@ set -e
DIGCMD="$DIG @10.53.0.2 -p ${PORT}"
RNDCCMD="$RNDC -c ../_common/rndc.conf -p ${CONTROLPORT} -s"
+NS_PARAMS="-X named.lock -m record -c named.conf -d 99 -g -U 4 -T maxcachesize=2097152"
if ! $FEATURETEST --have-json-c
then
@@ -48,6 +49,32 @@ if [ ! "$PERL_JSON" ] && [ ! "$PERL_XML" ]; then
exit 0
fi
+retry_quiet_fast() {
+ __retries="${1}"
+ shift
+
+ while :; do
+ if "$@"; then
+ return 0
+ fi
+ __retries=$((__retries-1))
+ if [ "${__retries}" -gt 0 ]; then
+ # sleep for 0.1 seconds
+ perl -e 'select(undef, undef, undef, .1)'
+ else
+ return 1
+ fi
+ done
+}
+
+wait_for_log_fast() (
+ timeout="$1"
+ msg="$2"
+ file="$3"
+ retry_quiet_fast "$timeout" _search_log "$msg" "$file" && return 0
+ echo_i "exceeded time limit waiting for literal '$msg' in $file"
+ return 1
+)
getzones() {
sleep 1
@@ -63,6 +90,19 @@ getzones() {
return $result
}
+getxfrins() {
+ echo_i "... using $1"
+ case $1 in
+ xml) path='xml/v3/xfrins' ;;
+ json) path='json/v1/xfrins' ;;
+ *) return 1 ;;
+ esac
+ file=`$PERL fetch.pl -s 10.53.0.3 -p ${EXTRAPORT1} $path`
+ cp $file $file.$1.$3
+ result=$?
+ return $result
+}
+
# TODO: Move loadkeys_on to conf.sh.common
loadkeys_on() {
nsidx=$1
@@ -655,5 +695,38 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
+echo_i "Retransfering 'example' from ns1 to ns3 in slow mode ($n)"
+ret=0
+i=0
+# Restart ns1 with '-T transferslowly' to see the xfrins information in ns3's statschannel while it's ongoing
+stop_server ns1
+start_server --noclean --restart --port ${PORT} ns1 -- "-D statschannel-ns1 $NS_PARAMS -T transferslowly"
+# Request a retransfer of the "example" zone
+nextpart ns3/named.run > /dev/null
+$RNDCCMD 10.53.0.3 retransfer example | sed "s/^/ns3 /" | cat_i
+wait_for_log_fast 200 "zone example/IN: Transfer started" ns3/named.run || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+n=$((n + 1))
+
+# We have now less than one second to catch the zone transfer in process
+echo_i "Checking zone transfer information in the statistics channel ($n)"
+ret=0
+i=0
+getxfrins xml example x$n || ret=1
+getxfrins json example j$n || ret=1
+grep -F 'Initial SOA' xfrins.xml.x$n >/dev/null || ret=1
+grep -F '"state":"Initial SOA"' xfrins.json.j$n >/dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+n=$((n + 1))
+
+echo_i "Wait for slow zone transfer to complete ($n)"
+ret=0
+wait_for_log 20 "zone example/IN: zone transfer finished: success" ns3/named.run || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=$((status + ret))
+n=$((n + 1))
+
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1
diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst
index 3ec2fffb91..332c189bb6 100644
--- a/doc/arm/reference.rst
+++ b/doc/arm/reference.rst
@@ -1932,7 +1932,8 @@ default is used.
operations and the number of authoritative answers per query type. The
default is ``terse``, providing minimal statistics on zones
(including name and current serial number, but not query type
- counters).
+ counters), and also information about the currently ongoing incoming zone
+ transfers.
These statistics may be accessed via the ``statistics-channel`` or
using :option:`rndc stats`, which dumps them to the file listed in the
@@ -5655,6 +5656,7 @@ Broken-out subsets of the statistics can be viewed at
http://127.0.0.1:8888/xml/v3/status (server uptime and last
reconfiguration time), http://127.0.0.1:8888/xml/v3/server (server and
resolver statistics), http://127.0.0.1:8888/xml/v3/zones (zone
+statistics), http://127.0.0.1:8888/xml/v3/xfrins (incoming zone transfer
statistics), http://127.0.0.1:8888/xml/v3/net (network status and socket
statistics), http://127.0.0.1:8888/xml/v3/mem (memory manager
statistics), and http://127.0.0.1:8888/xml/v3/traffic (traffic sizes).
@@ -5664,6 +5666,7 @@ http://127.0.0.1:8888/json, with the broken-out subsets at
http://127.0.0.1:8888/json/v1/status (server uptime and last
reconfiguration time), http://127.0.0.1:8888/json/v1/server (server and
resolver statistics), http://127.0.0.1:8888/json/v1/zones (zone
+statistics), http://127.0.0.1:8888/json/v1/xfrins (incoming zone transfer
statistics), http://127.0.0.1:8888/json/v1/net (network status and
socket statistics), http://127.0.0.1:8888/json/v1/mem (memory manager
statistics), and http://127.0.0.1:8888/json/v1/traffic (traffic sizes).
@@ -7535,6 +7538,146 @@ Outgoing Queries
The number of outgoing queries for each RR type sent from the internal
resolver, maintained per view.
+Incoming Zone Transfers
+ Information about in-progress incoming zone transfers.
+
+ This section describes the information, which can be seen in the
+ HTML table about in-progress incoming zone transfers. It lists
+ the meaning, units and possible range of values of each column,
+ and the key/attribute/element name (in parentheses) for the JSON
+ and XML output formats.
+
+ ``Zone Name`` (``name``)
+ Text string. This is the name of the zone being transferred,
+ as specified in the :any:`zone` declaration on this server.
+
+ ``Zone Type`` (``type``)
+ Text string. This is the type of zone being transferred, as
+ specified in the ``zone`` declaration on this server. Possible
+ values are: ``secondary``, ``stub``, ``redirect``, ``mirror``.
+
+ ``Local Serial`` (``serial``)
+ 32 bit unsigned Integer. This is the current (old) serial
+ number of the zone being transferred. It comes from the SOA
+ record held on the current server.
+
+ ``Remote Serial`` (``remoteserial``)
+ 32 bit unsigned Integer. This is the new serial number of the
+ zone being transferred. It comes from the SOA record held on
+ the primary server from which the zone is being transferred.
+
+ ``IXFR`` (``ixfr``)
+ Boolean. This says whether the transfer is incremental (using
+ IXFR) or full (using AXFR). Possible values are: ``Yes``,
+ ``No``.
+
+ ``State`` (``state``)
+ Text string. This is the current state of the transfer for
+ this zone. Possible values and their meanings are:
+
+ ``Needs Refresh``
+ The zone is flagged for a refresh, but the process
+ hasn't started yet.
+
+ ``Pending``
+ The zone is flagged for a refresh, but the process is
+ in waiting state because of rate-limiting, see
+ :any:`serial-query-rate`.
+
+ ``Deferred``
+ The zone is going to be refreshed, but the process was
+ deferred due to quota, see :any:`transfers-in` and
+ :any:`transfers-per-ns`.
+
+ ``SOA Query``
+ Sending SOA query to get the zone serial number, then
+ follow with a zone transfer, if necessary.
+
+ ``Got SOA``
+ An answer for the SOA query from the previous step is
+ received, initiating a transfer.
+
+ ``Initial SOA``
+ Waiting for the transfer to start, which is expected
+ to begin with an initial SOA record.
+
+ ``First Data``
+ Waiting for the first data record of the transfer.
+
+ ``Receiving IXFR Data``
+ Receiving data for an IXFR type incremental zone
+ transfer.
+
+ ``Finalizing IXFR``
+ Finalizing an IXFR type incremental zone transfer.
+
+ ``Receiving AXFR Data``
+ Receiving data for an AXFR type zone transfer.
+
+ ``Finalizing AXFR``
+ Finalizing an AXFR type zone transfer.
+
+ .. note::
+ State names can change between BIND versions.
+
+ ``Additional Refresh Queued`` (``refreshqueued``)
+ Boolean. This shows that the zone is flagged for a refresh.
+ This can be set to ``Yes`` either when the zone transfer is
+ still in one of the pending states (see the description of
+ the ``State`` column), or when the transfer is in a running
+ state, but the zone was marked for another refresh again (e.g.
+ because of "notify" request from a primary server). Possible
+ values are: ``Yes``, ``No``.
+
+ ``Local Address`` (``localaddr``)
+ IP address - IPv4 or IPv6, as appropriate, and port number.
+ This shows the source address used to establish the connection
+ for the transfer.
+
+ ``Remote Address`` (``remoteaddr``)
+ IP address - IPv4 or IPv6, as appropriate, and port number.
+ This shows the destination address used to establish the
+ connection for the transfer.
+
+ ``Transport`` (``transport``)
+ Text string. This is the transport protocol in use for the
+ transfer. Possible values are: ``TCP``, ``TLS``.
+
+ ``TSIG Key Name`` (``tsigkeyname``)
+ Text string. This is the name of the TSIG key specified for
+ use with this zone in the :any:`zone` declaration (if any).
+
+ ``Duration (s)`` (``duration``)
+ 64 bit unsigned Integer. This is the time, in seconds, that
+ the transfer has been running so far. The clock starts after
+ the zone transfer process is initialized, and restarts shortly
+ after, when transport connection has been established and the
+ XFR request has been sent.
+
+ ``Messages Received`` (``nmsg``)
+ 64 bit unsigned Integer. This is the number of DNS messages
+ received. It does not include transport overheads, such as
+ TCP ACK.
+
+ ``Records Received`` (``nrecs``)
+ 64 bit unsigned Integer. This is the number of individual RRs
+ received so far. If an address record has, for example, five
+ addresses associated with the same name, it counts as five
+ RRs.
+
+ ``Bytes Received`` (``nbytes``)
+ 64 bit unsigned Integer. This is the number of usable bytes
+ of DNS data. It does not include transport overhead.
+
+ .. note::
+ Depending on the current state of the transfer, some of the
+ values may be empty or set to ``-`` (meaning "not available").
+ Also, in the case of the JSON output format, the corresponding
+ keys can be missing or values can be set to ``NULL``. For
+ example, it isn't known whether a transfer is using AXFR or
+ IXFR until the first data is received (see the description
+ of the ``State`` column).
+
Name Server Statistics
Statistics counters for incoming request processing.
diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst
index 0cfdcb2f3f..28c11682c6 100644
--- a/doc/notes/notes-current.rst
+++ b/doc/notes/notes-current.rst
@@ -20,7 +20,8 @@ Security Fixes
New Features
~~~~~~~~~~~~
-- None.
+- The statstics channel now includes information about incoming zone transfers
+ currently in progress. :gl:`#3883`
- The new :any:`resolver-use-dns64` option enables ``named`` to apply
:any:`dns64` rules to IPv4 server addresses when sending recursive
diff --git a/lib/dns/include/dns/transport.h b/lib/dns/include/dns/transport.h
index e6499a97e7..705b1f4f76 100644
--- a/lib/dns/include/dns/transport.h
+++ b/lib/dns/include/dns/transport.h
@@ -40,23 +40,23 @@ dns_transport_new(const dns_name_t *name, dns_transport_type_t type,
*/
dns_transport_type_t
-dns_transport_get_type(dns_transport_t *transport);
+dns_transport_get_type(const dns_transport_t *transport);
char *
-dns_transport_get_certfile(dns_transport_t *transport);
+dns_transport_get_certfile(const dns_transport_t *transport);
char *
-dns_transport_get_keyfile(dns_transport_t *transport);
+dns_transport_get_keyfile(const dns_transport_t *transport);
char *
-dns_transport_get_cafile(dns_transport_t *transport);
+dns_transport_get_cafile(const dns_transport_t *transport);
char *
-dns_transport_get_remote_hostname(dns_transport_t *transport);
+dns_transport_get_remote_hostname(const dns_transport_t *transport);
char *
-dns_transport_get_endpoint(dns_transport_t *transport);
+dns_transport_get_endpoint(const dns_transport_t *transport);
dns_http_mode_t
-dns_transport_get_mode(dns_transport_t *transport);
+dns_transport_get_mode(const dns_transport_t *transport);
char *
-dns_transport_get_ciphers(dns_transport_t *transport);
+dns_transport_get_ciphers(const dns_transport_t *transport);
char *
-dns_transport_get_tlsname(dns_transport_t *transport);
+dns_transport_get_tlsname(const dns_transport_t *transport);
uint32_t
dns_transport_get_tls_versions(const dns_transport_t *transport);
bool
diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h
index 2d956a38b2..3d10bb42fe 100644
--- a/lib/dns/include/dns/xfrin.h
+++ b/lib/dns/include/dns/xfrin.h
@@ -28,6 +28,7 @@
#include
#include
+#include
#include
#include
@@ -82,6 +83,109 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
* the zone has a database.
*/
+isc_time_t
+dns_xfrin_getstarttime(const dns_xfrin_t *xfr);
+/*%<
+ * Get the start time of the xfrin object.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li Transfer start time
+ *
+ */
+
+void
+dns_xfrin_getstate(const dns_xfrin_t *xfr, const char **statestr,
+ bool *is_first_data_received, bool *is_ixfr);
+/*%<
+ * Get the current state of the xfrin object as a character string, and whether
+ * it's currently known to be an IXFR transfer as a boolean value.
+ *
+ * Notes:
+ *\li The 'is_ixfr' value is valid only if 'is_first_data_received' is true.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ */
+
+uint32_t
+dns_xfrin_getendserial(const dns_xfrin_t *xfr);
+/*%<
+ * Get the 'end_serial' of the xfrin object.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li Serial number of the new version zone (if it's already known), or 0.
+ *
+ */
+
+void
+dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
+ uint64_t *nbytesp);
+/*%<
+ * Get various statistics values of the xfrin object: number of the received
+ * messages, number of the received records, number of the received bytes.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ */
+
+const isc_sockaddr_t *
+dns_xfrin_getsourceaddr(const dns_xfrin_t *xfr);
+/*%<
+ * Get the source socket address of the xfrin object.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li const pointer to the zone transfer's source socket address
+ */
+
+const isc_sockaddr_t *
+dns_xfrin_getprimaryaddr(const dns_xfrin_t *xfr);
+/*%<
+ * Get the socket address of the primary server of the xfrin object.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li const pointer to the zone transfer's primary server's socket address
+ */
+
+const dns_transport_t *
+dns_xfrin_gettransport(const dns_xfrin_t *xfr);
+/*%<
+ * Get the trnasport of the xfrin object.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li const pointer to the zone transfer's transport
+ *
+ */
+
+const dns_name_t *
+dns_xfrin_gettsigkeyname(const dns_xfrin_t *xfr);
+/*%<
+ * Get the name of the xfrin object's TSIG key.
+ *
+ * Requires:
+ *\li 'xfr' is a valid dns_xfrin_t.
+ *
+ * Returns:
+ *\li const pointer to the zone transfer's TSIG key's name or NULL
+ *
+ */
+
void
dns_xfrin_shutdown(dns_xfrin_t *xfr);
/*%<
diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
index b7084676db..233793cc24 100644
--- a/lib/dns/include/dns/zone.h
+++ b/lib/dns/include/dns/zone.h
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
/* Define to 1 for detailed reference tracing */
@@ -1789,6 +1790,28 @@ dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state);
*\li 'state' to be a valid DNS_ZONESTATE_ constant.
*/
+isc_result_t
+dns_zone_getxfr(dns_zone_t *zone, dns_xfrin_t **xfrp, bool *is_running,
+ bool *is_deferred, bool *is_pending, bool *needs_refresh);
+/*%<
+ * Returns the xfrin associated with the zone (if any) with the current
+ * transfer states (as booleans). When no longer needed, the returned xfrin
+ * must be detached.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *\li 'xfrp' to be non NULL and '*xfrp' to be NULL.
+ *\li 'is_running' to be non NULL.
+ *\li 'is_deferred' to be non NULL.
+ *\li 'is_pending' to be non NULL.
+ *\li 'needs_refresh' to be non NULL.
+ *
+ * Returns:
+ * ISC_R_SUCCESS xfrin exists and the information was returned.
+ * ISC_R_NOTFOUND no xfrin was found for the zone.
+ * ISC_R_FAILED error while trying to get the xfrin information
+ */
+
void
dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
isc_sockaddr_t *local, isc_time_t *now);
diff --git a/lib/dns/transport.c b/lib/dns/transport.c
index 010bff7de0..c7e01c0af4 100644
--- a/lib/dns/transport.c
+++ b/lib/dns/transport.c
@@ -94,49 +94,49 @@ list_add(dns_transport_list_t *list, const dns_name_t *name,
}
dns_transport_type_t
-dns_transport_get_type(dns_transport_t *transport) {
+dns_transport_get_type(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->type);
}
char *
-dns_transport_get_certfile(dns_transport_t *transport) {
+dns_transport_get_certfile(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.certfile);
}
char *
-dns_transport_get_keyfile(dns_transport_t *transport) {
+dns_transport_get_keyfile(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.keyfile);
}
char *
-dns_transport_get_cafile(dns_transport_t *transport) {
+dns_transport_get_cafile(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.cafile);
}
char *
-dns_transport_get_remote_hostname(dns_transport_t *transport) {
+dns_transport_get_remote_hostname(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.remote_hostname);
}
char *
-dns_transport_get_endpoint(dns_transport_t *transport) {
+dns_transport_get_endpoint(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->doh.endpoint);
}
dns_http_mode_t
-dns_transport_get_mode(dns_transport_t *transport) {
+dns_transport_get_mode(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->doh.mode);
@@ -294,14 +294,14 @@ dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) {
}
char *
-dns_transport_get_ciphers(dns_transport_t *transport) {
+dns_transport_get_ciphers(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.ciphers);
}
char *
-dns_transport_get_tlsname(dns_transport_t *transport) {
+dns_transport_get_tlsname(const dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
return (transport->tls.tlsname);
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
index 0ee5d3ac57..c830533a27 100644
--- a/lib/dns/xfrin.c
+++ b/lib/dns/xfrin.c
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
#include
#include
@@ -140,11 +141,14 @@ struct dns_xfrin {
dns_diff_t diff; /*%< Pending database changes */
int difflen; /*%< Number of pending tuples */
- xfrin_state_t state;
+ _Atomic xfrin_state_t state;
uint32_t end_serial;
uint32_t expireopt;
- bool edns, is_ixfr, expireoptset;
+ bool edns, expireoptset;
+ atomic_bool is_ixfr;
+ isc_mutex_t statslock;
+ /* Locked by statslock. */
unsigned int nmsg; /*%< Number of messages recvd */
unsigned int nrecs; /*%< Number of records recvd */
uint64_t nbytes; /*%< Number of bytes received */
@@ -269,7 +273,7 @@ static isc_result_t
axfr_init(dns_xfrin_t *xfr) {
isc_result_t result;
- xfr->is_ixfr = false;
+ atomic_store(&xfr->is_ixfr, false);
if (xfr->db != NULL) {
dns_db_detach(&xfr->db);
@@ -385,7 +389,7 @@ ixfr_init(dns_xfrin_t *xfr) {
return (DNS_R_FORMERR);
}
- xfr->is_ixfr = true;
+ atomic_store(&xfr->is_ixfr, true);
INSIST(xfr->db != NULL);
xfr->difflen = 0;
@@ -491,7 +495,9 @@ static isc_result_t
xfr_rr(dns_xfrin_t *xfr, dns_name_t *name, uint32_t ttl, dns_rdata_t *rdata) {
isc_result_t result;
+ LOCK(&xfr->statslock);
xfr->nrecs++;
+ UNLOCK(&xfr->statslock);
if (rdata->type == dns_rdatatype_none ||
dns_rdatatype_ismeta(rdata->type))
@@ -519,7 +525,7 @@ xfr_rr(dns_xfrin_t *xfr, dns_name_t *name, uint32_t ttl, dns_rdata_t *rdata) {
}
redo:
- switch (xfr->state) {
+ switch (atomic_load(&xfr->state)) {
case XFRST_SOAQUERY:
if (rdata->type != dns_rdatatype_soa) {
xfrin_log(xfr, ISC_LOG_NOTICE,
@@ -536,7 +542,7 @@ redo:
xfr->ixfr.request_serial, xfr->end_serial);
FAIL(DNS_R_UPTODATE);
}
- xfr->state = XFRST_GOTSOA;
+ atomic_store(&xfr->state, XFRST_GOTSOA);
break;
case XFRST_GOTSOA:
@@ -578,7 +584,7 @@ redo:
xfr->firstsoa_data = isc_mem_allocate(xfr->mctx, rdata->length);
memcpy(xfr->firstsoa_data, rdata->data, rdata->length);
xfr->firstsoa.data = xfr->firstsoa_data;
- xfr->state = XFRST_FIRSTDATA;
+ atomic_store(&xfr->state, XFRST_FIRSTDATA);
break;
case XFRST_FIRSTDATA:
@@ -593,25 +599,25 @@ redo:
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"got incremental response");
CHECK(ixfr_init(xfr));
- xfr->state = XFRST_IXFR_DELSOA;
+ atomic_store(&xfr->state, XFRST_IXFR_DELSOA);
} else {
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"got nonincremental response");
CHECK(axfr_init(xfr));
- xfr->state = XFRST_AXFR;
+ atomic_store(&xfr->state, XFRST_AXFR);
}
goto redo;
case XFRST_IXFR_DELSOA:
INSIST(rdata->type == dns_rdatatype_soa);
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
- xfr->state = XFRST_IXFR_DEL;
+ atomic_store(&xfr->state, XFRST_IXFR_DEL);
break;
case XFRST_IXFR_DEL:
if (rdata->type == dns_rdatatype_soa) {
uint32_t soa_serial = dns_soa_getserial(rdata);
- xfr->state = XFRST_IXFR_ADDSOA;
+ atomic_store(&xfr->state, XFRST_IXFR_ADDSOA);
xfr->ixfr.current_serial = soa_serial;
goto redo;
}
@@ -621,7 +627,7 @@ redo:
case XFRST_IXFR_ADDSOA:
INSIST(rdata->type == dns_rdatatype_soa);
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
- xfr->state = XFRST_IXFR_ADD;
+ atomic_store(&xfr->state, XFRST_IXFR_ADD);
break;
case XFRST_IXFR_ADD:
@@ -629,7 +635,7 @@ redo:
uint32_t soa_serial = dns_soa_getserial(rdata);
if (soa_serial == xfr->end_serial) {
CHECK(ixfr_commit(xfr));
- xfr->state = XFRST_IXFR_END;
+ atomic_store(&xfr->state, XFRST_IXFR_END);
break;
} else if (soa_serial != xfr->ixfr.current_serial) {
xfrin_log(xfr, ISC_LOG_NOTICE,
@@ -639,7 +645,7 @@ redo:
FAIL(DNS_R_FORMERR);
} else {
CHECK(ixfr_commit(xfr));
- xfr->state = XFRST_IXFR_DELSOA;
+ atomic_store(&xfr->state, XFRST_IXFR_DELSOA);
goto redo;
}
}
@@ -674,7 +680,7 @@ redo:
FAIL(DNS_R_FORMERR);
}
CHECK(axfr_commit(xfr));
- xfr->state = XFRST_AXFR_END;
+ atomic_store(&xfr->state, XFRST_AXFR_END);
break;
}
break;
@@ -762,6 +768,110 @@ xfrin_idledout(void *xfr) {
xfrin_fail(xfr, ISC_R_TIMEDOUT, "maximum idle time exceeded");
}
+isc_time_t
+dns_xfrin_getstarttime(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ return (xfr->start);
+}
+
+void
+dns_xfrin_getstate(const dns_xfrin_t *xfr, const char **statestr,
+ bool *is_first_data_received, bool *is_ixfr) {
+ xfrin_state_t state;
+
+ REQUIRE(VALID_XFRIN(xfr));
+ REQUIRE(statestr != NULL && *statestr == NULL);
+ REQUIRE(is_ixfr != NULL);
+
+ state = atomic_load(&xfr->state);
+ *statestr = "";
+ *is_first_data_received = (state > XFRST_FIRSTDATA);
+ *is_ixfr = atomic_load(&xfr->is_ixfr);
+
+ switch (state) {
+ case XFRST_SOAQUERY:
+ *statestr = "SOA Query";
+ break;
+ case XFRST_GOTSOA:
+ *statestr = "Got SOA";
+ break;
+ case XFRST_INITIALSOA:
+ *statestr = "Initial SOA";
+ break;
+ case XFRST_FIRSTDATA:
+ *statestr = "First Data";
+ break;
+ case XFRST_IXFR_DELSOA:
+ case XFRST_IXFR_DEL:
+ case XFRST_IXFR_ADDSOA:
+ case XFRST_IXFR_ADD:
+ *statestr = "Receiving IXFR Data";
+ break;
+ case XFRST_IXFR_END:
+ *statestr = "Finalizing IXFR";
+ break;
+ case XFRST_AXFR:
+ *statestr = "Receiving AXFR Data";
+ break;
+ case XFRST_AXFR_END:
+ *statestr = "Finalizing AXFR";
+ break;
+ }
+}
+
+uint32_t
+dns_xfrin_getendserial(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ return (xfr->end_serial);
+}
+
+void
+dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
+ uint64_t *nbytesp) {
+ REQUIRE(VALID_XFRIN(xfr));
+ REQUIRE(nmsgp != NULL && nrecsp != NULL && nbytesp != NULL);
+
+ LOCK(&xfr->statslock);
+ *nmsgp = xfr->nmsg;
+ *nrecsp = xfr->nrecs;
+ *nbytesp = xfr->nbytes;
+ UNLOCK(&xfr->statslock);
+}
+
+const isc_sockaddr_t *
+dns_xfrin_getsourceaddr(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ return (&xfr->sourceaddr);
+}
+
+const isc_sockaddr_t *
+dns_xfrin_getprimaryaddr(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ return (&xfr->primaryaddr);
+}
+
+const dns_transport_t *
+dns_xfrin_gettransport(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ return (xfr->transport);
+}
+
+const dns_name_t *
+dns_xfrin_gettsigkeyname(const dns_xfrin_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ if (xfr->tsigkey == NULL || xfr->tsigkey->key == NULL) {
+ return (NULL);
+ }
+
+ return (dst_key_name(xfr->tsigkey->key));
+}
+
void
dns_xfrin_shutdown(dns_xfrin_t *xfr) {
REQUIRE(VALID_XFRIN(xfr));
@@ -823,7 +933,7 @@ xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg) {
{
xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s", msg,
isc_result_totext(result));
- if (xfr->is_ixfr) {
+ if (atomic_load(&xfr->is_ixfr)) {
/*
* Pass special result code to force AXFR retry
*/
@@ -877,7 +987,10 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db,
dns_view_weakattach(dns_zone_getview(zone), &xfr->view);
dns_name_init(&xfr->name, NULL);
+ isc_mutex_init(&xfr->statslock);
+
atomic_init(&xfr->shuttingdown, false);
+ atomic_init(&xfr->is_ixfr, false);
if (db != NULL) {
dns_db_attach(db, &xfr->db);
@@ -886,9 +999,9 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db,
dns_diff_init(xfr->mctx, &xfr->diff);
if (reqtype == dns_rdatatype_soa) {
- xfr->state = XFRST_SOAQUERY;
+ atomic_init(&xfr->state, XFRST_SOAQUERY);
} else {
- xfr->state = XFRST_INITIALSOA;
+ atomic_init(&xfr->state, XFRST_INITIALSOA);
}
xfr->start = isc_time_now();
@@ -1261,9 +1374,11 @@ xfrin_send_request(dns_xfrin_t *xfr) {
CHECK(add_opt(msg, udpsize, reqnsid, reqexpire));
}
+ LOCK(&xfr->statslock);
xfr->nmsg = 0;
xfr->nrecs = 0;
xfr->nbytes = 0;
+ UNLOCK(&xfr->statslock);
xfr->start = isc_time_now();
msg->id = xfr->id;
if (xfr->tsigctx != NULL) {
@@ -1395,9 +1510,11 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
dns_message_setclass(msg, xfr->rdclass);
+ LOCK(&xfr->statslock);
if (xfr->nmsg > 0) {
msg->tcp_continuation = 1;
}
+ UNLOCK(&xfr->statslock);
isc_buffer_init(&buffer, region->base, region->length);
isc_buffer_add(&buffer, region->length);
@@ -1421,8 +1538,8 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
{
if (result == ISC_R_SUCCESS &&
msg->rcode == dns_rcode_formerr && xfr->edns &&
- (xfr->state == XFRST_SOAQUERY ||
- xfr->state == XFRST_INITIALSOA))
+ (atomic_load(&xfr->state) == XFRST_SOAQUERY ||
+ atomic_load(&xfr->state) == XFRST_INITIALSOA))
{
xfr->edns = false;
dns_message_detach(&msg);
@@ -1457,7 +1574,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
dns_message_detach(&msg);
xfrin_reset(xfr);
xfr->reqtype = dns_rdatatype_soa;
- xfr->state = XFRST_SOAQUERY;
+ atomic_store(&xfr->state, XFRST_SOAQUERY);
try_again:
result = xfrin_start(xfr);
if (result != ISC_R_SUCCESS) {
@@ -1481,7 +1598,8 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
goto failure;
}
- if ((xfr->state == XFRST_SOAQUERY || xfr->state == XFRST_INITIALSOA) &&
+ if ((atomic_load(&xfr->state) == XFRST_SOAQUERY ||
+ atomic_load(&xfr->state) == XFRST_INITIALSOA) &&
msg->counts[DNS_SECTION_QUESTION] != 1)
{
xfrin_log(xfr, ISC_LOG_NOTICE, "missing question section");
@@ -1531,7 +1649,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
* if the first RR in the answer section is not a SOA record.
*/
if (xfr->reqtype == dns_rdatatype_ixfr &&
- xfr->state == XFRST_INITIALSOA &&
+ atomic_load(&xfr->state) == XFRST_INITIALSOA &&
msg->counts[DNS_SECTION_ANSWER] == 0)
{
xfrin_log(xfr, ISC_LOG_DEBUG(3),
@@ -1598,24 +1716,29 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
} else if (dns_message_gettsigkey(msg) != NULL) {
xfr->sincetsig++;
+ LOCK(&xfr->statslock);
if (xfr->sincetsig > 100 || xfr->nmsg == 0 ||
- xfr->state == XFRST_AXFR_END ||
- xfr->state == XFRST_IXFR_END)
+ atomic_load(&xfr->state) == XFRST_AXFR_END ||
+ atomic_load(&xfr->state) == XFRST_IXFR_END)
{
+ UNLOCK(&xfr->statslock);
result = DNS_R_EXPECTEDTSIG;
goto failure;
}
+ UNLOCK(&xfr->statslock);
}
/*
* Update the number of messages received.
*/
+ LOCK(&xfr->statslock);
xfr->nmsg++;
/*
* Update the number of bytes received.
*/
xfr->nbytes += buffer.used;
+ UNLOCK(&xfr->statslock);
/*
* Take the context back.
@@ -1628,10 +1751,10 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
get_edns_expire(xfr, msg);
}
- switch (xfr->state) {
+ switch (atomic_load(&xfr->state)) {
case XFRST_GOTSOA:
xfr->reqtype = dns_rdatatype_axfr;
- xfr->state = XFRST_INITIALSOA;
+ atomic_store(&xfr->state, XFRST_INITIALSOA);
CHECK(xfrin_send_request(xfr));
break;
case XFRST_AXFR_END:
@@ -1724,6 +1847,7 @@ xfrin_destroy(dns_xfrin_t *xfr) {
if (msecs == 0) {
msecs = 1;
}
+ LOCK(&xfr->statslock);
persec = (xfr->nbytes * 1000) / msecs;
xfrin_log(xfr, ISC_LOG_INFO,
"Transfer completed: %d messages, %d records, "
@@ -1732,6 +1856,8 @@ xfrin_destroy(dns_xfrin_t *xfr) {
xfr->nmsg, xfr->nrecs, xfr->nbytes,
(unsigned int)(msecs / 1000), (unsigned int)(msecs % 1000),
(unsigned int)persec, xfr->end_serial);
+ UNLOCK(&xfr->statslock);
+ isc_mutex_destroy(&xfr->statslock);
if (xfr->dispentry != NULL) {
dns_dispatch_done(&xfr->dispentry);
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index 9a87540586..8e9c4ca5d6 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -19164,6 +19164,45 @@ dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) {
return (count);
}
+isc_result_t
+dns_zone_getxfr(dns_zone_t *zone, dns_xfrin_t **xfrp, bool *is_running,
+ bool *is_deferred, bool *is_pending, bool *needs_refresh) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(xfrp != NULL && *xfrp == NULL);
+
+ if (zone->zmgr == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_read);
+ LOCK_ZONE(zone);
+ if (zone->xfr != NULL) {
+ dns_xfrin_attach(zone->xfr, xfrp);
+ }
+ if (zone->statelist == &zone->zmgr->xfrin_in_progress) {
+ *is_running = true;
+ *is_deferred = false;
+ *is_pending = false;
+ } else if (zone->statelist == &zone->zmgr->waiting_for_xfrin) {
+ *is_running = false;
+ *is_deferred = true;
+ *is_pending = false;
+ } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH)) {
+ *is_running = false;
+ *is_deferred = false;
+ *is_pending = true;
+ } else {
+ *is_running = false;
+ *is_deferred = false;
+ *is_pending = false;
+ }
+ *needs_refresh = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDREFRESH);
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_read);
+
+ return (ISC_R_SUCCESS);
+}
+
void
dns_zone_lock_keyfiles(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));