Home | History | Annotate | Line # | Download | only in named
      1 /*	$NetBSD: statschannel.c,v 1.20 2026/06/19 20:09:59 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <stdbool.h>
     20 
     21 #include <isc/buffer.h>
     22 #include <isc/httpd.h>
     23 #include <isc/mem.h>
     24 #include <isc/once.h>
     25 #include <isc/stats.h>
     26 #include <isc/string.h>
     27 #include <isc/util.h>
     28 
     29 #include <dns/adb.h>
     30 #include <dns/cache.h>
     31 #include <dns/db.h>
     32 #include <dns/opcode.h>
     33 #include <dns/rcode.h>
     34 #include <dns/rdataclass.h>
     35 #include <dns/rdatatype.h>
     36 #include <dns/resolver.h>
     37 #include <dns/stats.h>
     38 #include <dns/transport.h>
     39 #include <dns/view.h>
     40 #include <dns/xfrin.h>
     41 #include <dns/zt.h>
     42 
     43 #include <ns/stats.h>
     44 
     45 #include <named/log.h>
     46 #include <named/server.h>
     47 #include <named/statschannel.h>
     48 
     49 #if HAVE_JSON_C
     50 #include <json_object.h>
     51 #include <linkhash.h>
     52 #endif /* HAVE_JSON_C */
     53 
     54 #if HAVE_LIBXML2
     55 #include <libxml/xmlwriter.h>
     56 #define ISC_XMLCHAR (const xmlChar *)
     57 #endif /* HAVE_LIBXML2 */
     58 
     59 #include "xsl_p.h"
     60 
     61 #define STATS_XML_VERSION_MAJOR "3"
     62 #define STATS_XML_VERSION_MINOR "14"
     63 #define STATS_XML_VERSION STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR
     64 
     65 #define STATS_JSON_VERSION_MAJOR "1"
     66 #define STATS_JSON_VERSION_MINOR "8"
     67 #define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR
     68 
     69 struct named_statschannel {
     70 	/* Unlocked */
     71 	isc_httpdmgr_t *httpdmgr;
     72 	isc_sockaddr_t address;
     73 	isc_mem_t *mctx;
     74 
     75 	/*
     76 	 * Locked by channel lock
     77 	 */
     78 	isc_mutex_t lock;
     79 	dns_acl_t *acl;
     80 
     81 	/* Locked by main loop. */
     82 	ISC_LINK(struct named_statschannel) link;
     83 };
     84 
     85 typedef struct stats_dumparg {
     86 	isc_statsformat_t type;
     87 	void *arg;		 /* type dependent argument */
     88 	int ncounters;		 /* for general statistics */
     89 	int *counterindices;	 /* for general statistics */
     90 	uint64_t *countervalues; /* for general statistics */
     91 	isc_result_t result;
     92 } stats_dumparg_t;
     93 
     94 static isc_once_t once = ISC_ONCE_INIT;
     95 
     96 #if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
     97 #define EXTENDED_STATS
     98 #else /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
     99 #undef EXTENDED_STATS
    100 #endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
    101 
    102 #ifdef EXTENDED_STATS
    103 static const char *
    104 user_zonetype(dns_zone_t *zone) {
    105 	dns_zonetype_t ztype;
    106 	dns_view_t *view;
    107 	static const struct zt {
    108 		const dns_zonetype_t type;
    109 		const char *const string;
    110 	} typemap[] = { { dns_zone_none, "none" },
    111 			{ dns_zone_primary, "primary" },
    112 			{ dns_zone_secondary, "secondary" },
    113 			{ dns_zone_mirror, "mirror" },
    114 			{ dns_zone_stub, "stub" },
    115 			{ dns_zone_staticstub, "static-stub" },
    116 			{ dns_zone_key, "key" },
    117 			{ dns_zone_dlz, "dlz" },
    118 			{ dns_zone_redirect, "redirect" },
    119 			{ 0, NULL } };
    120 	const struct zt *tp;
    121 
    122 	if ((dns_zone_getoptions(zone) & DNS_ZONEOPT_AUTOEMPTY) != 0) {
    123 		return "builtin";
    124 	}
    125 
    126 	view = dns_zone_getview(zone);
    127 	if (view != NULL && strcmp(view->name, "_bind") == 0) {
    128 		return "builtin";
    129 	}
    130 
    131 	ztype = dns_zone_gettype(zone);
    132 	for (tp = typemap; tp->string != NULL && tp->type != ztype; tp++) {
    133 		/* empty */
    134 	}
    135 	return tp->string;
    136 }
    137 #endif /* ifdef EXTENDED_STATS */
    138 
    139 /*%
    140  * Statistics descriptions.  These could be statistically initialized at
    141  * compile time, but we configure them run time in the init_desc() function
    142  * below so that they'll be less susceptible to counter name changes.
    143  */
    144 static const char *nsstats_desc[ns_statscounter_max];
    145 static const char *resstats_desc[dns_resstatscounter_max];
    146 static const char *adbstats_desc[dns_adbstats_max];
    147 static const char *zonestats_desc[dns_zonestatscounter_max];
    148 static const char *sockstats_desc[isc_sockstatscounter_max];
    149 static const char *dnssecstats_desc[dns_dnssecstats_max];
    150 static const char *udpinsizestats_desc[dns_sizecounter_in_max];
    151 static const char *udpoutsizestats_desc[dns_sizecounter_out_max];
    152 static const char *tcpinsizestats_desc[dns_sizecounter_in_max];
    153 static const char *tcpoutsizestats_desc[dns_sizecounter_out_max];
    154 static const char *dnstapstats_desc[dns_dnstapcounter_max];
    155 static const char *gluecachestats_desc[dns_gluecachestatscounter_max];
    156 #if defined(EXTENDED_STATS)
    157 static const char *nsstats_xmldesc[ns_statscounter_max];
    158 static const char *resstats_xmldesc[dns_resstatscounter_max];
    159 static const char *adbstats_xmldesc[dns_adbstats_max];
    160 static const char *zonestats_xmldesc[dns_zonestatscounter_max];
    161 static const char *sockstats_xmldesc[isc_sockstatscounter_max];
    162 static const char *dnssecstats_xmldesc[dns_dnssecstats_max];
    163 static const char *udpinsizestats_xmldesc[dns_sizecounter_in_max];
    164 static const char *udpoutsizestats_xmldesc[dns_sizecounter_out_max];
    165 static const char *tcpinsizestats_xmldesc[dns_sizecounter_in_max];
    166 static const char *tcpoutsizestats_xmldesc[dns_sizecounter_out_max];
    167 static const char *dnstapstats_xmldesc[dns_dnstapcounter_max];
    168 static const char *gluecachestats_xmldesc[dns_gluecachestatscounter_max];
    169 #else /* if defined(EXTENDED_STATS) */
    170 #define nsstats_xmldesc		NULL
    171 #define resstats_xmldesc	NULL
    172 #define adbstats_xmldesc	NULL
    173 #define zonestats_xmldesc	NULL
    174 #define sockstats_xmldesc	NULL
    175 #define dnssecstats_xmldesc	NULL
    176 #define udpinsizestats_xmldesc	NULL
    177 #define udpoutsizestats_xmldesc NULL
    178 #define tcpinsizestats_xmldesc	NULL
    179 #define tcpoutsizestats_xmldesc NULL
    180 #define dnstapstats_xmldesc	NULL
    181 #define gluecachestats_xmldesc	NULL
    182 #endif /* EXTENDED_STATS */
    183 
    184 #define TRY0(a)                       \
    185 	do {                          \
    186 		xmlrc = (a);          \
    187 		if (xmlrc < 0)        \
    188 			goto cleanup; \
    189 	} while (0)
    190 
    191 /*%
    192  * Mapping arrays to represent statistics counters in the order of our
    193  * preference, regardless of the order of counter indices.  For example,
    194  * nsstats_desc[nsstats_index[0]] will be the description that is shown first.
    195  */
    196 static int nsstats_index[ns_statscounter_max];
    197 static int resstats_index[dns_resstatscounter_max];
    198 static int adbstats_index[dns_adbstats_max];
    199 static int zonestats_index[dns_zonestatscounter_max];
    200 static int sockstats_index[isc_sockstatscounter_max];
    201 static int dnssecstats_index[dns_dnssecstats_max];
    202 static int udpinsizestats_index[dns_sizecounter_in_max];
    203 static int udpoutsizestats_index[dns_sizecounter_out_max];
    204 static int tcpinsizestats_index[dns_sizecounter_in_max];
    205 static int tcpoutsizestats_index[dns_sizecounter_out_max];
    206 static int dnstapstats_index[dns_dnstapcounter_max];
    207 static int gluecachestats_index[dns_gluecachestatscounter_max];
    208 
    209 static void
    210 set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
    211 	 const char *xdesc, const char **xdescs) {
    212 	REQUIRE(counter < maxcounter);
    213 	REQUIRE(fdescs != NULL && fdescs[counter] == NULL);
    214 #if defined(EXTENDED_STATS)
    215 	REQUIRE(xdescs != NULL && xdescs[counter] == NULL);
    216 #endif /* if defined(EXTENDED_STATS) */
    217 
    218 	fdescs[counter] = fdesc;
    219 #if defined(EXTENDED_STATS)
    220 	xdescs[counter] = xdesc;
    221 #else  /* if defined(EXTENDED_STATS) */
    222 	UNUSED(xdesc);
    223 	UNUSED(xdescs);
    224 #endif /* if defined(EXTENDED_STATS) */
    225 }
    226 
    227 static const char *
    228 get_histo_desc(const char *prefix, int i, int inf, bool ext) {
    229 	static char buf[(DNS_SIZEHISTO_MAXIN + DNS_SIZEHISTO_MAXOUT) * 80];
    230 	static size_t used = 0;
    231 	char *desc = buf + used;
    232 	size_t space = sizeof(buf) - used;
    233 	int min = DNS_SIZEHISTO_QUANTUM * i;
    234 	int max = DNS_SIZEHISTO_QUANTUM * (i + 1) - 1;
    235 	int len = 0;
    236 
    237 	if (!ext && i < inf) {
    238 		len = snprintf(desc, space, "%s %u-%u bytes", prefix, min, max);
    239 	} else if (!ext && i >= inf) {
    240 		len = snprintf(desc, space, "%s %u+ bytes", prefix, min);
    241 	} else if (ext && i < inf) {
    242 		len = snprintf(desc, space, "%u-%u", min, max);
    243 	} else if (ext && i >= inf) {
    244 		len = snprintf(desc, space, "%u+", min);
    245 	}
    246 	INSIST(0 < len && (size_t)len < space);
    247 	used += len + 1;
    248 	return desc;
    249 }
    250 
    251 static void
    252 init_desc(void) {
    253 	int i;
    254 
    255 	/* Initialize name server statistics */
    256 	for (i = 0; i < ns_statscounter_max; i++) {
    257 		nsstats_desc[i] = NULL;
    258 	}
    259 #if defined(EXTENDED_STATS)
    260 	for (i = 0; i < ns_statscounter_max; i++) {
    261 		nsstats_xmldesc[i] = NULL;
    262 	}
    263 #endif /* if defined(EXTENDED_STATS) */
    264 
    265 #define SET_NSSTATDESC(counterid, desc, xmldesc)                           \
    266 	do {                                                               \
    267 		set_desc(ns_statscounter_##counterid, ns_statscounter_max, \
    268 			 desc, nsstats_desc, xmldesc, nsstats_xmldesc);    \
    269 		nsstats_index[i++] = ns_statscounter_##counterid;          \
    270 	} while (0)
    271 
    272 	i = 0;
    273 	SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4");
    274 	SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6");
    275 	SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0");
    276 	SET_NSSTATDESC(badednsver,
    277 		       "requests with unsupported EDNS version received",
    278 		       "ReqBadEDNSVer");
    279 	SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG");
    280 	SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
    281 	SET_NSSTATDESC(invalidsig, "requests with invalid signature",
    282 		       "ReqBadSIG");
    283 	SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP");
    284 	SET_NSSTATDESC(tcphighwater, "TCP connection high-water",
    285 		       "TCPConnHighWater");
    286 	SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
    287 	SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
    288 	SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
    289 	SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej");
    290 	SET_NSSTATDESC(response, "responses sent", "Response");
    291 	SET_NSSTATDESC(truncatedresp, "truncated responses sent",
    292 		       "TruncatedResp");
    293 	SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0");
    294 	SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG");
    295 	SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0");
    296 	SET_NSSTATDESC(success, "queries resulted in successful answer",
    297 		       "QrySuccess");
    298 	SET_NSSTATDESC(authans, "queries resulted in authoritative answer",
    299 		       "QryAuthAns");
    300 	SET_NSSTATDESC(nonauthans,
    301 		       "queries resulted in non authoritative answer",
    302 		       "QryNoauthAns");
    303 	SET_NSSTATDESC(referral, "queries resulted in referral answer",
    304 		       "QryReferral");
    305 	SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset");
    306 	SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL");
    307 	SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR");
    308 	SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN");
    309 	SET_NSSTATDESC(recursion, "queries caused recursion", "QryRecursion");
    310 	SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate");
    311 	SET_NSSTATDESC(dropped, "queries dropped", "QryDropped");
    312 	SET_NSSTATDESC(failure, "other query failures", "QryFailure");
    313 	SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone");
    314 	SET_NSSTATDESC(updatereqfwd, "update requests forwarded",
    315 		       "UpdateReqFwd");
    316 	SET_NSSTATDESC(updaterespfwd, "update responses forwarded",
    317 		       "UpdateRespFwd");
    318 	SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail");
    319 	SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone");
    320 	SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail");
    321 	SET_NSSTATDESC(updatebadprereq,
    322 		       "updates rejected due to prerequisite failure",
    323 		       "UpdateBadPrereq");
    324 	SET_NSSTATDESC(recurshighwater, "Recursive clients high-water",
    325 		       "RecursHighwater");
    326 	SET_NSSTATDESC(recursclients, "recursing clients", "RecursClients");
    327 	SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64");
    328 	SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
    329 		       "RateDropped");
    330 	SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
    331 		       "RateSlipped");
    332 	SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
    333 		       "RPZRewrites");
    334 	SET_NSSTATDESC(udp, "UDP queries received", "QryUDP");
    335 	SET_NSSTATDESC(tcp, "TCP queries received", "QryTCP");
    336 	SET_NSSTATDESC(nsidopt, "NSID option received", "NSIDOpt");
    337 	SET_NSSTATDESC(expireopt, "Expire option received", "ExpireOpt");
    338 	SET_NSSTATDESC(keepaliveopt, "EDNS TCP keepalive option received",
    339 		       "KeepAliveOpt");
    340 	SET_NSSTATDESC(padopt, "EDNS padding option received", "PadOpt");
    341 	SET_NSSTATDESC(otheropt, "Other EDNS option received", "OtherOpt");
    342 	SET_NSSTATDESC(cookiein, "COOKIE option received", "CookieIn");
    343 	SET_NSSTATDESC(cookienew, "COOKIE - client only", "CookieNew");
    344 	SET_NSSTATDESC(cookiebadsize, "COOKIE - bad size", "CookieBadSize");
    345 	SET_NSSTATDESC(cookiebadtime, "COOKIE - bad time", "CookieBadTime");
    346 	SET_NSSTATDESC(cookienomatch, "COOKIE - no match", "CookieNoMatch");
    347 	SET_NSSTATDESC(cookiematch, "COOKIE - match", "CookieMatch");
    348 	SET_NSSTATDESC(ecsopt, "EDNS client subnet option received", "ECSOpt");
    349 	SET_NSSTATDESC(nxdomainredirect,
    350 		       "queries resulted in NXDOMAIN that were redirected",
    351 		       "QryNXRedir");
    352 	SET_NSSTATDESC(nxdomainredirect_rlookup,
    353 		       "queries resulted in NXDOMAIN that were redirected and "
    354 		       "resulted in a successful remote lookup",
    355 		       "QryNXRedirRLookup");
    356 	SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE");
    357 	SET_NSSTATDESC(nxdomainsynth, "synthesized a NXDOMAIN response",
    358 		       "SynthNXDOMAIN");
    359 	SET_NSSTATDESC(nodatasynth, "synthesized a no-data response",
    360 		       "SynthNODATA");
    361 	SET_NSSTATDESC(wildcardsynth, "synthesized a wildcard response",
    362 		       "SynthWILDCARD");
    363 	SET_NSSTATDESC(trystale,
    364 		       "attempts to use stale cache data after lookup failure",
    365 		       "QryTryStale");
    366 	SET_NSSTATDESC(usedstale,
    367 		       "successful uses of stale cache data after lookup "
    368 		       "failure",
    369 		       "QryUsedStale");
    370 	SET_NSSTATDESC(prefetch, "queries triggered prefetch", "Prefetch");
    371 	SET_NSSTATDESC(keytagopt, "Keytag option received", "KeyTagOpt");
    372 	SET_NSSTATDESC(reclimitdropped,
    373 		       "queries dropped due to recursive client limit",
    374 		       "RecLimitDropped");
    375 	SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota");
    376 
    377 	INSIST(i == ns_statscounter_max);
    378 
    379 	/* Initialize resolver statistics */
    380 	for (i = 0; i < dns_resstatscounter_max; i++) {
    381 		resstats_desc[i] = NULL;
    382 	}
    383 #if defined(EXTENDED_STATS)
    384 	for (i = 0; i < dns_resstatscounter_max; i++) {
    385 		resstats_xmldesc[i] = NULL;
    386 	}
    387 #endif /* if defined(EXTENDED_STATS) */
    388 
    389 #define SET_RESSTATDESC(counterid, desc, xmldesc)                      \
    390 	do {                                                           \
    391 		set_desc(dns_resstatscounter_##counterid,              \
    392 			 dns_resstatscounter_max, desc, resstats_desc, \
    393 			 xmldesc, resstats_xmldesc);                   \
    394 		resstats_index[i++] = dns_resstatscounter_##counterid; \
    395 	} while (0)
    396 
    397 	i = 0;
    398 	SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4");
    399 	SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6");
    400 	SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4");
    401 	SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6");
    402 	SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN");
    403 	SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL");
    404 	SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR");
    405 	SET_RESSTATDESC(othererror, "other errors received", "OtherError");
    406 	SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail");
    407 	SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch");
    408 	SET_RESSTATDESC(truncated, "truncated responses received", "Truncated");
    409 	SET_RESSTATDESC(lame, "lame delegations received", "Lame");
    410 	SET_RESSTATDESC(retry, "query retries", "Retry");
    411 	SET_RESSTATDESC(dispabort, "queries aborted due to quota",
    412 			"QueryAbort");
    413 	SET_RESSTATDESC(dispsockfail, "failures in opening query sockets",
    414 			"QuerySockFail");
    415 	SET_RESSTATDESC(disprequdp, "UDP queries in progress", "QueryCurUDP");
    416 	SET_RESSTATDESC(dispreqtcp, "TCP queries in progress", "QueryCurTCP");
    417 	SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout");
    418 	SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4");
    419 	SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6");
    420 	SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed",
    421 			"GlueFetchv4Fail");
    422 	SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed",
    423 			"GlueFetchv6Fail");
    424 	SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt");
    425 	SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk");
    426 	SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded",
    427 			"ValNegOk");
    428 	SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail");
    429 	SET_RESSTATDESC(queryrtt0,
    430 			"queries with RTT < " DNS_RESOLVER_QRYRTTCLASS0STR "ms",
    431 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
    432 	SET_RESSTATDESC(queryrtt1,
    433 			"queries with RTT " DNS_RESOLVER_QRYRTTCLASS0STR
    434 			"-" DNS_RESOLVER_QRYRTTCLASS1STR "ms",
    435 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
    436 	SET_RESSTATDESC(queryrtt2,
    437 			"queries with RTT " DNS_RESOLVER_QRYRTTCLASS1STR
    438 			"-" DNS_RESOLVER_QRYRTTCLASS2STR "ms",
    439 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
    440 	SET_RESSTATDESC(queryrtt3,
    441 			"queries with RTT " DNS_RESOLVER_QRYRTTCLASS2STR
    442 			"-" DNS_RESOLVER_QRYRTTCLASS3STR "ms",
    443 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
    444 	SET_RESSTATDESC(queryrtt4,
    445 			"queries with RTT " DNS_RESOLVER_QRYRTTCLASS3STR
    446 			"-" DNS_RESOLVER_QRYRTTCLASS4STR "ms",
    447 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
    448 	SET_RESSTATDESC(queryrtt5,
    449 			"queries with RTT > " DNS_RESOLVER_QRYRTTCLASS4STR "ms",
    450 			"QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
    451 	SET_RESSTATDESC(nfetch, "active fetches", "NumFetch");
    452 	SET_RESSTATDESC(buckets, "bucket size", "BucketSize");
    453 	SET_RESSTATDESC(refused, "REFUSED received", "REFUSED");
    454 	SET_RESSTATDESC(cookienew, "COOKIE send with client cookie only",
    455 			"ClientCookieOut");
    456 	SET_RESSTATDESC(cookieout, "COOKIE sent with client and server cookie",
    457 			"ServerCookieOut");
    458 	SET_RESSTATDESC(cookiein, "COOKIE replies received", "CookieIn");
    459 	SET_RESSTATDESC(cookieok, "COOKIE client ok", "CookieClientOk");
    460 	SET_RESSTATDESC(badvers, "bad EDNS version", "BadEDNSVersion");
    461 	SET_RESSTATDESC(badcookie, "bad cookie rcode", "BadCookieRcode");
    462 	SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota");
    463 	SET_RESSTATDESC(serverquota, "spilled due to server quota",
    464 			"ServerQuota");
    465 	SET_RESSTATDESC(clientquota, "spilled due to clients per query quota",
    466 			"ClientQuota");
    467 	SET_RESSTATDESC(nextitem, "waited for next item", "NextItem");
    468 	SET_RESSTATDESC(priming, "priming queries", "Priming");
    469 	SET_RESSTATDESC(mismatchtcp,
    470 			"queries retried over TCP after a response with "
    471 			"mismatched query id",
    472 			"MismatchTCP");
    473 
    474 	INSIST(i == dns_resstatscounter_max);
    475 
    476 	/* Initialize adb statistics */
    477 	for (i = 0; i < dns_adbstats_max; i++) {
    478 		adbstats_desc[i] = NULL;
    479 	}
    480 #if defined(EXTENDED_STATS)
    481 	for (i = 0; i < dns_adbstats_max; i++) {
    482 		adbstats_xmldesc[i] = NULL;
    483 	}
    484 #endif /* if defined(EXTENDED_STATS) */
    485 
    486 #define SET_ADBSTATDESC(id, desc, xmldesc)                          \
    487 	do {                                                        \
    488 		set_desc(dns_adbstats_##id, dns_adbstats_max, desc, \
    489 			 adbstats_desc, xmldesc, adbstats_xmldesc); \
    490 		adbstats_index[i++] = dns_adbstats_##id;            \
    491 	} while (0)
    492 	i = 0;
    493 	SET_ADBSTATDESC(nentries, "Address hash table size", "nentries");
    494 	SET_ADBSTATDESC(entriescnt, "Addresses in hash table", "entriescnt");
    495 	SET_ADBSTATDESC(nnames, "Name hash table size", "nnames");
    496 	SET_ADBSTATDESC(namescnt, "Names in hash table", "namescnt");
    497 
    498 	INSIST(i == dns_adbstats_max);
    499 
    500 	/* Initialize zone statistics */
    501 	for (i = 0; i < dns_zonestatscounter_max; i++) {
    502 		zonestats_desc[i] = NULL;
    503 	}
    504 #if defined(EXTENDED_STATS)
    505 	for (i = 0; i < dns_zonestatscounter_max; i++) {
    506 		zonestats_xmldesc[i] = NULL;
    507 	}
    508 #endif /* if defined(EXTENDED_STATS) */
    509 
    510 #define SET_ZONESTATDESC(counterid, desc, xmldesc)                       \
    511 	do {                                                             \
    512 		set_desc(dns_zonestatscounter_##counterid,               \
    513 			 dns_zonestatscounter_max, desc, zonestats_desc, \
    514 			 xmldesc, zonestats_xmldesc);                    \
    515 		zonestats_index[i++] = dns_zonestatscounter_##counterid; \
    516 	} while (0)
    517 
    518 	i = 0;
    519 	SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4");
    520 	SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6");
    521 	SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4");
    522 	SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6");
    523 	SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej");
    524 	SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4");
    525 	SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6");
    526 	SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4");
    527 	SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6");
    528 	SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4");
    529 	SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6");
    530 	SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded",
    531 			 "XfrSuccess");
    532 	SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail");
    533 	INSIST(i == dns_zonestatscounter_max);
    534 
    535 	/* Initialize socket statistics */
    536 	for (i = 0; i < isc_sockstatscounter_max; i++) {
    537 		sockstats_desc[i] = NULL;
    538 	}
    539 #if defined(EXTENDED_STATS)
    540 	for (i = 0; i < isc_sockstatscounter_max; i++) {
    541 		sockstats_xmldesc[i] = NULL;
    542 	}
    543 #endif /* if defined(EXTENDED_STATS) */
    544 
    545 #define SET_SOCKSTATDESC(counterid, desc, xmldesc)                       \
    546 	do {                                                             \
    547 		set_desc(isc_sockstatscounter_##counterid,               \
    548 			 isc_sockstatscounter_max, desc, sockstats_desc, \
    549 			 xmldesc, sockstats_xmldesc);                    \
    550 		sockstats_index[i++] = isc_sockstatscounter_##counterid; \
    551 	} while (0)
    552 
    553 	i = 0;
    554 	SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open");
    555 	SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open");
    556 	SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open");
    557 	SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open");
    558 	SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures",
    559 			 "UDP4OpenFail");
    560 	SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures",
    561 			 "UDP6OpenFail");
    562 	SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures",
    563 			 "TCP4OpenFail");
    564 	SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures",
    565 			 "TCP6OpenFail");
    566 	SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close");
    567 	SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close");
    568 	SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close");
    569 	SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close");
    570 	SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures",
    571 			 "UDP4BindFail");
    572 	SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures",
    573 			 "UDP6BindFail");
    574 	SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures",
    575 			 "TCP4BindFail");
    576 	SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures",
    577 			 "TCP6BindFail");
    578 	SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures",
    579 			 "UDP4ConnFail");
    580 	SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures",
    581 			 "UDP6ConnFail");
    582 	SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures",
    583 			 "TCP4ConnFail");
    584 	SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures",
    585 			 "TCP6ConnFail");
    586 	SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established",
    587 			 "UDP4Conn");
    588 	SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established",
    589 			 "UDP6Conn");
    590 	SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established",
    591 			 "TCP4Conn");
    592 	SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established",
    593 			 "TCP6Conn");
    594 	SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures",
    595 			 "TCP4AcceptFail");
    596 	SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures",
    597 			 "TCP6AcceptFail");
    598 	SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted",
    599 			 "TCP4Accept");
    600 	SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted",
    601 			 "TCP6Accept");
    602 	SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr");
    603 	SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr");
    604 	SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr");
    605 	SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr");
    606 	SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr");
    607 	SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr");
    608 	SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr");
    609 	SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr");
    610 	SET_SOCKSTATDESC(udp4active, "UDP/IPv4 sockets active", "UDP4Active");
    611 	SET_SOCKSTATDESC(udp6active, "UDP/IPv6 sockets active", "UDP6Active");
    612 	SET_SOCKSTATDESC(tcp4active, "TCP/IPv4 sockets active", "TCP4Active");
    613 	SET_SOCKSTATDESC(tcp6active, "TCP/IPv6 sockets active", "TCP6Active");
    614 	SET_SOCKSTATDESC(tcp4clients, "TCP/IPv4 clients currently connected",
    615 			 "TCP4Clients");
    616 	SET_SOCKSTATDESC(tcp6clients, "TCP/IPv6 clients currently connected",
    617 			 "TCP6Clients");
    618 	INSIST(i == isc_sockstatscounter_max);
    619 
    620 	/* Initialize DNSSEC statistics */
    621 	for (i = 0; i < dns_dnssecstats_max; i++) {
    622 		dnssecstats_desc[i] = NULL;
    623 	}
    624 #if defined(EXTENDED_STATS)
    625 	for (i = 0; i < dns_dnssecstats_max; i++) {
    626 		dnssecstats_xmldesc[i] = NULL;
    627 	}
    628 #endif /* if defined(EXTENDED_STATS) */
    629 
    630 #define SET_DNSSECSTATDESC(counterid, desc, xmldesc)                       \
    631 	do {                                                               \
    632 		set_desc(dns_dnssecstats_##counterid, dns_dnssecstats_max, \
    633 			 desc, dnssecstats_desc, xmldesc,                  \
    634 			 dnssecstats_xmldesc);                             \
    635 		dnssecstats_index[i++] = dns_dnssecstats_##counterid;      \
    636 	} while (0)
    637 
    638 	i = 0;
    639 	SET_DNSSECSTATDESC(asis,
    640 			   "dnssec validation success with signer "
    641 			   "\"as is\"",
    642 			   "DNSSECasis");
    643 	SET_DNSSECSTATDESC(downcase,
    644 			   "dnssec validation success with signer "
    645 			   "lower cased",
    646 			   "DNSSECdowncase");
    647 	SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature",
    648 			   "DNSSECwild");
    649 	SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail");
    650 	INSIST(i == dns_dnssecstats_max);
    651 
    652 	/* Initialize dnstap statistics */
    653 	for (i = 0; i < dns_dnstapcounter_max; i++) {
    654 		dnstapstats_desc[i] = NULL;
    655 	}
    656 #if defined(EXTENDED_STATS)
    657 	for (i = 0; i < dns_dnstapcounter_max; i++) {
    658 		dnstapstats_xmldesc[i] = NULL;
    659 	}
    660 #endif /* if defined(EXTENDED_STATS) */
    661 
    662 #define SET_DNSTAPSTATDESC(counterid, desc, xmldesc)                           \
    663 	do {                                                                   \
    664 		set_desc(dns_dnstapcounter_##counterid, dns_dnstapcounter_max, \
    665 			 desc, dnstapstats_desc, xmldesc,                      \
    666 			 dnstapstats_xmldesc);                                 \
    667 		dnstapstats_index[i++] = dns_dnstapcounter_##counterid;        \
    668 	} while (0)
    669 	i = 0;
    670 	SET_DNSTAPSTATDESC(success, "dnstap messages written", "DNSTAPsuccess");
    671 	SET_DNSTAPSTATDESC(drop, "dnstap messages dropped", "DNSTAPdropped");
    672 	INSIST(i == dns_dnstapcounter_max);
    673 
    674 #define SET_GLUECACHESTATDESC(counterid, desc, xmldesc)         \
    675 	do {                                                    \
    676 		set_desc(dns_gluecachestatscounter_##counterid, \
    677 			 dns_gluecachestatscounter_max, desc,   \
    678 			 gluecachestats_desc, xmldesc,          \
    679 			 gluecachestats_xmldesc);               \
    680 		gluecachestats_index[i++] =                     \
    681 			dns_gluecachestatscounter_##counterid;  \
    682 	} while (0)
    683 	i = 0;
    684 	SET_GLUECACHESTATDESC(hits_present, "Hits for present glue (cached)",
    685 			      "GLUECACHEhitspresent");
    686 	SET_GLUECACHESTATDESC(hits_absent,
    687 			      "Hits for non-existent glue (cached)",
    688 			      "GLUECACHEhitsabsent");
    689 	SET_GLUECACHESTATDESC(inserts_present,
    690 			      "Miss-plus-cache-inserts for present glue",
    691 			      "GLUECACHEinsertspresent");
    692 	SET_GLUECACHESTATDESC(inserts_absent,
    693 			      "Miss-plus-cache-inserts for non-existent glue",
    694 			      "GLUECACHEinsertsabsent");
    695 	INSIST(i == dns_gluecachestatscounter_max);
    696 
    697 	/* Sanity check */
    698 	for (i = 0; i < ns_statscounter_max; i++) {
    699 		INSIST(nsstats_desc[i] != NULL);
    700 	}
    701 	for (i = 0; i < dns_resstatscounter_max; i++) {
    702 		INSIST(resstats_desc[i] != NULL);
    703 	}
    704 	for (i = 0; i < dns_adbstats_max; i++) {
    705 		INSIST(adbstats_desc[i] != NULL);
    706 	}
    707 	for (i = 0; i < dns_zonestatscounter_max; i++) {
    708 		INSIST(zonestats_desc[i] != NULL);
    709 	}
    710 	for (i = 0; i < isc_sockstatscounter_max; i++) {
    711 		INSIST(sockstats_desc[i] != NULL);
    712 	}
    713 	for (i = 0; i < dns_dnssecstats_max; i++) {
    714 		INSIST(dnssecstats_desc[i] != NULL);
    715 	}
    716 	for (i = 0; i < dns_dnstapcounter_max; i++) {
    717 		INSIST(dnstapstats_desc[i] != NULL);
    718 	}
    719 	for (i = 0; i < dns_gluecachestatscounter_max; i++) {
    720 		INSIST(gluecachestats_desc[i] != NULL);
    721 	}
    722 #if defined(EXTENDED_STATS)
    723 	for (i = 0; i < ns_statscounter_max; i++) {
    724 		INSIST(nsstats_xmldesc[i] != NULL);
    725 	}
    726 	for (i = 0; i < dns_resstatscounter_max; i++) {
    727 		INSIST(resstats_xmldesc[i] != NULL);
    728 	}
    729 	for (i = 0; i < dns_adbstats_max; i++) {
    730 		INSIST(adbstats_xmldesc[i] != NULL);
    731 	}
    732 	for (i = 0; i < dns_zonestatscounter_max; i++) {
    733 		INSIST(zonestats_xmldesc[i] != NULL);
    734 	}
    735 	for (i = 0; i < isc_sockstatscounter_max; i++) {
    736 		INSIST(sockstats_xmldesc[i] != NULL);
    737 	}
    738 	for (i = 0; i < dns_dnssecstats_max; i++) {
    739 		INSIST(dnssecstats_xmldesc[i] != NULL);
    740 	}
    741 	for (i = 0; i < dns_dnstapcounter_max; i++) {
    742 		INSIST(dnstapstats_xmldesc[i] != NULL);
    743 	}
    744 	for (i = 0; i < dns_gluecachestatscounter_max; i++) {
    745 		INSIST(gluecachestats_xmldesc[i] != NULL);
    746 	}
    747 #endif /* if defined(EXTENDED_STATS) */
    748 
    749 	/* Initialize traffic size statistics */
    750 
    751 	for (i = 0; i < DNS_SIZEHISTO_MAXOUT; i++) {
    752 		udpoutsizestats_index[i] = i;
    753 		tcpoutsizestats_index[i] = i;
    754 		udpoutsizestats_desc[i] = get_histo_desc(
    755 			"responses sent", i, DNS_SIZEHISTO_MAXOUT, false);
    756 		tcpoutsizestats_desc[i] = udpoutsizestats_desc[i];
    757 #if defined(EXTENDED_STATS)
    758 		udpoutsizestats_xmldesc[i] = get_histo_desc(
    759 			"responses sent", i, DNS_SIZEHISTO_MAXOUT, true);
    760 		tcpoutsizestats_xmldesc[i] = udpoutsizestats_xmldesc[i];
    761 #endif /* if defined(EXTENDED_STATS) */
    762 	}
    763 
    764 	for (i = 0; i <= DNS_SIZEHISTO_MAXIN; i++) {
    765 		udpinsizestats_index[i] = i;
    766 		tcpinsizestats_index[i] = i;
    767 		udpinsizestats_desc[i] = get_histo_desc(
    768 			"requests received", i, DNS_SIZEHISTO_MAXIN, false);
    769 		tcpinsizestats_desc[i] = udpinsizestats_desc[i];
    770 #if defined(EXTENDED_STATS)
    771 		if (i < DNS_SIZEHISTO_MAXIN) {
    772 			udpinsizestats_xmldesc[i] = udpoutsizestats_xmldesc[i];
    773 			tcpinsizestats_xmldesc[i] = tcpoutsizestats_xmldesc[i];
    774 		} else {
    775 			udpinsizestats_xmldesc[i] =
    776 				get_histo_desc("requests received", i,
    777 					       DNS_SIZEHISTO_MAXIN, true);
    778 			tcpinsizestats_xmldesc[i] = udpinsizestats_xmldesc[i];
    779 		}
    780 #endif /* if defined(EXTENDED_STATS) */
    781 	}
    782 }
    783 
    784 /*%
    785  * Dump callback functions.
    786  */
    787 
    788 static isc_result_t
    789 dump_counters(isc_statsformat_t type, void *arg, const char *category,
    790 	      const char **desc, int ncounters, int *indices, uint64_t *values,
    791 	      int options);
    792 
    793 static void
    794 generalstat_dump(isc_statscounter_t counter, uint64_t val, void *arg) {
    795 	stats_dumparg_t *dumparg = arg;
    796 
    797 	REQUIRE(counter < dumparg->ncounters);
    798 	dumparg->countervalues[counter] = val;
    799 }
    800 
    801 static isc_result_t
    802 dump_stats(isc_stats_t *stats, isc_statsformat_t type, void *arg,
    803 	   const char *category, const char **desc, int ncounters, int *indices,
    804 	   uint64_t *values, int options) {
    805 	stats_dumparg_t dumparg;
    806 
    807 	dumparg.type = type;
    808 	dumparg.ncounters = ncounters;
    809 	dumparg.counterindices = indices;
    810 	dumparg.countervalues = values;
    811 
    812 	memset(values, 0, sizeof(values[0]) * ncounters);
    813 	isc_stats_dump(stats, generalstat_dump, &dumparg, options);
    814 
    815 	return dump_counters(type, arg, category, desc, ncounters, indices,
    816 			     values, options);
    817 }
    818 
    819 #if defined(EXTENDED_STATS)
    820 static isc_result_t
    821 dump_histo(isc_histomulti_t *hm, isc_statsformat_t type, void *arg,
    822 	   const char *category, const char **desc, int ncounters, int *indices,
    823 	   uint64_t *values, int options) {
    824 	isc_histo_t *hg = NULL;
    825 
    826 	isc_histomulti_merge(&hg, hm);
    827 	for (int i = 0; i < ncounters; i++) {
    828 		isc_histo_get(hg, i, NULL, NULL, &values[i]);
    829 	}
    830 	isc_histo_destroy(&hg);
    831 
    832 	return dump_counters(type, arg, category, desc, ncounters, indices,
    833 			     values, options);
    834 }
    835 #endif /* defined(EXTENDED_STATS) */
    836 
    837 static isc_result_t
    838 dump_counters(isc_statsformat_t type, void *arg, const char *category,
    839 	      const char **desc, int ncounters, int *indices, uint64_t *values,
    840 	      int options) {
    841 	int i, idx;
    842 	uint64_t value;
    843 	FILE *fp;
    844 #ifdef HAVE_LIBXML2
    845 	void *writer;
    846 	int xmlrc;
    847 #endif /* ifdef HAVE_LIBXML2 */
    848 #ifdef HAVE_JSON_C
    849 	json_object *job, *cat, *counter;
    850 #endif /* ifdef HAVE_JSON_C */
    851 
    852 #if !defined(EXTENDED_STATS)
    853 	UNUSED(category);
    854 #endif /* if !defined(EXTENDED_STATS) */
    855 
    856 #ifdef HAVE_JSON_C
    857 	cat = job = (json_object *)arg;
    858 	if (ncounters > 0 && type == isc_statsformat_json) {
    859 		if (category != NULL) {
    860 			cat = json_object_new_object();
    861 			if (cat == NULL) {
    862 				return ISC_R_NOMEMORY;
    863 			}
    864 			json_object_object_add(job, category, cat);
    865 		}
    866 	}
    867 #endif /* ifdef HAVE_JSON_C */
    868 
    869 	for (i = 0; i < ncounters; i++) {
    870 		idx = indices[i];
    871 		value = values[idx];
    872 
    873 		if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0) {
    874 			continue;
    875 		}
    876 
    877 		switch (type) {
    878 		case isc_statsformat_file:
    879 			fp = arg;
    880 			fprintf(fp, "%20" PRIu64 " %s\n", value, desc[idx]);
    881 			break;
    882 		case isc_statsformat_xml:
    883 #ifdef HAVE_LIBXML2
    884 			writer = arg;
    885 
    886 			if (category != NULL) {
    887 				/* <NameOfCategory> */
    888 				TRY0(xmlTextWriterStartElement(
    889 					writer, ISC_XMLCHAR category));
    890 
    891 				/* <name> inside category */
    892 				TRY0(xmlTextWriterStartElement(
    893 					writer, ISC_XMLCHAR "name"));
    894 				TRY0(xmlTextWriterWriteString(
    895 					writer, ISC_XMLCHAR desc[idx]));
    896 				TRY0(xmlTextWriterEndElement(writer));
    897 				/* </name> */
    898 
    899 				/* <counter> */
    900 				TRY0(xmlTextWriterStartElement(
    901 					writer, ISC_XMLCHAR "counter"));
    902 				TRY0(xmlTextWriterWriteFormatString(
    903 					writer, "%" PRIu64, value));
    904 
    905 				TRY0(xmlTextWriterEndElement(writer));
    906 				/* </counter> */
    907 				TRY0(xmlTextWriterEndElement(writer));
    908 				/* </NameOfCategory> */
    909 			} else {
    910 				TRY0(xmlTextWriterStartElement(
    911 					writer, ISC_XMLCHAR "counter"));
    912 				TRY0(xmlTextWriterWriteAttribute(
    913 					writer, ISC_XMLCHAR "name",
    914 					ISC_XMLCHAR desc[idx]));
    915 				TRY0(xmlTextWriterWriteFormatString(
    916 					writer, "%" PRIu64, value));
    917 				TRY0(xmlTextWriterEndElement(writer));
    918 				/* counter */
    919 			}
    920 
    921 #endif /* ifdef HAVE_LIBXML2 */
    922 			break;
    923 		case isc_statsformat_json:
    924 #ifdef HAVE_JSON_C
    925 			counter = json_object_new_int64(value);
    926 			if (counter == NULL) {
    927 				return ISC_R_NOMEMORY;
    928 			}
    929 			json_object_object_add(cat, desc[idx], counter);
    930 #endif /* ifdef HAVE_JSON_C */
    931 			break;
    932 		}
    933 	}
    934 	return ISC_R_SUCCESS;
    935 #ifdef HAVE_LIBXML2
    936 cleanup:
    937 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
    938 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
    939 		      "failed at dump_counters()");
    940 	return ISC_R_FAILURE;
    941 #endif /* ifdef HAVE_LIBXML2 */
    942 }
    943 
    944 static void
    945 rdtypestat_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
    946 	char typebuf[64];
    947 	const char *typestr;
    948 	stats_dumparg_t *dumparg = arg;
    949 	FILE *fp;
    950 #ifdef HAVE_LIBXML2
    951 	void *writer;
    952 	int xmlrc;
    953 #endif /* ifdef HAVE_LIBXML2 */
    954 #ifdef HAVE_JSON_C
    955 	json_object *zoneobj, *obj;
    956 #endif /* ifdef HAVE_JSON_C */
    957 
    958 	if ((DNS_RDATASTATSTYPE_ATTR(type) &
    959 	     DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) == 0)
    960 	{
    961 		dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
    962 				     sizeof(typebuf));
    963 		typestr = typebuf;
    964 	} else {
    965 		typestr = "Others";
    966 	}
    967 
    968 	switch (dumparg->type) {
    969 	case isc_statsformat_file:
    970 		fp = dumparg->arg;
    971 		fprintf(fp, "%20" PRIu64 " %s\n", val, typestr);
    972 		break;
    973 	case isc_statsformat_xml:
    974 #ifdef HAVE_LIBXML2
    975 		writer = dumparg->arg;
    976 
    977 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
    978 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
    979 						 ISC_XMLCHAR typestr));
    980 
    981 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
    982 
    983 		TRY0(xmlTextWriterEndElement(writer)); /* type */
    984 #endif						       /* ifdef HAVE_LIBXML2 */
    985 		break;
    986 	case isc_statsformat_json:
    987 #ifdef HAVE_JSON_C
    988 		zoneobj = (json_object *)dumparg->arg;
    989 		obj = json_object_new_int64(val);
    990 		if (obj == NULL) {
    991 			return;
    992 		}
    993 		json_object_object_add(zoneobj, typestr, obj);
    994 #endif /* ifdef HAVE_JSON_C */
    995 		break;
    996 	}
    997 	return;
    998 #ifdef HAVE_LIBXML2
    999 cleanup:
   1000 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1001 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1002 		      "failed at rdtypestat_dump()");
   1003 	dumparg->result = ISC_R_FAILURE;
   1004 	return;
   1005 #endif /* ifdef HAVE_LIBXML2 */
   1006 }
   1007 
   1008 static bool
   1009 rdatastatstype_attr(dns_rdatastatstype_t type, unsigned int attr) {
   1010 	return (DNS_RDATASTATSTYPE_ATTR(type) & attr) != 0;
   1011 }
   1012 
   1013 static void
   1014 rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
   1015 	stats_dumparg_t *dumparg = arg;
   1016 	FILE *fp;
   1017 	char typebuf[64];
   1018 	const char *typestr;
   1019 	bool nxrrset = false;
   1020 	bool stale = false;
   1021 	bool ancient = false;
   1022 #ifdef HAVE_LIBXML2
   1023 	void *writer;
   1024 	int xmlrc;
   1025 #endif /* ifdef HAVE_LIBXML2 */
   1026 #ifdef HAVE_JSON_C
   1027 	json_object *zoneobj, *obj;
   1028 	char buf[1024];
   1029 #endif /* ifdef HAVE_JSON_C */
   1030 
   1031 	if ((DNS_RDATASTATSTYPE_ATTR(type) &
   1032 	     DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0)
   1033 	{
   1034 		typestr = "NXDOMAIN";
   1035 	} else if ((DNS_RDATASTATSTYPE_ATTR(type) &
   1036 		    DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0)
   1037 	{
   1038 		typestr = "Others";
   1039 	} else {
   1040 		dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
   1041 				     sizeof(typebuf));
   1042 		typestr = typebuf;
   1043 	}
   1044 
   1045 	nxrrset = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_NXRRSET);
   1046 	stale = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_STALE);
   1047 	ancient = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_ANCIENT);
   1048 
   1049 	switch (dumparg->type) {
   1050 	case isc_statsformat_file:
   1051 		fp = dumparg->arg;
   1052 		fprintf(fp, "%20" PRIu64 " %s%s%s%s\n", val, ancient ? "~" : "",
   1053 			stale ? "#" : "", nxrrset ? "!" : "", typestr);
   1054 		break;
   1055 	case isc_statsformat_xml:
   1056 #ifdef HAVE_LIBXML2
   1057 		writer = dumparg->arg;
   1058 
   1059 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
   1060 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
   1061 		TRY0(xmlTextWriterWriteFormatString(
   1062 			writer, "%s%s%s%s", ancient ? "~" : "",
   1063 			stale ? "#" : "", nxrrset ? "!" : "", typestr));
   1064 		TRY0(xmlTextWriterEndElement(writer)); /* name */
   1065 
   1066 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
   1067 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
   1068 		TRY0(xmlTextWriterEndElement(writer)); /* counter */
   1069 
   1070 		TRY0(xmlTextWriterEndElement(writer)); /* rrset */
   1071 #endif						       /* ifdef HAVE_LIBXML2 */
   1072 		break;
   1073 	case isc_statsformat_json:
   1074 #ifdef HAVE_JSON_C
   1075 		zoneobj = (json_object *)dumparg->arg;
   1076 		snprintf(buf, sizeof(buf), "%s%s%s%s", ancient ? "~" : "",
   1077 			 stale ? "#" : "", nxrrset ? "!" : "", typestr);
   1078 		obj = json_object_new_int64(val);
   1079 		if (obj == NULL) {
   1080 			return;
   1081 		}
   1082 		json_object_object_add(zoneobj, buf, obj);
   1083 #endif /* ifdef HAVE_JSON_C */
   1084 		break;
   1085 	}
   1086 	return;
   1087 #ifdef HAVE_LIBXML2
   1088 cleanup:
   1089 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1090 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1091 		      "failed at rdatasetstats_dump()");
   1092 	dumparg->result = ISC_R_FAILURE;
   1093 #endif /* ifdef HAVE_LIBXML2 */
   1094 }
   1095 
   1096 static void
   1097 opcodestat_dump(dns_opcode_t code, uint64_t val, void *arg) {
   1098 	FILE *fp;
   1099 	isc_buffer_t b;
   1100 	char codebuf[64];
   1101 	stats_dumparg_t *dumparg = arg;
   1102 #ifdef HAVE_LIBXML2
   1103 	void *writer;
   1104 	int xmlrc;
   1105 #endif /* ifdef HAVE_LIBXML2 */
   1106 #ifdef HAVE_JSON_C
   1107 	json_object *zoneobj, *obj;
   1108 #endif /* ifdef HAVE_JSON_C */
   1109 
   1110 	isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
   1111 	dns_opcode_totext(code, &b);
   1112 	codebuf[isc_buffer_usedlength(&b)] = '\0';
   1113 
   1114 	switch (dumparg->type) {
   1115 	case isc_statsformat_file:
   1116 		fp = dumparg->arg;
   1117 		fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
   1118 		break;
   1119 	case isc_statsformat_xml:
   1120 #ifdef HAVE_LIBXML2
   1121 		writer = dumparg->arg;
   1122 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
   1123 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   1124 						 ISC_XMLCHAR codebuf));
   1125 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
   1126 		TRY0(xmlTextWriterEndElement(writer)); /* counter */
   1127 #endif						       /* ifdef HAVE_LIBXML2 */
   1128 		break;
   1129 	case isc_statsformat_json:
   1130 #ifdef HAVE_JSON_C
   1131 		zoneobj = (json_object *)dumparg->arg;
   1132 		obj = json_object_new_int64(val);
   1133 		if (obj == NULL) {
   1134 			return;
   1135 		}
   1136 		json_object_object_add(zoneobj, codebuf, obj);
   1137 #endif /* ifdef HAVE_JSON_C */
   1138 		break;
   1139 	}
   1140 	return;
   1141 
   1142 #ifdef HAVE_LIBXML2
   1143 cleanup:
   1144 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1145 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1146 		      "failed at opcodestat_dump()");
   1147 	dumparg->result = ISC_R_FAILURE;
   1148 	return;
   1149 #endif /* ifdef HAVE_LIBXML2 */
   1150 }
   1151 
   1152 static void
   1153 rcodestat_dump(dns_rcode_t code, uint64_t val, void *arg) {
   1154 	FILE *fp;
   1155 	isc_buffer_t b;
   1156 	char codebuf[64];
   1157 	stats_dumparg_t *dumparg = arg;
   1158 #ifdef HAVE_LIBXML2
   1159 	void *writer;
   1160 	int xmlrc;
   1161 #endif /* ifdef HAVE_LIBXML2 */
   1162 #ifdef HAVE_JSON_C
   1163 	json_object *zoneobj, *obj;
   1164 #endif /* ifdef HAVE_JSON_C */
   1165 
   1166 	isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
   1167 	dns_rcode_totext(code, &b);
   1168 	codebuf[isc_buffer_usedlength(&b)] = '\0';
   1169 
   1170 	switch (dumparg->type) {
   1171 	case isc_statsformat_file:
   1172 		fp = dumparg->arg;
   1173 		fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
   1174 		break;
   1175 	case isc_statsformat_xml:
   1176 #ifdef HAVE_LIBXML2
   1177 		writer = dumparg->arg;
   1178 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
   1179 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   1180 						 ISC_XMLCHAR codebuf));
   1181 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
   1182 		TRY0(xmlTextWriterEndElement(writer)); /* counter */
   1183 #endif						       /* ifdef HAVE_LIBXML2 */
   1184 		break;
   1185 	case isc_statsformat_json:
   1186 #ifdef HAVE_JSON_C
   1187 		zoneobj = (json_object *)dumparg->arg;
   1188 		obj = json_object_new_int64(val);
   1189 		if (obj == NULL) {
   1190 			return;
   1191 		}
   1192 		json_object_object_add(zoneobj, codebuf, obj);
   1193 #endif /* ifdef HAVE_JSON_C */
   1194 		break;
   1195 	}
   1196 	return;
   1197 
   1198 #ifdef HAVE_LIBXML2
   1199 cleanup:
   1200 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1201 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1202 		      "failed at rcodestat_dump()");
   1203 	dumparg->result = ISC_R_FAILURE;
   1204 	return;
   1205 #endif /* ifdef HAVE_LIBXML2 */
   1206 }
   1207 
   1208 #if defined(EXTENDED_STATS)
   1209 static void
   1210 dnssecsignstat_dump(uint32_t kval, uint64_t val, void *arg) {
   1211 	FILE *fp;
   1212 	char tagbuf[64];
   1213 	stats_dumparg_t *dumparg = arg;
   1214 #ifdef HAVE_LIBXML2
   1215 	xmlTextWriterPtr writer;
   1216 	int xmlrc;
   1217 #endif /* ifdef HAVE_LIBXML2 */
   1218 #ifdef HAVE_JSON_C
   1219 	json_object *zoneobj, *obj;
   1220 #endif /* ifdef HAVE_JSON_C */
   1221 
   1222 	/*
   1223 	 * kval is '(algorithm << 16) | keyid'.
   1224 	 */
   1225 	snprintf(tagbuf, sizeof(tagbuf), "%u+%u", (kval >> 16) & 0xff,
   1226 		 kval & 0xffff);
   1227 
   1228 	switch (dumparg->type) {
   1229 	case isc_statsformat_file:
   1230 		fp = dumparg->arg;
   1231 		fprintf(fp, "%20" PRIu64 " %s\n", val, tagbuf);
   1232 		break;
   1233 	case isc_statsformat_xml:
   1234 #ifdef HAVE_LIBXML2
   1235 		writer = dumparg->arg;
   1236 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
   1237 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   1238 						 ISC_XMLCHAR tagbuf));
   1239 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
   1240 		TRY0(xmlTextWriterEndElement(writer)); /* counter */
   1241 #endif						       /* ifdef HAVE_LIBXML2 */
   1242 		break;
   1243 	case isc_statsformat_json:
   1244 #ifdef HAVE_JSON_C
   1245 		zoneobj = (json_object *)dumparg->arg;
   1246 		obj = json_object_new_int64(val);
   1247 		if (obj == NULL) {
   1248 			return;
   1249 		}
   1250 		json_object_object_add(zoneobj, tagbuf, obj);
   1251 #endif /* ifdef HAVE_JSON_C */
   1252 		break;
   1253 	}
   1254 	return;
   1255 #ifdef HAVE_LIBXML2
   1256 cleanup:
   1257 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1258 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1259 		      "failed at dnssecsignstat_dump()");
   1260 	dumparg->result = ISC_R_FAILURE;
   1261 	return;
   1262 #endif /* ifdef HAVE_LIBXML2 */
   1263 }
   1264 #endif /* defined(EXTENDED_STATS) */
   1265 
   1266 #ifdef HAVE_LIBXML2
   1267 /*
   1268  * Which statistics to include when rendering to XML
   1269  */
   1270 #define STATS_XML_STATUS  0x00 /* display only common statistics */
   1271 #define STATS_XML_SERVER  0x01
   1272 #define STATS_XML_ZONES	  0x02
   1273 #define STATS_XML_XFRINS  0x04
   1274 #define STATS_XML_NET	  0x08
   1275 #define STATS_XML_MEM	  0x10
   1276 #define STATS_XML_TRAFFIC 0x20
   1277 #define STATS_XML_ALL	  0xff
   1278 
   1279 static isc_result_t
   1280 zone_xmlrender(dns_zone_t *zone, void *arg) {
   1281 	isc_result_t result;
   1282 	char buf[1024 + 32]; /* sufficiently large for zone name and class */
   1283 	dns_rdataclass_t rdclass;
   1284 	uint32_t serial;
   1285 	xmlTextWriterPtr writer = arg;
   1286 	dns_zonestat_level_t statlevel;
   1287 	int xmlrc;
   1288 	stats_dumparg_t dumparg;
   1289 	const char *ztype;
   1290 	isc_time_t timestamp;
   1291 
   1292 	statlevel = dns_zone_getstatlevel(zone);
   1293 	if (statlevel == dns_zonestat_none) {
   1294 		return ISC_R_SUCCESS;
   1295 	}
   1296 
   1297 	dumparg.type = isc_statsformat_xml;
   1298 	dumparg.arg = writer;
   1299 
   1300 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone"));
   1301 
   1302 	dns_zone_nameonly(zone, buf, sizeof(buf));
   1303 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   1304 					 ISC_XMLCHAR buf));
   1305 
   1306 	rdclass = dns_zone_getclass(zone);
   1307 	dns_rdataclass_format(rdclass, buf, sizeof(buf));
   1308 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "rdataclass",
   1309 					 ISC_XMLCHAR buf));
   1310 
   1311 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
   1312 	ztype = user_zonetype(zone);
   1313 	if (ztype != NULL) {
   1314 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype));
   1315 	} else {
   1316 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "unknown"));
   1317 	}
   1318 	TRY0(xmlTextWriterEndElement(writer)); /* type */
   1319 
   1320 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
   1321 	if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) {
   1322 		TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
   1323 	} else {
   1324 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1325 	}
   1326 	TRY0(xmlTextWriterEndElement(writer)); /* serial */
   1327 
   1328 	/*
   1329 	 * Export zone timers to the statistics channel in XML format.  For
   1330 	 * primary zones, only include the loaded time.  For secondary zones,
   1331 	 * also include the expire and refresh times.
   1332 	 */
   1333 	CHECK(dns_zone_getloadtime(zone, &timestamp));
   1334 
   1335 	isc_time_formatISO8601(&timestamp, buf, 64);
   1336 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "loaded"));
   1337 	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
   1338 	TRY0(xmlTextWriterEndElement(writer));
   1339 
   1340 	if (dns_zone_gettype(zone) == dns_zone_secondary) {
   1341 		CHECK(dns_zone_getexpiretime(zone, &timestamp));
   1342 		isc_time_formatISO8601(&timestamp, buf, 64);
   1343 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "expires"));
   1344 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
   1345 		TRY0(xmlTextWriterEndElement(writer));
   1346 
   1347 		CHECK(dns_zone_getrefreshtime(zone, &timestamp));
   1348 		isc_time_formatISO8601(&timestamp, buf, 64);
   1349 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refresh"));
   1350 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
   1351 		TRY0(xmlTextWriterEndElement(writer));
   1352 	}
   1353 
   1354 	if (statlevel == dns_zonestat_full) {
   1355 		isc_stats_t *zonestats;
   1356 		isc_stats_t *gluecachestats;
   1357 		dns_stats_t *rcvquerystats;
   1358 		dns_stats_t *dnssecsignstats;
   1359 		uint64_t nsstat_values[ns_statscounter_max];
   1360 		uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
   1361 
   1362 		zonestats = dns_zone_getrequeststats(zone);
   1363 		if (zonestats != NULL) {
   1364 			TRY0(xmlTextWriterStartElement(writer,
   1365 						       ISC_XMLCHAR "counters"));
   1366 			TRY0(xmlTextWriterWriteAttribute(writer,
   1367 							 ISC_XMLCHAR "type",
   1368 							 ISC_XMLCHAR "rcode"));
   1369 
   1370 			CHECK(dump_stats(zonestats, isc_statsformat_xml, writer,
   1371 					 NULL, nsstats_xmldesc,
   1372 					 ns_statscounter_max, nsstats_index,
   1373 					 nsstat_values, ISC_STATSDUMP_VERBOSE));
   1374 			/* counters type="rcode"*/
   1375 			TRY0(xmlTextWriterEndElement(writer));
   1376 		}
   1377 
   1378 		gluecachestats = dns_zone_getgluecachestats(zone);
   1379 		if (gluecachestats != NULL) {
   1380 			TRY0(xmlTextWriterStartElement(writer,
   1381 						       ISC_XMLCHAR "counters"));
   1382 			TRY0(xmlTextWriterWriteAttribute(
   1383 				writer, ISC_XMLCHAR "type",
   1384 				ISC_XMLCHAR "gluecache"));
   1385 
   1386 			CHECK(dump_stats(gluecachestats, isc_statsformat_xml,
   1387 					 writer, NULL, gluecachestats_xmldesc,
   1388 					 dns_gluecachestatscounter_max,
   1389 					 gluecachestats_index,
   1390 					 gluecachestats_values,
   1391 					 ISC_STATSDUMP_VERBOSE));
   1392 			/* counters type="rcode"*/
   1393 			TRY0(xmlTextWriterEndElement(writer));
   1394 		}
   1395 
   1396 		rcvquerystats = dns_zone_getrcvquerystats(zone);
   1397 		if (rcvquerystats != NULL) {
   1398 			TRY0(xmlTextWriterStartElement(writer,
   1399 						       ISC_XMLCHAR "counters"));
   1400 			TRY0(xmlTextWriterWriteAttribute(writer,
   1401 							 ISC_XMLCHAR "type",
   1402 							 ISC_XMLCHAR "qtype"));
   1403 
   1404 			dumparg.result = ISC_R_SUCCESS;
   1405 			dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
   1406 						&dumparg, 0);
   1407 			CHECK(dumparg.result);
   1408 
   1409 			/* counters type="qtype"*/
   1410 			TRY0(xmlTextWriterEndElement(writer));
   1411 		}
   1412 
   1413 		dnssecsignstats = dns_zone_getdnssecsignstats(zone);
   1414 		if (dnssecsignstats != NULL) {
   1415 			/* counters type="dnssec-sign"*/
   1416 			TRY0(xmlTextWriterStartElement(writer,
   1417 						       ISC_XMLCHAR "counters"));
   1418 			TRY0(xmlTextWriterWriteAttribute(
   1419 				writer, ISC_XMLCHAR "type",
   1420 				ISC_XMLCHAR "dnssec-sign"));
   1421 
   1422 			dumparg.result = ISC_R_SUCCESS;
   1423 			dns_dnssecsignstats_dump(
   1424 				dnssecsignstats, dns_dnssecsignstats_sign,
   1425 				dnssecsignstat_dump, &dumparg, 0);
   1426 			CHECK(dumparg.result);
   1427 
   1428 			/* counters type="dnssec-sign"*/
   1429 			TRY0(xmlTextWriterEndElement(writer));
   1430 
   1431 			/* counters type="dnssec-refresh"*/
   1432 			TRY0(xmlTextWriterStartElement(writer,
   1433 						       ISC_XMLCHAR "counters"));
   1434 			TRY0(xmlTextWriterWriteAttribute(
   1435 				writer, ISC_XMLCHAR "type",
   1436 				ISC_XMLCHAR "dnssec-refresh"));
   1437 
   1438 			dumparg.result = ISC_R_SUCCESS;
   1439 			dns_dnssecsignstats_dump(
   1440 				dnssecsignstats, dns_dnssecsignstats_refresh,
   1441 				dnssecsignstat_dump, &dumparg, 0);
   1442 			CHECK(dumparg.result);
   1443 
   1444 			/* counters type="dnssec-refresh"*/
   1445 			TRY0(xmlTextWriterEndElement(writer));
   1446 		}
   1447 	}
   1448 
   1449 	TRY0(xmlTextWriterEndElement(writer)); /* zone */
   1450 
   1451 	return ISC_R_SUCCESS;
   1452 cleanup:
   1453 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1454 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1455 		      "Failed at zone_xmlrender()");
   1456 	return ISC_R_FAILURE;
   1457 }
   1458 
   1459 static isc_result_t
   1460 xfrin_xmlrender(dns_zone_t *zone, void *arg) {
   1461 	char buf[1024 + 32]; /* sufficiently large for zone name and class */
   1462 	dns_rdataclass_t rdclass;
   1463 	const char *ztype;
   1464 	uint32_t serial;
   1465 	isc_sockaddr_t addr;
   1466 	const isc_sockaddr_t *addrp = NULL;
   1467 	char addr_buf[ISC_SOCKADDR_FORMATSIZE];
   1468 	dns_transport_type_t transport_type;
   1469 	xmlTextWriterPtr writer = arg;
   1470 	dns_zonestat_level_t statlevel;
   1471 	int xmlrc;
   1472 	dns_xfrin_t *xfr = NULL;
   1473 	bool is_firstrefresh, is_running, is_deferred, is_presoa, is_pending;
   1474 	bool needs_refresh;
   1475 	bool is_first_data_received, is_ixfr;
   1476 	unsigned int nmsg = 0;
   1477 	unsigned int nrecs = 0;
   1478 	uint64_t nbytes = 0;
   1479 	uint64_t rate = 0;
   1480 
   1481 	statlevel = dns_zone_getstatlevel(zone);
   1482 	if (statlevel == dns_zonestat_none) {
   1483 		return ISC_R_SUCCESS;
   1484 	}
   1485 
   1486 	if (dns_zone_getxfr(zone, &xfr, &is_firstrefresh, &is_running,
   1487 			    &is_deferred, &is_presoa, &is_pending,
   1488 			    &needs_refresh) != ISC_R_SUCCESS)
   1489 	{
   1490 		/*
   1491 		 * Failed to get information about the zone's incoming transfer
   1492 		 * (if any), but we still want to continue generating the
   1493 		 * remaining parts of the output.
   1494 		 */
   1495 		return ISC_R_SUCCESS;
   1496 	}
   1497 
   1498 	if (!is_running && !is_deferred && !is_presoa && !is_pending &&
   1499 	    !needs_refresh)
   1500 	{
   1501 		if (xfr != NULL) {
   1502 			dns_xfrin_detach(&xfr);
   1503 		}
   1504 		/* No ongoing/queued transfer. */
   1505 		return ISC_R_SUCCESS;
   1506 	}
   1507 
   1508 	if (is_running && xfr == NULL) {
   1509 		/* The transfer is finished, and it's shutting down. */
   1510 		return ISC_R_SUCCESS;
   1511 	}
   1512 
   1513 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "xfrin"));
   1514 
   1515 	dns_zone_nameonly(zone, buf, sizeof(buf));
   1516 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   1517 					 ISC_XMLCHAR buf));
   1518 
   1519 	rdclass = dns_zone_getclass(zone);
   1520 	dns_rdataclass_format(rdclass, buf, sizeof(buf));
   1521 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "class",
   1522 					 ISC_XMLCHAR buf));
   1523 
   1524 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
   1525 	ztype = user_zonetype(zone);
   1526 	if (ztype != NULL) {
   1527 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype));
   1528 	} else {
   1529 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1530 	}
   1531 	TRY0(xmlTextWriterEndElement(writer));
   1532 
   1533 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
   1534 	if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) {
   1535 		TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
   1536 	} else {
   1537 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1538 	}
   1539 	TRY0(xmlTextWriterEndElement(writer));
   1540 
   1541 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteserial"));
   1542 	if (is_running) {
   1543 		serial = dns_xfrin_getendserial(xfr);
   1544 		if (serial != 0) {
   1545 			TRY0(xmlTextWriterWriteFormatString(writer, "%u",
   1546 							    serial));
   1547 		} else {
   1548 			TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1549 		}
   1550 	} else {
   1551 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1552 	}
   1553 	TRY0(xmlTextWriterEndElement(writer));
   1554 
   1555 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "firstrefresh"));
   1556 	TRY0(xmlTextWriterWriteString(
   1557 		writer, ISC_XMLCHAR(is_firstrefresh ? "Yes" : "No")));
   1558 	TRY0(xmlTextWriterEndElement(writer));
   1559 
   1560 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state"));
   1561 	if (is_running) {
   1562 		const char *xfr_state = NULL;
   1563 
   1564 		dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received,
   1565 				   &is_ixfr);
   1566 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR xfr_state));
   1567 	} else if (is_deferred) {
   1568 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Deferred"));
   1569 	} else if (is_presoa) {
   1570 		TRY0(xmlTextWriterWriteString(writer,
   1571 					      ISC_XMLCHAR "Refresh SOA"));
   1572 	} else if (is_pending) {
   1573 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Pending"));
   1574 	} else if (needs_refresh) {
   1575 		TRY0(xmlTextWriterWriteString(writer,
   1576 					      ISC_XMLCHAR "Needs Refresh"));
   1577 	} else {
   1578 		UNREACHABLE();
   1579 	}
   1580 	TRY0(xmlTextWriterEndElement(writer));
   1581 
   1582 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refreshqueued"));
   1583 	TRY0(xmlTextWriterWriteString(
   1584 		writer,
   1585 		ISC_XMLCHAR(is_running && needs_refresh ? "Yes" : "No")));
   1586 	TRY0(xmlTextWriterEndElement(writer));
   1587 
   1588 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "localaddr"));
   1589 	if (is_running) {
   1590 		addrp = dns_xfrin_getsourceaddr(xfr);
   1591 		isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
   1592 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf));
   1593 	} else if (is_presoa) {
   1594 		dns_zone_getsourceaddr(zone, &addr);
   1595 		isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf));
   1596 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf));
   1597 	} else {
   1598 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1599 	}
   1600 	TRY0(xmlTextWriterEndElement(writer));
   1601 
   1602 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteaddr"));
   1603 	if (is_running) {
   1604 		addrp = dns_xfrin_getprimaryaddr(xfr);
   1605 		isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
   1606 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf));
   1607 	} else if (is_presoa) {
   1608 		if (dns_zone_getprimaryaddr(zone, &addr) == ISC_R_SUCCESS) {
   1609 			isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf));
   1610 			TRY0(xmlTextWriterWriteString(writer,
   1611 						      ISC_XMLCHAR addr_buf));
   1612 		} else {
   1613 			TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1614 		}
   1615 	} else {
   1616 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1617 	}
   1618 	TRY0(xmlTextWriterEndElement(writer));
   1619 
   1620 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "soatransport"));
   1621 	if (is_running || is_presoa) {
   1622 		if (is_running) {
   1623 			transport_type = dns_xfrin_getsoatransporttype(xfr);
   1624 		} else {
   1625 			transport_type = dns_zone_getrequesttransporttype(zone);
   1626 		}
   1627 		if (transport_type == DNS_TRANSPORT_UDP) {
   1628 			TRY0(xmlTextWriterWriteString(writer,
   1629 						      ISC_XMLCHAR "UDP"));
   1630 		} else if (transport_type == DNS_TRANSPORT_TCP) {
   1631 			TRY0(xmlTextWriterWriteString(writer,
   1632 						      ISC_XMLCHAR "TCP"));
   1633 		} else if (transport_type == DNS_TRANSPORT_TLS) {
   1634 			TRY0(xmlTextWriterWriteString(writer,
   1635 						      ISC_XMLCHAR "TLS"));
   1636 		} else if (transport_type == DNS_TRANSPORT_NONE) {
   1637 			TRY0(xmlTextWriterWriteString(writer,
   1638 						      ISC_XMLCHAR "None"));
   1639 		} else {
   1640 			/* We don't expect any other SOA transport type. */
   1641 			TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1642 		}
   1643 	} else {
   1644 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1645 	}
   1646 	TRY0(xmlTextWriterEndElement(writer));
   1647 
   1648 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "transport"));
   1649 	if (is_running) {
   1650 		transport_type = dns_xfrin_gettransporttype(xfr);
   1651 		if (transport_type == DNS_TRANSPORT_TCP) {
   1652 			TRY0(xmlTextWriterWriteString(writer,
   1653 						      ISC_XMLCHAR "TCP"));
   1654 		} else if (transport_type == DNS_TRANSPORT_TLS) {
   1655 			TRY0(xmlTextWriterWriteString(writer,
   1656 						      ISC_XMLCHAR "TLS"));
   1657 		} else {
   1658 			/* We don't expect any other transport type. */
   1659 			TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1660 		}
   1661 	} else {
   1662 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
   1663 	}
   1664 	TRY0(xmlTextWriterEndElement(writer));
   1665 
   1666 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tsigkeyname"));
   1667 	if (is_running) {
   1668 		const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr);
   1669 		char tsigkeyname_buf[DNS_NAME_FORMATSIZE];
   1670 
   1671 		if (tsigkeyname != NULL) {
   1672 			dns_name_format(tsigkeyname, tsigkeyname_buf,
   1673 					sizeof(tsigkeyname_buf));
   1674 			TRY0(xmlTextWriterWriteString(
   1675 				writer, ISC_XMLCHAR tsigkeyname_buf));
   1676 		}
   1677 	}
   1678 	TRY0(xmlTextWriterEndElement(writer));
   1679 
   1680 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "duration"));
   1681 	if (is_running || is_deferred || is_presoa || is_pending) {
   1682 		isc_time_t start = is_running ? dns_xfrin_getstarttime(xfr)
   1683 					      : dns_zone_getxfrintime(zone);
   1684 		isc_time_t now = isc_time_now();
   1685 		isc_time_t diff;
   1686 		uint32_t sec;
   1687 
   1688 		isc_time_subtract(&now, &start, &diff);
   1689 		sec = isc_time_seconds(&diff);
   1690 		TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu32, sec));
   1691 	} else {
   1692 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "0"));
   1693 	}
   1694 	TRY0(xmlTextWriterEndElement(writer));
   1695 
   1696 	if (is_running) {
   1697 		dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
   1698 	}
   1699 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nmsg"));
   1700 	TRY0(xmlTextWriterWriteFormatString(writer, "%u", nmsg));
   1701 	TRY0(xmlTextWriterEndElement(writer));
   1702 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nrecs"));
   1703 	TRY0(xmlTextWriterWriteFormatString(writer, "%u", nrecs));
   1704 	TRY0(xmlTextWriterEndElement(writer));
   1705 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nbytes"));
   1706 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, nbytes));
   1707 	TRY0(xmlTextWriterEndElement(writer));
   1708 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rate"));
   1709 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, rate));
   1710 	TRY0(xmlTextWriterEndElement(writer));
   1711 
   1712 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ixfr"));
   1713 	if (is_running && is_first_data_received) {
   1714 		TRY0(xmlTextWriterWriteString(
   1715 			writer, ISC_XMLCHAR(is_ixfr ? "Yes" : "No")));
   1716 	} else {
   1717 		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ""));
   1718 	}
   1719 	TRY0(xmlTextWriterEndElement(writer));
   1720 
   1721 	TRY0(xmlTextWriterEndElement(writer)); /* xfrin */
   1722 
   1723 	if (xfr != NULL) {
   1724 		dns_xfrin_detach(&xfr);
   1725 	}
   1726 
   1727 	return ISC_R_SUCCESS;
   1728 
   1729 cleanup:
   1730 	if (xfr != NULL) {
   1731 		dns_xfrin_detach(&xfr);
   1732 	}
   1733 
   1734 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   1735 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   1736 		      "Failed at xfrin_xmlrender()");
   1737 
   1738 	return ISC_R_FAILURE;
   1739 }
   1740 
   1741 static isc_result_t
   1742 generatexml(named_server_t *server, uint32_t flags, int *buflen,
   1743 	    xmlChar **buf) {
   1744 	char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   1745 	char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   1746 	char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   1747 	isc_time_t now = isc_time_now();
   1748 	xmlTextWriterPtr writer = NULL;
   1749 	xmlDocPtr doc = NULL;
   1750 	int xmlrc;
   1751 	dns_view_t *view;
   1752 	stats_dumparg_t dumparg;
   1753 	dns_stats_t *cacherrstats;
   1754 	uint64_t nsstat_values[ns_statscounter_max];
   1755 	uint64_t resstat_values[dns_resstatscounter_max];
   1756 	uint64_t adbstat_values[dns_adbstats_max];
   1757 	uint64_t zonestat_values[dns_zonestatscounter_max];
   1758 	uint64_t sockstat_values[isc_sockstatscounter_max];
   1759 	uint64_t udpinsizestat_values[DNS_SIZEHISTO_MAXIN + 1];
   1760 	uint64_t udpoutsizestat_values[DNS_SIZEHISTO_MAXOUT + 1];
   1761 	uint64_t tcpinsizestat_values[DNS_SIZEHISTO_MAXIN + 1];
   1762 	uint64_t tcpoutsizestat_values[DNS_SIZEHISTO_MAXOUT + 1];
   1763 #ifdef HAVE_DNSTAP
   1764 	uint64_t dnstapstat_values[dns_dnstapcounter_max];
   1765 #endif /* ifdef HAVE_DNSTAP */
   1766 	isc_result_t result;
   1767 
   1768 	isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof boottime);
   1769 	isc_time_formatISO8601ms(&named_g_configtime, configtime,
   1770 				 sizeof configtime);
   1771 	isc_time_formatISO8601ms(&now, nowstr, sizeof nowstr);
   1772 
   1773 	writer = xmlNewTextWriterDoc(&doc, 0);
   1774 	if (writer == NULL) {
   1775 		goto cleanup;
   1776 	}
   1777 	TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL));
   1778 	TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet",
   1779 				  ISC_XMLCHAR "type=\"text/xsl\" "
   1780 					      "href=\"/bind9.xsl\""));
   1781 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
   1782 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
   1783 					 ISC_XMLCHAR STATS_XML_VERSION));
   1784 
   1785 	/* Set common fields for statistics dump */
   1786 	dumparg.type = isc_statsformat_xml;
   1787 	dumparg.arg = writer;
   1788 
   1789 	/* Render server information */
   1790 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
   1791 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time"));
   1792 	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime));
   1793 	TRY0(xmlTextWriterEndElement(writer)); /* boot-time */
   1794 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "config-time"));
   1795 	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR configtime));
   1796 	TRY0(xmlTextWriterEndElement(writer)); /* config-time */
   1797 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time"));
   1798 	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr));
   1799 	TRY0(xmlTextWriterEndElement(writer)); /* current-time */
   1800 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "version"));
   1801 	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR PACKAGE_VERSION));
   1802 	TRY0(xmlTextWriterEndElement(writer)); /* version */
   1803 
   1804 	if ((flags & STATS_XML_SERVER) != 0) {
   1805 		dumparg.result = ISC_R_SUCCESS;
   1806 
   1807 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1808 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1809 						 ISC_XMLCHAR "opcode"));
   1810 
   1811 		dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
   1812 				     &dumparg, ISC_STATSDUMP_VERBOSE);
   1813 		CHECK(dumparg.result);
   1814 
   1815 		TRY0(xmlTextWriterEndElement(writer));
   1816 
   1817 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1818 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1819 						 ISC_XMLCHAR "rcode"));
   1820 
   1821 		dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump,
   1822 				    &dumparg, ISC_STATSDUMP_VERBOSE);
   1823 		CHECK(dumparg.result);
   1824 
   1825 		TRY0(xmlTextWriterEndElement(writer));
   1826 
   1827 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1828 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1829 						 ISC_XMLCHAR "qtype"));
   1830 
   1831 		dumparg.result = ISC_R_SUCCESS;
   1832 		dns_rdatatypestats_dump(server->sctx->rcvquerystats,
   1833 					rdtypestat_dump, &dumparg, 0);
   1834 		CHECK(dumparg.result);
   1835 
   1836 		TRY0(xmlTextWriterEndElement(writer)); /* counters */
   1837 
   1838 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1839 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1840 						 ISC_XMLCHAR "nsstat"));
   1841 
   1842 		CHECK(dump_stats(ns_stats_get(server->sctx->nsstats),
   1843 				 isc_statsformat_xml, writer, NULL,
   1844 				 nsstats_xmldesc, ns_statscounter_max,
   1845 				 nsstats_index, nsstat_values,
   1846 				 ISC_STATSDUMP_VERBOSE));
   1847 
   1848 		TRY0(xmlTextWriterEndElement(writer)); /* /nsstat */
   1849 
   1850 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1851 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1852 						 ISC_XMLCHAR "zonestat"));
   1853 
   1854 		CHECK(dump_stats(server->zonestats, isc_statsformat_xml, writer,
   1855 				 NULL, zonestats_xmldesc,
   1856 				 dns_zonestatscounter_max, zonestats_index,
   1857 				 zonestat_values, ISC_STATSDUMP_VERBOSE));
   1858 
   1859 		TRY0(xmlTextWriterEndElement(writer)); /* /zonestat */
   1860 
   1861 		/*
   1862 		 * Most of the common resolver statistics entries are 0, so
   1863 		 * we don't use the verbose dump here.
   1864 		 */
   1865 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1866 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1867 						 ISC_XMLCHAR "resstat"));
   1868 		CHECK(dump_stats(server->resolverstats, isc_statsformat_xml,
   1869 				 writer, NULL, resstats_xmldesc,
   1870 				 dns_resstatscounter_max, resstats_index,
   1871 				 resstat_values, 0));
   1872 
   1873 		TRY0(xmlTextWriterEndElement(writer)); /* resstat */
   1874 
   1875 #ifdef HAVE_DNSTAP
   1876 		if (server->dtenv != NULL) {
   1877 			isc_stats_t *dnstapstats = NULL;
   1878 			TRY0(xmlTextWriterStartElement(writer,
   1879 						       ISC_XMLCHAR "counters"));
   1880 			TRY0(xmlTextWriterWriteAttribute(writer,
   1881 							 ISC_XMLCHAR "type",
   1882 							 ISC_XMLCHAR "dnstap"));
   1883 			dns_dt_getstats(named_g_server->dtenv, &dnstapstats);
   1884 			result = dump_stats(
   1885 				dnstapstats, isc_statsformat_xml, writer, NULL,
   1886 				dnstapstats_xmldesc, dns_dnstapcounter_max,
   1887 				dnstapstats_index, dnstapstat_values, 0);
   1888 			isc_stats_detach(&dnstapstats);
   1889 			CHECK(result);
   1890 
   1891 			TRY0(xmlTextWriterEndElement(writer)); /* dnstap */
   1892 		}
   1893 #endif /* ifdef HAVE_DNSTAP */
   1894 	}
   1895 
   1896 	if ((flags & STATS_XML_NET) != 0) {
   1897 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1898 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1899 						 ISC_XMLCHAR "sockstat"));
   1900 
   1901 		CHECK(dump_stats(server->sockstats, isc_statsformat_xml, writer,
   1902 				 NULL, sockstats_xmldesc,
   1903 				 isc_sockstatscounter_max, sockstats_index,
   1904 				 sockstat_values, ISC_STATSDUMP_VERBOSE));
   1905 
   1906 		TRY0(xmlTextWriterEndElement(writer)); /* /sockstat */
   1907 	}
   1908 	TRY0(xmlTextWriterEndElement(writer)); /* /server */
   1909 
   1910 	if ((flags & STATS_XML_TRAFFIC) != 0) {
   1911 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "traffic"));
   1912 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv4"));
   1913 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
   1914 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1915 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1916 						 ISC_XMLCHAR "request-size"));
   1917 
   1918 		CHECK(dump_histo(server->sctx->udpinstats4, isc_statsformat_xml,
   1919 				 writer, NULL, udpinsizestats_xmldesc,
   1920 				 dns_sizecounter_in_max, udpinsizestats_index,
   1921 				 udpinsizestat_values, 0));
   1922 
   1923 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1924 
   1925 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1926 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1927 						 ISC_XMLCHAR "response-size"));
   1928 
   1929 		CHECK(dump_histo(
   1930 			server->sctx->udpoutstats4, isc_statsformat_xml, writer,
   1931 			NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max,
   1932 			udpoutsizestats_index, udpoutsizestat_values, 0));
   1933 
   1934 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1935 		TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
   1936 
   1937 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
   1938 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1939 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1940 						 ISC_XMLCHAR "request-size"));
   1941 
   1942 		CHECK(dump_histo(server->sctx->tcpinstats4, isc_statsformat_xml,
   1943 				 writer, NULL, tcpinsizestats_xmldesc,
   1944 				 dns_sizecounter_in_max, tcpinsizestats_index,
   1945 				 tcpinsizestat_values, 0));
   1946 
   1947 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1948 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1949 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1950 						 ISC_XMLCHAR "response-size"));
   1951 
   1952 		CHECK(dump_histo(
   1953 			server->sctx->tcpoutstats4, isc_statsformat_xml, writer,
   1954 			NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max,
   1955 			tcpoutsizestats_index, tcpoutsizestat_values, 0));
   1956 
   1957 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1958 		TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
   1959 		TRY0(xmlTextWriterEndElement(writer)); /* </ipv4> */
   1960 
   1961 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv6"));
   1962 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
   1963 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1964 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1965 						 ISC_XMLCHAR "request-size"));
   1966 
   1967 		CHECK(dump_histo(server->sctx->udpinstats6, isc_statsformat_xml,
   1968 				 writer, NULL, udpinsizestats_xmldesc,
   1969 				 dns_sizecounter_in_max, udpinsizestats_index,
   1970 				 udpinsizestat_values, 0));
   1971 
   1972 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1973 
   1974 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1975 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1976 						 ISC_XMLCHAR "response-size"));
   1977 
   1978 		CHECK(dump_histo(
   1979 			server->sctx->udpoutstats6, isc_statsformat_xml, writer,
   1980 			NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max,
   1981 			udpoutsizestats_index, udpoutsizestat_values, 0));
   1982 
   1983 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1984 		TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
   1985 
   1986 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
   1987 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1988 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   1989 						 ISC_XMLCHAR "request-size"));
   1990 
   1991 		CHECK(dump_histo(server->sctx->tcpinstats6, isc_statsformat_xml,
   1992 				 writer, NULL, tcpinsizestats_xmldesc,
   1993 				 dns_sizecounter_in_max, tcpinsizestats_index,
   1994 				 tcpinsizestat_values, 0));
   1995 
   1996 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   1997 
   1998 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   1999 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   2000 						 ISC_XMLCHAR "response-size"));
   2001 
   2002 		CHECK(dump_histo(
   2003 			server->sctx->tcpoutstats6, isc_statsformat_xml, writer,
   2004 			NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max,
   2005 			tcpoutsizestats_index, tcpoutsizestat_values, 0));
   2006 
   2007 		TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
   2008 		TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
   2009 		TRY0(xmlTextWriterEndElement(writer)); /* </ipv6> */
   2010 		TRY0(xmlTextWriterEndElement(writer)); /* </traffic> */
   2011 	}
   2012 
   2013 	/*
   2014 	 * Render views.  For each view we know of, call its
   2015 	 * rendering function.
   2016 	 */
   2017 	view = ISC_LIST_HEAD(server->viewlist);
   2018 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
   2019 	while (view != NULL && ((flags & (STATS_XML_SERVER | STATS_XML_ZONES |
   2020 					  STATS_XML_XFRINS)) != 0))
   2021 	{
   2022 		isc_stats_t *istats = NULL;
   2023 		dns_stats_t *dstats = NULL;
   2024 		dns_adb_t *adb = NULL;
   2025 
   2026 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view"));
   2027 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
   2028 						 ISC_XMLCHAR view->name));
   2029 
   2030 		if ((flags & STATS_XML_ZONES) != 0) {
   2031 			TRY0(xmlTextWriterStartElement(writer,
   2032 						       ISC_XMLCHAR "zones"));
   2033 			CHECK(dns_view_apply(view, true, NULL, zone_xmlrender,
   2034 					     writer));
   2035 			TRY0(xmlTextWriterEndElement(writer)); /* /zones */
   2036 		}
   2037 
   2038 		if ((flags & STATS_XML_XFRINS) != 0) {
   2039 			TRY0(xmlTextWriterStartElement(writer,
   2040 						       ISC_XMLCHAR "xfrins"));
   2041 			CHECK(dns_zt_apply(view->zonetable, true, NULL,
   2042 					   xfrin_xmlrender, writer));
   2043 			TRY0(xmlTextWriterEndElement(writer)); /* /xfrins */
   2044 		}
   2045 
   2046 		if ((flags & STATS_XML_SERVER) == 0) {
   2047 			TRY0(xmlTextWriterEndElement(writer)); /* /view */
   2048 			view = ISC_LIST_NEXT(view, link);
   2049 			continue;
   2050 		}
   2051 
   2052 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   2053 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   2054 						 ISC_XMLCHAR "resqtype"));
   2055 
   2056 		dns_resolver_getquerystats(view->resolver, &dstats);
   2057 		if (dstats != NULL) {
   2058 			dumparg.result = ISC_R_SUCCESS;
   2059 			dns_rdatatypestats_dump(dstats, rdtypestat_dump,
   2060 						&dumparg, 0);
   2061 			CHECK(dumparg.result);
   2062 		}
   2063 		dns_stats_detach(&dstats);
   2064 		TRY0(xmlTextWriterEndElement(writer));
   2065 
   2066 		/* <resstats> */
   2067 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   2068 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   2069 						 ISC_XMLCHAR "resstats"));
   2070 		dns_resolver_getstats(view->resolver, &istats);
   2071 		if (istats != NULL) {
   2072 			CHECK(dump_stats(istats, isc_statsformat_xml, writer,
   2073 					 NULL, resstats_xmldesc,
   2074 					 dns_resstatscounter_max,
   2075 					 resstats_index, resstat_values,
   2076 					 ISC_STATSDUMP_VERBOSE));
   2077 		}
   2078 		isc_stats_detach(&istats);
   2079 		TRY0(xmlTextWriterEndElement(writer)); /* </resstats> */
   2080 
   2081 		cacherrstats = dns_db_getrrsetstats(view->cachedb);
   2082 		if (cacherrstats != NULL) {
   2083 			TRY0(xmlTextWriterStartElement(writer,
   2084 						       ISC_XMLCHAR "cache"));
   2085 			TRY0(xmlTextWriterWriteAttribute(
   2086 				writer, ISC_XMLCHAR "name",
   2087 				ISC_XMLCHAR dns_cache_getname(view->cache)));
   2088 			dumparg.result = ISC_R_SUCCESS;
   2089 			dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
   2090 					       &dumparg, 0);
   2091 			CHECK(dumparg.result);
   2092 			TRY0(xmlTextWriterEndElement(writer)); /* cache */
   2093 		}
   2094 
   2095 		/* <adbstats> */
   2096 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   2097 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   2098 						 ISC_XMLCHAR "adbstat"));
   2099 		dns_view_getadb(view, &adb);
   2100 		if (adb != NULL) {
   2101 			result = dump_stats(dns_adb_getstats(adb),
   2102 					    isc_statsformat_xml, writer, NULL,
   2103 					    adbstats_xmldesc, dns_adbstats_max,
   2104 					    adbstats_index, adbstat_values,
   2105 					    ISC_STATSDUMP_VERBOSE);
   2106 			dns_adb_detach(&adb);
   2107 			CHECK(result);
   2108 		}
   2109 		TRY0(xmlTextWriterEndElement(writer)); /* </adbstats> */
   2110 
   2111 		/* <cachestats> */
   2112 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
   2113 		TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
   2114 						 ISC_XMLCHAR "cachestats"));
   2115 		TRY0(dns_cache_renderxml(view->cache, writer));
   2116 		TRY0(xmlTextWriterEndElement(writer)); /* </cachestats> */
   2117 
   2118 		TRY0(xmlTextWriterEndElement(writer)); /* view */
   2119 
   2120 		view = ISC_LIST_NEXT(view, link);
   2121 	}
   2122 	TRY0(xmlTextWriterEndElement(writer)); /* /views */
   2123 
   2124 	if ((flags & STATS_XML_MEM) != 0) {
   2125 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
   2126 		TRY0(isc_mem_renderxml(writer));
   2127 		TRY0(xmlTextWriterEndElement(writer)); /* /memory */
   2128 	}
   2129 
   2130 	TRY0(xmlTextWriterEndElement(writer)); /* /statistics */
   2131 	TRY0(xmlTextWriterEndDocument(writer));
   2132 
   2133 	xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 0);
   2134 	if (*buf == NULL) {
   2135 		goto cleanup;
   2136 	}
   2137 
   2138 	xmlFreeTextWriter(writer);
   2139 	xmlFreeDoc(doc);
   2140 	return ISC_R_SUCCESS;
   2141 
   2142 cleanup:
   2143 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   2144 		      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   2145 		      "failed generating XML response");
   2146 	if (writer != NULL) {
   2147 		xmlFreeTextWriter(writer);
   2148 	}
   2149 	if (doc != NULL) {
   2150 		xmlFreeDoc(doc);
   2151 	}
   2152 	return ISC_R_FAILURE;
   2153 }
   2154 
   2155 static void
   2156 wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
   2157 	UNUSED(arg);
   2158 
   2159 	xmlFree(isc_buffer_base(buffer));
   2160 }
   2161 
   2162 static isc_result_t
   2163 render_xml(uint32_t flags, void *arg, unsigned int *retcode,
   2164 	   const char **retmsg, const char **mimetype, isc_buffer_t *b,
   2165 	   isc_httpdfree_t **freecb, void **freecb_args) {
   2166 	unsigned char *msg = NULL;
   2167 	int msglen;
   2168 	named_server_t *server = arg;
   2169 	isc_result_t result;
   2170 
   2171 	result = generatexml(server, flags, &msglen, &msg);
   2172 
   2173 	if (result == ISC_R_SUCCESS) {
   2174 		*retcode = 200;
   2175 		*retmsg = "OK";
   2176 		*mimetype = "text/xml";
   2177 		isc_buffer_reinit(b, msg, msglen);
   2178 		isc_buffer_add(b, msglen);
   2179 		*freecb = wrap_xmlfree;
   2180 		*freecb_args = NULL;
   2181 	} else {
   2182 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   2183 			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   2184 			      "failed at rendering XML()");
   2185 	}
   2186 
   2187 	return result;
   2188 }
   2189 
   2190 static isc_result_t
   2191 render_xml_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2192 	       void *arg, unsigned int *retcode, const char **retmsg,
   2193 	       const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
   2194 	       void **freecb_args) {
   2195 	UNUSED(httpd);
   2196 	UNUSED(urlinfo);
   2197 	return render_xml(STATS_XML_ALL, arg, retcode, retmsg, mimetype, b,
   2198 			  freecb, freecb_args);
   2199 }
   2200 
   2201 static isc_result_t
   2202 render_xml_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2203 		  void *arg, unsigned int *retcode, const char **retmsg,
   2204 		  const char **mimetype, isc_buffer_t *b,
   2205 		  isc_httpdfree_t **freecb, void **freecb_args) {
   2206 	UNUSED(httpd);
   2207 	UNUSED(urlinfo);
   2208 	return render_xml(STATS_XML_STATUS, arg, retcode, retmsg, mimetype, b,
   2209 			  freecb, freecb_args);
   2210 }
   2211 
   2212 static isc_result_t
   2213 render_xml_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2214 		  void *arg, unsigned int *retcode, const char **retmsg,
   2215 		  const char **mimetype, isc_buffer_t *b,
   2216 		  isc_httpdfree_t **freecb, void **freecb_args) {
   2217 	UNUSED(httpd);
   2218 	UNUSED(urlinfo);
   2219 	return render_xml(STATS_XML_SERVER, arg, retcode, retmsg, mimetype, b,
   2220 			  freecb, freecb_args);
   2221 }
   2222 
   2223 static isc_result_t
   2224 render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2225 		 void *arg, unsigned int *retcode, const char **retmsg,
   2226 		 const char **mimetype, isc_buffer_t *b,
   2227 		 isc_httpdfree_t **freecb, void **freecb_args) {
   2228 	UNUSED(httpd);
   2229 	UNUSED(urlinfo);
   2230 	return render_xml(STATS_XML_ZONES, arg, retcode, retmsg, mimetype, b,
   2231 			  freecb, freecb_args);
   2232 }
   2233 
   2234 static isc_result_t
   2235 render_xml_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2236 		  void *arg, unsigned int *retcode, const char **retmsg,
   2237 		  const char **mimetype, isc_buffer_t *b,
   2238 		  isc_httpdfree_t **freecb, void **freecb_args) {
   2239 	UNUSED(httpd);
   2240 	UNUSED(urlinfo);
   2241 	return render_xml(STATS_XML_XFRINS, arg, retcode, retmsg, mimetype, b,
   2242 			  freecb, freecb_args);
   2243 }
   2244 
   2245 static isc_result_t
   2246 render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2247 	       void *arg, unsigned int *retcode, const char **retmsg,
   2248 	       const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
   2249 	       void **freecb_args) {
   2250 	UNUSED(httpd);
   2251 	UNUSED(urlinfo);
   2252 	return render_xml(STATS_XML_NET, arg, retcode, retmsg, mimetype, b,
   2253 			  freecb, freecb_args);
   2254 }
   2255 
   2256 static isc_result_t
   2257 render_xml_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2258 	       void *arg, unsigned int *retcode, const char **retmsg,
   2259 	       const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
   2260 	       void **freecb_args) {
   2261 	UNUSED(httpd);
   2262 	UNUSED(urlinfo);
   2263 	return render_xml(STATS_XML_MEM, arg, retcode, retmsg, mimetype, b,
   2264 			  freecb, freecb_args);
   2265 }
   2266 
   2267 static isc_result_t
   2268 render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   2269 		   void *arg, unsigned int *retcode, const char **retmsg,
   2270 		   const char **mimetype, isc_buffer_t *b,
   2271 		   isc_httpdfree_t **freecb, void **freecb_args) {
   2272 	UNUSED(httpd);
   2273 	UNUSED(urlinfo);
   2274 	return render_xml(STATS_XML_TRAFFIC, arg, retcode, retmsg, mimetype, b,
   2275 			  freecb, freecb_args);
   2276 }
   2277 
   2278 #endif /* HAVE_LIBXML2 */
   2279 
   2280 #ifdef HAVE_JSON_C
   2281 /*
   2282  * Which statistics to include when rendering to JSON
   2283  */
   2284 #define STATS_JSON_STATUS  0x00 /* display only common statistics */
   2285 #define STATS_JSON_SERVER  0x01
   2286 #define STATS_JSON_ZONES   0x02
   2287 #define STATS_JSON_XFRINS  0x04
   2288 #define STATS_JSON_NET	   0x08
   2289 #define STATS_JSON_MEM	   0x10
   2290 #define STATS_JSON_TRAFFIC 0x20
   2291 #define STATS_JSON_ALL	   0xff
   2292 
   2293 #define CHECKMEM(m)                              \
   2294 	do {                                     \
   2295 		if (m == NULL) {                 \
   2296 			result = ISC_R_NOMEMORY; \
   2297 			goto cleanup;            \
   2298 		}                                \
   2299 	} while (0)
   2300 
   2301 static void
   2302 wrap_jsonfree(isc_buffer_t *buffer, void *arg) {
   2303 	json_object_put(isc_buffer_base(buffer));
   2304 	if (arg != NULL) {
   2305 		json_object_put((json_object *)arg);
   2306 	}
   2307 }
   2308 
   2309 static json_object *
   2310 addzone(char *name, char *classname, const char *ztype, uint32_t serial,
   2311 	bool add_serial) {
   2312 	json_object *node = json_object_new_object();
   2313 
   2314 	if (node == NULL) {
   2315 		return NULL;
   2316 	}
   2317 
   2318 	json_object_object_add(node, "name", json_object_new_string(name));
   2319 	json_object_object_add(node, "class",
   2320 			       json_object_new_string(classname));
   2321 	if (add_serial) {
   2322 		json_object_object_add(node, "serial",
   2323 				       json_object_new_int64(serial));
   2324 	}
   2325 	if (ztype != NULL) {
   2326 		json_object_object_add(node, "type",
   2327 				       json_object_new_string(ztype));
   2328 	}
   2329 	return node;
   2330 }
   2331 
   2332 static isc_result_t
   2333 zone_jsonrender(dns_zone_t *zone, void *arg) {
   2334 	isc_result_t result = ISC_R_SUCCESS;
   2335 	char buf[1024 + 32]; /* sufficiently large for zone name and class */
   2336 	char classbuf[64];   /* sufficiently large for class */
   2337 	char *zone_name_only = NULL;
   2338 	char *class_only = NULL;
   2339 	dns_rdataclass_t rdclass;
   2340 	uint32_t serial;
   2341 	json_object *zonearray = (json_object *)arg;
   2342 	json_object *zoneobj = NULL;
   2343 	dns_zonestat_level_t statlevel;
   2344 	isc_time_t timestamp;
   2345 
   2346 	statlevel = dns_zone_getstatlevel(zone);
   2347 	if (statlevel == dns_zonestat_none) {
   2348 		return ISC_R_SUCCESS;
   2349 	}
   2350 
   2351 	dns_zone_nameonly(zone, buf, sizeof(buf));
   2352 	zone_name_only = buf;
   2353 
   2354 	rdclass = dns_zone_getclass(zone);
   2355 	dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
   2356 	class_only = classbuf;
   2357 
   2358 	if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) {
   2359 		zoneobj = addzone(zone_name_only, class_only,
   2360 				  user_zonetype(zone), 0, false);
   2361 	} else {
   2362 		zoneobj = addzone(zone_name_only, class_only,
   2363 				  user_zonetype(zone), serial, true);
   2364 	}
   2365 
   2366 	if (zoneobj == NULL) {
   2367 		return ISC_R_NOMEMORY;
   2368 	}
   2369 
   2370 	/*
   2371 	 * Export zone timers to the statistics channel in JSON format.
   2372 	 * For primary zones, only include the loaded time.  For secondary
   2373 	 * zones, also include the expire and refresh times.
   2374 	 */
   2375 
   2376 	CHECK(dns_zone_getloadtime(zone, &timestamp));
   2377 
   2378 	isc_time_formatISO8601(&timestamp, buf, 64);
   2379 	json_object_object_add(zoneobj, "loaded", json_object_new_string(buf));
   2380 
   2381 	if (dns_zone_gettype(zone) == dns_zone_secondary) {
   2382 		CHECK(dns_zone_getexpiretime(zone, &timestamp));
   2383 		isc_time_formatISO8601(&timestamp, buf, 64);
   2384 		json_object_object_add(zoneobj, "expires",
   2385 				       json_object_new_string(buf));
   2386 
   2387 		CHECK(dns_zone_getrefreshtime(zone, &timestamp));
   2388 		isc_time_formatISO8601(&timestamp, buf, 64);
   2389 		json_object_object_add(zoneobj, "refresh",
   2390 				       json_object_new_string(buf));
   2391 	}
   2392 
   2393 	if (statlevel == dns_zonestat_full) {
   2394 		isc_stats_t *zonestats;
   2395 		isc_stats_t *gluecachestats;
   2396 		dns_stats_t *rcvquerystats;
   2397 		dns_stats_t *dnssecsignstats;
   2398 		uint64_t nsstat_values[ns_statscounter_max];
   2399 		uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
   2400 
   2401 		zonestats = dns_zone_getrequeststats(zone);
   2402 		if (zonestats != NULL) {
   2403 			json_object *counters = json_object_new_object();
   2404 			if (counters == NULL) {
   2405 				result = ISC_R_NOMEMORY;
   2406 				goto cleanup;
   2407 			}
   2408 
   2409 			result = dump_stats(zonestats, isc_statsformat_json,
   2410 					    counters, NULL, nsstats_xmldesc,
   2411 					    ns_statscounter_max, nsstats_index,
   2412 					    nsstat_values, 0);
   2413 			if (result != ISC_R_SUCCESS) {
   2414 				json_object_put(counters);
   2415 				goto cleanup;
   2416 			}
   2417 
   2418 			if (json_object_get_object(counters)->count != 0) {
   2419 				json_object_object_add(zoneobj, "rcodes",
   2420 						       counters);
   2421 			} else {
   2422 				json_object_put(counters);
   2423 			}
   2424 		}
   2425 
   2426 		gluecachestats = dns_zone_getgluecachestats(zone);
   2427 		if (gluecachestats != NULL) {
   2428 			json_object *counters = json_object_new_object();
   2429 			if (counters == NULL) {
   2430 				result = ISC_R_NOMEMORY;
   2431 				goto cleanup;
   2432 			}
   2433 
   2434 			result = dump_stats(
   2435 				gluecachestats, isc_statsformat_json, counters,
   2436 				NULL, gluecachestats_xmldesc,
   2437 				dns_gluecachestatscounter_max,
   2438 				gluecachestats_index, gluecachestats_values, 0);
   2439 			if (result != ISC_R_SUCCESS) {
   2440 				json_object_put(counters);
   2441 				goto cleanup;
   2442 			}
   2443 
   2444 			if (json_object_get_object(counters)->count != 0) {
   2445 				json_object_object_add(zoneobj, "gluecache",
   2446 						       counters);
   2447 			} else {
   2448 				json_object_put(counters);
   2449 			}
   2450 		}
   2451 
   2452 		rcvquerystats = dns_zone_getrcvquerystats(zone);
   2453 		if (rcvquerystats != NULL) {
   2454 			stats_dumparg_t dumparg;
   2455 			json_object *counters = json_object_new_object();
   2456 			CHECKMEM(counters);
   2457 
   2458 			dumparg.type = isc_statsformat_json;
   2459 			dumparg.arg = counters;
   2460 			dumparg.result = ISC_R_SUCCESS;
   2461 			dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
   2462 						&dumparg, 0);
   2463 			if (dumparg.result != ISC_R_SUCCESS) {
   2464 				json_object_put(counters);
   2465 				goto cleanup;
   2466 			}
   2467 
   2468 			if (json_object_get_object(counters)->count != 0) {
   2469 				json_object_object_add(zoneobj, "qtypes",
   2470 						       counters);
   2471 			} else {
   2472 				json_object_put(counters);
   2473 			}
   2474 		}
   2475 
   2476 		dnssecsignstats = dns_zone_getdnssecsignstats(zone);
   2477 		if (dnssecsignstats != NULL) {
   2478 			stats_dumparg_t dumparg;
   2479 			json_object *sign_counters = json_object_new_object();
   2480 			CHECKMEM(sign_counters);
   2481 
   2482 			dumparg.type = isc_statsformat_json;
   2483 			dumparg.arg = sign_counters;
   2484 			dumparg.result = ISC_R_SUCCESS;
   2485 			dns_dnssecsignstats_dump(
   2486 				dnssecsignstats, dns_dnssecsignstats_sign,
   2487 				dnssecsignstat_dump, &dumparg, 0);
   2488 			if (dumparg.result != ISC_R_SUCCESS) {
   2489 				json_object_put(sign_counters);
   2490 				goto cleanup;
   2491 			}
   2492 
   2493 			if (json_object_get_object(sign_counters)->count != 0) {
   2494 				json_object_object_add(zoneobj, "dnssec-sign",
   2495 						       sign_counters);
   2496 			} else {
   2497 				json_object_put(sign_counters);
   2498 			}
   2499 
   2500 			json_object *refresh_counters =
   2501 				json_object_new_object();
   2502 			CHECKMEM(refresh_counters);
   2503 
   2504 			dumparg.type = isc_statsformat_json;
   2505 			dumparg.arg = refresh_counters;
   2506 			dumparg.result = ISC_R_SUCCESS;
   2507 			dns_dnssecsignstats_dump(
   2508 				dnssecsignstats, dns_dnssecsignstats_refresh,
   2509 				dnssecsignstat_dump, &dumparg, 0);
   2510 			if (dumparg.result != ISC_R_SUCCESS) {
   2511 				json_object_put(refresh_counters);
   2512 				goto cleanup;
   2513 			}
   2514 
   2515 			if (json_object_get_object(refresh_counters)->count !=
   2516 			    0)
   2517 			{
   2518 				json_object_object_add(zoneobj,
   2519 						       "dnssec-refresh",
   2520 						       refresh_counters);
   2521 			} else {
   2522 				json_object_put(refresh_counters);
   2523 			}
   2524 		}
   2525 	}
   2526 
   2527 	json_object_array_add(zonearray, zoneobj);
   2528 	zoneobj = NULL;
   2529 	result = ISC_R_SUCCESS;
   2530 
   2531 cleanup:
   2532 	if (zoneobj != NULL) {
   2533 		json_object_put(zoneobj);
   2534 	}
   2535 	return result;
   2536 }
   2537 
   2538 static isc_result_t
   2539 xfrin_jsonrender(dns_zone_t *zone, void *arg) {
   2540 	isc_result_t result;
   2541 	char buf[1024 + 32]; /* sufficiently large for zone name and class */
   2542 	char classbuf[64];   /* sufficiently large for class */
   2543 	char *zone_name_only = NULL;
   2544 	char *class_only = NULL;
   2545 	dns_rdataclass_t rdclass;
   2546 	uint32_t serial;
   2547 	json_object *xfrinarray = (json_object *)arg;
   2548 	json_object *xfrinobj = NULL;
   2549 	isc_sockaddr_t addr;
   2550 	const isc_sockaddr_t *addrp = NULL;
   2551 	char addr_buf[ISC_SOCKADDR_FORMATSIZE];
   2552 	dns_transport_type_t transport_type;
   2553 	dns_zonestat_level_t statlevel;
   2554 	dns_xfrin_t *xfr = NULL;
   2555 	bool is_firstrefresh, is_running, is_deferred, is_presoa, is_pending;
   2556 	bool needs_refresh;
   2557 	bool is_first_data_received, is_ixfr;
   2558 	unsigned int nmsg = 0;
   2559 	unsigned int nrecs = 0;
   2560 	uint64_t nbytes = 0;
   2561 	uint64_t rate = 0;
   2562 
   2563 	statlevel = dns_zone_getstatlevel(zone);
   2564 	if (statlevel == dns_zonestat_none) {
   2565 		return ISC_R_SUCCESS;
   2566 	}
   2567 
   2568 	dns_zone_nameonly(zone, buf, sizeof(buf));
   2569 	zone_name_only = buf;
   2570 
   2571 	rdclass = dns_zone_getclass(zone);
   2572 	dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
   2573 	class_only = classbuf;
   2574 
   2575 	if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) {
   2576 		xfrinobj = addzone(zone_name_only, class_only,
   2577 				   user_zonetype(zone), 0, false);
   2578 	} else {
   2579 		xfrinobj = addzone(zone_name_only, class_only,
   2580 				   user_zonetype(zone), serial, true);
   2581 	}
   2582 
   2583 	if (xfrinobj == NULL) {
   2584 		result = ISC_R_NOMEMORY;
   2585 		goto cleanup;
   2586 	}
   2587 
   2588 	result = dns_zone_getxfr(zone, &xfr, &is_firstrefresh, &is_running,
   2589 				 &is_deferred, &is_presoa, &is_pending,
   2590 				 &needs_refresh);
   2591 	if (result != ISC_R_SUCCESS) {
   2592 		result = ISC_R_SUCCESS;
   2593 		goto cleanup;
   2594 	}
   2595 
   2596 	if (!is_running && !is_deferred && !is_presoa && !is_pending &&
   2597 	    !needs_refresh)
   2598 	{
   2599 		/* No ongoing/queued transfer. */
   2600 		goto cleanup;
   2601 	}
   2602 
   2603 	if (is_running && xfr == NULL) {
   2604 		/* The transfer is finished, and it's shutting down. */
   2605 		goto cleanup;
   2606 	}
   2607 
   2608 	if (is_running) {
   2609 		serial = dns_xfrin_getendserial(xfr);
   2610 		if (serial != 0) {
   2611 			json_object_object_add(xfrinobj, "remoteserial",
   2612 					       json_object_new_int64(serial));
   2613 		}
   2614 	}
   2615 
   2616 	json_object_object_add(
   2617 		xfrinobj, "firstrefresh",
   2618 		json_object_new_string(is_firstrefresh ? "Yes" : "No"));
   2619 
   2620 	if (is_running) {
   2621 		const char *xfr_state = NULL;
   2622 
   2623 		dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received,
   2624 				   &is_ixfr);
   2625 		json_object_object_add(xfrinobj, "state",
   2626 				       json_object_new_string(xfr_state));
   2627 	} else if (is_deferred) {
   2628 		json_object_object_add(xfrinobj, "state",
   2629 				       json_object_new_string("Deferred"));
   2630 	} else if (is_presoa) {
   2631 		json_object_object_add(xfrinobj, "state",
   2632 				       json_object_new_string("Refresh SOA"));
   2633 	} else if (is_pending) {
   2634 		json_object_object_add(xfrinobj, "state",
   2635 				       json_object_new_string("Pending"));
   2636 	} else if (needs_refresh) {
   2637 		json_object_object_add(xfrinobj, "state",
   2638 				       json_object_new_string("Needs Refresh"));
   2639 	} else {
   2640 		UNREACHABLE();
   2641 	}
   2642 
   2643 	json_object_object_add(
   2644 		xfrinobj, "refreshqueued",
   2645 		json_object_new_string(is_running && needs_refresh ? "Yes"
   2646 								   : "No"));
   2647 
   2648 	if (is_running) {
   2649 		addrp = dns_xfrin_getsourceaddr(xfr);
   2650 		isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
   2651 		json_object_object_add(xfrinobj, "localaddr",
   2652 				       json_object_new_string(addr_buf));
   2653 	} else if (is_presoa) {
   2654 		dns_zone_getsourceaddr(zone, &addr);
   2655 		isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf));
   2656 		json_object_object_add(xfrinobj, "localaddr",
   2657 				       json_object_new_string(addr_buf));
   2658 	} else {
   2659 		json_object_object_add(xfrinobj, "localaddr",
   2660 				       json_object_new_string("-"));
   2661 	}
   2662 
   2663 	if (is_running) {
   2664 		addrp = dns_xfrin_getprimaryaddr(xfr);
   2665 		isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf));
   2666 		json_object_object_add(xfrinobj, "remoteaddr",
   2667 				       json_object_new_string(addr_buf));
   2668 	} else if (is_presoa) {
   2669 		if (dns_zone_getprimaryaddr(zone, &addr) == ISC_R_SUCCESS) {
   2670 			isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf));
   2671 			json_object_object_add(
   2672 				xfrinobj, "remoteaddr",
   2673 				json_object_new_string(addr_buf));
   2674 		} else {
   2675 			json_object_object_add(xfrinobj, "remoteaddr",
   2676 					       json_object_new_string("-"));
   2677 		}
   2678 	} else {
   2679 		json_object_object_add(xfrinobj, "remoteaddr",
   2680 				       json_object_new_string("-"));
   2681 	}
   2682 
   2683 	if (is_running || is_presoa) {
   2684 		if (is_running) {
   2685 			transport_type = dns_xfrin_getsoatransporttype(xfr);
   2686 		} else {
   2687 			transport_type = dns_zone_getrequesttransporttype(zone);
   2688 		}
   2689 
   2690 		if (transport_type == DNS_TRANSPORT_UDP) {
   2691 			json_object_object_add(xfrinobj, "soatransport",
   2692 					       json_object_new_string("UDP"));
   2693 		} else if (transport_type == DNS_TRANSPORT_TCP) {
   2694 			json_object_object_add(xfrinobj, "soatransport",
   2695 					       json_object_new_string("TCP"));
   2696 		} else if (transport_type == DNS_TRANSPORT_TLS) {
   2697 			json_object_object_add(xfrinobj, "soatransport",
   2698 					       json_object_new_string("TLS"));
   2699 		} else if (transport_type == DNS_TRANSPORT_NONE) {
   2700 			json_object_object_add(xfrinobj, "soatransport",
   2701 					       json_object_new_string("None"));
   2702 		} else {
   2703 			/* We don't expect any other SOA transport type. */
   2704 			json_object_object_add(xfrinobj, "soatransport",
   2705 					       json_object_new_string("-"));
   2706 		}
   2707 	} else {
   2708 		json_object_object_add(xfrinobj, "soatransport",
   2709 				       json_object_new_string("-"));
   2710 	}
   2711 
   2712 	if (is_running) {
   2713 		transport_type = dns_xfrin_gettransporttype(xfr);
   2714 		if (transport_type == DNS_TRANSPORT_TCP) {
   2715 			json_object_object_add(xfrinobj, "transport",
   2716 					       json_object_new_string("TCP"));
   2717 		} else if (transport_type == DNS_TRANSPORT_TLS) {
   2718 			json_object_object_add(xfrinobj, "transport",
   2719 					       json_object_new_string("TLS"));
   2720 		} else {
   2721 			/* We don't expect any other transport type. */
   2722 			json_object_object_add(xfrinobj, "transport",
   2723 					       json_object_new_string("-"));
   2724 		}
   2725 	} else {
   2726 		json_object_object_add(xfrinobj, "transport",
   2727 				       json_object_new_string("-"));
   2728 	}
   2729 
   2730 	if (is_running) {
   2731 		const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr);
   2732 		char tsigkeyname_buf[DNS_NAME_FORMATSIZE];
   2733 
   2734 		if (tsigkeyname != NULL) {
   2735 			dns_name_format(tsigkeyname, tsigkeyname_buf,
   2736 					sizeof(tsigkeyname_buf));
   2737 			json_object_object_add(
   2738 				xfrinobj, "tsigkeyname",
   2739 				json_object_new_string(tsigkeyname_buf));
   2740 		} else {
   2741 			json_object_object_add(xfrinobj, "tsigkeyname", NULL);
   2742 		}
   2743 	} else {
   2744 		json_object_object_add(xfrinobj, "tsigkeyname", NULL);
   2745 	}
   2746 
   2747 	if (is_running || is_deferred || is_presoa || is_pending) {
   2748 		isc_time_t start = is_running ? dns_xfrin_getstarttime(xfr)
   2749 					      : dns_zone_getxfrintime(zone);
   2750 		isc_time_t now = isc_time_now();
   2751 		isc_time_t diff;
   2752 		uint32_t sec;
   2753 
   2754 		isc_time_subtract(&now, &start, &diff);
   2755 		sec = isc_time_seconds(&diff);
   2756 		json_object_object_add(xfrinobj, "duration",
   2757 				       json_object_new_int64((int64_t)sec));
   2758 	} else {
   2759 		json_object_object_add(xfrinobj, "duration",
   2760 				       json_object_new_int64(0));
   2761 	}
   2762 
   2763 	if (is_running) {
   2764 		dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
   2765 	}
   2766 	json_object_object_add(xfrinobj, "nmsg",
   2767 			       json_object_new_int64((int64_t)nmsg));
   2768 	json_object_object_add(xfrinobj, "nrecs",
   2769 			       json_object_new_int64((int64_t)nrecs));
   2770 	json_object_object_add(
   2771 		xfrinobj, "nbytes",
   2772 		json_object_new_int64(nbytes > INT64_MAX ? INT64_MAX
   2773 							 : (int64_t)nbytes));
   2774 	json_object_object_add(xfrinobj, "rate",
   2775 			       json_object_new_int64(rate > INT64_MAX
   2776 							     ? INT64_MAX
   2777 							     : (int64_t)rate));
   2778 
   2779 	if (is_running && is_first_data_received) {
   2780 		json_object_object_add(
   2781 			xfrinobj, "ixfr",
   2782 			json_object_new_string(is_ixfr ? "Yes" : "No"));
   2783 	} else {
   2784 		json_object_object_add(xfrinobj, "ixfr",
   2785 				       json_object_new_string(""));
   2786 	}
   2787 
   2788 	json_object_array_add(xfrinarray, xfrinobj);
   2789 	xfrinobj = NULL;
   2790 	result = ISC_R_SUCCESS;
   2791 
   2792 cleanup:
   2793 	if (xfr != NULL) {
   2794 		dns_xfrin_detach(&xfr);
   2795 	}
   2796 	if (xfrinobj != NULL) {
   2797 		json_object_put(xfrinobj);
   2798 	}
   2799 	return result;
   2800 }
   2801 
   2802 static isc_result_t
   2803 generatejson(named_server_t *server, size_t *msglen, const char **msg,
   2804 	     json_object **rootp, uint32_t flags) {
   2805 	dns_view_t *view;
   2806 	isc_result_t result = ISC_R_SUCCESS;
   2807 	json_object *bindstats, *viewlist, *counters, *obj;
   2808 	json_object *traffic = NULL;
   2809 	json_object *udpreq4 = NULL, *udpresp4 = NULL;
   2810 	json_object *tcpreq4 = NULL, *tcpresp4 = NULL;
   2811 	json_object *udpreq6 = NULL, *udpresp6 = NULL;
   2812 	json_object *tcpreq6 = NULL, *tcpresp6 = NULL;
   2813 	uint64_t nsstat_values[ns_statscounter_max];
   2814 	uint64_t resstat_values[dns_resstatscounter_max];
   2815 	uint64_t adbstat_values[dns_adbstats_max];
   2816 	uint64_t zonestat_values[dns_zonestatscounter_max];
   2817 	uint64_t sockstat_values[isc_sockstatscounter_max];
   2818 	uint64_t udpinsizestat_values[dns_sizecounter_in_max];
   2819 	uint64_t udpoutsizestat_values[dns_sizecounter_out_max];
   2820 	uint64_t tcpinsizestat_values[dns_sizecounter_in_max];
   2821 	uint64_t tcpoutsizestat_values[dns_sizecounter_out_max];
   2822 #ifdef HAVE_DNSTAP
   2823 	uint64_t dnstapstat_values[dns_dnstapcounter_max];
   2824 #endif /* ifdef HAVE_DNSTAP */
   2825 	stats_dumparg_t dumparg;
   2826 	char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   2827 	char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   2828 	char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
   2829 	isc_time_t now;
   2830 
   2831 	REQUIRE(msglen != NULL);
   2832 	REQUIRE(msg != NULL && *msg == NULL);
   2833 	REQUIRE(rootp == NULL || *rootp == NULL);
   2834 
   2835 	bindstats = json_object_new_object();
   2836 	if (bindstats == NULL) {
   2837 		return ISC_R_NOMEMORY;
   2838 	}
   2839 
   2840 	/*
   2841 	 * These statistics are included no matter which URL we use.
   2842 	 */
   2843 	obj = json_object_new_string(STATS_JSON_VERSION);
   2844 	CHECKMEM(obj);
   2845 	json_object_object_add(bindstats, "json-stats-version", obj);
   2846 
   2847 	now = isc_time_now();
   2848 	isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof(boottime));
   2849 	isc_time_formatISO8601ms(&named_g_configtime, configtime,
   2850 				 sizeof configtime);
   2851 	isc_time_formatISO8601ms(&now, nowstr, sizeof(nowstr));
   2852 
   2853 	obj = json_object_new_string(boottime);
   2854 	CHECKMEM(obj);
   2855 	json_object_object_add(bindstats, "boot-time", obj);
   2856 
   2857 	obj = json_object_new_string(configtime);
   2858 	CHECKMEM(obj);
   2859 	json_object_object_add(bindstats, "config-time", obj);
   2860 
   2861 	obj = json_object_new_string(nowstr);
   2862 	CHECKMEM(obj);
   2863 	json_object_object_add(bindstats, "current-time", obj);
   2864 	obj = json_object_new_string(PACKAGE_VERSION);
   2865 	CHECKMEM(obj);
   2866 	json_object_object_add(bindstats, "version", obj);
   2867 
   2868 	if ((flags & STATS_JSON_SERVER) != 0) {
   2869 		/* OPCODE counters */
   2870 		counters = json_object_new_object();
   2871 
   2872 		dumparg.result = ISC_R_SUCCESS;
   2873 		dumparg.type = isc_statsformat_json;
   2874 		dumparg.arg = counters;
   2875 
   2876 		dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
   2877 				     &dumparg, ISC_STATSDUMP_VERBOSE);
   2878 		if (dumparg.result != ISC_R_SUCCESS) {
   2879 			json_object_put(counters);
   2880 			goto cleanup;
   2881 		}
   2882 
   2883 		if (json_object_get_object(counters)->count != 0) {
   2884 			json_object_object_add(bindstats, "opcodes", counters);
   2885 		} else {
   2886 			json_object_put(counters);
   2887 		}
   2888 
   2889 		/* OPCODE counters */
   2890 		counters = json_object_new_object();
   2891 
   2892 		dumparg.type = isc_statsformat_json;
   2893 		dumparg.arg = counters;
   2894 
   2895 		dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump,
   2896 				    &dumparg, ISC_STATSDUMP_VERBOSE);
   2897 		if (dumparg.result != ISC_R_SUCCESS) {
   2898 			json_object_put(counters);
   2899 			goto cleanup;
   2900 		}
   2901 
   2902 		if (json_object_get_object(counters)->count != 0) {
   2903 			json_object_object_add(bindstats, "rcodes", counters);
   2904 		} else {
   2905 			json_object_put(counters);
   2906 		}
   2907 
   2908 		/* QTYPE counters */
   2909 		counters = json_object_new_object();
   2910 
   2911 		dumparg.result = ISC_R_SUCCESS;
   2912 		dumparg.arg = counters;
   2913 
   2914 		dns_rdatatypestats_dump(server->sctx->rcvquerystats,
   2915 					rdtypestat_dump, &dumparg, 0);
   2916 		if (dumparg.result != ISC_R_SUCCESS) {
   2917 			json_object_put(counters);
   2918 			goto cleanup;
   2919 		}
   2920 
   2921 		if (json_object_get_object(counters)->count != 0) {
   2922 			json_object_object_add(bindstats, "qtypes", counters);
   2923 		} else {
   2924 			json_object_put(counters);
   2925 		}
   2926 
   2927 		/* server stat counters */
   2928 		counters = json_object_new_object();
   2929 
   2930 		dumparg.result = ISC_R_SUCCESS;
   2931 		dumparg.arg = counters;
   2932 
   2933 		result = dump_stats(ns_stats_get(server->sctx->nsstats),
   2934 				    isc_statsformat_json, counters, NULL,
   2935 				    nsstats_xmldesc, ns_statscounter_max,
   2936 				    nsstats_index, nsstat_values, 0);
   2937 		if (result != ISC_R_SUCCESS) {
   2938 			json_object_put(counters);
   2939 			goto cleanup;
   2940 		}
   2941 
   2942 		if (json_object_get_object(counters)->count != 0) {
   2943 			json_object_object_add(bindstats, "nsstats", counters);
   2944 		} else {
   2945 			json_object_put(counters);
   2946 		}
   2947 
   2948 		/* zone stat counters */
   2949 		counters = json_object_new_object();
   2950 
   2951 		dumparg.result = ISC_R_SUCCESS;
   2952 		dumparg.arg = counters;
   2953 
   2954 		result = dump_stats(server->zonestats, isc_statsformat_json,
   2955 				    counters, NULL, zonestats_xmldesc,
   2956 				    dns_zonestatscounter_max, zonestats_index,
   2957 				    zonestat_values, 0);
   2958 		if (result != ISC_R_SUCCESS) {
   2959 			json_object_put(counters);
   2960 			goto cleanup;
   2961 		}
   2962 
   2963 		if (json_object_get_object(counters)->count != 0) {
   2964 			json_object_object_add(bindstats, "zonestats",
   2965 					       counters);
   2966 		} else {
   2967 			json_object_put(counters);
   2968 		}
   2969 
   2970 		/* resolver stat counters */
   2971 		counters = json_object_new_object();
   2972 
   2973 		dumparg.result = ISC_R_SUCCESS;
   2974 		dumparg.arg = counters;
   2975 
   2976 		result = dump_stats(server->resolverstats, isc_statsformat_json,
   2977 				    counters, NULL, resstats_xmldesc,
   2978 				    dns_resstatscounter_max, resstats_index,
   2979 				    resstat_values, 0);
   2980 		if (result != ISC_R_SUCCESS) {
   2981 			json_object_put(counters);
   2982 			goto cleanup;
   2983 		}
   2984 
   2985 		if (json_object_get_object(counters)->count != 0) {
   2986 			json_object_object_add(bindstats, "resstats", counters);
   2987 		} else {
   2988 			json_object_put(counters);
   2989 		}
   2990 
   2991 #ifdef HAVE_DNSTAP
   2992 		/* dnstap stat counters */
   2993 		if (named_g_server->dtenv != NULL) {
   2994 			isc_stats_t *dnstapstats = NULL;
   2995 			dns_dt_getstats(named_g_server->dtenv, &dnstapstats);
   2996 			counters = json_object_new_object();
   2997 			dumparg.result = ISC_R_SUCCESS;
   2998 			dumparg.arg = counters;
   2999 			result = dump_stats(dnstapstats, isc_statsformat_json,
   3000 					    counters, NULL, dnstapstats_xmldesc,
   3001 					    dns_dnstapcounter_max,
   3002 					    dnstapstats_index,
   3003 					    dnstapstat_values, 0);
   3004 			isc_stats_detach(&dnstapstats);
   3005 			if (result != ISC_R_SUCCESS) {
   3006 				json_object_put(counters);
   3007 				goto cleanup;
   3008 			}
   3009 
   3010 			if (json_object_get_object(counters)->count != 0) {
   3011 				json_object_object_add(bindstats, "dnstapstats",
   3012 						       counters);
   3013 			} else {
   3014 				json_object_put(counters);
   3015 			}
   3016 		}
   3017 #endif /* ifdef HAVE_DNSTAP */
   3018 	}
   3019 
   3020 	if ((flags &
   3021 	     (STATS_JSON_SERVER | STATS_JSON_ZONES | STATS_JSON_XFRINS)) != 0)
   3022 	{
   3023 		viewlist = json_object_new_object();
   3024 		CHECKMEM(viewlist);
   3025 
   3026 		json_object_object_add(bindstats, "views", viewlist);
   3027 
   3028 		view = ISC_LIST_HEAD(server->viewlist);
   3029 		while (view != NULL) {
   3030 			json_object *za, *xa, *v = json_object_new_object();
   3031 			dns_adb_t *adb = NULL;
   3032 
   3033 			CHECKMEM(v);
   3034 			json_object_object_add(viewlist, view->name, v);
   3035 
   3036 			za = json_object_new_array();
   3037 			CHECKMEM(za);
   3038 
   3039 			if ((flags & STATS_JSON_ZONES) != 0) {
   3040 				CHECK(dns_view_apply(view, true, NULL,
   3041 						     zone_jsonrender, za));
   3042 			}
   3043 
   3044 			if (json_object_array_length(za) != 0) {
   3045 				json_object_object_add(v, "zones", za);
   3046 			} else {
   3047 				json_object_put(za);
   3048 			}
   3049 
   3050 			xa = json_object_new_array();
   3051 			CHECKMEM(xa);
   3052 
   3053 			if ((flags & STATS_JSON_XFRINS) != 0) {
   3054 				CHECK(dns_zt_apply(view->zonetable, true, NULL,
   3055 						   xfrin_jsonrender, xa));
   3056 			}
   3057 
   3058 			if (json_object_array_length(xa) != 0) {
   3059 				json_object_object_add(v, "xfrins", xa);
   3060 			} else {
   3061 				json_object_put(xa);
   3062 			}
   3063 
   3064 			if ((flags & STATS_JSON_SERVER) != 0) {
   3065 				json_object *res = NULL;
   3066 				dns_stats_t *dstats = NULL;
   3067 				isc_stats_t *istats = NULL;
   3068 
   3069 				res = json_object_new_object();
   3070 				CHECKMEM(res);
   3071 				json_object_object_add(v, "resolver", res);
   3072 
   3073 				dns_resolver_getstats(view->resolver, &istats);
   3074 				if (istats != NULL) {
   3075 					counters = json_object_new_object();
   3076 					CHECKMEM(counters);
   3077 
   3078 					result = dump_stats(
   3079 						istats, isc_statsformat_json,
   3080 						counters, NULL,
   3081 						resstats_xmldesc,
   3082 						dns_resstatscounter_max,
   3083 						resstats_index, resstat_values,
   3084 						0);
   3085 					if (result != ISC_R_SUCCESS) {
   3086 						json_object_put(counters);
   3087 						result = dumparg.result;
   3088 						goto cleanup;
   3089 					}
   3090 
   3091 					json_object_object_add(res, "stats",
   3092 							       counters);
   3093 					isc_stats_detach(&istats);
   3094 				}
   3095 
   3096 				dns_resolver_getquerystats(view->resolver,
   3097 							   &dstats);
   3098 				if (dstats != NULL) {
   3099 					counters = json_object_new_object();
   3100 					CHECKMEM(counters);
   3101 
   3102 					dumparg.arg = counters;
   3103 					dumparg.result = ISC_R_SUCCESS;
   3104 					dns_rdatatypestats_dump(dstats,
   3105 								rdtypestat_dump,
   3106 								&dumparg, 0);
   3107 					if (dumparg.result != ISC_R_SUCCESS) {
   3108 						json_object_put(counters);
   3109 						result = dumparg.result;
   3110 						goto cleanup;
   3111 					}
   3112 
   3113 					json_object_object_add(res, "qtypes",
   3114 							       counters);
   3115 					dns_stats_detach(&dstats);
   3116 				}
   3117 
   3118 				dstats = dns_db_getrrsetstats(view->cachedb);
   3119 				if (dstats != NULL) {
   3120 					counters = json_object_new_object();
   3121 					CHECKMEM(counters);
   3122 
   3123 					dumparg.arg = counters;
   3124 					dumparg.result = ISC_R_SUCCESS;
   3125 					dns_rdatasetstats_dump(
   3126 						dstats, rdatasetstats_dump,
   3127 						&dumparg, 0);
   3128 					if (dumparg.result != ISC_R_SUCCESS) {
   3129 						json_object_put(counters);
   3130 						result = dumparg.result;
   3131 						goto cleanup;
   3132 					}
   3133 
   3134 					json_object_object_add(res, "cache",
   3135 							       counters);
   3136 				}
   3137 
   3138 				counters = json_object_new_object();
   3139 				CHECKMEM(counters);
   3140 
   3141 				result = dns_cache_renderjson(view->cache,
   3142 							      counters);
   3143 				if (result != ISC_R_SUCCESS) {
   3144 					json_object_put(counters);
   3145 					goto cleanup;
   3146 				}
   3147 
   3148 				json_object_object_add(res, "cachestats",
   3149 						       counters);
   3150 
   3151 				dns_view_getadb(view, &adb);
   3152 				if (adb != NULL) {
   3153 					istats = dns_adb_getstats(adb);
   3154 					dns_adb_detach(&adb);
   3155 				}
   3156 				if (istats != NULL) {
   3157 					counters = json_object_new_object();
   3158 					CHECKMEM(counters);
   3159 
   3160 					result = dump_stats(
   3161 						istats, isc_statsformat_json,
   3162 						counters, NULL,
   3163 						adbstats_xmldesc,
   3164 						dns_adbstats_max,
   3165 						adbstats_index, adbstat_values,
   3166 						0);
   3167 					if (result != ISC_R_SUCCESS) {
   3168 						json_object_put(counters);
   3169 						result = dumparg.result;
   3170 						goto cleanup;
   3171 					}
   3172 
   3173 					json_object_object_add(res, "adb",
   3174 							       counters);
   3175 				}
   3176 			}
   3177 
   3178 			view = ISC_LIST_NEXT(view, link);
   3179 		}
   3180 	}
   3181 
   3182 	if ((flags & STATS_JSON_NET) != 0) {
   3183 		/* socket stat counters */
   3184 		counters = json_object_new_object();
   3185 
   3186 		dumparg.result = ISC_R_SUCCESS;
   3187 		dumparg.arg = counters;
   3188 
   3189 		result = dump_stats(server->sockstats, isc_statsformat_json,
   3190 				    counters, NULL, sockstats_xmldesc,
   3191 				    isc_sockstatscounter_max, sockstats_index,
   3192 				    sockstat_values, 0);
   3193 		if (result != ISC_R_SUCCESS) {
   3194 			json_object_put(counters);
   3195 			goto cleanup;
   3196 		}
   3197 
   3198 		if (json_object_get_object(counters)->count != 0) {
   3199 			json_object_object_add(bindstats, "sockstats",
   3200 					       counters);
   3201 		} else {
   3202 			json_object_put(counters);
   3203 		}
   3204 	}
   3205 
   3206 	if ((flags & STATS_JSON_MEM) != 0) {
   3207 		json_object *memory = json_object_new_object();
   3208 		CHECKMEM(memory);
   3209 
   3210 		result = isc_mem_renderjson(memory);
   3211 		if (result != ISC_R_SUCCESS) {
   3212 			json_object_put(memory);
   3213 			goto cleanup;
   3214 		}
   3215 
   3216 		json_object_object_add(bindstats, "memory", memory);
   3217 	}
   3218 
   3219 	if ((flags & STATS_JSON_TRAFFIC) != 0) {
   3220 		traffic = json_object_new_object();
   3221 		CHECKMEM(traffic);
   3222 
   3223 		udpreq4 = json_object_new_object();
   3224 		CHECKMEM(udpreq4);
   3225 
   3226 		udpresp4 = json_object_new_object();
   3227 		CHECKMEM(udpresp4);
   3228 
   3229 		tcpreq4 = json_object_new_object();
   3230 		CHECKMEM(tcpreq4);
   3231 
   3232 		tcpresp4 = json_object_new_object();
   3233 		CHECKMEM(tcpresp4);
   3234 
   3235 		udpreq6 = json_object_new_object();
   3236 		CHECKMEM(udpreq6);
   3237 
   3238 		udpresp6 = json_object_new_object();
   3239 		CHECKMEM(udpresp6);
   3240 
   3241 		tcpreq6 = json_object_new_object();
   3242 		CHECKMEM(tcpreq6);
   3243 
   3244 		tcpresp6 = json_object_new_object();
   3245 		CHECKMEM(tcpresp6);
   3246 
   3247 		CHECK(dump_histo(server->sctx->udpinstats4,
   3248 				 isc_statsformat_json, udpreq4, NULL,
   3249 				 udpinsizestats_xmldesc, dns_sizecounter_in_max,
   3250 				 udpinsizestats_index, udpinsizestat_values,
   3251 				 0));
   3252 
   3253 		CHECK(dump_histo(server->sctx->udpoutstats4,
   3254 				 isc_statsformat_json, udpresp4, NULL,
   3255 				 udpoutsizestats_xmldesc,
   3256 				 dns_sizecounter_out_max, udpoutsizestats_index,
   3257 				 udpoutsizestat_values, 0));
   3258 
   3259 		CHECK(dump_histo(server->sctx->tcpinstats4,
   3260 				 isc_statsformat_json, tcpreq4, NULL,
   3261 				 tcpinsizestats_xmldesc, dns_sizecounter_in_max,
   3262 				 tcpinsizestats_index, tcpinsizestat_values,
   3263 				 0));
   3264 
   3265 		CHECK(dump_histo(server->sctx->tcpoutstats4,
   3266 				 isc_statsformat_json, tcpresp4, NULL,
   3267 				 tcpoutsizestats_xmldesc,
   3268 				 dns_sizecounter_out_max, tcpoutsizestats_index,
   3269 				 tcpoutsizestat_values, 0));
   3270 
   3271 		CHECK(dump_histo(server->sctx->udpinstats6,
   3272 				 isc_statsformat_json, udpreq6, NULL,
   3273 				 udpinsizestats_xmldesc, dns_sizecounter_in_max,
   3274 				 udpinsizestats_index, udpinsizestat_values,
   3275 				 0));
   3276 
   3277 		CHECK(dump_histo(server->sctx->udpoutstats6,
   3278 				 isc_statsformat_json, udpresp6, NULL,
   3279 				 udpoutsizestats_xmldesc,
   3280 				 dns_sizecounter_out_max, udpoutsizestats_index,
   3281 				 udpoutsizestat_values, 0));
   3282 
   3283 		CHECK(dump_histo(server->sctx->tcpinstats6,
   3284 				 isc_statsformat_json, tcpreq6, NULL,
   3285 				 tcpinsizestats_xmldesc, dns_sizecounter_in_max,
   3286 				 tcpinsizestats_index, tcpinsizestat_values,
   3287 				 0));
   3288 
   3289 		CHECK(dump_histo(server->sctx->tcpoutstats6,
   3290 				 isc_statsformat_json, tcpresp6, NULL,
   3291 				 tcpoutsizestats_xmldesc,
   3292 				 dns_sizecounter_out_max, tcpoutsizestats_index,
   3293 				 tcpoutsizestat_values, 0));
   3294 
   3295 		json_object_object_add(traffic,
   3296 				       "dns-udp-requests-sizes-received-ipv4",
   3297 				       udpreq4);
   3298 		json_object_object_add(
   3299 			traffic, "dns-udp-responses-sizes-sent-ipv4", udpresp4);
   3300 		json_object_object_add(traffic,
   3301 				       "dns-tcp-requests-sizes-received-ipv4",
   3302 				       tcpreq4);
   3303 		json_object_object_add(
   3304 			traffic, "dns-tcp-responses-sizes-sent-ipv4", tcpresp4);
   3305 		json_object_object_add(traffic,
   3306 				       "dns-udp-requests-sizes-received-ipv6",
   3307 				       udpreq6);
   3308 		json_object_object_add(
   3309 			traffic, "dns-udp-responses-sizes-sent-ipv6", udpresp6);
   3310 		json_object_object_add(traffic,
   3311 				       "dns-tcp-requests-sizes-received-ipv6",
   3312 				       tcpreq6);
   3313 		json_object_object_add(
   3314 			traffic, "dns-tcp-responses-sizes-sent-ipv6", tcpresp6);
   3315 		json_object_object_add(bindstats, "traffic", traffic);
   3316 		udpreq4 = NULL;
   3317 		udpresp4 = NULL;
   3318 		tcpreq4 = NULL;
   3319 		tcpresp4 = NULL;
   3320 		udpreq6 = NULL;
   3321 		udpresp6 = NULL;
   3322 		tcpreq6 = NULL;
   3323 		tcpresp6 = NULL;
   3324 		traffic = NULL;
   3325 	}
   3326 
   3327 	*msg = json_object_to_json_string_ext(bindstats,
   3328 					      JSON_C_TO_STRING_PRETTY);
   3329 	*msglen = strlen(*msg);
   3330 
   3331 	if (rootp != NULL) {
   3332 		*rootp = bindstats;
   3333 		bindstats = NULL;
   3334 	}
   3335 
   3336 	result = ISC_R_SUCCESS;
   3337 
   3338 cleanup:
   3339 	if (udpreq4 != NULL) {
   3340 		json_object_put(udpreq4);
   3341 	}
   3342 	if (udpresp4 != NULL) {
   3343 		json_object_put(udpresp4);
   3344 	}
   3345 	if (tcpreq4 != NULL) {
   3346 		json_object_put(tcpreq4);
   3347 	}
   3348 	if (tcpresp4 != NULL) {
   3349 		json_object_put(tcpresp4);
   3350 	}
   3351 	if (udpreq6 != NULL) {
   3352 		json_object_put(udpreq6);
   3353 	}
   3354 	if (udpresp6 != NULL) {
   3355 		json_object_put(udpresp6);
   3356 	}
   3357 	if (tcpreq6 != NULL) {
   3358 		json_object_put(tcpreq6);
   3359 	}
   3360 	if (tcpresp6 != NULL) {
   3361 		json_object_put(tcpresp6);
   3362 	}
   3363 	if (traffic != NULL) {
   3364 		json_object_put(traffic);
   3365 	}
   3366 	if (bindstats != NULL) {
   3367 		json_object_put(bindstats);
   3368 	}
   3369 
   3370 	return result;
   3371 }
   3372 
   3373 static isc_result_t
   3374 render_json(uint32_t flags, void *arg, unsigned int *retcode,
   3375 	    const char **retmsg, const char **mimetype, isc_buffer_t *b,
   3376 	    isc_httpdfree_t **freecb, void **freecb_args) {
   3377 	isc_result_t result;
   3378 	json_object *bindstats = NULL;
   3379 	named_server_t *server = arg;
   3380 	const char *msg = NULL;
   3381 	size_t msglen = 0;
   3382 	char *p;
   3383 
   3384 	result = generatejson(server, &msglen, &msg, &bindstats, flags);
   3385 	if (result == ISC_R_SUCCESS) {
   3386 		*retcode = 200;
   3387 		*retmsg = "OK";
   3388 		*mimetype = "application/json";
   3389 		p = UNCONST(msg);
   3390 		isc_buffer_reinit(b, p, msglen);
   3391 		isc_buffer_add(b, msglen);
   3392 		*freecb = wrap_jsonfree;
   3393 		*freecb_args = bindstats;
   3394 	} else {
   3395 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3396 			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
   3397 			      "failed at rendering JSON()");
   3398 	}
   3399 
   3400 	return result;
   3401 }
   3402 
   3403 static isc_result_t
   3404 render_json_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3405 		void *arg, unsigned int *retcode, const char **retmsg,
   3406 		const char **mimetype, isc_buffer_t *b,
   3407 		isc_httpdfree_t **freecb, void **freecb_args) {
   3408 	UNUSED(httpd);
   3409 	UNUSED(urlinfo);
   3410 	return render_json(STATS_JSON_ALL, arg, retcode, retmsg, mimetype, b,
   3411 			   freecb, freecb_args);
   3412 }
   3413 
   3414 static isc_result_t
   3415 render_json_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3416 		   void *arg, unsigned int *retcode, const char **retmsg,
   3417 		   const char **mimetype, isc_buffer_t *b,
   3418 		   isc_httpdfree_t **freecb, void **freecb_args) {
   3419 	UNUSED(httpd);
   3420 	UNUSED(urlinfo);
   3421 	return render_json(STATS_JSON_STATUS, arg, retcode, retmsg, mimetype, b,
   3422 			   freecb, freecb_args);
   3423 }
   3424 
   3425 static isc_result_t
   3426 render_json_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3427 		   void *arg, unsigned int *retcode, const char **retmsg,
   3428 		   const char **mimetype, isc_buffer_t *b,
   3429 		   isc_httpdfree_t **freecb, void **freecb_args) {
   3430 	UNUSED(httpd);
   3431 	UNUSED(urlinfo);
   3432 	return render_json(STATS_JSON_SERVER, arg, retcode, retmsg, mimetype, b,
   3433 			   freecb, freecb_args);
   3434 }
   3435 
   3436 static isc_result_t
   3437 render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3438 		  void *arg, unsigned int *retcode, const char **retmsg,
   3439 		  const char **mimetype, isc_buffer_t *b,
   3440 		  isc_httpdfree_t **freecb, void **freecb_args) {
   3441 	UNUSED(httpd);
   3442 	UNUSED(urlinfo);
   3443 	return render_json(STATS_JSON_ZONES, arg, retcode, retmsg, mimetype, b,
   3444 			   freecb, freecb_args);
   3445 }
   3446 
   3447 static isc_result_t
   3448 render_json_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3449 		   void *arg, unsigned int *retcode, const char **retmsg,
   3450 		   const char **mimetype, isc_buffer_t *b,
   3451 		   isc_httpdfree_t **freecb, void **freecb_args) {
   3452 	UNUSED(httpd);
   3453 	UNUSED(urlinfo);
   3454 	return render_json(STATS_JSON_XFRINS, arg, retcode, retmsg, mimetype, b,
   3455 			   freecb, freecb_args);
   3456 }
   3457 
   3458 static isc_result_t
   3459 render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3460 		void *arg, unsigned int *retcode, const char **retmsg,
   3461 		const char **mimetype, isc_buffer_t *b,
   3462 		isc_httpdfree_t **freecb, void **freecb_args) {
   3463 	UNUSED(httpd);
   3464 	UNUSED(urlinfo);
   3465 	return render_json(STATS_JSON_MEM, arg, retcode, retmsg, mimetype, b,
   3466 			   freecb, freecb_args);
   3467 }
   3468 
   3469 static isc_result_t
   3470 render_json_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3471 		void *arg, unsigned int *retcode, const char **retmsg,
   3472 		const char **mimetype, isc_buffer_t *b,
   3473 		isc_httpdfree_t **freecb, void **freecb_args) {
   3474 	UNUSED(httpd);
   3475 	UNUSED(urlinfo);
   3476 	return render_json(STATS_JSON_NET, arg, retcode, retmsg, mimetype, b,
   3477 			   freecb, freecb_args);
   3478 }
   3479 
   3480 static isc_result_t
   3481 render_json_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
   3482 		    void *arg, unsigned int *retcode, const char **retmsg,
   3483 		    const char **mimetype, isc_buffer_t *b,
   3484 		    isc_httpdfree_t **freecb, void **freecb_args) {
   3485 	UNUSED(httpd);
   3486 	UNUSED(urlinfo);
   3487 	return render_json(STATS_JSON_TRAFFIC, arg, retcode, retmsg, mimetype,
   3488 			   b, freecb, freecb_args);
   3489 }
   3490 
   3491 #endif /* HAVE_JSON_C */
   3492 
   3493 #if HAVE_LIBXML2
   3494 /*
   3495  * This is only needed if we have libxml2 and was confusingly returned if
   3496  * neither of libxml2 or json-c is configured.
   3497  */
   3498 static isc_result_t
   3499 render_xsl(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *args,
   3500 	   unsigned int *retcode, const char **retmsg, const char **mimetype,
   3501 	   isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
   3502 	isc_result_t result;
   3503 	char *p = NULL;
   3504 
   3505 	UNUSED(httpd);
   3506 	UNUSED(args);
   3507 
   3508 	*freecb = NULL;
   3509 	*freecb_args = NULL;
   3510 	*mimetype = "text/xslt+xml";
   3511 
   3512 	if (isc_httpdurl_isstatic(urlinfo)) {
   3513 		time_t t1, t2;
   3514 		const isc_time_t *when;
   3515 		const isc_time_t *loadtime;
   3516 
   3517 		when = isc_httpd_if_modified_since(httpd);
   3518 
   3519 		if (isc_time_isepoch(when)) {
   3520 			goto send;
   3521 		}
   3522 
   3523 		result = isc_time_secondsastimet(when, &t1);
   3524 		if (result != ISC_R_SUCCESS) {
   3525 			goto send;
   3526 		}
   3527 
   3528 		loadtime = isc_httpdurl_loadtime(urlinfo);
   3529 
   3530 		result = isc_time_secondsastimet(loadtime, &t2);
   3531 		if (result != ISC_R_SUCCESS) {
   3532 			goto send;
   3533 		}
   3534 
   3535 		if (t1 < t2) {
   3536 			goto send;
   3537 		}
   3538 
   3539 		*retcode = 304;
   3540 		*retmsg = "Not modified";
   3541 		goto end;
   3542 	}
   3543 
   3544 send:
   3545 	*retcode = 200;
   3546 	*retmsg = "OK";
   3547 	p = UNCONST(xslmsg);
   3548 	isc_buffer_reinit(b, p, strlen(xslmsg));
   3549 	isc_buffer_add(b, strlen(xslmsg));
   3550 end:
   3551 	return ISC_R_SUCCESS;
   3552 }
   3553 #endif
   3554 
   3555 static void
   3556 shutdown_listener(named_statschannel_t *listener) {
   3557 	char socktext[ISC_SOCKADDR_FORMATSIZE];
   3558 	isc_sockaddr_format(&listener->address, socktext, sizeof(socktext));
   3559 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3560 		      NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
   3561 		      "stopping statistics channel on %s", socktext);
   3562 
   3563 	isc_httpdmgr_shutdown(&listener->httpdmgr);
   3564 }
   3565 
   3566 #if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
   3567 static bool
   3568 client_ok(const isc_sockaddr_t *fromaddr, void *arg) {
   3569 	named_statschannel_t *listener = arg;
   3570 	dns_aclenv_t *env =
   3571 		ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
   3572 	isc_netaddr_t netaddr;
   3573 	char socktext[ISC_SOCKADDR_FORMATSIZE];
   3574 	int match;
   3575 
   3576 	REQUIRE(listener != NULL);
   3577 
   3578 	isc_netaddr_fromsockaddr(&netaddr, fromaddr);
   3579 
   3580 	LOCK(&listener->lock);
   3581 	if ((dns_acl_match(&netaddr, NULL, listener->acl, env, &match, NULL) ==
   3582 	     ISC_R_SUCCESS) &&
   3583 	    match > 0)
   3584 	{
   3585 		UNLOCK(&listener->lock);
   3586 		return true;
   3587 	}
   3588 	UNLOCK(&listener->lock);
   3589 
   3590 	isc_sockaddr_format(fromaddr, socktext, sizeof(socktext));
   3591 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3592 		      NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
   3593 		      "rejected statistics connection from %s", socktext);
   3594 
   3595 	return false;
   3596 }
   3597 #endif
   3598 
   3599 #if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
   3600 static void
   3601 destroy_listener(void *arg) {
   3602 	named_statschannel_t *listener = (named_statschannel_t *)arg;
   3603 
   3604 	REQUIRE(listener != NULL);
   3605 	REQUIRE(!ISC_LINK_LINKED(listener, link));
   3606 
   3607 	/* We don't have to acquire the lock here since it's already unlinked */
   3608 	dns_acl_detach(&listener->acl);
   3609 
   3610 	isc_mutex_destroy(&listener->lock);
   3611 	isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
   3612 }
   3613 #endif
   3614 
   3615 static isc_result_t
   3616 add_listener(named_server_t *server, named_statschannel_t **listenerp,
   3617 	     const cfg_obj_t *listen_params, const cfg_obj_t *config,
   3618 	     isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
   3619 	     const char *socktext) {
   3620 #if !defined(HAVE_LIBXML2) && !defined(HAVE_JSON_C)
   3621 	UNUSED(server);
   3622 	UNUSED(listenerp);
   3623 	UNUSED(listen_params);
   3624 	UNUSED(config);
   3625 	UNUSED(addr);
   3626 	UNUSED(aclconfctx);
   3627 	UNUSED(socktext);
   3628 
   3629 	return ISC_R_NOTIMPLEMENTED;
   3630 #else
   3631 	isc_result_t result;
   3632 	named_statschannel_t *listener = NULL;
   3633 	const cfg_obj_t *allow = NULL;
   3634 	dns_acl_t *new_acl = NULL;
   3635 	int pf;
   3636 
   3637 	listener = isc_mem_get(server->mctx, sizeof(*listener));
   3638 	*listener = (named_statschannel_t){ .address = *addr };
   3639 	ISC_LINK_INIT(listener, link);
   3640 	isc_mutex_init(&listener->lock);
   3641 	isc_mem_attach(server->mctx, &listener->mctx);
   3642 
   3643 	allow = cfg_tuple_get(listen_params, "allow");
   3644 	if (allow != NULL && cfg_obj_islist(allow)) {
   3645 		result = cfg_acl_fromconfig(allow, config, named_g_lctx,
   3646 					    aclconfctx, listener->mctx, 0,
   3647 					    &new_acl);
   3648 	} else {
   3649 		result = dns_acl_any(listener->mctx, &new_acl);
   3650 	}
   3651 	CHECK(result);
   3652 
   3653 	dns_acl_attach(new_acl, &listener->acl);
   3654 	dns_acl_detach(&new_acl);
   3655 
   3656 	pf = isc_sockaddr_pf(&listener->address);
   3657 	if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
   3658 	    (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
   3659 	{
   3660 		CHECK(ISC_R_FAMILYNOSUPPORT);
   3661 	}
   3662 
   3663 	CHECK(isc_httpdmgr_create(named_g_netmgr, server->mctx, addr, client_ok,
   3664 				  destroy_listener, listener,
   3665 				  &listener->httpdmgr));
   3666 
   3667 #ifdef HAVE_LIBXML2
   3668 	isc_httpdmgr_addurl(listener->httpdmgr, "/", false, render_xml_all,
   3669 			    server);
   3670 	isc_httpdmgr_addurl(listener->httpdmgr, "/xml", false, render_xml_all,
   3671 			    server);
   3672 	isc_httpdmgr_addurl(listener->httpdmgr,
   3673 			    "/xml/v" STATS_XML_VERSION_MAJOR, false,
   3674 			    render_xml_all, server);
   3675 	isc_httpdmgr_addurl(listener->httpdmgr,
   3676 			    "/xml/v" STATS_XML_VERSION_MAJOR "/status", false,
   3677 			    render_xml_status, server);
   3678 	isc_httpdmgr_addurl(listener->httpdmgr,
   3679 			    "/xml/v" STATS_XML_VERSION_MAJOR "/server", false,
   3680 			    render_xml_server, server);
   3681 	isc_httpdmgr_addurl(listener->httpdmgr,
   3682 			    "/xml/v" STATS_XML_VERSION_MAJOR "/zones", false,
   3683 			    render_xml_zones, server);
   3684 	isc_httpdmgr_addurl(listener->httpdmgr,
   3685 			    "/xml/v" STATS_XML_VERSION_MAJOR "/xfrins", false,
   3686 			    render_xml_xfrins, server);
   3687 	isc_httpdmgr_addurl(listener->httpdmgr,
   3688 			    "/xml/v" STATS_XML_VERSION_MAJOR "/net", false,
   3689 			    render_xml_net, server);
   3690 	isc_httpdmgr_addurl(listener->httpdmgr,
   3691 			    "/xml/v" STATS_XML_VERSION_MAJOR "/mem", false,
   3692 			    render_xml_mem, server);
   3693 	isc_httpdmgr_addurl(listener->httpdmgr,
   3694 			    "/xml/v" STATS_XML_VERSION_MAJOR "/traffic", false,
   3695 			    render_xml_traffic, server);
   3696 	isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", true, render_xsl,
   3697 			    server);
   3698 #endif /* ifdef HAVE_LIBXML2 */
   3699 #ifdef HAVE_JSON_C
   3700 	isc_httpdmgr_addurl(listener->httpdmgr, "/json", false, render_json_all,
   3701 			    server);
   3702 	isc_httpdmgr_addurl(listener->httpdmgr,
   3703 			    "/json/v" STATS_JSON_VERSION_MAJOR, false,
   3704 			    render_json_all, server);
   3705 	isc_httpdmgr_addurl(listener->httpdmgr,
   3706 			    "/json/v" STATS_JSON_VERSION_MAJOR "/status", false,
   3707 			    render_json_status, server);
   3708 	isc_httpdmgr_addurl(listener->httpdmgr,
   3709 			    "/json/v" STATS_JSON_VERSION_MAJOR "/server", false,
   3710 			    render_json_server, server);
   3711 	isc_httpdmgr_addurl(listener->httpdmgr,
   3712 			    "/json/v" STATS_JSON_VERSION_MAJOR "/zones", false,
   3713 			    render_json_zones, server);
   3714 	isc_httpdmgr_addurl(listener->httpdmgr,
   3715 			    "/json/v" STATS_JSON_VERSION_MAJOR "/xfrins", false,
   3716 			    render_json_xfrins, server);
   3717 	isc_httpdmgr_addurl(listener->httpdmgr,
   3718 			    "/json/v" STATS_JSON_VERSION_MAJOR "/net", false,
   3719 			    render_json_net, server);
   3720 	isc_httpdmgr_addurl(listener->httpdmgr,
   3721 			    "/json/v" STATS_JSON_VERSION_MAJOR "/mem", false,
   3722 			    render_json_mem, server);
   3723 	isc_httpdmgr_addurl(listener->httpdmgr,
   3724 			    "/json/v" STATS_JSON_VERSION_MAJOR "/traffic",
   3725 			    false, render_json_traffic, server);
   3726 #endif /* ifdef HAVE_JSON_C */
   3727 
   3728 	*listenerp = listener;
   3729 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3730 		      NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
   3731 		      "statistics channel listening on %s", socktext);
   3732 
   3733 	return ISC_R_SUCCESS;
   3734 
   3735 cleanup:
   3736 	if (listener->acl != NULL) {
   3737 		dns_acl_detach(&listener->acl);
   3738 	}
   3739 	isc_mutex_destroy(&listener->lock);
   3740 	isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
   3741 
   3742 	return result;
   3743 #endif
   3744 }
   3745 
   3746 static void
   3747 update_listener(named_server_t *server, named_statschannel_t **listenerp,
   3748 		const cfg_obj_t *listen_params, const cfg_obj_t *config,
   3749 		isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
   3750 		const char *socktext) {
   3751 	named_statschannel_t *listener;
   3752 	const cfg_obj_t *allow = NULL;
   3753 	dns_acl_t *new_acl = NULL;
   3754 	isc_result_t result = ISC_R_SUCCESS;
   3755 
   3756 	for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL;
   3757 	     listener = ISC_LIST_NEXT(listener, link))
   3758 	{
   3759 		if (isc_sockaddr_equal(addr, &listener->address)) {
   3760 			break;
   3761 		}
   3762 	}
   3763 
   3764 	if (listener == NULL) {
   3765 		*listenerp = NULL;
   3766 		return;
   3767 	}
   3768 
   3769 	/*
   3770 	 * Now, keep the old access list unless a new one can be made.
   3771 	 */
   3772 	allow = cfg_tuple_get(listen_params, "allow");
   3773 	if (allow != NULL && cfg_obj_islist(allow)) {
   3774 		result = cfg_acl_fromconfig(allow, config, named_g_lctx,
   3775 					    aclconfctx, listener->mctx, 0,
   3776 					    &new_acl);
   3777 	} else {
   3778 		result = dns_acl_any(listener->mctx, &new_acl);
   3779 	}
   3780 
   3781 	if (result == ISC_R_SUCCESS) {
   3782 		LOCK(&listener->lock);
   3783 
   3784 		dns_acl_detach(&listener->acl);
   3785 		dns_acl_attach(new_acl, &listener->acl);
   3786 		dns_acl_detach(&new_acl);
   3787 
   3788 		UNLOCK(&listener->lock);
   3789 	} else {
   3790 		cfg_obj_log(listen_params, named_g_lctx, ISC_LOG_WARNING,
   3791 			    "couldn't install new acl for "
   3792 			    "statistics channel %s: %s",
   3793 			    socktext, isc_result_totext(result));
   3794 	}
   3795 
   3796 	*listenerp = listener;
   3797 }
   3798 
   3799 isc_result_t
   3800 named_statschannels_configure(named_server_t *server, const cfg_obj_t *config,
   3801 			      cfg_aclconfctx_t *aclconfctx) {
   3802 	named_statschannel_t *listener, *listener_next;
   3803 	named_statschannellist_t new_listeners;
   3804 	const cfg_obj_t *statschannellist = NULL;
   3805 	const cfg_listelt_t *element, *element2;
   3806 	char socktext[ISC_SOCKADDR_FORMATSIZE];
   3807 
   3808 	isc_once_do(&once, init_desc);
   3809 
   3810 	ISC_LIST_INIT(new_listeners);
   3811 
   3812 	/*
   3813 	 * Get the list of named.conf 'statistics-channels' statements.
   3814 	 */
   3815 	(void)cfg_map_get(config, "statistics-channels", &statschannellist);
   3816 
   3817 	/*
   3818 	 * Run through the new address/port list, noting sockets that are
   3819 	 * already being listened on and moving them to the new list.
   3820 	 *
   3821 	 * Identifying duplicate addr/port combinations is left to either
   3822 	 * the underlying config code, or to the bind attempt getting an
   3823 	 * address-in-use error.
   3824 	 */
   3825 	if (statschannellist != NULL) {
   3826 #ifndef EXTENDED_STATS
   3827 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3828 			      NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
   3829 			      "statistics-channels specified but not effective "
   3830 			      "due to missing XML and/or JSON library");
   3831 #else /* EXTENDED_STATS */
   3832 #ifndef HAVE_LIBXML2
   3833 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3834 			      NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
   3835 			      "statistics-channels: XML library missing, "
   3836 			      "only JSON stats will be available");
   3837 #endif /* !HAVE_LIBXML2 */
   3838 #ifndef HAVE_JSON_C
   3839 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
   3840 			      NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
   3841 			      "statistics-channels: JSON library missing, "
   3842 			      "only XML stats will be available");
   3843 #endif /* !HAVE_JSON_C */
   3844 #endif /* EXTENDED_STATS */
   3845 
   3846 		for (element = cfg_list_first(statschannellist);
   3847 		     element != NULL; element = cfg_list_next(element))
   3848 		{
   3849 			const cfg_obj_t *statschannel;
   3850 			const cfg_obj_t *listenercfg = NULL;
   3851 
   3852 			statschannel = cfg_listelt_value(element);
   3853 			(void)cfg_map_get(statschannel, "inet", &listenercfg);
   3854 			if (listenercfg == NULL) {
   3855 				continue;
   3856 			}
   3857 
   3858 			for (element2 = cfg_list_first(listenercfg);
   3859 			     element2 != NULL;
   3860 			     element2 = cfg_list_next(element2))
   3861 			{
   3862 				const cfg_obj_t *listen_params;
   3863 				const cfg_obj_t *obj;
   3864 				isc_sockaddr_t addr;
   3865 
   3866 				listen_params = cfg_listelt_value(element2);
   3867 
   3868 				obj = cfg_tuple_get(listen_params, "address");
   3869 				addr = *cfg_obj_assockaddr(obj);
   3870 				if (isc_sockaddr_getport(&addr) == 0) {
   3871 					isc_sockaddr_setport(
   3872 						&addr,
   3873 						NAMED_STATSCHANNEL_HTTPPORT);
   3874 				}
   3875 
   3876 				isc_sockaddr_format(&addr, socktext,
   3877 						    sizeof(socktext));
   3878 
   3879 				isc_log_write(named_g_lctx,
   3880 					      NAMED_LOGCATEGORY_GENERAL,
   3881 					      NAMED_LOGMODULE_SERVER,
   3882 					      ISC_LOG_DEBUG(9),
   3883 					      "processing statistics "
   3884 					      "channel %s",
   3885 					      socktext);
   3886 
   3887 				update_listener(server, &listener,
   3888 						listen_params, config, &addr,
   3889 						aclconfctx, socktext);
   3890 
   3891 				if (listener != NULL) {
   3892 					/*
   3893 					 * Remove the listener from the old
   3894 					 * list, so it won't be shut down.
   3895 					 */
   3896 					ISC_LIST_UNLINK(server->statschannels,
   3897 							listener, link);
   3898 				} else {
   3899 					/*
   3900 					 * This is a new listener.
   3901 					 */
   3902 					isc_result_t r;
   3903 
   3904 					r = add_listener(server, &listener,
   3905 							 listen_params, config,
   3906 							 &addr, aclconfctx,
   3907 							 socktext);
   3908 					if (r != ISC_R_SUCCESS) {
   3909 						cfg_obj_log(
   3910 							listen_params,
   3911 							named_g_lctx,
   3912 							ISC_LOG_WARNING,
   3913 							"couldn't allocate "
   3914 							"statistics channel"
   3915 							" %s: %s",
   3916 							socktext,
   3917 							isc_result_totext(r));
   3918 					}
   3919 				}
   3920 
   3921 				if (listener != NULL) {
   3922 					ISC_LIST_APPEND(new_listeners, listener,
   3923 							link);
   3924 				}
   3925 			}
   3926 		}
   3927 	}
   3928 
   3929 	for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL;
   3930 	     listener = listener_next)
   3931 	{
   3932 		listener_next = ISC_LIST_NEXT(listener, link);
   3933 		ISC_LIST_UNLINK(server->statschannels, listener, link);
   3934 		shutdown_listener(listener);
   3935 	}
   3936 
   3937 	ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
   3938 	return ISC_R_SUCCESS;
   3939 }
   3940 
   3941 void
   3942 named_statschannels_shutdown(named_server_t *server) {
   3943 	named_statschannel_t *listener;
   3944 
   3945 	while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) {
   3946 		ISC_LIST_UNLINK(server->statschannels, listener, link);
   3947 		shutdown_listener(listener);
   3948 	}
   3949 }
   3950 
   3951 isc_result_t
   3952 named_stats_dump(named_server_t *server, FILE *fp) {
   3953 	isc_result_t result;
   3954 	dns_view_t *view;
   3955 	dns_zone_t *zone, *next;
   3956 	stats_dumparg_t dumparg;
   3957 	uint64_t nsstat_values[ns_statscounter_max];
   3958 	uint64_t resstat_values[dns_resstatscounter_max];
   3959 	uint64_t adbstat_values[dns_adbstats_max];
   3960 	uint64_t zonestat_values[dns_zonestatscounter_max];
   3961 	uint64_t sockstat_values[isc_sockstatscounter_max];
   3962 	uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
   3963 	isc_stdtime_t now = isc_stdtime_now();
   3964 
   3965 	isc_once_do(&once, init_desc);
   3966 
   3967 	/* Set common fields */
   3968 	dumparg.type = isc_statsformat_file;
   3969 	dumparg.arg = fp;
   3970 
   3971 	fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
   3972 
   3973 	fprintf(fp, "++ Incoming Requests ++\n");
   3974 	dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
   3975 			     &dumparg, 0);
   3976 
   3977 	fprintf(fp, "++ Incoming Queries ++\n");
   3978 	dns_rdatatypestats_dump(server->sctx->rcvquerystats, rdtypestat_dump,
   3979 				&dumparg, 0);
   3980 
   3981 	fprintf(fp, "++ Outgoing Rcodes ++\n");
   3982 	dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump, &dumparg,
   3983 			    0);
   3984 
   3985 	fprintf(fp, "++ Outgoing Queries ++\n");
   3986 	for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
   3987 	     view = ISC_LIST_NEXT(view, link))
   3988 	{
   3989 		dns_stats_t *dstats = NULL;
   3990 		dns_resolver_getquerystats(view->resolver, &dstats);
   3991 		if (dstats == NULL) {
   3992 			continue;
   3993 		}
   3994 		if (strcmp(view->name, "_default") == 0) {
   3995 			fprintf(fp, "[View: default]\n");
   3996 		} else {
   3997 			fprintf(fp, "[View: %s]\n", view->name);
   3998 		}
   3999 		dns_rdatatypestats_dump(dstats, rdtypestat_dump, &dumparg, 0);
   4000 		dns_stats_detach(&dstats);
   4001 	}
   4002 
   4003 	fprintf(fp, "++ Name Server Statistics ++\n");
   4004 	(void)dump_stats(ns_stats_get(server->sctx->nsstats),
   4005 			 isc_statsformat_file, fp, NULL, nsstats_desc,
   4006 			 ns_statscounter_max, nsstats_index, nsstat_values, 0);
   4007 
   4008 	fprintf(fp, "++ Zone Maintenance Statistics ++\n");
   4009 	(void)dump_stats(server->zonestats, isc_statsformat_file, fp, NULL,
   4010 			 zonestats_desc, dns_zonestatscounter_max,
   4011 			 zonestats_index, zonestat_values, 0);
   4012 
   4013 	fprintf(fp, "++ Resolver Statistics ++\n");
   4014 	fprintf(fp, "[Common]\n");
   4015 	(void)dump_stats(server->resolverstats, isc_statsformat_file, fp, NULL,
   4016 			 resstats_desc, dns_resstatscounter_max, resstats_index,
   4017 			 resstat_values, 0);
   4018 	for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
   4019 	     view = ISC_LIST_NEXT(view, link))
   4020 	{
   4021 		isc_stats_t *istats = NULL;
   4022 		dns_resolver_getstats(view->resolver, &istats);
   4023 		if (istats == NULL) {
   4024 			continue;
   4025 		}
   4026 		if (strcmp(view->name, "_default") == 0) {
   4027 			fprintf(fp, "[View: default]\n");
   4028 		} else {
   4029 			fprintf(fp, "[View: %s]\n", view->name);
   4030 		}
   4031 		(void)dump_stats(istats, isc_statsformat_file, fp, NULL,
   4032 				 resstats_desc, dns_resstatscounter_max,
   4033 				 resstats_index, resstat_values, 0);
   4034 		isc_stats_detach(&istats);
   4035 	}
   4036 
   4037 	fprintf(fp, "++ Cache Statistics ++\n");
   4038 	for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
   4039 	     view = ISC_LIST_NEXT(view, link))
   4040 	{
   4041 		if (strcmp(view->name, "_default") == 0) {
   4042 			fprintf(fp, "[View: default]\n");
   4043 		} else {
   4044 			fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
   4045 				dns_cache_getname(view->cache));
   4046 		}
   4047 		/*
   4048 		 * Avoid dumping redundant statistics when the cache is shared.
   4049 		 */
   4050 		if (dns_view_iscacheshared(view)) {
   4051 			continue;
   4052 		}
   4053 		dns_cache_dumpstats(view->cache, fp);
   4054 	}
   4055 
   4056 	fprintf(fp, "++ Cache DB RRsets ++\n");
   4057 	for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
   4058 	     view = ISC_LIST_NEXT(view, link))
   4059 	{
   4060 		dns_stats_t *cacherrstats;
   4061 
   4062 		cacherrstats = dns_db_getrrsetstats(view->cachedb);
   4063 		if (cacherrstats == NULL) {
   4064 			continue;
   4065 		}
   4066 		if (strcmp(view->name, "_default") == 0) {
   4067 			fprintf(fp, "[View: default]\n");
   4068 		} else {
   4069 			fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
   4070 				dns_cache_getname(view->cache));
   4071 		}
   4072 		if (dns_view_iscacheshared(view)) {
   4073 			/*
   4074 			 * Avoid dumping redundant statistics when the cache is
   4075 			 * shared.
   4076 			 */
   4077 			continue;
   4078 		}
   4079 		dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
   4080 				       &dumparg, 0);
   4081 	}
   4082 
   4083 	fprintf(fp, "++ ADB stats ++\n");
   4084 	for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
   4085 	     view = ISC_LIST_NEXT(view, link))
   4086 	{
   4087 		dns_adb_t *adb = NULL;
   4088 		isc_stats_t *adbstats = NULL;
   4089 
   4090 		dns_view_getadb(view, &adb);
   4091 		if (adb != NULL) {
   4092 			adbstats = dns_adb_getstats(adb);
   4093 			dns_adb_detach(&adb);
   4094 		}
   4095 		if (adbstats == NULL) {
   4096 			continue;
   4097 		}
   4098 		if (strcmp(view->name, "_default") == 0) {
   4099 			fprintf(fp, "[View: default]\n");
   4100 		} else {
   4101 			fprintf(fp, "[View: %s]\n", view->name);
   4102 		}
   4103 		(void)dump_stats(adbstats, isc_statsformat_file, fp, NULL,
   4104 				 adbstats_desc, dns_adbstats_max,
   4105 				 adbstats_index, adbstat_values, 0);
   4106 	}
   4107 
   4108 	fprintf(fp, "++ Socket I/O Statistics ++\n");
   4109 	(void)dump_stats(server->sockstats, isc_statsformat_file, fp, NULL,
   4110 			 sockstats_desc, isc_sockstatscounter_max,
   4111 			 sockstats_index, sockstat_values, 0);
   4112 
   4113 	fprintf(fp, "++ Per Zone Query Statistics ++\n");
   4114 	zone = NULL;
   4115 	for (result = dns_zone_first(server->zonemgr, &zone);
   4116 	     result == ISC_R_SUCCESS;
   4117 	     next = NULL, result = dns_zone_next(zone, &next), zone = next)
   4118 	{
   4119 		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
   4120 		if (zonestats != NULL) {
   4121 			char zonename[DNS_NAME_FORMATSIZE];
   4122 
   4123 			view = dns_zone_getview(zone);
   4124 			if (view == NULL) {
   4125 				continue;
   4126 			}
   4127 
   4128 			dns_name_format(dns_zone_getorigin(zone), zonename,
   4129 					sizeof(zonename));
   4130 			fprintf(fp, "[%s", zonename);
   4131 			if (strcmp(view->name, "_default") != 0) {
   4132 				fprintf(fp, " (view: %s)", view->name);
   4133 			}
   4134 			fprintf(fp, "]\n");
   4135 
   4136 			(void)dump_stats(zonestats, isc_statsformat_file, fp,
   4137 					 NULL, nsstats_desc,
   4138 					 ns_statscounter_max, nsstats_index,
   4139 					 nsstat_values, 0);
   4140 		}
   4141 	}
   4142 
   4143 	fprintf(fp, "++ Per Zone Glue Cache Statistics ++\n");
   4144 	zone = NULL;
   4145 	for (result = dns_zone_first(server->zonemgr, &zone);
   4146 	     result == ISC_R_SUCCESS;
   4147 	     next = NULL, result = dns_zone_next(zone, &next), zone = next)
   4148 	{
   4149 		isc_stats_t *gluecachestats = dns_zone_getgluecachestats(zone);
   4150 		if (gluecachestats != NULL) {
   4151 			char zonename[DNS_NAME_FORMATSIZE];
   4152 
   4153 			view = dns_zone_getview(zone);
   4154 			if (view == NULL) {
   4155 				continue;
   4156 			}
   4157 
   4158 			dns_name_format(dns_zone_getorigin(zone), zonename,
   4159 					sizeof(zonename));
   4160 			fprintf(fp, "[%s", zonename);
   4161 			if (strcmp(view->name, "_default") != 0) {
   4162 				fprintf(fp, " (view: %s)", view->name);
   4163 			}
   4164 			fprintf(fp, "]\n");
   4165 
   4166 			(void)dump_stats(gluecachestats, isc_statsformat_file,
   4167 					 fp, NULL, gluecachestats_desc,
   4168 					 dns_gluecachestatscounter_max,
   4169 					 gluecachestats_index,
   4170 					 gluecachestats_values, 0);
   4171 		}
   4172 	}
   4173 
   4174 	fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
   4175 
   4176 	return ISC_R_SUCCESS; /* this function currently always succeeds */
   4177 }
   4178