]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bind9/bin/named/statschannel.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / bind9 / bin / named / statschannel.c
1 /*
2  * Copyright (C) 2008-2010  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id: statschannel.c,v 1.14.64.11 2010/02/04 23:47:46 tbox Exp $ */
18
19 /*! \file */
20
21 #include <config.h>
22
23 #include <isc/buffer.h>
24 #include <isc/httpd.h>
25 #include <isc/mem.h>
26 #include <isc/once.h>
27 #include <isc/print.h>
28 #include <isc/socket.h>
29 #include <isc/stats.h>
30 #include <isc/task.h>
31
32 #include <dns/db.h>
33 #include <dns/opcode.h>
34 #include <dns/resolver.h>
35 #include <dns/rdataclass.h>
36 #include <dns/rdatatype.h>
37 #include <dns/stats.h>
38 #include <dns/view.h>
39 #include <dns/zt.h>
40
41 #include <named/log.h>
42 #include <named/server.h>
43 #include <named/statschannel.h>
44
45 #include "bind9.xsl.h"
46
47 struct ns_statschannel {
48         /* Unlocked */
49         isc_httpdmgr_t                          *httpdmgr;
50         isc_sockaddr_t                          address;
51         isc_mem_t                               *mctx;
52
53         /*
54          * Locked by channel lock: can be referenced and modified by both
55          * the server task and the channel task.
56          */
57         isc_mutex_t                             lock;
58         dns_acl_t                               *acl;
59
60         /* Locked by server task */
61         ISC_LINK(struct ns_statschannel)        link;
62 };
63
64 typedef enum { statsformat_file, statsformat_xml } statsformat_t;
65
66 typedef struct
67 stats_dumparg {
68         statsformat_t   type;
69         void            *arg;           /* type dependent argument */
70         int             ncounters;      /* used for general statistics */
71         int             *counterindices; /* used for general statistics */
72         isc_uint64_t    *countervalues;  /* used for general statistics */
73         isc_result_t    result;
74 } stats_dumparg_t;
75
76 static isc_once_t once = ISC_ONCE_INIT;
77
78 /*%
79  * Statistics descriptions.  These could be statistically initialized at
80  * compile time, but we configure them run time in the init_desc() function
81  * below so that they'll be less susceptible to counter name changes.
82  */
83 static const char *nsstats_desc[dns_nsstatscounter_max];
84 static const char *resstats_desc[dns_resstatscounter_max];
85 static const char *zonestats_desc[dns_zonestatscounter_max];
86 static const char *sockstats_desc[isc_sockstatscounter_max];
87 #ifdef HAVE_LIBXML2
88 static const char *nsstats_xmldesc[dns_nsstatscounter_max];
89 static const char *resstats_xmldesc[dns_resstatscounter_max];
90 static const char *zonestats_xmldesc[dns_zonestatscounter_max];
91 static const char *sockstats_xmldesc[isc_sockstatscounter_max];
92 #else
93 #define nsstats_xmldesc NULL
94 #define resstats_xmldesc NULL
95 #define zonestats_xmldesc NULL
96 #define sockstats_xmldesc NULL
97 #endif  /* HAVE_LIBXML2 */
98
99 #define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
100
101 /*%
102  * Mapping arrays to represent statistics counters in the order of our
103  * preference, regardless of the order of counter indices.  For example,
104  * nsstats_desc[nsstats_index[0]] will be the description that is shown first.
105  */
106 static int nsstats_index[dns_nsstatscounter_max];
107 static int resstats_index[dns_resstatscounter_max];
108 static int zonestats_index[dns_zonestatscounter_max];
109 static int sockstats_index[isc_sockstatscounter_max];
110
111 static inline void
112 set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
113          const char *xdesc, const char **xdescs)
114 {
115         REQUIRE(counter < maxcounter);
116         REQUIRE(fdescs[counter] == NULL);
117 #ifdef HAVE_LIBXML2
118         REQUIRE(xdescs[counter] == NULL);
119 #endif
120
121         fdescs[counter] = fdesc;
122 #ifdef HAVE_LIBXML2
123         xdescs[counter] = xdesc;
124 #else
125         UNUSED(xdesc);
126         UNUSED(xdescs);
127 #endif
128 }
129
130 static void
131 init_desc(void) {
132         int i;
133
134         /* Initialize name server statistics */
135         for (i = 0; i < dns_nsstatscounter_max; i++)
136                 nsstats_desc[i] = NULL;
137 #ifdef HAVE_LIBXML2
138         for (i = 0; i < dns_nsstatscounter_max; i++)
139                 nsstats_xmldesc[i] = NULL;
140 #endif
141
142 #define SET_NSSTATDESC(counterid, desc, xmldesc) \
143         do { \
144                 set_desc(dns_nsstatscounter_ ## counterid, \
145                          dns_nsstatscounter_max, \
146                          desc, nsstats_desc, xmldesc, nsstats_xmldesc); \
147                 nsstats_index[i++] = dns_nsstatscounter_ ## counterid; \
148         } while (0)
149
150         i = 0;
151         SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4");
152         SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6");
153         SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0");
154         SET_NSSTATDESC(badednsver,
155                        "requests with unsupported EDNS version received",
156                        "ReqBadEDNSVer");
157         SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG");
158         SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
159         SET_NSSTATDESC(invalidsig, "requests with invalid signature",
160                        "ReqBadSIG");
161         SET_NSSTATDESC(tcp, "TCP requests received", "ReqTCP");
162         SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
163         SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
164         SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
165         SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej");
166         SET_NSSTATDESC(response, "responses sent", "Response");
167         SET_NSSTATDESC(truncatedresp, "truncated responses sent",
168                        "TruncatedResp");
169         SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0");
170         SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG");
171         SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0");
172         SET_NSSTATDESC(success, "queries resulted in successful answer",
173                        "QrySuccess");
174         SET_NSSTATDESC(authans, "queries resulted in authoritative answer",
175                        "QryAuthAns");
176         SET_NSSTATDESC(nonauthans,
177                        "queries resulted in non authoritative answer",
178                        "QryNoauthAns");
179         SET_NSSTATDESC(referral, "queries resulted in referral answer",
180                        "QryReferral");
181         SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset");
182         SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL");
183         SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR");
184         SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN");
185         SET_NSSTATDESC(recursion, "queries caused recursion","QryRecursion");
186         SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate");
187         SET_NSSTATDESC(dropped, "queries dropped", "QryDropped");
188         SET_NSSTATDESC(failure, "other query failures", "QryFailure");
189         SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone");
190         SET_NSSTATDESC(updatereqfwd, "update requests forwarded",
191                        "UpdateReqFwd");
192         SET_NSSTATDESC(updaterespfwd, "update responses forwarded",
193                        "UpdateRespFwd");
194         SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail");
195         SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone");
196         SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail");
197         SET_NSSTATDESC(updatebadprereq,
198                        "updates rejected due to prerequisite failure",
199                        "UpdateBadPrereq");
200         INSIST(i == dns_nsstatscounter_max);
201
202         /* Initialize resolver statistics */
203         for (i = 0; i < dns_resstatscounter_max; i++)
204                 resstats_desc[i] = NULL;
205 #ifdef  HAVE_LIBXML2
206         for (i = 0; i < dns_resstatscounter_max; i++)
207                 resstats_xmldesc[i] = NULL;
208 #endif
209
210 #define SET_RESSTATDESC(counterid, desc, xmldesc) \
211         do { \
212                 set_desc(dns_resstatscounter_ ## counterid, \
213                          dns_resstatscounter_max, \
214                          desc, resstats_desc, xmldesc, resstats_xmldesc); \
215                 resstats_index[i++] = dns_resstatscounter_ ## counterid; \
216         } while (0)
217
218         i = 0;
219         SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4");
220         SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6");
221         SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4");
222         SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6");
223         SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN");
224         SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL");
225         SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR");
226         SET_RESSTATDESC(othererror, "other errors received", "OtherError");
227         SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail");
228         SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch");
229         SET_RESSTATDESC(truncated, "truncated responses received", "Truncated");
230         SET_RESSTATDESC(lame, "lame delegations received", "Lame");
231         SET_RESSTATDESC(retry, "query retries", "Retry");
232         SET_RESSTATDESC(dispabort, "queries aborted due to quota",
233                         "QueryAbort");
234         SET_RESSTATDESC(dispsockfail, "failures in opening query sockets",
235                         "QuerySockFail");
236         SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout");
237         SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4");
238         SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6");
239         SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed",
240                         "GlueFetchv4Fail");
241         SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed",
242                         "GlueFetchv6Fail");
243         SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt");
244         SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk");
245         SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded",
246                         "ValNegOk");
247         SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail");
248         SET_RESSTATDESC(queryrtt0, "queries with RTT < "
249                         DNS_RESOLVER_QRYRTTCLASS0STR "ms",
250                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
251         SET_RESSTATDESC(queryrtt1, "queries with RTT "
252                         DNS_RESOLVER_QRYRTTCLASS0STR "-"
253                         DNS_RESOLVER_QRYRTTCLASS1STR "ms",
254                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
255         SET_RESSTATDESC(queryrtt2, "queries with RTT "
256                         DNS_RESOLVER_QRYRTTCLASS1STR "-"
257                         DNS_RESOLVER_QRYRTTCLASS2STR "ms",
258                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
259         SET_RESSTATDESC(queryrtt3, "queries with RTT "
260                         DNS_RESOLVER_QRYRTTCLASS2STR "-"
261                         DNS_RESOLVER_QRYRTTCLASS3STR "ms",
262                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
263         SET_RESSTATDESC(queryrtt4, "queries with RTT "
264                         DNS_RESOLVER_QRYRTTCLASS3STR "-"
265                         DNS_RESOLVER_QRYRTTCLASS4STR "ms",
266                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
267         SET_RESSTATDESC(queryrtt5, "queries with RTT > "
268                         DNS_RESOLVER_QRYRTTCLASS4STR "ms",
269                         "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
270         INSIST(i == dns_resstatscounter_max);
271
272         /* Initialize zone statistics */
273         for (i = 0; i < dns_zonestatscounter_max; i++)
274                 zonestats_desc[i] = NULL;
275 #ifdef  HAVE_LIBXML2
276         for (i = 0; i < dns_zonestatscounter_max; i++)
277                 zonestats_xmldesc[i] = NULL;
278 #endif
279
280 #define SET_ZONESTATDESC(counterid, desc, xmldesc) \
281         do { \
282                 set_desc(dns_zonestatscounter_ ## counterid, \
283                          dns_zonestatscounter_max, \
284                          desc, zonestats_desc, xmldesc, zonestats_xmldesc); \
285                 zonestats_index[i++] = dns_zonestatscounter_ ## counterid; \
286         } while (0)
287
288         i = 0;
289         SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4");
290         SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6");
291         SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4");
292         SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6");
293         SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej");
294         SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4");
295         SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6");
296         SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4");
297         SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6");
298         SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4");
299         SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6");
300         SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded","XfrSuccess");
301         SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail");
302         INSIST(i == dns_zonestatscounter_max);
303
304         /* Initialize socket statistics */
305         for (i = 0; i < isc_sockstatscounter_max; i++)
306                 sockstats_desc[i] = NULL;
307 #ifdef  HAVE_LIBXML2
308         for (i = 0; i < isc_sockstatscounter_max; i++)
309                 sockstats_xmldesc[i] = NULL;
310 #endif
311
312 #define SET_SOCKSTATDESC(counterid, desc, xmldesc) \
313         do { \
314                 set_desc(isc_sockstatscounter_ ## counterid, \
315                          isc_sockstatscounter_max, \
316                          desc, sockstats_desc, xmldesc, sockstats_xmldesc); \
317                 sockstats_index[i++] = isc_sockstatscounter_ ## counterid; \
318         } while (0)
319
320         i = 0;
321         SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open");
322         SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open");
323         SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open");
324         SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open");
325         SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen");
326         SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures",
327                          "UDP4OpenFail");
328         SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures",
329                          "UDP6OpenFail");
330         SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures",
331                          "TCP4OpenFail");
332         SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures",
333                          "TCP6OpenFail");
334         SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures",
335                          "UnixOpenFail");
336         SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close");
337         SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close");
338         SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close");
339         SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close");
340         SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose");
341         SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed",
342                          "FDWatchClose");
343         SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures",
344                          "UDP4BindFail");
345         SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures",
346                          "UDP6BindFail");
347         SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures",
348                          "TCP4BindFail");
349         SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures",
350                          "TCP6BindFail");
351         SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures",
352                          "UnixBindFail");
353         SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures",
354                          "FdwatchBindFail");
355         SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures",
356                          "UDP4ConnFail");
357         SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures",
358                          "UDP6ConnFail");
359         SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures",
360                          "TCP4ConnFail");
361         SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures",
362                          "TCP6ConnFail");
363         SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures",
364                          "UnixConnFail");
365         SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures",
366                          "FDwatchConnFail");
367         SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established",
368                          "UDP4Conn");
369         SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established",
370                          "UDP6Conn");
371         SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established",
372                          "TCP4Conn");
373         SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established",
374                          "TCP6Conn");
375         SET_SOCKSTATDESC(unixconnect, "Unix domain connections established",
376                          "UnixConn");
377         SET_SOCKSTATDESC(fdwatchconnect,
378                          "FDwatch domain connections established",
379                          "FDwatchConn");
380         SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures",
381                          "TCP4AcceptFail");
382         SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures",
383                          "TCP6AcceptFail");
384         SET_SOCKSTATDESC(unixacceptfail,
385                          "Unix domain connection accept failures",
386                          "UnixAcceptFail");
387         SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted",
388                          "TCP4Accept");
389         SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted",
390                          "TCP6Accept");
391         SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted",
392                          "UnixAccept");
393         SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr");
394         SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr");
395         SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr");
396         SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr");
397         SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors",
398                          "UnixSendErr");
399         SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors",
400                          "FDwatchSendErr");
401         SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr");
402         SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr");
403         SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr");
404         SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr");
405         SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors",
406                          "UnixRecvErr");
407         SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors",
408                          "FDwatchRecvErr");
409         INSIST(i == isc_sockstatscounter_max);
410
411         /* Sanity check */
412         for (i = 0; i < dns_nsstatscounter_max; i++)
413                 INSIST(nsstats_desc[i] != NULL);
414         for (i = 0; i < dns_resstatscounter_max; i++)
415                 INSIST(resstats_desc[i] != NULL);
416         for (i = 0; i < dns_zonestatscounter_max; i++)
417                 INSIST(zonestats_desc[i] != NULL);
418         for (i = 0; i < isc_sockstatscounter_max; i++)
419                 INSIST(sockstats_desc[i] != NULL);
420 #ifdef  HAVE_LIBXML2
421         for (i = 0; i < dns_nsstatscounter_max; i++)
422                 INSIST(nsstats_xmldesc[i] != NULL);
423         for (i = 0; i < dns_resstatscounter_max; i++)
424                 INSIST(resstats_xmldesc[i] != NULL);
425         for (i = 0; i < dns_zonestatscounter_max; i++)
426                 INSIST(zonestats_xmldesc[i] != NULL);
427         for (i = 0; i < isc_sockstatscounter_max; i++)
428                 INSIST(sockstats_xmldesc[i] != NULL);
429 #endif
430 }
431
432 /*%
433  * Dump callback functions.
434  */
435 static void
436 generalstat_dump(isc_statscounter_t counter, isc_uint64_t val, void *arg) {
437         stats_dumparg_t *dumparg = arg;
438
439         REQUIRE(counter < dumparg->ncounters);
440         dumparg->countervalues[counter] = val;
441 }
442
443 static isc_result_t
444 dump_counters(isc_stats_t *stats, statsformat_t type, void *arg,
445               const char *category, const char **desc, int ncounters,
446               int *indices, isc_uint64_t *values, int options)
447 {
448         int i, index;
449         isc_uint64_t value;
450         stats_dumparg_t dumparg;
451         FILE *fp;
452 #ifdef HAVE_LIBXML2
453         xmlTextWriterPtr writer;
454         int xmlrc;
455 #endif
456
457 #ifndef HAVE_LIBXML2
458         UNUSED(category);
459 #endif
460
461         dumparg.type = type;
462         dumparg.ncounters = ncounters;
463         dumparg.counterindices = indices;
464         dumparg.countervalues = values;
465
466         memset(values, 0, sizeof(values[0]) * ncounters);
467         isc_stats_dump(stats, generalstat_dump, &dumparg, options);
468
469         for (i = 0; i < ncounters; i++) {
470                 index = indices[i];
471                 value = values[index];
472
473                 if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0)
474                         continue;
475
476                 switch (dumparg.type) {
477                 case statsformat_file:
478                         fp = arg;
479                         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
480                                 value, desc[index]);
481                         break;
482                 case statsformat_xml:
483 #ifdef HAVE_LIBXML2
484                         writer = arg;
485
486                         if (category != NULL) {
487                                 TRY0(xmlTextWriterStartElement(writer,
488                                                                ISC_XMLCHAR
489                                                                category));
490                                 TRY0(xmlTextWriterStartElement(writer,
491                                                                ISC_XMLCHAR
492                                                                "name"));
493                                 TRY0(xmlTextWriterWriteString(writer,
494                                                               ISC_XMLCHAR
495                                                               desc[index]));
496                                 TRY0(xmlTextWriterEndElement(writer)); /* name */
497
498                                 TRY0(xmlTextWriterStartElement(writer,
499                                                                ISC_XMLCHAR
500                                                                "counter"));
501                         } else {
502                                 TRY0(xmlTextWriterStartElement(writer,
503                                                                ISC_XMLCHAR
504                                                                desc[index]));
505                         }
506                         TRY0(xmlTextWriterWriteFormatString(writer,
507                                                             "%"
508                                                             ISC_PRINT_QUADFORMAT
509                                                             "u", value));
510                         TRY0(xmlTextWriterEndElement(writer)); /* counter */
511                         if (category != NULL)
512                                 TRY0(xmlTextWriterEndElement(writer)); /* category */
513 #endif
514                         break;
515                 }
516         }
517         return (ISC_R_SUCCESS);
518 #ifdef HAVE_LIBXML2
519  error:
520         return (ISC_R_FAILURE);
521 #endif
522 }
523
524 static void
525 rdtypestat_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
526         char typebuf[64];
527         const char *typestr;
528         stats_dumparg_t *dumparg = arg;
529         FILE *fp;
530 #ifdef HAVE_LIBXML2
531         xmlTextWriterPtr writer;
532         int xmlrc;
533 #endif
534
535         if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_OTHERTYPE)
536             == 0) {
537                 dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
538                                      sizeof(typebuf));
539                 typestr = typebuf;
540         } else
541                 typestr = "Others";
542
543         switch (dumparg->type) {
544         case statsformat_file:
545                 fp = dumparg->arg;
546                 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, typestr);
547                 break;
548         case statsformat_xml:
549 #ifdef HAVE_LIBXML2
550                 writer = dumparg->arg;
551
552                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdtype"));
553
554                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
555                 TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR typestr));
556                 TRY0(xmlTextWriterEndElement(writer)); /* name */
557
558                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
559                 TRY0(xmlTextWriterWriteFormatString(writer,
560                                                "%" ISC_PRINT_QUADFORMAT "u",
561                                                val));
562                 TRY0(xmlTextWriterEndElement(writer)); /* counter */
563
564                 TRY0(xmlTextWriterEndElement(writer)); /* rdtype */
565 #endif
566                 break;
567         }
568         return;
569 #ifdef HAVE_LIBXML2
570  error:
571         dumparg->result = ISC_R_FAILURE;
572         return;
573 #endif
574 }
575
576 static void
577 rdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
578         stats_dumparg_t *dumparg = arg;
579         FILE *fp;
580         char typebuf[64];
581         const char *typestr;
582         isc_boolean_t nxrrset = ISC_FALSE;
583 #ifdef HAVE_LIBXML2
584         xmlTextWriterPtr writer;
585         int xmlrc;
586 #endif
587
588         if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXDOMAIN)
589             != 0) {
590                 typestr = "NXDOMAIN";
591         } else if ((DNS_RDATASTATSTYPE_ATTR(type) &
592                     DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0) {
593                 typestr = "Others";
594         } else {
595                 dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
596                                      sizeof(typebuf));
597                 typestr = typebuf;
598         }
599
600         if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXRRSET)
601             != 0)
602                 nxrrset = ISC_TRUE;
603
604         switch (dumparg->type) {
605         case statsformat_file:
606                 fp = dumparg->arg;
607                 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s%s\n", val,
608                         nxrrset ? "!" : "", typestr);
609                 break;
610         case statsformat_xml:
611 #ifdef HAVE_LIBXML2
612                 writer = dumparg->arg;
613
614                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
615                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
616                 TRY0(xmlTextWriterWriteFormatString(writer, "%s%s",
617                                                nxrrset ? "!" : "", typestr));
618                 TRY0(xmlTextWriterEndElement(writer)); /* name */
619
620                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
621                 TRY0(xmlTextWriterWriteFormatString(writer,
622                                                "%" ISC_PRINT_QUADFORMAT "u",
623                                                val));
624                 TRY0(xmlTextWriterEndElement(writer)); /* counter */
625
626                 TRY0(xmlTextWriterEndElement(writer)); /* rrset */
627 #endif
628                 break;
629         }
630         return;
631 #ifdef HAVE_LIBXML2
632  error:
633         dumparg->result = ISC_R_FAILURE;
634 #endif
635
636 }
637
638 static void
639 opcodestat_dump(dns_opcode_t code, isc_uint64_t val, void *arg) {
640         FILE *fp = arg;
641         isc_buffer_t b;
642         char codebuf[64];
643         stats_dumparg_t *dumparg = arg;
644 #ifdef HAVE_LIBXML2
645         xmlTextWriterPtr writer;
646         int xmlrc;
647 #endif
648
649         isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
650         dns_opcode_totext(code, &b);
651         codebuf[isc_buffer_usedlength(&b)] = '\0';
652
653         switch (dumparg->type) {
654         case statsformat_file:
655                 fp = dumparg->arg;
656                 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, codebuf);
657                 break;
658         case statsformat_xml:
659 #ifdef HAVE_LIBXML2
660                 writer = dumparg->arg;
661
662                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "opcode"));
663
664                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
665                 TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR codebuf));
666                 TRY0(xmlTextWriterEndElement(writer)); /* name */
667
668                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
669                 TRY0(xmlTextWriterWriteFormatString(writer,
670                                                "%" ISC_PRINT_QUADFORMAT "u",
671                                                val));
672                 TRY0(xmlTextWriterEndElement(writer)); /* counter */
673
674                 TRY0(xmlTextWriterEndElement(writer)); /* opcode */
675 #endif
676                 break;
677         }
678         return;
679
680 #ifdef HAVE_LIBXML2
681  error:
682         dumparg->result = ISC_R_FAILURE;
683         return;
684 #endif
685 }
686
687 #ifdef HAVE_LIBXML2
688
689 /* XXXMLG below here sucks. */
690
691
692 static isc_result_t
693 zone_xmlrender(dns_zone_t *zone, void *arg) {
694         char buf[1024 + 32];    /* sufficiently large for zone name and class */
695         dns_rdataclass_t rdclass;
696         isc_uint32_t serial;
697         xmlTextWriterPtr writer = arg;
698         isc_stats_t *zonestats;
699         isc_uint64_t nsstat_values[dns_nsstatscounter_max];
700         int xmlrc;
701         isc_result_t result;
702
703         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone"));
704
705         dns_zone_name(zone, buf, sizeof(buf));
706         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
707         TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
708         TRY0(xmlTextWriterEndElement(writer));
709
710         rdclass = dns_zone_getclass(zone);
711         dns_rdataclass_format(rdclass, buf, sizeof(buf));
712         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdataclass"));
713         TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
714         TRY0(xmlTextWriterEndElement(writer));
715
716         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
717         if (dns_zone_getserial2(zone, &serial) == ISC_R_SUCCESS)
718                 TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
719         else
720                 TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
721         TRY0(xmlTextWriterEndElement(writer));
722
723         zonestats = dns_zone_getrequeststats(zone);
724         if (zonestats != NULL) {
725                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
726                 result = dump_counters(zonestats, statsformat_xml, writer, NULL,
727                                       nsstats_xmldesc, dns_nsstatscounter_max,
728                                       nsstats_index, nsstat_values,
729                                       ISC_STATSDUMP_VERBOSE);
730                 if (result != ISC_R_SUCCESS)
731                         goto error;
732                 TRY0(xmlTextWriterEndElement(writer)); /* counters */
733         }
734
735         TRY0(xmlTextWriterEndElement(writer)); /* zone */
736
737         return (ISC_R_SUCCESS);
738  error:
739         return (ISC_R_FAILURE);
740 }
741
742 static isc_result_t
743 generatexml(ns_server_t *server, int *buflen, xmlChar **buf) {
744         char boottime[sizeof "yyyy-mm-ddThh:mm:ssZ"];
745         char nowstr[sizeof "yyyy-mm-ddThh:mm:ssZ"];
746         isc_time_t now;
747         xmlTextWriterPtr writer = NULL;
748         xmlDocPtr doc = NULL;
749         int xmlrc;
750         dns_view_t *view;
751         stats_dumparg_t dumparg;
752         dns_stats_t *cachestats;
753         isc_uint64_t nsstat_values[dns_nsstatscounter_max];
754         isc_uint64_t resstat_values[dns_resstatscounter_max];
755         isc_uint64_t zonestat_values[dns_zonestatscounter_max];
756         isc_uint64_t sockstat_values[isc_sockstatscounter_max];
757         isc_result_t result;
758
759         isc_time_now(&now);
760         isc_time_formatISO8601(&ns_g_boottime, boottime, sizeof boottime);
761         isc_time_formatISO8601(&now, nowstr, sizeof nowstr);
762
763         writer = xmlNewTextWriterDoc(&doc, 0);
764         if (writer == NULL)
765                 goto error;
766         TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL));
767         TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet",
768                         ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.xsl\""));
769         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "isc"));
770         TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
771                                          ISC_XMLCHAR "1.0"));
772
773         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind"));
774         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
775         TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
776                                          ISC_XMLCHAR "2.2"));
777
778         /* Set common fields for statistics dump */
779         dumparg.type = statsformat_xml;
780         dumparg.arg = writer;
781
782         /*
783          * Start by rendering the views we know of here.  For each view we
784          * know of, call its rendering function.
785          */
786         view = ISC_LIST_HEAD(server->viewlist);
787         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
788         while (view != NULL) {
789                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view"));
790
791                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
792                 TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR view->name));
793                 TRY0(xmlTextWriterEndElement(writer));
794
795                 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zones"));
796                 result = dns_zt_apply(view->zonetable, ISC_TRUE, zone_xmlrender,
797                                       writer);
798                 if (result != ISC_R_SUCCESS)
799                         goto error;
800                 TRY0(xmlTextWriterEndElement(writer));
801
802                 if (view->resquerystats != NULL) {
803                         dumparg.result = ISC_R_SUCCESS;
804                         dns_rdatatypestats_dump(view->resquerystats,
805                                                 rdtypestat_dump, &dumparg, 0);
806                         if (dumparg.result != ISC_R_SUCCESS)
807                                 goto error;
808                 }
809
810                 if (view->resstats != NULL) {
811                         result = dump_counters(view->resstats, statsformat_xml,
812                                                writer, "resstat",
813                                                resstats_xmldesc,
814                                                dns_resstatscounter_max,
815                                                resstats_index, resstat_values,
816                                                ISC_STATSDUMP_VERBOSE);
817                         if (result != ISC_R_SUCCESS)
818                                 goto error;
819                 }
820
821                 cachestats = dns_db_getrrsetstats(view->cachedb);
822                 if (cachestats != NULL) {
823                         TRY0(xmlTextWriterStartElement(writer,
824                                                        ISC_XMLCHAR "cache"));
825                         TRY0(xmlTextWriterWriteAttribute(writer,
826                                                          ISC_XMLCHAR "name",
827                                                          ISC_XMLCHAR
828                                                          view->name));
829                         dumparg.result = ISC_R_SUCCESS;
830                         dns_rdatasetstats_dump(cachestats, rdatasetstats_dump,
831                                                &dumparg, 0);
832                         if (dumparg.result != ISC_R_SUCCESS)
833                                 goto error;
834                         TRY0(xmlTextWriterEndElement(writer)); /* cache */
835                 }
836
837                 TRY0(xmlTextWriterEndElement(writer)); /* view */
838
839                 view = ISC_LIST_NEXT(view, link);
840         }
841         TRY0(xmlTextWriterEndElement(writer)); /* views */
842
843         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "socketmgr"));
844         isc_socketmgr_renderxml(ns_g_socketmgr, writer);
845         TRY0(xmlTextWriterEndElement(writer)); /* socketmgr */
846
847         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr"));
848         isc_taskmgr_renderxml(ns_g_taskmgr, writer);
849         TRY0(xmlTextWriterEndElement(writer)); /* taskmgr */
850
851         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
852         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time"));
853         TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime));
854         TRY0(xmlTextWriterEndElement(writer));
855         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time"));
856         TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr));
857         TRY0(xmlTextWriterEndElement(writer));
858
859         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "requests"));
860         dumparg.result = ISC_R_SUCCESS;
861         dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg,
862                              0);
863         if (dumparg.result != ISC_R_SUCCESS)
864                 goto error;
865         TRY0(xmlTextWriterEndElement(writer)); /* requests */
866
867         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "queries-in"));
868         dumparg.result = ISC_R_SUCCESS;
869         dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
870                                 &dumparg, 0);
871         if (dumparg.result != ISC_R_SUCCESS)
872                 goto error;
873         TRY0(xmlTextWriterEndElement(writer)); /* queries-in */
874
875         result = dump_counters(server->nsstats, statsformat_xml, writer,
876                                "nsstat", nsstats_xmldesc,
877                                 dns_nsstatscounter_max,
878                                 nsstats_index, nsstat_values,
879                                 ISC_STATSDUMP_VERBOSE);
880         if (result != ISC_R_SUCCESS)
881                 goto error;
882
883         result = dump_counters(server->zonestats, statsformat_xml, writer,
884                                "zonestat", zonestats_xmldesc,
885                                dns_zonestatscounter_max, zonestats_index,
886                                zonestat_values, ISC_STATSDUMP_VERBOSE);
887         if (result != ISC_R_SUCCESS)
888                 goto error;
889
890         /*
891          * Most of the common resolver statistics entries are 0, so we don't
892          * use the verbose dump here.
893          */
894         result = dump_counters(server->resolverstats, statsformat_xml, writer,
895                                "resstat", resstats_xmldesc,
896                                dns_resstatscounter_max, resstats_index,
897                                resstat_values, 0);
898         if (result != ISC_R_SUCCESS)
899                 goto error;
900
901         result = dump_counters(server->sockstats, statsformat_xml, writer,
902                                "sockstat", sockstats_xmldesc,
903                                isc_sockstatscounter_max, sockstats_index,
904                                sockstat_values, ISC_STATSDUMP_VERBOSE);
905         if (result != ISC_R_SUCCESS)
906                 goto error;
907
908         TRY0(xmlTextWriterEndElement(writer)); /* server */
909
910         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
911         isc_mem_renderxml(writer);
912         TRY0(xmlTextWriterEndElement(writer)); /* memory */
913
914         TRY0(xmlTextWriterEndElement(writer)); /* statistics */
915         TRY0(xmlTextWriterEndElement(writer)); /* bind */
916         TRY0(xmlTextWriterEndElement(writer)); /* isc */
917
918         TRY0(xmlTextWriterEndDocument(writer));
919
920         xmlFreeTextWriter(writer);
921
922         xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 1);
923         xmlFreeDoc(doc);
924         return (ISC_R_SUCCESS);
925
926  error:
927         if (writer != NULL)
928                 xmlFreeTextWriter(writer);
929         if (doc != NULL)
930                 xmlFreeDoc(doc);
931         return (ISC_R_FAILURE);
932 }
933
934 static void
935 wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
936         UNUSED(arg);
937
938         xmlFree(isc_buffer_base(buffer));
939 }
940
941 static isc_result_t
942 render_index(const char *url, const char *querystring, void *arg,
943              unsigned int *retcode, const char **retmsg, const char **mimetype,
944              isc_buffer_t *b, isc_httpdfree_t **freecb,
945              void **freecb_args)
946 {
947         unsigned char *msg;
948         int msglen;
949         ns_server_t *server = arg;
950         isc_result_t result;
951
952         UNUSED(url);
953         UNUSED(querystring);
954
955         result = generatexml(server, &msglen, &msg);
956
957         if (result == ISC_R_SUCCESS) {
958                 *retcode = 200;
959                 *retmsg = "OK";
960                 *mimetype = "text/xml";
961                 isc_buffer_reinit(b, msg, msglen);
962                 isc_buffer_add(b, msglen);
963                 *freecb = wrap_xmlfree;
964                 *freecb_args = NULL;
965         }
966
967         return (result);
968 }
969
970 #endif  /* HAVE_LIBXML2 */
971
972 static isc_result_t
973 render_xsl(const char *url, const char *querystring, void *args,
974            unsigned int *retcode, const char **retmsg, const char **mimetype,
975            isc_buffer_t *b, isc_httpdfree_t **freecb,
976            void **freecb_args)
977 {
978         UNUSED(url);
979         UNUSED(querystring);
980         UNUSED(args);
981
982         *retcode = 200;
983         *retmsg = "OK";
984         *mimetype = "text/xslt+xml";
985         isc_buffer_reinit(b, xslmsg, strlen(xslmsg));
986         isc_buffer_add(b, strlen(xslmsg));
987         *freecb = NULL;
988         *freecb_args = NULL;
989
990         return (ISC_R_SUCCESS);
991 }
992
993 static void
994 shutdown_listener(ns_statschannel_t *listener) {
995         char socktext[ISC_SOCKADDR_FORMATSIZE];
996         isc_sockaddr_format(&listener->address, socktext, sizeof(socktext));
997         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,NS_LOGMODULE_SERVER,
998                       ISC_LOG_NOTICE, "stopping statistics channel on %s",
999                       socktext);
1000
1001         isc_httpdmgr_shutdown(&listener->httpdmgr);
1002 }
1003
1004 static isc_boolean_t
1005 client_ok(const isc_sockaddr_t *fromaddr, void *arg) {
1006         ns_statschannel_t *listener = arg;
1007         isc_netaddr_t netaddr;
1008         char socktext[ISC_SOCKADDR_FORMATSIZE];
1009         int match;
1010
1011         REQUIRE(listener != NULL);
1012
1013         isc_netaddr_fromsockaddr(&netaddr, fromaddr);
1014
1015         LOCK(&listener->lock);
1016         if (dns_acl_match(&netaddr, NULL, listener->acl, &ns_g_server->aclenv,
1017                           &match, NULL) == ISC_R_SUCCESS && match > 0) {
1018                 UNLOCK(&listener->lock);
1019                 return (ISC_TRUE);
1020         }
1021         UNLOCK(&listener->lock);
1022
1023         isc_sockaddr_format(fromaddr, socktext, sizeof(socktext));
1024         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1025                       NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1026                       "rejected statistics connection from %s", socktext);
1027
1028         return (ISC_FALSE);
1029 }
1030
1031 static void
1032 destroy_listener(void *arg) {
1033         ns_statschannel_t *listener = arg;
1034
1035         REQUIRE(listener != NULL);
1036         REQUIRE(!ISC_LINK_LINKED(listener, link));
1037
1038         /* We don't have to acquire the lock here since it's already unlinked */
1039         dns_acl_detach(&listener->acl);
1040
1041         DESTROYLOCK(&listener->lock);
1042         isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
1043 }
1044
1045 static isc_result_t
1046 add_listener(ns_server_t *server, ns_statschannel_t **listenerp,
1047              const cfg_obj_t *listen_params, const cfg_obj_t *config,
1048              isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1049              const char *socktext)
1050 {
1051         isc_result_t result;
1052         ns_statschannel_t *listener;
1053         isc_task_t *task = NULL;
1054         isc_socket_t *sock = NULL;
1055         const cfg_obj_t *allow;
1056         dns_acl_t *new_acl = NULL;
1057
1058         listener = isc_mem_get(server->mctx, sizeof(*listener));
1059         if (listener == NULL)
1060                 return (ISC_R_NOMEMORY);
1061
1062         listener->httpdmgr = NULL;
1063         listener->address = *addr;
1064         listener->acl = NULL;
1065         listener->mctx = NULL;
1066         ISC_LINK_INIT(listener, link);
1067
1068         result = isc_mutex_init(&listener->lock);
1069         if (result != ISC_R_SUCCESS) {
1070                 isc_mem_put(server->mctx, listener, sizeof(*listener));
1071                 return (ISC_R_FAILURE);
1072         }
1073
1074         isc_mem_attach(server->mctx, &listener->mctx);
1075
1076         allow = cfg_tuple_get(listen_params, "allow");
1077         if (allow != NULL && cfg_obj_islist(allow)) {
1078                 result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1079                                             aclconfctx, listener->mctx, 0,
1080                                             &new_acl);
1081         } else
1082                 result = dns_acl_any(listener->mctx, &new_acl);
1083         if (result != ISC_R_SUCCESS)
1084                 goto cleanup;
1085         dns_acl_attach(new_acl, &listener->acl);
1086         dns_acl_detach(&new_acl);
1087
1088         result = isc_task_create(ns_g_taskmgr, 0, &task);
1089         if (result != ISC_R_SUCCESS)
1090                 goto cleanup;
1091         isc_task_setname(task, "statchannel", NULL);
1092
1093         result = isc_socket_create(ns_g_socketmgr, isc_sockaddr_pf(addr),
1094                                    isc_sockettype_tcp, &sock);
1095         if (result != ISC_R_SUCCESS)
1096                 goto cleanup;
1097         isc_socket_setname(sock, "statchannel", NULL);
1098
1099 #ifndef ISC_ALLOW_MAPPED
1100         isc_socket_ipv6only(sock, ISC_TRUE);
1101 #endif
1102
1103         result = isc_socket_bind(sock, addr, ISC_SOCKET_REUSEADDRESS);
1104         if (result != ISC_R_SUCCESS)
1105                 goto cleanup;
1106
1107         result = isc_httpdmgr_create(server->mctx, sock, task, client_ok,
1108                                      destroy_listener, listener, ns_g_timermgr,
1109                                      &listener->httpdmgr);
1110         if (result != ISC_R_SUCCESS)
1111                 goto cleanup;
1112
1113 #ifdef HAVE_LIBXML2
1114         isc_httpdmgr_addurl(listener->httpdmgr, "/", render_index, server);
1115 #endif
1116         isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", render_xsl,
1117                             server);
1118
1119         *listenerp = listener;
1120         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1121                       NS_LOGMODULE_SERVER, ISC_LOG_NOTICE,
1122                       "statistics channel listening on %s", socktext);
1123
1124 cleanup:
1125         if (result != ISC_R_SUCCESS) {
1126                 if (listener->acl != NULL)
1127                         dns_acl_detach(&listener->acl);
1128                 DESTROYLOCK(&listener->lock);
1129                 isc_mem_putanddetach(&listener->mctx, listener,
1130                                      sizeof(*listener));
1131         }
1132         if (task != NULL)
1133                 isc_task_detach(&task);
1134         if (sock != NULL)
1135                 isc_socket_detach(&sock);
1136
1137         return (result);
1138 }
1139
1140 static void
1141 update_listener(ns_server_t *server, ns_statschannel_t **listenerp,
1142                 const cfg_obj_t *listen_params, const cfg_obj_t *config,
1143                 isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1144                 const char *socktext)
1145 {
1146         ns_statschannel_t *listener;
1147         const cfg_obj_t *allow = NULL;
1148         dns_acl_t *new_acl = NULL;
1149         isc_result_t result = ISC_R_SUCCESS;
1150
1151         for (listener = ISC_LIST_HEAD(server->statschannels);
1152              listener != NULL;
1153              listener = ISC_LIST_NEXT(listener, link))
1154                 if (isc_sockaddr_equal(addr, &listener->address))
1155                         break;
1156
1157         if (listener == NULL) {
1158                 *listenerp = NULL;
1159                 return;
1160         }
1161
1162         /*
1163          * Now, keep the old access list unless a new one can be made.
1164          */
1165         allow = cfg_tuple_get(listen_params, "allow");
1166         if (allow != NULL && cfg_obj_islist(allow)) {
1167                 result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1168                                             aclconfctx, listener->mctx, 0,
1169                                             &new_acl);
1170         } else
1171                 result = dns_acl_any(listener->mctx, &new_acl);
1172
1173         if (result == ISC_R_SUCCESS) {
1174                 LOCK(&listener->lock);
1175
1176                 dns_acl_detach(&listener->acl);
1177                 dns_acl_attach(new_acl, &listener->acl);
1178                 dns_acl_detach(&new_acl);
1179
1180                 UNLOCK(&listener->lock);
1181         } else {
1182                 cfg_obj_log(listen_params, ns_g_lctx, ISC_LOG_WARNING,
1183                             "couldn't install new acl for "
1184                             "statistics channel %s: %s",
1185                             socktext, isc_result_totext(result));
1186         }
1187
1188         *listenerp = listener;
1189 }
1190
1191 isc_result_t
1192 ns_statschannels_configure(ns_server_t *server, const cfg_obj_t *config,
1193                          cfg_aclconfctx_t *aclconfctx)
1194 {
1195         ns_statschannel_t *listener, *listener_next;
1196         ns_statschannellist_t new_listeners;
1197         const cfg_obj_t *statschannellist = NULL;
1198         const cfg_listelt_t *element, *element2;
1199         char socktext[ISC_SOCKADDR_FORMATSIZE];
1200
1201         RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
1202
1203         ISC_LIST_INIT(new_listeners);
1204
1205         /*
1206          * Get the list of named.conf 'statistics-channels' statements.
1207          */
1208         (void)cfg_map_get(config, "statistics-channels", &statschannellist);
1209
1210         /*
1211          * Run through the new address/port list, noting sockets that are
1212          * already being listened on and moving them to the new list.
1213          *
1214          * Identifying duplicate addr/port combinations is left to either
1215          * the underlying config code, or to the bind attempt getting an
1216          * address-in-use error.
1217          */
1218         if (statschannellist != NULL) {
1219 #ifndef HAVE_LIBXML2
1220                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1221                               NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1222                               "statistics-channels specified but not effective "
1223                               "due to missing XML library");
1224 #endif
1225
1226                 for (element = cfg_list_first(statschannellist);
1227                      element != NULL;
1228                      element = cfg_list_next(element)) {
1229                         const cfg_obj_t *statschannel;
1230                         const cfg_obj_t *listenercfg = NULL;
1231
1232                         statschannel = cfg_listelt_value(element);
1233                         (void)cfg_map_get(statschannel, "inet",
1234                                           &listenercfg);
1235                         if (listenercfg == NULL)
1236                                 continue;
1237
1238                         for (element2 = cfg_list_first(listenercfg);
1239                              element2 != NULL;
1240                              element2 = cfg_list_next(element2)) {
1241                                 const cfg_obj_t *listen_params;
1242                                 const cfg_obj_t *obj;
1243                                 isc_sockaddr_t addr;
1244
1245                                 listen_params = cfg_listelt_value(element2);
1246
1247                                 obj = cfg_tuple_get(listen_params, "address");
1248                                 addr = *cfg_obj_assockaddr(obj);
1249                                 if (isc_sockaddr_getport(&addr) == 0)
1250                                         isc_sockaddr_setport(&addr, NS_STATSCHANNEL_HTTPPORT);
1251
1252                                 isc_sockaddr_format(&addr, socktext,
1253                                                     sizeof(socktext));
1254
1255                                 isc_log_write(ns_g_lctx,
1256                                               NS_LOGCATEGORY_GENERAL,
1257                                               NS_LOGMODULE_SERVER,
1258                                               ISC_LOG_DEBUG(9),
1259                                               "processing statistics "
1260                                               "channel %s",
1261                                               socktext);
1262
1263                                 update_listener(server, &listener,
1264                                                 listen_params, config, &addr,
1265                                                 aclconfctx, socktext);
1266
1267                                 if (listener != NULL) {
1268                                         /*
1269                                          * Remove the listener from the old
1270                                          * list, so it won't be shut down.
1271                                          */
1272                                         ISC_LIST_UNLINK(server->statschannels,
1273                                                         listener, link);
1274                                 } else {
1275                                         /*
1276                                          * This is a new listener.
1277                                          */
1278                                         isc_result_t r;
1279
1280                                         r = add_listener(server, &listener,
1281                                                          listen_params, config,
1282                                                          &addr, aclconfctx,
1283                                                          socktext);
1284                                         if (r != ISC_R_SUCCESS) {
1285                                                 cfg_obj_log(listen_params,
1286                                                             ns_g_lctx,
1287                                                             ISC_LOG_WARNING,
1288                                                             "couldn't allocate "
1289                                                             "statistics channel"
1290                                                             " %s: %s",
1291                                                             socktext,
1292                                                             isc_result_totext(r));
1293                                         }
1294                                 }
1295
1296                                 if (listener != NULL)
1297                                         ISC_LIST_APPEND(new_listeners, listener,
1298                                                         link);
1299                         }
1300                 }
1301         }
1302
1303         for (listener = ISC_LIST_HEAD(server->statschannels);
1304              listener != NULL;
1305              listener = listener_next) {
1306                 listener_next = ISC_LIST_NEXT(listener, link);
1307                 ISC_LIST_UNLINK(server->statschannels, listener, link);
1308                 shutdown_listener(listener);
1309         }
1310
1311         ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
1312         return (ISC_R_SUCCESS);
1313 }
1314
1315 void
1316 ns_statschannels_shutdown(ns_server_t *server) {
1317         ns_statschannel_t *listener;
1318
1319         while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) {
1320                 ISC_LIST_UNLINK(server->statschannels, listener, link);
1321                 shutdown_listener(listener);
1322         }
1323 }
1324
1325 isc_result_t
1326 ns_stats_dump(ns_server_t *server, FILE *fp) {
1327         isc_stdtime_t now;
1328         isc_result_t result;
1329         dns_view_t *view;
1330         dns_zone_t *zone, *next;
1331         stats_dumparg_t dumparg;
1332         isc_uint64_t nsstat_values[dns_nsstatscounter_max];
1333         isc_uint64_t resstat_values[dns_resstatscounter_max];
1334         isc_uint64_t zonestat_values[dns_zonestatscounter_max];
1335         isc_uint64_t sockstat_values[isc_sockstatscounter_max];
1336
1337         RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
1338
1339         /* Set common fields */
1340         dumparg.type = statsformat_file;
1341         dumparg.arg = fp;
1342
1343         isc_stdtime_get(&now);
1344         fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
1345
1346         fprintf(fp, "++ Incoming Requests ++\n");
1347         dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg, 0);
1348
1349         fprintf(fp, "++ Incoming Queries ++\n");
1350         dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
1351                                 &dumparg, 0);
1352
1353         fprintf(fp, "++ Outgoing Queries ++\n");
1354         for (view = ISC_LIST_HEAD(server->viewlist);
1355              view != NULL;
1356              view = ISC_LIST_NEXT(view, link)) {
1357                 if (view->resquerystats == NULL)
1358                         continue;
1359                 if (strcmp(view->name, "_default") == 0)
1360                         fprintf(fp, "[View: default]\n");
1361                 else
1362                         fprintf(fp, "[View: %s]\n", view->name);
1363                 dns_rdatatypestats_dump(view->resquerystats, rdtypestat_dump,
1364                                         &dumparg, 0);
1365         }
1366
1367         fprintf(fp, "++ Name Server Statistics ++\n");
1368         (void) dump_counters(server->nsstats, statsformat_file, fp, NULL,
1369                              nsstats_desc, dns_nsstatscounter_max,
1370                              nsstats_index, nsstat_values, 0);
1371
1372         fprintf(fp, "++ Zone Maintenance Statistics ++\n");
1373         (void) dump_counters(server->zonestats, statsformat_file, fp, NULL,
1374                              zonestats_desc, dns_zonestatscounter_max,
1375                              zonestats_index, zonestat_values, 0);
1376
1377         fprintf(fp, "++ Resolver Statistics ++\n");
1378         fprintf(fp, "[Common]\n");
1379         (void) dump_counters(server->resolverstats, statsformat_file, fp, NULL,
1380                              resstats_desc, dns_resstatscounter_max,
1381                              resstats_index, resstat_values, 0);
1382         for (view = ISC_LIST_HEAD(server->viewlist);
1383              view != NULL;
1384              view = ISC_LIST_NEXT(view, link)) {
1385                 if (view->resstats == NULL)
1386                         continue;
1387                 if (strcmp(view->name, "_default") == 0)
1388                         fprintf(fp, "[View: default]\n");
1389                 else
1390                         fprintf(fp, "[View: %s]\n", view->name);
1391                 (void) dump_counters(view->resstats, statsformat_file, fp, NULL,
1392                                      resstats_desc, dns_resstatscounter_max,
1393                                      resstats_index, resstat_values, 0);
1394         }
1395
1396         fprintf(fp, "++ Cache DB RRsets ++\n");
1397         for (view = ISC_LIST_HEAD(server->viewlist);
1398              view != NULL;
1399              view = ISC_LIST_NEXT(view, link)) {
1400                 dns_stats_t *cachestats;
1401
1402                 cachestats = dns_db_getrrsetstats(view->cachedb);
1403                 if (cachestats == NULL)
1404                         continue;
1405                 if (strcmp(view->name, "_default") == 0)
1406                         fprintf(fp, "[View: default]\n");
1407                 else
1408                         fprintf(fp, "[View: %s]\n", view->name);
1409                 dns_rdatasetstats_dump(cachestats, rdatasetstats_dump, &dumparg,
1410                                        0);
1411         }
1412
1413         fprintf(fp, "++ Socket I/O Statistics ++\n");
1414         (void) dump_counters(server->sockstats, statsformat_file, fp, NULL,
1415                              sockstats_desc, isc_sockstatscounter_max,
1416                              sockstats_index, sockstat_values, 0);
1417
1418         fprintf(fp, "++ Per Zone Query Statistics ++\n");
1419         zone = NULL;
1420         for (result = dns_zone_first(server->zonemgr, &zone);
1421              result == ISC_R_SUCCESS;
1422              next = NULL, result = dns_zone_next(zone, &next), zone = next)
1423         {
1424                 isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
1425                 if (zonestats != NULL) {
1426                         char zonename[DNS_NAME_FORMATSIZE];
1427
1428                         dns_name_format(dns_zone_getorigin(zone),
1429                                         zonename, sizeof(zonename));
1430                         view = dns_zone_getview(zone);
1431
1432                         fprintf(fp, "[%s", zonename);
1433                         if (strcmp(view->name, "_default") != 0)
1434                                 fprintf(fp, " (view: %s)", view->name);
1435                         fprintf(fp, "]\n");
1436
1437                         (void) dump_counters(zonestats, statsformat_file, fp,
1438                                              NULL, nsstats_desc,
1439                                              dns_nsstatscounter_max,
1440                                              nsstats_index, nsstat_values, 0);
1441                 }
1442         }
1443
1444         fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
1445
1446         return (ISC_R_SUCCESS); /* this function currently always succeeds */
1447 }