Merge branch '3883-expose-data-about-transfers-in-progress' into 'main'

Draft: Implement exposing data about zone transfers in progress

Closes #3883

See merge request isc-projects/bind9!7984
This commit is contained in:
Arаm Sаrgsyаn 2023-09-22 09:40:23 +00:00
commit 2eb8afde52
14 changed files with 1098 additions and 51 deletions

View file

@ -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]

View file

@ -303,6 +303,7 @@
<a href="/xml/v3/status">Status</a>,
<a href="/xml/v3/server">Server</a>,
<a href="/xml/v3/zones">Zones</a>,
<a href="/xml/v3/xfrins">Incoming Zone Transfers</a>,
<a href="/xml/v3/net">Network</a>,
<a href="/xml/v3/mem">Memory</a> and
<a href="/xml/v3/traffic">Traffic Size</a></p>
@ -907,6 +908,59 @@
</xsl:for-each>
</xsl:for-each>
</xsl:if>
<xsl:if test="views/view/xfrins/xfrin">
<xsl:for-each select="views/view">
<h3>Incoming Zone Transfers for View <xsl:value-of select="@name"/></h3>
<table class="xfrins">
<thead>
<tr>
<th>Zone Name</th>
<th>Zone Type</th>
<th>Local Serial</th>
<th>Remote Serial</th>
<th>IXFR</th>
<th>State</th>
<th>Additional Refresh Queued</th>
<th>Local Address</th>
<th>Remote Address</th>
<th>Transport</th>
<th>TSIG Key Name</th>
<th>Duration (s)</th>
<th>Messages Received</th>
<th>Records Received</th>
<th>Bytes Received</th>
</tr>
</thead>
<tbody>
<xsl:for-each select="xfrins/xfrin">
<xsl:variable name="css-class16">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">even</xsl:when>
<xsl:otherwise>odd</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr class="{$css-class16}">
<td><xsl:value-of select="@name"/></td>
<td><xsl:value-of select="type"/></td>
<td><xsl:value-of select="serial"/></td>
<td><xsl:value-of select="remoteserial"/></td>
<td><xsl:value-of select="ixfr"/></td>
<td><xsl:value-of select="state"/></td>
<td><xsl:value-of select="refreshqueued"/></td>
<td><xsl:value-of select="localaddr"/></td>
<td><xsl:value-of select="remoteaddr"/></td>
<td><xsl:value-of select="transport"/></td>
<td><xsl:value-of select="tsigkeyname"/></td>
<td><xsl:value-of select="duration"/></td>
<td><xsl:value-of select="nmsg"/></td>
<td><xsl:value-of select="nrecs"/></td>
<td><xsl:value-of select="nbytes"/></td>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:for-each>
</xsl:if>
<xsl:if test="memory/summary">
<h2>Memory Usage Summary</h2>
<table class="counters">

View file

@ -33,7 +33,9 @@
#include <dns/rdatatype.h>
#include <dns/resolver.h>
#include <dns/stats.h>
#include <dns/transport.h>
#include <dns/view.h>
#include <dns/xfrin.h>
#include <dns/zt.h>
#include <ns/stats.h>
@ -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);

View file

@ -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__

View file

@ -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";

View file

@ -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 '<state>Initial SOA</state>' 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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -28,6 +28,7 @@
#include <isc/lang.h>
#include <isc/refcount.h>
#include <isc/sockaddr.h>
#include <isc/tls.h>
#include <dns/transport.h>
@ -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);
/*%<

View file

@ -35,6 +35,7 @@
#include <dns/rdatastruct.h>
#include <dns/rpz.h>
#include <dns/types.h>
#include <dns/xfrin.h>
#include <dns/zt.h>
/* 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);

View file

@ -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);

View file

@ -16,6 +16,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <isc/atomic.h>
#include <isc/mem.h>
#include <isc/random.h>
#include <isc/result.h>
@ -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);

View file

@ -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));