diff --git a/CHANGES b/CHANGES
index 22334f80dd..a56787bc82 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+2122. [func] Experimental http server and statistics support
+ for named via xml.
+
2121. [func] Add a 10 slot dead masters cache (LRU) with a 600
second timeout. [RT #16553]
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
index 37e1b12c49..846ab1c70f 100644
--- a/bin/named/Makefile.in
+++ b/bin/named/Makefile.in
@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: Makefile.in,v 1.87 2005/09/05 00:10:51 marka Exp $
+# $Id: Makefile.in,v 1.88 2006/12/21 06:02:29 marka Exp $
srcdir = @srcdir@
VPATH = @srcdir@
@@ -84,6 +84,8 @@ OBJS = builtin.@O@ client.@O@ config.@O@ control.@O@ \
UOBJS = unix/os.@O@
+GENERATED = bind9.xsl.h
+
SRCS = builtin.c client.c config.c control.c \
controlconf.c interfacemgr.c \
listenlist.c log.c logconf.c main.c notify.c \
@@ -128,7 +130,12 @@ docclean manclean maintainer-clean::
rm -f ${MANOBJS}
clean distclean maintainer-clean::
- rm -f ${TARGETS} ${OBJS}
+ rm -f ${TARGETS} ${OBJS} ${GENERATED}
+
+bind9.xsl.h: bind9.xsl convertxsl.pl
+ perl convertxsl.pl < ${srcdir}/bind9.xsl > bind9.xsl.h
+
+server.o:: bind9.xsl.h
installdirs:
$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir}
diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
new file mode 100644
index 0000000000..5dec7db5f7
--- /dev/null
+++ b/bin/named/bind9.xsl
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+ BIND 9 Statistics
+
+
+
+
+
+
+
+ Times |
+
+ boot-time |
+ |
+
+
+ current-time |
+ |
+
+
+
+
+
+
+ Server statistics |
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+ Zones for View |
+
+
+ Name |
+ Class |
+ Serial |
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
diff --git a/bin/named/convertxsl.pl b/bin/named/convertxsl.pl
new file mode 100755
index 0000000000..cf1d016a31
--- /dev/null
+++ b/bin/named/convertxsl.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+print 'char msg[] = "';
+
+my $lines = '';
+
+while (<>) {
+ chomp;
+ $lines .= $_;
+}
+
+$lines =~ s/[\ \t]+/ /g;
+$lines =~ s/\>\ \\>\
#include
#include
+#include
#include
@@ -48,6 +49,7 @@ EXTERN isc_taskmgr_t * ns_g_taskmgr INIT(NULL);
EXTERN dns_dispatchmgr_t * ns_g_dispatchmgr INIT(NULL);
EXTERN isc_entropy_t * ns_g_entropy INIT(NULL);
EXTERN isc_entropy_t * ns_g_fallbackentropy INIT(NULL);
+EXTERN isc_mib_t * ns_g_mib INIT(NULL);
/*
* XXXRTH We're going to want multiple timer managers eventually. One
@@ -113,6 +115,7 @@ EXTERN const char * lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR
EXTERN const char * ns_g_username INIT(NULL);
EXTERN int ns_g_listen INIT(3);
+EXTERN isc_time_t ns_g_boottime;
#undef EXTERN
#undef INIT
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
index da357a2632..8566de92a8 100644
--- a/bin/named/include/named/server.h
+++ b/bin/named/include/named/server.h
@@ -15,21 +15,23 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: server.h,v 1.84 2006/12/04 01:52:45 marka Exp $ */
+/* $Id: server.h,v 1.85 2006/12/21 06:02:30 marka Exp $ */
#ifndef NAMED_SERVER_H
#define NAMED_SERVER_H 1
/*! \file */
+#include
#include
-#include
#include
-#include
#include
+#include
+#include
+#include
-#include
#include
+#include
#include
@@ -97,6 +99,9 @@ struct ns_server {
ns_dispatchlist_t dispatches;
dns_acache_t *acache;
+
+ isc_httpdmgr_t *httpd;
+ isc_sockaddr_t httpd_sockaddr;
};
#define NS_SERVER_MAGIC ISC_MAGIC('S','V','E','R')
diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h
index 15d9fad777..36807fa77c 100644
--- a/bin/named/include/named/types.h
+++ b/bin/named/include/named/types.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: types.h,v 1.23 2005/04/29 00:22:33 marka Exp $ */
+/* $Id: types.h,v 1.24 2006/12/21 06:02:30 marka Exp $ */
#ifndef NAMED_TYPES_H
#define NAMED_TYPES_H 1
@@ -28,6 +28,8 @@ typedef struct ns_client ns_client_t;
typedef struct ns_clientmgr ns_clientmgr_t;
typedef struct ns_query ns_query_t;
typedef struct ns_server ns_server_t;
+typedef struct ns_xmld ns_xmld_t;
+typedef struct ns_xmldmgr ns_xmldmgr_t;
typedef struct ns_interface ns_interface_t;
typedef struct ns_interfacemgr ns_interfacemgr_t;
typedef struct ns_lwresd ns_lwresd_t;
diff --git a/bin/named/main.c b/bin/named/main.c
index 566ec011d1..cc0bbe1754 100644
--- a/bin/named/main.c
+++ b/bin/named/main.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: main.c,v 1.153 2006/11/10 18:44:46 marka Exp $ */
+/* $Id: main.c,v 1.154 2006/12/21 06:02:29 marka Exp $ */
/*! \file */
@@ -670,6 +670,14 @@ setup(void) {
ns_g_conffile = absolute_conffile;
}
+ /*
+ * Record the server's startup time.
+ */
+ result = isc_time_now(&ns_g_boottime);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_time_now() failed: %s",
+ isc_result_totext(result));
+
result = create_managers();
if (result != ISC_R_SUCCESS)
ns_main_earlyfatal("create_managers() failed: %s",
diff --git a/bin/named/server.c b/bin/named/server.c
index d7d2e3cf29..c619e64ebc 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: server.c,v 1.469 2006/12/07 05:05:09 marka Exp $ */
+/* $Id: server.c,v 1.470 2006/12/21 06:02:29 marka Exp $ */
/*! \file */
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -38,6 +39,7 @@
#include
#include
#include
+#include
#include
@@ -217,6 +219,31 @@ static const struct {
{ NULL, ISC_FALSE }
};
+#ifdef HAVE_LIBXML2
+
+void
+server_httpd_create(ns_server_t *server);
+
+static isc_result_t
+render_index(const char *url, const char *querystring, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args);
+
+static isc_result_t
+render_xsl(const char *url, const char *querystring, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args);
+
+void
+tree_walk(xmlTextWriterPtr writer, isc_mib_t *mib, isc_mibnode_t *node);
+
+void
+server_generatexml(ns_server_t *server, unsigned int *buflen, xmlChar **buf);
+
+#endif /* HAVE_LIBXML2 */
+
static void
fatal(const char *msg, isc_result_t result);
@@ -2783,6 +2810,39 @@ load_configuration(const char *filename, ns_server_t *server,
INSIST(result == ISC_R_SUCCESS);
server->aclenv.match_mapped = cfg_obj_asboolean(obj);
+#ifdef HAVE_LIBXML2
+ /*
+ * [Re]configure the httpd server.
+ *
+ * If it is no longer there but was previously configured, destroy
+ * it here.
+ *
+ * If the IP address or port has changed, destroy the old server
+ * and create a new one.
+ *
+ * XXXMLG this will have to change later. Eventually, we will want
+ * XXXMLG to start it once, and add/remove listener ports as the
+ * XXXMLG user wants, which will allow more than one.
+ * XXXMLG We will also want to support IPv6 and some form of ACL.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "stats-server", &obj);
+
+ if (result == ISC_R_SUCCESS && obj != NULL) {
+ if (!isc_sockaddr_equal(cfg_obj_assockaddr(obj),
+ &server->httpd_sockaddr)) {
+ if (server->httpd != NULL)
+ isc_httpdmgr_shutdown(&server->httpd);
+ server->httpd_sockaddr = *cfg_obj_assockaddr(obj);
+ server_httpd_create(server);
+
+ }
+ } else {
+ if (server->httpd != NULL)
+ isc_httpdmgr_shutdown(&server->httpd);
+ }
+#endif
+
v4ports = NULL;
v6ports = NULL;
(void)ns_config_get(maps, "avoid-v4-udp-ports", &v4ports);
@@ -3450,6 +3510,11 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
ISC_LOG_INFO, "shutting down%s",
flush ? ": flushing changes" : "");
+#ifdef HAVE_LIBXML2
+ if (server->httpd != NULL)
+ isc_httpdmgr_shutdown(&server->httpd);
+#endif
+
ns_controls_shutdown(server->controls);
end_reserved_dispatches(server, ISC_TRUE);
@@ -3490,6 +3555,41 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
isc_event_free(&event);
}
+#ifdef HAVE_LIBXML2
+
+void
+server_httpd_create(ns_server_t *server)
+{
+ isc_socket_t *sock;
+ isc_task_t *task;
+ isc_result_t result;
+
+ task = NULL;
+ result = isc_task_create(ns_g_taskmgr, 0, &task);
+ INSIST(result == ISC_R_SUCCESS);
+
+ sock = NULL;
+ result = isc_socket_create(ns_g_socketmgr, PF_INET,
+ isc_sockettype_tcp, &sock);
+ INSIST(result == ISC_R_SUCCESS);
+
+ result = isc_socket_bind(sock, &server->httpd_sockaddr);
+ INSIST(result == ISC_R_SUCCESS);
+
+ server->httpd = NULL;
+ result = isc_httpdmgr_create(ns_g_mctx, sock, task, ns_g_timermgr,
+ &server->httpd);
+ INSIST(result == ISC_R_SUCCESS);
+
+ isc_httpdmgr_addurl(server->httpd, "/", render_index, server);
+ isc_httpdmgr_addurl(server->httpd, "/bind9.xsl", render_xsl, server);
+
+ isc_task_detach(&task);
+ isc_socket_detach(&sock);
+}
+
+#endif
+
void
ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
isc_result_t result;
@@ -3599,6 +3699,11 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
server->dispatchgen = 0;
ISC_LIST_INIT(server->dispatches);
+ /*
+ * HTTP server configuration.
+ */
+ server->httpd = NULL;
+
server->magic = NS_SERVER_MAGIC;
*serverp = server;
}
@@ -3786,6 +3891,7 @@ reload(ns_server_t *server) {
"reloading zones failed: %s",
isc_result_totext(result));
}
+
cleanup:
return (result);
}
@@ -3802,6 +3908,7 @@ reconfig(ns_server_t *server) {
"loading new zones failed: %s",
isc_result_totext(result));
}
+
cleanup: ;
}
@@ -5044,3 +5151,196 @@ ns_smf_add_message(isc_buffer_t *text) {
return (ISC_R_SUCCESS);
}
#endif /* HAVE_LIBSCF */
+
+#ifdef HAVE_LIBXML2
+
+/* XXXMLG below here sucks. */
+
+#define TRY(a) do { result = (a); INSIST(result == ISC_R_SUCCESS); } while(0);
+#define TRY0(a) do { xmlrc = (a); INSIST(xmlrc >= 0); } while(0);
+
+#define NODES 8
+#define SPACES 3
+
+void
+tree_walk(xmlTextWriterPtr writer, isc_mib_t *mib, isc_mibnode_t *node)
+{
+ char buf[128];
+ int xmlrc;
+
+ while (node != NULL) {
+ if (node->type == ISC_MIBNODETYPE_NODE)
+ if (!isc_mibnode_haschildren(node))
+ goto nextnode;
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR node->name));
+
+ switch (node->type) {
+ case ISC_MIBNODETYPE_NODE:
+ tree_walk(writer, mib, isc_mib_firstnode(mib, node));
+ break;
+ case ISC_MIBNODETYPE_UINT32:
+ sprintf(buf, "%u", *(unsigned int *)(node->data));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR buf));
+ break;
+ case ISC_MIBNODETYPE_INT32:
+ sprintf(buf, "%d", *(int *)(node->data));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR buf));
+ break;
+ case ISC_MIBNODETYPE_UINT64:
+ sprintf(buf, "%qu",
+ *(unsigned long long *)(node->data));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR buf));
+ break;
+ case ISC_MIBNODETYPE_INT64:
+ sprintf(buf, "%qd", *(long long *)(node->data));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR buf));
+ break;
+ case ISC_MIBNODETYPE_STRING:
+ sprintf(buf, "%s", *(char **)(node->data));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR buf));
+ break;
+ }
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ nextnode:
+ node = isc_mib_nextnode(mib, node);
+ }
+}
+
+void
+server_generatexml(ns_server_t *server, unsigned int *buflen, xmlChar **buf)
+{
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ssZ"];
+ isc_time_t now;
+ xmlTextWriterPtr writer;
+ xmlDocPtr doc;
+ int xmlrc;
+ dns_view_t *view;
+ int i;
+
+ isc_time_now(&now);
+ isc_time_formatISO8601(&ns_g_boottime, boottime, sizeof boottime);
+ isc_time_formatISO8601(&now, nowstr, sizeof nowstr);
+
+ writer = xmlNewTextWriterDoc(&doc, 0);
+ TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL));
+ TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet",
+ ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.xsl\""));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "isc"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR "1.0"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR "1.0"));
+
+ /*
+ * Start by rendering the views we know of here. For each view we
+ * know of, call its rendering function.
+ */
+ view = ISC_LIST_HEAD(server->viewlist);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
+ while (view != NULL) {
+ dns_view_xmlrender(view, writer, ISC_XML_RENDERALL);
+ view = ISC_LIST_NEXT(view, link);
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* views */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime);
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr);
+ xmlTextWriterEndElement(writer);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ for (i = 0; i < DNS_STATS_NCOUNTERS; i++) {
+ xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR dns_statscounter_names[i]);
+ xmlTextWriterWriteFormatString(writer,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ server->querystats[i]);
+ xmlTextWriterEndElement(writer);
+ }
+ xmlTextWriterEndElement(writer); /* counters */
+ xmlTextWriterEndElement(writer); /* server */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* statistics */
+ TRY0(xmlTextWriterEndElement(writer)); /* bind */
+ TRY0(xmlTextWriterEndElement(writer)); /* isc */
+
+ TRY0(xmlTextWriterEndDocument(writer));
+
+ xmlFreeTextWriter(writer);
+
+ xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 1);
+ xmlFreeDoc(doc);
+}
+
+static void
+wrap_xmlfree(isc_buffer_t *buffer, void *arg)
+{
+ UNUSED(arg);
+
+ xmlFree(isc_buffer_base(buffer));
+}
+
+static isc_result_t
+render_index(const char *url, const char *querystring, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args)
+{
+ unsigned char *msg;
+ unsigned int msglen;
+ ns_server_t *server = arg;
+
+ UNUSED(url);
+ UNUSED(querystring);
+
+ server_generatexml(server, &msglen, &msg);
+
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xml";
+ isc_buffer_reinit(b, msg, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_xmlfree;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+render_xsl(const char *url, const char *querystring, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args)
+{
+#include "bind9.xsl.h"
+
+ UNUSED(url);
+ UNUSED(querystring);
+ UNUSED(args);
+
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xslt+xml";
+ isc_buffer_reinit(b, msg, strlen(msg));
+ isc_buffer_add(b, strlen(msg));
+ *freecb = NULL;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LIBXML2 */
diff --git a/config.h.in b/config.h.in
index 4beb1c85f9..9a28189820 100644
--- a/config.h.in
+++ b/config.h.in
@@ -16,7 +16,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: config.h.in,v 1.83 2006/12/04 01:54:53 marka Exp $ */
+/* $Id: config.h.in,v 1.86 2006/12/22 01:46:18 marka Exp $ */
/*! \file */
@@ -199,6 +199,9 @@ int sigwait(const unsigned int *set, int *sig);
/* Define to 1 if you have the `thr' library (-lthr). */
#undef HAVE_LIBTHR
+/* Define if libxml2 was found */
+#undef HAVE_LIBXML2
+
/* Define to 1 if you have the header file. */
#undef HAVE_LINUX_CAPABILITY_H
diff --git a/configure b/configure
index 1fefcc4a98..d32495698f 100755
--- a/configure
+++ b/configure
@@ -14,7 +14,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
-# $Id: configure,v 1.409 2006/12/04 04:27:29 marka Exp $
+# $Id: configure,v 1.410 2006/12/21 06:03:37 marka Exp $
#
# Portions Copyright (C) 1996-2001 Nominum, Inc.
#
@@ -29,7 +29,7 @@
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-# From configure.in Revision: 1.421 .
+# From configure.in Revision: 1.422 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.59.
#
@@ -1078,6 +1078,7 @@ Optional Packages:
--with-gssapi=PATH Specify path for system-supplied GSSAPI
--with-randomdev=PATH Specify path for random device
--with-ptl2 on NetBSD, use the ptl2 thread library (experimental)
+ --with-libxml2=PATH Build with libxml2 library yes|no|path
--with-purify=PATH use Rational purify
--with-libtool use GNU libtool (following indented options supported)
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
@@ -7839,6 +7840,52 @@ fi
ISC_THREAD_DIR=$thread_dir
+#
+# was --with-libxml2 specified?
+#
+echo "$as_me:$LINENO: checking for libxml2 library" >&5
+echo $ECHO_N "checking for libxml2 library... $ECHO_C" >&6
+
+# Check whether --with-libxml2 or --without-libxml2 was given.
+if test "${with_libxml2+set}" = set; then
+ withval="$with_libxml2"
+ use_libxml2="$withval"
+else
+ use_libxml2="auto"
+fi;
+
+case "$use_libxml2" in
+ no)
+ DST_LIBXML2_INC=""
+ ;;
+ auto|yes)
+ libxml2_libs=`xml2-config --libs`
+ libxml2_cflags=`xml2-config --cflags`
+ ;;
+ *)
+ if test -f "$use_libxml2/bin/xml2-config" ; then
+ libxml2_libs=`$use_libxml2/bin/xml2-config --libs`
+ libxml2_cflags=`$use_libxml2/bin/xml2-config --cflags`
+ fi
+ ;;
+esac
+
+if test "X$libxml2_libs" != "X"
+then
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ CFLAGS="$CFLAGS $libxml2_cflags"
+ LIBS="$LIBS $libxml2_libs"
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBXML2 1
+_ACEOF
+
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
#
# In solaris 10, SMF can manage named service
#
@@ -9177,7 +9224,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 9180 "configure"' > conftest.$ac_ext
+ echo '#line 9227 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -10174,7 +10221,7 @@ fi
# Provide some information about the compiler.
-echo "$as_me:10177:" \
+echo "$as_me:10224:" \
"checking for Fortran 77 compiler version" >&5
ac_compiler=`set X $ac_compile; echo $2`
{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5
@@ -11235,11 +11282,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:11238: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:11285: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:11242: \$? = $ac_status" >&5
+ echo "$as_me:11289: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -11478,11 +11525,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:11481: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:11528: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:11485: \$? = $ac_status" >&5
+ echo "$as_me:11532: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -11538,11 +11585,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:11541: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:11588: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:11545: \$? = $ac_status" >&5
+ echo "$as_me:11592: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -13723,7 +13770,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext < conftest.$ac_ext <&5)
+ (eval echo "\"\$as_me:16068: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:16025: \$? = $ac_status" >&5
+ echo "$as_me:16072: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -16078,11 +16125,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:16081: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:16128: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:16085: \$? = $ac_status" >&5
+ echo "$as_me:16132: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -17439,7 +17486,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext < conftest.$ac_ext <&5)
+ (eval echo "\"\$as_me:18424: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:18381: \$? = $ac_status" >&5
+ echo "$as_me:18428: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -18434,11 +18481,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:18437: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:18484: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:18441: \$? = $ac_status" >&5
+ echo "$as_me:18488: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -20473,11 +20520,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:20476: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:20523: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:20480: \$? = $ac_status" >&5
+ echo "$as_me:20527: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -20716,11 +20763,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:20719: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:20766: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:20723: \$? = $ac_status" >&5
+ echo "$as_me:20770: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
@@ -20776,11 +20823,11 @@ else
-e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:20779: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:20826: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:20783: \$? = $ac_status" >&5
+ echo "$as_me:20830: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -22961,7 +23008,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext < conftest.$ac_ext <
#include
#include
+#include
#include
#include
@@ -100,6 +101,7 @@ struct dns_view {
isc_event_t resevent;
isc_event_t adbevent;
isc_event_t reqevent;
+
/* Configurable data. */
dns_tsig_keyring_t * statickeys;
dns_tsig_keyring_t * dynamickeys;
@@ -801,4 +803,12 @@ dns_view_freezezones(dns_view_t *view, isc_boolean_t freeze);
* Requires:
* \li 'view' is valid.
*/
+
+#ifdef HAVE_LIBXML2
+
+isc_result_t
+dns_view_xmlrender(dns_view_t *view, xmlTextWriterPtr xml, int flags);
+
+#endif
+
#endif /* DNS_VIEW_H */
diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
index 53768b9c9c..5ea270cffb 100644
--- a/lib/dns/include/dns/zone.h
+++ b/lib/dns/include/dns/zone.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: zone.h,v 1.147 2006/12/18 23:58:14 marka Exp $ */
+/* $Id: zone.h,v 1.148 2006/12/21 06:02:30 marka Exp $ */
#ifndef DNS_ZONE_H
#define DNS_ZONE_H 1
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
#include
@@ -1196,6 +1197,8 @@ dns_zone_next(dns_zone_t *zone, dns_zone_t **next);
* (result ISC_R_NOMORE).
*/
+
+
isc_result_t
dns_zone_first(dns_zonemgr_t *zmgr, dns_zone_t **first);
/*%<
@@ -1594,6 +1597,13 @@ dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg);
* delivered to 'myview'.
*/
+#ifdef HAVE_LIBXML2
+
+isc_result_t
+dns_zone_xmlrender(dns_zone_t *zone, xmlTextWriterPtr xml, int flags);
+
+#endif
+
ISC_LANG_ENDDECLS
#endif /* DNS_ZONE_H */
diff --git a/lib/dns/view.c b/lib/dns/view.c
index cddcb7a114..41cd8f131f 100644
--- a/lib/dns/view.c
+++ b/lib/dns/view.c
@@ -15,16 +15,17 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: view.c,v 1.137 2006/03/09 23:21:54 marka Exp $ */
+/* $Id: view.c,v 1.138 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
#include
#include
-#include
#include /* Required for HP/UX (and others?) */
+#include
#include
+#include
#include
#include
@@ -1364,3 +1365,47 @@ dns_view_freezezones(dns_view_t *view, isc_boolean_t value) {
REQUIRE(DNS_VIEW_VALID(view));
return (dns_zt_freezezones(view->zonetable, value));
}
+
+#ifdef HAVE_LIBXML2
+
+struct xmlarg {
+ int flags;
+ xmlTextWriterPtr xml;
+};
+
+static isc_result_t
+zone_xmlrender(dns_zone_t *zone, void *arg) {
+ struct xmlarg *xmlarg = arg;
+
+ return (dns_zone_xmlrender(zone, xmlarg->xml, xmlarg->flags));
+}
+
+isc_result_t
+dns_view_xmlrender(dns_view_t *view, xmlTextWriterPtr xml, int flags)
+{
+ struct xmlarg xmlargs;
+
+ xmlargs.flags = flags;
+ xmlargs.xml = xml;
+
+ /* XXXMLG render config data here */
+
+ if ((flags & ISC_XML_RENDERSTATS) != 0) {
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "view");
+
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(xml, ISC_XMLCHAR view->name);
+ xmlTextWriterEndElement(xml);
+
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "zones");
+ dns_zt_apply(view->zonetable, ISC_FALSE, zone_xmlrender,
+ &xmlargs);
+ xmlTextWriterEndElement(xml);
+
+ xmlTextWriterEndElement(xml);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LIBXML2 */
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index dc01d7c7ba..fee509f50a 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: zone.c,v 1.460 2006/12/18 23:58:14 marka Exp $ */
+/* $Id: zone.c,v 1.461 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
#include
#include
@@ -253,6 +254,11 @@ struct dns_zone {
isc_uint32_t notifydelay;
dns_isselffunc_t isself;
void *isselfarg;
+
+ char * strnamerd;
+ char * strname;
+ char * strrdclass;
+ char * strviewname;
};
#define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0))
@@ -444,6 +450,10 @@ static void zone_shutdown(isc_task_t *, isc_event_t *);
static void zone_loaddone(void *arg, isc_result_t result);
static isc_result_t zone_startload(dns_db_t *db, dns_zone_t *zone,
isc_time_t loadtime);
+static void zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_name_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length);
#if 0
/* ondestroy example */
@@ -571,6 +581,10 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
goto free_dblock;
zone->irefs = 0;
dns_name_init(&zone->origin, NULL);
+ zone->strnamerd = NULL;
+ zone->strname = NULL;
+ zone->strrdclass = NULL;
+ zone->strviewname = NULL;
zone->masterfile = NULL;
zone->masterformat = dns_masterformat_none;
zone->keydirectory = NULL;
@@ -699,7 +713,7 @@ zone_free(dns_zone_t *zone) {
if (zone->task != NULL)
isc_task_detach(&zone->task);
- if (zone->zmgr)
+ if (zone->zmgr != NULL)
dns_zonemgr_releasezone(zone->zmgr, zone);
/* Unmanaged objects */
@@ -737,6 +751,14 @@ zone_free(dns_zone_t *zone) {
dns_acl_detach(&zone->xfr_acl);
if (dns_name_dynamic(&zone->origin))
dns_name_free(&zone->origin, zone->mctx);
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strname != NULL)
+ isc_mem_free(zone->mctx, zone->strname);
+ if (zone->strrdclass != NULL)
+ isc_mem_free(zone->mctx, zone->strrdclass);
+ if (zone->strviewname != NULL)
+ isc_mem_free(zone->mctx, zone->strviewname);
if (zone->ssutable != NULL)
dns_ssutable_detach(&zone->ssutable);
@@ -755,6 +777,7 @@ zone_free(dns_zone_t *zone) {
*/
void
dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) {
+ char namebuf[1024];
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(rdclass != dns_rdataclass_none);
@@ -766,6 +789,17 @@ dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) {
REQUIRE(zone->rdclass == dns_rdataclass_none ||
zone->rdclass == rdclass);
zone->rdclass = rdclass;
+
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strrdclass != NULL)
+ isc_mem_free(zone->mctx, zone->strrdclass);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_rdclass_tostr(zone, namebuf, sizeof namebuf);
+ zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf);
+
UNLOCK_ZONE(zone);
}
@@ -900,12 +934,24 @@ dns_zone_setdbtype(dns_zone_t *zone,
void
dns_zone_setview(dns_zone_t *zone, dns_view_t *view) {
+ char namebuf[1024];
REQUIRE(DNS_ZONE_VALID(zone));
LOCK_ZONE(zone);
if (zone->view != NULL)
dns_view_weakdetach(&zone->view);
dns_view_weakattach(view, &zone->view);
+
+ if (zone->strviewname != NULL)
+ isc_mem_free(zone->mctx, zone->strviewname);
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_viewname_tostr(zone, namebuf, sizeof namebuf);
+ zone->strviewname = isc_mem_strdup(zone->mctx, namebuf);
+
UNLOCK_ZONE(zone);
}
@@ -921,6 +967,7 @@ dns_zone_getview(dns_zone_t *zone) {
isc_result_t
dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) {
isc_result_t result;
+ char namebuf[1024];
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(origin != NULL);
@@ -931,6 +978,17 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) {
dns_name_init(&zone->origin, NULL);
}
result = dns_name_dup(origin, zone->mctx, &zone->origin);
+
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strname != NULL)
+ isc_mem_free(zone->mctx, zone->strname);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_name_tostr(zone, namebuf, sizeof namebuf);
+ zone->strname = isc_mem_strdup(zone->mctx, namebuf);
+
UNLOCK_ZONE(zone);
return (result);
}
@@ -6001,7 +6059,7 @@ dns_zone_getjournalsize(dns_zone_t *zone) {
}
static void
-zone_tostr(dns_zone_t *zone, char *buf, size_t length) {
+zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) {
isc_result_t result = ISC_R_FAILURE;
isc_buffer_t buffer;
@@ -6032,29 +6090,88 @@ zone_tostr(dns_zone_t *zone, char *buf, size_t length) {
buf[isc_buffer_usedlength(&buffer)] = '\0';
}
+static void
+zone_name_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_result_t result = ISC_R_FAILURE;
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+ if (dns_name_dynamic(&zone->origin))
+ result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer);
+ if (result != ISC_R_SUCCESS &&
+ isc_buffer_availablelength(&buffer) >= (sizeof("") - 1))
+ isc_buffer_putstr(&buffer, "");
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+static void
+zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+ (void)dns_rdataclass_totext(zone->rdclass, &buffer);
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+static void
+zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+
+ if (zone->view == NULL) {
+ isc_buffer_putstr(&buffer, "_none");
+ } else if (strlen(zone->view->name)
+ < isc_buffer_availablelength(&buffer)) {
+ isc_buffer_putstr(&buffer, zone->view->name);
+ } else {
+ isc_buffer_putstr(&buffer, "_toolong");
+ }
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
void
dns_zone_name(dns_zone_t *zone, char *buf, size_t length) {
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(buf != NULL);
- zone_tostr(zone, buf, length);
+ zone_namerd_tostr(zone, buf, length);
}
static void
notify_log(dns_zone_t *zone, int level, const char *fmt, ...) {
va_list ap;
char message[4096];
- char namebuf[1024+32];
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
- zone_tostr(zone, namebuf, sizeof(namebuf));
-
va_start(ap, fmt);
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, DNS_LOGMODULE_ZONE,
- level, "zone %s: %s", namebuf, message);
+ level, "zone %s: %s", zone->strnamerd, message);
}
void
@@ -6062,36 +6179,30 @@ dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category,
int level, const char *fmt, ...) {
va_list ap;
char message[4096];
- char namebuf[1024+32];
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
- zone_tostr(zone, namebuf, sizeof(namebuf));
-
va_start(ap, fmt);
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE,
- level, "zone %s: %s", namebuf, message);
+ level, "zone %s: %s", zone->strnamerd, message);
}
void
dns_zone_log(dns_zone_t *zone, int level, const char *fmt, ...) {
va_list ap;
char message[4096];
- char namebuf[1024+32];
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
- zone_tostr(zone, namebuf, sizeof(namebuf));
-
va_start(ap, fmt);
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
- level, "zone %s: %s", namebuf, message);
+ level, "zone %s: %s", zone->strnamerd, message);
}
static void
@@ -6100,19 +6211,16 @@ zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel,
{
va_list ap;
char message[4096];
- char namebuf[1024+32];
int level = ISC_LOG_DEBUG(debuglevel);
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
- zone_tostr(zone, namebuf, sizeof(namebuf));
-
va_start(ap, fmt);
vsnprintf(message, sizeof(message), fmt, ap);
va_end(ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
- level, "%s: zone %s: %s", me, namebuf, message);
+ level, "%s: zone %s: %s", me, zone->strnamerd, message);
}
static int
@@ -7274,8 +7382,10 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
NULL, NULL,
zone->task, zone_timer, zone,
&zone->timer);
+
if (result != ISC_R_SUCCESS)
goto cleanup_task;
+
/*
* The timer "holds" a iref.
*/
@@ -8134,3 +8244,47 @@ dns_zone_getnotifydelay(dns_zone_t *zone) {
return (zone->notifydelay);
}
+
+#ifdef HAVE_LIBXML2
+
+isc_result_t
+dns_zone_xmlrender(dns_zone_t *zone, xmlTextWriterPtr xml, int flags)
+{
+ int i;
+
+ /* XXXMLG render config data here */
+
+ if ((flags & ISC_XML_RENDERSTATS) != 0) {
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "zone");
+
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(xml, ISC_XMLCHAR zone->strname);
+ xmlTextWriterEndElement(xml);
+
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "rdataclass");
+ xmlTextWriterWriteString(xml, ISC_XMLCHAR zone->strrdclass);
+ xmlTextWriterEndElement(xml);
+
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "serial");
+ xmlTextWriterWriteFormatString(xml, "%u", zone->serial);
+ xmlTextWriterEndElement(xml);
+
+ if (zone->counters != NULL) {
+ xmlTextWriterStartElement(xml, ISC_XMLCHAR "counters");
+ for (i = 0 ; i < DNS_STATS_NCOUNTERS ; i++) {
+ xmlTextWriterStartElement(xml,
+ ISC_XMLCHAR dns_statscounter_names[i]);
+ xmlTextWriterWriteFormatString(xml,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ zone->counters[i]);
+ xmlTextWriterEndElement(xml);
+ }
+ xmlTextWriterEndElement(xml); /* counters */
+ }
+ xmlTextWriterEndElement(xml); /* zone */
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LIBXML2 */
diff --git a/lib/dns/zt.c b/lib/dns/zt.c
index 5a32a287cf..43371e88d6 100644
--- a/lib/dns/zt.c
+++ b/lib/dns/zt.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: zt.c,v 1.43 2005/11/30 03:33:49 marka Exp $ */
+/* $Id: zt.c,v 1.44 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
@@ -63,7 +63,8 @@ static isc_result_t
freezezones(dns_zone_t *zone, void *uap);
isc_result_t
-dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
+dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp)
+{
dns_zt_t *zt;
isc_result_t result;
diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
index aea9df4e0e..27d4f66ede 100644
--- a/lib/isc/Makefile.in
+++ b/lib/isc/Makefile.in
@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: Makefile.in,v 1.87 2006/01/27 23:57:46 marka Exp $
+# $Id: Makefile.in,v 1.88 2006/12/21 06:02:30 marka Exp $
srcdir = @srcdir@
VPATH = @srcdir@
@@ -53,26 +53,30 @@ WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
OBJS = @ISC_EXTRA_OBJS@ \
assertions.@O@ base64.@O@ bitstring.@O@ buffer.@O@ \
bufferlist.@O@ commandline.@O@ error.@O@ event.@O@ \
- hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@\
- lex.@O@ lfsr.@O@ lib.@O@ log.@O@ md5.@O@ \
- mem.@O@ mutexblock.@O@ netaddr.@O@ netscope.@O@ ondestroy.@O@ \
+ hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
+ httpd.@O@ \
+ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
+ md5.@O@ mem.@O@ mib.@O@ mutexblock.@O@ \
+ netaddr.@O@ netscope.@O@ ondestroy.@O@ \
parseint.@O@ quota.@O@ random.@O@ \
ratelimiter.@O@ refcount.@O@ region.@O@ result.@O@ rwlock.@O@ \
- serial.@O@ sha1.@O@ sha2.@O@ sockaddr.@O@ string.@O@ \
- strtoul.@O@ symtab.@O@ task.@O@ taskpool.@O@ timer.@O@ \
- version.@O@ ${UNIXOBJS} ${NLSOBJS} ${THREADOBJS}
+ serial.@O@ sha1.@O@ sha2.@O@ sockaddr.@O@ \
+ string.@O@ strtoul.@O@ symtab.@O@ task.@O@ taskpool.@O@ \
+ timer.@O@ version.@O@ ${UNIXOBJS} ${NLSOBJS} ${THREADOBJS}
# Alphabetically
SRCS = @ISC_EXTRA_SRCS@ \
assertions.c base64.c bitstring.c buffer.c \
bufferlist.c commandline.c error.c event.c \
heap.c hex.c hmacmd5.c hmacsha.c \
+ httpd.c \
lex.c lfsr.c lib.c log.c \
- md5.c mem.c mutexblock.c netaddr.c netscope.c ondestroy.c \
+ md5.c mem.c mib.c mutexblock.c \
+ netaddr.c netscope.c ondestroy.c \
parseint.c quota.c random.c \
ratelimiter.c refcount.c region.c result.c rwlock.c \
- serial.c sha1.c sha2.c sockaddr.c string.c strtoul.c symtab.c \
- task.c taskpool.c timer.c version.c
+ serial.c sha1.c sha2.c sockaddr.c string.c strtoul.c \
+ symtab.c task.c taskpool.c timer.c version.c
LIBS = @LIBS@
diff --git a/lib/isc/buffer.c b/lib/isc/buffer.c
index de417dfd11..e36407fc7c 100644
--- a/lib/isc/buffer.c
+++ b/lib/isc/buffer.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: buffer.c,v 1.44 2006/12/05 00:13:48 marka Exp $ */
+/* $Id: buffer.c,v 1.45 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
@@ -39,6 +39,35 @@ isc__buffer_init(isc_buffer_t *b, const void *base, unsigned int length) {
ISC__BUFFER_INIT(b, base, length);
}
+void
+isc__buffer_initnull(isc_buffer_t *b) {
+ /*
+ * Initialize a new buffer which has no backing store. This can
+ * later be grown as needed and swapped in place.
+ */
+
+ ISC__BUFFER_INIT(b, NULL, 0);
+}
+
+void
+isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
+ /*
+ * Re-initialize the buffer enough to reconfigure the base of the
+ * buffer. We will swap in the new buffer, after copying any
+ * data we contain into the new buffer and adjusting all of our
+ * internal pointers.
+ *
+ * The buffer must not be smaller than the length of the original
+ * buffer.
+ */
+ REQUIRE(b->length <= length);
+ REQUIRE(base != NULL);
+
+ (void)memmove(base, b->base, b->length);
+ b->base = base;
+ b->length = length;
+}
+
void
isc__buffer_invalidate(isc_buffer_t *b) {
/*
diff --git a/lib/isc/httpd.c b/lib/isc/httpd.c
new file mode 100644
index 0000000000..ddb6931ac6
--- /dev/null
+++ b/lib/isc/httpd.c
@@ -0,0 +1,945 @@
+/*
+ * copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: httpd.c,v 1.4 2006/12/21 06:02:30 marka Exp $ */
+
+/*! \file */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+/*%
+ * TODO:
+ *
+ * o Put in better checks to make certain things are passed in correctly.
+ * This includes a magic number for externally-visable structures,
+ * checking for NULL-ness before dereferencing, etc.
+ * o Make the URL processing external functions which will fill-in a buffer
+ * structure we provide, or return an error and we will render a generic
+ * page and close the client.
+ */
+
+#define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
+#define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
+
+#ifdef DEBUG_HTTPD
+#define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
+#define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
+#define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
+#else
+#define ENTER(x) do { } while(0)
+#define EXIT(x) do { } while(0)
+#define NOTICE(x) do { } while(0)
+#endif
+
+#define HTTP_RECVLEN 1024
+#define HTTP_SENDGROW 1024
+#define HTTP_SEND_MAXLEN 10240
+
+/*%
+ * HTTP urls. These are the URLs we manage, and the function to call to
+ * provide the data for it. We pass in the base url (so the same function
+ * can handle multiple requests), and a structure to fill in to return a
+ * result to the client. We also pass in a pointer to be filled in for
+ * the data cleanup function.
+ */
+struct isc_httpdurl {
+ char *url;
+ isc_httpdaction_t *action;
+ void *action_arg;
+ ISC_LINK(isc_httpdurl_t) link;
+};
+
+#define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
+#define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
+
+/*% http client */
+struct isc_httpd {
+ isc_httpdmgr_t *mgr; /*%< our parent */
+ ISC_LINK(isc_httpd_t) link;
+ unsigned int state;
+ isc_socket_t *sock;
+
+ /*%
+ * Received data state.
+ */
+ char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
+ isc_uint32_t recvlen; /*%< length recv'd */
+ unsigned int method;
+ char *url;
+ char *querystring;
+ char *protocol;
+
+ /*
+ * Flags on the httpd client.
+ */
+ int flags;
+
+ /*%
+ * Transmit data state.
+ *
+ * This is the data buffer we will transmit.
+ *
+ * This free function pointer is filled in by the rendering function
+ * we call. The free function is called after the data is transmitted
+ * to the client.
+ *
+ * The bufflist is the list of buffers we are currently transmitting.
+ * The headerdata is where we render our headers to. If we run out of
+ * space when rendering a header, we will change the size of our
+ * buffer. We will not free it until we are finished, and will
+ * allocate an additional HTTP_SENDGROW bytes per header space grow.
+ *
+ * We currently use two buffers total, one for the headers (which
+ * we manage) and another for the client to fill in (which it manages,
+ * it provides the space for it, etc) -- we will pass that buffer
+ * structure back to the caller, who is responsible for managing the
+ * space it may have allocated as backing store for it. This second
+ * buffer is bodybuffer, and we only allocate the buffer itself, not
+ * the backing store.
+ */
+ isc_bufferlist_t bufflist;
+ char *headerdata; /*%< send header buf */
+ unsigned int headerlen; /*%< current header buffer size */
+ isc_buffer_t headerbuffer;
+
+ const char *mimetype;
+ unsigned int retcode;
+ const char *retmsg;
+ isc_buffer_t bodybuffer;
+ isc_httpdfree_t *freecb;
+ void *freecb_arg;
+};
+
+/*% lightweight socket manager for httpd output */
+struct isc_httpdmgr {
+ isc_mem_t *mctx;
+ isc_socket_t *sock; /*%< listening socket */
+ isc_task_t *task; /*%< owning task */
+ isc_timermgr_t *timermgr;
+
+ unsigned int flags;
+ ISC_LIST(isc_httpd_t) running; /*%< running clients */
+
+ isc_mutex_t lock;
+
+ ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
+ isc_httpdaction_t *render_404;
+};
+
+/*%
+ * HTTP methods.
+ */
+#define ISC_HTTPD_METHODUNKNOWN 0
+#define ISC_HTTPD_METHODGET 1
+#define ISC_HTTPD_METHODPOST 2
+
+/*%
+ * Client states.
+ *
+ * _IDLE The client is not doing anything at all. This state should
+ * only occur just after creation, and just before being
+ * destroyed.
+ *
+ * _RECV The client is waiting for data after issuing a socket recv().
+ *
+ * _RECVDONE Data has been received, and is being processed.
+ *
+ * _SEND All data for a response has completed, and a reply was
+ * sent via a socket send() call.
+ *
+ * _SENDDONE Send is completed.
+ *
+ * Badly formatted state table:
+ *
+ * IDLE -> RECV when client has a recv() queued.
+ *
+ * RECV -> RECVDONE when recvdone event received.
+ *
+ * RECVDONE -> SEND if the data for a reply is at hand.
+ *
+ * SEND -> RECV when a senddone event was received.
+ *
+ * At any time -> RECV on error. If RECV fails, the client will
+ * self-destroy, closing the socket and freeing memory.
+ */
+#define ISC_HTTPD_STATEIDLE 0
+#define ISC_HTTPD_STATERECV 1
+#define ISC_HTTPD_STATERECVDONE 2
+#define ISC_HTTPD_STATESEND 3
+#define ISC_HTTPD_STATESENDDONE 4
+
+#define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
+#define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
+#define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
+#define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
+
+/*%
+ * Overall magic test that means we're not idle.
+ */
+#define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
+#define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
+#define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
+#define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
+
+static void isc_httpd_accept(isc_task_t *, isc_event_t *);
+static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
+static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
+static void destroy_client(isc_httpd_t **);
+static isc_result_t process_request(isc_httpd_t *, int);
+static void httpdmgr_destroy(isc_httpdmgr_t *);
+static isc_result_t grow_headerspace(isc_httpd_t *);
+static void reset_client(isc_httpd_t *httpd);
+static isc_result_t render_404(const char *, const char *,
+ void *,
+ unsigned int *, const char **,
+ const char **, isc_buffer_t *,
+ isc_httpdfree_t **, void **);
+
+static void
+destroy_client(isc_httpd_t **httpdp)
+{
+ isc_httpd_t *httpd = *httpdp;
+ *httpdp = NULL;
+ isc_httpdmgr_t *httpdmgr = httpd->mgr;
+
+ LOCK(&httpdmgr->lock);
+
+ isc_socket_detach(&httpd->sock);
+ ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
+
+ if (httpd->headerlen > 0)
+ isc_mem_put(httpdmgr->mctx, httpd->headerdata,
+ httpd->headerlen);
+
+ isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
+
+ UNLOCK(&httpdmgr->lock);
+
+ httpdmgr_destroy(httpdmgr);
+}
+
+isc_result_t
+isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
+ isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
+{
+ isc_result_t result;
+ isc_httpdmgr_t *httpd;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(sock != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(tmgr != NULL);
+ REQUIRE(httpdp != NULL && *httpdp == NULL);
+
+ httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
+ if (httpd == NULL)
+ return (ISC_R_NOMEMORY);
+
+ isc_mutex_init(&httpd->lock);
+ httpd->mctx = NULL;
+ isc_mem_attach(mctx, &httpd->mctx);
+ httpd->sock = NULL;
+ isc_socket_attach(sock, &httpd->sock);
+ httpd->task = NULL;
+ isc_task_attach(task, &httpd->task);
+ httpd->timermgr = tmgr; /* XXHTTPG no attach function? */
+
+ ISC_LIST_INIT(httpd->running);
+ ISC_LIST_INIT(httpd->urls);
+
+ /* XXHTTPG ignore errors on isc_socket_listen() */
+ (void)isc_socket_listen(sock, SOMAXCONN);
+
+ result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
+ return (result);
+ }
+
+ httpd->render_404 = render_404;
+
+ *httpdp = httpd;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
+{
+ isc_mem_t *mctx;
+ isc_httpdurl_t *url;
+
+ ENTER("httpdmgr_destroy");
+
+ LOCK(&httpdmgr->lock);
+
+ if (!MSHUTTINGDOWN(httpdmgr)) {
+ NOTICE("httpdmgr_destroy not shutting down yet");
+ UNLOCK(&httpdmgr->lock);
+ return;
+ }
+
+ /*
+ * If all clients are not shut down, don't do anything yet.
+ */
+ if (!ISC_LIST_EMPTY(httpdmgr->running)) {
+ NOTICE("httpdmgr_destroy clients still active");
+ UNLOCK(&httpdmgr->lock);
+ return;
+ }
+
+ NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
+
+ isc_socket_detach(&httpdmgr->sock);
+ isc_task_detach(&httpdmgr->task);
+ httpdmgr->timermgr = NULL;
+
+ /*
+ * Clear out the list of all actions we know about. Just free the
+ * memory.
+ */
+ url = ISC_LIST_HEAD(httpdmgr->urls);
+ while (url != NULL) {
+ isc_mem_free(httpdmgr->mctx, url->url);
+ ISC_LIST_UNLINK(httpdmgr->urls, url, link);
+ isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
+ url = ISC_LIST_HEAD(httpdmgr->urls);
+ }
+
+ UNLOCK(&httpdmgr->lock);
+ isc_mutex_destroy(&httpdmgr->lock);
+
+ mctx = httpdmgr->mctx;
+ isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
+ isc_mem_detach(&mctx);
+
+ EXIT("httpdmgr_destroy");
+}
+
+#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
+#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
+
+static isc_result_t
+process_request(isc_httpd_t *httpd, int length)
+{
+ char *s;
+ char *p;
+ int delim;
+
+ ENTER("request");
+
+ httpd->recvlen += length;
+
+ httpd->recvbuf[httpd->recvlen] = 0;
+
+ /*
+ * If we don't find a blank line in our buffer, return that we need
+ * more data.
+ */
+ s = strstr(httpd->recvbuf, "\r\n\r\n");
+ delim = 1;
+ if (s == NULL) {
+ s = strstr(httpd->recvbuf, "\n\n");
+ delim = 2;
+ }
+ if (s == NULL)
+ return (ISC_R_NOTFOUND);
+
+ /*
+ * Determine if this is a POST or GET method. Any other values will
+ * cause an error to be returned.
+ */
+ if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
+ httpd->method = ISC_HTTPD_METHODGET;
+ p = httpd->recvbuf + 4;
+ } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
+ httpd->method = ISC_HTTPD_METHODPOST;
+ p = httpd->recvbuf + 5;
+ } else {
+ return (ISC_R_RANGE);
+ }
+
+ /*
+ * From now on, p is the start of our buffer.
+ */
+
+ /*
+ * Extract the URL.
+ */
+ s = p;
+ while (LENGTHOK(s) && BUFLENOK(s) &&
+ (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
+ s++;
+ if (!LENGTHOK(s))
+ return (ISC_R_NOTFOUND);
+ if (!BUFLENOK(s))
+ return (ISC_R_NOMEMORY);
+ *s = 0;
+
+ /*
+ * Make the URL relative.
+ */
+ if ((strncmp(p, "http:/", 6) == 0)
+ || (strncmp(p, "https:/", 7) == 0)) {
+ /* Skip first / */
+ while (*p != '/' && *p != 0)
+ p++;
+ if (*p == 0)
+ return (ISC_R_RANGE);
+ p++;
+ /* Skip second / */
+ while (*p != '/' && *p != 0)
+ p++;
+ if (*p == 0)
+ return (ISC_R_RANGE);
+ p++;
+ /* Find third / */
+ while (*p != '/' && *p != 0)
+ p++;
+ if (*p == 0) {
+ p--;
+ *p = '/';
+ }
+ }
+
+ httpd->url = p;
+ p = s + delim;
+ s = p;
+
+ /*
+ * Now, see if there is a ? mark in the URL. If so, this is
+ * part of the query string, and we will split it from the URL.
+ */
+ httpd->querystring = strchr(httpd->url, '?');
+ if (httpd->querystring != NULL) {
+ *(httpd->querystring) = 0;
+ httpd->querystring++;
+ }
+
+ /*
+ * Extract the HTTP/1.X protocol. We will bounce on anything but
+ * HTTP/1.1 for now.
+ */
+ while (LENGTHOK(s) && BUFLENOK(s) &&
+ (*s != '\n' && *s != '\r' && *s != '\0'))
+ s++;
+ if (!LENGTHOK(s))
+ return (ISC_R_NOTFOUND);
+ if (!BUFLENOK(s))
+ return (ISC_R_NOMEMORY);
+ *s = 0;
+ if ((strncmp(p, "HTTP/1.0", 8) != 0)
+ && (strncmp(p, "HTTP/1.1", 8) != 0))
+ return (ISC_R_RANGE);
+ httpd->protocol = p;
+ p = s + 1;
+ s = p;
+
+ if (strstr(s, "Connection: close") != NULL)
+ httpd->flags |= HTTPD_CLOSE;
+
+ if (strstr(s, "Host: ") != NULL)
+ httpd->flags |= HTTPD_FOUNDHOST;
+
+ /*
+ * Standards compliance hooks here.
+ */
+ if (strcmp(httpd->protocol, "HTTP/1.1") == 0
+ && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
+ return (ISC_R_RANGE);
+
+ EXIT("request");
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
+{
+ isc_result_t result;
+ isc_httpdmgr_t *httpdmgr = ev->ev_arg;
+ isc_httpd_t *httpd;
+ isc_region_t r;
+ isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
+
+ ENTER("accept");
+
+ LOCK(&httpdmgr->lock);
+ if (MSHUTTINGDOWN(httpdmgr)) {
+ NOTICE("accept shutting down, goto out");
+ goto out;
+ }
+
+ if (nev->result == ISC_R_CANCELED) {
+ NOTICE("accept canceled, goto out");
+ goto out;
+ }
+
+ if (nev->result != ISC_R_SUCCESS) {
+ /* XXHTTPG log failure */
+ NOTICE("accept returned failure, goto requeue");
+ goto requeue;
+ }
+
+ httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
+ if (httpd == NULL) {
+ /* XXHTTPG log failure */
+ NOTICE("accept failed to allocate memory, goto requeue");
+ goto requeue;
+ }
+
+ httpd->mgr = httpdmgr;
+ ISC_LINK_INIT(httpd, link);
+ ISC_LIST_APPEND(httpdmgr->running, httpd, link);
+ ISC_HTTPD_SETRECV(httpd);
+ httpd->sock = nev->newsocket;
+ httpd->flags = 0;
+
+ /*
+ * Initialize the buffer for our headers.
+ */
+ httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
+ if (httpd->headerdata == NULL) {
+ isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
+ goto requeue;
+ }
+ httpd->headerlen = HTTP_SENDGROW;
+ isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
+ httpd->headerlen);
+
+ ISC_LIST_INIT(httpd->bufflist);
+
+ isc_buffer_initnull(&httpd->bodybuffer);
+ reset_client(httpd);
+
+ r.base = (unsigned char *)httpd->recvbuf;
+ r.length = HTTP_RECVLEN - 1;
+ result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
+ httpd);
+ NOTICE("accept queued recv on socket");
+
+ requeue:
+ result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
+ httpdmgr);
+ if (result != ISC_R_SUCCESS) {
+ /* XXHTTPG what to do? Log failure... */
+ NOTICE("accept could not reaccept due to failure");
+ }
+
+ out:
+ UNLOCK(&httpdmgr->lock);
+
+ httpdmgr_destroy(httpdmgr);
+
+ isc_event_free(&ev);
+
+ EXIT("accept");
+}
+
+static isc_result_t
+render_404(const char *url, const char *querystring,
+ void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ static char msg[] = "No such URL.";
+
+ UNUSED(url);
+ UNUSED(querystring);
+ UNUSED(arg);
+
+ *retcode = 404;
+ *retmsg = "No such URL";
+ *mimetype = "text/plain";
+ isc_buffer_reinit(b, msg, strlen(msg));
+ isc_buffer_add(b, strlen(msg));
+ *freecb = NULL;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
+{
+ isc_region_t r;
+ isc_result_t result;
+ isc_httpd_t *httpd = ev->ev_arg;
+ isc_socketevent_t *sev = (isc_socketevent_t *)ev;
+ isc_httpdurl_t *url;
+ isc_time_t now;
+ char datebuf[32]; /* Only need 30, but safety first */
+
+ ENTER("recv");
+
+ INSIST(ISC_HTTPD_ISRECV(httpd));
+
+ if (sev->result != ISC_R_SUCCESS) {
+ NOTICE("recv destroying client");
+ destroy_client(&httpd);
+ goto out;
+ }
+
+ result = process_request(httpd, sev->n);
+ if (result == ISC_R_NOTFOUND) {
+ if (httpd->recvlen >= HTTP_RECVLEN - 1) {
+ destroy_client(&httpd);
+ goto out;
+ }
+ r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
+ r.length = HTTP_RECVLEN - httpd->recvlen - 1;
+ result = isc_socket_recv(httpd->sock, &r, 1, task,
+ isc_httpd_recvdone, httpd);
+ goto out;
+ } else if (result != ISC_R_SUCCESS) {
+ destroy_client(&httpd);
+ goto out;
+ }
+
+ ISC_HTTPD_SETSEND(httpd);
+
+ /*
+ * XXXMLG Call function here. Provide an add-header function
+ * which will append the common headers to a response we generate.
+ */
+ isc_buffer_initnull(&httpd->bodybuffer);
+ isc_time_now(&now);
+ isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
+ url = ISC_LIST_HEAD(httpd->mgr->urls);
+ while (url != NULL) {
+ if (strcmp(httpd->url, url->url) == 0)
+ break;
+ url = ISC_LIST_NEXT(url, link);
+ }
+ if (url == NULL)
+ result = httpd->mgr->render_404(httpd->url, httpd->querystring,
+ NULL,
+ &httpd->retcode,
+ &httpd->retmsg,
+ &httpd->mimetype,
+ &httpd->bodybuffer,
+ &httpd->freecb,
+ &httpd->freecb_arg);
+ else
+ result = url->action(httpd->url, httpd->querystring,
+ url->action_arg,
+ &httpd->retcode, &httpd->retmsg,
+ &httpd->mimetype, &httpd->bodybuffer,
+ &httpd->freecb, &httpd->freecb_arg);
+ if (result != ISC_R_SUCCESS) {
+ destroy_client(&httpd);
+ goto out;
+ }
+
+ isc_httpd_response(httpd);
+ isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
+ isc_httpd_addheader(httpd, "Date", datebuf);
+ isc_httpd_addheader(httpd, "Expires", datebuf);
+ isc_httpd_addheader(httpd, "Last-Modified", datebuf);
+ isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
+ isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
+ isc_httpd_addheader(httpd, "Server: libisc", NULL);
+ isc_httpd_addheaderuint(httpd, "Content-Length",
+ isc_buffer_usedlength(&httpd->bodybuffer));
+ isc_httpd_endheaders(httpd); /* done */
+
+ ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
+ /*
+ * Link the data buffer into our send queue, should we have any data
+ * rendered into it. If no data is present, we won't do anything
+ * with the buffer.
+ */
+ if (isc_buffer_length(&httpd->bodybuffer) > 0)
+ ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
+
+ httpd->freecb = NULL;
+ result = isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
+ isc_httpd_senddone, httpd);
+
+ out:
+ isc_event_free(&ev);
+ EXIT("recv");
+}
+
+void
+isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
+{
+ isc_httpdmgr_t *httpdmgr;
+ isc_httpd_t *httpd;
+ httpdmgr = *httpdmgrp;
+ *httpdmgrp = NULL;
+
+ ENTER("isc_httpdmgr_shutdown");
+
+ LOCK(&httpdmgr->lock);
+
+ MSETSHUTTINGDOWN(httpdmgr);
+
+ isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
+
+ httpd = ISC_LIST_HEAD(httpdmgr->running);
+ while (httpd != NULL) {
+ isc_socket_cancel(httpd->sock, httpdmgr->task,
+ ISC_SOCKCANCEL_ALL);
+ httpd = ISC_LIST_NEXT(httpd, link);
+ }
+
+ UNLOCK(&httpdmgr->lock);
+
+ EXIT("isc_httpdmgr_shutdown");
+}
+
+static isc_result_t
+grow_headerspace(isc_httpd_t *httpd)
+{
+ char *newspace;
+ unsigned int newlen;
+ isc_region_t r;
+
+ newlen = httpd->headerlen + HTTP_SENDGROW;
+ if (newlen > HTTP_SEND_MAXLEN)
+ return (ISC_R_NOSPACE);
+
+ newspace = isc_mem_get(httpd->mgr->mctx, newlen);
+ if (newspace == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_buffer_region(&httpd->headerbuffer, &r);
+ isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
+
+ isc_mem_put(httpd->mgr->mctx, r.base, r.length);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_httpd_response(isc_httpd_t *httpd)
+{
+ isc_result_t result;
+ unsigned int needlen;
+
+ needlen = strlen(httpd->protocol) + 1; /* protocol + space */
+ needlen += 3 + 1; /* room for response code, always 3 bytes */
+ needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
+
+ if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
+ result = grow_headerspace(httpd);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
+ httpd->protocol, httpd->retcode, httpd->retmsg);
+ isc_buffer_add(&httpd->headerbuffer, needlen);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
+ const char *val)
+{
+ isc_result_t result;
+ unsigned int needlen;
+
+ needlen = strlen(name); /* name itself */
+ if (val != NULL)
+ needlen += 2 + strlen(val); /* : and val */
+ needlen += 2; /* CRLF */
+
+ if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
+ result = grow_headerspace(httpd);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ if (val != NULL)
+ sprintf(isc_buffer_used(&httpd->headerbuffer),
+ "%s: %s\r\n", name, val);
+ else
+ sprintf(isc_buffer_used(&httpd->headerbuffer),
+ "%s\r\n", name);
+
+ isc_buffer_add(&httpd->headerbuffer, needlen);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_httpd_endheaders(isc_httpd_t *httpd)
+{
+ isc_result_t result;
+
+ if (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
+ result = grow_headerspace(httpd);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
+ isc_buffer_add(&httpd->headerbuffer, 2);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
+ isc_result_t result;
+ unsigned int needlen;
+ char buf[sizeof "18446744073709551616"];
+
+ sprintf(buf, "%d", val);
+
+ needlen = strlen(name); /* name itself */
+ needlen += 2 + strlen(buf); /* : and val */
+ needlen += 2; /* CRLF */
+
+ if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
+ result = grow_headerspace(httpd);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ sprintf(isc_buffer_used(&httpd->headerbuffer),
+ "%s: %s\r\n", name, buf);
+
+ isc_buffer_add(&httpd->headerbuffer, needlen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
+{
+ isc_httpd_t *httpd = ev->ev_arg;
+ isc_region_t r;
+ isc_result_t result;
+ isc_socketevent_t *sev = (isc_socketevent_t *)ev;
+
+ ENTER("senddone");
+ INSIST(ISC_HTTPD_ISSEND(httpd));
+
+ /*
+ * First, unlink our header buffer from the socket's bufflist. This
+ * is sort of an evil hack, since we know our buffer will be there,
+ * and we know it's address, so we can just remove it directly.
+ */
+ NOTICE("senddone unlinked header");
+ ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
+
+ /*
+ * We will always want to clean up our receive buffer, even if we
+ * got an error on send or we are shutting down.
+ *
+ * We will pass in the buffer only if there is data in it. If
+ * there is no data, we will pass in a NULL.
+ */
+ if (httpd->freecb != NULL) {
+ isc_buffer_t *b = NULL;
+ if (isc_buffer_length(&httpd->bodybuffer) > 0)
+ b = &httpd->bodybuffer;
+ httpd->freecb(b, httpd->freecb_arg);
+ NOTICE("senddone free callback performed");
+ }
+ if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
+ ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
+ NOTICE("senddone body buffer unlinked");
+ }
+
+ if (sev->result != ISC_R_SUCCESS) {
+ destroy_client(&httpd);
+ goto out;
+ }
+
+ if ((httpd->flags & HTTPD_CLOSE) != 0) {
+ destroy_client(&httpd);
+ goto out;
+ }
+
+ ISC_HTTPD_SETRECV(httpd);
+
+ NOTICE("senddone restarting recv on socket");
+
+ reset_client(httpd);
+
+ r.base = (unsigned char *)httpd->recvbuf;
+ r.length = HTTP_RECVLEN - 1;
+ result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
+ httpd);
+
+out:
+ isc_event_free(&ev);
+ EXIT("senddone");
+}
+
+static void
+reset_client(isc_httpd_t *httpd)
+{
+ /*
+ * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
+ * any outstanding buffers. If we have buffers, we have a leak.
+ */
+ INSIST(ISC_HTTPD_ISRECV(httpd));
+ INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
+ INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
+
+ httpd->recvbuf[0] = 0;
+ httpd->recvlen = 0;
+ httpd->method = ISC_HTTPD_METHODUNKNOWN;
+ httpd->url = NULL;
+ httpd->querystring = NULL;
+ httpd->protocol = NULL;
+ httpd->flags = 0;
+
+ isc_buffer_clear(&httpd->headerbuffer);
+ isc_buffer_invalidate(&httpd->bodybuffer);
+}
+
+isc_result_t
+isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
+ isc_httpdaction_t *func, void *arg)
+{
+ isc_httpdurl_t *item;
+
+ if (url == NULL) {
+ httpdmgr->render_404 = func;
+ return (ISC_R_SUCCESS);
+ }
+
+ item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
+ if (item == NULL)
+ return (ISC_R_NOMEMORY);
+
+ item->url = isc_mem_strdup(httpdmgr->mctx, url);
+ if (item->url == NULL) {
+ isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
+ return (ISC_R_NOMEMORY);
+ }
+
+ item->action = func;
+ item->action_arg = arg;
+ ISC_LINK_INIT(item, link);
+ ISC_LIST_APPEND(httpdmgr->urls, item, link);
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in
index 2d99a4eea4..e87b645dfc 100644
--- a/lib/isc/include/isc/Makefile.in
+++ b/lib/isc/include/isc/Makefile.in
@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: Makefile.in,v 1.58 2006/01/27 23:57:46 marka Exp $
+# $Id: Makefile.in,v 1.59 2006/12/21 06:02:30 marka Exp $
srcdir = @srcdir@
VPATH = @srcdir@
@@ -30,14 +30,18 @@ HEADERS = app.h assertions.h base64.h bitstring.h boolean.h buffer.h \
bufferlist.h commandline.h entropy.h error.h event.h \
eventclass.h file.h formatcheck.h fsaccess.h \
hash.h heap.h hex.h hmacmd5.h \
+ httpd.h \
interfaceiter.h @ISC_IPV6_H@ lang.h lex.h \
- lfsr.h lib.h list.h log.h magic.h md5.h mem.h msgcat.h msgs.h \
+ lfsr.h lib.h list.h log.h \
+ magic.h md5.h mem.h mib.h msgcat.h msgs.h \
mutexblock.h netaddr.h ondestroy.h os.h parseint.h \
print.h quota.h random.h ratelimiter.h \
refcount.h region.h resource.h \
result.h resultclass.h rwlock.h serial.h sha1.h sha2.h \
- sockaddr.h socket.h stdio.h stdlib.h string.h symtab.h \
- task.h taskpool.h timer.h types.h util.h version.h
+ sockaddr.h socket.h stdio.h stdlib.h string.h \
+ symtab.h \
+ task.h taskpool.h timer.h types.h util.h version.h \
+ xml.h
SUBDIRS =
TARGETS =
diff --git a/lib/isc/include/isc/buffer.h b/lib/isc/include/isc/buffer.h
index 3bb777337a..a8b2edf74c 100644
--- a/lib/isc/include/isc/buffer.h
+++ b/lib/isc/include/isc/buffer.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: buffer.h,v 1.47 2006/12/05 00:13:48 marka Exp $ */
+/* $Id: buffer.h,v 1.48 2006/12/21 06:02:30 marka Exp $ */
#ifndef ISC_BUFFER_H
#define ISC_BUFFER_H 1
@@ -234,6 +234,26 @@ isc__buffer_init(isc_buffer_t *b, const void *base, unsigned int length);
*
*/
+void
+isc__buffer_initnull(isc_buffer_t *b);
+/*!<
+ *\brief Initialize a buffer 'b' with a null data and zero length/
+ */
+
+void
+isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length);
+/*!<
+ * \brief Make 'b' refer to the 'length'-byte region starting at base.
+ * Any existing data will be copied.
+ *
+ * Requires:
+ *
+ *\li 'length' > 0 AND length >= previous length
+ *
+ *\li 'base' is a pointer to a sequence of 'length' bytes.
+ *
+ */
+
void
isc__buffer_invalidate(isc_buffer_t *b);
/*!<
@@ -661,6 +681,8 @@ ISC_LANG_ENDDECLS
(_b)->magic = ISC_BUFFER_MAGIC; \
} while (0)
+#define ISC__BUFFER_INITNULL(_b) ISC__BUFFER_INIT(_b, NULL, 0)
+
#define ISC__BUFFER_INVALIDATE(_b) \
do { \
(_b)->magic = 0; \
@@ -802,6 +824,7 @@ ISC_LANG_ENDDECLS
#if defined(ISC_BUFFER_USEINLINE)
#define isc_buffer_init ISC__BUFFER_INIT
+#define isc_buffer_initnull ISC__BUFFER_INITNULL
#define isc_buffer_invalidate ISC__BUFFER_INVALIDATE
#define isc_buffer_region ISC__BUFFER_REGION
#define isc_buffer_usedregion ISC__BUFFER_USEDREGION
@@ -823,6 +846,7 @@ ISC_LANG_ENDDECLS
#define isc_buffer_putuint32 ISC__BUFFER_PUTUINT32
#else
#define isc_buffer_init isc__buffer_init
+#define isc_buffer_initnull isc__buffer_initnull
#define isc_buffer_invalidate isc__buffer_invalidate
#define isc_buffer_region isc__buffer_region
#define isc_buffer_usedregion isc__buffer_usedregion
diff --git a/lib/isc/include/isc/httpd.h b/lib/isc/include/isc/httpd.h
new file mode 100644
index 0000000000..92c1948fbb
--- /dev/null
+++ b/lib/isc/include/isc/httpd.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: httpd.h,v 1.4 2006/12/21 06:02:30 marka Exp $ */
+
+#ifndef ISC_HTTPD_H
+#define ISC_HTTPD_H 1
+
+/*! \file */
+
+#include
+#include
+#include
+#include
+#include
+
+#define HTTPD_EVENTCLASS ISC_EVENTCLASS(4300)
+#define HTTPD_SHUTDOWN (HTTPD_EVENTCLASS + 0x0001)
+
+#define ISC_HTTPDMGR_FLAGSHUTTINGDOWN 0x00000001
+
+/*
+ * Create a new http daemon which will send, once every time period,
+ * a http-like header followed by HTTP data.
+ */
+isc_result_t
+isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *socket, isc_task_t *task,
+ isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp);
+
+void
+isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdp);
+
+isc_result_t
+isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
+ isc_httpdaction_t func, void *arg);
+
+isc_result_t
+isc_httpd_response(isc_httpd_t *httpd);
+
+isc_result_t
+isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
+ const char *val);
+
+isc_result_t
+isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val);
+
+isc_result_t isc_httpd_endheaders(isc_httpd_t *httpd);
+
+#endif /* ISC_HTTPD_H */
diff --git a/lib/isc/include/isc/mib.h b/lib/isc/include/isc/mib.h
new file mode 100644
index 0000000000..48213bef80
--- /dev/null
+++ b/lib/isc/include/isc/mib.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: mib.h,v 1.4 2006/12/21 06:02:30 marka Exp $ */
+
+#ifndef ISC_MIB_H
+#define ISC_MIB_H
+
+/*! \file mib.h
+ * \brief Statistics structures.
+ *
+ * \li MP:
+ * The statistics structures defined in this file handle all locking
+ * provided the API is used properly.
+ *
+ * This module defines a MIB database.
+ *
+ * Two entities are defined: providers and consumers. Providers will
+ * create and attach mib elements to the root or to other nodes, and update
+ * the MIB elements themselves. Consumers will read this data.
+ *
+ * Note that consumers cannot currently update the MIB, just read it. We
+ * may want to add this later.
+ *
+ * General assumptions about the use of the mib system, and design
+ * requirements:
+ *
+ * (1) Mib must be fast to update, with as little locking as feasable.
+ * On simple integers, this should require no locks if the system
+ * supports atomic increments and reads of integers.
+ *
+ * (2) Mib must be fast to read, also with as little locking as possible.
+ * The mib tree has a read/write lock to protect the structure of
+ * the entire mib tree.
+ *
+ * (3) The mib tree itself is expected to be updated infrequently, and
+ * so a simple read/write lock is used to protect the struture.
+ *
+ * (4) Sometimes complicated data will require special handling to protect
+ * during read or updates. When this is necessary, a pointer to a lock
+ * structure can be associated with each mib variable. This lock
+ * can be shared (for space savings).
+ *
+ * Constraints of use:
+ *
+ * (1) Each mib structure has an implied owner, which may be a module, an
+ * "object" like a zone, or other distinct object. It is required the
+ * owner of the mib node will be the one to create, modify the structure
+ * of, and delete it as needed.
+ *
+ * (2) The mib structure must be fully configured before inserting it into
+ * the tree. However, mib objects can be added and removed dynamically
+ * as required.
+ *
+ * (3) Mib have names. These names cannot contain periods, as this is
+ * used as the delimiter between names.
+ *
+ * (4) Walking a list of nodes must be done in the forward order only, never
+ * in the reverse direction. This is to avoid deadlocks, and to optimize
+ * locking. Locking will only be performed as needed while walking the
+ * mibnode list, and if the lock needed does not change it will not
+ * be unlocked until isc_mib_iterunlock() is called to explicitly
+ * unlock, or isc_mib_iterdestroy() is called to implicitly unlock it.
+ *
+ * (5) When walking the tree, or updating statistics, it is required that
+ * the mibnode locks be held for as little a time as possible. Any
+ * data should be copied quickly or the lock should be explicitly
+ * released.
+ *
+ * (6) When updating mib, the mibnode lock should be held as little as
+ * possible.
+ *
+ * (7) Even with locks there is no guarantee they will always be used, so
+ * users of this cannot assume reading two or more variables which
+ * share the same statistics lock will result in consistent data. For
+ * example, if there are three data items, "a", "b", and "total", where
+ * total = a + b, it is possible "a" will be updated using atomic
+ * operations, and then "total" will be incremented using the same
+ * operations. Atomic operations on integers will not always use the
+ * node's lock, so it is possible that total will not always be the sum
+ * of "a" and "b".
+ *
+ * (8) Consumers are read-only -- no modification is allowed. Search
+ * functions will return data that must not be modified. Removal of
+ * a node implies that the node's exact pointer is known. That is,
+ * no search is needed. Searching then removing a node is considered
+ * a misuse of this API.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ISC_MIB_MAXNAME 12 /* max mib length */
+#define ISC_MIB_NAMELEN 32 /* longest ASCII name length per node */
+#define ISC_MIB_DEFSIZE 8 /* default object set size */
+
+/*
+ * Node types.
+ */
+#define ISC_MIBNODETYPE_INVALID 0 /* Invalid node */
+#define ISC_MIBNODETYPE_NODE 1 /* node is a node */
+#define ISC_MIBNODETYPE_UINT32 2 /* node is an unsigned 32-bit integer */
+#define ISC_MIBNODETYPE_INT32 3 /* node is an signed 32-bit integer */
+#define ISC_MIBNODETYPE_UINT64 4 /* node is an unsigned 64-bit integer */
+#define ISC_MIBNODETYPE_INT64 5 /* node is an signed 64-bit integer */
+#define ISC_MIBNODETYPE_STRING 6 /* node is a string */
+
+/*
+ * Node flags. These define flags used on isc_mibnode_t.
+ */
+#define ISC_MIBNODEFLAG_PERMANENT 0x00000001 /* cannot free */
+
+typedef struct isc_mibnode isc_mibnode_t;
+typedef struct isc_mib isc_mib_t;
+
+/*
+ * This is a description of the data element we are tracking. We call this
+ * a "node."
+ */
+struct isc_mibnode {
+ isc_uint32_t type;
+ isc_uint32_t flags;
+ char *name;
+ isc_mibnode_t *parent;
+ isc_mutex_t *lock;
+ void *data; /* used if we are a data node */
+ ISC_LIST(isc_mibnode_t) nodes; /* used if we are a list node */
+ ISC_LINK(isc_mibnode_t) link;
+};
+
+/*
+ * Initialize a tree's root node.
+ */
+isc_result_t isc_mib_create(isc_mem_t *mem, isc_mib_t **rootp);
+
+/*
+ * Destroy a MIB.
+ */
+void isc_mib_destroy(isc_mib_t **rootp);
+
+/*
+ * FUNCTIONS BELOW THIS POINT SHOULD BE CALLED ONLY FROM PROVIDERS.
+ */
+
+/*
+ * Create and initialize a new node. This will allocate a node structure,
+ * and call isc_mibnode_init() to initialize it.
+ *
+ * This function allocates memory, so a corresponding call to
+ * isc_mibnode_destroy() must be made to free the memory allocated by
+ * this function and by isc_mibnode_init().
+ */
+isc_result_t isc_mibnode_create(isc_mib_t *mib, isc_uint32_t type,
+ const char *name,
+ isc_uint32_t flags, isc_mutex_t *lock,
+ void *item, unsigned int itemlen,
+ isc_mibnode_t **nodep);
+
+/*
+ * Initialize a static or pre-allocated node.
+ * This will set it up but NOT link it into the tree.
+ *
+ * This function allocates memory from the mib's memory context, so a
+ * call to isc_mibnode_invalidate() must be called to destroy it.
+ */
+isc_result_t isc_mibnode_init(isc_mib_t *mib, isc_mibnode_t *node,
+ isc_uint32_t type, const char *name,
+ isc_mutex_t *lock, isc_uint32_t flags,
+ void *item, unsigned int itemlen);
+
+void isc_mibnode_invalidate(isc_mib_t *mib, isc_mibnode_t *node);
+
+void isc_mib_add(isc_mib_t *root, isc_mibnode_t *parent, isc_mibnode_t *node);
+void isc_mib_remove(isc_mib_t *root, isc_mibnode_t *node);
+
+void isc_mibnode_destroy(isc_mib_t *mib, isc_mibnode_t **nodep);
+isc_boolean_t isc_mibnode_haschildren(isc_mibnode_t *node);
+
+/*
+ * Walk a mib. This performs a depth-first traversal of the mib tree.
+ * Locking is automatic. After walking is completed, isc_mib_release()
+ * must be called.
+ *
+ * Also, have a way to find a node's parent.
+ */
+isc_mibnode_t *isc_mib_firstnode(isc_mib_t *mib, isc_mibnode_t *parent);
+isc_mibnode_t *isc_mib_nextnode(isc_mib_t *mib, isc_mibnode_t *previous);
+isc_mibnode_t *isc_mib_parent(isc_mib_t *mib, isc_mibnode_t *node);
+
+/*
+ * Release any locks held on the mib and node.
+ * This is the last step in searching and tree-walking.
+ *
+ * If 'node' is NULL, only the tree is unlocked.
+ */
+void isc_mib_unlock(isc_mib_t *mib, isc_mibnode_t *node);
+void isc_mib_lock(isc_mib_t *mib, isc_mibnode_t *node);
+
+void
+isc_mibnode_getdata(isc_mibnode_t *node,
+ isc_mibnode_t *previous,
+ isc_boolean_t lock,
+ void *item, unsigned int itemlen);
+
+#endif /* ISC_MIB_H */
diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h
index f70dc0ae82..1865400d36 100644
--- a/lib/isc/include/isc/types.h
+++ b/lib/isc/include/isc/types.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: types.h,v 1.38 2005/12/06 16:54:49 explorer Exp $ */
+/* $Id: types.h,v 1.39 2006/12/21 06:02:30 marka Exp $ */
#ifndef ISC_TYPES_H
#define ISC_TYPES_H 1
@@ -52,6 +52,9 @@ typedef ISC_LIST(isc_event_t) isc_eventlist_t; /*%< Event List */
typedef unsigned int isc_eventtype_t; /*%< Event Type */
typedef isc_uint32_t isc_fsaccess_t; /*%< FS Access */
typedef struct isc_hash isc_hash_t; /*%< Hash */
+typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */
+typedef struct isc_httpdmgr isc_httpdmgr_t; /*%< HTTP manager */
+typedef struct isc_httpdurl isc_httpdurl_t; /*%< HTTP URL */
typedef struct isc_interface isc_interface_t; /*%< Interface */
typedef struct isc_interfaceiter isc_interfaceiter_t; /*%< Interface Iterator */
typedef struct isc_interval isc_interval_t; /*%< Interval */
@@ -87,6 +90,16 @@ typedef struct isc_timermgr isc_timermgr_t; /*%< Timer Manager */
typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *);
typedef int (*isc_sockfdwatch_t)(isc_task_t *, isc_socket_t *, void *);
+typedef void (isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function */
+typedef isc_result_t (isc_httpdaction_t)(const char *url,
+ const char *querystring,
+ void *arg,
+ unsigned int *retcode,
+ const char **retmsg,
+ const char **mimetype,
+ isc_buffer_t *body,
+ isc_httpdfree_t **freecb,
+ void **freecb_args);
/*% Resource */
typedef enum {
diff --git a/lib/isc/include/isc/xml.h b/lib/isc/include/isc/xml.h
new file mode 100644
index 0000000000..146e8c46dc
--- /dev/null
+++ b/lib/isc/include/isc/xml.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: xml.h,v 1.2 2006/12/21 06:02:30 marka Exp $ */
+
+#ifndef ISC_XML_H
+#define ISC_XML_H 1
+
+/*
+ * This file is here mostly to make it easy to add additional libxml header
+ * files as needed across all the users of this file. Rather than place
+ * these libxml includes in each file, one include makes it easy to handle
+ * the ifdef as well as adding the ability to add additional functions
+ * which may be useful.
+ */
+
+#ifdef HAVE_LIBXML2
+#include
+#include
+#endif
+
+#define ISC_XMLCHAR (const xmlChar *)
+
+#define ISC_XML_RENDERCONFIG 0x00000001 /* render config data */
+#define ISC_XML_RENDERSTATS 0x00000002 /* render stats */
+#define ISC_XML_RENDERALL 0x000000ff /* render everything */
+
+#endif /* ISC_XML_H */
diff --git a/lib/isc/mib.c b/lib/isc/mib.c
new file mode 100644
index 0000000000..07263e5b1a
--- /dev/null
+++ b/lib/isc/mib.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: mib.c,v 1.4 2006/12/21 06:02:30 marka Exp $ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * The root of a statistics tree.
+ *
+ * We use a isc_refcount_t for reference counting, which is a self-locked
+ * type. It is very efficient and may use atomic operations rather than
+ * locks.
+ */
+struct isc_mib {
+ isc_mem_t *mem;
+ isc_rwlock_t rwlock;
+ ISC_LIST(isc_mibnode_t) nodes;
+ isc_refcount_t refs;
+};
+
+isc_result_t
+isc_mib_create(isc_mem_t *mem, isc_mib_t **rootp)
+{
+ isc_result_t result;
+ isc_mib_t *root;
+
+ REQUIRE(rootp != NULL && *rootp == NULL);
+
+ root = isc_mem_get(mem, sizeof *root);
+ if (root == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_rwlock_init(&root->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mem, root, sizeof *root);
+ return (result);
+ }
+
+ ISC_LIST_INIT(root->nodes);
+
+ root->mem = NULL;
+ isc_mem_attach(mem, &root->mem);
+
+ isc_refcount_init(&root->refs, 1);
+
+ *rootp = root;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_mibnode_create(isc_mib_t *mib, isc_uint32_t type,
+ const char *name, isc_uint32_t flags,
+ isc_mutex_t *lock, void *item, unsigned int itemlen,
+ isc_mibnode_t **nodep)
+{
+ isc_result_t result;
+ isc_mibnode_t *node;
+
+ REQUIRE(nodep != NULL && *nodep == NULL);
+ REQUIRE((flags & ISC_MIBNODEFLAG_PERMANENT) == 0);
+
+ node = isc_mem_get(mib->mem, sizeof *node);
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+
+ *nodep = node;
+
+ result = isc_mibnode_init(mib, node, type, name, lock, flags,
+ item, itemlen);
+
+ return (result);
+}
+
+void
+isc_mibnode_destroy(isc_mib_t *mib, isc_mibnode_t **nodep)
+{
+ isc_mibnode_t *node;
+
+ REQUIRE(nodep != NULL && *nodep != NULL);
+
+ node = *nodep;
+ REQUIRE((node->flags & ISC_MIBNODEFLAG_PERMANENT) == 0);
+ isc_mibnode_invalidate(mib, node);
+ *nodep = NULL;
+
+ isc_mem_put(mib->mem, node, sizeof *node);
+}
+
+/*
+ * XXXMLG Should break this out into two functions, one which is used
+ * internally for most of the setting, and another which simply makes
+ * certain that the ISC_MIBNODEFLAG_PERMANENT is set when the client calls
+ * _init() and _invalidate() directly.
+ */
+void
+isc_mibnode_invalidate(isc_mib_t *mib, isc_mibnode_t *node)
+{
+ REQUIRE(node != NULL);
+ REQUIRE(!ISC_LINK_LINKED(node, link));
+
+ switch (node->type) {
+ case ISC_MIBNODETYPE_NODE:
+ REQUIRE(ISC_LIST_EMPTY(node->nodes));
+ break;
+ }
+
+ isc_mem_free(mib->mem, node->name);
+ node = ISC_MIBNODETYPE_INVALID;
+}
+
+/*
+ * Initialize a statically allocated mibnode. The caller will need to call
+ * isc_mibnode_invalidate() after it is no longer in use.
+ */
+isc_result_t
+isc_mibnode_init(isc_mib_t *mib, isc_mibnode_t *node, isc_uint32_t type,
+ const char *name, isc_mutex_t *lock, isc_uint32_t flags,
+ void *item, unsigned int itemlen)
+{
+ REQUIRE(mib != NULL);
+ REQUIRE(node != NULL);
+ REQUIRE(name != NULL);
+
+ ISC_LINK_INIT(node, link);
+ node->type = type;
+ node->name = isc_mem_strdup(mib->mem, name);
+ if (node->name == NULL)
+ return (ISC_R_NOMEMORY);
+ node->parent = NULL;
+ node->lock = NULL;
+ node->flags = flags;
+ node->lock = lock;
+ ISC_LIST_INIT(node->nodes);
+
+ switch (type) {
+ case ISC_MIBNODETYPE_NODE:
+ break;
+ case ISC_MIBNODETYPE_STRING:
+ REQUIRE(itemlen >= sizeof(char *));
+ node->data = item;
+ break;
+ case ISC_MIBNODETYPE_UINT32:
+ case ISC_MIBNODETYPE_INT32:
+ REQUIRE(itemlen >= sizeof(isc_uint32_t));
+ node->data = item;
+ break;
+ case ISC_MIBNODETYPE_UINT64:
+ case ISC_MIBNODETYPE_INT64:
+ REQUIRE(itemlen >= sizeof(isc_uint64_t));
+ node->data = item;
+ break;
+ default:
+ isc_error_runtimecheck(__FILE__, __LINE__,
+ "Invalid type");
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_mib_destroy(isc_mib_t **rootp)
+{
+ isc_mib_t *root;
+ isc_mem_t *mem;
+ unsigned int refs;
+
+ REQUIRE(rootp != NULL && *rootp != NULL);
+
+ root = *rootp;
+ rootp = NULL;
+
+ isc_refcount_decrement(&root->refs, &refs);
+ INSIST(refs == 0);
+ isc_refcount_destroy(&root->refs);
+
+ REQUIRE(ISC_LIST_EMPTY(root->nodes));
+
+ /* record and then forget the root's memory context */
+ mem = root->mem;
+ root->mem = NULL;
+
+ isc_rwlock_destroy(&root->rwlock);
+
+ isc_mem_putanddetach(&mem, root, sizeof *root);
+}
+
+void
+isc_mib_add(isc_mib_t *root, isc_mibnode_t *parent, isc_mibnode_t *node)
+{
+ REQUIRE(root != NULL);
+ REQUIRE(node != NULL);
+
+ RWLOCK(&root->rwlock, isc_rwlocktype_write);
+ isc_refcount_increment(&root->refs, NULL);
+
+ if (parent == NULL) {
+ ISC_LIST_APPEND(root->nodes, node, link);
+ node->parent = NULL;
+ } else {
+ REQUIRE(parent->type == ISC_MIBNODETYPE_NODE);
+ ISC_LIST_APPEND(parent->nodes, node, link);
+ node->parent = parent;
+ }
+
+ RWUNLOCK(&root->rwlock, isc_rwlocktype_write);
+}
+
+void
+isc_mib_remove(isc_mib_t *root, isc_mibnode_t *node)
+{
+ REQUIRE(root != NULL);
+ REQUIRE(node != NULL);
+
+ RWLOCK(&root->rwlock, isc_rwlocktype_write);
+ isc_refcount_decrement(&root->refs, NULL);
+
+ if (node->parent == NULL)
+ ISC_LIST_UNLINK(root->nodes, node, link);
+ else
+ ISC_LIST_UNLINK(node->parent->nodes, node, link);
+ node->parent = NULL;
+
+ RWUNLOCK(&root->rwlock, isc_rwlocktype_write);
+}
+
+isc_boolean_t
+isc_mibnode_haschildren(isc_mibnode_t *node)
+{
+ REQUIRE(node != NULL);
+ REQUIRE(node->type == ISC_MIBNODETYPE_NODE);
+
+ if (ISC_LIST_HEAD(node->nodes) == NULL)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+isc_mibnode_t *
+isc_mib_firstnode(isc_mib_t *mib, isc_mibnode_t *parent)
+{
+ isc_mibnode_t *node;
+
+ if (parent != NULL) {
+ node = ISC_LIST_HEAD(parent->nodes);
+ } else {
+ node = ISC_LIST_HEAD(mib->nodes);
+ }
+ if (node != NULL && node->lock != NULL)
+ LOCK(node->lock);
+
+ return (node);
+}
+
+isc_mibnode_t *
+isc_mib_nextnode(isc_mib_t *mib, isc_mibnode_t *previous)
+{
+ isc_mibnode_t *node;
+
+ UNUSED(mib);
+
+ node = ISC_LIST_NEXT(previous, link);
+
+ /*
+ * Could optimize this... XXXMLG
+ */
+ if (previous != NULL && previous->lock != NULL)
+ UNLOCK(previous->lock);
+ if (node != NULL && node->lock != NULL)
+ LOCK(node->lock);
+
+ return (node);
+}
+
+isc_mibnode_t *
+isc_mib_parent(isc_mib_t *mib, isc_mibnode_t *node)
+{
+ UNUSED(mib);
+
+ return (node->parent);
+}
+
+void
+isc_mib_lock(isc_mib_t *mib, isc_mibnode_t *node)
+{
+ RWLOCK(&mib->rwlock, isc_rwlocktype_read);
+ isc_refcount_increment(&mib->refs, NULL);
+
+ if (node != NULL && node->lock != NULL)
+ LOCK(node->lock);
+}
+
+void
+isc_mib_unlock(isc_mib_t *mib, isc_mibnode_t *node)
+{
+ if (node != NULL && node->lock != NULL)
+ UNLOCK(node->lock);
+ if (mib != NULL) {
+ RWUNLOCK(&mib->rwlock, isc_rwlocktype_read);
+ isc_refcount_decrement(&mib->refs, NULL);
+ }
+}
+
+void
+isc_mibnode_getdata(isc_mibnode_t *node, isc_mibnode_t *previous,
+ isc_boolean_t lock, void *item, unsigned int itemlen)
+{
+ if (previous != NULL && previous->lock != NULL)
+ UNLOCK(previous->lock);
+ if (lock && node != NULL && node->lock != NULL)
+ LOCK(node->lock);
+
+ switch (node->type) {
+ case ISC_MIBNODETYPE_NODE:
+ break;
+ case ISC_MIBNODETYPE_STRING:
+ REQUIRE(itemlen >= sizeof(char *));
+ *((char **)(item)) = *((char **)(node->data));
+ break;
+ case ISC_MIBNODETYPE_UINT32:
+ case ISC_MIBNODETYPE_INT32:
+ REQUIRE(itemlen >= sizeof(isc_uint32_t));
+ *((isc_uint32_t *)(item)) = *((isc_uint32_t *)(node->data));
+ break;
+ case ISC_MIBNODETYPE_UINT64:
+ case ISC_MIBNODETYPE_INT64:
+ REQUIRE(itemlen >= sizeof(isc_uint64_t));
+ *((isc_uint64_t *)(item)) = *((isc_uint64_t *)(node->data));
+ break;
+ default:
+ isc_error_runtimecheck(__FILE__, __LINE__,
+ "Invalid type");
+ }
+}
diff --git a/lib/isc/unix/include/isc/time.h b/lib/isc/unix/include/isc/time.h
index e118df90ae..fb2e254e54 100644
--- a/lib/isc/unix/include/isc/time.h
+++ b/lib/isc/unix/include/isc/time.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: time.h,v 1.32 2005/04/29 00:23:54 marka Exp $ */
+/* $Id: time.h,v 1.33 2006/12/21 06:02:30 marka Exp $ */
#ifndef ISC_TIME_H
#define ISC_TIME_H 1
@@ -110,7 +110,7 @@ isc_time_settoepoch(isc_time_t *t);
* Set 't' to the time of the epoch.
*
* Notes:
- * \li The date of the epoch is platform-dependent.
+ *\li The date of the epoch is platform-dependent.
*
* Requires:
*
@@ -199,7 +199,7 @@ isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result);
*\li 't', 'i', and 'result' are valid pointers.
*
* Returns:
- * \li Success
+ *\li Success
*\li Out of range
* The interval added to the time is too large to
* be represented in the current definition of isc_time_t.
@@ -295,7 +295,35 @@ isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len);
*
* Requires:
*\li 'len' > 0
- * \li 'buf' points to an array of at least len chars
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using a format like "Mon, 30 Aug 2000 04:06:47 GMT"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ssZ"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
*
*/
diff --git a/lib/isc/unix/time.c b/lib/isc/unix/time.c
index 51a7cadbca..aad41b1396 100644
--- a/lib/isc/unix/time.c
+++ b/lib/isc/unix/time.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: time.c,v 1.49 2005/04/29 00:23:52 marka Exp $ */
+/* $Id: time.c,v 1.50 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
@@ -412,3 +412,27 @@ isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
else
snprintf(buf, len, "99-Bad-9999 99:99:99.999");
}
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
+ INSIST(flen < len);
+}
+
+void
+isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%d-%dT%H:%M:%SZ", gmtime(&now));
+ INSIST(flen < len);
+}
diff --git a/lib/isc/win32/include/isc/time.h b/lib/isc/win32/include/isc/time.h
index ca7b85384c..ddfcc35968 100644
--- a/lib/isc/win32/include/isc/time.h
+++ b/lib/isc/win32/include/isc/time.h
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: time.h,v 1.27 2004/03/16 05:52:23 marka Exp $ */
+/* $Id: time.h,v 1.28 2006/12/21 06:02:30 marka Exp $ */
#ifndef ISC_TIME_H
#define ISC_TIME_H 1
@@ -243,6 +243,35 @@ isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len);
* 'buf' points to an array of at least len chars
*
*/
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using a format like "Mon, 30 Aug 2000 04:06:47 GMT"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ * 'len' > 0
+ * 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ssZ"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
isc_uint32_t
isc_time_seconds(const isc_time_t *t);
diff --git a/lib/isc/win32/time.c b/lib/isc/win32/time.c
index 7fa77028f6..53208bc9ed 100644
--- a/lib/isc/win32/time.c
+++ b/lib/isc/win32/time.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: time.c,v 1.38 2004/03/16 05:52:22 marka Exp $ */
+/* $Id: time.c,v 1.39 2006/12/21 06:02:30 marka Exp $ */
#include
@@ -249,3 +249,43 @@ isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
} else
snprintf(buf, len, badtime);
}
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
+ SYSTEMTIME st;
+ char DateBuf[50];
+ char TimeBuf[50];
+
+ REQUIRE(len > 0);
+ if (FileTimeToSystemTime(&t->absolute, &st)) {
+ GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "ddd',', dd-MMM-yyyy",
+ DateBuf, 50);
+ GetTimeFormat(LOCALE_USER_DEFAULT,
+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
+ &st, "hh':'mm':'ss", TimeBuf, 50);
+
+ snprintf(buf, len, "%s %s GMT", DateBuf, TimeBuf);
+ } else {
+ buf[0] = 0;
+ }
+}
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
+ SYSTEMTIME st;
+ char DateBuf[50];
+ char TimeBuf[50];
+
+ REQUIRE(len > 0);
+ if (FileTimeToSystemTime(&t->absolute, &st)) {
+ GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "yyyy-MM-dd",
+ DateBuf, 50);
+ GetTimeFormat(LOCALE_USER_DEFAULT,
+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
+ &st, "hh':'mm':'ss", TimeBuf, 50);
+
+ snprintf(buf, len, "%sT%sZ", DateBuf, TimeBuf);
+ } else {
+ buf[0] = 0;
+ }
+}
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index b135af8e7b..dad4e54ad1 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: namedconf.c,v 1.71 2006/12/04 01:52:46 marka Exp $ */
+/* $Id: namedconf.c,v 1.72 2006/12/21 06:02:30 marka Exp $ */
/*! \file */
@@ -98,6 +98,9 @@ static cfg_type_t cfg_type_portiplist;
static cfg_type_t cfg_type_querysource4;
static cfg_type_t cfg_type_querysource6;
static cfg_type_t cfg_type_querysource;
+static cfg_type_t cfg_type_addrport4;
+static cfg_type_t cfg_type_addrport6;
+static cfg_type_t cfg_type_addrport;
static cfg_type_t cfg_type_server;
static cfg_type_t cfg_type_server_key_kludge;
static cfg_type_t cfg_type_size;
@@ -654,6 +657,8 @@ options_clauses[] = {
{ "use-ixfr", &cfg_type_boolean, 0 },
{ "version", &cfg_type_qstringornone, 0 },
{ "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
+ { "stats-server", &cfg_type_addrport4, 0 },
+ { "stats-server-v6", &cfg_type_addrport6, 0 },
{ NULL, NULL, 0 }
};
@@ -1475,6 +1480,7 @@ print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK;
static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK;
+
static cfg_type_t cfg_type_querysource4 = {
"querysource4", parse_querysource, NULL, cfg_doc_terminal,
NULL, &sockaddr4wild_flags
@@ -1489,6 +1495,95 @@ static cfg_type_t cfg_type_querysource = {
"querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
};
+static isc_result_t
+parse_addrport(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isc_netaddr_t netaddr;
+ in_port_t port;
+ unsigned int have_address = 0;
+ unsigned int have_port = 0;
+ const unsigned int *flagp = type->of;
+
+ if ((*flagp & CFG_ADDR_V4OK) != 0)
+ isc_netaddr_any(&netaddr);
+ else if ((*flagp & CFG_ADDR_V6OK) != 0)
+ isc_netaddr_any6(&netaddr);
+ else
+ INSIST(0);
+
+ port = 0;
+
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ if (strcasecmp(TOKEN_STRING(pctx),
+ "address") == 0)
+ {
+ /* read "address" */
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_rawaddr(pctx, *flagp,
+ &netaddr));
+ have_address++;
+ } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
+ {
+ /* read "port" */
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_rawport(pctx,
+ CFG_ADDR_WILDOK,
+ &port));
+ have_port++;
+ } else if (have_port == 0 && have_address == 0) {
+ return (cfg_parse_sockaddr(pctx, type, ret));
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected 'address' or 'port'");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ } else
+ break;
+ }
+ if (have_address > 1 || have_port > 1 ||
+ have_address + have_port == 0) {
+ cfg_parser_error(pctx, 0, "expected one address and/or port");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_addrport, &obj));
+ isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+print_addrport(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ isc_netaddr_t na;
+ isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
+ cfg_print_chars(pctx, "address ", 8);
+ cfg_print_rawaddr(pctx, &na);
+ cfg_print_chars(pctx, " port ", 6);
+ cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
+}
+
+static cfg_type_t cfg_type_addrport4 = {
+ "addrport4", parse_addrport, NULL, cfg_doc_terminal,
+ NULL, &sockaddr4wild_flags
+};
+
+static cfg_type_t cfg_type_addrport6 = {
+ "addrport6", parse_addrport, NULL, cfg_doc_terminal,
+ NULL, &sockaddr6wild_flags
+};
+
+static cfg_type_t cfg_type_addrport = {
+ "addrport", NULL, print_addrport, NULL, &cfg_rep_sockaddr, NULL
+};
+
/*% addrmatchelt */
static isc_result_t
diff --git a/xml/statistics-schema.rnc b/xml/statistics-schema.rnc
new file mode 100644
index 0000000000..ce1bef30c2
--- /dev/null
+++ b/xml/statistics-schema.rnc
@@ -0,0 +1,65 @@
+# default namespace = "http://isc.org/isc/1.0/statistics/1.0/"
+
+start = isc
+
+isc = element isc { attribute version { xsd:string }, statistics }
+
+statistics = element statistics {
+ attribute version { xsd:string },
+ node+
+}
+
+node = element node {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:integer }
+ & attribute description { xsd:string }*
+ ),
+ ( node | statistic )+
+}
+
+statistic = ( stat-ui32 | stat-i32 | stat-ui64 | stat-i64 | stat-string )*
+
+stat-ui32 = element statistic {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:int }
+ & attribute description { xsd:string }*
+ & attribute type { xsd:string { pattern = "uint32" } }
+ ),
+ xsd:unsignedInt
+}
+
+stat-i32 = element statistic {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:int }
+ & attribute description { xsd:string }*
+ & attribute type { xsd:string { pattern = "int32" } }
+ ),
+ xsd:int
+}
+
+stat-ui64 = element statistic {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:int }
+ & attribute description { xsd:string }*
+ & attribute type { xsd:string { pattern = "uint64" } }
+ ),
+ xsd:unsignedLong
+}
+
+stat-i64 = element statistic {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:int }
+ & attribute description { xsd:string }*
+ & attribute type { xsd:string { pattern = "int64" } }
+ ),
+ xsd:long
+}
+
+stat-string = element statistic {
+ ( attribute name { xsd:string }
+ & attribute id { xsd:int }
+ & attribute description { xsd:string }*
+ & attribute type { xsd:string { pattern = "string" } }
+ ),
+ xsd:string
+}
diff --git a/xml/statistics-schema.xml b/xml/statistics-schema.xml
new file mode 100644
index 0000000000..7d9c8af54f
--- /dev/null
+++ b/xml/statistics-schema.xml
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ uint32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ int32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ uint64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ int64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ string
+
+
+
+
+
+
+