1130561Sobrien/*	$NetBSD: statschannel.c,v 1.5 2012/12/04 23:38:38 spz Exp $	*/
2130561Sobrien
3130561Sobrien/*
4130561Sobrien * Copyright (C) 2008-2012  Internet Systems Consortium, Inc. ("ISC")
5130561Sobrien *
6130561Sobrien * Permission to use, copy, modify, and/or distribute this software for any
7130561Sobrien * purpose with or without fee is hereby granted, provided that the above
8130561Sobrien * copyright notice and this permission notice appear in all copies.
9130561Sobrien *
10130561Sobrien * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11130561Sobrien * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12130561Sobrien * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13130561Sobrien * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14130561Sobrien * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15130561Sobrien * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16130561Sobrien * PERFORMANCE OF THIS SOFTWARE.
17130561Sobrien */
18130561Sobrien
19130561Sobrien/* Id: statschannel.c,v 1.28 2011/03/12 04:59:46 tbox Exp  */
20130561Sobrien
21130561Sobrien/*! \file */
22130561Sobrien
23130561Sobrien#include <config.h>
24130561Sobrien
25130561Sobrien#include <isc/buffer.h>
26130561Sobrien#include <isc/httpd.h>
27130561Sobrien#include <isc/mem.h>
28130561Sobrien#include <isc/once.h>
29130561Sobrien#include <isc/print.h>
30130561Sobrien#include <isc/socket.h>
31130561Sobrien#include <isc/stats.h>
32130561Sobrien#include <isc/task.h>
33130561Sobrien
34130561Sobrien#include <dns/cache.h>
35130561Sobrien#include <dns/db.h>
36130561Sobrien#include <dns/opcode.h>
37130561Sobrien#include <dns/resolver.h>
38130561Sobrien#include <dns/rdataclass.h>
39130561Sobrien#include <dns/rdatatype.h>
40130561Sobrien#include <dns/stats.h>
41130561Sobrien#include <dns/view.h>
42130561Sobrien#include <dns/zt.h>
43130561Sobrien
44130561Sobrien#include <named/log.h>
45130561Sobrien#include <named/server.h>
46130561Sobrien#include <named/statschannel.h>
47130561Sobrien
48130561Sobrien#include "bind9.xsl.h"
49130561Sobrien
50130561Sobrienstruct ns_statschannel {
51130561Sobrien	/* Unlocked */
52130561Sobrien	isc_httpdmgr_t				*httpdmgr;
53130561Sobrien	isc_sockaddr_t				address;
54130561Sobrien	isc_mem_t				*mctx;
55130561Sobrien
56130561Sobrien	/*
57130561Sobrien	 * Locked by channel lock: can be referenced and modified by both
58130561Sobrien	 * the server task and the channel task.
59130561Sobrien	 */
60130561Sobrien	isc_mutex_t				lock;
61130561Sobrien	dns_acl_t				*acl;
62130561Sobrien
63130561Sobrien	/* Locked by server task */
64130561Sobrien	ISC_LINK(struct ns_statschannel)	link;
65130561Sobrien};
66130561Sobrien
67130561Sobrientypedef enum { statsformat_file, statsformat_xml } statsformat_t;
68130561Sobrien
69130561Sobrientypedef struct
70130561Sobrienstats_dumparg {
71130561Sobrien	statsformat_t	type;
72130561Sobrien	void		*arg;		/* type dependent argument */
73130561Sobrien	int		ncounters;	/* used for general statistics */
74130561Sobrien	int		*counterindices; /* used for general statistics */
75130561Sobrien	isc_uint64_t	*countervalues;	 /* used for general statistics */
76130561Sobrien	isc_result_t	result;
77130561Sobrien} stats_dumparg_t;
78130561Sobrien
79130561Sobrienstatic isc_once_t once = ISC_ONCE_INIT;
80130561Sobrien
81130561Sobrien/*%
82130561Sobrien * Statistics descriptions.  These could be statistically initialized at
83130561Sobrien * compile time, but we configure them run time in the init_desc() function
84130561Sobrien * below so that they'll be less susceptible to counter name changes.
85130561Sobrien */
86130561Sobrienstatic const char *nsstats_desc[dns_nsstatscounter_max];
87130561Sobrienstatic const char *resstats_desc[dns_resstatscounter_max];
88130561Sobrienstatic const char *zonestats_desc[dns_zonestatscounter_max];
89130561Sobrienstatic const char *sockstats_desc[isc_sockstatscounter_max];
90130561Sobrienstatic const char *dnssecstats_desc[dns_dnssecstats_max];
91130561Sobrien#ifdef HAVE_LIBXML2
92130561Sobrienstatic const char *nsstats_xmldesc[dns_nsstatscounter_max];
93130561Sobrienstatic const char *resstats_xmldesc[dns_resstatscounter_max];
94130561Sobrienstatic const char *zonestats_xmldesc[dns_zonestatscounter_max];
95130561Sobrienstatic const char *sockstats_xmldesc[isc_sockstatscounter_max];
96130561Sobrienstatic const char *dnssecstats_xmldesc[dns_dnssecstats_max];
97130561Sobrien#else
98130561Sobrien#define nsstats_xmldesc NULL
99130561Sobrien#define resstats_xmldesc NULL
100130561Sobrien#define zonestats_xmldesc NULL
101130561Sobrien#define sockstats_xmldesc NULL
102130561Sobrien#define dnssecstats_xmldesc NULL
103130561Sobrien#endif	/* HAVE_LIBXML2 */
104130561Sobrien
105130561Sobrien#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
106130561Sobrien
107130561Sobrien/*%
108130561Sobrien * Mapping arrays to represent statistics counters in the order of our
109130561Sobrien * preference, regardless of the order of counter indices.  For example,
110130561Sobrien * nsstats_desc[nsstats_index[0]] will be the description that is shown first.
111130561Sobrien */
112130561Sobrienstatic int nsstats_index[dns_nsstatscounter_max];
113130561Sobrienstatic int resstats_index[dns_resstatscounter_max];
114130561Sobrienstatic int zonestats_index[dns_zonestatscounter_max];
115130561Sobrienstatic int sockstats_index[isc_sockstatscounter_max];
116130561Sobrienstatic int dnssecstats_index[dns_dnssecstats_max];
117130561Sobrien
118130561Sobrienstatic inline void
119130561Sobrienset_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
120130561Sobrien	 const char *xdesc, const char **xdescs)
121130561Sobrien{
122130561Sobrien	REQUIRE(counter < maxcounter);
123130561Sobrien	REQUIRE(fdescs[counter] == NULL);
124130561Sobrien#ifdef HAVE_LIBXML2
125130561Sobrien	REQUIRE(xdescs[counter] == NULL);
126130561Sobrien#endif
127130561Sobrien
128130561Sobrien	fdescs[counter] = fdesc;
129130561Sobrien#ifdef HAVE_LIBXML2
130130561Sobrien	xdescs[counter] = xdesc;
131130561Sobrien#else
132130561Sobrien	UNUSED(xdesc);
133130561Sobrien	UNUSED(xdescs);
134130561Sobrien#endif
135130561Sobrien}
136130561Sobrien
137130561Sobrienstatic void
138130561Sobrieninit_desc(void) {
139130561Sobrien	int i;
140130561Sobrien
141130561Sobrien	/* Initialize name server statistics */
142130561Sobrien	for (i = 0; i < dns_nsstatscounter_max; i++)
143130561Sobrien		nsstats_desc[i] = NULL;
144130561Sobrien#ifdef HAVE_LIBXML2
145130561Sobrien	for (i = 0; i < dns_nsstatscounter_max; i++)
146130561Sobrien		nsstats_xmldesc[i] = NULL;
147130561Sobrien#endif
148130561Sobrien
149130561Sobrien#define SET_NSSTATDESC(counterid, desc, xmldesc) \
150130561Sobrien	do { \
151130561Sobrien		set_desc(dns_nsstatscounter_ ## counterid, \
152130561Sobrien			 dns_nsstatscounter_max, \
153130561Sobrien			 desc, nsstats_desc, xmldesc, nsstats_xmldesc); \
154130561Sobrien		nsstats_index[i++] = dns_nsstatscounter_ ## counterid; \
155130561Sobrien	} while (/*CONSTCOND*/0)
156130561Sobrien
157130561Sobrien	i = 0;
158130561Sobrien	SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4");
159130561Sobrien	SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6");
160130561Sobrien	SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0");
161130561Sobrien	SET_NSSTATDESC(badednsver,
162130561Sobrien		       "requests with unsupported EDNS version received",
163130561Sobrien		       "ReqBadEDNSVer");
164130561Sobrien	SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG");
165130561Sobrien	SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
166130561Sobrien	SET_NSSTATDESC(invalidsig, "requests with invalid signature",
167130561Sobrien		       "ReqBadSIG");
168130561Sobrien	SET_NSSTATDESC(tcp, "TCP requests received", "ReqTCP");
169130561Sobrien	SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
170130561Sobrien	SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
171130561Sobrien	SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
172130561Sobrien	SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej");
173130561Sobrien	SET_NSSTATDESC(response, "responses sent", "Response");
174130561Sobrien	SET_NSSTATDESC(truncatedresp, "truncated responses sent",
175130561Sobrien		       "TruncatedResp");
176130561Sobrien	SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0");
177130561Sobrien	SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG");
178130561Sobrien	SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0");
179130561Sobrien	SET_NSSTATDESC(success, "queries resulted in successful answer",
180130561Sobrien		       "QrySuccess");
181130561Sobrien	SET_NSSTATDESC(authans, "queries resulted in authoritative answer",
182130561Sobrien		       "QryAuthAns");
183130561Sobrien	SET_NSSTATDESC(nonauthans,
184130561Sobrien		       "queries resulted in non authoritative answer",
185130561Sobrien		       "QryNoauthAns");
186130561Sobrien	SET_NSSTATDESC(referral, "queries resulted in referral answer",
187130561Sobrien		       "QryReferral");
188130561Sobrien	SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset");
189130561Sobrien	SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL");
190130561Sobrien	SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR");
191130561Sobrien	SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN");
192130561Sobrien	SET_NSSTATDESC(recursion, "queries caused recursion","QryRecursion");
193130561Sobrien	SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate");
194130561Sobrien	SET_NSSTATDESC(dropped, "queries dropped", "QryDropped");
195130561Sobrien	SET_NSSTATDESC(failure, "other query failures", "QryFailure");
196130561Sobrien	SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone");
197130561Sobrien	SET_NSSTATDESC(updatereqfwd, "update requests forwarded",
198130561Sobrien		       "UpdateReqFwd");
199130561Sobrien	SET_NSSTATDESC(updaterespfwd, "update responses forwarded",
200130561Sobrien		       "UpdateRespFwd");
201130561Sobrien	SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail");
202130561Sobrien	SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone");
203130561Sobrien	SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail");
204130561Sobrien	SET_NSSTATDESC(updatebadprereq,
205130561Sobrien		       "updates rejected due to prerequisite failure",
206130561Sobrien		       "UpdateBadPrereq");
207130561Sobrien	INSIST(i == dns_nsstatscounter_max);
208130561Sobrien
209130561Sobrien	/* Initialize resolver statistics */
210130561Sobrien	for (i = 0; i < dns_resstatscounter_max; i++)
211130561Sobrien		resstats_desc[i] = NULL;
212130561Sobrien#ifdef  HAVE_LIBXML2
213130561Sobrien	for (i = 0; i < dns_resstatscounter_max; i++)
214130561Sobrien		resstats_xmldesc[i] = NULL;
215130561Sobrien#endif
216130561Sobrien
217130561Sobrien#define SET_RESSTATDESC(counterid, desc, xmldesc) \
218130561Sobrien	do { \
219130561Sobrien		set_desc(dns_resstatscounter_ ## counterid, \
220130561Sobrien			 dns_resstatscounter_max, \
221130561Sobrien			 desc, resstats_desc, xmldesc, resstats_xmldesc); \
222130561Sobrien		resstats_index[i++] = dns_resstatscounter_ ## counterid; \
223130561Sobrien	} while (/*CONSTCOND*/0)
224130561Sobrien
225130561Sobrien	i = 0;
226130561Sobrien	SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4");
227130561Sobrien	SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6");
228130561Sobrien	SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4");
229130561Sobrien	SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6");
230130561Sobrien	SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN");
231130561Sobrien	SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL");
232130561Sobrien	SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR");
233130561Sobrien	SET_RESSTATDESC(othererror, "other errors received", "OtherError");
234130561Sobrien	SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail");
235130561Sobrien	SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch");
236130561Sobrien	SET_RESSTATDESC(truncated, "truncated responses received", "Truncated");
237130561Sobrien	SET_RESSTATDESC(lame, "lame delegations received", "Lame");
238130561Sobrien	SET_RESSTATDESC(retry, "query retries", "Retry");
239130561Sobrien	SET_RESSTATDESC(dispabort, "queries aborted due to quota",
240130561Sobrien			"QueryAbort");
241130561Sobrien	SET_RESSTATDESC(dispsockfail, "failures in opening query sockets",
242130561Sobrien			"QuerySockFail");
243130561Sobrien	SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout");
244130561Sobrien	SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4");
245130561Sobrien	SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6");
246130561Sobrien	SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed",
247130561Sobrien			"GlueFetchv4Fail");
248130561Sobrien	SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed",
249130561Sobrien			"GlueFetchv6Fail");
250130561Sobrien	SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt");
251130561Sobrien	SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk");
252130561Sobrien	SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded",
253130561Sobrien			"ValNegOk");
254130561Sobrien	SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail");
255130561Sobrien	SET_RESSTATDESC(queryrtt0, "queries with RTT < "
256130561Sobrien			DNS_RESOLVER_QRYRTTCLASS0STR "ms",
257130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
258130561Sobrien	SET_RESSTATDESC(queryrtt1, "queries with RTT "
259130561Sobrien			DNS_RESOLVER_QRYRTTCLASS0STR "-"
260130561Sobrien			DNS_RESOLVER_QRYRTTCLASS1STR "ms",
261130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
262130561Sobrien	SET_RESSTATDESC(queryrtt2, "queries with RTT "
263130561Sobrien			DNS_RESOLVER_QRYRTTCLASS1STR "-"
264130561Sobrien			DNS_RESOLVER_QRYRTTCLASS2STR "ms",
265130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
266130561Sobrien	SET_RESSTATDESC(queryrtt3, "queries with RTT "
267130561Sobrien			DNS_RESOLVER_QRYRTTCLASS2STR "-"
268130561Sobrien			DNS_RESOLVER_QRYRTTCLASS3STR "ms",
269130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
270130561Sobrien	SET_RESSTATDESC(queryrtt4, "queries with RTT "
271130561Sobrien			DNS_RESOLVER_QRYRTTCLASS3STR "-"
272130561Sobrien			DNS_RESOLVER_QRYRTTCLASS4STR "ms",
273130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
274130561Sobrien	SET_RESSTATDESC(queryrtt5, "queries with RTT > "
275130561Sobrien			DNS_RESOLVER_QRYRTTCLASS4STR "ms",
276130561Sobrien			"QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
277130561Sobrien	INSIST(i == dns_resstatscounter_max);
278130561Sobrien
279130561Sobrien	/* Initialize zone statistics */
280130561Sobrien	for (i = 0; i < dns_zonestatscounter_max; i++)
281130561Sobrien		zonestats_desc[i] = NULL;
282130561Sobrien#ifdef  HAVE_LIBXML2
283130561Sobrien	for (i = 0; i < dns_zonestatscounter_max; i++)
284130561Sobrien		zonestats_xmldesc[i] = NULL;
285130561Sobrien#endif
286130561Sobrien
287130561Sobrien#define SET_ZONESTATDESC(counterid, desc, xmldesc) \
288130561Sobrien	do { \
289130561Sobrien		set_desc(dns_zonestatscounter_ ## counterid, \
290130561Sobrien			 dns_zonestatscounter_max, \
291130561Sobrien			 desc, zonestats_desc, xmldesc, zonestats_xmldesc); \
292130561Sobrien		zonestats_index[i++] = dns_zonestatscounter_ ## counterid; \
293130561Sobrien	} while (/*CONSTCOND*/0)
294130561Sobrien
295130561Sobrien	i = 0;
296130561Sobrien	SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4");
297130561Sobrien	SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6");
298130561Sobrien	SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4");
299130561Sobrien	SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6");
300130561Sobrien	SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej");
301130561Sobrien	SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4");
302130561Sobrien	SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6");
303130561Sobrien	SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4");
304130561Sobrien	SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6");
305130561Sobrien	SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4");
306130561Sobrien	SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6");
307130561Sobrien	SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded","XfrSuccess");
308130561Sobrien	SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail");
309130561Sobrien	INSIST(i == dns_zonestatscounter_max);
310130561Sobrien
311130561Sobrien	/* Initialize socket statistics */
312130561Sobrien	for (i = 0; i < isc_sockstatscounter_max; i++)
313130561Sobrien		sockstats_desc[i] = NULL;
314130561Sobrien#ifdef  HAVE_LIBXML2
315130561Sobrien	for (i = 0; i < isc_sockstatscounter_max; i++)
316130561Sobrien		sockstats_xmldesc[i] = NULL;
317130561Sobrien#endif
318130561Sobrien
319130561Sobrien#define SET_SOCKSTATDESC(counterid, desc, xmldesc) \
320130561Sobrien	do { \
321130561Sobrien		set_desc(isc_sockstatscounter_ ## counterid, \
322130561Sobrien			 isc_sockstatscounter_max, \
323130561Sobrien			 desc, sockstats_desc, xmldesc, sockstats_xmldesc); \
324130561Sobrien		sockstats_index[i++] = isc_sockstatscounter_ ## counterid; \
325130561Sobrien	} while (/*CONSTCOND*/0)
326130561Sobrien
327130561Sobrien	i = 0;
328130561Sobrien	SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open");
329130561Sobrien	SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open");
330130561Sobrien	SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open");
331130561Sobrien	SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open");
332130561Sobrien	SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen");
333130561Sobrien	SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures",
334130561Sobrien			 "UDP4OpenFail");
335130561Sobrien	SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures",
336130561Sobrien			 "UDP6OpenFail");
337130561Sobrien	SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures",
338130561Sobrien			 "TCP4OpenFail");
339130561Sobrien	SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures",
340130561Sobrien			 "TCP6OpenFail");
341130561Sobrien	SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures",
342130561Sobrien			 "UnixOpenFail");
343130561Sobrien	SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close");
344130561Sobrien	SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close");
345130561Sobrien	SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close");
346130561Sobrien	SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close");
347130561Sobrien	SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose");
348130561Sobrien	SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed",
349130561Sobrien			 "FDWatchClose");
350130561Sobrien	SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures",
351130561Sobrien			 "UDP4BindFail");
352130561Sobrien	SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures",
353130561Sobrien			 "UDP6BindFail");
354130561Sobrien	SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures",
355130561Sobrien			 "TCP4BindFail");
356130561Sobrien	SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures",
357130561Sobrien			 "TCP6BindFail");
358130561Sobrien	SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures",
359130561Sobrien			 "UnixBindFail");
360130561Sobrien	SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures",
361130561Sobrien			 "FdwatchBindFail");
362130561Sobrien	SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures",
363130561Sobrien			 "UDP4ConnFail");
364130561Sobrien	SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures",
365130561Sobrien			 "UDP6ConnFail");
366130561Sobrien	SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures",
367130561Sobrien			 "TCP4ConnFail");
368130561Sobrien	SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures",
369130561Sobrien			 "TCP6ConnFail");
370130561Sobrien	SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures",
371130561Sobrien			 "UnixConnFail");
372130561Sobrien	SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures",
373130561Sobrien			 "FDwatchConnFail");
374130561Sobrien	SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established",
375130561Sobrien			 "UDP4Conn");
376130561Sobrien	SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established",
377130561Sobrien			 "UDP6Conn");
378130561Sobrien	SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established",
379130561Sobrien			 "TCP4Conn");
380130561Sobrien	SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established",
381130561Sobrien			 "TCP6Conn");
382130561Sobrien	SET_SOCKSTATDESC(unixconnect, "Unix domain connections established",
383130561Sobrien			 "UnixConn");
384130561Sobrien	SET_SOCKSTATDESC(fdwatchconnect,
385130561Sobrien			 "FDwatch domain connections established",
386130561Sobrien			 "FDwatchConn");
387130561Sobrien	SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures",
388130561Sobrien			 "TCP4AcceptFail");
389130561Sobrien	SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures",
390130561Sobrien			 "TCP6AcceptFail");
391130561Sobrien	SET_SOCKSTATDESC(unixacceptfail,
392130561Sobrien			 "Unix domain connection accept failures",
393130561Sobrien			 "UnixAcceptFail");
394130561Sobrien	SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted",
395130561Sobrien			 "TCP4Accept");
396130561Sobrien	SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted",
397130561Sobrien			 "TCP6Accept");
398130561Sobrien	SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted",
399130561Sobrien			 "UnixAccept");
400130561Sobrien	SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr");
401130561Sobrien	SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr");
402130561Sobrien	SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr");
403130561Sobrien	SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr");
404130561Sobrien	SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors",
405130561Sobrien			 "UnixSendErr");
406130561Sobrien	SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors",
407130561Sobrien			 "FDwatchSendErr");
408130561Sobrien	SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr");
409130561Sobrien	SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr");
410130561Sobrien	SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr");
411130561Sobrien	SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr");
412130561Sobrien	SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors",
413130561Sobrien			 "UnixRecvErr");
414130561Sobrien	SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors",
415130561Sobrien			 "FDwatchRecvErr");
416130561Sobrien	INSIST(i == isc_sockstatscounter_max);
417130561Sobrien
418130561Sobrien	/* Initialize DNSSEC statistics */
419130561Sobrien	for (i = 0; i < dns_dnssecstats_max; i++)
420130561Sobrien		dnssecstats_desc[i] = NULL;
421130561Sobrien#ifdef  HAVE_LIBXML2
422130561Sobrien	for (i = 0; i < dns_dnssecstats_max; i++)
423130561Sobrien		dnssecstats_xmldesc[i] = NULL;
424130561Sobrien#endif
425130561Sobrien
426130561Sobrien#define SET_DNSSECSTATDESC(counterid, desc, xmldesc) \
427130561Sobrien	do { \
428130561Sobrien		set_desc(dns_dnssecstats_ ## counterid, \
429130561Sobrien			 dns_dnssecstats_max, \
430130561Sobrien			 desc, dnssecstats_desc,\
431130561Sobrien			 xmldesc, dnssecstats_xmldesc); \
432130561Sobrien		dnssecstats_index[i++] = dns_dnssecstats_ ## counterid; \
433130561Sobrien	} while (/*CONSTCOND*/0)
434130561Sobrien
435130561Sobrien	i = 0;
436130561Sobrien	SET_DNSSECSTATDESC(asis, "dnssec validation success with signer "
437130561Sobrien			   "\"as is\"", "DNSSECasis");
438130561Sobrien	SET_DNSSECSTATDESC(downcase, "dnssec validation success with signer "
439130561Sobrien			   "lower cased", "DNSSECdowncase");
440130561Sobrien	SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature",
441130561Sobrien			   "DNSSECwild");
442130561Sobrien	SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail");
443130561Sobrien	INSIST(i == dns_dnssecstats_max);
444130561Sobrien
445130561Sobrien	/* Sanity check */
446130561Sobrien	for (i = 0; i < dns_nsstatscounter_max; i++)
447130561Sobrien		INSIST(nsstats_desc[i] != NULL);
448130561Sobrien	for (i = 0; i < dns_resstatscounter_max; i++)
449130561Sobrien		INSIST(resstats_desc[i] != NULL);
450130561Sobrien	for (i = 0; i < dns_zonestatscounter_max; i++)
451130561Sobrien		INSIST(zonestats_desc[i] != NULL);
452130561Sobrien	for (i = 0; i < isc_sockstatscounter_max; i++)
453130561Sobrien		INSIST(sockstats_desc[i] != NULL);
454130561Sobrien	for (i = 0; i < dns_dnssecstats_max; i++)
455130561Sobrien		INSIST(dnssecstats_desc[i] != NULL);
456130561Sobrien#ifdef  HAVE_LIBXML2
457130561Sobrien	for (i = 0; i < dns_nsstatscounter_max; i++)
458130561Sobrien		INSIST(nsstats_xmldesc[i] != NULL);
459130561Sobrien	for (i = 0; i < dns_resstatscounter_max; i++)
460130561Sobrien		INSIST(resstats_xmldesc[i] != NULL);
461130561Sobrien	for (i = 0; i < dns_zonestatscounter_max; i++)
462130561Sobrien		INSIST(zonestats_xmldesc[i] != NULL);
463130561Sobrien	for (i = 0; i < isc_sockstatscounter_max; i++)
464130561Sobrien		INSIST(sockstats_xmldesc[i] != NULL);
465130561Sobrien	for (i = 0; i < dns_dnssecstats_max; i++)
466130561Sobrien		INSIST(dnssecstats_xmldesc[i] != NULL);
467130561Sobrien#endif
468130561Sobrien}
469130561Sobrien
470130561Sobrien/*%
471130561Sobrien * Dump callback functions.
472130561Sobrien */
473130561Sobrienstatic void
474130561Sobriengeneralstat_dump(isc_statscounter_t counter, isc_uint64_t val, void *arg) {
475130561Sobrien	stats_dumparg_t *dumparg = arg;
476130561Sobrien
477130561Sobrien	REQUIRE(counter < dumparg->ncounters);
478130561Sobrien	dumparg->countervalues[counter] = val;
479130561Sobrien}
480130561Sobrien
481130561Sobrienstatic isc_result_t
482130561Sobriendump_counters(isc_stats_t *stats, statsformat_t type, void *arg,
483130561Sobrien	      const char *category, const char **desc, int ncounters,
484130561Sobrien	      int *indices, isc_uint64_t *values, int options)
485130561Sobrien{
486130561Sobrien	int i, index;
487130561Sobrien	isc_uint64_t value;
488130561Sobrien	stats_dumparg_t dumparg;
489130561Sobrien	FILE *fp;
490130561Sobrien#ifdef HAVE_LIBXML2
491130561Sobrien	xmlTextWriterPtr writer;
492130561Sobrien	int xmlrc;
493130561Sobrien#endif
494130561Sobrien
495130561Sobrien#ifndef HAVE_LIBXML2
496130561Sobrien	UNUSED(category);
497130561Sobrien#endif
498130561Sobrien
499130561Sobrien	dumparg.type = type;
500130561Sobrien	dumparg.ncounters = ncounters;
501130561Sobrien	dumparg.counterindices = indices;
502130561Sobrien	dumparg.countervalues = values;
503130561Sobrien
504130561Sobrien	memset(values, 0, sizeof(values[0]) * ncounters);
505130561Sobrien	isc_stats_dump(stats, generalstat_dump, &dumparg, options);
506130561Sobrien
507130561Sobrien	for (i = 0; i < ncounters; i++) {
508130561Sobrien		index = indices[i];
509130561Sobrien		value = values[index];
510130561Sobrien
511130561Sobrien		if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0)
512130561Sobrien			continue;
513130561Sobrien
514130561Sobrien		switch (dumparg.type) {
515130561Sobrien		case statsformat_file:
516130561Sobrien			fp = arg;
517130561Sobrien			fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
518130561Sobrien				value, desc[index]);
519130561Sobrien			break;
520130561Sobrien		case statsformat_xml:
521130561Sobrien#ifdef HAVE_LIBXML2
522130561Sobrien			writer = arg;
523130561Sobrien
524130561Sobrien			if (category != NULL) {
525130561Sobrien				TRY0(xmlTextWriterStartElement(writer,
526130561Sobrien							       ISC_XMLCHAR
527130561Sobrien							       category));
528130561Sobrien				TRY0(xmlTextWriterStartElement(writer,
529130561Sobrien							       ISC_XMLCHAR
530130561Sobrien							       "name"));
531130561Sobrien				TRY0(xmlTextWriterWriteString(writer,
532130561Sobrien							      ISC_XMLCHAR
533130561Sobrien							      desc[index]));
534130561Sobrien				TRY0(xmlTextWriterEndElement(writer)); /* name */
535130561Sobrien
536130561Sobrien				TRY0(xmlTextWriterStartElement(writer,
537130561Sobrien							       ISC_XMLCHAR
538130561Sobrien							       "counter"));
539130561Sobrien			} else {
540130561Sobrien				TRY0(xmlTextWriterStartElement(writer,
541130561Sobrien							       ISC_XMLCHAR
542130561Sobrien							       desc[index]));
543130561Sobrien			}
544130561Sobrien			TRY0(xmlTextWriterWriteFormatString(writer,
545130561Sobrien							    "%"
546130561Sobrien							    ISC_PRINT_QUADFORMAT
547130561Sobrien							    "u", value));
548130561Sobrien			TRY0(xmlTextWriterEndElement(writer)); /* counter */
549130561Sobrien			if (category != NULL)
550130561Sobrien				TRY0(xmlTextWriterEndElement(writer)); /* category */
551130561Sobrien#endif
552130561Sobrien			break;
553130561Sobrien		}
554130561Sobrien	}
555130561Sobrien	return (ISC_R_SUCCESS);
556130561Sobrien#ifdef HAVE_LIBXML2
557130561Sobrien error:
558130561Sobrien	return (ISC_R_FAILURE);
559130561Sobrien#endif
560130561Sobrien}
561130561Sobrien
562130561Sobrienstatic void
563130561Sobrienrdtypestat_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
564130561Sobrien	char typebuf[64];
565130561Sobrien	const char *typestr;
566130561Sobrien	stats_dumparg_t *dumparg = arg;
567130561Sobrien	FILE *fp;
568130561Sobrien#ifdef HAVE_LIBXML2
569130561Sobrien	xmlTextWriterPtr writer;
570130561Sobrien	int xmlrc;
571130561Sobrien#endif
572130561Sobrien
573130561Sobrien	if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_OTHERTYPE)
574130561Sobrien	    == 0) {
575130561Sobrien		dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
576130561Sobrien				     sizeof(typebuf));
577130561Sobrien		typestr = typebuf;
578130561Sobrien	} else
579130561Sobrien		typestr = "Others";
580130561Sobrien
581130561Sobrien	switch (dumparg->type) {
582130561Sobrien	case statsformat_file:
583130561Sobrien		fp = dumparg->arg;
584130561Sobrien		fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, typestr);
585130561Sobrien		break;
586130561Sobrien	case statsformat_xml:
587130561Sobrien#ifdef HAVE_LIBXML2
588130561Sobrien		writer = dumparg->arg;
589130561Sobrien
590130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdtype"));
591130561Sobrien
592130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
593130561Sobrien		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR typestr));
594130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* name */
595130561Sobrien
596130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
597130561Sobrien		TRY0(xmlTextWriterWriteFormatString(writer,
598130561Sobrien					       "%" ISC_PRINT_QUADFORMAT "u",
599130561Sobrien					       val));
600130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* counter */
601130561Sobrien
602130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* rdtype */
603130561Sobrien#endif
604130561Sobrien		break;
605130561Sobrien	}
606130561Sobrien	return;
607130561Sobrien#ifdef HAVE_LIBXML2
608130561Sobrien error:
609130561Sobrien	dumparg->result = ISC_R_FAILURE;
610130561Sobrien	return;
611130561Sobrien#endif
612130561Sobrien}
613130561Sobrien
614130561Sobrienstatic void
615130561Sobrienrdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
616130561Sobrien	stats_dumparg_t *dumparg = arg;
617130561Sobrien	FILE *fp;
618130561Sobrien	char typebuf[64];
619130561Sobrien	const char *typestr;
620130561Sobrien	isc_boolean_t nxrrset = ISC_FALSE;
621130561Sobrien#ifdef HAVE_LIBXML2
622130561Sobrien	xmlTextWriterPtr writer;
623130561Sobrien	int xmlrc;
624130561Sobrien#endif
625130561Sobrien
626130561Sobrien	if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXDOMAIN)
627130561Sobrien	    != 0) {
628130561Sobrien		typestr = "NXDOMAIN";
629130561Sobrien	} else if ((DNS_RDATASTATSTYPE_ATTR(type) &
630130561Sobrien		    DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0) {
631130561Sobrien		typestr = "Others";
632130561Sobrien	} else {
633130561Sobrien		dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
634130561Sobrien				     sizeof(typebuf));
635130561Sobrien		typestr = typebuf;
636130561Sobrien	}
637130561Sobrien
638130561Sobrien	if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXRRSET)
639130561Sobrien	    != 0)
640130561Sobrien		nxrrset = ISC_TRUE;
641130561Sobrien
642130561Sobrien	switch (dumparg->type) {
643130561Sobrien	case statsformat_file:
644130561Sobrien		fp = dumparg->arg;
645130561Sobrien		fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s%s\n", val,
646130561Sobrien			nxrrset ? "!" : "", typestr);
647130561Sobrien		break;
648130561Sobrien	case statsformat_xml:
649130561Sobrien#ifdef HAVE_LIBXML2
650130561Sobrien		writer = dumparg->arg;
651130561Sobrien
652130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
653130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
654130561Sobrien		TRY0(xmlTextWriterWriteFormatString(writer, "%s%s",
655130561Sobrien					       nxrrset ? "!" : "", typestr));
656130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* name */
657130561Sobrien
658130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
659130561Sobrien		TRY0(xmlTextWriterWriteFormatString(writer,
660130561Sobrien					       "%" ISC_PRINT_QUADFORMAT "u",
661130561Sobrien					       val));
662130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* counter */
663130561Sobrien
664130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* rrset */
665130561Sobrien#endif
666130561Sobrien		break;
667130561Sobrien	}
668130561Sobrien	return;
669130561Sobrien#ifdef HAVE_LIBXML2
670130561Sobrien error:
671130561Sobrien	dumparg->result = ISC_R_FAILURE;
672130561Sobrien#endif
673130561Sobrien
674130561Sobrien}
675130561Sobrien
676130561Sobrienstatic void
677130561Sobrienopcodestat_dump(dns_opcode_t code, isc_uint64_t val, void *arg) {
678130561Sobrien	FILE *fp;
679130561Sobrien	isc_buffer_t b;
680130561Sobrien	char codebuf[64];
681130561Sobrien	stats_dumparg_t *dumparg = arg;
682130561Sobrien#ifdef HAVE_LIBXML2
683130561Sobrien	xmlTextWriterPtr writer;
684130561Sobrien	int xmlrc;
685130561Sobrien#endif
686130561Sobrien
687130561Sobrien	isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
688130561Sobrien	dns_opcode_totext(code, &b);
689130561Sobrien	codebuf[isc_buffer_usedlength(&b)] = '\0';
690130561Sobrien
691130561Sobrien	switch (dumparg->type) {
692130561Sobrien	case statsformat_file:
693130561Sobrien		fp = dumparg->arg;
694130561Sobrien		fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, codebuf);
695130561Sobrien		break;
696130561Sobrien	case statsformat_xml:
697130561Sobrien#ifdef HAVE_LIBXML2
698130561Sobrien		writer = dumparg->arg;
699130561Sobrien
700130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "opcode"));
701130561Sobrien
702130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
703130561Sobrien		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR codebuf));
704130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* name */
705130561Sobrien
706130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
707130561Sobrien		TRY0(xmlTextWriterWriteFormatString(writer,
708130561Sobrien					       "%" ISC_PRINT_QUADFORMAT "u",
709130561Sobrien					       val));
710130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* counter */
711130561Sobrien
712130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* opcode */
713130561Sobrien#endif
714130561Sobrien		break;
715130561Sobrien	}
716130561Sobrien	return;
717130561Sobrien
718130561Sobrien#ifdef HAVE_LIBXML2
719130561Sobrien error:
720130561Sobrien	dumparg->result = ISC_R_FAILURE;
721130561Sobrien	return;
722130561Sobrien#endif
723130561Sobrien}
724130561Sobrien
725130561Sobrien#ifdef HAVE_LIBXML2
726130561Sobrien
727130561Sobrien/* XXXMLG below here sucks. */
728130561Sobrien
729130561Sobrien
730130561Sobrienstatic isc_result_t
731130561Sobrienzone_xmlrender(dns_zone_t *zone, void *arg) {
732130561Sobrien	char buf[1024 + 32];	/* sufficiently large for zone name and class */
733130561Sobrien	dns_rdataclass_t rdclass;
734130561Sobrien	isc_uint32_t serial;
735130561Sobrien	xmlTextWriterPtr writer = arg;
736130561Sobrien	isc_stats_t *zonestats;
737130561Sobrien	isc_uint64_t nsstat_values[dns_nsstatscounter_max];
738130561Sobrien	int xmlrc;
739130561Sobrien	isc_result_t result;
740130561Sobrien
741130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone"));
742130561Sobrien
743130561Sobrien	dns_zone_name(zone, buf, sizeof(buf));
744130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
745130561Sobrien	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
746130561Sobrien	TRY0(xmlTextWriterEndElement(writer));
747130561Sobrien
748130561Sobrien	rdclass = dns_zone_getclass(zone);
749130561Sobrien	dns_rdataclass_format(rdclass, buf, sizeof(buf));
750130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdataclass"));
751130561Sobrien	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
752130561Sobrien	TRY0(xmlTextWriterEndElement(writer));
753130561Sobrien
754130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
755130561Sobrien	if (dns_zone_getserial2(zone, &serial) == ISC_R_SUCCESS)
756130561Sobrien		TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
757130561Sobrien	else
758130561Sobrien		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
759130561Sobrien	TRY0(xmlTextWriterEndElement(writer));
760130561Sobrien
761130561Sobrien	zonestats = dns_zone_getrequeststats(zone);
762130561Sobrien	if (zonestats != NULL) {
763130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
764130561Sobrien		result = dump_counters(zonestats, statsformat_xml, writer, NULL,
765130561Sobrien				      nsstats_xmldesc, dns_nsstatscounter_max,
766130561Sobrien				      nsstats_index, nsstat_values,
767130561Sobrien				      ISC_STATSDUMP_VERBOSE);
768130561Sobrien		if (result != ISC_R_SUCCESS)
769130561Sobrien			goto error;
770130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* counters */
771130561Sobrien	}
772130561Sobrien
773130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* zone */
774130561Sobrien
775130561Sobrien	return (ISC_R_SUCCESS);
776130561Sobrien error:
777130561Sobrien	return (ISC_R_FAILURE);
778130561Sobrien}
779130561Sobrien
780130561Sobrienstatic isc_result_t
781130561Sobriengeneratexml(ns_server_t *server, int *buflen, xmlChar **buf) {
782130561Sobrien	char boottime[sizeof "yyyy-mm-ddThh:mm:ssZ"];
783130561Sobrien	char nowstr[sizeof "yyyy-mm-ddThh:mm:ssZ"];
784130561Sobrien	isc_time_t now;
785130561Sobrien	xmlTextWriterPtr writer = NULL;
786130561Sobrien	xmlDocPtr doc = NULL;
787130561Sobrien	int xmlrc;
788130561Sobrien	dns_view_t *view;
789130561Sobrien	stats_dumparg_t dumparg;
790130561Sobrien	dns_stats_t *cachestats;
791130561Sobrien	isc_uint64_t nsstat_values[dns_nsstatscounter_max];
792130561Sobrien	isc_uint64_t resstat_values[dns_resstatscounter_max];
793130561Sobrien	isc_uint64_t zonestat_values[dns_zonestatscounter_max];
794130561Sobrien	isc_uint64_t sockstat_values[isc_sockstatscounter_max];
795130561Sobrien	isc_result_t result;
796130561Sobrien
797130561Sobrien	isc_time_now(&now);
798130561Sobrien	isc_time_formatISO8601(&ns_g_boottime, boottime, sizeof boottime);
799130561Sobrien	isc_time_formatISO8601(&now, nowstr, sizeof nowstr);
800130561Sobrien
801130561Sobrien	writer = xmlNewTextWriterDoc(&doc, 0);
802130561Sobrien	if (writer == NULL)
803130561Sobrien		goto error;
804130561Sobrien	TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL));
805130561Sobrien	TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet",
806130561Sobrien			ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.xsl\""));
807130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "isc"));
808130561Sobrien	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
809130561Sobrien					 ISC_XMLCHAR "1.0"));
810130561Sobrien
811130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind"));
812130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
813130561Sobrien	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
814130561Sobrien					 ISC_XMLCHAR "2.2"));
815130561Sobrien
816130561Sobrien	/* Set common fields for statistics dump */
817130561Sobrien	dumparg.type = statsformat_xml;
818130561Sobrien	dumparg.arg = writer;
819130561Sobrien
820130561Sobrien	/*
821130561Sobrien	 * Start by rendering the views we know of here.  For each view we
822130561Sobrien	 * know of, call its rendering function.
823130561Sobrien	 */
824130561Sobrien	view = ISC_LIST_HEAD(server->viewlist);
825130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
826130561Sobrien	while (view != NULL) {
827130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view"));
828130561Sobrien
829130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
830130561Sobrien		TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR view->name));
831130561Sobrien		TRY0(xmlTextWriterEndElement(writer));
832130561Sobrien
833130561Sobrien		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zones"));
834130561Sobrien		result = dns_zt_apply(view->zonetable, ISC_TRUE, zone_xmlrender,
835130561Sobrien				      writer);
836130561Sobrien		if (result != ISC_R_SUCCESS)
837130561Sobrien			goto error;
838130561Sobrien		TRY0(xmlTextWriterEndElement(writer));
839130561Sobrien
840130561Sobrien		if (view->resquerystats != NULL) {
841130561Sobrien			dumparg.result = ISC_R_SUCCESS;
842130561Sobrien			dns_rdatatypestats_dump(view->resquerystats,
843130561Sobrien						rdtypestat_dump, &dumparg, 0);
844130561Sobrien			if (dumparg.result != ISC_R_SUCCESS)
845130561Sobrien				goto error;
846130561Sobrien		}
847130561Sobrien
848130561Sobrien		if (view->resstats != NULL) {
849130561Sobrien			result = dump_counters(view->resstats, statsformat_xml,
850130561Sobrien					       writer, "resstat",
851130561Sobrien					       resstats_xmldesc,
852130561Sobrien					       dns_resstatscounter_max,
853130561Sobrien					       resstats_index, resstat_values,
854130561Sobrien					       ISC_STATSDUMP_VERBOSE);
855130561Sobrien			if (result != ISC_R_SUCCESS)
856130561Sobrien				goto error;
857130561Sobrien		}
858130561Sobrien
859130561Sobrien		cachestats = dns_db_getrrsetstats(view->cachedb);
860130561Sobrien		if (cachestats != NULL) {
861130561Sobrien			TRY0(xmlTextWriterStartElement(writer,
862130561Sobrien						       ISC_XMLCHAR "cache"));
863130561Sobrien			TRY0(xmlTextWriterWriteAttribute(writer,
864130561Sobrien					 ISC_XMLCHAR "name",
865130561Sobrien					 ISC_XMLCHAR
866130561Sobrien					 dns_cache_getname(view->cache)));
867130561Sobrien			dumparg.result = ISC_R_SUCCESS;
868130561Sobrien			dns_rdatasetstats_dump(cachestats, rdatasetstats_dump,
869130561Sobrien					       &dumparg, 0);
870130561Sobrien			if (dumparg.result != ISC_R_SUCCESS)
871130561Sobrien				goto error;
872130561Sobrien			TRY0(xmlTextWriterEndElement(writer)); /* cache */
873130561Sobrien		}
874130561Sobrien
875130561Sobrien		TRY0(xmlTextWriterEndElement(writer)); /* view */
876130561Sobrien
877130561Sobrien		view = ISC_LIST_NEXT(view, link);
878130561Sobrien	}
879130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* views */
880130561Sobrien
881130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "socketmgr"));
882130561Sobrien	isc_socketmgr_renderxml(ns_g_socketmgr, writer);
883130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* socketmgr */
884130561Sobrien
885130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr"));
886130561Sobrien	isc_taskmgr_renderxml(ns_g_taskmgr, writer);
887130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* taskmgr */
888130561Sobrien
889130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
890130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time"));
891130561Sobrien	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime));
892130561Sobrien	TRY0(xmlTextWriterEndElement(writer));
893130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time"));
894130561Sobrien	TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr));
895130561Sobrien	TRY0(xmlTextWriterEndElement(writer));
896130561Sobrien
897130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "requests"));
898130561Sobrien	dumparg.result = ISC_R_SUCCESS;
899130561Sobrien	dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg,
900130561Sobrien			     0);
901130561Sobrien	if (dumparg.result != ISC_R_SUCCESS)
902130561Sobrien		goto error;
903130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* requests */
904130561Sobrien
905130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "queries-in"));
906130561Sobrien	dumparg.result = ISC_R_SUCCESS;
907130561Sobrien	dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
908130561Sobrien				&dumparg, 0);
909130561Sobrien	if (dumparg.result != ISC_R_SUCCESS)
910130561Sobrien		goto error;
911130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* queries-in */
912130561Sobrien
913130561Sobrien	result = dump_counters(server->nsstats, statsformat_xml, writer,
914130561Sobrien			       "nsstat", nsstats_xmldesc,
915130561Sobrien				dns_nsstatscounter_max,
916130561Sobrien				nsstats_index, nsstat_values,
917130561Sobrien				ISC_STATSDUMP_VERBOSE);
918130561Sobrien	if (result != ISC_R_SUCCESS)
919130561Sobrien		goto error;
920130561Sobrien
921130561Sobrien	result = dump_counters(server->zonestats, statsformat_xml, writer,
922130561Sobrien			       "zonestat", zonestats_xmldesc,
923130561Sobrien			       dns_zonestatscounter_max, zonestats_index,
924130561Sobrien			       zonestat_values, ISC_STATSDUMP_VERBOSE);
925130561Sobrien	if (result != ISC_R_SUCCESS)
926130561Sobrien		goto error;
927130561Sobrien
928130561Sobrien	/*
929130561Sobrien	 * Most of the common resolver statistics entries are 0, so we don't
930130561Sobrien	 * use the verbose dump here.
931130561Sobrien	 */
932130561Sobrien	result = dump_counters(server->resolverstats, statsformat_xml, writer,
933130561Sobrien			       "resstat", resstats_xmldesc,
934130561Sobrien			       dns_resstatscounter_max, resstats_index,
935130561Sobrien			       resstat_values, 0);
936130561Sobrien	if (result != ISC_R_SUCCESS)
937130561Sobrien		goto error;
938130561Sobrien
939130561Sobrien	result = dump_counters(server->sockstats, statsformat_xml, writer,
940130561Sobrien			       "sockstat", sockstats_xmldesc,
941130561Sobrien			       isc_sockstatscounter_max, sockstats_index,
942130561Sobrien			       sockstat_values, ISC_STATSDUMP_VERBOSE);
943130561Sobrien	if (result != ISC_R_SUCCESS)
944130561Sobrien		goto error;
945130561Sobrien
946130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* server */
947130561Sobrien
948130561Sobrien	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
949130561Sobrien	isc_mem_renderxml(writer);
950130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* memory */
951130561Sobrien
952130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* statistics */
953130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* bind */
954130561Sobrien	TRY0(xmlTextWriterEndElement(writer)); /* isc */
955130561Sobrien
956130561Sobrien	TRY0(xmlTextWriterEndDocument(writer));
957130561Sobrien
958130561Sobrien	xmlFreeTextWriter(writer);
959130561Sobrien
960130561Sobrien	xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 1);
961130561Sobrien	xmlFreeDoc(doc);
962130561Sobrien	return (ISC_R_SUCCESS);
963130561Sobrien
964130561Sobrien error:
965130561Sobrien	if (writer != NULL)
966130561Sobrien		xmlFreeTextWriter(writer);
967130561Sobrien	if (doc != NULL)
968130561Sobrien		xmlFreeDoc(doc);
969130561Sobrien	return (ISC_R_FAILURE);
970130561Sobrien}
971130561Sobrien
972130561Sobrienstatic void
973130561Sobrienwrap_xmlfree(isc_buffer_t *buffer, void *arg) {
974130561Sobrien	UNUSED(arg);
975130561Sobrien
976130561Sobrien	xmlFree(isc_buffer_base(buffer));
977130561Sobrien}
978130561Sobrien
979130561Sobrienstatic isc_result_t
980130561Sobrienrender_index(const char *url, const char *querystring, void *arg,
981130561Sobrien	     unsigned int *retcode, const char **retmsg, const char **mimetype,
982130561Sobrien	     isc_buffer_t *b, isc_httpdfree_t **freecb,
983130561Sobrien	     void **freecb_args)
984130561Sobrien{
985130561Sobrien	unsigned char *msg;
986130561Sobrien	int msglen;
987130561Sobrien	ns_server_t *server = arg;
988130561Sobrien	isc_result_t result;
989130561Sobrien
990130561Sobrien	UNUSED(url);
991130561Sobrien	UNUSED(querystring);
992130561Sobrien
993130561Sobrien	result = generatexml(server, &msglen, &msg);
994130561Sobrien
995130561Sobrien	if (result == ISC_R_SUCCESS) {
996130561Sobrien		*retcode = 200;
997130561Sobrien		*retmsg = "OK";
998130561Sobrien		*mimetype = "text/xml";
999130561Sobrien		isc_buffer_reinit(b, msg, msglen);
1000130561Sobrien		isc_buffer_add(b, msglen);
1001130561Sobrien		*freecb = wrap_xmlfree;
1002130561Sobrien		*freecb_args = NULL;
1003130561Sobrien	}
1004130561Sobrien
1005130561Sobrien	return (result);
1006130561Sobrien}
1007130561Sobrien
1008130561Sobrien#endif	/* HAVE_LIBXML2 */
1009130561Sobrien
1010130561Sobrienstatic isc_result_t
1011130561Sobrienrender_xsl(const char *url, const char *querystring, void *args,
1012130561Sobrien	   unsigned int *retcode, const char **retmsg, const char **mimetype,
1013130561Sobrien	   isc_buffer_t *b, isc_httpdfree_t **freecb,
1014130561Sobrien	   void **freecb_args)
1015130561Sobrien{
1016130561Sobrien	UNUSED(url);
1017130561Sobrien	UNUSED(querystring);
1018130561Sobrien	UNUSED(args);
1019130561Sobrien
1020130561Sobrien	*retcode = 200;
1021130561Sobrien	*retmsg = "OK";
1022130561Sobrien	*mimetype = "text/xslt+xml";
1023130561Sobrien	isc_buffer_reinit(b, xslmsg, strlen(xslmsg));
1024130561Sobrien	isc_buffer_add(b, strlen(xslmsg));
1025130561Sobrien	*freecb = NULL;
1026130561Sobrien	*freecb_args = NULL;
1027130561Sobrien
1028130561Sobrien	return (ISC_R_SUCCESS);
1029130561Sobrien}
1030130561Sobrien
1031130561Sobrienstatic void
1032130561Sobrienshutdown_listener(ns_statschannel_t *listener) {
1033130561Sobrien	char socktext[ISC_SOCKADDR_FORMATSIZE];
1034130561Sobrien	isc_sockaddr_format(&listener->address, socktext, sizeof(socktext));
1035130561Sobrien	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,NS_LOGMODULE_SERVER,
1036130561Sobrien		      ISC_LOG_NOTICE, "stopping statistics channel on %s",
1037130561Sobrien		      socktext);
1038130561Sobrien
1039130561Sobrien	isc_httpdmgr_shutdown(&listener->httpdmgr);
1040130561Sobrien}
1041130561Sobrien
1042130561Sobrienstatic isc_boolean_t
1043130561Sobrienclient_ok(const isc_sockaddr_t *fromaddr, void *arg) {
1044130561Sobrien	ns_statschannel_t *listener = arg;
1045130561Sobrien	isc_netaddr_t netaddr;
1046130561Sobrien	char socktext[ISC_SOCKADDR_FORMATSIZE];
1047130561Sobrien	int match;
1048130561Sobrien
1049130561Sobrien	REQUIRE(listener != NULL);
1050130561Sobrien
1051130561Sobrien	isc_netaddr_fromsockaddr(&netaddr, fromaddr);
1052130561Sobrien
1053130561Sobrien	LOCK(&listener->lock);
1054130561Sobrien	if (dns_acl_match(&netaddr, NULL, listener->acl, &ns_g_server->aclenv,
1055130561Sobrien			  &match, NULL) == ISC_R_SUCCESS && match > 0) {
1056130561Sobrien		UNLOCK(&listener->lock);
1057130561Sobrien		return (ISC_TRUE);
1058130561Sobrien	}
1059130561Sobrien	UNLOCK(&listener->lock);
1060130561Sobrien
1061130561Sobrien	isc_sockaddr_format(fromaddr, socktext, sizeof(socktext));
1062130561Sobrien	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1063130561Sobrien		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1064130561Sobrien		      "rejected statistics connection from %s", socktext);
1065130561Sobrien
1066130561Sobrien	return (ISC_FALSE);
1067130561Sobrien}
1068130561Sobrien
1069130561Sobrienstatic void
1070130561Sobriendestroy_listener(void *arg) {
1071130561Sobrien	ns_statschannel_t *listener = arg;
1072130561Sobrien
1073130561Sobrien	REQUIRE(listener != NULL);
1074130561Sobrien	REQUIRE(!ISC_LINK_LINKED(listener, link));
1075130561Sobrien
1076130561Sobrien	/* We don't have to acquire the lock here since it's already unlinked */
1077130561Sobrien	dns_acl_detach(&listener->acl);
1078130561Sobrien
1079130561Sobrien	DESTROYLOCK(&listener->lock);
1080130561Sobrien	isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
1081130561Sobrien}
1082130561Sobrien
1083130561Sobrienstatic isc_result_t
1084130561Sobrienadd_listener(ns_server_t *server, ns_statschannel_t **listenerp,
1085130561Sobrien	     const cfg_obj_t *listen_params, const cfg_obj_t *config,
1086130561Sobrien	     isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1087130561Sobrien	     const char *socktext)
1088130561Sobrien{
1089130561Sobrien	isc_result_t result;
1090130561Sobrien	ns_statschannel_t *listener;
1091130561Sobrien	isc_task_t *task = NULL;
1092130561Sobrien	isc_socket_t *sock = NULL;
1093130561Sobrien	const cfg_obj_t *allow;
1094130561Sobrien	dns_acl_t *new_acl = NULL;
1095130561Sobrien
1096130561Sobrien	listener = isc_mem_get(server->mctx, sizeof(*listener));
1097130561Sobrien	if (listener == NULL)
1098130561Sobrien		return (ISC_R_NOMEMORY);
1099130561Sobrien
1100130561Sobrien	listener->httpdmgr = NULL;
1101130561Sobrien	listener->address = *addr;
1102130561Sobrien	listener->acl = NULL;
1103130561Sobrien	listener->mctx = NULL;
1104130561Sobrien	ISC_LINK_INIT(listener, link);
1105130561Sobrien
1106130561Sobrien	result = isc_mutex_init(&listener->lock);
1107130561Sobrien	if (result != ISC_R_SUCCESS) {
1108130561Sobrien		isc_mem_put(server->mctx, listener, sizeof(*listener));
1109130561Sobrien		return (ISC_R_FAILURE);
1110130561Sobrien	}
1111130561Sobrien
1112130561Sobrien	isc_mem_attach(server->mctx, &listener->mctx);
1113130561Sobrien
1114130561Sobrien	allow = cfg_tuple_get(listen_params, "allow");
1115130561Sobrien	if (allow != NULL && cfg_obj_islist(allow)) {
1116130561Sobrien		result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1117130561Sobrien					    aclconfctx, listener->mctx, 0,
1118130561Sobrien					    &new_acl);
1119130561Sobrien	} else
1120130561Sobrien		result = dns_acl_any(listener->mctx, &new_acl);
1121130561Sobrien	if (result != ISC_R_SUCCESS)
1122130561Sobrien		goto cleanup;
1123130561Sobrien	dns_acl_attach(new_acl, &listener->acl);
1124130561Sobrien	dns_acl_detach(&new_acl);
1125130561Sobrien
1126130561Sobrien	result = isc_task_create(ns_g_taskmgr, 0, &task);
1127130561Sobrien	if (result != ISC_R_SUCCESS)
1128130561Sobrien		goto cleanup;
1129130561Sobrien	isc_task_setname(task, "statchannel", NULL);
1130130561Sobrien
1131130561Sobrien	result = isc_socket_create(ns_g_socketmgr, isc_sockaddr_pf(addr),
1132130561Sobrien				   isc_sockettype_tcp, &sock);
1133130561Sobrien	if (result != ISC_R_SUCCESS)
1134130561Sobrien		goto cleanup;
1135130561Sobrien	isc_socket_setname(sock, "statchannel", NULL);
1136130561Sobrien
1137130561Sobrien#ifndef ISC_ALLOW_MAPPED
1138130561Sobrien	isc_socket_ipv6only(sock, ISC_TRUE);
1139130561Sobrien#endif
1140130561Sobrien
1141130561Sobrien	result = isc_socket_bind(sock, addr, ISC_SOCKET_REUSEADDRESS);
1142130561Sobrien	if (result != ISC_R_SUCCESS)
1143130561Sobrien		goto cleanup;
1144130561Sobrien
1145130561Sobrien	result = isc_httpdmgr_create(server->mctx, sock, task, client_ok,
1146130561Sobrien				     destroy_listener, listener, ns_g_timermgr,
1147130561Sobrien				     &listener->httpdmgr);
1148130561Sobrien	if (result != ISC_R_SUCCESS)
1149130561Sobrien		goto cleanup;
1150130561Sobrien
1151130561Sobrien#ifdef HAVE_LIBXML2
1152130561Sobrien	isc_httpdmgr_addurl(listener->httpdmgr, "/", render_index, server);
1153130561Sobrien#endif
1154130561Sobrien	isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", render_xsl,
1155130561Sobrien			    server);
1156130561Sobrien
1157130561Sobrien	*listenerp = listener;
1158130561Sobrien	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1159130561Sobrien		      NS_LOGMODULE_SERVER, ISC_LOG_NOTICE,
1160130561Sobrien		      "statistics channel listening on %s", socktext);
1161130561Sobrien
1162130561Sobriencleanup:
1163130561Sobrien	if (result != ISC_R_SUCCESS) {
1164130561Sobrien		if (listener->acl != NULL)
1165130561Sobrien			dns_acl_detach(&listener->acl);
1166130561Sobrien		DESTROYLOCK(&listener->lock);
1167130561Sobrien		isc_mem_putanddetach(&listener->mctx, listener,
1168130561Sobrien				     sizeof(*listener));
1169130561Sobrien	}
1170130561Sobrien	if (task != NULL)
1171130561Sobrien		isc_task_detach(&task);
1172130561Sobrien	if (sock != NULL)
1173130561Sobrien		isc_socket_detach(&sock);
1174130561Sobrien
1175130561Sobrien	return (result);
1176130561Sobrien}
1177130561Sobrien
1178130561Sobrienstatic void
1179130561Sobrienupdate_listener(ns_server_t *server, ns_statschannel_t **listenerp,
1180130561Sobrien		const cfg_obj_t *listen_params, const cfg_obj_t *config,
1181130561Sobrien		isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1182130561Sobrien		const char *socktext)
1183130561Sobrien{
1184130561Sobrien	ns_statschannel_t *listener;
1185130561Sobrien	const cfg_obj_t *allow = NULL;
1186130561Sobrien	dns_acl_t *new_acl = NULL;
1187130561Sobrien	isc_result_t result = ISC_R_SUCCESS;
1188130561Sobrien
1189130561Sobrien	for (listener = ISC_LIST_HEAD(server->statschannels);
1190130561Sobrien	     listener != NULL;
1191130561Sobrien	     listener = ISC_LIST_NEXT(listener, link))
1192130561Sobrien		if (isc_sockaddr_equal(addr, &listener->address))
1193130561Sobrien			break;
1194130561Sobrien
1195130561Sobrien	if (listener == NULL) {
1196130561Sobrien		*listenerp = NULL;
1197130561Sobrien		return;
1198130561Sobrien	}
1199130561Sobrien
1200130561Sobrien	/*
1201130561Sobrien	 * Now, keep the old access list unless a new one can be made.
1202130561Sobrien	 */
1203130561Sobrien	allow = cfg_tuple_get(listen_params, "allow");
1204130561Sobrien	if (allow != NULL && cfg_obj_islist(allow)) {
1205130561Sobrien		result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1206130561Sobrien					    aclconfctx, listener->mctx, 0,
1207130561Sobrien					    &new_acl);
1208130561Sobrien	} else
1209130561Sobrien		result = dns_acl_any(listener->mctx, &new_acl);
1210130561Sobrien
1211130561Sobrien	if (result == ISC_R_SUCCESS) {
1212130561Sobrien		LOCK(&listener->lock);
1213130561Sobrien
1214130561Sobrien		dns_acl_detach(&listener->acl);
1215130561Sobrien		dns_acl_attach(new_acl, &listener->acl);
1216130561Sobrien		dns_acl_detach(&new_acl);
1217130561Sobrien
1218130561Sobrien		UNLOCK(&listener->lock);
1219130561Sobrien	} else {
1220130561Sobrien		cfg_obj_log(listen_params, ns_g_lctx, ISC_LOG_WARNING,
1221130561Sobrien			    "couldn't install new acl for "
1222130561Sobrien			    "statistics channel %s: %s",
1223130561Sobrien			    socktext, isc_result_totext(result));
1224130561Sobrien	}
1225130561Sobrien
1226130561Sobrien	*listenerp = listener;
1227130561Sobrien}
1228130561Sobrien
1229130561Sobrienisc_result_t
1230130561Sobrienns_statschannels_configure(ns_server_t *server, const cfg_obj_t *config,
1231130561Sobrien			 cfg_aclconfctx_t *aclconfctx)
1232130561Sobrien{
1233130561Sobrien	ns_statschannel_t *listener, *listener_next;
1234130561Sobrien	ns_statschannellist_t new_listeners;
1235130561Sobrien	const cfg_obj_t *statschannellist = NULL;
1236130561Sobrien	const cfg_listelt_t *element, *element2;
1237130561Sobrien	char socktext[ISC_SOCKADDR_FORMATSIZE];
1238130561Sobrien
1239130561Sobrien	RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
1240130561Sobrien
1241130561Sobrien	ISC_LIST_INIT(new_listeners);
1242130561Sobrien
1243130561Sobrien	/*
1244130561Sobrien	 * Get the list of named.conf 'statistics-channels' statements.
1245130561Sobrien	 */
1246130561Sobrien	(void)cfg_map_get(config, "statistics-channels", &statschannellist);
1247130561Sobrien
1248130561Sobrien	/*
1249130561Sobrien	 * Run through the new address/port list, noting sockets that are
1250130561Sobrien	 * already being listened on and moving them to the new list.
1251130561Sobrien	 *
1252130561Sobrien	 * Identifying duplicate addr/port combinations is left to either
1253130561Sobrien	 * the underlying config code, or to the bind attempt getting an
1254130561Sobrien	 * address-in-use error.
1255130561Sobrien	 */
1256130561Sobrien	if (statschannellist != NULL) {
1257130561Sobrien#ifndef HAVE_LIBXML2
1258130561Sobrien		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1259130561Sobrien			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1260130561Sobrien			      "statistics-channels specified but not effective "
1261130561Sobrien			      "due to missing XML library");
1262130561Sobrien#endif
1263130561Sobrien
1264130561Sobrien		for (element = cfg_list_first(statschannellist);
1265130561Sobrien		     element != NULL;
1266130561Sobrien		     element = cfg_list_next(element)) {
1267130561Sobrien			const cfg_obj_t *statschannel;
1268130561Sobrien			const cfg_obj_t *listenercfg = NULL;
1269130561Sobrien
1270130561Sobrien			statschannel = cfg_listelt_value(element);
1271130561Sobrien			(void)cfg_map_get(statschannel, "inet",
1272130561Sobrien					  &listenercfg);
1273130561Sobrien			if (listenercfg == NULL)
1274130561Sobrien				continue;
1275130561Sobrien
1276130561Sobrien			for (element2 = cfg_list_first(listenercfg);
1277130561Sobrien			     element2 != NULL;
1278130561Sobrien			     element2 = cfg_list_next(element2)) {
1279130561Sobrien				const cfg_obj_t *listen_params;
1280130561Sobrien				const cfg_obj_t *obj;
1281130561Sobrien				isc_sockaddr_t addr;
1282130561Sobrien
1283130561Sobrien				listen_params = cfg_listelt_value(element2);
1284130561Sobrien
1285130561Sobrien				obj = cfg_tuple_get(listen_params, "address");
1286130561Sobrien				addr = *cfg_obj_assockaddr(obj);
1287130561Sobrien				if (isc_sockaddr_getport(&addr) == 0)
1288130561Sobrien					isc_sockaddr_setport(&addr, NS_STATSCHANNEL_HTTPPORT);
1289130561Sobrien
1290130561Sobrien				isc_sockaddr_format(&addr, socktext,
1291130561Sobrien						    sizeof(socktext));
1292130561Sobrien
1293130561Sobrien				isc_log_write(ns_g_lctx,
1294130561Sobrien					      NS_LOGCATEGORY_GENERAL,
1295130561Sobrien					      NS_LOGMODULE_SERVER,
1296130561Sobrien					      ISC_LOG_DEBUG(9),
1297130561Sobrien					      "processing statistics "
1298130561Sobrien					      "channel %s",
1299130561Sobrien					      socktext);
1300130561Sobrien
1301130561Sobrien				update_listener(server, &listener,
1302130561Sobrien						listen_params, config, &addr,
1303130561Sobrien						aclconfctx, socktext);
1304130561Sobrien
1305130561Sobrien				if (listener != NULL) {
1306130561Sobrien					/*
1307130561Sobrien					 * Remove the listener from the old
1308130561Sobrien					 * list, so it won't be shut down.
1309130561Sobrien					 */
1310130561Sobrien					ISC_LIST_UNLINK(server->statschannels,
1311130561Sobrien							listener, link);
1312130561Sobrien				} else {
1313130561Sobrien					/*
1314130561Sobrien					 * This is a new listener.
1315130561Sobrien					 */
1316130561Sobrien					isc_result_t r;
1317130561Sobrien
1318130561Sobrien					r = add_listener(server, &listener,
1319130561Sobrien							 listen_params, config,
1320130561Sobrien							 &addr, aclconfctx,
1321130561Sobrien							 socktext);
1322130561Sobrien					if (r != ISC_R_SUCCESS) {
1323130561Sobrien						cfg_obj_log(listen_params,
1324130561Sobrien							    ns_g_lctx,
1325130561Sobrien							    ISC_LOG_WARNING,
1326130561Sobrien							    "couldn't allocate "
1327130561Sobrien							    "statistics channel"
1328130561Sobrien							    " %s: %s",
1329130561Sobrien							    socktext,
1330130561Sobrien							    isc_result_totext(r));
1331130561Sobrien					}
1332130561Sobrien				}
1333130561Sobrien
1334130561Sobrien				if (listener != NULL)
1335130561Sobrien					ISC_LIST_APPEND(new_listeners, listener,
1336130561Sobrien							link);
1337130561Sobrien			}
1338130561Sobrien		}
1339130561Sobrien	}
1340130561Sobrien
1341130561Sobrien	for (listener = ISC_LIST_HEAD(server->statschannels);
1342130561Sobrien	     listener != NULL;
1343130561Sobrien	     listener = listener_next) {
1344130561Sobrien		listener_next = ISC_LIST_NEXT(listener, link);
1345130561Sobrien		ISC_LIST_UNLINK(server->statschannels, listener, link);
1346130561Sobrien		shutdown_listener(listener);
1347130561Sobrien	}
1348130561Sobrien
1349130561Sobrien	ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
1350130561Sobrien	return (ISC_R_SUCCESS);
1351130561Sobrien}
1352130561Sobrien
1353130561Sobrienvoid
1354130561Sobrienns_statschannels_shutdown(ns_server_t *server) {
1355130561Sobrien	ns_statschannel_t *listener;
1356130561Sobrien
1357130561Sobrien	while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) {
1358130561Sobrien		ISC_LIST_UNLINK(server->statschannels, listener, link);
1359130561Sobrien		shutdown_listener(listener);
1360130561Sobrien	}
1361130561Sobrien}
1362130561Sobrien
1363130561Sobrienisc_result_t
1364130561Sobrienns_stats_dump(ns_server_t *server, FILE *fp) {
1365130561Sobrien	isc_stdtime_t now;
1366130561Sobrien	isc_result_t result;
1367130561Sobrien	dns_view_t *view;
1368130561Sobrien	dns_zone_t *zone, *next;
1369130561Sobrien	stats_dumparg_t dumparg;
1370130561Sobrien	isc_uint64_t nsstat_values[dns_nsstatscounter_max];
1371130561Sobrien	isc_uint64_t resstat_values[dns_resstatscounter_max];
1372130561Sobrien	isc_uint64_t zonestat_values[dns_zonestatscounter_max];
1373130561Sobrien	isc_uint64_t sockstat_values[isc_sockstatscounter_max];
1374130561Sobrien
1375130561Sobrien	RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
1376130561Sobrien
1377130561Sobrien	/* Set common fields */
1378130561Sobrien	dumparg.type = statsformat_file;
1379130561Sobrien	dumparg.arg = fp;
1380130561Sobrien
1381130561Sobrien	isc_stdtime_get(&now);
1382130561Sobrien	fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
1383130561Sobrien
1384130561Sobrien	fprintf(fp, "++ Incoming Requests ++\n");
1385130561Sobrien	dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg, 0);
1386130561Sobrien
1387130561Sobrien	fprintf(fp, "++ Incoming Queries ++\n");
1388130561Sobrien	dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
1389130561Sobrien				&dumparg, 0);
1390130561Sobrien
1391130561Sobrien	fprintf(fp, "++ Outgoing Queries ++\n");
1392130561Sobrien	for (view = ISC_LIST_HEAD(server->viewlist);
1393130561Sobrien	     view != NULL;
1394130561Sobrien	     view = ISC_LIST_NEXT(view, link)) {
1395130561Sobrien		if (view->resquerystats == NULL)
1396130561Sobrien			continue;
1397130561Sobrien		if (strcmp(view->name, "_default") == 0)
1398130561Sobrien			fprintf(fp, "[View: default]\n");
1399130561Sobrien		else
1400130561Sobrien			fprintf(fp, "[View: %s]\n", view->name);
1401130561Sobrien		dns_rdatatypestats_dump(view->resquerystats, rdtypestat_dump,
1402130561Sobrien					&dumparg, 0);
1403130561Sobrien	}
1404130561Sobrien
1405130561Sobrien	fprintf(fp, "++ Name Server Statistics ++\n");
1406130561Sobrien	(void) dump_counters(server->nsstats, statsformat_file, fp, NULL,
1407130561Sobrien			     nsstats_desc, dns_nsstatscounter_max,
1408130561Sobrien			     nsstats_index, nsstat_values, 0);
1409130561Sobrien
1410130561Sobrien	fprintf(fp, "++ Zone Maintenance Statistics ++\n");
1411130561Sobrien	(void) dump_counters(server->zonestats, statsformat_file, fp, NULL,
1412130561Sobrien			     zonestats_desc, dns_zonestatscounter_max,
1413130561Sobrien			     zonestats_index, zonestat_values, 0);
1414130561Sobrien
1415130561Sobrien	fprintf(fp, "++ Resolver Statistics ++\n");
1416130561Sobrien	fprintf(fp, "[Common]\n");
1417130561Sobrien	(void) dump_counters(server->resolverstats, statsformat_file, fp, NULL,
1418130561Sobrien			     resstats_desc, dns_resstatscounter_max,
1419130561Sobrien			     resstats_index, resstat_values, 0);
1420130561Sobrien	for (view = ISC_LIST_HEAD(server->viewlist);
1421130561Sobrien	     view != NULL;
1422130561Sobrien	     view = ISC_LIST_NEXT(view, link)) {
1423130561Sobrien		if (view->resstats == NULL)
1424130561Sobrien			continue;
1425130561Sobrien		if (strcmp(view->name, "_default") == 0)
1426130561Sobrien			fprintf(fp, "[View: default]\n");
1427130561Sobrien		else
1428130561Sobrien			fprintf(fp, "[View: %s]\n", view->name);
1429130561Sobrien		(void) dump_counters(view->resstats, statsformat_file, fp, NULL,
1430130561Sobrien				     resstats_desc, dns_resstatscounter_max,
1431130561Sobrien				     resstats_index, resstat_values, 0);
1432130561Sobrien	}
1433130561Sobrien
1434130561Sobrien	fprintf(fp, "++ Cache DB RRsets ++\n");
1435130561Sobrien	for (view = ISC_LIST_HEAD(server->viewlist);
1436130561Sobrien	     view != NULL;
1437130561Sobrien	     view = ISC_LIST_NEXT(view, link)) {
1438130561Sobrien		dns_stats_t *cachestats;
1439130561Sobrien
1440130561Sobrien		cachestats = dns_db_getrrsetstats(view->cachedb);
1441130561Sobrien		if (cachestats == NULL)
1442130561Sobrien			continue;
1443130561Sobrien		if (strcmp(view->name, "_default") == 0)
1444130561Sobrien			fprintf(fp, "[View: default]\n");
1445130561Sobrien		else
1446130561Sobrien			fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
1447130561Sobrien				dns_cache_getname(view->cache));
1448130561Sobrien		if (dns_view_iscacheshared(view)) {
1449130561Sobrien			/*
1450130561Sobrien			 * Avoid dumping redundant statistics when the cache is
1451130561Sobrien			 * shared.
1452130561Sobrien			 */
1453130561Sobrien			continue;
1454130561Sobrien		}
1455130561Sobrien		dns_rdatasetstats_dump(cachestats, rdatasetstats_dump, &dumparg,
1456130561Sobrien				       0);
1457130561Sobrien	}
1458130561Sobrien
1459130561Sobrien	fprintf(fp, "++ Socket I/O Statistics ++\n");
1460130561Sobrien	(void) dump_counters(server->sockstats, statsformat_file, fp, NULL,
1461130561Sobrien			     sockstats_desc, isc_sockstatscounter_max,
1462130561Sobrien			     sockstats_index, sockstat_values, 0);
1463130561Sobrien
1464130561Sobrien	fprintf(fp, "++ Per Zone Query Statistics ++\n");
1465130561Sobrien	zone = NULL;
1466130561Sobrien	for (result = dns_zone_first(server->zonemgr, &zone);
1467130561Sobrien	     result == ISC_R_SUCCESS;
1468130561Sobrien	     next = NULL, result = dns_zone_next(zone, &next), zone = next)
1469130561Sobrien	{
1470130561Sobrien		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
1471130561Sobrien		if (zonestats != NULL) {
1472130561Sobrien			char zonename[DNS_NAME_FORMATSIZE];
1473130561Sobrien
1474130561Sobrien			dns_name_format(dns_zone_getorigin(zone),
1475130561Sobrien					zonename, sizeof(zonename));
1476130561Sobrien			view = dns_zone_getview(zone);
1477130561Sobrien
1478130561Sobrien			fprintf(fp, "[%s", zonename);
1479130561Sobrien			if (strcmp(view->name, "_default") != 0)
1480130561Sobrien				fprintf(fp, " (view: %s)", view->name);
1481130561Sobrien			fprintf(fp, "]\n");
1482130561Sobrien
1483130561Sobrien			(void) dump_counters(zonestats, statsformat_file, fp,
1484130561Sobrien					     NULL, nsstats_desc,
1485130561Sobrien					     dns_nsstatscounter_max,
1486130561Sobrien					     nsstats_index, nsstat_values, 0);
1487130561Sobrien		}
1488130561Sobrien	}
1489130561Sobrien
1490130561Sobrien	fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
1491130561Sobrien
1492130561Sobrien	return (ISC_R_SUCCESS);	/* this function currently always succeeds */
1493130561Sobrien}
1494130561Sobrien