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