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