summaryrefslogtreecommitdiffstats
path: root/bin/named
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/named/Makefile.am123
-rw-r--r--bin/named/Makefile.in964
-rw-r--r--bin/named/bind9.xsl1106
-rw-r--r--bin/named/builtin.c650
-rw-r--r--bin/named/config.c1034
-rw-r--r--bin/named/control.c307
-rw-r--r--bin/named/controlconf.c1494
-rw-r--r--bin/named/dlz_dlopen_driver.c552
-rw-r--r--bin/named/fuzz.c782
-rw-r--r--bin/named/geoip.c147
-rw-r--r--bin/named/include/dlz/dlz_dlopen_driver.h20
-rw-r--r--bin/named/include/named/builtin.h24
-rw-r--r--bin/named/include/named/config.h82
-rw-r--r--bin/named/include/named/control.h108
-rw-r--r--bin/named/include/named/fuzz.h22
-rw-r--r--bin/named/include/named/geoip.h28
-rw-r--r--bin/named/include/named/globals.h163
-rw-r--r--bin/named/include/named/log.h84
-rw-r--r--bin/named/include/named/logconf.h25
-rw-r--r--bin/named/include/named/main.h36
-rw-r--r--bin/named/include/named/os.h75
-rw-r--r--bin/named/include/named/server.h396
-rw-r--r--bin/named/include/named/smf_globals.h38
-rw-r--r--bin/named/include/named/statschannel.h51
-rw-r--r--bin/named/include/named/tkeyconf.h43
-rw-r--r--bin/named/include/named/transportconf.h43
-rw-r--r--bin/named/include/named/tsigconf.h41
-rw-r--r--bin/named/include/named/types.h38
-rw-r--r--bin/named/include/named/zoneconf.h76
-rw-r--r--bin/named/log.c253
-rw-r--r--bin/named/logconf.c374
-rw-r--r--bin/named/main.c1663
-rw-r--r--bin/named/named.conf.rst67
-rw-r--r--bin/named/named.rst254
-rw-r--r--bin/named/os.c932
-rw-r--r--bin/named/server.c16755
-rw-r--r--bin/named/statschannel.c4084
-rw-r--r--bin/named/tkeyconf.c115
-rw-r--r--bin/named/transportconf.c263
-rw-r--r--bin/named/tsigconf.c181
-rw-r--r--bin/named/xsl_p.h16
-rw-r--r--bin/named/zoneconf.c2114
42 files changed, 35623 insertions, 0 deletions
diff --git a/bin/named/Makefile.am b/bin/named/Makefile.am
new file mode 100644
index 0000000..57a023b
--- /dev/null
+++ b/bin/named/Makefile.am
@@ -0,0 +1,123 @@
+include $(top_srcdir)/Makefile.top
+
+AM_CPPFLAGS += \
+ -I$(top_builddir)/include \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS) \
+ $(LIBNS_CFLAGS) \
+ $(LIBISCCC_CFLAGS) \
+ $(LIBISCCFG_CFLAGS) \
+ $(LIBBIND9_CFLAGS) \
+ $(OPENSSL_CFLAGS) \
+ $(LIBCAP_CFLAGS) \
+ $(LMDB_CFLAGS) \
+ $(MAXMINDDB_CFLAGS) \
+ $(DNSTAP_CFLAGS) \
+ $(LIBUV_CFLAGS) \
+ $(ZLIB_CFLAGS)
+
+if HAVE_JSON_C
+AM_CPPFLAGS += \
+ $(JSON_C_CFLAGS)
+endif HAVE_JSON_C
+
+if HAVE_LIBNGHTTP2
+AM_CPPFLAGS += \
+ $(LIBNGHTTP2_CFLAGS)
+endif HAVE_LIBNGHTTP2
+
+if HAVE_LIBXML2
+AM_CPPFLAGS += \
+ $(LIBXML2_CFLAGS)
+endif HAVE_LIBXML2
+
+AM_CPPFLAGS += \
+ -DNAMED_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNAMED_SYSCONFDIR=\"${sysconfdir}\"
+
+sbin_PROGRAMS = named
+
+nodist_named_SOURCES = xsl.c
+BUILT_SOURCES = xsl.c
+CLEANFILES = xsl.c
+
+EXTRA_DIST = bind9.xsl
+
+xsl.c: bind9.xsl Makefile
+ (echo 'const char xslmsg[] =' && \
+ $(SED) -e 's,\",\\\",g' \
+ -e 's,^,\",' \
+ -e 's,$$,\\n\",' && \
+ echo ";") \
+ < "${srcdir}/bind9.xsl" > $@
+
+named_SOURCES = \
+ builtin.c \
+ config.c \
+ control.c \
+ controlconf.c \
+ dlz_dlopen_driver.c \
+ fuzz.c \
+ log.c \
+ logconf.c \
+ main.c \
+ os.c \
+ server.c \
+ statschannel.c \
+ tkeyconf.c \
+ transportconf.c \
+ tsigconf.c \
+ zoneconf.c \
+ include/dlz/dlz_dlopen_driver.h \
+ include/named/builtin.h \
+ include/named/config.h \
+ include/named/control.h \
+ include/named/fuzz.h \
+ include/named/geoip.h \
+ include/named/globals.h \
+ include/named/log.h \
+ include/named/logconf.h \
+ include/named/main.h \
+ include/named/os.h \
+ include/named/server.h \
+ include/named/smf_globals.h \
+ include/named/statschannel.h \
+ include/named/tkeyconf.h \
+ include/named/transportconf.h \
+ include/named/tsigconf.h \
+ include/named/types.h \
+ include/named/zoneconf.h \
+ xsl_p.h
+
+if HAVE_GEOIP2
+AM_CPPFLAGS += \
+ -DMAXMINDDB_PREFIX=\"@MAXMINDDB_PREFIX@\"
+named_SOURCES += \
+ geoip.c
+endif
+
+named_LDADD = \
+ $(LIBISC_LIBS) \
+ $(LIBDNS_LIBS) \
+ $(LIBNS_LIBS) \
+ $(LIBISCCC_LIBS) \
+ $(LIBISCCFG_LIBS) \
+ $(LIBBIND9_LIBS) \
+ $(OPENSSL_LIBS) \
+ $(LIBCAP_LIBS) \
+ $(LMDB_LIBS) \
+ $(MAXMINDDB_LIBS) \
+ $(DNSTAP_LIBS) \
+ $(LIBUV_LIBS) \
+ $(LIBXML2_LIBS) \
+ $(ZLIB_LIBS)
+
+if HAVE_JSON_C
+named_LDADD += \
+ $(JSON_C_LIBS)
+endif HAVE_JSON_C
+
+if HAVE_LIBNGHTTP2
+named_LDADD += \
+ $(LIBNGHTTP2_LIBS)
+endif HAVE_LIBNGHTTP2
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
new file mode 100644
index 0000000..a38ce3b
--- /dev/null
+++ b/bin/named/Makefile.in
@@ -0,0 +1,964 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Hey Emacs, this is -*- makefile-automake -*- file!
+# vim: filetype=automake
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@HOST_MACOS_TRUE@am__append_1 = \
+@HOST_MACOS_TRUE@ -Wl,-flat_namespace
+
+@HAVE_JSON_C_TRUE@am__append_2 = \
+@HAVE_JSON_C_TRUE@ $(JSON_C_CFLAGS)
+
+@HAVE_LIBNGHTTP2_TRUE@am__append_3 = \
+@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_CFLAGS)
+
+@HAVE_LIBXML2_TRUE@am__append_4 = \
+@HAVE_LIBXML2_TRUE@ $(LIBXML2_CFLAGS)
+
+sbin_PROGRAMS = named$(EXEEXT)
+@HAVE_GEOIP2_TRUE@am__append_5 = \
+@HAVE_GEOIP2_TRUE@ -DMAXMINDDB_PREFIX=\"@MAXMINDDB_PREFIX@\"
+
+@HAVE_GEOIP2_TRUE@am__append_6 = \
+@HAVE_GEOIP2_TRUE@ geoip.c
+
+@HAVE_JSON_C_TRUE@am__append_7 = \
+@HAVE_JSON_C_TRUE@ $(JSON_C_LIBS)
+
+@HAVE_LIBNGHTTP2_TRUE@am__append_8 = \
+@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_LIBS)
+
+subdir = bin/named
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/ax_check_openssl.m4 \
+ $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4/ax_jemalloc.m4 \
+ $(top_srcdir)/m4/ax_lib_lmdb.m4 \
+ $(top_srcdir)/m4/ax_perl_module.m4 \
+ $(top_srcdir)/m4/ax_posix_shell.m4 \
+ $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 \
+ $(top_srcdir)/m4/ax_restore_flags.m4 \
+ $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__named_SOURCES_DIST = builtin.c config.c control.c controlconf.c \
+ dlz_dlopen_driver.c fuzz.c log.c logconf.c main.c os.c \
+ server.c statschannel.c tkeyconf.c transportconf.c tsigconf.c \
+ zoneconf.c include/dlz/dlz_dlopen_driver.h \
+ include/named/builtin.h include/named/config.h \
+ include/named/control.h include/named/fuzz.h \
+ include/named/geoip.h include/named/globals.h \
+ include/named/log.h include/named/logconf.h \
+ include/named/main.h include/named/os.h include/named/server.h \
+ include/named/smf_globals.h include/named/statschannel.h \
+ include/named/tkeyconf.h include/named/transportconf.h \
+ include/named/tsigconf.h include/named/types.h \
+ include/named/zoneconf.h xsl_p.h geoip.c
+@HAVE_GEOIP2_TRUE@am__objects_1 = geoip.$(OBJEXT)
+am_named_OBJECTS = builtin.$(OBJEXT) config.$(OBJEXT) \
+ control.$(OBJEXT) controlconf.$(OBJEXT) \
+ dlz_dlopen_driver.$(OBJEXT) fuzz.$(OBJEXT) log.$(OBJEXT) \
+ logconf.$(OBJEXT) main.$(OBJEXT) os.$(OBJEXT) server.$(OBJEXT) \
+ statschannel.$(OBJEXT) tkeyconf.$(OBJEXT) \
+ transportconf.$(OBJEXT) tsigconf.$(OBJEXT) zoneconf.$(OBJEXT) \
+ $(am__objects_1)
+nodist_named_OBJECTS = xsl.$(OBJEXT)
+named_OBJECTS = $(am_named_OBJECTS) $(nodist_named_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_JSON_C_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+@HAVE_LIBNGHTTP2_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1)
+named_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBNS_LIBS) \
+ $(LIBISCCC_LIBS) $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/builtin.Po ./$(DEPDIR)/config.Po \
+ ./$(DEPDIR)/control.Po ./$(DEPDIR)/controlconf.Po \
+ ./$(DEPDIR)/dlz_dlopen_driver.Po ./$(DEPDIR)/fuzz.Po \
+ ./$(DEPDIR)/geoip.Po ./$(DEPDIR)/log.Po ./$(DEPDIR)/logconf.Po \
+ ./$(DEPDIR)/main.Po ./$(DEPDIR)/os.Po ./$(DEPDIR)/server.Po \
+ ./$(DEPDIR)/statschannel.Po ./$(DEPDIR)/tkeyconf.Po \
+ ./$(DEPDIR)/transportconf.Po ./$(DEPDIR)/tsigconf.Po \
+ ./$(DEPDIR)/xsl.Po ./$(DEPDIR)/zoneconf.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(named_SOURCES) $(nodist_named_SOURCES)
+DIST_SOURCES = $(am__named_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__extra_recursive_targets = test-recursive unit-recursive \
+ doc-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL = @CURL@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEVELOPER_MODE = @DEVELOPER_MODE@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FSTRM_CAPTURE = @FSTRM_CAPTURE@
+FUZZ_LDFLAGS = @FUZZ_LDFLAGS@
+FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@
+JEMALLOC_LIBS = @JEMALLOC_LIBS@
+JSON_C_CFLAGS = @JSON_C_CFLAGS@
+JSON_C_LIBS = @JSON_C_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LATEXMK = @LATEXMK@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@
+LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUV_CFLAGS = @LIBUV_CFLAGS@
+LIBUV_LIBS = @LIBUV_LIBS@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@
+MAXMINDDB_LIBS = @MAXMINDDB_LIBS@
+MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@
+MKDIR_P = @MKDIR_P@
+NC = @NC@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PYTEST = @PYTEST@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+READLINE_CFLAGS = @READLINE_CFLAGS@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX_BUILD = @SPHINX_BUILD@
+STD_CFLAGS = @STD_CFLAGS@
+STD_CPPFLAGS = @STD_CPPFLAGS@
+STD_LDFLAGS = @STD_LDFLAGS@
+STRIP = @STRIP@
+TEST_CFLAGS = @TEST_CFLAGS@
+VERSION = @VERSION@
+XELATEX = @XELATEX@
+XSLTPROC = @XSLTPROC@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+AM_CFLAGS = \
+ $(STD_CFLAGS)
+
+AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \
+ -I$(srcdir)/include -I$(top_builddir)/include $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS) $(LIBNS_CFLAGS) $(LIBISCCC_CFLAGS) \
+ $(LIBISCCFG_CFLAGS) $(LIBBIND9_CFLAGS) $(OPENSSL_CFLAGS) \
+ $(LIBCAP_CFLAGS) $(LMDB_CFLAGS) $(MAXMINDDB_CFLAGS) \
+ $(DNSTAP_CFLAGS) $(LIBUV_CFLAGS) $(ZLIB_CFLAGS) \
+ $(am__append_2) $(am__append_3) $(am__append_4) \
+ -DNAMED_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNAMED_SYSCONFDIR=\"${sysconfdir}\" $(am__append_5)
+AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1)
+LDADD =
+LIBISC_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/isc/include \
+ -I$(top_builddir)/lib/isc/include
+
+LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la
+LIBDNS_CFLAGS = \
+ -I$(top_srcdir)/lib/dns/include \
+ -I$(top_builddir)/lib/dns/include
+
+LIBDNS_LIBS = \
+ $(top_builddir)/lib/dns/libdns.la
+
+LIBNS_CFLAGS = \
+ -I$(top_srcdir)/lib/ns/include
+
+LIBNS_LIBS = \
+ $(top_builddir)/lib/ns/libns.la
+
+LIBIRS_CFLAGS = \
+ -I$(top_srcdir)/lib/irs/include
+
+LIBIRS_LIBS = \
+ $(top_builddir)/lib/irs/libirs.la
+
+LIBISCCFG_CFLAGS = \
+ -I$(top_srcdir)/lib/isccfg/include
+
+LIBISCCFG_LIBS = \
+ $(top_builddir)/lib/isccfg/libisccfg.la
+
+LIBISCCC_CFLAGS = \
+ -I$(top_srcdir)/lib/isccc/include/
+
+LIBISCCC_LIBS = \
+ $(top_builddir)/lib/isccc/libisccc.la
+
+LIBBIND9_CFLAGS = \
+ -I$(top_srcdir)/lib/bind9/include
+
+LIBBIND9_LIBS = \
+ $(top_builddir)/lib/bind9/libbind9.la
+
+nodist_named_SOURCES = xsl.c
+BUILT_SOURCES = xsl.c
+CLEANFILES = xsl.c
+EXTRA_DIST = bind9.xsl
+named_SOURCES = builtin.c config.c control.c controlconf.c \
+ dlz_dlopen_driver.c fuzz.c log.c logconf.c main.c os.c \
+ server.c statschannel.c tkeyconf.c transportconf.c tsigconf.c \
+ zoneconf.c include/dlz/dlz_dlopen_driver.h \
+ include/named/builtin.h include/named/config.h \
+ include/named/control.h include/named/fuzz.h \
+ include/named/geoip.h include/named/globals.h \
+ include/named/log.h include/named/logconf.h \
+ include/named/main.h include/named/os.h include/named/server.h \
+ include/named/smf_globals.h include/named/statschannel.h \
+ include/named/tkeyconf.h include/named/transportconf.h \
+ include/named/tsigconf.h include/named/types.h \
+ include/named/zoneconf.h xsl_p.h $(am__append_6)
+named_LDADD = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBNS_LIBS) \
+ $(LIBISCCC_LIBS) $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) \
+ $(OPENSSL_LIBS) $(LIBCAP_LIBS) $(LMDB_LIBS) $(MAXMINDDB_LIBS) \
+ $(DNSTAP_LIBS) $(LIBUV_LIBS) $(LIBXML2_LIBS) $(ZLIB_LIBS) \
+ $(am__append_7) $(am__append_8)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/named/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign bin/named/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/Makefile.top $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+named$(EXEEXT): $(named_OBJECTS) $(named_DEPENDENCIES) $(EXTRA_named_DEPENDENCIES)
+ @rm -f named$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(named_OBJECTS) $(named_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/controlconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlz_dlopen_driver.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/geoip.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statschannel.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tkeyconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transportconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsigconf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xsl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zoneconf.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+test-local:
+unit-local:
+doc-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/builtin.Po
+ -rm -f ./$(DEPDIR)/config.Po
+ -rm -f ./$(DEPDIR)/control.Po
+ -rm -f ./$(DEPDIR)/controlconf.Po
+ -rm -f ./$(DEPDIR)/dlz_dlopen_driver.Po
+ -rm -f ./$(DEPDIR)/fuzz.Po
+ -rm -f ./$(DEPDIR)/geoip.Po
+ -rm -f ./$(DEPDIR)/log.Po
+ -rm -f ./$(DEPDIR)/logconf.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/os.Po
+ -rm -f ./$(DEPDIR)/server.Po
+ -rm -f ./$(DEPDIR)/statschannel.Po
+ -rm -f ./$(DEPDIR)/tkeyconf.Po
+ -rm -f ./$(DEPDIR)/transportconf.Po
+ -rm -f ./$(DEPDIR)/tsigconf.Po
+ -rm -f ./$(DEPDIR)/xsl.Po
+ -rm -f ./$(DEPDIR)/zoneconf.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+doc: doc-am
+
+doc-am: doc-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/builtin.Po
+ -rm -f ./$(DEPDIR)/config.Po
+ -rm -f ./$(DEPDIR)/control.Po
+ -rm -f ./$(DEPDIR)/controlconf.Po
+ -rm -f ./$(DEPDIR)/dlz_dlopen_driver.Po
+ -rm -f ./$(DEPDIR)/fuzz.Po
+ -rm -f ./$(DEPDIR)/geoip.Po
+ -rm -f ./$(DEPDIR)/log.Po
+ -rm -f ./$(DEPDIR)/logconf.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/os.Po
+ -rm -f ./$(DEPDIR)/server.Po
+ -rm -f ./$(DEPDIR)/statschannel.Po
+ -rm -f ./$(DEPDIR)/tkeyconf.Po
+ -rm -f ./$(DEPDIR)/transportconf.Po
+ -rm -f ./$(DEPDIR)/tsigconf.Po
+ -rm -f ./$(DEPDIR)/xsl.Po
+ -rm -f ./$(DEPDIR)/zoneconf.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+test: test-am
+
+test-am: test-local
+
+uninstall-am: uninstall-sbinPROGRAMS
+
+unit: unit-am
+
+unit-am: unit-local
+
+.MAKE: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir doc-am doc-local dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \
+ uninstall-am uninstall-sbinPROGRAMS unit-am unit-local
+
+.PRECIOUS: Makefile
+
+
+xsl.c: bind9.xsl Makefile
+ (echo 'const char xslmsg[] =' && \
+ $(SED) -e 's,\",\\\",g' \
+ -e 's,^,\",' \
+ -e 's,$$,\\n\",' && \
+ echo ";") \
+ < "${srcdir}/bind9.xsl" > $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
new file mode 100644
index 0000000..9dda61d
--- /dev/null
+++ b/bin/named/bind9.xsl
@@ -0,0 +1,1106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - SPDX-License-Identifier: MPL-2.0
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
+ <xsl:output method="html" indent="yes" version="4.0"/>
+ <!-- the version number **below** must match version in bin/named/statschannel.c -->
+ <!-- don't forget to update "/xml/v<STATS_XML_VERSION_MAJOR>" in the HTTP endpoints listed below -->
+ <xsl:template match="statistics[@version=&quot;3.13&quot;]">
+ <html>
+ <head>
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
+ <script type="text/javascript">
+ $(function($) {
+ var wid=0;
+ $('table.zones').each(function(i) { if( $(this).width() > wid ) wid = $(this).width(); return true; });
+ $('table.zones').css('min-width', wid );
+ $("h2+table,h3+table,h4+table,h2+div,h3+div,h2+script,h3+script").prev().append(' <a class="tabletoggle" href="#" style="font-size:small">Show/Hide</a>');
+ $(".tabletoggle").click(function(){
+ var n = $(this).closest("h2,h3,h4").next();
+ if (n.is("script")) { n = n.next(); }
+ if (n.is("div")) { n.toggleClass("hidden"); n = n.next(); }
+ if (n.is("table")) { n.toggleClass("hidden"); }
+ return false;
+ });
+ });
+ </script>
+
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript" src="https://www.google.com/jsapi"/>
+ <script type="text/javascript">
+
+ google.load("visualization", "1", {packages:["corechart"]});
+ google.setOnLoadCallback(loadGraphs);
+
+ var graphs=[];
+
+ function drawChart(chart_title,target,style,data) {
+ var data = google.visualization.arrayToDataTable(data);
+
+ var options = {
+ title: chart_title
+ };
+
+ var chart;
+ if (style == "barchart") {
+ chart = new google.visualization.BarChart(document.getElementById(target));
+ chart.draw(data, options);
+ } else if (style == "piechart") {
+ chart = new google.visualization.PieChart(document.getElementById(target));
+ chart.draw(data, options);
+ }
+ }
+
+ function loadGraphs(){
+ var g;
+
+ while(g = graphs.shift()){
+ // alert("going for: " + g.target);
+ if(g.data.length > 1){
+ drawChart(g.title,g.target,g.style,g.data);
+ }
+ }
+ }
+
+ <xsl:if test="server/counters[@type=&quot;qtype&quot;]/counter">
+ // Server Incoming Query Types
+ graphs.push({
+ 'title' : "Server Incoming Query Types",
+ 'target': 'chart_incoming_qtypes',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;qtype&quot;]/counter">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </xsl:if>
+
+ <xsl:if test="server/counters[@type=&quot;opcode&quot;]/counter">
+ // Server Incoming Requests by opcode
+ graphs.push({
+ 'title' : "Server Incoming Requests by DNS Opcode",
+ 'target': 'chart_incoming_opcodes',
+ 'style': 'barchart',
+ 'data': [['Opcode','Counter'],<xsl:for-each select="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]});
+ </xsl:if>
+ </script>
+ </xsl:if>
+ <style type="text/css">
+ body {
+ font-family: sans-serif;
+ background-color: #ffffff;
+ color: #000000;
+ font-size: 10pt;
+ }
+
+ .hidden{
+ display: none;
+ }
+
+ .odd{
+ background-color: #f0f0f0;
+ }
+
+ .even{
+ background-color: #ffffff;
+ }
+
+ p.footer{
+ font-style:italic;
+ color: grey;
+ }
+
+ table {
+ border-collapse: collapse;
+ border: 1px solid grey;
+ }
+
+ table.counters{
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.counters th {
+ text-align: right;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.counters td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.counters tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.info {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.info th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.info td {
+ text-align: center;
+ }
+ table.info tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.tasks {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.tasks th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.tasks td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.tasks td:nth-child(2) {
+ text-align: center;
+ }
+ table.tasks td:nth-child(4) {
+ text-align: center;
+ }
+ table.tasks tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.netstat {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.netstat th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.netstat td {
+ text-align: center;
+ }
+ table.netstat td:nth-child(4) {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.netstat td:nth-child(7) {
+ text-align: left;
+ }
+ table.netstat tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.mctx {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.mctx th {
+ text-align: center;
+ border: 1px solid grey;
+ }
+ table.mctx td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.mctx td:nth-child(-n+2) {
+ text-align: left;
+ width: 100px;
+ }
+ table.mctx tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.zones {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.zones th {
+ text-align: center;
+ border: 1px solid grey;
+ }
+ table.zones td {
+ text-align: center;
+ font-family: monospace;
+ }
+ table.zones td:nth-child(1) {
+ text-align: right;
+ }
+ table.zones td:nth-child(4) {
+ text-align: right;
+ }
+
+ .totals {
+ background-color: rgb(1,169,206);
+ color: #ffffff;
+ }
+ table.zones {
+ border: 1px solid grey;
+ }
+ table.zones td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.zones td:nth-child(2) {
+ text-align: center;
+ }
+ table.zones td:nth-child(3) {
+ text-align: left;
+ }
+ table.zones tr:hover{
+ background-color: #99ddff;
+ }
+
+ td, th {
+ padding-right: 5px;
+ padding-left: 5px;
+ border: 1px solid grey;
+ }
+
+ .header h1 {
+ color: rgb(1,169,206);
+ padding: 0px;
+ }
+
+ .content {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 4px;
+ }
+
+ .item {
+ padding: 4px;
+ text-align: right;
+ }
+
+ .value {
+ padding: 4px;
+ font-weight: bold;
+ }
+
+
+ h2 {
+ color: grey;
+ font-size: 14pt;
+ width:500px;
+ text-align:center;
+ }
+
+ h3 {
+ color: #444444;
+ font-size: 12pt;
+ width:500px;
+ text-align:center;
+ }
+ h4 {
+ color: rgb(1,169,206);
+ font-size: 10pt;
+ width:500px;
+ text-align:center;
+ }
+
+ .pie {
+ width:500px;
+ height: 500px;
+ }
+
+ </style>
+ <title>ISC BIND 9 Statistics</title>
+ </head>
+ <body>
+ <div class="header">
+ <h1>ISC Bind 9 Configuration and Statistics</h1>
+ </div>
+ <p>Alternate statistics views: <a href="/">All</a>,
+ <a href="/xml/v3/status">Status</a>,
+ <a href="/xml/v3/server">Server</a>,
+ <a href="/xml/v3/zones">Zones</a>,
+ <a href="/xml/v3/net">Network</a>,
+ <a href="/xml/v3/tasks">Tasks</a>,
+ <a href="/xml/v3/mem">Memory</a> and
+ <a href="/xml/v3/traffic">Traffic Size</a></p>
+ <hr/>
+ <h2>Server Status</h2>
+ <table class="info">
+ <tr class="odd">
+ <th>Boot time:</th>
+ <td>
+ <xsl:value-of select="server/boot-time"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Last reconfigured:</th>
+ <td>
+ <xsl:value-of select="server/config-time"/>
+ </td>
+ </tr>
+ <tr class="odd">
+ <th>Current time:</th>
+ <td>
+ <xsl:value-of select="server/current-time"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Server version:</th>
+ <td>
+ <xsl:value-of select="server/version"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ <xsl:if test="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0]">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <h2>Incoming Requests by DNS Opcode</h2>
+ <!-- Non Mozilla specific markup -->
+ <div class="pie" id="chart_incoming_opcodes">
+ [cannot display chart]
+ </div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class0">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class0}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <th class="totals">Total:</th>
+ <td class="totals">
+ <xsl:value-of select="sum(server/counters[@type=&quot;opcode&quot;]/counter)"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <h3>Incoming Queries by Query Type</h3>
+ <div class="pie" id="chart_incoming_qtypes">
+ [cannot display chart]
+ </div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <th class="totals">Total:</th>
+ <td class="totals">
+ <xsl:value-of select="sum(server/counters[@type=&quot;qtype&quot;]/counter)"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]">
+ <h2>Outgoing Queries per view</h2>
+ <xsl:for-each select="views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Outgoing Queries for view: <xsl:value-of select="@name"/>",
+ 'target': 'chart_outgoing_queries_view_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;resqtype&quot;]/counter">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_outgoing_queries_view_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;resqtype&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class1">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class1}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">
+ <h2>Server Statistics</h2>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title' : "Server Counters",
+ 'target': 'chart_server_nsstat_restype',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <div class="pie" id="chart_server_nsstat_restype">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class2">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class2}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <h2>Zone Maintenance Statistics</h2>
+ <script type="text/javascript">
+ graphs.push({
+ 'title' : "Zone Maintenance Stats",
+ 'target': 'chart_server_zone_maint',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <!-- Non Mozilla specific markup -->
+ <div class="pie" id="chart_server_zone_maint">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;zonestat&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class3">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class3}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;resstat&quot;]/counter[.&gt;0]">
+ <h2>Resolver Statistics (Common)</h2>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;resstat&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class4">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class4}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;resstats&quot;]/counter[.&gt;0]">
+ <h3>Resolver Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;resstats&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]">
+ <h3>ADB Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]">
+ <h3>Cache Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="cache/rrset">
+ <h3>Cache DB RRsets for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="cache/rrset">
+ <xsl:variable name="css-class6">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class6}">
+ <th>
+ <xsl:value-of select="name"/>
+ </th>
+ <td>
+ <xsl:value-of select="counter"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:if test="traffic//udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic//udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0] or traffic//tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic//tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h2>Traffic Size Statistics</h2>
+ </xsl:if>
+ <xsl:if test="traffic//udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <h4>UDP Requests Received</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic//udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th><xsl:value-of select="local-name(../../..)"/></th>
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic//udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h4>UDP Responses Sent</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic//udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th><xsl:value-of select="local-name(../../..)"/></th>
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic//tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <h4>TCP Requests Received</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic//tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th><xsl:value-of select="local-name(../../..)"/></th>
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic//tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h4>TCP Responses Sent</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic//tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th><xsl:value-of select="local-name(../../..)"/></th>
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]">
+ <h2>Socket I/O Statistics</h2>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="views/view/zones/zone">
+ <xsl:for-each select="views/view">
+ <h3>Zones for View <xsl:value-of select="@name"/></h3>
+ <table class="zones">
+ <thead><tr><th>Name</th><th>Class</th><th>Type</th><th>Serial</th><th>Loaded</th><th>Expires</th><th>Refresh</th></tr></thead>
+ <tbody>
+ <xsl:for-each select="zones/zone">
+ <xsl:variable name="css-class15">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class15}">
+ <td><xsl:value-of select="@name"/></td>
+ <td><xsl:value-of select="@rdataclass"/></td>
+ <td><xsl:value-of select="type"/></td>
+ <td><xsl:value-of select="serial"/></td>
+ <td><xsl:value-of select="loaded"/></td>
+ <td><xsl:value-of select="expires"/></td>
+ <td><xsl:value-of select="refresh"/></td></tr>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]">
+ <h2>Received QTYPES per view/zone</h2>
+ <xsl:for-each select="views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:variable name="thisview">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="zones/zone">
+ <xsl:if test="counters[@type=&quot;qtype&quot;]/counter[count(.) &gt; 0]">
+ <h4>Zone <xsl:value-of select="@name"/></h4>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Query types for zone <xsl:value-of select="@name"/>",
+ 'target': 'chart_qtype_<xsl:value-of select="../../@name"/>_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;qtype&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_qtype_{$thisview}_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:sort select="."/>
+ <xsl:variable name="css-class10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class10}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]">
+ <h2>Response Codes per view/zone</h2>
+ <xsl:for-each select="views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:variable name="thisview2">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="zones/zone">
+ <xsl:if test="counters[@type=&quot;rcode&quot;]/counter[. &gt; 0]">
+ <h4>Zone <xsl:value-of select="@name"/></h4>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Response codes for zone <xsl:value-of select="@name"/>",
+ 'target': 'chart_rescode_<xsl:value-of select="../../@name"/>_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_rescode_{$thisview2}_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">
+ <xsl:sort select="."/>
+ <xsl:variable name="css-class11">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class11}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="views/view[zones/zone/counters[@type=&quot;gluecache&quot;]/counter &gt;0]">
+ <h2>Glue cache statistics</h2>
+ <xsl:for-each select="views/view[zones/zone/counters[@type=&quot;gluecache&quot;]/counter &gt;0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:variable name="thisview2">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="zones/zone">
+ <xsl:if test="counters[@type=&quot;gluecache&quot;]/counter[. &gt; 0]">
+ <h4>Zone <xsl:value-of select="@name"/></h4>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;gluecache&quot;]/counter[. &gt; 0]">
+ <xsl:sort select="."/>
+ <xsl:variable name="css-class11">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class11}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="taskmgr/thread-model/type">
+ <h2>Task Manager Configuration</h2>
+ <table class="counters">
+ <tr>
+ <th class="even">Thread-Model</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/type"/>
+ </td>
+ </tr>
+ <tr class="odd">
+ <th>Worker Threads</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/worker-threads"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Default Quantum</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/default-quantum"/>
+ </td>
+ </tr>
+ <tr class="odd">
+ <th>Tasks Running</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/tasks-running"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Tasks Ready</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/tasks-ready"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="taskmgr/tasks/task">
+ <h2>Tasks</h2>
+ <table class="tasks">
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>References</th>
+ <th>State</th>
+ <th>Quantum</th>
+ <th>Events</th>
+ </tr>
+ <xsl:for-each select="taskmgr/tasks/task">
+ <xsl:sort select="name"/>
+ <xsl:variable name="css-class14">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class14}">
+ <td>
+ <xsl:value-of select="id"/>
+ </td>
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="references"/>
+ </td>
+ <td>
+ <xsl:value-of select="state"/>
+ </td>
+ <td>
+ <xsl:value-of select="quantum"/>
+ </td>
+ <td>
+ <xsl:value-of select="events"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="memory/summary">
+ <h2>Memory Usage Summary</h2>
+ <table class="counters">
+ <xsl:for-each select="memory/summary/*">
+ <xsl:variable name="css-class13">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class13}">
+ <th>
+ <xsl:value-of select="name()"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="memory/contexts/context">
+ <h2>Memory Contexts</h2>
+ <table class="mctx">
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>References</th>
+ <th>TotalUse</th>
+ <th>InUse</th>
+ <th>MaxUse</th>
+ <th>Malloced</th>
+ <th>MaxMalloced</th>
+ <th>BlockSize</th>
+ <th>Pools</th>
+ <th>HiWater</th>
+ <th>LoWater</th>
+ </tr>
+ <xsl:for-each select="memory/contexts/context">
+ <xsl:sort select="total" data-type="number" order="descending"/>
+ <xsl:variable name="css-class14">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class14}">
+ <td>
+ <xsl:value-of select="id"/>
+ </td>
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="references"/>
+ </td>
+ <td>
+ <xsl:value-of select="total"/>
+ </td>
+ <td>
+ <xsl:value-of select="inuse"/>
+ </td>
+ <td>
+ <xsl:value-of select="maxinuse"/>
+ </td>
+ <td>
+ <xsl:value-of select="malloced"/>
+ </td>
+ <td>
+ <xsl:value-of select="maxmalloced"/>
+ </td>
+ <td>
+ <xsl:value-of select="blocksize"/>
+ </td>
+ <td>
+ <xsl:value-of select="pools"/>
+ </td>
+ <td>
+ <xsl:value-of select="hiwater"/>
+ </td>
+ <td>
+ <xsl:value-of select="lowater"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ <hr/>
+ <p class="footer">Internet Systems Consortium Inc.<br/><a href="http://www.isc.org">http://www.isc.org</a></p>
+ </body>
+ </html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/bin/named/builtin.c b/bin/named/builtin.c
new file mode 100644
index 0000000..26348fd
--- /dev/null
+++ b/bin/named/builtin.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file
+ * \brief
+ * The built-in "version", "hostname", "id", "authors" and "empty" databases.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+
+#include <named/builtin.h>
+#include <named/globals.h>
+#include <named/os.h>
+#include <named/server.h>
+
+typedef struct builtin builtin_t;
+
+static isc_result_t
+do_authors_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_dns64_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_empty_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_hostname_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_id_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_ipv4only_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_ipv4reverse_lookup(dns_sdblookup_t *lookup);
+static isc_result_t
+do_version_lookup(dns_sdblookup_t *lookup);
+
+/*
+ * We can't use function pointers as the db_data directly
+ * because ANSI C does not guarantee that function pointers
+ * can safely be cast to void pointers and back.
+ */
+
+struct builtin {
+ isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
+ char *server;
+ char *contact;
+};
+
+static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
+static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
+static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
+static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
+static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
+static builtin_t ipv4only_builtin = { do_ipv4only_lookup, NULL, NULL };
+static builtin_t ipv4reverse_builtin = { do_ipv4reverse_lookup, NULL, NULL };
+static builtin_t version_builtin = { do_version_lookup, NULL, NULL };
+
+static dns_sdbimplementation_t *builtin_impl;
+static dns_sdbimplementation_t *dns64_impl;
+
+/*
+ * Pre computed HEX * 16 or 1 table.
+ */
+static const unsigned char hex16[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*00*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*20*/
+ 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 1, 1, 1, 1, 1, 1, /*30*/
+ 1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*40*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*50*/
+ 1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*60*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*70*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*80*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*90*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*A0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*B0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*C0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*D0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*E0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /*F0*/
+};
+
+static const unsigned char decimal[] = "0123456789";
+static const unsigned char ipv4only[] = "\010ipv4only\004arpa";
+
+static size_t
+dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
+ size_t i, j = 0;
+
+ for (i = 0; i < 4U; i++) {
+ unsigned char c = v[start++];
+ if (start == 7U) {
+ start++;
+ }
+ if (c > 99) {
+ rdata[j++] = 3;
+ rdata[j++] = decimal[c / 100];
+ c = c % 100;
+ rdata[j++] = decimal[c / 10];
+ c = c % 10;
+ rdata[j++] = decimal[c];
+ } else if (c > 9) {
+ rdata[j++] = 2;
+ rdata[j++] = decimal[c / 10];
+ c = c % 10;
+ rdata[j++] = decimal[c];
+ } else {
+ rdata[j++] = 1;
+ rdata[j++] = decimal[c];
+ }
+ }
+ memmove(&rdata[j], "\07in-addr\04arpa", 14);
+ return (j + 14);
+}
+
+static isc_result_t
+dns64_cname(const dns_name_t *zone, const dns_name_t *name,
+ dns_sdblookup_t *lookup) {
+ size_t zlen, nlen, j, len;
+ unsigned char v[16], n;
+ unsigned int i;
+ unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
+ unsigned char *ndata;
+
+ /*
+ * The combined length of the zone and name is 74.
+ *
+ * The minimum zone length is 10 ((3)ip6(4)arpa(0)).
+ *
+ * The length of name should always be even as we are expecting
+ * a series of nibbles.
+ */
+ zlen = zone->length;
+ nlen = name->length;
+ if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ /*
+ * We assume the zone name is well formed.
+ */
+
+ /*
+ * XXXMPA We could check the dns64 suffix here if we need to.
+ */
+ /*
+ * Check that name is a series of nibbles.
+ * Compute the byte values that correspond to the nibbles as we go.
+ *
+ * Shift the final result 4 bits, by setting 'i' to 1, if we if we
+ * have a odd number of nibbles so that "must be zero" tests below
+ * are byte aligned and we correctly return ISC_R_NOTFOUND or
+ * ISC_R_SUCCESS. We will not generate a CNAME in this case.
+ */
+ ndata = name->ndata;
+ i = (nlen % 4) == 2U ? 1 : 0;
+ j = nlen;
+ memset(v, 0, sizeof(v));
+ while (j != 0U) {
+ INSIST((i / 2) < sizeof(v));
+ if (ndata[0] != 1) {
+ return (ISC_R_NOTFOUND);
+ }
+ n = hex16[ndata[1] & 0xff];
+ if (n == 1) {
+ return (ISC_R_NOTFOUND);
+ }
+ v[i / 2] = n | (v[i / 2] >> 4);
+ j -= 2;
+ ndata += 2;
+ i++;
+ }
+
+ /*
+ * If we get here then we know name only consisted of nibbles.
+ * Now we need to determine if the name exists or not and whether
+ * it corresponds to a empty node in the zone or there should be
+ * a CNAME.
+ */
+#define ZLEN(x) (10 + (x) / 2)
+ switch (zlen) {
+ case ZLEN(32): /* prefix len 32 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 16U && v[(nlen - 1) / 4 - 4] != 0) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 8, rdata);
+ break;
+ case ZLEN(40): /* prefix len 40 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 12U && v[(nlen - 1) / 4 - 3] != 0) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 6, rdata);
+ break;
+ case ZLEN(48): /* prefix len 48 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 8U && v[(nlen - 1) / 4 - 2] != 0) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 5, rdata);
+ break;
+ case ZLEN(56): /* prefix len 56 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 4U && v[(nlen - 1) / 4 - 1] != 0) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 4, rdata);
+ break;
+ case ZLEN(64): /* prefix len 64 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (v[(nlen - 1) / 4] != 0) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 3, rdata);
+ break;
+ case ZLEN(96): /* prefix len 96 */
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U) {
+ return (ISC_R_SUCCESS);
+ }
+ len = dns64_rdata(v, 0, rdata);
+ break;
+ default:
+ /*
+ * This should never be reached unless someone adds a
+ * zone declaration with this internal type to named.conf.
+ */
+ return (ISC_R_NOTFOUND);
+ }
+
+ /*
+ * Reverse of 192.0.0.170 or 192.0.0.171 maps to ipv4only.arpa.
+ */
+ if ((v[0] == 170 || v[0] == 171) && v[1] == 0 && v[2] == 0 &&
+ v[3] == 192)
+ {
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_ptr, 3600,
+ ipv4only, sizeof(ipv4only)));
+ }
+
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_cname, 600, rdata,
+ (unsigned int)len));
+}
+
+static isc_result_t
+builtin_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo) {
+ builtin_t *b = (builtin_t *)dbdata;
+
+ UNUSED(zone);
+ UNUSED(methods);
+ UNUSED(clientinfo);
+
+ if (strcmp(name, "@") == 0) {
+ return (b->do_lookup(lookup));
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
+
+static isc_result_t
+dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo) {
+ builtin_t *b = (builtin_t *)dbdata;
+
+ UNUSED(methods);
+ UNUSED(clientinfo);
+
+ if (name->labels == 0 && name->length == 0) {
+ return (b->do_lookup(lookup));
+ } else {
+ return (dns64_cname(zone, name, lookup));
+ }
+}
+
+static isc_result_t
+put_txt(dns_sdblookup_t *lookup, const char *text) {
+ unsigned char buf[256];
+ unsigned int len = strlen(text);
+ if (len > 255) {
+ len = 255; /* Silently truncate */
+ }
+ buf[0] = len;
+ memmove(&buf[1], text, len);
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
+}
+
+static isc_result_t
+do_version_lookup(dns_sdblookup_t *lookup) {
+ if (named_g_server->version_set) {
+ if (named_g_server->version == NULL) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (put_txt(lookup, named_g_server->version));
+ }
+ } else {
+ return (put_txt(lookup, PACKAGE_VERSION));
+ }
+}
+
+static isc_result_t
+do_hostname_lookup(dns_sdblookup_t *lookup) {
+ if (named_g_server->hostname_set) {
+ if (named_g_server->hostname == NULL) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (put_txt(lookup, named_g_server->hostname));
+ }
+ } else {
+ char buf[256];
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ return (ISC_R_FAILURE);
+ }
+ return (put_txt(lookup, buf));
+ }
+}
+
+static isc_result_t
+do_authors_lookup(dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char **p;
+ static const char *authors[] = {
+ "Mark Andrews", "Curtis Blackburn", "James Brister",
+ "Ben Cottrell", "John H. DuBois III", "Francis Dupont",
+ "Michael Graff", "Andreas Gustafsson", "Bob Halley",
+ "Evan Hunt", "JINMEI Tatuya", "Witold Krecicki",
+ "David Lawrence", "Scott Mann", "Danny Mayer",
+ "Damien Neil", "Matt Nelson", "Jeremy C. Reed",
+ "Michael Sawyer", "Brian Wellington", NULL
+ };
+
+ /*
+ * If a version string is specified, disable the authors.bind zone.
+ */
+ if (named_g_server->version_set) {
+ return (ISC_R_SUCCESS);
+ }
+
+ for (p = authors; *p != NULL; p++) {
+ result = put_txt(lookup, *p);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_id_lookup(dns_sdblookup_t *lookup) {
+ if (named_g_server->sctx->usehostname) {
+ char buf[256];
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ return (ISC_R_FAILURE);
+ }
+ return (put_txt(lookup, buf));
+ } else if (named_g_server->sctx->server_id != NULL) {
+ return (put_txt(lookup, named_g_server->sctx->server_id));
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+}
+
+static isc_result_t
+do_dns64_lookup(dns_sdblookup_t *lookup) {
+ UNUSED(lookup);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_empty_lookup(dns_sdblookup_t *lookup) {
+ UNUSED(lookup);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_ipv4only_lookup(dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ unsigned char data[2][4] = { { 192, 0, 0, 170 }, { 192, 0, 0, 171 } };
+
+ for (int i = 0; i < 2; i++) {
+ result = dns_sdb_putrdata(lookup, dns_rdatatype_a, 3600,
+ data[i], 4);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_ipv4reverse_lookup(dns_sdblookup_t *lookup) {
+ isc_result_t result;
+
+ result = dns_sdb_putrdata(lookup, dns_rdatatype_ptr, 3600, ipv4only,
+ sizeof(ipv4only));
+ return (result);
+}
+
+static isc_result_t
+builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char *contact = "hostmaster";
+ const char *server = "@";
+ builtin_t *b = (builtin_t *)dbdata;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+
+ if (b == &empty_builtin) {
+ server = ".";
+ contact = ".";
+ } else {
+ if (b->server != NULL) {
+ server = b->server;
+ }
+ if (b->contact != NULL) {
+ contact = b->contact;
+ }
+ }
+
+ result = dns_sdb_putsoa(lookup, server, contact, 0);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_FAILURE);
+ }
+
+ result = dns_sdb_putrr(lookup, "ns", 0, server);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+builtin_create(const char *zone, int argc, char **argv, void *driverdata,
+ void **dbdata) {
+ REQUIRE(argc >= 1);
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (strcmp(argv[0], "dns64") == 0 || strcmp(argv[0], "empty") == 0 ||
+ strcmp(argv[0], "ipv4only") == 0 ||
+ strcmp(argv[0], "ipv4reverse") == 0)
+ {
+ if (argc != 3) {
+ return (DNS_R_SYNTAX);
+ }
+ } else if (argc != 1) {
+ return (DNS_R_SYNTAX);
+ }
+
+ if (strcmp(argv[0], "authors") == 0) {
+ *dbdata = &authors_builtin;
+ } else if (strcmp(argv[0], "hostname") == 0) {
+ *dbdata = &hostname_builtin;
+ } else if (strcmp(argv[0], "id") == 0) {
+ *dbdata = &id_builtin;
+ } else if (strcmp(argv[0], "version") == 0) {
+ *dbdata = &version_builtin;
+ } else if (strcmp(argv[0], "dns64") == 0 ||
+ strcmp(argv[0], "empty") == 0 ||
+ strcmp(argv[0], "ipv4only") == 0 ||
+ strcmp(argv[0], "ipv4reverse") == 0)
+ {
+ builtin_t *empty;
+ char *server;
+ char *contact;
+
+ if (argc != 3) {
+ return (DNS_R_SYNTAX);
+ }
+
+ /*
+ * We don't want built-in zones to fail. Fallback to
+ * the static configuration if memory allocation fails.
+ */
+ empty = isc_mem_get(named_g_mctx, sizeof(*empty));
+ server = isc_mem_strdup(named_g_mctx, argv[1]);
+ contact = isc_mem_strdup(named_g_mctx, argv[2]);
+ if (empty == NULL || server == NULL || contact == NULL) {
+ if (strcmp(argv[0], "dns64") == 0) {
+ *dbdata = &dns64_builtin;
+ } else if (strcmp(argv[0], "empty") == 0) {
+ *dbdata = &empty_builtin;
+ } else if (strcmp(argv[0], "ipv4only") == 0) {
+ *dbdata = &ipv4only_builtin;
+ } else {
+ *dbdata = &ipv4reverse_builtin;
+ }
+ if (server != NULL) {
+ isc_mem_free(named_g_mctx, server);
+ }
+ if (contact != NULL) {
+ isc_mem_free(named_g_mctx, contact);
+ }
+ if (empty != NULL) {
+ isc_mem_put(named_g_mctx, empty,
+ sizeof(*empty));
+ }
+ } else {
+ if (strcmp(argv[0], "dns64") == 0) {
+ memmove(empty, &dns64_builtin,
+ sizeof(empty_builtin));
+ } else if (strcmp(argv[0], "empty") == 0) {
+ memmove(empty, &empty_builtin,
+ sizeof(empty_builtin));
+ } else if (strcmp(argv[0], "ipv4only") == 0) {
+ memmove(empty, &ipv4only_builtin,
+ sizeof(empty_builtin));
+ } else {
+ memmove(empty, &ipv4reverse_builtin,
+ sizeof(empty_builtin));
+ }
+ empty->server = server;
+ empty->contact = contact;
+ *dbdata = empty;
+ }
+ } else {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static void
+builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
+ builtin_t *b = (builtin_t *)*dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ /*
+ * Don't free the static versions.
+ */
+ if (*dbdata == &authors_builtin || *dbdata == &dns64_builtin ||
+ *dbdata == &empty_builtin || *dbdata == &hostname_builtin ||
+ *dbdata == &id_builtin || *dbdata == &ipv4only_builtin ||
+ *dbdata == &ipv4reverse_builtin || *dbdata == &version_builtin)
+ {
+ return;
+ }
+
+ isc_mem_free(named_g_mctx, b->server);
+ isc_mem_free(named_g_mctx, b->contact);
+ isc_mem_put(named_g_mctx, b, sizeof(*b));
+}
+
+static dns_sdbmethods_t builtin_methods = {
+ builtin_lookup, builtin_authority, NULL, /* allnodes */
+ builtin_create, builtin_destroy, NULL
+};
+
+static dns_sdbmethods_t dns64_methods = {
+ NULL, builtin_authority, NULL, /* allnodes */
+ builtin_create, builtin_destroy, dns64_lookup,
+};
+
+isc_result_t
+named_builtin_init(void) {
+ RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA,
+ named_g_mctx,
+ &builtin_impl) == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL,
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_DNS64,
+ named_g_mctx,
+ &dns64_impl) == ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+void
+named_builtin_deinit(void) {
+ dns_sdb_unregister(&builtin_impl);
+ dns_sdb_unregister(&dns64_impl);
+}
diff --git a/bin/named/config.c b/bin/named/config.c
new file mode 100644
index 0000000..7f318a2
--- /dev/null
+++ b/bin/named/config.c
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <bind.keys.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/parseint.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/kasp.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+
+#include <dst/dst.h>
+
+#include <isccfg/grammar.h>
+#include <isccfg/namedconf.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+
+/*% default configuration */
+static char defaultconf[] = "\
+options {\n\
+ answer-cookie true;\n\
+ automatic-interface-scan yes;\n\
+ bindkeys-file \"" NAMED_SYSCONFDIR "/bind.keys\";\n\
+# blackhole {none;};\n"
+ " cookie-algorithm siphash24;\n"
+ " coresize default;\n\
+ datasize default;\n"
+ "\
+# directory <none>\n\
+ dnssec-policy \"none\";\n\
+ dump-file \"named_dump.db\";\n\
+ edns-udp-size 1232;\n\
+ files unlimited;\n"
+#if defined(HAVE_GEOIP2)
+ "\
+ geoip-directory \"" MAXMINDDB_PREFIX "/share/GeoIP\";\n"
+#elif defined(HAVE_GEOIP2)
+ "\
+ geoip-directory \".\";\n"
+#endif /* if defined(HAVE_GEOIP2) */
+ "\
+ heartbeat-interval 60;\n\
+ interface-interval 60;\n\
+# keep-response-order {none;};\n\
+ listen-on {any;};\n\
+ listen-on-v6 {any;};\n\
+# lock-file \"" NAMED_LOCALSTATEDIR "/run/named/named.lock\";\n\
+ match-mapped-addresses no;\n\
+ max-ixfr-ratio 100%;\n\
+ max-rsa-exponent-size 0; /* no limit */\n\
+ max-udp-size 1232;\n\
+ memstatistics-file \"named.memstats\";\n\
+ nocookie-udp-size 4096;\n\
+ notify-rate 20;\n\
+ nta-lifetime 3600;\n\
+ nta-recheck 300;\n\
+# pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\
+ port 53;\n"
+#if HAVE_SO_REUSEPORT_LB
+ "\
+ reuseport yes;\n"
+#else
+ "\
+ reuseport no;\n"
+#endif
+ "\
+ tls-port 853;\n"
+#if HAVE_LIBNGHTTP2
+ "\
+ http-port 80;\n\
+ https-port 443;\n\
+ http-listener-clients 300;\n\
+ http-streams-per-connection 100;\n"
+#endif
+ "\
+ prefetch 2 9;\n\
+ recursing-file \"named.recursing\";\n\
+ recursive-clients 1000;\n\
+ request-nsid false;\n\
+ reserved-sockets 512;\n\
+ resolver-query-timeout 10;\n\
+ rrset-order { order random; };\n\
+ secroots-file \"named.secroots\";\n\
+ send-cookie true;\n\
+ serial-query-rate 20;\n\
+ server-id none;\n\
+ session-keyalg hmac-sha256;\n\
+# session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
+ session-keyname local-ddns;\n\
+ stacksize default;\n\
+ startup-notify-rate 20;\n\
+ statistics-file \"named.stats\";\n\
+ tcp-advertised-timeout 300;\n\
+ tcp-clients 150;\n\
+ tcp-idle-timeout 300;\n\
+ tcp-initial-timeout 300;\n\
+ tcp-keepalive-timeout 300;\n\
+ tcp-listen-queue 10;\n\
+ tcp-receive-buffer 0;\n\
+ tcp-send-buffer 0;\n\
+# tkey-dhkey <none>\n\
+# tkey-domain <none>\n\
+# tkey-gssapi-credential <none>\n\
+ transfer-message-size 20480;\n\
+ transfers-in 10;\n\
+ transfers-out 10;\n\
+ transfers-per-ns 2;\n\
+ trust-anchor-telemetry yes;\n\
+ udp-receive-buffer 0;\n\
+ udp-send-buffer 0;\n\
+ update-quota 100;\n\
+\n\
+ /* view */\n\
+ allow-new-zones no;\n\
+ allow-notify {none;};\n\
+ allow-query-cache { localnets; localhost; };\n\
+ allow-query-cache-on { any; };\n\
+ allow-recursion { localnets; localhost; };\n\
+ allow-recursion-on { any; };\n\
+ allow-update-forwarding {none;};\n\
+ auth-nxdomain false;\n\
+ check-dup-records warn;\n\
+ check-mx warn;\n\
+ check-names primary fail;\n\
+ check-names response ignore;\n\
+ check-names secondary warn;\n\
+ check-spf warn;\n\
+ clients-per-query 10;\n\
+ dnssec-accept-expired no;\n\
+ dnssec-validation " VALIDATION_DEFAULT "; \n"
+#ifdef HAVE_DNSTAP
+ " dnstap-identity hostname;\n"
+#endif /* ifdef HAVE_DNSTAP */
+ "\
+ fetch-quota-params 100 0.1 0.3 0.7;\n\
+ fetches-per-server 0;\n\
+ fetches-per-zone 0;\n\
+ glue-cache yes;\n\
+ lame-ttl 0;\n"
+#ifdef HAVE_LMDB
+ " lmdb-mapsize 32M;\n"
+#endif /* ifdef HAVE_LMDB */
+ " max-cache-size 90%;\n\
+ max-cache-ttl 604800; /* 1 week */\n\
+ max-clients-per-query 100;\n\
+ max-ncache-ttl 10800; /* 3 hours */\n\
+ max-recursion-depth 7;\n\
+ max-recursion-queries 100;\n\
+ max-stale-ttl 86400; /* 1 day */\n\
+ message-compression yes;\n\
+ min-ncache-ttl 0; /* 0 hours */\n\
+ min-cache-ttl 0; /* 0 seconds */\n\
+ minimal-any false;\n\
+ minimal-responses no-auth-recursive;\n\
+ notify-source *;\n\
+ notify-source-v6 *;\n\
+ nsec3-test-zone no;\n\
+ parental-source *;\n\
+ parental-source-v6 *;\n\
+ provide-ixfr true;\n\
+ qname-minimization relaxed;\n\
+ query-source address *;\n\
+ query-source-v6 address *;\n\
+ recursion true;\n\
+ request-expire true;\n\
+ request-ixfr true;\n\
+ require-server-cookie no;\n\
+ resolver-nonbackoff-tries 3;\n\
+ resolver-retry-interval 800; /* in milliseconds */\n\
+ root-key-sentinel yes;\n\
+ servfail-ttl 1;\n\
+# sortlist <none>\n\
+ stale-answer-client-timeout off;\n\
+ stale-answer-enable false;\n\
+ stale-answer-ttl 30; /* 30 seconds */\n\
+ stale-cache-enable false;\n\
+ stale-refresh-time 30; /* 30 seconds */\n\
+ synth-from-dnssec yes;\n\
+# topology <none>\n\
+ transfer-format many-answers;\n\
+ v6-bias 50;\n\
+ zero-no-soa-ttl-cache no;\n\
+\n\
+ /* zone */\n\
+ allow-query {any;};\n\
+ allow-query-on {any;};\n\
+ allow-transfer {any;};\n\
+# also-notify <none>\n\
+ alt-transfer-source *;\n\
+ alt-transfer-source-v6 *;\n\
+ check-integrity yes;\n\
+ check-mx-cname warn;\n\
+ check-sibling yes;\n\
+ check-srv-cname warn;\n\
+ check-wildcard yes;\n\
+ dialup no;\n\
+ dnssec-dnskey-kskonly yes;\n\
+ dnssec-loadkeys-interval 60;\n\
+ dnssec-secure-to-insecure no;\n\
+ dnssec-update-mode maintain;\n\
+# forward <none>\n\
+# forwarders <none>\n\
+# inline-signing no;\n\
+ ixfr-from-differences false;\n\
+ max-journal-size default;\n\
+ max-records 0;\n\
+ max-refresh-time 2419200; /* 4 weeks */\n\
+ max-retry-time 1209600; /* 2 weeks */\n\
+ max-transfer-idle-in 60;\n\
+ max-transfer-idle-out 60;\n\
+ max-transfer-time-in 120;\n\
+ max-transfer-time-out 120;\n\
+ min-refresh-time 300;\n\
+ min-retry-time 500;\n\
+ multi-master no;\n\
+ notify yes;\n\
+ notify-delay 5;\n\
+ notify-to-soa no;\n\
+ serial-update-method increment;\n\
+ sig-signing-nodes 100;\n\
+ sig-signing-signatures 10;\n\
+ sig-signing-type 65534;\n\
+ sig-validity-interval 30; /* days */\n\
+ dnskey-sig-validity 0; /* default: sig-validity-interval */\n\
+ transfer-source *;\n\
+ transfer-source-v6 *;\n\
+ try-tcp-refresh yes; /* BIND 8 compat */\n\
+ update-check-ksk yes;\n\
+ zero-no-soa-ttl yes;\n\
+ zone-statistics terse;\n\
+};\n\
+"
+
+ "#\n\
+# Zones in the \"_bind\" view are NOT counted in the count of zones.\n\
+#\n\
+view \"_bind\" chaos {\n\
+ recursion no;\n\
+ notify no;\n\
+ allow-new-zones no;\n\
+ max-cache-size 2M;\n\
+\n\
+ # Prevent use of this zone in DNS amplified reflection DoS attacks\n\
+ rate-limit {\n\
+ responses-per-second 3;\n\
+ slip 0;\n\
+ min-table-size 10;\n\
+ };\n\
+\n\
+ zone \"version.bind\" chaos {\n\
+ type primary;\n\
+ database \"_builtin version\";\n\
+ };\n\
+\n\
+ zone \"hostname.bind\" chaos {\n\
+ type primary;\n\
+ database \"_builtin hostname\";\n\
+ };\n\
+\n\
+ zone \"authors.bind\" chaos {\n\
+ type primary;\n\
+ database \"_builtin authors\";\n\
+ };\n\
+\n\
+ zone \"id.server\" chaos {\n\
+ type primary;\n\
+ database \"_builtin id\";\n\
+ };\n\
+};\n\
+"
+ "#\n\
+# Built-in DNSSEC key and signing policies.\n\
+#\n\
+dnssec-policy \"default\" {\n\
+ keys {\n\
+ csk key-directory lifetime unlimited algorithm 13;\n\
+ };\n\
+\n\
+ dnskey-ttl " DNS_KASP_KEY_TTL ";\n\
+ publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\
+ retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\
+ purge-keys " DNS_KASP_PURGE_KEYS "; \n\
+ signatures-refresh " DNS_KASP_SIG_REFRESH "; \n\
+ signatures-validity " DNS_KASP_SIG_VALIDITY "; \n\
+ signatures-validity-dnskey " DNS_KASP_SIG_VALIDITY_DNSKEY "; \n\
+ max-zone-ttl " DNS_KASP_ZONE_MAXTTL "; \n\
+ zone-propagation-delay " DNS_KASP_ZONE_PROPDELAY "; \n\
+ parent-ds-ttl " DNS_KASP_DS_TTL "; \n\
+ parent-propagation-delay " DNS_KASP_PARENT_PROPDELAY "; \n\
+};\n\
+\n\
+dnssec-policy \"insecure\" {\n\
+ max-zone-ttl 0; \n\
+ keys { };\n\
+};\n\
+\n\
+"
+ "#\n\
+# Default trusted key(s), used if \n\
+# \"dnssec-validation auto;\" is set and\n\
+# " NAMED_SYSCONFDIR "/bind.keys doesn't exist).\n\
+#\n\
+# BEGIN TRUST ANCHORS\n"
+
+ /* Imported from bind.keys.h: */
+ TRUST_ANCHORS
+
+ "# END TRUST ANCHORS\n\
+\n\
+primaries " DEFAULT_IANA_ROOT_ZONE_PRIMARIES " {\n\
+ 2001:500:200::b; # b.root-servers.net\n\
+ 2001:500:2::c; # c.root-servers.net\n\
+ 2001:500:2f::f; # f.root-servers.net\n\
+ 2001:500:12::d0d; # g.root-servers.net\n\
+ 2001:7fd::1; # k.root-servers.net\n\
+ 2620:0:2830:202::132; # xfr.cjr.dns.icann.org\n\
+ 2620:0:2d0:202::132; # xfr.lax.dns.icann.org\n\
+ 199.9.14.201; # b.root-servers.net\n\
+ 192.33.4.12; # c.root-servers.net\n\
+ 192.5.5.241; # f.root-servers.net\n\
+ 192.112.36.4; # g.root-servers.net\n\
+ 193.0.14.129; # k.root-servers.net\n\
+ 192.0.47.132; # xfr.cjr.dns.icann.org\n\
+ 192.0.32.132; # xfr.lax.dns.icann.org\n\
+};\n\
+";
+
+isc_result_t
+named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1);
+ isc_buffer_add(&b, sizeof(defaultconf) - 1);
+ return (cfg_parse_buffer(parser, &b, __FILE__, 0, &cfg_type_namedconf,
+ CFG_PCTX_NODEPRECATED, conf));
+}
+
+const char *
+named_config_getdefault(void) {
+ return (defaultconf);
+}
+
+isc_result_t
+named_config_get(cfg_obj_t const *const *maps, const char *name,
+ const cfg_obj_t **obj) {
+ int i;
+
+ for (i = 0; maps[i] != NULL; i++) {
+ if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+named_checknames_get(const cfg_obj_t **maps, const char *const names[],
+ const cfg_obj_t **obj) {
+ const cfg_listelt_t *element;
+ const cfg_obj_t *checknames;
+ const cfg_obj_t *type;
+ const cfg_obj_t *value;
+ int i;
+
+ REQUIRE(maps != NULL);
+ REQUIRE(names != NULL);
+ REQUIRE(obj != NULL && *obj == NULL);
+
+ for (i = 0; maps[i] != NULL; i++) {
+ checknames = NULL;
+ if (cfg_map_get(maps[i], "check-names", &checknames) ==
+ ISC_R_SUCCESS)
+ {
+ /*
+ * Zone map entry is not a list.
+ */
+ if (checknames != NULL && !cfg_obj_islist(checknames)) {
+ *obj = checknames;
+ return (ISC_R_SUCCESS);
+ }
+ for (element = cfg_list_first(checknames);
+ element != NULL; element = cfg_list_next(element))
+ {
+ value = cfg_listelt_value(element);
+ type = cfg_tuple_get(value, "type");
+
+ for (size_t j = 0; names[j] != NULL; j++) {
+ if (strcasecmp(cfg_obj_asstring(type),
+ names[j]) == 0)
+ {
+ *obj = cfg_tuple_get(value,
+ "mode");
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+ }
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+int
+named_config_listcount(const cfg_obj_t *list) {
+ const cfg_listelt_t *e;
+ int i = 0;
+
+ for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e)) {
+ i++;
+ }
+
+ return (i);
+}
+
+isc_result_t
+named_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(classobj)) {
+ *classp = defclass;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(classobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdataclass_fromtext(classp, &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(classobj, named_g_lctx, ISC_LOG_ERROR,
+ "unknown class '%s'", r.base);
+ }
+ return (result);
+}
+
+isc_result_t
+named_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(typeobj)) {
+ *typep = deftype;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(typeobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdatatype_fromtext(typep, &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(typeobj, named_g_lctx, ISC_LOG_ERROR,
+ "unknown type '%s'", r.base);
+ }
+ return (result);
+}
+
+dns_zonetype_t
+named_config_getzonetype(const cfg_obj_t *zonetypeobj) {
+ dns_zonetype_t ztype = dns_zone_none;
+ const char *str;
+
+ str = cfg_obj_asstring(zonetypeobj);
+ if (strcasecmp(str, "primary") == 0 || strcasecmp(str, "master") == 0) {
+ ztype = dns_zone_primary;
+ } else if (strcasecmp(str, "secondary") == 0 ||
+ strcasecmp(str, "slave") == 0)
+ {
+ ztype = dns_zone_secondary;
+ } else if (strcasecmp(str, "mirror") == 0) {
+ ztype = dns_zone_mirror;
+ } else if (strcasecmp(str, "stub") == 0) {
+ ztype = dns_zone_stub;
+ } else if (strcasecmp(str, "static-stub") == 0) {
+ ztype = dns_zone_staticstub;
+ } else if (strcasecmp(str, "redirect") == 0) {
+ ztype = dns_zone_redirect;
+ } else {
+ UNREACHABLE();
+ }
+ return (ztype);
+}
+
+isc_result_t
+named_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, uint32_t *countp) {
+ int count, i = 0;
+ const cfg_obj_t *addrlist = NULL;
+ const cfg_obj_t *portobj = NULL;
+ const cfg_listelt_t *element = NULL;
+ isc_sockaddr_t *addrs = NULL;
+ in_port_t port;
+ isc_result_t result;
+
+ INSIST(addrsp != NULL && *addrsp == NULL);
+ INSIST(countp != NULL);
+
+ addrlist = cfg_tuple_get(list, "addresses");
+ count = named_config_listcount(addrlist);
+
+ portobj = cfg_tuple_get(list, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)val;
+ } else if (defport != 0) {
+ port = defport;
+ } else {
+ result = named_config_getport(config, "port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t));
+
+ for (element = cfg_list_first(addrlist); element != NULL;
+ element = cfg_list_next(element), i++)
+ {
+ const cfg_obj_t *addr;
+ INSIST(i < count);
+ addr = cfg_listelt_value(element);
+ addrs[i] = *cfg_obj_assockaddr(addr);
+ if (isc_sockaddr_getport(&addrs[i]) == 0) {
+ isc_sockaddr_setport(&addrs[i], port);
+ }
+ }
+ INSIST(i == count);
+
+ *addrsp = addrs;
+ *countp = count;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ uint32_t count) {
+ INSIST(addrsp != NULL && *addrsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ *addrsp = NULL;
+}
+
+static isc_result_t
+getremotesdef(const cfg_obj_t *cctx, const char *list, const char *name,
+ const cfg_obj_t **ret) {
+ isc_result_t result;
+ const cfg_obj_t *obj = NULL;
+ const cfg_listelt_t *elt;
+
+ REQUIRE(cctx != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ result = cfg_map_get(cctx, list, &obj);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ elt = cfg_list_first(obj);
+ while (elt != NULL) {
+ obj = cfg_listelt_value(elt);
+ if (strcasecmp(cfg_obj_asstring(cfg_tuple_get(obj, "name")),
+ name) == 0)
+ {
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+ }
+ elt = cfg_list_next(elt);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+named_config_getremotesdef(const cfg_obj_t *cctx, const char *list,
+ const char *name, const cfg_obj_t **ret) {
+ isc_result_t result;
+
+ if (strcmp(list, "parental-agents") == 0) {
+ return (getremotesdef(cctx, list, name, ret));
+ } else if (strcmp(list, "primaries") == 0) {
+ result = getremotesdef(cctx, list, name, ret);
+ if (result != ISC_R_SUCCESS) {
+ result = getremotesdef(cctx, "masters", name, ret);
+ }
+ return (result);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+named_config_getname(isc_mem_t *mctx, const cfg_obj_t *obj,
+ dns_name_t **namep) {
+ REQUIRE(namep != NULL && *namep == NULL);
+
+ const char *objstr;
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_fixedname_t fname;
+
+ if (!cfg_obj_isstring(obj)) {
+ *namep = NULL;
+ return (ISC_R_SUCCESS);
+ }
+
+ *namep = isc_mem_get(mctx, sizeof(**namep));
+ dns_name_init(*namep, NULL);
+
+ objstr = cfg_obj_asstring(obj);
+ isc_buffer_constinit(&b, objstr, strlen(objstr));
+ isc_buffer_add(&b, strlen(objstr));
+ dns_fixedname_init(&fname);
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b, dns_rootname,
+ 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, *namep, sizeof(**namep));
+ *namep = NULL;
+ return (result);
+ }
+ dns_name_dup(dns_fixedname_name(&fname), mctx, *namep);
+
+ return (ISC_R_SUCCESS);
+}
+
+#define grow_array(mctx, array, newlen, oldlen) \
+ if (newlen >= oldlen) { \
+ size_t newsize = (newlen + 16) * sizeof(array[0]); \
+ size_t oldsize = oldlen * sizeof(array[0]); \
+ void *tmp = isc_mem_get(mctx, newsize); \
+ memset(tmp, 0, newsize); \
+ if (oldlen != 0) { \
+ memmove(tmp, array, oldsize); \
+ isc_mem_put(mctx, array, oldsize); \
+ } \
+ array = tmp; \
+ oldlen = newlen + 16; \
+ }
+
+#define shrink_array(mctx, array, newlen, oldlen) \
+ if (newlen < oldlen) { \
+ void *tmp = NULL; \
+ size_t newsize = newlen * sizeof(array[0]); \
+ size_t oldsize = oldlen * sizeof(array[0]); \
+ if (newlen != 0) { \
+ tmp = isc_mem_get(mctx, newsize); \
+ memset(tmp, 0, newsize); \
+ memmove(tmp, array, newsize); \
+ } else { \
+ tmp = NULL; \
+ } \
+ isc_mem_put(mctx, array, oldsize); \
+ array = tmp; \
+ oldlen = newlen; \
+ }
+
+isc_result_t
+named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype,
+ const cfg_obj_t *list, isc_mem_t *mctx,
+ dns_ipkeylist_t *ipkl) {
+ uint32_t addrcount = 0, keycount = 0, tlscount = 0, i = 0;
+ uint32_t listcount = 0, l = 0, j;
+ uint32_t stackcount = 0, pushed = 0;
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj;
+ in_port_t port = (in_port_t)0;
+ in_port_t def_port;
+ in_port_t def_tlsport;
+ isc_sockaddr_t *addrs = NULL;
+ dns_name_t **keys = NULL;
+ dns_name_t **tlss = NULL;
+ struct {
+ const char *name;
+ in_port_t port;
+ isc_sockaddr_t *src4s;
+ isc_sockaddr_t *src6s;
+ } *lists = NULL;
+ struct {
+ const cfg_listelt_t *element;
+ in_port_t port;
+ } *stack = NULL;
+
+ REQUIRE(ipkl != NULL);
+ REQUIRE(ipkl->count == 0);
+ REQUIRE(ipkl->addrs == NULL);
+ REQUIRE(ipkl->keys == NULL);
+ REQUIRE(ipkl->tlss == NULL);
+ REQUIRE(ipkl->labels == NULL);
+ REQUIRE(ipkl->allocated == 0);
+
+ /*
+ * Get system defaults.
+ */
+ result = named_config_getport(config, "port", &def_port);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = named_config_getport(config, "tls-port", &def_tlsport);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+newlist:
+ addrlist = cfg_tuple_get(list, "addresses");
+ portobj = cfg_tuple_get(list, "port");
+
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ port = (in_port_t)val;
+ }
+
+ result = ISC_R_NOMEMORY;
+
+ element = cfg_list_first(addrlist);
+resume:
+ for (; element != NULL; element = cfg_list_next(element)) {
+ const cfg_obj_t *addr;
+ const cfg_obj_t *key;
+ const cfg_obj_t *tls;
+
+ addr = cfg_tuple_get(cfg_listelt_value(element),
+ "remoteselement");
+ key = cfg_tuple_get(cfg_listelt_value(element), "key");
+ tls = cfg_tuple_get(cfg_listelt_value(element), "tls");
+
+ if (!cfg_obj_issockaddr(addr)) {
+ const char *listname = cfg_obj_asstring(addr);
+ isc_result_t tresult;
+
+ /* Grow lists? */
+ grow_array(mctx, lists, l, listcount);
+
+ /* Seen? */
+ for (j = 0; j < l; j++) {
+ if (strcasecmp(lists[j].name, listname) == 0) {
+ break;
+ }
+ }
+ if (j < l) {
+ continue;
+ }
+ list = NULL;
+ tresult = named_config_getremotesdef(config, listtype,
+ listname, &list);
+ if (tresult == ISC_R_NOTFOUND) {
+ cfg_obj_log(addr, named_g_lctx, ISC_LOG_ERROR,
+ "%s \"%s\" not found", listtype,
+ listname);
+
+ result = tresult;
+ goto cleanup;
+ }
+ if (tresult != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ lists[l++].name = listname;
+ /* Grow stack? */
+ grow_array(mctx, stack, pushed, stackcount);
+ /*
+ * We want to resume processing this list on the
+ * next element.
+ */
+ stack[pushed].element = cfg_list_next(element);
+ stack[pushed].port = port;
+ pushed++;
+ goto newlist;
+ }
+
+ grow_array(mctx, addrs, i, addrcount);
+ grow_array(mctx, keys, i, keycount);
+ grow_array(mctx, tlss, i, tlscount);
+
+ addrs[i] = *cfg_obj_assockaddr(addr);
+
+ result = named_config_getname(mctx, key, &keys[i]);
+ if (result != ISC_R_SUCCESS) {
+ i++; /* Increment here so that cleanup on error works.
+ */
+ goto cleanup;
+ }
+
+ result = named_config_getname(mctx, tls, &tlss[i]);
+ if (result != ISC_R_SUCCESS) {
+ i++; /* Increment here so that cleanup on error works.
+ */
+ goto cleanup;
+ }
+
+ /* If the port is unset, take it from one of the upper levels */
+ if (isc_sockaddr_getport(&addrs[i]) == 0) {
+ in_port_t addr_port = port;
+
+ /* If unset, use the default port or tls-port */
+ if (addr_port == 0) {
+ if (tlss[i] != NULL) {
+ addr_port = def_tlsport;
+ } else {
+ addr_port = def_port;
+ }
+ }
+
+ isc_sockaddr_setport(&addrs[i], addr_port);
+ }
+
+ i++;
+ }
+ if (pushed != 0) {
+ pushed--;
+ element = stack[pushed].element;
+ port = stack[pushed].port;
+ goto resume;
+ }
+
+ shrink_array(mctx, addrs, i, addrcount);
+ shrink_array(mctx, keys, i, keycount);
+ shrink_array(mctx, tlss, i, tlscount);
+
+ if (lists != NULL) {
+ isc_mem_put(mctx, lists, listcount * sizeof(lists[0]));
+ }
+ if (stack != NULL) {
+ isc_mem_put(mctx, stack, stackcount * sizeof(stack[0]));
+ }
+
+ INSIST(keycount == addrcount);
+ INSIST(tlscount == addrcount);
+
+ ipkl->addrs = addrs;
+ ipkl->keys = keys;
+ ipkl->tlss = tlss;
+ ipkl->count = addrcount;
+ ipkl->allocated = addrcount;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (addrs != NULL) {
+ isc_mem_put(mctx, addrs, addrcount * sizeof(addrs[0]));
+ }
+ if (keys != NULL) {
+ for (j = 0; j < i; j++) {
+ if (keys[j] == NULL) {
+ continue;
+ }
+ if (dns_name_dynamic(keys[j])) {
+ dns_name_free(keys[j], mctx);
+ }
+ isc_mem_put(mctx, keys[j], sizeof(*keys[j]));
+ }
+ isc_mem_put(mctx, keys, keycount * sizeof(keys[0]));
+ }
+ if (tlss != NULL) {
+ for (j = 0; j < i; j++) {
+ if (tlss[j] == NULL) {
+ continue;
+ }
+ if (dns_name_dynamic(tlss[j])) {
+ dns_name_free(tlss[j], mctx);
+ }
+ isc_mem_put(mctx, tlss[j], sizeof(*tlss[j]));
+ }
+ isc_mem_put(mctx, tlss, tlscount * sizeof(tlss[0]));
+ }
+ if (lists != NULL) {
+ isc_mem_put(mctx, lists, listcount * sizeof(lists[0]));
+ }
+ if (stack != NULL) {
+ isc_mem_put(mctx, stack, stackcount * sizeof(stack[0]));
+ }
+ return (result);
+}
+
+isc_result_t
+named_config_getport(const cfg_obj_t *config, const char *type,
+ in_port_t *portp) {
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *portobj = NULL;
+ isc_result_t result;
+ int i;
+
+ (void)cfg_map_get(config, "options", &options);
+ i = 0;
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ result = named_config_get(maps, type, &portobj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range",
+ cfg_obj_asuint32(portobj));
+ return (ISC_R_RANGE);
+ }
+ *portp = (in_port_t)cfg_obj_asuint32(portobj);
+ return (ISC_R_SUCCESS);
+}
+
+struct keyalgorithms {
+ const char *str;
+ enum {
+ hmacnone,
+ hmacmd5,
+ hmacsha1,
+ hmacsha224,
+ hmacsha256,
+ hmacsha384,
+ hmacsha512
+ } hmac;
+ unsigned int type;
+ uint16_t size;
+} algorithms[] = { { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 },
+ { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 },
+ { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 },
+ { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 },
+ { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 },
+ { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 },
+ { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 },
+ { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 },
+ { NULL, hmacnone, DST_ALG_UNKNOWN, 0 } };
+
+isc_result_t
+named_config_getkeyalgorithm(const char *str, const dns_name_t **name,
+ uint16_t *digestbits) {
+ return (named_config_getkeyalgorithm2(str, name, NULL, digestbits));
+}
+
+isc_result_t
+named_config_getkeyalgorithm2(const char *str, const dns_name_t **name,
+ unsigned int *typep, uint16_t *digestbits) {
+ int i;
+ size_t len = 0;
+ uint16_t bits;
+ isc_result_t result;
+
+ for (i = 0; algorithms[i].str != NULL; i++) {
+ len = strlen(algorithms[i].str);
+ if (strncasecmp(algorithms[i].str, str, len) == 0 &&
+ (str[len] == '\0' ||
+ (algorithms[i].size != 0 && str[len] == '-')))
+ {
+ break;
+ }
+ }
+ if (algorithms[i].str == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+ if (str[len] == '-') {
+ result = isc_parse_uint16(&bits, str + len + 1, 10);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (bits > algorithms[i].size) {
+ return (ISC_R_RANGE);
+ }
+ } else if (algorithms[i].size == 0) {
+ bits = 128;
+ } else {
+ bits = algorithms[i].size;
+ }
+
+ if (name != NULL) {
+ switch (algorithms[i].hmac) {
+ case hmacmd5:
+ *name = dns_tsig_hmacmd5_name;
+ break;
+ case hmacsha1:
+ *name = dns_tsig_hmacsha1_name;
+ break;
+ case hmacsha224:
+ *name = dns_tsig_hmacsha224_name;
+ break;
+ case hmacsha256:
+ *name = dns_tsig_hmacsha256_name;
+ break;
+ case hmacsha384:
+ *name = dns_tsig_hmacsha384_name;
+ break;
+ case hmacsha512:
+ *name = dns_tsig_hmacsha512_name;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ if (typep != NULL) {
+ *typep = algorithms[i].type;
+ }
+ if (digestbits != NULL) {
+ *digestbits = bits;
+ }
+ return (ISC_R_SUCCESS);
+}
diff --git a/bin/named/control.c b/bin/named/control.c
new file mode 100644
index 0000000..454c128
--- /dev/null
+++ b/bin/named/control.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/app.h>
+#include <isc/event.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+
+#include <named/control.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/server.h>
+#ifdef HAVE_LIBSCF
+#include <named/smf_globals.h>
+#endif /* ifdef HAVE_LIBSCF */
+
+static isc_result_t
+getcommand(isc_lex_t *lex, char **cmdp) {
+ isc_result_t result;
+ isc_token_t token;
+
+ REQUIRE(cmdp != NULL && *cmdp == NULL);
+
+ result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF, &token);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ isc_lex_ungettoken(lex, &token);
+
+ if (token.type != isc_tokentype_string) {
+ return (ISC_R_FAILURE);
+ }
+
+ *cmdp = token.value.as_textregion.base;
+
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+command_compare(const char *str, const char *command) {
+ return (strcasecmp(str, command) == 0);
+}
+
+/*%
+ * This function is called to process the incoming command
+ * when a control channel message is received.
+ */
+isc_result_t
+named_control_docommand(isccc_sexpr_t *message, bool readonly,
+ isc_buffer_t **text) {
+ isccc_sexpr_t *data;
+ char *cmdline = NULL;
+ char *command = NULL;
+ isc_result_t result;
+ int log_level;
+ isc_buffer_t src;
+ isc_lex_t *lex = NULL;
+#ifdef HAVE_LIBSCF
+ named_smf_want_disable = 0;
+#endif /* ifdef HAVE_LIBSCF */
+
+ data = isccc_alist_lookup(message, "_data");
+ if (!isccc_alist_alistp(data)) {
+ /*
+ * No data section.
+ */
+ return (ISC_R_FAILURE);
+ }
+
+ result = isccc_cc_lookupstring(data, "type", &cmdline);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We have no idea what this is.
+ */
+ return (result);
+ }
+
+ result = isc_lex_create(named_g_mctx, strlen(cmdline), &lex);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ isc_buffer_init(&src, cmdline, strlen(cmdline));
+ isc_buffer_add(&src, strlen(cmdline));
+ result = isc_lex_openbuffer(lex, &src);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = getcommand(lex, &command);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /*
+ * Compare the 'command' parameter against all known control commands.
+ */
+ if ((command_compare(command, NAMED_COMMAND_NULL) &&
+ strlen(cmdline) == 4) ||
+ command_compare(command, NAMED_COMMAND_STATUS))
+ {
+ log_level = ISC_LOG_DEBUG(1);
+ } else {
+ log_level = ISC_LOG_INFO;
+ }
+
+ /*
+ * If this listener should have read-only access, reject
+ * restricted commands here. rndc nta is handled specially
+ * below.
+ */
+ if (readonly && !command_compare(command, NAMED_COMMAND_NTA) &&
+ !command_compare(command, NAMED_COMMAND_NULL) &&
+ !command_compare(command, NAMED_COMMAND_STATUS) &&
+ !command_compare(command, NAMED_COMMAND_SHOWZONE) &&
+ !command_compare(command, NAMED_COMMAND_TESTGEN) &&
+ !command_compare(command, NAMED_COMMAND_ZONESTATUS))
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, log_level,
+ "rejecting restricted control channel "
+ "command '%s'",
+ cmdline);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, log_level,
+ "received control channel command '%s'", cmdline);
+
+ /*
+ * After the lengthy "halt" and "stop", the commands are
+ * handled in alphabetical order of the NAMED_COMMAND_ macros.
+ */
+ if (command_compare(command, NAMED_COMMAND_HALT)) {
+#ifdef HAVE_LIBSCF
+ /*
+ * If we are managed by smf(5), AND in chroot, then
+ * we cannot connect to the smf repository, so just
+ * return with an appropriate message back to rndc.
+ */
+ if (named_smf_got_instance == 1 && named_smf_chroot == 1) {
+ result = named_smf_add_message(text);
+ goto cleanup;
+ }
+ /*
+ * If we are managed by smf(5) but not in chroot,
+ * try to disable ourselves the smf way.
+ */
+ if (named_smf_got_instance == 1 && named_smf_chroot == 0) {
+ named_smf_want_disable = 1;
+ }
+ /*
+ * If named_smf_got_instance = 0, named_smf_chroot
+ * is not relevant and we fall through to
+ * isc_app_shutdown below.
+ */
+#endif /* ifdef HAVE_LIBSCF */
+ /* Do not flush master files */
+ named_server_flushonshutdown(named_g_server, false);
+ named_os_shutdownmsg(cmdline, *text);
+ result = ISC_R_SHUTTINGDOWN;
+ } else if (command_compare(command, NAMED_COMMAND_STOP)) {
+ /*
+ * "stop" is the same as "halt" except it does
+ * flush master files.
+ */
+#ifdef HAVE_LIBSCF
+ if (named_smf_got_instance == 1 && named_smf_chroot == 1) {
+ result = named_smf_add_message(text);
+ goto cleanup;
+ }
+ if (named_smf_got_instance == 1 && named_smf_chroot == 0) {
+ named_smf_want_disable = 1;
+ }
+#endif /* ifdef HAVE_LIBSCF */
+ named_server_flushonshutdown(named_g_server, true);
+ named_os_shutdownmsg(cmdline, *text);
+ result = ISC_R_SHUTTINGDOWN;
+ } else if (command_compare(command, NAMED_COMMAND_ADDZONE) ||
+ command_compare(command, NAMED_COMMAND_MODZONE))
+ {
+ result = named_server_changezone(named_g_server, cmdline, text);
+ } else if (command_compare(command, NAMED_COMMAND_DELZONE)) {
+ result = named_server_delzone(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_DNSSEC)) {
+ result = named_server_dnssec(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_DNSTAP) ||
+ command_compare(command, NAMED_COMMAND_DNSTAPREOPEN))
+ {
+ result = named_server_dnstap(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_DUMPDB)) {
+ named_server_dumpdb(named_g_server, lex, text);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) {
+ result = named_server_dumpstats(named_g_server);
+ } else if (command_compare(command, NAMED_COMMAND_FLUSH)) {
+ result = named_server_flushcache(named_g_server, lex);
+ } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) {
+ result = named_server_flushnode(named_g_server, lex, false);
+ } else if (command_compare(command, NAMED_COMMAND_FLUSHTREE)) {
+ result = named_server_flushnode(named_g_server, lex, true);
+ } else if (command_compare(command, NAMED_COMMAND_FREEZE)) {
+ result = named_server_freeze(named_g_server, true, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_LOADKEYS) ||
+ command_compare(command, NAMED_COMMAND_SIGN))
+ {
+ result = named_server_rekey(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_MKEYS)) {
+ result = named_server_mkeys(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_NOTIFY)) {
+ result = named_server_notifycommand(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_NOTRACE)) {
+ named_g_debuglevel = 0;
+ isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NAMED_COMMAND_NTA)) {
+ result = named_server_nta(named_g_server, lex, readonly, text);
+ } else if (command_compare(command, NAMED_COMMAND_NULL)) {
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NAMED_COMMAND_QUERYLOG)) {
+ result = named_server_togglequerylog(named_g_server, lex);
+ } else if (command_compare(command, NAMED_COMMAND_RECONFIG)) {
+ result = named_server_reconfigcommand(named_g_server);
+ } else if (command_compare(command, NAMED_COMMAND_RECURSING)) {
+ result = named_server_dumprecursing(named_g_server);
+ } else if (command_compare(command, NAMED_COMMAND_REFRESH)) {
+ result = named_server_refreshcommand(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_RELOAD)) {
+ result = named_server_reloadcommand(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_RETRANSFER)) {
+ result = named_server_retransfercommand(named_g_server, lex,
+ text);
+ } else if (command_compare(command, NAMED_COMMAND_SCAN)) {
+ named_server_scan_interfaces(named_g_server);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NAMED_COMMAND_SECROOTS)) {
+ result = named_server_dumpsecroots(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_SERVESTALE)) {
+ result = named_server_servestale(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_SHOWZONE)) {
+ result = named_server_showzone(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_SIGNING)) {
+ result = named_server_signing(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_STATUS)) {
+ result = named_server_status(named_g_server, text);
+ } else if (command_compare(command, NAMED_COMMAND_SYNC)) {
+ result = named_server_sync(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_TCPTIMEOUTS)) {
+ result = named_server_tcptimeouts(lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_TESTGEN)) {
+ result = named_server_testgen(lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_THAW) ||
+ command_compare(command, NAMED_COMMAND_UNFREEZE))
+ {
+ result = named_server_freeze(named_g_server, false, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_TIMERPOKE)) {
+ isc_timermgr_poke(named_g_timermgr);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NAMED_COMMAND_TRACE)) {
+ result = named_server_setdebuglevel(named_g_server, lex);
+ } else if (command_compare(command, NAMED_COMMAND_TSIGDELETE)) {
+ result = named_server_tsigdelete(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_TSIGLIST)) {
+ result = named_server_tsiglist(named_g_server, text);
+ } else if (command_compare(command, NAMED_COMMAND_VALIDATION)) {
+ result = named_server_validation(named_g_server, lex, text);
+ } else if (command_compare(command, NAMED_COMMAND_ZONESTATUS)) {
+ result = named_server_zonestatus(named_g_server, lex, text);
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "unknown control channel command '%s'", command);
+ result = DNS_R_UNKNOWNCOMMAND;
+ }
+
+cleanup:
+ if (lex != NULL) {
+ isc_lex_destroy(&lex);
+ }
+
+ return (result);
+}
diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c
new file mode 100644
index 0000000..d402155
--- /dev/null
+++ b/bin/named/controlconf.c
@@ -0,0 +1,1494 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/app.h>
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/event.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/netmgr.h>
+#include <isc/nonce.h>
+#include <isc/random.h>
+#include <isc/result.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+#include <isccc/ccmsg.h>
+#include <isccc/events.h>
+#include <isccc/sexpr.h>
+#include <isccc/symtab.h>
+#include <isccc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <named/config.h>
+#include <named/control.h>
+#include <named/log.h>
+#include <named/server.h>
+
+typedef struct controlkey controlkey_t;
+typedef ISC_LIST(controlkey_t) controlkeylist_t;
+
+typedef struct controlconnection controlconnection_t;
+typedef ISC_LIST(controlconnection_t) controlconnectionlist_t;
+
+typedef struct controllistener controllistener_t;
+typedef ISC_LIST(controllistener_t) controllistenerlist_t;
+
+struct controlkey {
+ char *keyname;
+ uint32_t algorithm;
+ isc_region_t secret;
+ ISC_LINK(controlkey_t) link;
+};
+
+struct controlconnection {
+ isc_nmhandle_t *readhandle;
+ isc_nmhandle_t *sendhandle;
+ isc_nmhandle_t *cmdhandle;
+ isccc_ccmsg_t ccmsg;
+ bool reading;
+ bool sending;
+ controllistener_t *listener;
+ isccc_sexpr_t *ctrl;
+ isc_buffer_t *buffer;
+ isc_buffer_t *text;
+ isccc_sexpr_t *request;
+ isccc_sexpr_t *response;
+ uint32_t alg;
+ isccc_region_t secret;
+ uint32_t nonce;
+ isc_stdtime_t now;
+ isc_result_t result;
+ ISC_LINK(controlconnection_t) link;
+};
+
+struct controllistener {
+ named_controls_t *controls;
+ isc_mem_t *mctx;
+ isc_sockaddr_t address;
+ isc_nmsocket_t *sock;
+ dns_acl_t *acl;
+ bool exiting;
+ isc_refcount_t refs;
+ controlkeylist_t keys;
+ isc_mutex_t connections_lock;
+ controlconnectionlist_t connections;
+ isc_socktype_t type;
+ uint32_t perm;
+ uint32_t owner;
+ uint32_t group;
+ bool readonly;
+ ISC_LINK(controllistener_t) link;
+};
+
+struct named_controls {
+ named_server_t *server;
+ controllistenerlist_t listeners;
+ atomic_bool shuttingdown;
+ isc_mutex_t symtab_lock;
+ isccc_symtab_t *symtab;
+};
+
+static isc_result_t
+control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg);
+static void
+control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg);
+
+#define CLOCKSKEW 300
+
+static void
+free_controlkey(controlkey_t *key, isc_mem_t *mctx) {
+ if (key->keyname != NULL) {
+ isc_mem_free(mctx, key->keyname);
+ }
+ if (key->secret.base != NULL) {
+ isc_mem_put(mctx, key->secret.base, key->secret.length);
+ }
+ isc_mem_put(mctx, key, sizeof(*key));
+}
+
+static void
+free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) {
+ while (!ISC_LIST_EMPTY(*keylist)) {
+ controlkey_t *key = ISC_LIST_HEAD(*keylist);
+ ISC_LIST_UNLINK(*keylist, key, link);
+ free_controlkey(key, mctx);
+ }
+}
+
+static void
+free_listener(controllistener_t *listener) {
+ INSIST(listener->exiting);
+ INSIST(ISC_LIST_EMPTY(listener->connections));
+
+ isc_refcount_destroy(&listener->refs);
+
+ if (listener->sock != NULL) {
+ isc_nmsocket_close(&listener->sock);
+ }
+
+ free_controlkeylist(&listener->keys, listener->mctx);
+
+ if (listener->acl != NULL) {
+ dns_acl_detach(&listener->acl);
+ }
+ isc_mutex_destroy(&listener->connections_lock);
+
+ isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
+}
+
+static void
+maybe_free_listener(controllistener_t *listener) {
+ if (isc_refcount_decrement(&listener->refs) == 1) {
+ free_listener(listener);
+ }
+}
+
+static void
+shutdown_listener(controllistener_t *listener) {
+ if (!listener->exiting) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_UNLINK(listener->controls->listeners, listener, link);
+
+ isc_sockaddr_format(&listener->address, socktext,
+ sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "stopping command channel on %s", socktext);
+#if 0
+ /* XXX: no unix domain socket support */
+ if (listener->type == isc_socktype_unix) {
+ isc_socket_cleanunix(&listener->address, true);
+ }
+#endif
+ listener->exiting = true;
+ }
+
+ isc_nm_stoplistening(listener->sock);
+ maybe_free_listener(listener);
+}
+
+static bool
+address_ok(isc_sockaddr_t *sockaddr, controllistener_t *listener) {
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
+ isc_netaddr_t netaddr;
+ isc_result_t result;
+ int match;
+
+ /* ACL doesn't apply to unix domain sockets */
+ if (listener->type != isc_socktype_tcp) {
+ return (true);
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+
+ result = dns_acl_match(&netaddr, NULL, listener->acl, env, &match,
+ NULL);
+ return (result == ISC_R_SUCCESS && match > 0);
+}
+
+static void
+control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ controlconnection_t *conn = (controlconnection_t *)arg;
+ controllistener_t *listener = conn->listener;
+ isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle);
+
+ REQUIRE(conn->sending);
+
+ conn->sending = false;
+
+ if (conn->result == ISC_R_SHUTTINGDOWN) {
+ isc_app_shutdown();
+ goto cleanup_sendhandle;
+ }
+
+ if (atomic_load_acquire(&listener->controls->shuttingdown) ||
+ result == ISC_R_SHUTTINGDOWN)
+ {
+ goto cleanup_sendhandle;
+ } else if (result != ISC_R_SUCCESS) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "error sending command response to %s: %s",
+ socktext, isc_result_totext(result));
+ goto cleanup_sendhandle;
+ }
+
+ isc_nmhandle_attach(handle, &conn->readhandle);
+ conn->reading = true;
+
+ isc_nmhandle_detach(&conn->sendhandle);
+
+ isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn);
+ return;
+
+cleanup_sendhandle:
+ isc_nmhandle_detach(&conn->sendhandle);
+}
+
+static void
+log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(ccmsg->handle);
+
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_ERROR,
+ "invalid command from %s: %s", socktext,
+ isc_result_totext(result));
+}
+
+static void
+conn_cleanup(controlconnection_t *conn) {
+ controllistener_t *listener = conn->listener;
+
+ if (conn->response != NULL) {
+ isccc_sexpr_free(&conn->response);
+ }
+ if (conn->request != NULL) {
+ isccc_sexpr_free(&conn->request);
+ }
+ if (conn->secret.rstart != NULL) {
+ isc_mem_put(listener->mctx, conn->secret.rstart,
+ REGION_SIZE(conn->secret));
+ }
+ if (conn->text != NULL) {
+ isc_buffer_free(&conn->text);
+ }
+}
+
+static void
+control_respond(isc_nmhandle_t *handle, controlconnection_t *conn) {
+ controllistener_t *listener = conn->listener;
+ isccc_sexpr_t *data = NULL;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+
+ result = isccc_cc_createresponse(conn->request, conn->now,
+ conn->now + 60, &conn->response);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (conn->result == ISC_R_SHUTTINGDOWN) {
+ result = ISC_R_SUCCESS;
+ } else {
+ result = conn->result;
+ }
+
+ data = isccc_alist_lookup(conn->response, "_data");
+ if (data != NULL) {
+ if (isccc_cc_defineuint32(data, "result", result) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ if (data != NULL) {
+ const char *estr = isc_result_totext(result);
+ if (isccc_cc_definestring(data, "err", estr) == NULL) {
+ goto cleanup;
+ }
+ }
+ }
+
+ if (isc_buffer_usedlength(conn->text) > 0) {
+ if (data != NULL) {
+ char *str = (char *)isc_buffer_base(conn->text);
+ if (isccc_cc_definestring(data, "text", str) == NULL) {
+ goto cleanup;
+ }
+ }
+ }
+
+ conn->ctrl = isccc_alist_lookup(conn->response, "_ctrl");
+ if (conn->ctrl == NULL ||
+ isccc_cc_defineuint32(conn->ctrl, "_nonce", conn->nonce) == NULL)
+ {
+ goto cleanup;
+ }
+
+ if (conn->buffer == NULL) {
+ isc_buffer_allocate(listener->mctx, &conn->buffer, 2 * 2048);
+ }
+
+ isc_buffer_clear(conn->buffer);
+ /* Skip the length field (4 bytes) */
+ isc_buffer_add(conn->buffer, 4);
+
+ result = isccc_cc_towire(conn->response, &conn->buffer, conn->alg,
+ &conn->secret);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ isc_buffer_init(&b, conn->buffer->base, 4);
+ isc_buffer_putuint32(&b, conn->buffer->used - 4);
+
+ r.base = conn->buffer->base;
+ r.length = conn->buffer->used;
+
+ isc_nmhandle_attach(handle, &conn->sendhandle);
+ conn->sending = true;
+ conn_cleanup(conn);
+
+ isc_nmhandle_detach(&conn->cmdhandle);
+
+ isc_nm_send(conn->sendhandle, &r, control_senddone, conn);
+
+ return;
+
+cleanup:
+ conn_cleanup(conn);
+ isc_nmhandle_detach(&conn->cmdhandle);
+}
+
+static void
+control_command(isc_task_t *task, isc_event_t *event) {
+ controlconnection_t *conn = event->ev_arg;
+ controllistener_t *listener = conn->listener;
+
+ UNUSED(task);
+
+ if (atomic_load_acquire(&listener->controls->shuttingdown)) {
+ conn_cleanup(conn);
+ isc_nmhandle_detach(&conn->cmdhandle);
+ goto done;
+ }
+
+ conn->result = named_control_docommand(conn->request,
+ listener->readonly, &conn->text);
+ control_respond(conn->cmdhandle, conn);
+
+done:
+ isc_event_free(&event);
+}
+
+static void
+control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ controlconnection_t *conn = (controlconnection_t *)arg;
+ controllistener_t *listener = conn->listener;
+ controlkey_t *key = NULL;
+ isc_event_t *event = NULL;
+ isccc_time_t sent;
+ isccc_time_t exp;
+ uint32_t nonce;
+
+ conn->reading = false;
+
+ /* Is the server shutting down? */
+ if (atomic_load_acquire(&listener->controls->shuttingdown)) {
+ goto cleanup_readhandle;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_SHUTTINGDOWN) {
+ atomic_store_release(&listener->controls->shuttingdown,
+ true);
+ } else if (result != ISC_R_EOF) {
+ log_invalid(&conn->ccmsg, result);
+ }
+
+ goto cleanup_readhandle;
+ }
+
+ for (key = ISC_LIST_HEAD(listener->keys); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ isccc_region_t ccregion;
+
+ ccregion.rstart = isc_buffer_base(conn->ccmsg.buffer);
+ ccregion.rend = isc_buffer_used(conn->ccmsg.buffer);
+ conn->secret.rstart = isc_mem_get(listener->mctx,
+ key->secret.length);
+ memmove(conn->secret.rstart, key->secret.base,
+ key->secret.length);
+ conn->secret.rend = conn->secret.rstart + key->secret.length;
+ conn->alg = key->algorithm;
+ result = isccc_cc_fromwire(&ccregion, &conn->request, conn->alg,
+ &conn->secret);
+ if (result == ISC_R_SUCCESS) {
+ break;
+ }
+ isc_mem_put(listener->mctx, conn->secret.rstart,
+ REGION_SIZE(conn->secret));
+ }
+
+ if (key == NULL) {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup;
+ }
+
+ /* We shouldn't be getting a reply. */
+ if (isccc_cc_isreply(conn->request)) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ isc_stdtime_get(&conn->now);
+
+ /*
+ * Limit exposure to replay attacks.
+ */
+ conn->ctrl = isccc_alist_lookup(conn->request, "_ctrl");
+ if (!isccc_alist_alistp(conn->ctrl)) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ if (isccc_cc_lookupuint32(conn->ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
+ if ((sent + CLOCKSKEW) < conn->now ||
+ (sent - CLOCKSKEW) > conn->now)
+ {
+ log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
+ goto cleanup;
+ }
+ } else {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ /*
+ * Expire messages that are too old.
+ */
+ if (isccc_cc_lookupuint32(conn->ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
+ conn->now > exp)
+ {
+ log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
+ goto cleanup;
+ }
+
+ /*
+ * Duplicate suppression (required for UDP).
+ */
+ LOCK(&listener->controls->symtab_lock);
+ isccc_cc_cleansymtab(listener->controls->symtab, conn->now);
+ result = isccc_cc_checkdup(listener->controls->symtab, conn->request,
+ conn->now);
+ UNLOCK(&listener->controls->symtab_lock);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_EXISTS) {
+ result = ISCCC_R_DUPLICATE;
+ }
+ log_invalid(&conn->ccmsg, result);
+ goto cleanup;
+ }
+
+ if (conn->nonce != 0 &&
+ (isccc_cc_lookupuint32(conn->ctrl, "_nonce", &nonce) !=
+ ISC_R_SUCCESS ||
+ conn->nonce != nonce))
+ {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup;
+ }
+
+ isc_buffer_allocate(listener->mctx, &conn->text, 2 * 2048);
+
+ isc_nmhandle_attach(handle, &conn->cmdhandle);
+ isc_nmhandle_detach(&conn->readhandle);
+
+ if (conn->nonce == 0) {
+ /*
+ * Establish nonce.
+ */
+ while (conn->nonce == 0) {
+ isc_nonce_buf(&conn->nonce, sizeof(conn->nonce));
+ }
+ conn->result = ISC_R_SUCCESS;
+ control_respond(handle, conn);
+ return;
+ }
+
+ /*
+ * Trigger the command.
+ */
+
+ event = isc_event_allocate(listener->mctx, conn, NAMED_EVENT_COMMAND,
+ control_command, conn, sizeof(isc_event_t));
+ isc_task_send(named_g_server->task, &event);
+
+ return;
+
+cleanup:
+ conn_cleanup(conn);
+
+cleanup_readhandle:
+ /*
+ * readhandle could be NULL if we're shutting down,
+ * but if not we need to detach it.
+ */
+ if (conn->readhandle != NULL) {
+ isc_nmhandle_detach(&conn->readhandle);
+ }
+}
+
+static void
+conn_reset(void *arg) {
+ controlconnection_t *conn = (controlconnection_t *)arg;
+ controllistener_t *listener = conn->listener;
+
+ if (conn->buffer != NULL) {
+ isc_buffer_free(&conn->buffer);
+ }
+
+ if (conn->reading) {
+ isccc_ccmsg_cancelread(&conn->ccmsg);
+ return;
+ }
+
+ LOCK(&listener->connections_lock);
+ ISC_LIST_UNLINK(listener->connections, conn, link);
+ UNLOCK(&listener->connections_lock);
+#ifdef ENABLE_AFL
+ if (named_g_fuzz_type == isc_fuzz_rndc) {
+ named_fuzz_notify();
+ }
+#endif /* ifdef ENABLE_AFL */
+
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+}
+
+static void
+conn_put(void *arg) {
+ controlconnection_t *conn = (controlconnection_t *)arg;
+ controllistener_t *listener = conn->listener;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3),
+ "freeing control connection");
+ maybe_free_listener(listener);
+}
+
+static void
+newconnection(controllistener_t *listener, isc_nmhandle_t *handle) {
+ controlconnection_t *conn = NULL;
+
+ conn = isc_nmhandle_getdata(handle);
+ if (conn == NULL) {
+ conn = isc_nmhandle_getextra(handle);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3),
+ "allocate new control connection");
+ isc_nmhandle_setdata(handle, conn, conn_reset, conn_put);
+ isc_refcount_increment(&listener->refs);
+ }
+
+ *conn = (controlconnection_t){ .listener = listener,
+ .reading = false,
+ .alg = DST_ALG_UNKNOWN };
+
+ isccc_ccmsg_init(listener->mctx, handle, &conn->ccmsg);
+
+ /* Set a 32 KiB upper limit on incoming message. */
+ isccc_ccmsg_setmaxsize(&conn->ccmsg, 32768);
+
+ LOCK(&listener->connections_lock);
+ ISC_LIST_INITANDAPPEND(listener->connections, conn, link);
+ UNLOCK(&listener->connections_lock);
+
+ isc_nmhandle_attach(handle, &conn->readhandle);
+ conn->reading = true;
+
+ isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn);
+}
+
+static isc_result_t
+control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ controllistener_t *listener = arg;
+ isc_sockaddr_t peeraddr;
+
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_SHUTTINGDOWN) {
+ shutdown_listener(listener);
+ }
+ return (result);
+ }
+
+ peeraddr = isc_nmhandle_peeraddr(handle);
+ if (!address_ok(&peeraddr, listener)) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "rejected command channel message from %s",
+ socktext);
+ return (ISC_R_FAILURE);
+ }
+
+ newconnection(listener, handle);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+controls_shutdown(named_controls_t *controls) {
+ controllistener_t *listener = NULL;
+ controllistener_t *next = NULL;
+
+ for (listener = ISC_LIST_HEAD(controls->listeners); listener != NULL;
+ listener = next)
+ {
+ /*
+ * This is asynchronous. As listeners shut down, they will
+ * call their callbacks.
+ */
+ next = ISC_LIST_NEXT(listener, link);
+ shutdown_listener(listener);
+ }
+}
+
+void
+named_controls_shutdown(named_controls_t *controls) {
+ controls_shutdown(controls);
+ atomic_store_release(&controls->shuttingdown, true);
+}
+
+static isc_result_t
+cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
+ const cfg_obj_t **objp) {
+ const cfg_listelt_t *element = NULL;
+ const char *str = NULL;
+ const cfg_obj_t *obj = NULL;
+
+ for (element = cfg_list_first(keylist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_map_getname(obj));
+ if (strcasecmp(str, keyname) == 0) {
+ break;
+ }
+ }
+ if (element == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+ obj = cfg_listelt_value(element);
+ *objp = obj;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
+ controlkeylist_t *keyids) {
+ const cfg_listelt_t *element = NULL;
+ char *newstr = NULL;
+ const char *str = NULL;
+ const cfg_obj_t *obj = NULL;
+ controlkey_t *key = NULL;
+
+ for (element = cfg_list_first(keylist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+ newstr = isc_mem_strdup(mctx, str);
+ key = isc_mem_get(mctx, sizeof(*key));
+ key->keyname = newstr;
+ key->algorithm = DST_ALG_UNKNOWN;
+ key->secret.base = NULL;
+ key->secret.length = 0;
+ ISC_LINK_INIT(key, link);
+ ISC_LIST_APPEND(*keyids, key, link);
+ newstr = NULL;
+ }
+}
+
+static void
+register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
+ controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) {
+ controlkey_t *keyid = NULL, *next = NULL;
+ const cfg_obj_t *keydef = NULL;
+ char secret[1024];
+ isc_buffer_t b;
+ isc_result_t result;
+
+ /*
+ * Find the keys corresponding to the keyids used by this listener.
+ */
+ for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
+ next = ISC_LIST_NEXT(keyid, link);
+
+ result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't find key '%s' for use with "
+ "command channel %s",
+ keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ } else {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+ unsigned int algtype;
+
+ (void)cfg_map_get(keydef, "algorithm", &algobj);
+ (void)cfg_map_get(keydef, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ result = named_config_getkeyalgorithm2(algstr, NULL,
+ &algtype, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(control, named_g_lctx,
+ ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel %s",
+ algstr, keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ keyid->algorithm = algtype;
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(keydef, named_g_lctx,
+ ISC_LOG_WARNING,
+ "secret for key '%s' on "
+ "command channel %s: %s",
+ keyid->keyname, socktext,
+ isc_result_totext(result));
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx,
+ keyid->secret.length);
+ memmove(keyid->secret.base, isc_buffer_base(&b),
+ keyid->secret.length);
+ }
+ }
+}
+
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) { \
+ goto cleanup; \
+ } \
+ } while (0)
+
+static isc_result_t
+get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
+ isc_result_t result;
+ cfg_parser_t *pctx = NULL;
+ cfg_obj_t *config = NULL;
+ const cfg_obj_t *key = NULL;
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+ controlkey_t *keyid = NULL;
+ char secret[1024];
+ unsigned int algtype;
+ isc_buffer_t b;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_INFO,
+ "configuring command channel from '%s'", named_g_keyfile);
+ if (!isc_file_exists(named_g_keyfile)) {
+ return (ISC_R_FILENOTFOUND);
+ }
+
+ CHECK(cfg_parser_create(mctx, named_g_lctx, &pctx));
+ CHECK(cfg_parse_file(pctx, named_g_keyfile, &cfg_type_rndckey,
+ &config));
+ CHECK(cfg_map_get(config, "key", &key));
+
+ keyid = isc_mem_get(mctx, sizeof(*keyid));
+ keyid->keyname = isc_mem_strdup(mctx,
+ cfg_obj_asstring(cfg_map_getname(key)));
+ keyid->secret.base = NULL;
+ keyid->secret.length = 0;
+ keyid->algorithm = DST_ALG_UNKNOWN;
+ ISC_LINK_INIT(keyid, link);
+ if (keyid->keyname == NULL) {
+ CHECK(ISC_R_NOMEMORY);
+ }
+
+ CHECK(bind9_check_key(key, named_g_lctx));
+
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ result = named_config_getkeyalgorithm2(algstr, NULL, &algtype, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel",
+ algstr, keyid->keyname);
+ goto cleanup;
+ }
+
+ keyid->algorithm = algtype;
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING,
+ "secret for key '%s' on command channel: %s",
+ keyid->keyname, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx, keyid->secret.length);
+ memmove(keyid->secret.base, isc_buffer_base(&b), keyid->secret.length);
+ ISC_LIST_APPEND(*keyids, keyid, link);
+ keyid = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (keyid != NULL) {
+ free_controlkey(keyid, mctx);
+ }
+ if (config != NULL) {
+ cfg_obj_destroy(pctx, &config);
+ }
+ if (pctx != NULL) {
+ cfg_parser_destroy(&pctx);
+ }
+ return (result);
+}
+
+/*
+ * Ensures that both '*global_keylistp' and '*control_keylistp' are
+ * valid or both are NULL.
+ */
+static void
+get_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
+ const cfg_obj_t **global_keylistp,
+ const cfg_obj_t **control_keylistp) {
+ isc_result_t result;
+ const cfg_obj_t *control_keylist = NULL;
+ const cfg_obj_t *global_keylist = NULL;
+
+ REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
+ REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
+
+ control_keylist = cfg_tuple_get(control, "keys");
+
+ if (!cfg_obj_isvoid(control_keylist) &&
+ cfg_list_first(control_keylist) != NULL)
+ {
+ result = cfg_map_get(config, "key", &global_keylist);
+
+ if (result == ISC_R_SUCCESS) {
+ *global_keylistp = global_keylist;
+ *control_keylistp = control_keylist;
+ }
+ }
+}
+
+static void
+update_listener(named_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_socktype_t type) {
+ controllistener_t *listener = NULL;
+ const cfg_obj_t *allow = NULL;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ controlkeylist_t keys;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (listener = ISC_LIST_HEAD(cp->listeners); listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ {
+ if (isc_sockaddr_equal(addr, &listener->address)) {
+ break;
+ }
+ }
+
+ if (listener == NULL) {
+ *listenerp = NULL;
+ return;
+ }
+
+ /*
+ * There is already a listener for this sockaddr.
+ * Update the access list and key information.
+ *
+ * First try to deal with the key situation. There are a few
+ * possibilities:
+ * (a) It had an explicit keylist and still has an explicit keylist.
+ * (b) It had an automagic key and now has an explicit keylist.
+ * (c) It had an explicit keylist and now needs an automagic key.
+ * (d) It has an automagic key and still needs the automagic key.
+ *
+ * (c) and (d) are the annoying ones. The caller needs to know
+ * that it should use the automagic configuration for key information
+ * in place of the named.conf configuration.
+ *
+ * XXXDCL There is one other hazard that has not been dealt with,
+ * the problem that if a key change is being caused by a control
+ * channel reload, then the response will be with the new key
+ * and not able to be decrypted by the client.
+ */
+ if (control != NULL) {
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+ }
+
+ if (control_keylist != NULL) {
+ INSIST(global_keylist != NULL);
+
+ ISC_LIST_INIT(keys);
+ controlkeylist_fromcfg(control_keylist, listener->mctx, &keys);
+ free_controlkeylist(&listener->keys, listener->mctx);
+ listener->keys = keys;
+ register_keys(control, global_keylist, &listener->keys,
+ listener->mctx, socktext);
+ } else {
+ free_controlkeylist(&listener->keys, listener->mctx);
+ result = get_rndckey(listener->mctx, &listener->keys);
+ }
+
+ if (result != ISC_R_SUCCESS && global_keylist != NULL) {
+ /*
+ * This message might be a little misleading since the
+ * "new keys" might in fact be identical to the old ones,
+ * but tracking whether they are identical just for the
+ * sake of avoiding this message would be too much trouble.
+ */
+ if (control != NULL) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+ }
+
+ /*
+ * Now, keep the old access list unless a new one can be made.
+ */
+ if (control != NULL && type == isc_socktype_tcp) {
+ allow = cfg_tuple_get(control, "allow");
+ result = cfg_acl_fromconfig(allow, config, named_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else {
+ result = dns_acl_any(listener->mctx, &new_acl);
+ }
+
+ if (control != NULL) {
+ const cfg_obj_t *readonly = NULL;
+
+ readonly = cfg_tuple_get(control, "read-only");
+ if (!cfg_obj_isvoid(readonly)) {
+ listener->readonly = cfg_obj_asboolean(readonly);
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_detach(&listener->acl);
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+ /* XXXDCL say the old acl is still used? */
+ } else if (control != NULL) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+#if 0
+ /* XXX: no unix socket support yet */
+ if (result == ISC_R_SUCCESS && type == isc_socktype_unix) {
+ uint32_t perm, owner, group;
+ perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
+ owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
+ group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
+ result = ISC_R_SUCCESS;
+ if (listener->perm != perm || listener->owner != owner ||
+ listener->group != group)
+ {
+ result = isc_socket_permunix(&listener->address, perm,
+ owner, group);
+ }
+ if (result == ISC_R_SUCCESS) {
+ listener->perm = perm;
+ listener->owner = owner;
+ listener->group = group;
+ } else if (control != NULL) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't update ownership/permission for "
+ "command channel %s",
+ socktext);
+ }
+ }
+#endif
+
+ *listenerp = listener;
+}
+
+static void
+add_listener(named_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_socktype_t type) {
+ isc_mem_t *mctx = cp->server->mctx;
+ controllistener_t *listener = NULL;
+ const cfg_obj_t *allow = NULL;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ int pf;
+
+ listener = isc_mem_get(mctx, sizeof(*listener));
+ *listener = (controllistener_t){ .controls = cp,
+ .address = *addr,
+ .type = type };
+ isc_mem_attach(mctx, &listener->mctx);
+ isc_mutex_init(&listener->connections_lock);
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_INIT(listener->keys);
+ ISC_LIST_INIT(listener->connections);
+ isc_refcount_init(&listener->refs, 1);
+
+ /*
+ * Make the ACL.
+ */
+ if (control != NULL && type == isc_socktype_tcp) {
+ const cfg_obj_t *readonly = NULL;
+
+ allow = cfg_tuple_get(control, "allow");
+ CHECK(cfg_acl_fromconfig(allow, config, named_g_lctx,
+ aclconfctx, mctx, 0, &new_acl));
+
+ readonly = cfg_tuple_get(control, "read-only");
+ if (!cfg_obj_isvoid(readonly)) {
+ listener->readonly = cfg_obj_asboolean(readonly);
+ }
+ } else {
+ CHECK(dns_acl_any(mctx, &new_acl));
+ }
+
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ if (config != NULL) {
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+ }
+
+ if (control_keylist != NULL) {
+ controlkeylist_fromcfg(control_keylist, listener->mctx,
+ &listener->keys);
+ register_keys(control, global_keylist, &listener->keys,
+ listener->mctx, socktext);
+ } else {
+ result = get_rndckey(mctx, &listener->keys);
+ if (result != ISC_R_SUCCESS && control != NULL) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't install keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+ }
+
+ pf = isc_sockaddr_pf(&listener->address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+ (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ {
+ CHECK(ISC_R_FAMILYNOSUPPORT);
+ }
+
+#if 0
+ /* XXX: no unix socket support yet */
+ if (type == isc_socktype_unix) {
+ isc_socket_cleanunix(&listener->address, false);
+ }
+#endif
+
+ CHECK(isc_nm_listentcp(
+ named_g_netmgr, &listener->address, control_newconn, listener,
+ sizeof(controlconnection_t), 5, NULL, &listener->sock));
+#if 0
+ /* XXX: no unix socket support yet */
+ if (type == isc_socktype_unix) {
+ listener->perm =
+ cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
+ listener->owner =
+ cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
+ listener->group =
+ cfg_obj_asuint32(cfg_tuple_get(control, "group"));
+ result = isc_socket_permunix(&listener->address, listener->perm,
+ listener->owner, listener->group);
+ }
+#endif
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "command channel listening on %s", socktext);
+ *listenerp = listener;
+ return;
+
+cleanup:
+ isc_refcount_decrement(&listener->refs);
+ listener->exiting = true;
+ free_listener(listener);
+
+ if (control != NULL) {
+ cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't add command channel %s: %s", socktext,
+ isc_result_totext(result));
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "couldn't add command channel %s: %s", socktext,
+ isc_result_totext(result));
+ }
+
+ *listenerp = NULL;
+
+ /* XXXDCL return error results? fail hard? */
+}
+
+isc_result_t
+named_controls_configure(named_controls_t *cp, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx) {
+ controllistener_t *listener = NULL;
+ controllistenerlist_t new_listeners;
+ const cfg_obj_t *controlslist = NULL;
+ const cfg_listelt_t *element, *element2;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_INIT(new_listeners);
+
+ /*
+ * Get the list of named.conf 'controls' statements.
+ */
+ (void)cfg_map_get(config, "controls", &controlslist);
+
+ /*
+ * Run through the new control channel list, noting sockets that
+ * are already being listened on and moving them to the new list.
+ *
+ * Identifying duplicate addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ if (controlslist != NULL) {
+ for (element = cfg_list_first(controlslist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *controls = NULL;
+ const cfg_obj_t *inetcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "inet", &inetcontrols);
+ if (inetcontrols == NULL) {
+ continue;
+ }
+
+ for (element2 = cfg_list_first(inetcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *control = NULL;
+ const cfg_obj_t *obj = NULL;
+ isc_sockaddr_t addr;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ obj = cfg_tuple_get(control, "address");
+ addr = *cfg_obj_assockaddr(obj);
+ if (isc_sockaddr_getport(&addr) == 0) {
+ isc_sockaddr_setport(
+ &addr, NAMED_CONTROL_PORT);
+ }
+
+ isc_sockaddr_format(&addr, socktext,
+ sizeof(socktext));
+
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel %s",
+ socktext);
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx, socktext,
+ isc_socktype_tcp);
+
+ if (listener != NULL) {
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners, listener,
+ link);
+ } else {
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ socktext,
+ isc_socktype_tcp);
+ }
+
+ if (listener != NULL) {
+ ISC_LIST_APPEND(new_listeners, listener,
+ link);
+ }
+ }
+ }
+ for (element = cfg_list_first(controlslist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *controls = NULL;
+ const cfg_obj_t *unixcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "unix", &unixcontrols);
+ if (unixcontrols == NULL) {
+ continue;
+ }
+
+ cfg_obj_log(controls, named_g_lctx, ISC_LOG_ERROR,
+ "UNIX domain sockets not yet supported");
+ return (ISC_R_FAILURE);
+
+#if 0
+ /* XXX: no unix domain socket support in netmgr */
+ for (element2 = cfg_list_first(unixcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *control = NULL;
+ const cfg_obj_t *path = NULL;
+ isc_sockaddr_t addr;
+ isc_result_t result;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ path = cfg_tuple_get(control, "path");
+ result = isc_sockaddr_frompath(
+ &addr, cfg_obj_asstring(path));
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "control channel '%s': %s",
+ cfg_obj_asstring(path),
+ isc_result_totext(result));
+ continue;
+ }
+
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel '%s'",
+ cfg_obj_asstring(path));
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_socktype_unix);
+
+ if (listener != NULL) {
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners, listener,
+ link);
+ } else {
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_socktype_unix);
+ }
+
+ if (listener != NULL) {
+ ISC_LIST_APPEND(new_listeners, listener,
+ link);
+ }
+ }
+#endif
+ }
+ } else {
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ isc_sockaddr_t addr;
+
+ if (i == 0) {
+ struct in_addr localhost;
+
+ if (isc_net_probeipv4() != ISC_R_SUCCESS) {
+ continue;
+ }
+ localhost.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&addr, &localhost, 0);
+ } else {
+ if (isc_net_probeipv6() != ISC_R_SUCCESS) {
+ continue;
+ }
+ isc_sockaddr_fromin6(&addr, &in6addr_loopback,
+ 0);
+ }
+ isc_sockaddr_setport(&addr, NAMED_CONTROL_PORT);
+
+ isc_sockaddr_format(&addr, socktext, sizeof(socktext));
+
+ update_listener(cp, &listener, NULL, NULL, &addr, NULL,
+ socktext, isc_socktype_tcp);
+
+ if (listener != NULL) {
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners, listener, link);
+ } else {
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, NULL, NULL, &addr,
+ NULL, socktext, isc_socktype_tcp);
+ }
+
+ if (listener != NULL) {
+ ISC_LIST_APPEND(new_listeners, listener, link);
+ }
+ }
+ }
+
+ /*
+ * named_control_shutdown() will stop whatever is on the global
+ * listeners list, which currently only has whatever sockaddrs
+ * were in the previous configuration (if any) that do not
+ * remain in the current configuration.
+ */
+ controls_shutdown(cp);
+
+ /*
+ * Put all of the valid listeners on the listeners list.
+ * Anything already on listeners in the process of shutting
+ * down will be taken care of by listen_done().
+ */
+ ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_controls_create(named_server_t *server, named_controls_t **ctrlsp) {
+ isc_mem_t *mctx = server->mctx;
+ isc_result_t result;
+ named_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
+
+ *controls = (named_controls_t){
+ .server = server,
+ };
+
+ ISC_LIST_INIT(controls->listeners);
+
+ atomic_init(&controls->shuttingdown, false);
+ isc_mutex_init(&controls->symtab_lock);
+ LOCK(&controls->symtab_lock);
+ result = isccc_cc_createsymtab(&controls->symtab);
+ UNLOCK(&controls->symtab_lock);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_mutex_destroy(&controls->symtab_lock);
+ isc_mem_put(server->mctx, controls, sizeof(*controls));
+ return (result);
+ }
+ *ctrlsp = controls;
+ return (ISC_R_SUCCESS);
+}
+
+void
+named_controls_destroy(named_controls_t **ctrlsp) {
+ named_controls_t *controls = *ctrlsp;
+ *ctrlsp = NULL;
+
+ REQUIRE(ISC_LIST_EMPTY(controls->listeners));
+
+ LOCK(&controls->symtab_lock);
+ isccc_symtab_destroy(&controls->symtab);
+ UNLOCK(&controls->symtab_lock);
+ isc_mutex_destroy(&controls->symtab_lock);
+ isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
+}
diff --git a/bin/named/dlz_dlopen_driver.c b/bin/named/dlz_dlopen_driver.c
new file mode 100644
index 0000000..f636cd2
--- /dev/null
+++ b/bin/named/dlz_dlopen_driver.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/dlz_dlopen.h>
+#include <dns/log.h>
+
+#include <dlz/dlz_dlopen_driver.h>
+#include <named/globals.h>
+
+static dns_sdlzimplementation_t *dlz_dlopen = NULL;
+
+typedef struct dlopen_data {
+ isc_mem_t *mctx;
+ char *dl_path;
+ char *dlzname;
+ uv_lib_t dl_handle;
+ void *dbdata;
+ unsigned int flags;
+ isc_mutex_t lock;
+ int version;
+ bool in_configure;
+
+ dlz_dlopen_version_t *dlz_version;
+ dlz_dlopen_create_t *dlz_create;
+ dlz_dlopen_findzonedb_t *dlz_findzonedb;
+ dlz_dlopen_lookup_t *dlz_lookup;
+ dlz_dlopen_authority_t *dlz_authority;
+ dlz_dlopen_allnodes_t *dlz_allnodes;
+ dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
+ dlz_dlopen_newversion_t *dlz_newversion;
+ dlz_dlopen_closeversion_t *dlz_closeversion;
+ dlz_dlopen_configure_t *dlz_configure;
+ dlz_dlopen_ssumatch_t *dlz_ssumatch;
+ dlz_dlopen_addrdataset_t *dlz_addrdataset;
+ dlz_dlopen_subrdataset_t *dlz_subrdataset;
+ dlz_dlopen_delrdataset_t *dlz_delrdataset;
+ dlz_dlopen_destroy_t *dlz_destroy;
+} dlopen_data_t;
+
+/* Modules can choose whether they are lock-safe or not. */
+#define MAYBE_LOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ !cd->in_configure) \
+ LOCK(&cd->lock); \
+ } while (0)
+
+#define MAYBE_UNLOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ !cd->in_configure) \
+ UNLOCK(&cd->lock); \
+ } while (0)
+
+/*
+ * Log a message at the given level.
+ */
+static void
+dlopen_log(int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ,
+ ISC_LOG_DEBUG(level), fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * SDLZ methods
+ */
+
+static isc_result_t
+dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzallnodes_t *allnodes) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_allnodes == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+ const char *client) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_allowzonexfr == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzlookup_t *lookup) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_authority == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_authority(zone, cd->dbdata, lookup);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
+ void *dbdata, dns_sdlzlookup_t *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_lookup(zone, name, cd->dbdata, lookup, methods,
+ clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Load a symbol from the library
+ */
+static void *
+dl_load_symbol(dlopen_data_t *cd, const char *symbol, bool mandatory) {
+ void *ptr = NULL;
+ int r = uv_dlsym(&cd->dl_handle, symbol, &ptr);
+ if (r != 0) {
+ const char *errmsg = uv_dlerror(&cd->dl_handle);
+ if (errmsg == NULL) {
+ errmsg = "returned function pointer is NULL";
+ }
+ if (mandatory) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: library '%s' is missing "
+ "required symbol '%s': %s",
+ cd->dl_path, symbol, errmsg);
+ }
+ }
+
+ return (ptr);
+}
+
+static void
+dlopen_dlz_destroy(void *driverarg, void *dbdata);
+
+/*
+ * Called at startup for each dlopen zone in named.conf
+ */
+static isc_result_t
+dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+ void *driverarg, void **dbdata) {
+ dlopen_data_t *cd;
+ isc_mem_t *mctx = NULL;
+ isc_result_t result = ISC_R_FAILURE;
+ int r;
+
+ UNUSED(driverarg);
+
+ if (argc < 2) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen driver for '%s' needs a path to "
+ "the shared library",
+ dlzname);
+ return (ISC_R_FAILURE);
+ }
+
+ isc_mem_create(&mctx);
+ cd = isc_mem_get(mctx, sizeof(*cd));
+ memset(cd, 0, sizeof(*cd));
+
+ cd->mctx = mctx;
+
+ cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
+ cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
+
+ /* Initialize the lock */
+ isc_mutex_init(&cd->lock);
+
+ r = uv_dlopen(cd->dl_path, &cd->dl_handle);
+ if (r != 0) {
+ const char *errmsg = uv_dlerror(&cd->dl_handle);
+ if (errmsg == NULL) {
+ errmsg = "unknown error";
+ }
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen failed to open library '%s': %s",
+ cd->dl_path, errmsg);
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ /* Find the symbols */
+ cd->dlz_version =
+ (dlz_dlopen_version_t *)dl_load_symbol(cd, "dlz_version", true);
+ cd->dlz_create = (dlz_dlopen_create_t *)dl_load_symbol(cd, "dlz_create",
+ true);
+ cd->dlz_lookup = (dlz_dlopen_lookup_t *)dl_load_symbol(cd, "dlz_lookup",
+ true);
+ cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)dl_load_symbol(
+ cd, "dlz_findzonedb", true);
+
+ if (cd->dlz_create == NULL || cd->dlz_version == NULL ||
+ cd->dlz_lookup == NULL || cd->dlz_findzonedb == NULL)
+ {
+ /* We're missing a required symbol */
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)dl_load_symbol(
+ cd, "dlz_allowzonexfr", false);
+ cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)dl_load_symbol(
+ cd, "dlz_allnodes", (cd->dlz_allowzonexfr != NULL));
+ cd->dlz_authority = (dlz_dlopen_authority_t *)dl_load_symbol(
+ cd, "dlz_authority", false);
+ cd->dlz_newversion = (dlz_dlopen_newversion_t *)dl_load_symbol(
+ cd, "dlz_newversion", false);
+ cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)dl_load_symbol(
+ cd, "dlz_closeversion", (cd->dlz_newversion != NULL));
+ cd->dlz_configure = (dlz_dlopen_configure_t *)dl_load_symbol(
+ cd, "dlz_configure", false);
+ cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)dl_load_symbol(
+ cd, "dlz_ssumatch", false);
+ cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)dl_load_symbol(
+ cd, "dlz_addrdataset", false);
+ cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)dl_load_symbol(
+ cd, "dlz_subrdataset", false);
+ cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)dl_load_symbol(
+ cd, "dlz_delrdataset", false);
+ cd->dlz_destroy = (dlz_dlopen_destroy_t *)dl_load_symbol(
+ cd, "dlz_destroy", false);
+
+ /* Check the version of the API is the same */
+ cd->version = cd->dlz_version(&cd->flags);
+ if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
+ cd->version > DLZ_DLOPEN_VERSION)
+ {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: %s: incorrect driver API version %d, "
+ "requires %d",
+ cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ /*
+ * Call the library's create function. Note that this is an
+ * extended version of dlz create, with the addition of
+ * named function pointers for helper functions that the
+ * driver will need. This avoids the need for the backend to
+ * link the BIND9 libraries
+ */
+ MAYBE_LOCK(cd);
+ result = cd->dlz_create(dlzname, argc - 1, argv + 1, &cd->dbdata, "log",
+ dlopen_log, "putrr", dns_sdlz_putrr,
+ "putnamedrr", dns_sdlz_putnamedrr,
+ "writeable_zone", dns_dlz_writeablezone, NULL);
+ MAYBE_UNLOCK(cd);
+ if (result != ISC_R_SUCCESS) {
+ goto failed;
+ }
+
+ *dbdata = cd;
+
+ return (ISC_R_SUCCESS);
+
+failed:
+ dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
+
+ dlopen_dlz_destroy(NULL, cd);
+
+ return (result);
+}
+
+/*
+ * Called when bind is shutting down
+ */
+static void
+dlopen_dlz_destroy(void *driverarg, void *dbdata) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_destroy && cd->dbdata) {
+ MAYBE_LOCK(cd);
+ cd->dlz_destroy(cd->dbdata);
+ MAYBE_UNLOCK(cd);
+ }
+
+ uv_dlclose(&cd->dl_handle);
+ isc_mutex_destroy(&cd->lock);
+ isc_mem_free(cd->mctx, cd->dl_path);
+ isc_mem_free(cd->mctx, cd->dlzname);
+ isc_mem_putanddetach(&cd->mctx, cd, sizeof(*cd));
+}
+
+/*
+ * Called to start a transaction
+ */
+static isc_result_t
+dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
+ void **versionp) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_newversion(zone, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Called to end a transaction
+ */
+static void
+dlopen_dlz_closeversion(const char *zone, bool commit, void *driverarg,
+ void *dbdata, void **versionp) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL) {
+ *versionp = NULL;
+ return;
+ }
+
+ MAYBE_LOCK(cd);
+ cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+}
+
+/*
+ * Called on startup to configure any writeable zones
+ */
+static isc_result_t
+dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *driverarg,
+ void *dbdata) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_configure == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ MAYBE_LOCK(cd);
+ cd->in_configure = true;
+ result = cd->dlz_configure(view, dlzdb, cd->dbdata);
+ cd->in_configure = false;
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Check for authority to change a name.
+ */
+static bool
+dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
+ const char *type, const char *key, uint32_t keydatalen,
+ unsigned char *keydata, void *driverarg, void *dbdata) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ bool ret;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_ssumatch == NULL) {
+ return (false);
+ }
+
+ MAYBE_LOCK(cd);
+ ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
+ keydata, cd->dbdata);
+ MAYBE_UNLOCK(cd);
+
+ return (ret);
+}
+
+/*
+ * Add an rdataset.
+ */
+static isc_result_t
+dlopen_dlz_addrdataset(const char *name, const char *rdatastr, void *driverarg,
+ void *dbdata, void *version) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_addrdataset == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Subtract an rdataset.
+ */
+static isc_result_t
+dlopen_dlz_subrdataset(const char *name, const char *rdatastr, void *driverarg,
+ void *dbdata, void *version) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_subrdataset == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Delete a rdataset.
+ */
+static isc_result_t
+dlopen_dlz_delrdataset(const char *name, const char *type, void *driverarg,
+ void *dbdata, void *version) {
+ dlopen_data_t *cd = (dlopen_data_t *)dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_delrdataset == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+static dns_sdlzmethods_t dlz_dlopen_methods = {
+ dlopen_dlz_create, dlopen_dlz_destroy, dlopen_dlz_findzonedb,
+ dlopen_dlz_lookup, dlopen_dlz_authority, dlopen_dlz_allnodes,
+ dlopen_dlz_allowzonexfr, dlopen_dlz_newversion, dlopen_dlz_closeversion,
+ dlopen_dlz_configure, dlopen_dlz_ssumatch, dlopen_dlz_addrdataset,
+ dlopen_dlz_subrdataset, dlopen_dlz_delrdataset
+};
+
+/*
+ * Register driver with BIND
+ */
+isc_result_t
+dlz_dlopen_init(isc_mem_t *mctx) {
+ isc_result_t result;
+
+ dlopen_log(2, "Registering DLZ_dlopen driver");
+
+ result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
+ DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
+ DNS_SDLZFLAG_THREADSAFE,
+ mctx, &dlz_dlopen);
+
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR("dns_sdlzregister() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ }
+
+ return (result);
+}
+
+/*
+ * Unregister the driver
+ */
+void
+dlz_dlopen_clear(void) {
+ dlopen_log(2, "Unregistering DLZ_dlopen driver");
+ if (dlz_dlopen != NULL) {
+ dns_sdlzunregister(&dlz_dlopen);
+ }
+}
diff --git a/bin/named/fuzz.c b/bin/named/fuzz.c
new file mode 100644
index 0000000..fb0d56f
--- /dev/null
+++ b/bin/named/fuzz.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <named/fuzz.h>
+
+#ifdef ENABLE_AFL
+#include <arpa/inet.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/condition.h>
+#include <isc/mutex.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/server.h>
+
+/*
+ * We are using pthreads directly because we might be using it with
+ * unthreaded version of BIND, where all thread functions are
+ * mocks. Since AFL for now only works on Linux it's not a problem.
+ */
+static pthread_cond_t cond;
+static pthread_mutex_t mutex;
+static bool ready;
+
+/*
+ * In "client:" mode, this thread reads fuzzed query messages from AFL
+ * from standard input and sends it to named's listening port (DNS) that
+ * is passed in the -A client:<address>:<port> option. It can be used to
+ * test named from the client side.
+ */
+static void *
+fuzz_thread_client(void *arg) {
+ char *host;
+ char *port;
+ struct sockaddr_in servaddr;
+ int sockfd;
+ void *buf;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "address:port" syntax. Due to
+ * the syntax used, this only supports IPv4 addresses.
+ */
+ host = strdup(named_g_fuzz_addr);
+ RUNTIME_CHECK(host != NULL);
+
+ port = strchr(host, ':');
+ RUNTIME_CHECK(port != NULL);
+ *port = 0;
+ ++port;
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(port));
+
+ free(host);
+
+ /*
+ * Wait for named to start. This is set in run_server() in the
+ * named thread.
+ */
+ while (!named_g_run_done) {
+ usleep(10000);
+ }
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(sockfd != -1);
+
+ buf = malloc(65536);
+ RUNTIME_CHECK(buf != NULL);
+
+ /*
+ * Processing fuzzed packets 100,000 times before shutting down
+ * the app.
+ */
+#ifdef __AFL_LOOP
+ for (int loop = 0; loop < 100000; loop++) {
+#else /* ifdef __AFL_LOOP */
+ {
+#endif /* ifdef __AFL_LOOP */
+ ssize_t length;
+ ssize_t sent;
+
+ length = read(0, buf, 65536);
+ if (length <= 0) {
+ usleep(1000000);
+ goto next;
+ }
+
+ /*
+ * Ignore packets that are larger than 4096 bytes.
+ */
+ if (length > 4096) {
+ /*
+ * AFL_CMIN doesn't support persistent mode, so
+ * shutdown the server.
+ */
+ if (getenv("AFL_CMIN")) {
+ free(buf);
+ close(sockfd);
+ named_server_flushonshutdown(named_g_server,
+ false);
+ isc_app_shutdown();
+ return (NULL);
+ }
+ raise(SIGSTOP);
+ goto next;
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+ ready = false;
+
+ sent = sendto(sockfd, buf, length, 0,
+ (struct sockaddr *)&servaddr, sizeof(servaddr));
+ RUNTIME_CHECK(sent == length);
+
+ /*
+ * Read the reply message from named to unclog it. Don't
+ * bother if there isn't a reply.
+ */
+ (void)recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL);
+
+ while (!ready) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+ next:;
+ }
+
+ free(buf);
+ close(sockfd);
+
+ named_server_flushonshutdown(named_g_server, false);
+ isc_app_shutdown();
+
+ return (NULL);
+}
+
+/*
+ * In "resolver:" mode, this thread reads fuzzed reply messages from AFL
+ * from standard input. It also sets up a listener as a remote
+ * authoritative server and sends a driver query to the client side of
+ * named(resolver). When named(resolver) connects to this authoritative
+ * server, this thread writes the fuzzed reply message from AFL to it.
+ *
+ * -A resolver:<saddress>:<sport>:<raddress>:<rport>
+ *
+ * Here, <saddress>:<sport> is where named(resolver) is listening on.
+ * <raddress>:<rport> is where the thread is supposed to setup the
+ * authoritative server. This address should be configured via the root
+ * zone to be the authoritiative server for aaaaaaaaaa.example.
+ *
+ * named(resolver) when being fuzzed will not cache answers.
+ */
+static void *
+fuzz_thread_resolver(void *arg) {
+ char *sqtype, *shost, *sport, *rhost, *rport;
+ struct sockaddr_in servaddr, recaddr, recvaddr;
+ /*
+ * Query for aaaaaaaaaa.example./A in wire format with RD=1,
+ * EDNS and DO=1. 0x88, 0x0c at the start is the ID field which
+ * will be updated for each query.
+ */
+ char respacket[] = { 0x88, 0x0c, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x0a, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x07,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 };
+ /*
+ * Response for example./DNSKEY in wire format. Note that RRSIGs
+ * were generated with this DNSKEY that are used as seeds for
+ * AFL in the DNSSEC fuzzing job. So the DNSKEY content of this
+ * message must not change, or the corresponding RRSIGs will
+ * have to be updated. 0x8d, 0xf6 at the start is the ID field
+ * which will be made to match the query.
+ */
+ const uint8_t dnskey_wf[] = {
+ 0x8d, 0xf6, 0x84, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x01, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x00, 0x00, 0x30, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x30, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x08, 0x01, 0x00, 0x03,
+ 0x08, 0x03, 0x01, 0x00, 0x01, 0xbd, 0x81, 0xdc, 0x7f, 0x16,
+ 0xd4, 0x81, 0x7c, 0x1f, 0x9f, 0x6a, 0x68, 0xdd, 0xd4, 0xda,
+ 0x48, 0xd9, 0x1c, 0xbd, 0xa6, 0x46, 0x1a, 0xf0, 0xb4, 0xb9,
+ 0xec, 0x3d, 0x6c, 0x0b, 0x57, 0xc7, 0xd6, 0x54, 0x66, 0xe6,
+ 0x6c, 0xd5, 0x90, 0x3a, 0x78, 0x7d, 0x7f, 0x78, 0x80, 0xa2,
+ 0x89, 0x61, 0x6d, 0x8a, 0x2b, 0xcd, 0x0a, 0x77, 0x7a, 0xad,
+ 0xc9, 0x61, 0x53, 0x53, 0x8c, 0x99, 0x72, 0x86, 0x14, 0x74,
+ 0x9c, 0x49, 0x2a, 0x47, 0x23, 0xf7, 0x02, 0x07, 0x73, 0x1c,
+ 0x5c, 0x2e, 0xb4, 0x9a, 0xa4, 0xd7, 0x98, 0x42, 0xc3, 0xd2,
+ 0xfe, 0xbf, 0xf3, 0xb3, 0x6a, 0x52, 0x92, 0xd5, 0xfa, 0x47,
+ 0x00, 0xe3, 0xd9, 0x59, 0x31, 0x95, 0x48, 0x40, 0xfc, 0x06,
+ 0x73, 0x90, 0xc6, 0x73, 0x96, 0xba, 0x29, 0x91, 0xe2, 0xac,
+ 0xa3, 0xa5, 0x6d, 0x91, 0x6d, 0x52, 0xb9, 0x34, 0xba, 0x68,
+ 0x4f, 0xad, 0xf0, 0xc3, 0xf3, 0x1d, 0x6d, 0x61, 0x76, 0xe5,
+ 0x3d, 0xa3, 0x9b, 0x2a, 0x0c, 0x92, 0xb3, 0x78, 0x6b, 0xf1,
+ 0x20, 0xd6, 0x90, 0xb7, 0xac, 0xe2, 0xf8, 0x2b, 0x94, 0x10,
+ 0x79, 0xce, 0xa8, 0x60, 0x42, 0xea, 0x6a, 0x18, 0x2f, 0xc0,
+ 0xd8, 0x05, 0x0a, 0x3b, 0x06, 0x0f, 0x02, 0x7e, 0xff, 0x33,
+ 0x46, 0xee, 0xb6, 0x21, 0x25, 0x90, 0x63, 0x4b, 0x3b, 0x5e,
+ 0xb2, 0x72, 0x3a, 0xcb, 0x91, 0x41, 0xf4, 0x20, 0x50, 0x78,
+ 0x1c, 0x93, 0x95, 0xda, 0xfa, 0xae, 0x85, 0xc5, 0xd7, 0x6b,
+ 0x92, 0x0c, 0x70, 0x6b, 0xe4, 0xb7, 0x29, 0x3a, 0x2e, 0x18,
+ 0x88, 0x82, 0x33, 0x7c, 0xa8, 0xea, 0xb8, 0x31, 0x8f, 0xaf,
+ 0x50, 0xc5, 0x9c, 0x08, 0x56, 0x8f, 0x09, 0x76, 0x4e, 0xdf,
+ 0x97, 0x75, 0x9d, 0x00, 0x52, 0x7f, 0xdb, 0xec, 0x30, 0xcb,
+ 0x1c, 0x4c, 0x2a, 0x21, 0x93, 0xc4, 0x6d, 0x85, 0xa9, 0x40,
+ 0x3b, 0xc0, 0x0c, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x2c, 0x01, 0x1b, 0x00, 0x30, 0x08, 0x01, 0x00, 0x00, 0x01,
+ 0x2c, 0x67, 0x74, 0x85, 0x80, 0x58, 0xb3, 0xc5, 0x17, 0x36,
+ 0x90, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
+ 0x45, 0xac, 0xd3, 0x82, 0x69, 0xf3, 0x10, 0x3a, 0x97, 0x2c,
+ 0x6a, 0xa9, 0x78, 0x99, 0xea, 0xb0, 0xcc, 0xf7, 0xaf, 0x33,
+ 0x51, 0x5b, 0xdf, 0x77, 0x04, 0x18, 0x14, 0x99, 0x61, 0xeb,
+ 0x8d, 0x76, 0x3f, 0xd1, 0x71, 0x14, 0x43, 0x80, 0x53, 0xc2,
+ 0x3b, 0x9f, 0x09, 0x4f, 0xb3, 0x51, 0x04, 0x89, 0x0e, 0xc8,
+ 0x54, 0x12, 0xcd, 0x07, 0x20, 0xbe, 0x94, 0xc2, 0xda, 0x99,
+ 0xdd, 0x1e, 0xf8, 0xb0, 0x84, 0x2e, 0xf9, 0x19, 0x35, 0x36,
+ 0xf5, 0xd0, 0x5d, 0x82, 0x18, 0x74, 0xa0, 0x00, 0xb6, 0x15,
+ 0x57, 0x40, 0x5f, 0x78, 0x2d, 0x27, 0xac, 0xc7, 0x8a, 0x29,
+ 0x55, 0xa9, 0xcd, 0xbc, 0xf7, 0x3e, 0xff, 0xae, 0x1a, 0x5a,
+ 0x1d, 0xac, 0x0d, 0x78, 0x0e, 0x08, 0x33, 0x6c, 0x59, 0x70,
+ 0x40, 0xb9, 0x65, 0xbd, 0x35, 0xbb, 0x9a, 0x70, 0xdc, 0x93,
+ 0x66, 0xb0, 0xef, 0xfe, 0xf0, 0x32, 0xa6, 0xee, 0xb7, 0x03,
+ 0x89, 0xa2, 0x4d, 0xe0, 0xf1, 0x20, 0xdf, 0x39, 0xe8, 0xe3,
+ 0xcc, 0x95, 0xe9, 0x9a, 0xad, 0xbf, 0xbd, 0x7c, 0xf7, 0xd7,
+ 0xde, 0x47, 0x9e, 0xf6, 0x17, 0xbb, 0x84, 0xa9, 0xed, 0xf2,
+ 0x45, 0x61, 0x6d, 0x13, 0x0b, 0x06, 0x29, 0x50, 0xde, 0xfd,
+ 0x42, 0xb0, 0x66, 0x2c, 0x1c, 0x2b, 0x63, 0xcb, 0x4e, 0xb9,
+ 0x31, 0xc4, 0xea, 0xd2, 0x07, 0x3a, 0x08, 0x79, 0x19, 0x4b,
+ 0x4c, 0x50, 0x97, 0x02, 0xd7, 0x26, 0x41, 0x2f, 0xdd, 0x57,
+ 0xaa, 0xb0, 0xa0, 0x21, 0x4e, 0x74, 0xb6, 0x97, 0x4b, 0x8b,
+ 0x09, 0x9c, 0x3d, 0x29, 0xfb, 0x12, 0x27, 0x47, 0x8f, 0xb8,
+ 0xc5, 0x8e, 0x65, 0xcd, 0xca, 0x2f, 0xba, 0xf5, 0x3e, 0xec,
+ 0x56, 0xc3, 0xc9, 0xa1, 0x62, 0x7d, 0xf2, 0x9f, 0x90, 0x16,
+ 0x1d, 0xbf, 0x97, 0x28, 0xe1, 0x92, 0xb1, 0x53, 0xab, 0xc4,
+ 0xe0, 0x99, 0xbb, 0x19, 0x90, 0x7c, 0x00, 0x00, 0x29, 0x10,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
+ };
+
+ int sockfd;
+ int listenfd;
+ int loop;
+ uint16_t qtype;
+ char *buf, *rbuf;
+ char *nameptr;
+ unsigned int i;
+ uint8_t llen;
+ uint64_t seed;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "qtype:saddress:sport:raddress:rport"
+ * syntax. Due to the syntax used, this only supports IPv4 addresses.
+ */
+ sqtype = strdup(named_g_fuzz_addr);
+ RUNTIME_CHECK(sqtype != NULL);
+
+ shost = strchr(sqtype, ':');
+ RUNTIME_CHECK(shost != NULL);
+ *shost = 0;
+ shost++;
+
+ sport = strchr(shost, ':');
+ RUNTIME_CHECK(sport != NULL);
+ *sport = 0;
+ sport++;
+
+ rhost = strchr(sport, ':');
+ RUNTIME_CHECK(rhost != NULL);
+ *rhost = 0;
+ rhost++;
+
+ rport = strchr(rhost, ':');
+ RUNTIME_CHECK(rport != NULL);
+ *rport = 0;
+ rport++;
+
+ /*
+ * Patch in the qtype into the question section of respacket.
+ */
+ qtype = atoi(sqtype);
+ respacket[32] = (qtype >> 8) & 0xff;
+ respacket[33] = qtype & 0xff;
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(sport));
+
+ memset(&recaddr, 0, sizeof(recaddr));
+ recaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1);
+ recaddr.sin_port = htons(atoi(rport));
+
+ free(sqtype);
+
+ /*
+ * Wait for named to start. This is set in run_server() in the
+ * named thread.
+ */
+ while (!named_g_run_done) {
+ usleep(10000);
+ }
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(sockfd != -1);
+
+ listenfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(listenfd != -1);
+
+ RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr,
+ sizeof(struct sockaddr_in)) == 0);
+
+ buf = malloc(65536);
+ rbuf = malloc(65536);
+ RUNTIME_CHECK(buf != NULL);
+ RUNTIME_CHECK(rbuf != NULL);
+
+ seed = 42;
+
+ /*
+ * Processing fuzzed packets 100,000 times before shutting down
+ * the app.
+ */
+ for (loop = 0; loop < 100000; loop++) {
+ ssize_t length;
+ ssize_t sent;
+ unsigned short id;
+ socklen_t socklen;
+
+ memset(buf, 0, 12);
+ length = read(0, buf, 65536);
+ if (length <= 0) {
+ usleep(1000000);
+ continue;
+ }
+
+ if (length > 4096) {
+ if (getenv("AFL_CMIN")) {
+ free(buf);
+ free(rbuf);
+ close(sockfd);
+ close(listenfd);
+ named_server_flushonshutdown(named_g_server,
+ false);
+ isc_app_shutdown();
+ return (NULL);
+ }
+ raise(SIGSTOP);
+ continue;
+ }
+
+ if (length < 12) {
+ length = 12;
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+ ready = false;
+
+ /* Use a unique query ID. */
+ seed = 1664525 * seed + 1013904223;
+ id = seed & 0xffff;
+ respacket[0] = (id >> 8) & 0xff;
+ respacket[1] = id & 0xff;
+
+ /*
+ * Flush any pending data on the authoritative server.
+ */
+ socklen = sizeof(recvaddr);
+ (void)recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT,
+ (struct sockaddr *)&recvaddr, &socklen);
+
+ /*
+ * Send a fixed client query to named(resolver) of
+ * aaaaaaaaaa.example./A. This is the starting query
+ * driver.
+ */
+ sent = sendto(sockfd, respacket, sizeof(respacket), 0,
+ (struct sockaddr *)&servaddr, sizeof(servaddr));
+ RUNTIME_CHECK(sent == sizeof(respacket));
+
+ /*
+ * named(resolver) will process the query above and send
+ * an upstream query to the authoritative server. We
+ * handle that here as the upstream authoritative server
+ * on listenfd.
+ */
+ socklen = sizeof(recvaddr);
+ sent = recvfrom(listenfd, rbuf, 65536, 0,
+ (struct sockaddr *)&recvaddr, &socklen);
+ RUNTIME_CHECK(sent > 0);
+
+ /*
+ * Copy QID and set QR so that response is always
+ * accepted by named(resolver).
+ */
+ buf[0] = rbuf[0];
+ buf[1] = rbuf[1];
+ buf[2] |= 0x80;
+
+ /*
+ * NOTE: We are not copying the QNAME or setting
+ * rcode=NOERROR each time. So the resolver may fail the
+ * client query (driver) / wander due to this. AA flag
+ * may also not be set based on the contents of the AFL
+ * fuzzed packet.
+ */
+
+ /*
+ * A hack - set QTYPE to the one from query so that we
+ * can easily share packets between instances. If we
+ * write over something else we'll get FORMERR anyway.
+ */
+
+ /* Skip DNS header to get to the name */
+ nameptr = buf + 12;
+
+ /* Skip the name to get to the qtype */
+ i = 0;
+ while (((llen = nameptr[i]) != 0) && (i < 255) &&
+ (((nameptr + i + 1 + llen) - buf) < length))
+ {
+ i += 1 + llen;
+ }
+
+ if (i <= 255) {
+ nameptr += 1 + i;
+ /* Patch the qtype */
+ if ((nameptr - buf) < (length - 2)) {
+ *nameptr++ = (qtype >> 8) & 0xff;
+ *nameptr++ = qtype & 0xff;
+ }
+ /* Patch the qclass */
+ if ((nameptr - buf) < (length - 2)) {
+ *nameptr++ = 0;
+ *nameptr++ = 1;
+ }
+ }
+
+ /*
+ * Send the reply to named(resolver).
+ */
+ sent = sendto(listenfd, buf, length, 0,
+ (struct sockaddr *)&recvaddr, sizeof(recvaddr));
+ RUNTIME_CHECK(sent == length);
+
+ /* We might get additional questions here (e.g. for CNAME). */
+ for (;;) {
+ fd_set fds;
+ struct timeval tv;
+ int rv;
+ int max;
+
+ FD_ZERO(&fds);
+ FD_SET(listenfd, &fds);
+ FD_SET(sockfd, &fds);
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ max = (listenfd > sockfd ? listenfd : sockfd) + 1;
+
+ rv = select(max, &fds, NULL, NULL, &tv);
+ RUNTIME_CHECK(rv > 0);
+
+ if (FD_ISSET(sockfd, &fds)) {
+ /*
+ * It's the reply from named(resolver)
+ * to the client(query driver), so we're
+ * done.
+ */
+ (void)recvfrom(sockfd, buf, 65536, 0, NULL,
+ NULL);
+ break;
+ }
+
+ /*
+ * We've got additional question (eg. due to
+ * CNAME). Bounce it - setting QR flag and
+ * NOERROR rcode and sending it back.
+ */
+ length = recvfrom(listenfd, buf, 65536, 0,
+ (struct sockaddr *)&recvaddr,
+ &socklen);
+
+ /*
+ * If this is a DNSKEY query, send the DNSKEY,
+ * otherwise, bounce the query.
+ */
+
+ /* Skip DNS header to get to the name */
+ nameptr = buf + 12;
+
+ /* Skip the name to get to the qtype */
+ i = 0;
+ while (((llen = nameptr[i]) != 0) && (i < 255) &&
+ (((nameptr + i + 1 + llen) - buf) < length))
+ {
+ i += 1 + llen;
+ }
+
+ if (i <= 255) {
+ nameptr += 1 + i;
+ /*
+ * Patch in the DNSKEY reply without
+ * touching the ID field. Note that we
+ * don't compare the name in the
+ * question section in the query, but we
+ * don't expect to receive any query for
+ * type DNSKEY but for the name
+ * "example."
+ */
+ if ((nameptr - buf) < (length - 2)) {
+ uint8_t hb, lb;
+ hb = *nameptr++;
+ lb = *nameptr++;
+ qtype = (hb << 8) | lb;
+
+ if (qtype == 48) {
+ memmove(buf + 2, dnskey_wf + 2,
+ sizeof(dnskey_wf) - 2);
+ length = sizeof(dnskey_wf);
+ }
+ }
+ }
+
+ buf[2] |= 0x80;
+ buf[3] &= 0xF0;
+ sent = sendto(listenfd, buf, length, 0,
+ (struct sockaddr *)&recvaddr,
+ sizeof(recvaddr));
+ RUNTIME_CHECK(sent == length);
+ }
+
+ while (!ready) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+ }
+
+ free(buf);
+ free(rbuf);
+ close(sockfd);
+ close(listenfd);
+ named_server_flushonshutdown(named_g_server, false);
+ isc_app_shutdown();
+
+#ifdef __AFL_LOOP
+ /*
+ * This is here just for the signature, that's how AFL detects
+ * if it's a 'persistent mode' binary. It has to occur somewhere
+ * in the file, that's all. < wpk_> AFL checks the binary for
+ * this signature ("##SIG_AFL_PERSISTENT##") and runs the binary
+ * in persistent mode if it's present.
+ */
+ __AFL_LOOP(0);
+#endif /* ifdef __AFL_LOOP */
+
+ return (NULL);
+}
+
+/*
+ * In "tcp:", "http:" and "rndc:" modes, this thread reads fuzzed query
+ * blobs from AFL from standard input and sends it to the corresponding
+ * TCP listening port of named (port 53 DNS, or the HTTP statistics
+ * channels listener or the rndc port) that is passed in the -A
+ * <mode>:<address>:<port> option. It can be used to test named from the
+ * client side.
+ */
+static void *
+fuzz_thread_tcp(void *arg) {
+ char *host;
+ char *port;
+ struct sockaddr_in servaddr;
+ int sockfd;
+ char *buf;
+ int loop;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "address:port" syntax. Due to
+ * the syntax used, this only supports IPv4 addresses.
+ */
+ host = strdup(named_g_fuzz_addr);
+ RUNTIME_CHECK(host != NULL);
+
+ port = strchr(host, ':');
+ RUNTIME_CHECK(port != NULL);
+ *port = 0;
+ ++port;
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(port));
+
+ free(host);
+
+ /*
+ * Wait for named to start. This is set in run_server() in the
+ * named thread.
+ */
+ while (!named_g_run_done) {
+ usleep(10000);
+ }
+
+ buf = malloc(65539);
+ RUNTIME_CHECK(buf != NULL);
+
+ /*
+ * Processing fuzzed packets 100,000 times before shutting down
+ * the app.
+ */
+ for (loop = 0; loop < 100000; loop++) {
+ ssize_t length;
+ ssize_t sent;
+ int yes;
+ int r;
+
+ if (named_g_fuzz_type == isc_fuzz_tcpclient) {
+ /*
+ * To fuzz DNS TCP client we have to put 16-bit
+ * message length preceding the start of packet.
+ */
+ length = read(0, buf + 2, 65535);
+ buf[0] = (length >> 8) & 0xff;
+ buf[1] = length & 0xff;
+ length += 2;
+ } else {
+ /*
+ * Other types of TCP clients such as HTTP, etc.
+ */
+ length = read(0, buf, 65535);
+ }
+ if (length <= 0) {
+ usleep(1000000);
+ continue;
+ }
+ if (named_g_fuzz_type == isc_fuzz_http) {
+ /*
+ * This guarantees that the request will be
+ * processed.
+ */
+ INSIST(length <= 65535);
+ buf[length++] = '\r';
+ buf[length++] = '\n';
+ buf[length++] = '\r';
+ buf[length++] = '\n';
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+ ready = false;
+ yes = 1;
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ RUNTIME_CHECK(sockfd != -1);
+ RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
+ sizeof(int)) == 0);
+
+ do {
+ r = connect(sockfd, (struct sockaddr *)&servaddr,
+ sizeof(servaddr));
+ if (r != 0) {
+ usleep(10000);
+ }
+ } while (r != 0);
+
+ /*
+ * Send the fuzzed query blob to the target server.
+ */
+ sent = write(sockfd, buf, length);
+ RUNTIME_CHECK(sent == length);
+
+ close(sockfd);
+
+ while (!ready) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+ }
+
+ free(buf);
+ close(sockfd);
+ named_server_flushonshutdown(named_g_server, false);
+ isc_app_shutdown();
+
+ return (NULL);
+}
+
+#endif /* ENABLE_AFL */
+
+/*
+ * named has finished processing a message and has sent the
+ * reply. Signal the fuzz thread using the condition variable, to read
+ * and process the next item from AFL.
+ */
+void
+named_fuzz_notify(void) {
+#ifdef ENABLE_AFL
+ if (getenv("AFL_CMIN")) {
+ named_server_flushonshutdown(named_g_server, false);
+ isc_app_shutdown();
+ return;
+ }
+
+ raise(SIGSTOP);
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+ ready = true;
+
+ RUNTIME_CHECK(pthread_cond_signal(&cond) == 0);
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+#endif /* ENABLE_AFL */
+}
+
+void
+named_fuzz_setup(void) {
+#ifdef ENABLE_AFL
+ if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) {
+ pthread_t thread;
+ void *(fn) = NULL;
+
+ switch (named_g_fuzz_type) {
+ case isc_fuzz_client:
+ fn = fuzz_thread_client;
+ break;
+
+ case isc_fuzz_http:
+ case isc_fuzz_tcpclient:
+ case isc_fuzz_rndc:
+ fn = fuzz_thread_tcp;
+ break;
+
+ case isc_fuzz_resolver:
+ fn = fuzz_thread_resolver;
+ break;
+
+ default:
+ RUNTIME_CHECK(fn != NULL);
+ }
+
+ RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0);
+ RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0);
+ RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0);
+ }
+#endif /* ENABLE_AFL */
+}
diff --git a/bin/named/geoip.c b/bin/named/geoip.c
new file mode 100644
index 0000000..0ba4ef0
--- /dev/null
+++ b/bin/named/geoip.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#if defined(HAVE_GEOIP2)
+#include <maxminddb.h>
+#endif /* if defined(HAVE_GEOIP2) */
+
+#include <isc/dir.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/geoip.h>
+
+#include <named/geoip.h>
+#include <named/log.h>
+
+static dns_geoip_databases_t geoip_table;
+
+#if defined(HAVE_GEOIP2)
+static MMDB_s geoip_country, geoip_city, geoip_as, geoip_isp, geoip_domain;
+
+static MMDB_s *
+open_geoip2(const char *dir, const char *dbfile, MMDB_s *mmdb) {
+ char pathbuf[PATH_MAX];
+ unsigned int n;
+ int ret;
+
+ n = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, dbfile);
+ if (n >= sizeof(pathbuf)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "GeoIP2 database '%s/%s': path too long", dir,
+ dbfile);
+ return (NULL);
+ }
+
+ ret = MMDB_open(pathbuf, MMDB_MODE_MMAP, mmdb);
+ if (ret == MMDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "opened GeoIP2 database '%s'", pathbuf);
+ return (mmdb);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "unable to open GeoIP2 database '%s' (status %d)",
+ pathbuf, ret);
+
+ return (NULL);
+}
+#endif /* HAVE_GEOIP2 */
+
+void
+named_geoip_init(void) {
+#if defined(HAVE_GEOIP2)
+ if (named_g_geoip == NULL) {
+ named_g_geoip = &geoip_table;
+ }
+#else /* if defined(HAVE_GEOIP2) */
+ return;
+#endif /* if defined(HAVE_GEOIP2) */
+}
+
+void
+named_geoip_load(char *dir) {
+#if defined(HAVE_GEOIP2)
+ REQUIRE(dir != NULL);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "looking for GeoIP2 databases in '%s'", dir);
+
+ named_g_geoip->country = open_geoip2(dir, "GeoIP2-Country.mmdb",
+ &geoip_country);
+ if (named_g_geoip->country == NULL) {
+ named_g_geoip->country = open_geoip2(
+ dir, "GeoLite2-Country.mmdb", &geoip_country);
+ }
+
+ named_g_geoip->city = open_geoip2(dir, "GeoIP2-City.mmdb", &geoip_city);
+ if (named_g_geoip->city == NULL) {
+ named_g_geoip->city = open_geoip2(dir, "GeoLite2-City.mmdb",
+ &geoip_city);
+ }
+
+ named_g_geoip->as = open_geoip2(dir, "GeoIP2-ASN.mmdb", &geoip_as);
+ if (named_g_geoip->as == NULL) {
+ named_g_geoip->as = open_geoip2(dir, "GeoLite2-ASN.mmdb",
+ &geoip_as);
+ }
+
+ named_g_geoip->isp = open_geoip2(dir, "GeoIP2-ISP.mmdb", &geoip_isp);
+ named_g_geoip->domain = open_geoip2(dir, "GeoIP2-Domain.mmdb",
+ &geoip_domain);
+#else /* if defined(HAVE_GEOIP2) */
+ UNUSED(dir);
+
+ return;
+#endif /* if defined(HAVE_GEOIP2) */
+}
+
+void
+named_geoip_unload(void) {
+#ifdef HAVE_GEOIP2
+ if (named_g_geoip->country != NULL) {
+ MMDB_close(named_g_geoip->country);
+ named_g_geoip->country = NULL;
+ }
+ if (named_g_geoip->city != NULL) {
+ MMDB_close(named_g_geoip->city);
+ named_g_geoip->city = NULL;
+ }
+ if (named_g_geoip->as != NULL) {
+ MMDB_close(named_g_geoip->as);
+ named_g_geoip->as = NULL;
+ }
+ if (named_g_geoip->isp != NULL) {
+ MMDB_close(named_g_geoip->isp);
+ named_g_geoip->isp = NULL;
+ }
+ if (named_g_geoip->domain != NULL) {
+ MMDB_close(named_g_geoip->domain);
+ named_g_geoip->domain = NULL;
+ }
+#endif /* ifdef HAVE_GEOIP2 */
+}
+
+void
+named_geoip_shutdown(void) {
+#ifdef HAVE_GEOIP2
+ named_geoip_unload();
+#endif /* HAVE_GEOIP2 */
+}
diff --git a/bin/named/include/dlz/dlz_dlopen_driver.h b/bin/named/include/dlz/dlz_dlopen_driver.h
new file mode 100644
index 0000000..64d6388
--- /dev/null
+++ b/bin/named/include/dlz/dlz_dlopen_driver.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+isc_result_t
+dlz_dlopen_init(isc_mem_t *mctx);
+
+void
+dlz_dlopen_clear(void);
diff --git a/bin/named/include/named/builtin.h b/bin/named/include/named/builtin.h
new file mode 100644
index 0000000..fbfc599
--- /dev/null
+++ b/bin/named/include/named/builtin.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/types.h>
+
+isc_result_t
+named_builtin_init(void);
+
+void
+named_builtin_deinit(void);
diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h
new file mode 100644
index 0000000..d9c5aa3
--- /dev/null
+++ b/bin/named/include/named/config.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <inttypes.h>
+
+#include <dns/types.h>
+#include <dns/zone.h>
+
+#include <isccfg/cfg.h>
+
+#define DEFAULT_IANA_ROOT_ZONE_PRIMARIES "_default_iana_root_zone_primaries"
+
+isc_result_t
+named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf);
+
+const char *
+named_config_getdefault(void);
+
+isc_result_t
+named_config_get(cfg_obj_t const *const *maps, const char *name,
+ const cfg_obj_t **obj);
+
+isc_result_t
+named_checknames_get(const cfg_obj_t **maps, const char *const names[],
+ const cfg_obj_t **obj);
+
+int
+named_config_listcount(const cfg_obj_t *list);
+
+isc_result_t
+named_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp);
+
+isc_result_t
+named_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep);
+
+dns_zonetype_t
+named_config_getzonetype(const cfg_obj_t *zonetypeobj);
+
+isc_result_t
+named_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, uint32_t *countp);
+
+void
+named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ uint32_t count);
+
+isc_result_t
+named_config_getremotesdef(const cfg_obj_t *cctx, const char *list,
+ const char *name, const cfg_obj_t **ret);
+
+isc_result_t
+named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype,
+ const cfg_obj_t *list, isc_mem_t *mctx,
+ dns_ipkeylist_t *ipkl);
+
+isc_result_t
+named_config_getport(const cfg_obj_t *config, const char *type,
+ in_port_t *portp);
+
+isc_result_t
+named_config_getkeyalgorithm(const char *str, const dns_name_t **name,
+ uint16_t *digestbits);
+isc_result_t
+named_config_getkeyalgorithm2(const char *str, const dns_name_t **name,
+ unsigned int *typep, uint16_t *digestbits);
diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h
new file mode 100644
index 0000000..29b5677
--- /dev/null
+++ b/bin/named/include/named/control.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file
+ * \brief
+ * The name server command channel.
+ */
+
+#include <stdbool.h>
+
+#include <isccfg/aclconf.h>
+
+#include <isccc/types.h>
+#include <named/types.h>
+
+#define NAMED_CONTROL_PORT 953
+
+#define NAMED_COMMAND_STOP "stop"
+#define NAMED_COMMAND_HALT "halt"
+#define NAMED_COMMAND_RELOAD "reload"
+#define NAMED_COMMAND_RECONFIG "reconfig"
+#define NAMED_COMMAND_REFRESH "refresh"
+#define NAMED_COMMAND_RETRANSFER "retransfer"
+#define NAMED_COMMAND_DUMPSTATS "stats"
+#define NAMED_COMMAND_QUERYLOG "querylog"
+#define NAMED_COMMAND_DUMPDB "dumpdb"
+#define NAMED_COMMAND_SECROOTS "secroots"
+#define NAMED_COMMAND_TRACE "trace"
+#define NAMED_COMMAND_NOTRACE "notrace"
+#define NAMED_COMMAND_FLUSH "flush"
+#define NAMED_COMMAND_FLUSHNAME "flushname"
+#define NAMED_COMMAND_FLUSHTREE "flushtree"
+#define NAMED_COMMAND_STATUS "status"
+#define NAMED_COMMAND_TSIGLIST "tsig-list"
+#define NAMED_COMMAND_TSIGDELETE "tsig-delete"
+#define NAMED_COMMAND_FREEZE "freeze"
+#define NAMED_COMMAND_UNFREEZE "unfreeze"
+#define NAMED_COMMAND_THAW "thaw"
+#define NAMED_COMMAND_TIMERPOKE "timerpoke"
+#define NAMED_COMMAND_RECURSING "recursing"
+#define NAMED_COMMAND_NULL "null"
+#define NAMED_COMMAND_NOTIFY "notify"
+#define NAMED_COMMAND_VALIDATION "validation"
+#define NAMED_COMMAND_SCAN "scan"
+#define NAMED_COMMAND_SIGN "sign"
+#define NAMED_COMMAND_LOADKEYS "loadkeys"
+#define NAMED_COMMAND_ADDZONE "addzone"
+#define NAMED_COMMAND_MODZONE "modzone"
+#define NAMED_COMMAND_DELZONE "delzone"
+#define NAMED_COMMAND_SHOWZONE "showzone"
+#define NAMED_COMMAND_SYNC "sync"
+#define NAMED_COMMAND_SIGNING "signing"
+#define NAMED_COMMAND_DNSSEC "dnssec"
+#define NAMED_COMMAND_ZONESTATUS "zonestatus"
+#define NAMED_COMMAND_NTA "nta"
+#define NAMED_COMMAND_TESTGEN "testgen"
+#define NAMED_COMMAND_MKEYS "managed-keys"
+#define NAMED_COMMAND_DNSTAPREOPEN "dnstap-reopen"
+#define NAMED_COMMAND_DNSTAP "dnstap"
+#define NAMED_COMMAND_TCPTIMEOUTS "tcp-timeouts"
+#define NAMED_COMMAND_SERVESTALE "serve-stale"
+
+isc_result_t
+named_controls_create(named_server_t *server, named_controls_t **ctrlsp);
+/*%<
+ * Create an initial, empty set of command channels for 'server'.
+ */
+
+void
+named_controls_destroy(named_controls_t **ctrlsp);
+/*%<
+ * Destroy a set of command channels.
+ *
+ * Requires:
+ * Shutdown of the channels has completed.
+ */
+
+isc_result_t
+named_controls_configure(named_controls_t *controls, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx);
+/*%<
+ * Configure zero or more command channels into 'controls'
+ * as defined in the configuration parse tree 'config'.
+ * The channels will evaluate ACLs in the context of
+ * 'aclconfctx'.
+ */
+
+void
+named_controls_shutdown(named_controls_t *controls);
+/*%<
+ * Initiate shutdown of all the command channels in 'controls'.
+ */
+
+isc_result_t
+named_control_docommand(isccc_sexpr_t *message, bool readonly,
+ isc_buffer_t **text);
diff --git a/bin/named/include/named/fuzz.h b/bin/named/include/named/fuzz.h
new file mode 100644
index 0000000..69af8da
--- /dev/null
+++ b/bin/named/include/named/fuzz.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <isc/fuzz.h>
+
+#pragma once
+
+void
+named_fuzz_notify(void);
+
+void
+named_fuzz_setup(void);
diff --git a/bin/named/include/named/geoip.h b/bin/named/include/named/geoip.h
new file mode 100644
index 0000000..d1852ef
--- /dev/null
+++ b/bin/named/include/named/geoip.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+extern dns_geoip_databases_t *named_g_geoip;
+
+void
+named_geoip_init(void);
+
+void
+named_geoip_load(char *dir);
+
+void
+named_geoip_unload(void);
+
+void
+named_geoip_shutdown(void);
diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h
new file mode 100644
index 0000000..c65e933
--- /dev/null
+++ b/bin/named/include/named/globals.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/log.h>
+#include <isc/net.h>
+#include <isc/netmgr.h>
+#include <isc/rwlock.h>
+
+#include <dns/acl.h>
+#include <dns/zone.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+#include <dst/dst.h>
+#include <named/fuzz.h>
+#include <named/types.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NAMED_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else /* ifdef NAMED_MAIN */
+#define EXTERN extern
+#define INIT(v)
+#endif /* ifdef NAMED_MAIN */
+
+#ifndef NAMED_RUN_PID_DIR
+#define NAMED_RUN_PID_DIR 1
+#endif /* ifndef NAMED_RUN_PID_DIR */
+
+EXTERN isc_mem_t *named_g_mctx INIT(NULL);
+EXTERN unsigned int named_g_cpus INIT(0);
+EXTERN unsigned int named_g_udpdisp INIT(0);
+EXTERN isc_taskmgr_t *named_g_taskmgr INIT(NULL);
+EXTERN dns_dispatchmgr_t *named_g_dispatchmgr INIT(NULL);
+EXTERN unsigned int named_g_cpus_detected INIT(1);
+
+#ifdef ENABLE_AFL
+EXTERN bool named_g_run_done INIT(false);
+#endif /* ifdef ENABLE_AFL */
+/*
+ * XXXRTH We're going to want multiple timer managers eventually. One
+ * for really short timers, another for client timers, and one
+ * for zone timers.
+ */
+EXTERN isc_timermgr_t *named_g_timermgr INIT(NULL);
+EXTERN isc_nm_t *named_g_netmgr INIT(NULL);
+EXTERN cfg_parser_t *named_g_parser INIT(NULL);
+EXTERN cfg_parser_t *named_g_addparser INIT(NULL);
+EXTERN const char *named_g_version INIT(PACKAGE_VERSION);
+EXTERN const char *named_g_product INIT(PACKAGE_NAME);
+EXTERN const char *named_g_description INIT(PACKAGE_DESCRIPTION);
+EXTERN const char *named_g_srcid INIT(PACKAGE_SRCID);
+EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS);
+EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER);
+EXTERN in_port_t named_g_port INIT(0);
+EXTERN in_port_t named_g_tlsport INIT(0);
+EXTERN in_port_t named_g_httpsport INIT(0);
+EXTERN in_port_t named_g_httpport INIT(0);
+
+EXTERN in_port_t named_g_http_listener_clients INIT(0);
+EXTERN in_port_t named_g_http_streams_per_conn INIT(0);
+
+EXTERN named_server_t *named_g_server INIT(NULL);
+
+/*
+ * Logging.
+ */
+EXTERN isc_log_t *named_g_lctx INIT(NULL);
+EXTERN isc_logcategory_t *named_g_categories INIT(NULL);
+EXTERN isc_logmodule_t *named_g_modules INIT(NULL);
+EXTERN unsigned int named_g_debuglevel INIT(0);
+
+/*
+ * Current configuration information.
+ */
+EXTERN cfg_obj_t *named_g_config INIT(NULL);
+EXTERN const cfg_obj_t *named_g_defaults INIT(NULL);
+EXTERN const char *named_g_conffile INIT(NAMED_SYSCONFDIR "/named.conf");
+EXTERN const char *named_g_defaultbindkeys INIT(NAMED_SYSCONFDIR "/bind.keys");
+EXTERN const char *named_g_keyfile INIT(NAMED_SYSCONFDIR "/rndc.key");
+
+EXTERN dns_tsigkey_t *named_g_sessionkey INIT(NULL);
+EXTERN dns_name_t named_g_sessionkeyname;
+EXTERN bool named_g_conffileset INIT(false);
+EXTERN cfg_aclconfctx_t *named_g_aclconfctx INIT(NULL);
+
+/*
+ * Initial resource limits.
+ */
+EXTERN isc_resourcevalue_t named_g_initstacksize INIT(0);
+EXTERN isc_resourcevalue_t named_g_initdatasize INIT(0);
+EXTERN isc_resourcevalue_t named_g_initcoresize INIT(0);
+EXTERN isc_resourcevalue_t named_g_initopenfiles INIT(0);
+
+/*
+ * Misc.
+ */
+EXTERN bool named_g_coreok INIT(true);
+EXTERN const char *named_g_chrootdir INIT(NULL);
+EXTERN bool named_g_foreground INIT(false);
+EXTERN bool named_g_logstderr INIT(false);
+EXTERN bool named_g_nosyslog INIT(false);
+EXTERN const char *named_g_logfile INIT(NULL);
+
+EXTERN const char *named_g_defaultsessionkeyfile INIT(NAMED_LOCALSTATEDIR
+ "/run/named/"
+ "session.key");
+EXTERN const char *named_g_defaultlockfile INIT(NAMED_LOCALSTATEDIR "/run/"
+ "named/"
+ "named."
+ "lock");
+EXTERN bool named_g_forcelock INIT(false);
+
+#if NAMED_RUN_PID_DIR
+EXTERN const char *named_g_defaultpidfile INIT(NAMED_LOCALSTATEDIR "/run/named/"
+ "named.pid");
+#else /* if NAMED_RUN_PID_DIR */
+EXTERN const char *named_g_defaultpidfile INIT(NAMED_LOCALSTATEDIR "/run/"
+ "named.pid");
+#endif /* if NAMED_RUN_PID_DIR */
+
+EXTERN const char *named_g_username INIT(NULL);
+
+EXTERN const char *named_g_engine INIT(NULL);
+
+EXTERN isc_time_t named_g_boottime;
+EXTERN isc_time_t named_g_configtime;
+EXTERN bool named_g_memstatistics INIT(false);
+EXTERN bool named_g_keepstderr INIT(false);
+
+EXTERN unsigned int named_g_tat_interval INIT(24 * 3600);
+EXTERN unsigned int named_g_maxcachesize INIT(0);
+
+#if defined(HAVE_GEOIP2)
+EXTERN dns_geoip_databases_t *named_g_geoip INIT(NULL);
+#endif /* if defined(HAVE_GEOIP2) */
+
+EXTERN const char *named_g_fuzz_addr INIT(NULL);
+EXTERN isc_fuzztype_t named_g_fuzz_type INIT(isc_fuzz_none);
+
+EXTERN dns_acl_t *named_g_mapped INIT(NULL);
+
+#undef EXTERN
+#undef INIT
diff --git a/bin/named/include/named/log.h b/bin/named/include/named/log.h
new file mode 100644
index 0000000..f18e93a
--- /dev/null
+++ b/bin/named/include/named/log.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/log.h>
+#include <isc/types.h>
+
+#include <dns/log.h>
+
+#include <named/globals.h> /* Required for named_g_(categories|modules). */
+
+/* Unused slot 0. */
+#define NAMED_LOGCATEGORY_UNMATCHED (&named_g_categories[1])
+
+/*
+ * Backwards compatibility.
+ */
+#define NAMED_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL
+
+#define NAMED_LOGMODULE_MAIN (&named_g_modules[0])
+#define NAMED_LOGMODULE_SERVER (&named_g_modules[1])
+#define NAMED_LOGMODULE_CONTROL (&named_g_modules[2])
+
+isc_result_t
+named_log_init(bool safe);
+/*%
+ * Initialize the logging system and set up an initial default
+ * logging default configuration that will be used until the
+ * config file has been read.
+ *
+ * If 'safe' is true, use a default configuration that refrains
+ * from opening files. This is to avoid creating log files
+ * as root.
+ */
+
+void
+named_log_setdefaultchannels(isc_logconfig_t *lcfg);
+/*%
+ * Set up logging channels according to the named defaults, which
+ * may differ from the logging library defaults. Currently,
+ * this just means setting up default_debug.
+ */
+
+void
+named_log_setsafechannels(isc_logconfig_t *lcfg);
+/*%
+ * Like named_log_setdefaultchannels(), but omits any logging to files.
+ */
+
+void
+named_log_setdefaultsslkeylogfile(isc_logconfig_t *lcfg);
+/*%
+ * If the SSLKEYLOGFILE environment variable is set, sets up a default
+ * logging channel for writing TLS pre-master secrets to the path stored
+ * in that environment variable (for debugging purposes).
+ */
+
+isc_result_t
+named_log_setdefaultcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category default" to go to the right places.
+ */
+
+isc_result_t
+named_log_setunmatchedcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category unmatched" to go to the right places.
+ */
+
+void
+named_log_shutdown(void);
diff --git a/bin/named/include/named/logconf.h b/bin/named/include/named/logconf.h
new file mode 100644
index 0000000..65add46
--- /dev/null
+++ b/bin/named/include/named/logconf.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/log.h>
+
+isc_result_t
+named_logconfig(isc_logconfig_t *logconf, const cfg_obj_t *logstmt);
+/*%<
+ * Set up the logging configuration in '*logconf' according to
+ * the named.conf data in 'logstmt'.
+ */
diff --git a/bin/named/include/named/main.h b/bin/named/include/named/main.h
new file mode 100644
index 0000000..42fd138
--- /dev/null
+++ b/bin/named/include/named/main.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/attributes.h>
+
+/*! \file */
+
+#ifdef ISC_MAIN_HOOK
+#define main(argc, argv) bindmain(argc, argv)
+#endif /* ifdef ISC_MAIN_HOOK */
+
+/*
+ * Commandline arguments for named;
+ */
+#define NAMED_MAIN_ARGS "46A:c:Cd:D:E:fFgL:M:m:n:N:p:sS:t:T:U:u:vVx:X:"
+
+noreturn void
+named_main_earlyfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+named_main_earlywarning(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+named_main_setmemstats(const char *);
diff --git a/bin/named/include/named/os.h b/bin/named/include/named/os.h
new file mode 100644
index 0000000..0f7c1c5
--- /dev/null
+++ b/bin/named/include/named/os.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <pwd.h>
+#include <stdbool.h>
+
+#include <isc/types.h>
+
+void
+named_os_init(const char *progname);
+
+void
+named_os_daemonize(void);
+
+void
+named_os_opendevnull(void);
+
+void
+named_os_closedevnull(void);
+
+void
+named_os_chroot(const char *root);
+
+void
+named_os_inituserinfo(const char *username);
+
+void
+named_os_changeuser(void);
+
+uid_t
+ns_os_uid(void);
+
+void
+named_os_adjustnofile(void);
+
+void
+named_os_minprivs(void);
+
+FILE *
+named_os_openfile(const char *filename, mode_t mode, bool switch_user);
+
+void
+named_os_writepidfile(const char *filename, bool first_time);
+
+bool
+named_os_issingleton(const char *filename);
+
+void
+named_os_shutdown(void);
+
+void
+named_os_shutdownmsg(char *command, isc_buffer_t *text);
+
+void
+named_os_tzset(void);
+
+void
+named_os_started(void);
+
+const char *
+named_os_uname(void);
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
new file mode 100644
index 0000000..075e2ec
--- /dev/null
+++ b/bin/named/include/named/server.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/quota.h>
+#include <isc/sockaddr.h>
+#include <isc/tls.h>
+#include <isc/types.h>
+
+#include <dns/acl.h>
+#include <dns/dnstap.h>
+#include <dns/stats.h>
+#include <dns/types.h>
+
+#include <ns/interfacemgr.h>
+#include <ns/server.h>
+#include <ns/stats.h>
+#include <ns/types.h>
+
+#include <named/types.h>
+
+#define NAMED_EVENTCLASS ISC_EVENTCLASS(0x4E43)
+#define NAMED_EVENT_RELOAD (NAMED_EVENTCLASS + 0)
+#define NAMED_EVENT_DELZONE (NAMED_EVENTCLASS + 1)
+#define NAMED_EVENT_COMMAND (NAMED_EVENTCLASS + 2)
+#define NAMED_EVENT_TATSEND (NAMED_EVENTCLASS + 3)
+
+/*%
+ * Name server state. Better here than in lots of separate global variables.
+ */
+struct named_server {
+ unsigned int magic;
+ isc_mem_t *mctx;
+
+ ns_server_t *sctx;
+
+ isc_task_t *task;
+
+ char *statsfile; /*%< Statistics file name */
+ char *dumpfile; /*%< Dump file name */
+ char *secrootsfile; /*%< Secroots file name */
+ char *bindkeysfile; /*%< bind.keys file name
+ * */
+ char *recfile; /*%< Recursive file name */
+ bool version_set; /*%< User has set version
+ * */
+ char *version; /*%< User-specified version */
+ bool hostname_set; /*%< User has set hostname
+ * */
+ char *hostname; /*%< User-specified hostname
+ * */
+
+ /* Server data structures. */
+ dns_loadmgr_t *loadmgr;
+ dns_zonemgr_t *zonemgr;
+ dns_viewlist_t viewlist;
+ dns_kasplist_t kasplist;
+ ns_interfacemgr_t *interfacemgr;
+ dns_db_t *in_roothints;
+
+ isc_timer_t *interface_timer;
+ isc_timer_t *heartbeat_timer;
+ isc_timer_t *pps_timer;
+ isc_timer_t *tat_timer;
+
+ uint32_t interface_interval;
+ uint32_t heartbeat_interval;
+
+ atomic_int reload_status;
+
+ bool flushonshutdown;
+
+ named_cachelist_t cachelist; /*%< Possibly shared caches
+ * */
+ isc_stats_t *zonestats; /*% Zone management stats */
+ isc_stats_t *resolverstats; /*% Resolver stats */
+ isc_stats_t *sockstats; /*%< Socket stats */
+
+ named_controls_t *controls; /*%< Control channels */
+ unsigned int dispatchgen;
+ named_dispatchlist_t dispatches;
+
+ named_statschannellist_t statschannels;
+
+ dst_key_t *sessionkey;
+ char *session_keyfile;
+ dns_name_t *session_keyname;
+ unsigned int session_keyalg;
+ uint16_t session_keybits;
+ bool interface_auto;
+ unsigned char secret[32]; /*%< Server Cookie Secret */
+ ns_cookiealg_t cookiealg;
+
+ dns_dtenv_t *dtenv; /*%< Dnstap environment */
+
+ char *lockfile;
+
+ isc_tlsctx_cache_t *tlsctx_server_cache;
+ isc_tlsctx_cache_t *tlsctx_client_cache;
+};
+
+#define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R')
+#define NAMED_SERVER_VALID(s) ISC_MAGIC_VALID(s, NAMED_SERVER_MAGIC)
+
+void
+named_server_create(isc_mem_t *mctx, named_server_t **serverp);
+/*%<
+ * Create a server object with default settings.
+ * This function either succeeds or causes the program to exit
+ * with a fatal error.
+ */
+
+void
+named_server_destroy(named_server_t **serverp);
+/*%<
+ * Destroy a server object, freeing its memory.
+ */
+
+void
+named_server_reloadwanted(named_server_t *server);
+/*%<
+ * Inform a server that a reload is wanted. This function
+ * may be called asynchronously, from outside the server's task.
+ * If a reload is already scheduled or in progress, the call
+ * is ignored.
+ */
+
+void
+named_server_scan_interfaces(named_server_t *server);
+/*%<
+ * Trigger a interface scan.
+ * Must only be called when running under server->task.
+ */
+
+void
+named_server_flushonshutdown(named_server_t *server, bool flush);
+/*%<
+ * Inform the server that the zones should be flushed to disk on shutdown.
+ */
+
+isc_result_t
+named_server_reloadcommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "reload" command from the command channel.
+ */
+
+isc_result_t
+named_server_reconfigcommand(named_server_t *server);
+/*%<
+ * Act on a "reconfig" command from the command channel.
+ */
+
+isc_result_t
+named_server_notifycommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "notify" command from the command channel.
+ */
+
+isc_result_t
+named_server_refreshcommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "refresh" command from the command channel.
+ */
+
+isc_result_t
+named_server_retransfercommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "retransfer" command from the command channel.
+ */
+
+isc_result_t
+named_server_togglequerylog(named_server_t *server, isc_lex_t *lex);
+/*%<
+ * Enable/disable logging of queries. (Takes "yes" or "no" argument,
+ * but can also be used as a toggle for backward comptibility.)
+ */
+
+/*%
+ * Save the current NTAs for all views to files.
+ */
+isc_result_t
+named_server_saventa(named_server_t *server);
+
+/*%
+ * Load NTAs for all views from files.
+ */
+isc_result_t
+named_server_loadnta(named_server_t *server);
+
+/*%
+ * Dump the current statistics to the statistics file.
+ */
+isc_result_t
+named_server_dumpstats(named_server_t *server);
+
+/*%
+ * Dump the current cache to the dump file.
+ */
+isc_result_t
+named_server_dumpdb(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Dump the current security roots to the secroots file.
+ */
+isc_result_t
+named_server_dumpsecroots(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Change or increment the server debug level.
+ */
+isc_result_t
+named_server_setdebuglevel(named_server_t *server, isc_lex_t *lex);
+
+/*%
+ * Flush the server's cache(s)
+ */
+isc_result_t
+named_server_flushcache(named_server_t *server, isc_lex_t *lex);
+
+/*%
+ * Flush a particular name from the server's cache. If 'tree' is false,
+ * also flush the name from the ADB and badcache. If 'tree' is true, also
+ * flush all the names under the specified name.
+ */
+isc_result_t
+named_server_flushnode(named_server_t *server, isc_lex_t *lex, bool tree);
+
+/*%
+ * Report the server's status.
+ */
+isc_result_t
+named_server_status(named_server_t *server, isc_buffer_t **text);
+
+/*%
+ * Report a list of dynamic and static tsig keys, per view.
+ */
+isc_result_t
+named_server_tsiglist(named_server_t *server, isc_buffer_t **text);
+
+/*%
+ * Delete a specific key (with optional view).
+ */
+isc_result_t
+named_server_tsigdelete(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Enable or disable updates for a zone.
+ */
+isc_result_t
+named_server_freeze(named_server_t *server, bool freeze, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Dump zone updates to disk, optionally removing the journal file
+ */
+isc_result_t
+named_server_sync(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Update a zone's DNSKEY set from the key repository. If
+ * the command that triggered the call to this function was "sign",
+ * then force a full signing of the zone. If it was "loadkeys",
+ * then don't sign the zone; any needed changes to signatures can
+ * take place incrementally.
+ */
+isc_result_t
+named_server_rekey(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Dump the current recursive queries.
+ */
+isc_result_t
+named_server_dumprecursing(named_server_t *server);
+
+/*%
+ * Maintain a list of dispatches that require reserved ports.
+ */
+void
+named_add_reserved_dispatch(named_server_t *server, const isc_sockaddr_t *addr);
+
+/*%
+ * Enable or disable dnssec validation.
+ */
+isc_result_t
+named_server_validation(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Add a zone to a running process, or modify an existing zone
+ */
+isc_result_t
+named_server_changezone(named_server_t *server, char *command,
+ isc_buffer_t **text);
+
+/*%
+ * Deletes a zone from a running process
+ */
+isc_result_t
+named_server_delzone(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Show current configuration for a given zone
+ */
+isc_result_t
+named_server_showzone(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Lists the status of the signing records for a given zone.
+ */
+isc_result_t
+named_server_signing(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Lists the DNSSEC status for a given zone.
+ */
+isc_result_t
+named_server_dnssec(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Lists status information for a given zone (e.g., name, type, files,
+ * load time, expiry, etc).
+ */
+isc_result_t
+named_server_zonestatus(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Adds/updates a Negative Trust Anchor (NTA) for a specified name and
+ * duration, in a particular view if specified, or in all views.
+ */
+isc_result_t
+named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly,
+ isc_buffer_t **text);
+
+/*%
+ * Generates a test sequence that is only for use in system tests. The
+ * argument is the size of required output in bytes.
+ */
+isc_result_t
+named_server_testgen(isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Force fefresh or print status for managed keys zones.
+ */
+isc_result_t
+named_server_mkeys(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Close and reopen DNSTAP output file.
+ */
+isc_result_t
+named_server_dnstap(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Display or update tcp-{initial,idle,keepalive,advertised}-timeout options.
+ */
+isc_result_t
+named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Control whether stale answers are served or not when configured in
+ * named.conf.
+ */
+isc_result_t
+named_server_servestale(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
diff --git a/bin/named/include/named/smf_globals.h b/bin/named/include/named/smf_globals.h
new file mode 100644
index 0000000..b052822
--- /dev/null
+++ b/bin/named/include/named/smf_globals.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <libscf.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NAMED_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else /* ifdef NAMED_MAIN */
+#define EXTERN extern
+#define INIT(v)
+#endif /* ifdef NAMED_MAIN */
+
+EXTERN unsigned int named_smf_got_instance INIT(0);
+EXTERN unsigned int named_smf_chroot INIT(0);
+EXTERN unsigned int named_smf_want_disable INIT(0);
+
+isc_result_t
+named_smf_add_message(isc_buffer_t **text);
+isc_result_t
+named_smf_get_instance(char **name, int debug, isc_mem_t *mctx);
+
+#undef EXTERN
+#undef INIT
diff --git a/bin/named/include/named/statschannel.h b/bin/named/include/named/statschannel.h
new file mode 100644
index 0000000..8240dc1
--- /dev/null
+++ b/bin/named/include/named/statschannel.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file
+ * \brief
+ * The statistics channels built-in the name server.
+ */
+
+#include <isccfg/aclconf.h>
+
+#include <isccc/types.h>
+#include <named/types.h>
+
+#define NAMED_STATSCHANNEL_HTTPPORT 80
+
+isc_result_t
+named_statschannels_configure(named_server_t *server, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx);
+/*%<
+ * [Re]configure the statistics channels.
+ *
+ * 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.
+ */
+
+void
+named_statschannels_shutdown(named_server_t *server);
+/*%<
+ * Initiate shutdown of all the statistics channel listeners.
+ */
+
+isc_result_t
+named_stats_dump(named_server_t *server, FILE *fp);
+/*%<
+ * Dump statistics counters managed by the server to the file fp.
+ */
diff --git a/bin/named/include/named/tkeyconf.h b/bin/named/include/named/tkeyconf.h
new file mode 100644
index 0000000..79639d6
--- /dev/null
+++ b/bin/named/include/named/tkeyconf.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+named_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ dns_tkeyctx_t **tctxp);
+/*%<
+ * Create a TKEY context and configure it, including the default DH key
+ * and default domain, according to 'options'.
+ *
+ * Requires:
+ *\li 'cfg' is a valid configuration options object.
+ *\li 'mctx' is not NULL
+ *\li 'tctx' is not NULL
+ *\li '*tctx' is NULL
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/bin/named/include/named/transportconf.h b/bin/named/include/named/transportconf.h
new file mode 100644
index 0000000..1e472ff
--- /dev/null
+++ b/bin/named/include/named/transportconf.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/transport.h>
+
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_transport_list_t **listp);
+/*%<
+ * Create a list of transport objects (DoT or DoH) and configure them
+ * according to 'key-file', 'cert-file', 'ca-file' or 'hostname'
+ * statements.
+ *
+ * Requires:
+ * \li 'config' is not NULL.
+ * \li 'vconfig' is not NULL.
+ * \li 'mctx' is not NULL
+ * \li 'listp' is not NULL, and '*listp' is NULL
+ *
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/bin/named/include/named/tsigconf.h b/bin/named/include/named/tsigconf.h
new file mode 100644
index 0000000..32a0120
--- /dev/null
+++ b/bin/named/include/named/tsigconf.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+named_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp);
+/*%<
+ * Create a TSIG key ring and configure it according to the 'key'
+ * statements in the global and view configuration objects.
+ *
+ * Requires:
+ * \li 'config' is not NULL.
+ * \li 'vconfig' is not NULL.
+ * \li 'mctx' is not NULL
+ * \li 'ringp' is not NULL, and '*ringp' is NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h
new file mode 100644
index 0000000..585c141
--- /dev/null
+++ b/bin/named/include/named/types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <dns/types.h>
+
+typedef struct named_cache named_cache_t;
+typedef ISC_LIST(named_cache_t) named_cachelist_t;
+typedef struct named_server named_server_t;
+typedef struct named_xmld named_xmld_t;
+typedef struct named_xmldmgr named_xmldmgr_t;
+typedef struct named_controls named_controls_t;
+typedef struct named_dispatch named_dispatch_t;
+typedef ISC_LIST(named_dispatch_t) named_dispatchlist_t;
+typedef struct named_statschannel named_statschannel_t;
+typedef ISC_LIST(named_statschannel_t) named_statschannellist_t;
+
+/*%
+ * Used for server->reload_status as printed by `rndc status`
+ */
+typedef enum {
+ NAMED_RELOAD_DONE,
+ NAMED_RELOAD_IN_PROGRESS,
+ NAMED_RELOAD_FAILED,
+} named_reload_t;
diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h
new file mode 100644
index 0000000..387d8a1
--- /dev/null
+++ b/bin/named/include/named/zoneconf.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_kasplist_t *kasplist, dns_zone_t *zone,
+ dns_zone_t *raw);
+/*%<
+ * Configure or reconfigure a zone according to the named.conf
+ * data.
+ *
+ * The zone origin is not configured, it is assumed to have been set
+ * at zone creation time.
+ *
+ * Require:
+ * \li 'ac' to point to an initialized cfg_aclconfctx_t.
+ * \li 'kasplist' to be initialized.
+ * \li 'zone' to be initialized.
+ */
+
+bool
+named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig);
+/*%<
+ * If 'zone' can be safely reconfigured according to the configuration
+ * data in 'zconfig', return true. If the configuration data is so
+ * different from the current zone state that the zone needs to be destroyed
+ * and recreated, return false.
+ */
+
+bool
+named_zone_inlinesigning(const cfg_obj_t *zconfig);
+/*%<
+ * Determine if zone uses inline-signing. This is true if inline-signing
+ * is set to yes.
+ */
+
+isc_result_t
+named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone,
+ dns_rdataclass_t rdclass, dns_name_t *name);
+/*%>
+ * configure a DLZ zone, setting up the database methods and calling
+ * postload to load the origin values
+ *
+ * Require:
+ * \li 'dlzdatabase' to be a valid dlz database
+ * \li 'zone' to be initialized.
+ * \li 'rdclass' to be a valid rdataclass
+ * \li 'name' to be a valid zone origin name
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/bin/named/log.c b/bin/named/log.c
new file mode 100644
index 0000000..6efea02
--- /dev/null
+++ b/bin/named/log.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdlib.h>
+
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+
+#include <isccfg/log.h>
+
+#include <ns/log.h>
+
+#include <named/log.h>
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif /* ifndef ISC_FACILITY */
+
+/*%
+ * When adding a new category, be sure to add the appropriate
+ * \#define to <named/log.h> and to update the list in
+ * bin/check/check-tool.c.
+ */
+static isc_logcategory_t categories[] = { { "", 0 },
+ { "unmatched", 0 },
+ { NULL, 0 } };
+
+/*%
+ * When adding a new module, be sure to add the appropriate
+ * \#define to <dns/log.h>.
+ */
+static isc_logmodule_t modules[] = {
+ { "main", 0 }, { "server", 0 }, { "control", 0 }, { NULL, 0 }
+};
+
+isc_result_t
+named_log_init(bool safe) {
+ isc_result_t result;
+ isc_logconfig_t *lcfg = NULL;
+
+ named_g_categories = categories;
+ named_g_modules = modules;
+
+ /*
+ * Setup a logging context.
+ */
+ isc_log_create(named_g_mctx, &named_g_lctx, &lcfg);
+
+ /*
+ * named-checktool.c:setup_logging() needs to be kept in sync.
+ */
+ isc_log_registercategories(named_g_lctx, named_g_categories);
+ isc_log_registermodules(named_g_lctx, named_g_modules);
+ isc_log_setcontext(named_g_lctx);
+ dns_log_init(named_g_lctx);
+ dns_log_setcontext(named_g_lctx);
+ cfg_log_init(named_g_lctx);
+ ns_log_init(named_g_lctx);
+ ns_log_setcontext(named_g_lctx);
+
+ if (safe) {
+ named_log_setsafechannels(lcfg);
+ } else {
+ named_log_setdefaultchannels(lcfg);
+ }
+
+ result = named_log_setdefaultcategory(lcfg);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ named_log_setdefaultsslkeylogfile(lcfg);
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_log_destroy(&named_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+
+ return (result);
+}
+
+void
+named_log_setdefaultchannels(isc_logconfig_t *lcfg) {
+ isc_logdestination_t destination;
+
+ /*
+ * By default, the logging library makes "default_debug" log to
+ * stderr. In BIND, we want to override this and log to named.run
+ * instead, unless the -g option was given.
+ */
+ if (!named_g_logstderr) {
+ destination.file.stream = NULL;
+ destination.file.name = "named.run";
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC, &destination,
+ ISC_LOG_PRINTTIME | ISC_LOG_DEBUGONLY);
+ }
+
+ if (named_g_logfile != NULL) {
+ destination.file.stream = NULL;
+ destination.file.name = named_g_logfile;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(lcfg, "default_logfile", ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC, &destination,
+ ISC_LOG_PRINTTIME |
+ ISC_LOG_PRINTCATEGORY |
+ ISC_LOG_PRINTLEVEL);
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG,
+ ISC_LOG_INFO, &destination, 0);
+#endif /* if ISC_FACILITY != LOG_DAEMON */
+
+ /*
+ * Set the initial debug level.
+ */
+ isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel);
+}
+
+void
+named_log_setsafechannels(isc_logconfig_t *lcfg) {
+ isc_logdestination_t destination;
+
+ if (!named_g_logstderr) {
+ isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TONULL,
+ ISC_LOG_DYNAMIC, NULL, 0);
+
+ /*
+ * Setting the debug level to zero should get the output
+ * discarded a bit faster.
+ */
+ isc_log_setdebuglevel(named_g_lctx, 0);
+ } else {
+ isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel);
+ }
+
+ if (named_g_logfile != NULL) {
+ destination.file.stream = NULL;
+ destination.file.name = named_g_logfile;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(lcfg, "default_logfile", ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC, &destination,
+ ISC_LOG_PRINTTIME |
+ ISC_LOG_PRINTCATEGORY |
+ ISC_LOG_PRINTLEVEL);
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG,
+ ISC_LOG_INFO, &destination, 0);
+#endif /* if ISC_FACILITY != LOG_DAEMON */
+}
+
+/*
+ * If the SSLKEYLOGFILE environment variable is set, TLS pre-master secrets are
+ * logged (for debugging purposes) to the file whose path is provided in that
+ * variable. Set up a default logging channel which maintains up to 10 files
+ * containing TLS pre-master secrets, each up to 100 MB in size. If the
+ * SSLKEYLOGFILE environment variable is set to the string "config", suppress
+ * creation of the default channel, allowing custom logging channel
+ * configuration for TLS pre-master secrets to be provided via the "logging"
+ * stanza in the configuration file.
+ */
+void
+named_log_setdefaultsslkeylogfile(isc_logconfig_t *lcfg) {
+ const char *sslkeylogfile_path = getenv("SSLKEYLOGFILE");
+ isc_logdestination_t destination = {
+ .file = {
+ .name = sslkeylogfile_path,
+ .versions = 10,
+ .suffix = isc_log_rollsuffix_timestamp,
+ .maximum_size = 100 * 1024 * 1024,
+ },
+ };
+ isc_result_t result;
+
+ if (sslkeylogfile_path == NULL ||
+ strcmp(sslkeylogfile_path, "config") == 0)
+ {
+ return;
+ }
+
+ isc_log_createchannel(lcfg, "default_sslkeylogfile", ISC_LOG_TOFILE,
+ ISC_LOG_INFO, &destination, 0);
+ result = isc_log_usechannel(lcfg, "default_sslkeylogfile",
+ ISC_LOGCATEGORY_SSLKEYLOG, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_log_setdefaultcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ result = isc_log_usechannel(lcfg, "default_debug",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (!named_g_logstderr) {
+ if (named_g_logfile != NULL) {
+ result = isc_log_usechannel(lcfg, "default_logfile",
+ ISC_LOGCATEGORY_DEFAULT,
+ NULL);
+ } else if (!named_g_nosyslog) {
+ result = isc_log_usechannel(lcfg, "default_syslog",
+ ISC_LOGCATEGORY_DEFAULT,
+ NULL);
+ }
+ }
+
+cleanup:
+ return (result);
+}
+
+isc_result_t
+named_log_setunmatchedcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+
+ result = isc_log_usechannel(lcfg, "null", NAMED_LOGCATEGORY_UNMATCHED,
+ NULL);
+ return (result);
+}
+
+void
+named_log_shutdown(void) {
+ isc_log_destroy(&named_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+}
diff --git a/bin/named/logconf.c b/bin/named/logconf.c
new file mode 100644
index 0000000..5fd0904
--- /dev/null
+++ b/bin/named/logconf.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/file.h>
+#include <isc/offset.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/syslog.h>
+#include <isc/util.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/log.h>
+
+#include <named/log.h>
+#include <named/logconf.h>
+
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+/*%
+ * Set up a logging category according to the named.conf data
+ * in 'ccat' and add it to 'logconfig'.
+ */
+static isc_result_t
+category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) {
+ isc_result_t result;
+ const char *catname;
+ isc_logcategory_t *category;
+ isc_logmodule_t *module;
+ const cfg_obj_t *destinations = NULL;
+ const cfg_listelt_t *element = NULL;
+
+ catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name"));
+ category = isc_log_categorybyname(named_g_lctx, catname);
+ if (category == NULL) {
+ cfg_obj_log(ccat, named_g_lctx, ISC_LOG_ERROR,
+ "unknown logging category '%s' ignored", catname);
+ /*
+ * Allow further processing by returning success.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ if (logconfig == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ module = NULL;
+
+ destinations = cfg_tuple_get(ccat, "destinations");
+ for (element = cfg_list_first(destinations); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ const char *channelname = cfg_obj_asstring(channel);
+
+ result = isc_log_usechannel(logconfig, channelname, category,
+ module);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "logging channel '%s': %s", channelname,
+ isc_result_totext(result));
+ return (result);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Set up a logging channel according to the named.conf data
+ * in 'cchan' and add it to 'logconfig'.
+ */
+static isc_result_t
+channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_logdestination_t dest;
+ unsigned int type;
+ unsigned int flags = 0;
+ int level;
+ const char *channelname;
+ const cfg_obj_t *fileobj = NULL;
+ const cfg_obj_t *syslogobj = NULL;
+ const cfg_obj_t *nullobj = NULL;
+ const cfg_obj_t *stderrobj = NULL;
+ const cfg_obj_t *severity = NULL;
+ int i;
+
+ channelname = cfg_obj_asstring(cfg_map_getname(channel));
+
+ (void)cfg_map_get(channel, "file", &fileobj);
+ (void)cfg_map_get(channel, "syslog", &syslogobj);
+ (void)cfg_map_get(channel, "null", &nullobj);
+ (void)cfg_map_get(channel, "stderr", &stderrobj);
+
+ i = 0;
+ if (fileobj != NULL) {
+ i++;
+ }
+ if (syslogobj != NULL) {
+ i++;
+ }
+ if (nullobj != NULL) {
+ i++;
+ }
+ if (stderrobj != NULL) {
+ i++;
+ }
+
+ if (i != 1) {
+ cfg_obj_log(channel, named_g_lctx, ISC_LOG_ERROR,
+ "channel '%s': exactly one of file, syslog, "
+ "null, and stderr must be present",
+ channelname);
+ return (ISC_R_FAILURE);
+ }
+
+ type = ISC_LOG_TONULL;
+
+ if (fileobj != NULL) {
+ const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
+ const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
+ const cfg_obj_t *versionsobj = cfg_tuple_get(fileobj,
+ "versions");
+ const cfg_obj_t *suffixobj = cfg_tuple_get(fileobj, "suffix");
+ int32_t versions = ISC_LOG_ROLLNEVER;
+ isc_log_rollsuffix_t suffix = isc_log_rollsuffix_increment;
+ isc_offset_t size = 0;
+ uint64_t maxoffset;
+
+ /*
+ * isc_offset_t is a signed integer type, so the maximum
+ * value is all 1s except for the MSB.
+ */
+ switch (sizeof(isc_offset_t)) {
+ case 4:
+ maxoffset = 0x7fffffffULL;
+ break;
+ case 8:
+ maxoffset = 0x7fffffffffffffffULL;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ type = ISC_LOG_TOFILE;
+
+ if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) {
+ versions = cfg_obj_asuint32(versionsobj);
+ } else if (versionsobj != NULL &&
+ cfg_obj_isstring(versionsobj) &&
+ strcasecmp(cfg_obj_asstring(versionsobj),
+ "unlimited") == 0)
+ {
+ versions = ISC_LOG_ROLLINFINITE;
+ }
+ if (sizeobj != NULL && cfg_obj_isuint64(sizeobj) &&
+ cfg_obj_asuint64(sizeobj) < maxoffset)
+ {
+ size = (isc_offset_t)cfg_obj_asuint64(sizeobj);
+ }
+ if (suffixobj != NULL && cfg_obj_isstring(suffixobj) &&
+ strcasecmp(cfg_obj_asstring(suffixobj), "timestamp") == 0)
+ {
+ suffix = isc_log_rollsuffix_timestamp;
+ }
+
+ dest.file.stream = NULL;
+ dest.file.name = cfg_obj_asstring(pathobj);
+ dest.file.versions = versions;
+ dest.file.suffix = suffix;
+ dest.file.maximum_size = size;
+ } else if (syslogobj != NULL) {
+ int facility = LOG_DAEMON;
+
+ type = ISC_LOG_TOSYSLOG;
+
+ if (cfg_obj_isstring(syslogobj)) {
+ const char *facilitystr = cfg_obj_asstring(syslogobj);
+ (void)isc_syslog_facilityfromstring(facilitystr,
+ &facility);
+ }
+ dest.facility = facility;
+ } else if (stderrobj != NULL) {
+ type = ISC_LOG_TOFILEDESC;
+ dest.file.stream = stderr;
+ dest.file.name = NULL;
+ dest.file.versions = ISC_LOG_ROLLNEVER;
+ dest.file.suffix = isc_log_rollsuffix_increment;
+ dest.file.maximum_size = 0;
+ }
+
+ /*
+ * Munge flags.
+ */
+ {
+ const cfg_obj_t *printcat = NULL;
+ const cfg_obj_t *printsev = NULL;
+ const cfg_obj_t *printtime = NULL;
+ const cfg_obj_t *buffered = NULL;
+
+ (void)cfg_map_get(channel, "print-category", &printcat);
+ (void)cfg_map_get(channel, "print-severity", &printsev);
+ (void)cfg_map_get(channel, "print-time", &printtime);
+ (void)cfg_map_get(channel, "buffered", &buffered);
+
+ if (printcat != NULL && cfg_obj_asboolean(printcat)) {
+ flags |= ISC_LOG_PRINTCATEGORY;
+ }
+ if (printsev != NULL && cfg_obj_asboolean(printsev)) {
+ flags |= ISC_LOG_PRINTLEVEL;
+ }
+ if (buffered != NULL && cfg_obj_asboolean(buffered)) {
+ flags |= ISC_LOG_BUFFERED;
+ }
+ if (printtime != NULL && cfg_obj_isboolean(printtime)) {
+ if (cfg_obj_asboolean(printtime)) {
+ flags |= ISC_LOG_PRINTTIME;
+ }
+ } else if (printtime != NULL) { /* local/iso8601/iso8601-utc */
+ const char *s = cfg_obj_asstring(printtime);
+ flags |= ISC_LOG_PRINTTIME;
+ if (strcasecmp(s, "iso8601") == 0) {
+ flags |= ISC_LOG_ISO8601;
+ } else if (strcasecmp(s, "iso8601-utc") == 0) {
+ flags |= ISC_LOG_ISO8601 | ISC_LOG_UTC;
+ }
+ }
+ }
+
+ level = ISC_LOG_INFO;
+ if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
+ if (cfg_obj_isstring(severity)) {
+ const char *str = cfg_obj_asstring(severity);
+ if (strcasecmp(str, "critical") == 0) {
+ level = ISC_LOG_CRITICAL;
+ } else if (strcasecmp(str, "error") == 0) {
+ level = ISC_LOG_ERROR;
+ } else if (strcasecmp(str, "warning") == 0) {
+ level = ISC_LOG_WARNING;
+ } else if (strcasecmp(str, "notice") == 0) {
+ level = ISC_LOG_NOTICE;
+ } else if (strcasecmp(str, "info") == 0) {
+ level = ISC_LOG_INFO;
+ } else if (strcasecmp(str, "dynamic") == 0) {
+ level = ISC_LOG_DYNAMIC;
+ }
+ } else {
+ /* debug */
+ level = cfg_obj_asuint32(severity);
+ }
+ }
+
+ if (logconfig != NULL) {
+ isc_log_createchannel(logconfig, channelname, type, level,
+ &dest, flags);
+ }
+
+ if (type == ISC_LOG_TOFILE) {
+ FILE *fp;
+
+ /*
+ * Test to make sure that file is a plain file.
+ * Fix defect #22771
+ */
+ result = isc_file_isplainfile(dest.file.name);
+ if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) {
+ /*
+ * Test that the file can be opened, since
+ * isc_log_open() can't effectively report
+ * failures when called in isc_log_doit().
+ */
+ result = isc_stdio_open(dest.file.name, "a", &fp);
+ if (result != ISC_R_SUCCESS) {
+ if (logconfig != NULL && !named_g_nosyslog) {
+ syslog(LOG_ERR,
+ "isc_stdio_open '%s' failed: "
+ "%s",
+ dest.file.name,
+ isc_result_totext(result));
+ }
+ } else {
+ (void)isc_stdio_close(fp);
+ }
+ goto done;
+ }
+ if (logconfig != NULL && !named_g_nosyslog) {
+ syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s",
+ dest.file.name, isc_result_totext(result));
+ }
+ }
+
+done:
+ return (result);
+}
+
+isc_result_t
+named_logconfig(isc_logconfig_t *logconfig, const cfg_obj_t *logstmt) {
+ isc_result_t result;
+ const cfg_obj_t *channels = NULL;
+ const cfg_obj_t *categories = NULL;
+ const cfg_listelt_t *element;
+ bool default_set = false;
+ bool unmatched_set = false;
+ const cfg_obj_t *catname;
+
+ if (logconfig != NULL) {
+ named_log_setdefaultchannels(logconfig);
+ named_log_setdefaultsslkeylogfile(logconfig);
+ }
+
+ (void)cfg_map_get(logstmt, "channel", &channels);
+ for (element = cfg_list_first(channels); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ CHECK(channel_fromconf(channel, logconfig));
+ }
+
+ (void)cfg_map_get(logstmt, "category", &categories);
+ for (element = cfg_list_first(categories); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *category = cfg_listelt_value(element);
+ CHECK(category_fromconf(category, logconfig));
+ if (!default_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "default") == 0) {
+ default_set = true;
+ }
+ }
+ if (!unmatched_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
+ {
+ unmatched_set = true;
+ }
+ }
+ }
+
+ if (logconfig != NULL && !default_set) {
+ CHECK(named_log_setdefaultcategory(logconfig));
+ }
+
+ if (logconfig != NULL && !unmatched_set) {
+ CHECK(named_log_setunmatchedcategory(logconfig));
+ }
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ return (result);
+}
diff --git a/bin/named/main.c b/bin/named/main.c
new file mode 100644
index 0000000..154e17e
--- /dev/null
+++ b/bin/named/main.c
@@ -0,0 +1,1663 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uv.h>
+
+#ifdef HAVE_DNSTAP
+#include <protobuf-c/protobuf-c.h>
+#endif
+
+#include <isc/app.h>
+#include <isc/attributes.h>
+#include <isc/backtrace.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/httpd.h>
+#include <isc/managers.h>
+#include <isc/netmgr.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/dispatch.h>
+#include <dns/dyndb.h>
+#include <dns/name.h>
+#include <dns/resolver.h>
+#include <dns/view.h>
+
+#include <dlz/dlz_dlopen_driver.h>
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+#include <gperftools/profiler.h>
+#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */
+
+#ifdef HAVE_JSON_C
+#include <json_c_version.h>
+#endif /* HAVE_JSON_C */
+
+#ifdef HAVE_GEOIP2
+#include <maxminddb.h>
+#endif /* ifdef HAVE_GEOIP2 */
+
+/*
+ * Defining NAMED_MAIN provides storage declarations (rather than extern)
+ * for variables in named/globals.h.
+ */
+#define NAMED_MAIN 1
+
+#include <ns/interfacemgr.h>
+
+#include <named/builtin.h>
+#include <named/config.h>
+#include <named/control.h>
+#include <named/fuzz.h>
+#include <named/globals.h> /* Explicit, though named/log.h includes it. */
+#include <named/log.h>
+#include <named/main.h>
+#include <named/os.h>
+#include <named/server.h>
+#ifdef HAVE_LIBSCF
+#include <named/smf_globals.h>
+#endif /* ifdef HAVE_LIBSCF */
+
+#include <openssl/crypto.h>
+#include <openssl/opensslv.h>
+#ifdef HAVE_LIBXML2
+#include <libxml/parser.h>
+#include <libxml/xmlversion.h>
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif /* ifdef HAVE_ZLIB */
+#ifdef HAVE_LIBNGHTTP2
+#include <nghttp2/nghttp2.h>
+#endif
+/*
+ * Include header files for database drivers here.
+ */
+/* #include "xxdb.h" */
+
+/*
+ * The maximum number of stack frames to dump on assertion failure.
+ */
+#ifndef BACKTRACE_MAXFRAME
+#define BACKTRACE_MAXFRAME 128
+#endif /* ifndef BACKTRACE_MAXFRAME */
+
+extern unsigned int dns_zone_mkey_hour;
+extern unsigned int dns_zone_mkey_day;
+extern unsigned int dns_zone_mkey_month;
+
+static bool want_stats = false;
+static char program_name[NAME_MAX] = "named";
+static char absolute_conffile[PATH_MAX];
+static char saved_command_line[4096] = { 0 };
+static char ellipsis[5] = { 0 };
+static char version[512];
+static int maxudp = 0;
+
+/*
+ * -T options:
+ */
+static bool dropedns = false;
+static bool ednsformerr = false;
+static bool ednsnotimp = false;
+static bool ednsrefused = false;
+static bool fixedlocal = false;
+static bool noaa = false;
+static bool noedns = false;
+static bool nonearest = false;
+static bool nosoa = false;
+static bool notcp = false;
+static bool sigvalinsecs = false;
+static bool transferinsecs = false;
+static bool transferslowly = false;
+static bool transferstuck = false;
+
+/*
+ * -4 and -6
+ */
+static bool disable6 = false;
+static bool disable4 = false;
+
+void
+named_main_earlywarning(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (named_g_lctx != NULL) {
+ isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_WARNING, format,
+ args);
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+}
+
+void
+named_main_earlyfatal(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (named_g_lctx != NULL) {
+ isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, format,
+ args);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to early fatal error)");
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+
+ exit(1);
+}
+
+noreturn static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond);
+
+static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond) {
+ void *tracebuf[BACKTRACE_MAXFRAME];
+ int nframes;
+
+ /*
+ * Handle assertion failures.
+ */
+
+ if (named_g_lctx != NULL) {
+ /*
+ * Reset the assertion callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_assertion_setcallback(NULL);
+
+ nframes = isc_backtrace(tracebuf, BACKTRACE_MAXFRAME);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: %s(%s) failed%s", file, line,
+ isc_assertion_typetotext(type), cond,
+ (nframes > 0) ? ", back trace" : "");
+ if (nframes > 0) {
+ char **strs = isc_backtrace_symbols(tracebuf, nframes);
+ if (strs != NULL) {
+ for (int i = 0; i < nframes; i++) {
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN,
+ ISC_LOG_CRITICAL, "%s",
+ strs[i]);
+ }
+ }
+ }
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to assertion failure)");
+ } else {
+ fprintf(stderr, "%s:%d: %s(%s) failed\n", file, line,
+ isc_assertion_typetotext(type), cond);
+ fflush(stderr);
+ }
+
+ if (named_g_coreok) {
+ abort();
+ }
+ exit(1);
+}
+
+noreturn static void
+library_fatal_error(const char *file, int line, const char *func,
+ const char *format, va_list args) ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_fatal_error(const char *file, int line, const char *func,
+ const char *format, va_list args) {
+ /*
+ * Handle isc_error_fatal() calls from our libraries.
+ */
+
+ if (named_g_lctx != NULL) {
+ /*
+ * Reset the error callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_error_setfatal(NULL);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d:%s(): fatal error: ", file, line, func);
+ isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, format,
+ args);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to fatal error in library)");
+ } else {
+ fprintf(stderr, "%s:%d:%s(): fatal error: ", file, line, func);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+
+ if (named_g_coreok) {
+ abort();
+ }
+ exit(1);
+}
+
+static void
+library_unexpected_error(const char *file, int line, const char *func,
+ const char *format, va_list args)
+ ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_unexpected_error(const char *file, int line, const char *func,
+ const char *format, va_list args) {
+ /*
+ * Handle isc_error_unexpected() calls from our libraries.
+ */
+
+ if (named_g_lctx != NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_ERROR,
+ "%s:%d:%s(): unexpected error: ", file, line,
+ func);
+ isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_ERROR, format,
+ args);
+ } else {
+ fprintf(stderr, "%s:%d:%s(): fatal error: ", file, line, func);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+}
+
+static void
+usage(void) {
+ fprintf(stderr, "usage: named [-4|-6] [-c conffile] [-d debuglevel] "
+ "[-D comment] [-E engine]\n"
+ " [-f|-g] [-L logfile] [-n number_of_cpus] "
+ "[-p port] [-s]\n"
+ " [-S sockets] [-t chrootdir] [-u "
+ "username] [-U listeners]\n"
+ " [-X lockfile] [-m "
+ "{usage|trace|record|size|mctx}]\n"
+ " [-M fill|nofill]\n"
+ "usage: named [-v|-V|-C]\n");
+}
+
+static void
+save_command_line(int argc, char *argv[]) {
+ int i;
+ char *dst = saved_command_line;
+ char *eob = saved_command_line + sizeof(saved_command_line) - 1;
+ char *rollback = dst;
+
+ for (i = 1; i < argc && dst < eob; i++) {
+ char *src = argv[i];
+ bool quoted = false;
+
+ rollback = dst;
+ *dst++ = ' ';
+
+ while (*src != '\0' && dst < eob) {
+ if (isalnum(*(unsigned char *)src) || *src == ',' ||
+ *src == '-' || *src == '_' || *src == '.' ||
+ *src == '/')
+ {
+ *dst++ = *src++;
+ } else if (isprint(*(unsigned char *)src)) {
+ if (dst + 2 >= eob) {
+ goto add_ellipsis;
+ }
+ *dst++ = '\\';
+ *dst++ = *src++;
+ } else {
+ /*
+ * Control character found in the input,
+ * quote the whole arg and restart
+ */
+ if (!quoted) {
+ dst = rollback;
+ src = argv[i];
+
+ if (dst + 3 >= eob) {
+ goto add_ellipsis;
+ }
+
+ *dst++ = ' ';
+ *dst++ = '$';
+ *dst++ = '\'';
+
+ quoted = true;
+ continue;
+ } else {
+ char tmp[5];
+ int c = snprintf(tmp, sizeof(tmp),
+ "\\%03o", *src++);
+ if (dst + c >= eob) {
+ goto add_ellipsis;
+ }
+ memmove(dst, tmp, c);
+ dst += c;
+ }
+ }
+ }
+ if (quoted) {
+ if (dst == eob) {
+ goto add_ellipsis;
+ }
+ *dst++ = '\'';
+ }
+ }
+
+ if (dst < eob) {
+ return;
+ }
+add_ellipsis:
+ dst = rollback;
+ *dst = '\0';
+ strlcpy(ellipsis, " ...", sizeof(ellipsis));
+}
+
+static int
+parse_int(char *arg, const char *desc) {
+ char *endp;
+ int tmp;
+ long int ltmp;
+
+ ltmp = strtol(arg, &endp, 10);
+ tmp = (int)ltmp;
+ if (*endp != '\0') {
+ named_main_earlyfatal("%s '%s' must be numeric", desc, arg);
+ }
+ if (tmp < 0 || tmp != ltmp) {
+ named_main_earlyfatal("%s '%s' out of range", desc, arg);
+ }
+ return (tmp);
+}
+
+static struct flag_def {
+ const char *name;
+ unsigned int value;
+ bool negate;
+} mem_debug_flags[] = { { "none", 0, false },
+ { "trace", ISC_MEM_DEBUGTRACE, false },
+ { "record", ISC_MEM_DEBUGRECORD, false },
+ { "usage", ISC_MEM_DEBUGUSAGE, false },
+ { NULL, 0, false } },
+ mem_context_flags[] = { { "fill", ISC_MEMFLAG_FILL, false },
+ { "nofill", ISC_MEMFLAG_FILL, true },
+ { NULL, 0, false } };
+
+static void
+set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) {
+ bool clear = false;
+
+ for (;;) {
+ const struct flag_def *def;
+ const char *end = strchr(arg, ',');
+ int arglen;
+ if (end == NULL) {
+ end = arg + strlen(arg);
+ }
+ arglen = (int)(end - arg);
+ for (def = defs; def->name != NULL; def++) {
+ if (arglen == (int)strlen(def->name) &&
+ memcmp(arg, def->name, arglen) == 0)
+ {
+ if (def->value == 0) {
+ clear = true;
+ }
+ if (def->negate) {
+ *ret &= ~(def->value);
+ } else {
+ *ret |= def->value;
+ }
+ goto found;
+ }
+ }
+ named_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg);
+ found:
+ if (clear || (*end == '\0')) {
+ break;
+ }
+ arg = end + 1;
+ }
+
+ if (clear) {
+ *ret = 0;
+ }
+}
+
+static void
+list_dnssec_algorithms(isc_buffer_t *b) {
+ for (dst_algorithm_t i = DST_ALG_UNKNOWN; i < DST_MAX_ALGS; i++) {
+ if (i == DST_ALG_DH || i == DST_ALG_GSSAPI ||
+ (i >= DST_ALG_HMAC_FIRST && i <= DST_ALG_HMAC_LAST))
+ {
+ continue;
+ }
+ if (dst_algorithm_supported(i)) {
+ isc_buffer_putstr(b, " ");
+ (void)dns_secalg_totext(i, b);
+ }
+ }
+}
+
+static void
+list_ds_algorithms(isc_buffer_t *b) {
+ for (size_t i = 0; i < 256; i++) {
+ if (dst_ds_digest_supported(i)) {
+ isc_buffer_putstr(b, " ");
+ (void)dns_dsdigest_totext(i, b);
+ }
+ }
+}
+
+static void
+list_hmac_algorithms(isc_buffer_t *b) {
+ isc_buffer_t sb = *b;
+ for (dst_algorithm_t i = DST_ALG_HMAC_FIRST; i <= DST_ALG_HMAC_LAST;
+ i++)
+ {
+ if (i == DST_ALG_GSSAPI) {
+ continue;
+ }
+ if (dst_algorithm_supported(i)) {
+ isc_buffer_putstr(b, " ");
+ isc_buffer_putstr(b, dst_hmac_algorithm_totext(i));
+ }
+ }
+ for (unsigned char *s = isc_buffer_used(&sb); s != isc_buffer_used(b);
+ s++)
+ {
+ *s = toupper(*s);
+ }
+}
+
+static void
+logit(isc_buffer_t *b) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "%.*s",
+ (int)isc_buffer_usedlength(b),
+ (char *)isc_buffer_base(b));
+}
+
+static void
+printit(isc_buffer_t *b) {
+ printf("%.*s\n", (int)isc_buffer_usedlength(b),
+ (char *)isc_buffer_base(b));
+}
+
+static void
+format_supported_algorithms(void (*emit)(isc_buffer_t *b)) {
+ isc_buffer_t b;
+ char buf[512];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+ isc_buffer_putstr(&b, "DNSSEC algorithms:");
+ list_dnssec_algorithms(&b);
+ (*emit)(&b);
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+ isc_buffer_putstr(&b, "DS algorithms:");
+ list_ds_algorithms(&b);
+ (*emit)(&b);
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+ isc_buffer_putstr(&b, "HMAC algorithms:");
+ list_hmac_algorithms(&b);
+ (*emit)(&b);
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+ isc_buffer_printf(&b, "TKEY mode 2 support (Diffie-Hellman): %s",
+ (dst_algorithm_supported(DST_ALG_DH) &&
+ dst_algorithm_supported(DST_ALG_HMACMD5))
+ ? "yes"
+ : "non");
+ (*emit)(&b);
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+ isc_buffer_printf(&b, "TKEY mode 3 support (GSS-API): %s",
+ dst_algorithm_supported(DST_ALG_GSSAPI) ? "yes"
+ : "no");
+ (*emit)(&b);
+}
+
+static void
+printversion(bool verbose) {
+ char rndcconf[PATH_MAX], *dot = NULL;
+ isc_mem_t *mctx = NULL;
+ isc_result_t result;
+ isc_buffer_t b;
+ char buf[512];
+#if defined(HAVE_GEOIP2)
+ cfg_parser_t *parser = NULL;
+ cfg_obj_t *config = NULL;
+ const cfg_obj_t *defaults = NULL, *obj = NULL;
+#endif /* if defined(HAVE_GEOIP2) */
+
+ printf("%s%s <id:%s>\n", PACKAGE_STRING, PACKAGE_DESCRIPTION,
+ PACKAGE_SRCID);
+
+ if (!verbose) {
+ return;
+ }
+
+ printf("running on %s\n", named_os_uname());
+ printf("built by %s with %s\n", PACKAGE_BUILDER, PACKAGE_CONFIGARGS);
+#ifdef __clang__
+ printf("compiled by CLANG %s\n", __VERSION__);
+#else /* ifdef __clang__ */
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ printf("compiled by ICC %s\n", __VERSION__);
+#else /* if defined(__ICC) || defined(__INTEL_COMPILER) */
+#ifdef __GNUC__
+ printf("compiled by GCC %s\n", __VERSION__);
+#endif /* ifdef __GNUC__ */
+#endif /* if defined(__ICC) || defined(__INTEL_COMPILER) */
+#endif /* ifdef __clang__ */
+#ifdef _MSC_VER
+ printf("compiled by MSVC %d\n", _MSC_VER);
+#endif /* ifdef _MSC_VER */
+#ifdef __SUNPRO_C
+ printf("compiled by Solaris Studio %x\n", __SUNPRO_C);
+#endif /* ifdef __SUNPRO_C */
+ printf("compiled with OpenSSL version: %s\n", OPENSSL_VERSION_TEXT);
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */
+ printf("linked to OpenSSL version: %s\n",
+ OpenSSL_version(OPENSSL_VERSION));
+
+#else /* if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= \
+ * 0x10100000L */
+ printf("linked to OpenSSL version: %s\n",
+ SSLeay_version(SSLEAY_VERSION));
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
+ printf("compiled with libuv version: %d.%d.%d\n", UV_VERSION_MAJOR,
+ UV_VERSION_MINOR, UV_VERSION_PATCH);
+ printf("linked to libuv version: %s\n", uv_version_string());
+#if HAVE_LIBNGHTTP2
+ nghttp2_info *nginfo = NULL;
+ printf("compiled with libnghttp2 version: %s\n", NGHTTP2_VERSION);
+ nginfo = nghttp2_version(1);
+ printf("linked to libnghttp2 version: %s\n", nginfo->version_str);
+#endif
+#ifdef HAVE_LIBXML2
+ printf("compiled with libxml2 version: %s\n", LIBXML_DOTTED_VERSION);
+ printf("linked to libxml2 version: %s\n", xmlParserVersion);
+#endif /* ifdef HAVE_LIBXML2 */
+#if defined(HAVE_JSON_C)
+ printf("compiled with json-c version: %s\n", JSON_C_VERSION);
+ printf("linked to json-c version: %s\n", json_c_version());
+#endif /* if defined(HAVE_JSON_C) */
+#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION)
+ printf("compiled with zlib version: %s\n", ZLIB_VERSION);
+ printf("linked to zlib version: %s\n", zlibVersion());
+#endif /* if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) */
+#if defined(HAVE_GEOIP2)
+ /* Unfortunately, no version define on link time */
+ printf("linked to maxminddb version: %s\n", MMDB_lib_version());
+#endif /* if defined(HAVE_GEOIP2) */
+#if defined(HAVE_DNSTAP)
+ printf("compiled with protobuf-c version: %s\n", PROTOBUF_C_VERSION);
+ printf("linked to protobuf-c version: %s\n", protobuf_c_version());
+#endif /* if defined(HAVE_DNSTAP) */
+ printf("threads support is enabled\n");
+
+ isc_mem_create(&mctx);
+ result = dst_lib_init(mctx, named_g_engine);
+ if (result == ISC_R_SUCCESS) {
+ isc_buffer_init(&b, buf, sizeof(buf));
+ format_supported_algorithms(printit);
+ printf("\n");
+ dst_lib_destroy();
+ } else {
+ printf("DST initialization failure: %s\n",
+ isc_result_totext(result));
+ }
+
+ /*
+ * The default rndc.conf and rndc.key paths are in the same
+ * directory, but named only has rndc.key defined internally.
+ * We construct the rndc.conf path from it.
+ */
+ strlcpy(rndcconf, named_g_keyfile, sizeof(rndcconf));
+ dot = strrchr(rndcconf, '.');
+ if (dot != NULL) {
+ size_t len = dot - rndcconf + 1;
+ snprintf(dot + 1, PATH_MAX - len, "conf");
+ }
+
+ /*
+ * Print default configuration paths.
+ */
+ printf("default paths:\n");
+ printf(" named configuration: %s\n", named_g_conffile);
+ printf(" rndc configuration: %s\n", rndcconf);
+ printf(" DNSSEC root key: %s\n", named_g_defaultbindkeys);
+ printf(" nsupdate session key: %s\n", named_g_defaultsessionkeyfile);
+ printf(" named PID file: %s\n", named_g_defaultpidfile);
+ printf(" named lock file: %s\n", named_g_defaultlockfile);
+#if defined(HAVE_GEOIP2)
+#define RTC(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS)
+ RTC(cfg_parser_create(mctx, named_g_lctx, &parser));
+ RTC(named_config_parsedefaults(parser, &config));
+ RTC(cfg_map_get(config, "options", &defaults));
+ RTC(cfg_map_get(defaults, "geoip-directory", &obj));
+ if (cfg_obj_isstring(obj)) {
+ printf(" geoip-directory: %s\n", cfg_obj_asstring(obj));
+ }
+ cfg_obj_destroy(parser, &config);
+ cfg_parser_destroy(&parser);
+ isc_mem_detach(&mctx);
+#endif /* HAVE_GEOIP2 */
+}
+
+static void
+parse_fuzz_arg(void) {
+ if (!strncmp(isc_commandline_argument, "client:", 7)) {
+ named_g_fuzz_addr = isc_commandline_argument + 7;
+ named_g_fuzz_type = isc_fuzz_client;
+ } else if (!strncmp(isc_commandline_argument, "tcp:", 4)) {
+ named_g_fuzz_addr = isc_commandline_argument + 4;
+ named_g_fuzz_type = isc_fuzz_tcpclient;
+ } else if (!strncmp(isc_commandline_argument, "resolver:", 9)) {
+ named_g_fuzz_addr = isc_commandline_argument + 9;
+ named_g_fuzz_type = isc_fuzz_resolver;
+ } else if (!strncmp(isc_commandline_argument, "http:", 5)) {
+ named_g_fuzz_addr = isc_commandline_argument + 5;
+ named_g_fuzz_type = isc_fuzz_http;
+ } else if (!strncmp(isc_commandline_argument, "rndc:", 5)) {
+ named_g_fuzz_addr = isc_commandline_argument + 5;
+ named_g_fuzz_type = isc_fuzz_rndc;
+ } else {
+ named_main_earlyfatal("unknown fuzzing type '%s'",
+ isc_commandline_argument);
+ }
+}
+
+static void
+parse_T_opt(char *option) {
+ const char *p;
+ char *last = NULL;
+ /*
+ * force the server to behave (or misbehave) in
+ * specified ways for testing purposes.
+ */
+ if (!strcmp(option, "dropedns")) {
+ dropedns = true;
+ } else if (!strcmp(option, "ednsformerr")) {
+ ednsformerr = true;
+ } else if (!strcmp(option, "ednsnotimp")) {
+ ednsnotimp = true;
+ } else if (!strcmp(option, "ednsrefused")) {
+ ednsrefused = true;
+ } else if (!strcmp(option, "fixedlocal")) {
+ fixedlocal = true;
+ } else if (!strcmp(option, "keepstderr")) {
+ named_g_keepstderr = true;
+ } else if (!strcmp(option, "noaa")) {
+ noaa = true;
+ } else if (!strcmp(option, "noedns")) {
+ noedns = true;
+ } else if (!strcmp(option, "nonearest")) {
+ nonearest = true;
+ } else if (!strcmp(option, "nosoa")) {
+ nosoa = true;
+ } else if (!strcmp(option, "nosyslog")) {
+ named_g_nosyslog = true;
+ } else if (!strcmp(option, "notcp")) {
+ notcp = true;
+ } else if (!strncmp(option, "maxcachesize=", 13)) {
+ named_g_maxcachesize = atoi(option + 13);
+ } else if (!strcmp(option, "maxudp512")) {
+ maxudp = 512;
+ } else if (!strcmp(option, "maxudp1460")) {
+ maxudp = 1460;
+ } else if (!strncmp(option, "maxudp=", 7)) {
+ maxudp = atoi(option + 7);
+ if (maxudp <= 0) {
+ named_main_earlyfatal("bad maxudp");
+ }
+ } else if (!strncmp(option, "mkeytimers=", 11)) {
+ p = strtok_r(option + 11, "/", &last);
+ if (p == NULL) {
+ named_main_earlyfatal("bad mkeytimer");
+ }
+
+ dns_zone_mkey_hour = atoi(p);
+ if (dns_zone_mkey_hour == 0) {
+ named_main_earlyfatal("bad mkeytimer");
+ }
+
+ p = strtok_r(NULL, "/", &last);
+ if (p == NULL) {
+ dns_zone_mkey_day = (24 * dns_zone_mkey_hour);
+ dns_zone_mkey_month = (30 * dns_zone_mkey_day);
+ return;
+ }
+
+ dns_zone_mkey_day = atoi(p);
+ if (dns_zone_mkey_day < dns_zone_mkey_hour) {
+ named_main_earlyfatal("bad mkeytimer");
+ }
+
+ p = strtok_r(NULL, "/", &last);
+ if (p == NULL) {
+ dns_zone_mkey_month = (30 * dns_zone_mkey_day);
+ return;
+ }
+
+ dns_zone_mkey_month = atoi(p);
+ if (dns_zone_mkey_month < dns_zone_mkey_day) {
+ named_main_earlyfatal("bad mkeytimer");
+ }
+ } else if (!strcmp(option, "sigvalinsecs")) {
+ sigvalinsecs = true;
+ } else if (!strcmp(option, "transferinsecs")) {
+ transferinsecs = true;
+ } else if (!strcmp(option, "transferslowly")) {
+ transferslowly = true;
+ } else if (!strcmp(option, "transferstuck")) {
+ transferstuck = true;
+ } else if (!strncmp(option, "tat=", 4)) {
+ named_g_tat_interval = atoi(option + 4);
+ } else {
+ fprintf(stderr, "unknown -T flag '%s'\n", option);
+ }
+}
+
+static void
+parse_port(char *arg) {
+ enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT;
+ char *value = arg;
+ int port;
+
+ if (strncmp(arg, "dns=", 4) == 0) {
+ value = arg + 4;
+ } else if (strncmp(arg, "tls=", 4) == 0) {
+ value = arg + 4;
+ ptype = TLSPORT;
+ } else if (strncmp(arg, "https=", 6) == 0) {
+ value = arg + 6;
+ ptype = HTTPSPORT;
+ } else if (strncmp(arg, "http=", 5) == 0) {
+ value = arg + 6;
+ ptype = HTTPPORT;
+ }
+
+ port = parse_int(value, "port");
+ if (port < 1 || port > 65535) {
+ named_main_earlyfatal("port '%s' out of range", value);
+ }
+
+ switch (ptype) {
+ case DNSPORT:
+ named_g_port = port;
+ break;
+ case TLSPORT:
+ named_g_tlsport = port;
+ break;
+ case HTTPSPORT:
+ named_g_httpsport = port;
+ break;
+ case HTTPPORT:
+ named_g_httpport = port;
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+static void
+parse_command_line(int argc, char *argv[]) {
+ int ch;
+ const char *p;
+
+ save_command_line(argc, argv);
+
+ /*
+ * NAMED_MAIN_ARGS is defined in main.h, so that it can be used
+ * both by named and by ntservice hooks.
+ */
+ isc_commandline_errprint = false;
+ while ((ch = isc_commandline_parse(argc, argv, NAMED_MAIN_ARGS)) != -1)
+ {
+ switch (ch) {
+ case '4':
+ if (disable4) {
+ named_main_earlyfatal("cannot specify "
+ "-4 and -6");
+ }
+ if (isc_net_probeipv4() != ISC_R_SUCCESS) {
+ named_main_earlyfatal("IPv4 not supported "
+ "by OS");
+ }
+ isc_net_disableipv6();
+ disable6 = true;
+ break;
+ case '6':
+ if (disable6) {
+ named_main_earlyfatal("cannot specify "
+ "-4 and -6");
+ }
+ if (isc_net_probeipv6() != ISC_R_SUCCESS) {
+ named_main_earlyfatal("IPv6 not supported "
+ "by OS");
+ }
+ isc_net_disableipv4();
+ disable4 = true;
+ break;
+ case 'A':
+ parse_fuzz_arg();
+ break;
+ case 'c':
+ named_g_conffile = isc_commandline_argument;
+ named_g_conffileset = true;
+ break;
+ case 'C':
+ printf("# Built-in default values. "
+ "This is NOT the run-time configuration!\n");
+ printf("%s", named_config_getdefault());
+ exit(0);
+ case 'd':
+ named_g_debuglevel = parse_int(isc_commandline_argument,
+ "debug "
+ "level");
+ break;
+ case 'D':
+ /* Descriptive comment for 'ps'. */
+ break;
+ case 'E':
+ named_g_engine = isc_commandline_argument;
+ break;
+ case 'f':
+ named_g_foreground = true;
+ break;
+ case 'g':
+ named_g_foreground = true;
+ named_g_logstderr = true;
+ break;
+ case 'L':
+ named_g_logfile = isc_commandline_argument;
+ break;
+ case 'M':
+ set_flags(isc_commandline_argument, mem_context_flags,
+ &isc_mem_defaultflags);
+ break;
+ case 'm':
+ set_flags(isc_commandline_argument, mem_debug_flags,
+ &isc_mem_debugging);
+ break;
+ case 'N': /* Deprecated. */
+ case 'n':
+ named_g_cpus = parse_int(isc_commandline_argument,
+ "number of cpus");
+ if (named_g_cpus == 0) {
+ named_g_cpus = 1;
+ }
+ break;
+ case 'p':
+ parse_port(isc_commandline_argument);
+ break;
+ case 's':
+ /* XXXRTH temporary syntax */
+ want_stats = true;
+ break;
+ case 'S':
+ /* Formerly maxsocks */
+ break;
+ case 't':
+ /* XXXJAB should we make a copy? */
+ named_g_chrootdir = isc_commandline_argument;
+ break;
+ case 'T': /* NOT DOCUMENTED */
+ parse_T_opt(isc_commandline_argument);
+ break;
+ case 'U':
+ named_g_udpdisp = parse_int(isc_commandline_argument,
+ "number of UDP listeners "
+ "per interface");
+ break;
+ case 'u':
+ named_g_username = isc_commandline_argument;
+ break;
+ case 'v':
+ printversion(false);
+ exit(0);
+ case 'V':
+ printversion(true);
+ exit(0);
+ case 'x':
+ /* Obsolete. No longer in use. Ignore. */
+ break;
+ case 'X':
+ named_g_forcelock = true;
+ if (strcasecmp(isc_commandline_argument, "none") != 0) {
+ named_g_defaultlockfile =
+ isc_commandline_argument;
+ } else {
+ named_g_defaultlockfile = NULL;
+ }
+ break;
+ case 'F':
+ /* Reserved for FIPS mode */
+ FALLTHROUGH;
+ case '?':
+ usage();
+ if (isc_commandline_option == '?') {
+ exit(0);
+ }
+ p = strchr(NAMED_MAIN_ARGS, isc_commandline_option);
+ if (p == NULL || *++p != ':') {
+ named_main_earlyfatal("unknown option '-%c'",
+ isc_commandline_option);
+ } else {
+ named_main_earlyfatal("option '-%c' requires "
+ "an argument",
+ isc_commandline_option);
+ }
+ FALLTHROUGH;
+ default:
+ named_main_earlyfatal("parsing options returned %d",
+ ch);
+ }
+ }
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+ POST(argv);
+
+ if (argc > 0) {
+ usage();
+ named_main_earlyfatal("extra command line arguments");
+ }
+}
+
+static isc_result_t
+create_managers(void) {
+ isc_result_t result;
+
+ INSIST(named_g_cpus_detected > 0);
+
+ if (named_g_cpus == 0) {
+ named_g_cpus = named_g_cpus_detected;
+ }
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s",
+ named_g_cpus_detected, named_g_cpus_detected == 1 ? "" : "s",
+ named_g_cpus, named_g_cpus == 1 ? "" : "s");
+ if (named_g_udpdisp == 0) {
+ named_g_udpdisp = named_g_cpus_detected;
+ }
+ if (named_g_udpdisp > named_g_cpus) {
+ named_g_udpdisp = named_g_cpus;
+ }
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using %u UDP listener%s per interface", named_g_udpdisp,
+ named_g_udpdisp == 1 ? "" : "s");
+
+ result = isc_managers_create(named_g_mctx, named_g_cpus,
+ 0 /* quantum */, &named_g_netmgr,
+ &named_g_taskmgr, &named_g_timermgr);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ isc_nm_maxudp(named_g_netmgr, maxudp);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy_managers(void) {
+ isc_managers_destroy(&named_g_netmgr, &named_g_taskmgr,
+ &named_g_timermgr);
+}
+
+static void
+setup(void) {
+ isc_result_t result;
+ isc_resourcevalue_t old_openfiles;
+ ns_server_t *sctx;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif /* ifdef HAVE_LIBSCF */
+
+ /*
+ * Get the user and group information before changing the root
+ * directory, so the administrator does not need to keep a copy
+ * of the user and group databases in the chroot'ed environment.
+ */
+ named_os_inituserinfo(named_g_username);
+
+ /*
+ * Initialize time conversion information
+ */
+ named_os_tzset();
+
+ named_os_opendevnull();
+
+#ifdef HAVE_LIBSCF
+ /* Check if named is under smf control, before chroot. */
+ result = named_smf_get_instance(&instance, 0, named_g_mctx);
+ /* We don't care about instance, just check if we got one. */
+ if (result == ISC_R_SUCCESS) {
+ named_smf_got_instance = 1;
+ } else {
+ named_smf_got_instance = 0;
+ }
+ if (instance != NULL) {
+ isc_mem_free(named_g_mctx, instance);
+ }
+#endif /* HAVE_LIBSCF */
+
+ /*
+ * Check for the number of cpu's before named_os_chroot().
+ */
+ named_g_cpus_detected = isc_os_ncpus();
+
+ named_os_chroot(named_g_chrootdir);
+
+ /*
+ * For operating systems which have a capability mechanism, now
+ * is the time to switch to minimal privs and change our user id.
+ * On traditional UNIX systems, this call will be a no-op, and we
+ * will change the user ID after reading the config file the first
+ * time. (We need to read the config file to know which possibly
+ * privileged ports to bind() to.)
+ */
+ named_os_minprivs();
+
+ result = named_log_init(named_g_username != NULL);
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("named_log_init() failed: %s",
+ isc_result_totext(result));
+ }
+
+ /*
+ * Now is the time to daemonize (if we're not running in the
+ * foreground). We waited until now because we wanted to get
+ * a valid logging context setup. We cannot daemonize any later,
+ * because calling create_managers() will create threads, which
+ * would be lost after fork().
+ */
+ if (!named_g_foreground) {
+ named_os_daemonize();
+ }
+
+ /*
+ * We call isc_app_start() here as some versions of FreeBSD's fork()
+ * destroys all the signal handling it sets up.
+ */
+ result = isc_app_start();
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("isc_app_start() failed: %s",
+ isc_result_totext(result));
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "starting %s%s <id:%s>", PACKAGE_STRING,
+ PACKAGE_DESCRIPTION, PACKAGE_SRCID);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "running on %s",
+ named_os_uname());
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "built with %s",
+ PACKAGE_CONFIGARGS);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "running as: %s%s%s", program_name, saved_command_line,
+ ellipsis);
+#ifdef __clang__
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by CLANG %s", __VERSION__);
+#else /* ifdef __clang__ */
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by ICC %s", __VERSION__);
+#else /* if defined(__ICC) || defined(__INTEL_COMPILER) */
+#ifdef __GNUC__
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by GCC %s", __VERSION__);
+#endif /* ifdef __GNUC__ */
+#endif /* if defined(__ICC) || defined(__INTEL_COMPILER) */
+#endif /* ifdef __clang__ */
+#ifdef _MSC_VER
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by MSVC %d", _MSC_VER);
+#endif /* ifdef _MSC_VER */
+#ifdef __SUNPRO_C
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by Solaris Studio %x", __SUNPRO_C);
+#endif /* ifdef __SUNPRO_C */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with OpenSSL version: %s",
+ OPENSSL_VERSION_TEXT);
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to OpenSSL version: %s",
+ OpenSSL_version(OPENSSL_VERSION));
+#else /* if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= \
+ * 0x10100000L */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to OpenSSL version: %s",
+ SSLeay_version(SSLEAY_VERSION));
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with libuv version: %d.%d.%d", UV_VERSION_MAJOR,
+ UV_VERSION_MINOR, UV_VERSION_PATCH);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to libuv version: %s", uv_version_string());
+#ifdef HAVE_LIBXML2
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with libxml2 version: %s",
+ LIBXML_DOTTED_VERSION);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to libxml2 version: %s", xmlParserVersion);
+#endif /* ifdef HAVE_LIBXML2 */
+#if defined(HAVE_JSON_C)
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with json-c version: %s", JSON_C_VERSION);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to json-c version: %s", json_c_version());
+#endif /* if defined(HAVE_JSON_C) */
+#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION)
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with zlib version: %s", ZLIB_VERSION);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to zlib version: %s", zlibVersion());
+#endif /* if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "----------------------------------------------------");
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "BIND 9 is maintained by Internet Systems Consortium,");
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "Inc. (ISC), a non-profit 501(c)(3) public-benefit ");
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "corporation. Support and training for BIND 9 are ");
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "available at https://www.isc.org/support");
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "----------------------------------------------------");
+
+ /*
+ * Get the initial resource limits.
+ */
+ RUNTIME_CHECK(isc_resource_getlimit(isc_resource_stacksize,
+ &named_g_initstacksize) ==
+ ISC_R_SUCCESS);
+ RUNTIME_CHECK(isc_resource_getlimit(isc_resource_datasize,
+ &named_g_initdatasize) ==
+ ISC_R_SUCCESS);
+ RUNTIME_CHECK(isc_resource_getlimit(isc_resource_coresize,
+ &named_g_initcoresize) ==
+ ISC_R_SUCCESS);
+ RUNTIME_CHECK(isc_resource_getlimit(isc_resource_openfiles,
+ &named_g_initopenfiles) ==
+ ISC_R_SUCCESS);
+
+ /*
+ * System resources cannot effectively be tuned on some systems.
+ * Raise the limit in such cases for safety.
+ */
+ old_openfiles = named_g_initopenfiles;
+ named_os_adjustnofile();
+ RUNTIME_CHECK(isc_resource_getlimit(isc_resource_openfiles,
+ &named_g_initopenfiles) ==
+ ISC_R_SUCCESS);
+ if (old_openfiles != named_g_initopenfiles) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "adjusted limit on open files from "
+ "%" PRIu64 " to "
+ "%" PRIu64,
+ old_openfiles, named_g_initopenfiles);
+ }
+
+ /*
+ * If the named configuration filename is relative, prepend the current
+ * directory's name before possibly changing to another directory.
+ */
+ if (!isc_file_isabsolute(named_g_conffile)) {
+ result = isc_file_absolutepath(named_g_conffile,
+ absolute_conffile,
+ sizeof(absolute_conffile));
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("could not construct "
+ "absolute path "
+ "of configuration file: %s",
+ isc_result_totext(result));
+ }
+ named_g_conffile = absolute_conffile;
+ }
+
+ /*
+ * Record the server's startup time.
+ */
+ result = isc_time_now(&named_g_boottime);
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("isc_time_now() failed: %s",
+ isc_result_totext(result));
+ }
+
+ result = create_managers();
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("create_managers() failed: %s",
+ isc_result_totext(result));
+ }
+
+ named_builtin_init();
+
+ /*
+ * Add calls to register sdb drivers here.
+ */
+ /* xxdb_init(); */
+
+ /*
+ * Register the DLZ "dlopen" driver.
+ */
+ result = dlz_dlopen_init(named_g_mctx);
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("dlz_dlopen_init() failed: %s",
+ isc_result_totext(result));
+ }
+
+ named_server_create(named_g_mctx, &named_g_server);
+ ENSURE(named_g_server != NULL);
+ sctx = named_g_server->sctx;
+
+ /*
+ * Report supported algorithms now that dst_lib_init() has
+ * been called via named_server_create().
+ */
+ format_supported_algorithms(logit);
+
+ /*
+ * Modify server context according to command line options
+ */
+ if (disable4) {
+ ns_server_setoption(sctx, NS_SERVER_DISABLE4, true);
+ }
+ if (disable6) {
+ ns_server_setoption(sctx, NS_SERVER_DISABLE6, true);
+ }
+ if (dropedns) {
+ ns_server_setoption(sctx, NS_SERVER_DROPEDNS, true);
+ }
+ if (ednsformerr) { /* STD13 server */
+ ns_server_setoption(sctx, NS_SERVER_EDNSFORMERR, true);
+ }
+ if (ednsnotimp) {
+ ns_server_setoption(sctx, NS_SERVER_EDNSNOTIMP, true);
+ }
+ if (ednsrefused) {
+ ns_server_setoption(sctx, NS_SERVER_EDNSREFUSED, true);
+ }
+ if (fixedlocal) {
+ ns_server_setoption(sctx, NS_SERVER_FIXEDLOCAL, true);
+ }
+ if (noaa) {
+ ns_server_setoption(sctx, NS_SERVER_NOAA, true);
+ }
+ if (noedns) {
+ ns_server_setoption(sctx, NS_SERVER_NOEDNS, true);
+ }
+ if (nonearest) {
+ ns_server_setoption(sctx, NS_SERVER_NONEAREST, true);
+ }
+ if (nosoa) {
+ ns_server_setoption(sctx, NS_SERVER_NOSOA, true);
+ }
+ if (notcp) {
+ ns_server_setoption(sctx, NS_SERVER_NOTCP, true);
+ }
+ if (sigvalinsecs) {
+ ns_server_setoption(sctx, NS_SERVER_SIGVALINSECS, true);
+ }
+ if (transferinsecs) {
+ ns_server_setoption(sctx, NS_SERVER_TRANSFERINSECS, true);
+ }
+ if (transferslowly) {
+ ns_server_setoption(sctx, NS_SERVER_TRANSFERSLOWLY, true);
+ }
+ if (transferstuck) {
+ ns_server_setoption(sctx, NS_SERVER_TRANSFERSTUCK, true);
+ }
+}
+
+static void
+cleanup(void) {
+ destroy_managers();
+
+ if (named_g_mapped != NULL) {
+ dns_acl_detach(&named_g_mapped);
+ }
+
+ named_server_destroy(&named_g_server);
+
+ named_builtin_deinit();
+
+ /*
+ * Add calls to unregister sdb drivers here.
+ */
+ /* xxdb_clear(); */
+
+ /*
+ * Unregister "dlopen" DLZ driver.
+ */
+ dlz_dlopen_clear();
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "exiting");
+ named_log_shutdown();
+}
+
+static char *memstats = NULL;
+
+void
+named_main_setmemstats(const char *filename) {
+ /*
+ * Caller has to ensure locking.
+ */
+
+ if (memstats != NULL) {
+ free(memstats);
+ memstats = NULL;
+ }
+
+ if (filename == NULL) {
+ return;
+ }
+
+ memstats = strdup(filename);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * Get FMRI for the named process.
+ */
+isc_result_t
+named_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) {
+ scf_handle_t *h = NULL;
+ int namelen;
+ char *instance;
+
+ REQUIRE(ins_name != NULL && *ins_name == NULL);
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
+ if (debug) {
+ UNEXPECTED_ERROR("scf_handle_create() failed: %s",
+ scf_strerror(scf_error()));
+ }
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_handle_bind(h) == -1) {
+ if (debug) {
+ UNEXPECTED_ERROR("scf_handle_bind() failed: %s",
+ scf_strerror(scf_error()));
+ }
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((namelen = scf_myname(h, NULL, 0)) == -1) {
+ if (debug) {
+ UNEXPECTED_ERROR("scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ }
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) {
+ UNEXPECTED_ERROR("named_smf_get_instance memory "
+ "allocation failed: %s",
+ isc_result_totext(ISC_R_NOMEMORY));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_myname(h, instance, namelen + 1) == -1) {
+ if (debug) {
+ UNEXPECTED_ERROR("scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ }
+ scf_handle_destroy(h);
+ isc_mem_free(mctx, instance);
+ return (ISC_R_FAILURE);
+ }
+
+ scf_handle_destroy(h);
+ *ins_name = instance;
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_LIBSCF */
+
+/* main entry point, possibly hooked */
+
+int
+main(int argc, char *argv[]) {
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif /* ifdef HAVE_LIBSCF */
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+ (void)ProfilerStart(NULL);
+#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */
+
+#ifdef HAVE_LIBXML2
+ xmlInitParser();
+#endif /* HAVE_LIBXML2 */
+
+ /*
+ * Technically, this call is superfluous because on startup of the main
+ * program, the portable "C" locale is selected by default. This
+ * explicit call here is for a reference that the BIND 9 code base is
+ * not locale aware and the locale MUST be set to "C" (or "POSIX") when
+ * calling any BIND 9 library code. If you are calling external
+ * libraries that use locale, such calls must be wrapped into
+ * setlocale(LC_ALL, ""); before the call and setlocale(LC_ALL, "C");
+ * after the call, and no BIND 9 library calls must be made in between.
+ */
+ setlocale(LC_ALL, "C");
+
+ /*
+ * Record version in core image.
+ * strings named.core | grep "named version:"
+ */
+ strlcat(version,
+#if defined(NO_VERSION_DATE) || !defined(__DATE__)
+ "named version: BIND " PACKAGE_VERSION " <" PACKAGE_SRCID ">",
+#else
+ "named version: BIND " PACKAGE_VERSION " <" PACKAGE_SRCID
+ "> (" __DATE__ ")",
+#endif
+ sizeof(version));
+ result = isc_file_progname(*argv, program_name, sizeof(program_name));
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("program name too long");
+ }
+
+ isc_assertion_setcallback(assertion_failed);
+ isc_error_setfatal(library_fatal_error);
+ isc_error_setunexpected(library_unexpected_error);
+
+ named_os_init(program_name);
+
+ parse_command_line(argc, argv);
+
+#ifdef ENABLE_AFL
+ if (named_g_fuzz_type != isc_fuzz_none) {
+ named_fuzz_setup();
+ }
+
+ if (named_g_fuzz_type == isc_fuzz_resolver) {
+ dns_resolver_setfuzzing();
+ } else if (named_g_fuzz_type == isc_fuzz_http) {
+ isc_httpd_setfinishhook(named_fuzz_notify);
+ }
+#endif /* ifdef ENABLE_AFL */
+ /*
+ * Warn about common configuration error.
+ */
+ if (named_g_chrootdir != NULL) {
+ int len = strlen(named_g_chrootdir);
+ if (strncmp(named_g_chrootdir, named_g_conffile, len) == 0 &&
+ (named_g_conffile[len] == '/' ||
+ named_g_conffile[len] == '\\'))
+ {
+ named_main_earlywarning("config filename (-c %s) "
+ "contains chroot path (-t %s)",
+ named_g_conffile,
+ named_g_chrootdir);
+ }
+ }
+
+ isc_mem_create(&named_g_mctx);
+ isc_mem_setname(named_g_mctx, "main");
+
+ setup();
+ INSIST(named_g_server != NULL);
+
+ /*
+ * Start things running and then wait for a shutdown request
+ * or reload.
+ */
+ do {
+ result = isc_app_run();
+
+ if (result == ISC_R_RELOAD) {
+ named_server_reloadwanted(named_g_server);
+ } else if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR("isc_app_run(): %s",
+ isc_result_totext(result));
+ /*
+ * Force exit.
+ */
+ result = ISC_R_SUCCESS;
+ }
+ } while (result != ISC_R_SUCCESS);
+
+#ifdef HAVE_LIBSCF
+ if (named_smf_want_disable == 1) {
+ result = named_smf_get_instance(&instance, 1, named_g_mctx);
+ if (result == ISC_R_SUCCESS && instance != NULL) {
+ if (smf_disable_instance(instance, 0) != 0) {
+ UNEXPECTED_ERROR("smf_disable_instance() "
+ "failed for %s : %s",
+ instance,
+ scf_strerror(scf_error()));
+ }
+ }
+ if (instance != NULL) {
+ isc_mem_free(named_g_mctx, instance);
+ }
+ }
+#endif /* HAVE_LIBSCF */
+
+ cleanup();
+
+ if (want_stats) {
+ isc_mem_stats(named_g_mctx, stdout);
+ }
+
+ if (named_g_memstatistics && memstats != NULL) {
+ FILE *fp = NULL;
+ result = isc_stdio_open(memstats, "w", &fp);
+ if (result == ISC_R_SUCCESS) {
+ isc_mem_stats(named_g_mctx, fp);
+ (void)isc_stdio_close(fp);
+ }
+ }
+ isc_mem_destroy(&named_g_mctx);
+ isc_mem_checkdestroyed(stderr);
+
+ named_main_setmemstats(NULL);
+
+ isc_app_finish();
+
+ named_os_closedevnull();
+
+ named_os_shutdown();
+
+#ifdef HAVE_LIBXML2
+ xmlCleanupParser();
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+ ProfilerStop();
+#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */
+
+ return (0);
+}
diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst
new file mode 100644
index 0000000..8e93f8b
--- /dev/null
+++ b/bin/named/named.conf.rst
@@ -0,0 +1,67 @@
+.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+..
+.. SPDX-License-Identifier: MPL-2.0
+..
+.. This Source Code Form is subject to the terms of the Mozilla Public
+.. License, v. 2.0. If a copy of the MPL was not distributed with this
+.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
+..
+.. See the COPYRIGHT file distributed with this work for additional
+.. information regarding copyright ownership.
+
+.. highlight: console
+
+.. iscman:: named.conf
+
+named.conf - configuration file for **named**
+---------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`named.conf`
+
+Description
+~~~~~~~~~~~
+
+:file:`named.conf` is the configuration file for :iscman:`named`.
+
+For complete documentation about the configuration statements, please refer to
+the Configuration Reference section in the BIND 9 Administrator Reference
+Manual.
+
+Statements are enclosed in braces and terminated with a semi-colon.
+Clauses in the statements are also semi-colon terminated. The usual
+comment styles are supported:
+
+C style: /\* \*/
+
+C++ style: // to end of line
+
+Unix style: # to end of line
+
+.. literalinclude:: ../../doc/misc/options
+
+Any of these zone statements can also be set inside the view statement.
+
+.. literalinclude:: ../../doc/misc/primary.zoneopt
+.. literalinclude:: ../../doc/misc/secondary.zoneopt
+.. literalinclude:: ../../doc/misc/mirror.zoneopt
+.. literalinclude:: ../../doc/misc/forward.zoneopt
+.. literalinclude:: ../../doc/misc/hint.zoneopt
+.. literalinclude:: ../../doc/misc/redirect.zoneopt
+.. literalinclude:: ../../doc/misc/static-stub.zoneopt
+.. literalinclude:: ../../doc/misc/stub.zoneopt
+.. literalinclude:: ../../doc/misc/delegation-only.zoneopt
+.. literalinclude:: ../../doc/misc/in-view.zoneopt
+
+Files
+~~~~~
+
+|named_conf|
+
+See Also
+~~~~~~~~
+
+:iscman:`named(8) <named>`, :iscman:`named-checkconf(8) <named-checkconf>`, :iscman:`rndc(8) <rndc>`, :iscman:`rndc-confgen(8) <rndc-confgen>`, :iscman:`tsig-keygen(8) <tsig-keygen>`, BIND 9 Administrator Reference Manual.
+
diff --git a/bin/named/named.rst b/bin/named/named.rst
new file mode 100644
index 0000000..dc6e46d
--- /dev/null
+++ b/bin/named/named.rst
@@ -0,0 +1,254 @@
+.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+..
+.. SPDX-License-Identifier: MPL-2.0
+..
+.. This Source Code Form is subject to the terms of the Mozilla Public
+.. License, v. 2.0. If a copy of the MPL was not distributed with this
+.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
+..
+.. See the COPYRIGHT file distributed with this work for additional
+.. information regarding copyright ownership.
+
+.. highlight: console
+
+.. iscman:: named
+.. program:: named
+.. _man_named:
+
+named - Internet domain name server
+-----------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`named` [ [**-4**] | [**-6**] ] [**-c** config-file] [**-C**] [**-d** debug-level] [**-D** string] [**-E** engine-name] [**-f**] [**-g**] [**-L** logfile] [**-M** option] [**-m** flag] [**-n** #cpus] [**-p** port] [**-s**] [**-t** directory] [**-U** #listeners] [**-u** user] [**-v**] [**-V**] [**-X** lock-file]
+
+Description
+~~~~~~~~~~~
+
+:program:`named` is a Domain Name System (DNS) server, part of the BIND 9
+distribution from ISC. For more information on the DNS, see :rfc:`1033`,
+:rfc:`1034`, and :rfc:`1035`.
+
+When invoked without arguments, :program:`named` reads the default
+configuration file |named_conf|, reads any initial data, and
+listens for queries.
+
+Options
+~~~~~~~
+
+.. option:: -4
+
+ This option tells :program:`named` to use only IPv4, even if the host machine is capable of IPv6. :option:`-4` and
+ :option:`-6` are mutually exclusive.
+
+.. option:: -6
+
+ This option tells :program:`named` to use only IPv6, even if the host machine is capable of IPv4. :option:`-4` and
+ :option:`-6` are mutually exclusive.
+
+.. option:: -c config-file
+
+ This option tells :program:`named` to use ``config-file`` as its configuration file instead of the default,
+ |named_conf|. To ensure that the configuration file
+ can be reloaded after the server has changed its working directory
+ due to to a possible ``directory`` option in the configuration file,
+ ``config-file`` should be an absolute pathname.
+
+.. option:: -C
+
+ This option prints out the default built-in configuration and exits.
+
+ NOTE: This is for debugging purposes only and is not an
+ accurate representation of the actual configuration used by :iscman:`named`
+ at runtime.
+
+.. option:: -d debug-level
+
+ This option sets the daemon's debug level to ``debug-level``. Debugging traces from
+ :program:`named` become more verbose as the debug level increases.
+
+.. option:: -D string
+
+ This option specifies a string that is used to identify a instance of :program:`named`
+ in a process listing. The contents of ``string`` are not examined.
+
+.. option:: -E engine-name
+
+ When applicable, this option specifies the hardware to use for cryptographic
+ operations, such as a secure key store used for signing.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``).
+
+.. option:: -f
+
+ This option runs the server in the foreground (i.e., do not daemonize).
+
+.. option:: -g
+
+ This option runs the server in the foreground and forces all logging to ``stderr``.
+
+.. option:: -L logfile
+
+ This option sets the log to the file ``logfile`` by default, instead of the system log.
+
+.. option:: -M option
+
+ This option sets the default (comma-separated) memory context
+ options. The possible flags are:
+
+ - ``fill``: fill blocks of memory with tag values when they are
+ allocated or freed, to assist debugging of memory problems; this is
+ the implicit default if :program:`named` has been compiled with
+ ``--enable-developer``.
+
+ - ``nofill``: disable the behavior enabled by ``fill``; this is the
+ implicit default unless :program:`named` has been compiled with
+ ``--enable-developer``.
+
+.. option:: -m flag
+
+ This option turns on memory usage debugging flags. Possible flags are ``usage``,
+ ``trace``, ``record``, ``size``, and ``mctx``. These correspond to the
+ ``ISC_MEM_DEBUGXXXX`` flags described in ``<isc/mem.h>``.
+
+.. option:: -n #cpus
+
+ This option creates ``#cpus`` worker threads to take advantage of multiple CPUs. If
+ not specified, :program:`named` tries to determine the number of CPUs
+ present and creates one thread per CPU. If it is unable to determine
+ the number of CPUs, a single worker thread is created.
+
+.. option:: -p value
+
+ This option specifies the port(s) on which the server will listen
+ for queries. If ``value`` is of the form ``<portnum>`` or
+ ``dns=<portnum>``, the server will listen for DNS queries on
+ ``portnum``; if not not specified, the default is port 53. If
+ ``value`` is of the form ``tls=<portnum>``, the server will
+ listen for TLS queries on ``portnum``; the default is 853.
+ If ``value`` is of the form ``https=<portnum>``, the server will
+ listen for HTTPS queries on ``portnum``; the default is 443.
+ If ``value`` is of the form ``http=<portnum>``, the server will
+ listen for HTTP queries on ``portnum``; the default is 80.
+
+.. option:: -s
+
+ This option writes memory usage statistics to ``stdout`` on exit.
+
+.. note::
+
+ This option is mainly of interest to BIND 9 developers and may be
+ removed or changed in a future release.
+
+.. option:: -S #max-socks
+
+ This option is deprecated and no longer has any function.
+
+.. warning::
+
+ This option should be unnecessary for the vast majority of users.
+ The use of this option could even be harmful, because the specified
+ value may exceed the limitation of the underlying system API. It
+ is therefore set only when the default configuration causes
+ exhaustion of file descriptors and the operational environment is
+ known to support the specified number of sockets. Note also that
+ the actual maximum number is normally slightly fewer than the
+ specified value, because :program:`named` reserves some file descriptors
+ for its internal use.
+
+.. option:: -t directory
+
+ This option tells :program:`named` to chroot to ``directory`` after processing the command-line arguments, but
+ before reading the configuration file.
+
+.. warning::
+
+ This option should be used in conjunction with the :option:`-u` option,
+ as chrooting a process running as root doesn't enhance security on
+ most systems; the way ``chroot`` is defined allows a process
+ with root privileges to escape a chroot jail.
+
+.. option:: -U #listeners
+
+ This option tells :program:`named` the number of ``#listeners`` worker threads to listen on, for incoming UDP packets on
+ each address. If not specified, :program:`named` calculates a default
+ value based on the number of detected CPUs: 1 for 1 CPU, and the
+ number of detected CPUs minus one for machines with more than 1 CPU.
+ This cannot be increased to a value higher than the number of CPUs.
+ If :option:`-n` has been set to a higher value than the number of detected
+ CPUs, then :option:`-U` may be increased as high as that value, but no
+ higher.
+
+.. option:: -u user
+
+ This option sets the setuid to ``user`` after completing privileged operations, such as
+ creating sockets that listen on privileged ports.
+
+.. note::
+
+ On Linux, :program:`named` uses the kernel's capability mechanism to drop
+ all root privileges except the ability to ``bind`` to a
+ privileged port and set process resource limits. Unfortunately,
+ this means that the :option:`-u` option only works when :program:`named` is run
+ on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or later, since
+ previous kernels did not allow privileges to be retained after
+ ``setuid``.
+
+.. option:: -v
+
+ This option reports the version number and exits.
+
+.. option:: -V
+
+ This option reports the version number, build options, supported
+ cryptographics algorithms, and exits.
+
+.. option:: -X lock-file
+
+ This option acquires a lock on the specified file at runtime; this helps to
+ prevent duplicate :program:`named` instances from running simultaneously.
+ Use of this option overrides the ``lock-file`` option in
+ :iscman:`named.conf`. If set to ``none``, the lock file check is disabled.
+
+Signals
+~~~~~~~
+
+In routine operation, signals should not be used to control the
+nameserver; :iscman:`rndc` should be used instead.
+
+SIGHUP
+ This signal forces a reload of the server.
+
+SIGINT, SIGTERM
+ These signals shut down the server.
+
+The result of sending any other signals to the server is undefined.
+
+Configuration
+~~~~~~~~~~~~~
+
+The :program:`named` configuration file is too complex to describe in detail
+here. A complete description is provided in the BIND 9 Administrator
+Reference Manual.
+
+:program:`named` inherits the ``umask`` (file creation mode mask) from the
+parent process. If files created by :program:`named`, such as journal files,
+need to have custom permissions, the ``umask`` should be set explicitly
+in the script used to start the :program:`named` process.
+
+Files
+~~~~~
+
+|named_conf|
+ The default configuration file.
+
+|named_pid|
+ The default process-id file.
+
+See Also
+~~~~~~~~
+
+:rfc:`1033`, :rfc:`1034`, :rfc:`1035`, :iscman:`named-checkconf(8) <named-checkconf>`, :iscman:`named-checkzone(8) <named-checkzone>`, :iscman:`rndc(8) <rndc>`, :iscman:`named.conf(5) <named.conf>`, BIND 9 Administrator Reference Manual.
diff --git a/bin/named/os.c b/bin/named/os.c
new file mode 100644
index 0000000..7af4729
--- /dev/null
+++ b/bin/named/os.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif /* ifdef HAVE_UNAME */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#ifdef HAVE_TZSET
+#include <time.h>
+#endif /* ifdef HAVE_TZSET */
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/result.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+#include <named/main.h>
+#include <named/os.h>
+#ifdef HAVE_LIBSCF
+#include <named/smf_globals.h>
+#endif /* ifdef HAVE_LIBSCF */
+
+static char *pidfile = NULL;
+static char *lockfile = NULL;
+static int devnullfd = -1;
+static int singletonfd = -1;
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif /* ifndef ISC_FACILITY */
+
+static struct passwd *runas_pw = NULL;
+static bool done_setuid = false;
+static int dfd[2] = { -1, -1 };
+
+#ifdef HAVE_SYS_CAPABILITY_H
+
+static bool non_root = false;
+static bool non_root_caps = false;
+
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+static void
+linux_setcaps(cap_t caps) {
+ char strbuf[ISC_STRERRORSIZE];
+
+ if ((getuid() != 0 && !non_root_caps) || non_root) {
+ return;
+ }
+ if (cap_set_proc(caps) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("cap_set_proc() failed: %s:"
+ " please ensure that the capset kernel"
+ " module is loaded. see insmod(8)",
+ strbuf);
+ }
+}
+
+#define SET_CAP(flag) \
+ do { \
+ cap_flag_value_t curval; \
+ capval = (flag); \
+ err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
+ if (err != -1 && curval) { \
+ err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, \
+ CAP_SET); \
+ if (err == -1) { \
+ strerror_r(errno, strbuf, sizeof(strbuf)); \
+ named_main_earlyfatal("cap_set_proc failed: " \
+ "%s", \
+ strbuf); \
+ } \
+ \
+ err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, \
+ CAP_SET); \
+ if (err == -1) { \
+ strerror_r(errno, strbuf, sizeof(strbuf)); \
+ named_main_earlyfatal("cap_set_proc failed: " \
+ "%s", \
+ strbuf); \
+ } \
+ } \
+ } while (0)
+#define INIT_CAP \
+ do { \
+ caps = cap_init(); \
+ if (caps == NULL) { \
+ strerror_r(errno, strbuf, sizeof(strbuf)); \
+ named_main_earlyfatal("cap_init failed: %s", strbuf); \
+ } \
+ curcaps = cap_get_proc(); \
+ if (curcaps == NULL) { \
+ strerror_r(errno, strbuf, sizeof(strbuf)); \
+ named_main_earlyfatal("cap_get_proc failed: %s", \
+ strbuf); \
+ } \
+ } while (0)
+#define FREE_CAP \
+ { \
+ cap_free(caps); \
+ cap_free(curcaps); \
+ } \
+ while (0)
+
+static void
+linux_initialprivs(void) {
+ cap_t caps;
+ cap_t curcaps;
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+
+ /*%
+ * We don't need most privileges, so we drop them right away.
+ * Later on linux_minprivs() will be called, which will drop our
+ * capabilities to the minimum needed to run the server.
+ */
+ INIT_CAP;
+
+ /*
+ * We need to be able to bind() to privileged ports, notably port 53!
+ */
+ SET_CAP(CAP_NET_BIND_SERVICE);
+
+ /*
+ * We need chroot() initially too.
+ */
+ SET_CAP(CAP_SYS_CHROOT);
+
+ /*
+ * We need setuid() as the kernel supports keeping capabilities after
+ * setuid().
+ */
+ SET_CAP(CAP_SETUID);
+
+ /*
+ * Since we call initgroups, we need this.
+ */
+ SET_CAP(CAP_SETGID);
+
+ /*
+ * Without this, we run into problems reading a configuration file
+ * owned by a non-root user and non-world-readable on startup.
+ */
+ SET_CAP(CAP_DAC_READ_SEARCH);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ SET_CAP(CAP_SYS_RESOURCE);
+
+ /*
+ * We need to be able to set the ownership of the containing
+ * directory of the pid file when we create it.
+ */
+ SET_CAP(CAP_CHOWN);
+
+ linux_setcaps(caps);
+
+ FREE_CAP;
+}
+
+static void
+linux_minprivs(void) {
+ cap_t caps;
+ cap_t curcaps;
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+
+ INIT_CAP;
+ /*%
+ * Drop all privileges except the ability to bind() to privileged
+ * ports.
+ *
+ * It's important that we drop CAP_SYS_CHROOT. If we didn't, it
+ * chroot() could be used to escape from the chrooted area.
+ */
+
+ SET_CAP(CAP_NET_BIND_SERVICE);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ SET_CAP(CAP_SYS_RESOURCE);
+
+ linux_setcaps(caps);
+
+ FREE_CAP;
+}
+
+static void
+linux_keepcaps(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ /*%
+ * Ask the kernel to allow us to keep our capabilities after we
+ * setuid().
+ */
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ if (errno != EINVAL) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("prctl() failed: %s", strbuf);
+ }
+ } else {
+ non_root_caps = true;
+ if (getuid() != 0) {
+ non_root = true;
+ }
+ }
+}
+
+#endif /* HAVE_SYS_CAPABILITY_H */
+
+static void
+setup_syslog(const char *progname) {
+ int options;
+
+ options = LOG_PID;
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif /* ifdef LOG_NDELAY */
+ openlog(isc_file_basename(progname), options, ISC_FACILITY);
+}
+
+void
+named_os_init(const char *progname) {
+ setup_syslog(progname);
+#ifdef HAVE_SYS_CAPABILITY_H
+ linux_initialprivs();
+#endif /* ifdef HAVE_SYS_CAPABILITY_H */
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_IGN);
+#endif /* ifdef SIGXFSZ */
+}
+
+void
+named_os_daemonize(void) {
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+
+ if (pipe(dfd) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("pipe(): %s", strbuf);
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("fork(): %s", strbuf);
+ }
+ if (pid != 0) {
+ int n;
+ /*
+ * Wait for the child to finish loading for the first time.
+ * This would be so much simpler if fork() worked once we
+ * were multi-threaded.
+ */
+ (void)close(dfd[1]);
+ do {
+ char buf;
+ n = read(dfd[0], &buf, 1);
+ if (n == 1) {
+ _exit(0);
+ }
+ } while (n == -1 && errno == EINTR);
+ _exit(1);
+ }
+ (void)close(dfd[0]);
+
+ /*
+ * We're the child.
+ */
+
+ if (setsid() == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("setsid(): %s", strbuf);
+ }
+
+ /*
+ * Try to set stdin, stdout, and stderr to /dev/null, but press
+ * on even if it fails.
+ *
+ * XXXMLG The close() calls here are unneeded on all but NetBSD, but
+ * are harmless to include everywhere. dup2() is supposed to close
+ * the FD if it is in use, but unproven-pthreads-0.16 is broken
+ * and will end up closing the wrong FD. This will be fixed eventually,
+ * and these calls will be removed.
+ */
+ if (devnullfd != -1) {
+ if (devnullfd != STDIN_FILENO) {
+ (void)close(STDIN_FILENO);
+ (void)dup2(devnullfd, STDIN_FILENO);
+ }
+ if (devnullfd != STDOUT_FILENO) {
+ (void)close(STDOUT_FILENO);
+ (void)dup2(devnullfd, STDOUT_FILENO);
+ }
+ if (devnullfd != STDERR_FILENO && !named_g_keepstderr) {
+ (void)close(STDERR_FILENO);
+ (void)dup2(devnullfd, STDERR_FILENO);
+ }
+ }
+}
+
+void
+named_os_started(void) {
+ char buf = 0;
+
+ /*
+ * Signal to the parent that we started successfully.
+ */
+ if (dfd[0] != -1 && dfd[1] != -1) {
+ if (write(dfd[1], &buf, 1) != 1) {
+ named_main_earlyfatal("unable to signal parent that we "
+ "otherwise started "
+ "successfully.");
+ }
+ close(dfd[1]);
+ dfd[0] = dfd[1] = -1;
+ }
+}
+
+void
+named_os_opendevnull(void) {
+ devnullfd = open("/dev/null", O_RDWR, 0);
+}
+
+void
+named_os_closedevnull(void) {
+ if (devnullfd != STDIN_FILENO && devnullfd != STDOUT_FILENO &&
+ devnullfd != STDERR_FILENO)
+ {
+ close(devnullfd);
+ devnullfd = -1;
+ }
+}
+
+static bool
+all_digits(const char *s) {
+ if (*s == '\0') {
+ return (false);
+ }
+ while (*s != '\0') {
+ if (!isdigit((unsigned char)(*s))) {
+ return (false);
+ }
+ s++;
+ }
+ return (true);
+}
+
+void
+named_os_chroot(const char *root) {
+ char strbuf[ISC_STRERRORSIZE];
+#ifdef HAVE_LIBSCF
+ named_smf_chroot = 0;
+#endif /* ifdef HAVE_LIBSCF */
+ if (root != NULL) {
+#ifdef HAVE_CHROOT
+ if (chroot(root) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("chroot(): %s", strbuf);
+ }
+#else /* ifdef HAVE_CHROOT */
+ named_main_earlyfatal("chroot(): disabled");
+#endif /* ifdef HAVE_CHROOT */
+ if (chdir("/") < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("chdir(/): %s", strbuf);
+ }
+#ifdef HAVE_LIBSCF
+ /* Set named_smf_chroot flag on successful chroot. */
+ named_smf_chroot = 1;
+#endif /* ifdef HAVE_LIBSCF */
+ }
+}
+
+void
+named_os_inituserinfo(const char *username) {
+ if (username == NULL) {
+ return;
+ }
+
+ if (all_digits(username)) {
+ runas_pw = getpwuid((uid_t)atoi(username));
+ } else {
+ runas_pw = getpwnam(username);
+ }
+ endpwent();
+
+ if (runas_pw == NULL) {
+ named_main_earlyfatal("user '%s' unknown", username);
+ }
+
+ if (getuid() == 0) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("initgroups(): %s", strbuf);
+ }
+ }
+}
+
+void
+named_os_changeuser(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (runas_pw == NULL || done_setuid) {
+ return;
+ }
+
+ done_setuid = true;
+
+ if (setgid(runas_pw->pw_gid) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("setgid(): %s", strbuf);
+ }
+
+ if (setuid(runas_pw->pw_uid) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("setuid(): %s", strbuf);
+ }
+
+#if defined(HAVE_SYS_CAPABILITY_H)
+ /*
+ * Restore the ability of named to drop core after the setuid()
+ * call has disabled it.
+ */
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
+ strbuf);
+ }
+
+ linux_minprivs();
+#endif /* if defined(HAVE_SYS_CAPABILITY_H) */
+}
+
+uid_t
+ns_os_uid(void) {
+ if (runas_pw == NULL) {
+ return (0);
+ }
+ return (runas_pw->pw_uid);
+}
+
+void
+named_os_adjustnofile(void) {
+#if defined(__linux__) || defined(__sun)
+ isc_result_t result;
+ isc_resourcevalue_t newvalue;
+
+ /*
+ * Linux: max number of open files specified by one thread doesn't seem
+ * to apply to other threads on Linux.
+ * Sun: restriction needs to be removed sooner when hundreds of CPUs
+ * are available.
+ */
+ newvalue = ISC_RESOURCE_UNLIMITED;
+
+ result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlywarning("couldn't adjust limit on open files");
+ }
+#endif /* if defined(__linux__) || defined(__sun) */
+}
+
+void
+named_os_minprivs(void) {
+#if defined(HAVE_SYS_CAPABILITY_H)
+ linux_keepcaps();
+ named_os_changeuser();
+ linux_minprivs();
+#endif /* if defined(HAVE_SYS_CAPABILITY_H) */
+}
+
+static int
+safe_open(const char *filename, mode_t mode, bool append) {
+ int fd;
+ struct stat sb;
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT) {
+ return (-1);
+ }
+ } else if ((sb.st_mode & S_IFREG) == 0) {
+ errno = EOPNOTSUPP;
+ return (-1);
+ }
+
+ if (append) {
+ fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode);
+ } else {
+ if (unlink(filename) < 0 && errno != ENOENT) {
+ return (-1);
+ }
+ fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ int n;
+ if (pidfile != NULL) {
+ n = unlink(pidfile);
+ if (n == -1 && errno != ENOENT) {
+ named_main_earlywarning("unlink '%s': failed", pidfile);
+ }
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+static void
+cleanup_lockfile(void) {
+ if (singletonfd != -1) {
+ close(singletonfd);
+ singletonfd = -1;
+ }
+
+ if (lockfile != NULL) {
+ int n = unlink(lockfile);
+ if (n == -1 && errno != ENOENT) {
+ named_main_earlywarning("unlink '%s': failed",
+ lockfile);
+ }
+ free(lockfile);
+ lockfile = NULL;
+ }
+}
+
+/*
+ * Ensure that a directory exists.
+ * NOTE: This function overwrites the '/' characters in 'filename' with
+ * nulls. The caller should copy the filename to a fresh buffer first.
+ */
+static int
+mkdirpath(char *filename, void (*report)(const char *, ...)) {
+ char *slash = strrchr(filename, '/');
+ char strbuf[ISC_STRERRORSIZE];
+ unsigned int mode;
+
+ if (slash != NULL && slash != filename) {
+ struct stat sb;
+ *slash = '\0';
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't stat '%s': %s", filename,
+ strbuf);
+ goto error;
+ }
+ if (mkdirpath(filename, report) == -1) {
+ goto error;
+ }
+ /*
+ * Handle "//", "/./" and "/../" in path.
+ */
+ if (!strcmp(slash + 1, "") || !strcmp(slash + 1, ".") ||
+ !strcmp(slash + 1, ".."))
+ {
+ *slash = '/';
+ return (0);
+ }
+ mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
+ mode |= S_IRGRP | S_IXGRP; /* g=rx */
+ mode |= S_IROTH | S_IXOTH; /* o=rx */
+ if (mkdir(filename, mode) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't mkdir '%s': %s", filename,
+ strbuf);
+ goto error;
+ }
+ if (runas_pw != NULL &&
+ chown(filename, runas_pw->pw_uid,
+ runas_pw->pw_gid) == -1)
+ {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't chown '%s': %s", filename,
+ strbuf);
+ }
+ }
+ *slash = '/';
+ }
+ return (0);
+
+error:
+ *slash = '/';
+ return (-1);
+}
+
+#if !HAVE_SYS_CAPABILITY_H
+static void
+setperms(uid_t uid, gid_t gid) {
+#if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID)
+ char strbuf[ISC_STRERRORSIZE];
+#endif /* if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID) */
+#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
+ gid_t oldgid, tmpg;
+#endif /* if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */
+#if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
+ uid_t olduid, tmpu;
+#endif /* if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) */
+#if defined(HAVE_SETEGID)
+ if (getegid() != gid && setegid(gid) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("unable to set effective "
+ "gid to %ld: %s",
+ (long)gid, strbuf);
+ }
+#elif defined(HAVE_SETRESGID)
+ if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
+ if (setresgid(-1, gid, -1) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("unable to set effective "
+ "gid to %d: %s",
+ gid, strbuf);
+ }
+ }
+#endif /* if defined(HAVE_SETEGID) */
+
+#if defined(HAVE_SETEUID)
+ if (geteuid() != uid && seteuid(uid) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("unable to set effective "
+ "uid to %ld: %s",
+ (long)uid, strbuf);
+ }
+#elif defined(HAVE_SETRESUID)
+ if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
+ if (setresuid(-1, uid, -1) == -1) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("unable to set effective "
+ "uid to %d: %s",
+ uid, strbuf);
+ }
+ }
+#endif /* if defined(HAVE_SETEUID) */
+}
+#endif /* !HAVE_SYS_CAPABILITY_H */
+
+FILE *
+named_os_openfile(const char *filename, mode_t mode, bool switch_user) {
+ char strbuf[ISC_STRERRORSIZE], *f;
+ FILE *fp;
+ int fd;
+
+ /*
+ * Make the containing directory if it doesn't exist.
+ */
+ f = strdup(filename);
+ if (f == NULL) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("couldn't strdup() '%s': %s", filename,
+ strbuf);
+ return (NULL);
+ }
+ if (mkdirpath(f, named_main_earlywarning) == -1) {
+ free(f);
+ return (NULL);
+ }
+ free(f);
+
+ if (switch_user && runas_pw != NULL) {
+ uid_t olduid = getuid();
+ gid_t oldgid = getgid();
+#if HAVE_SYS_CAPABILITY_H
+ REQUIRE(olduid == runas_pw->pw_uid);
+ REQUIRE(oldgid == runas_pw->pw_gid);
+#else /* HAVE_SYS_CAPABILITY_H */
+ /* Set UID/GID to the one we'll be running with eventually */
+ setperms(runas_pw->pw_uid, runas_pw->pw_gid);
+#endif
+ fd = safe_open(filename, mode, false);
+
+#if !HAVE_SYS_CAPABILITY_H
+ /* Restore UID/GID to previous uid/gid */
+ setperms(olduid, oldgid);
+#endif
+
+ if (fd == -1) {
+ fd = safe_open(filename, mode, false);
+ if (fd != -1) {
+ named_main_earlywarning("Required root "
+ "permissions to open "
+ "'%s'.",
+ filename);
+ } else {
+ named_main_earlywarning("Could not open "
+ "'%s'.",
+ filename);
+ }
+ named_main_earlywarning("Please check file and "
+ "directory permissions "
+ "or reconfigure the filename.");
+ }
+ } else {
+ fd = safe_open(filename, mode, false);
+ }
+
+ if (fd < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("could not open file '%s': %s",
+ filename, strbuf);
+ return (NULL);
+ }
+
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlywarning("could not fdopen() file '%s': %s",
+ filename, strbuf);
+ }
+
+ return (fp);
+}
+
+void
+named_os_writepidfile(const char *filename, bool first_time) {
+ FILE *fh;
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+ void (*report)(const char *, ...);
+
+ /*
+ * The caller must ensure any required synchronization.
+ */
+
+ report = first_time ? named_main_earlyfatal : named_main_earlywarning;
+
+ cleanup_pidfile();
+
+ if (filename == NULL) {
+ return;
+ }
+
+ pidfile = strdup(filename);
+ if (pidfile == NULL) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't strdup() '%s': %s", filename, strbuf);
+ return;
+ }
+
+ fh = named_os_openfile(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
+ first_time);
+ if (fh == NULL) {
+ cleanup_pidfile();
+ return;
+ }
+ pid = getpid();
+ if (fprintf(fh, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(fh);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(fh) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(fh);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(fh);
+}
+
+bool
+named_os_issingleton(const char *filename) {
+ char strbuf[ISC_STRERRORSIZE];
+ struct flock lock;
+
+ if (singletonfd != -1) {
+ return (true);
+ }
+
+ if (strcasecmp(filename, "none") == 0) {
+ return (true);
+ }
+
+ /*
+ * Make the containing directory if it doesn't exist.
+ */
+ lockfile = strdup(filename);
+ if (lockfile == NULL) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ named_main_earlyfatal("couldn't allocate memory for '%s': %s",
+ filename, strbuf);
+ } else {
+ int ret = mkdirpath(lockfile, named_main_earlywarning);
+ if (ret == -1) {
+ named_main_earlywarning("couldn't create '%s'",
+ filename);
+ cleanup_lockfile();
+ return (false);
+ }
+ }
+
+ /*
+ * named_os_openfile() uses safeopen() which removes any existing
+ * files. We can't use that here.
+ */
+ singletonfd = open(filename, O_WRONLY | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (singletonfd == -1) {
+ cleanup_lockfile();
+ return (false);
+ }
+
+ memset(&lock, 0, sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+
+ /* Non-blocking (does not wait for lock) */
+ if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
+ close(singletonfd);
+ singletonfd = -1;
+ return (false);
+ }
+
+ return (true);
+}
+
+void
+named_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+ cleanup_lockfile();
+}
+
+void
+named_os_shutdownmsg(char *command, isc_buffer_t *text) {
+ char *last, *ptr;
+ pid_t pid;
+
+ /* Skip the command name. */
+ if (strtok_r(command, " \t", &last) == NULL) {
+ return;
+ }
+
+ if ((ptr = strtok_r(NULL, " \t", &last)) == NULL) {
+ return;
+ }
+
+ if (strcmp(ptr, "-p") != 0) {
+ return;
+ }
+
+ pid = getpid();
+
+ (void)isc_buffer_printf(text, "pid: %ld", (long)pid);
+}
+
+void
+named_os_tzset(void) {
+#ifdef HAVE_TZSET
+ tzset();
+#endif /* ifdef HAVE_TZSET */
+}
+
+#ifdef HAVE_UNAME
+static char unamebuf[sizeof(struct utsname)];
+#else
+static const char unamebuf[] = { "unknown architecture" };
+#endif
+static const char *unamep = NULL;
+
+static void
+getuname(void) {
+#ifdef HAVE_UNAME
+ struct utsname uts;
+
+ memset(&uts, 0, sizeof(uts));
+ if (uname(&uts) < 0) {
+ snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
+ return;
+ }
+
+ snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname,
+ uts.machine, uts.release, uts.version);
+#endif /* ifdef HAVE_UNAME */
+ unamep = unamebuf;
+}
+
+const char *
+named_os_uname(void) {
+ if (unamep == NULL) {
+ getuname();
+ }
+ return (unamep);
+}
diff --git a/bin/named/server.c b/bin/named/server.c
new file mode 100644
index 0000000..2f21fc5
--- /dev/null
+++ b/bin/named/server.c
@@ -0,0 +1,16755 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef HAVE_DNSTAP
+#include <fstrm.h>
+#endif
+
+#include <isc/aes.h>
+#include <isc/app.h>
+#include <isc/attributes.h>
+#include <isc/base64.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/hex.h>
+#include <isc/hmac.h>
+#include <isc/httpd.h>
+#include <isc/lex.h>
+#include <isc/meminfo.h>
+#include <isc/netmgr.h>
+#include <isc/nonce.h>
+#include <isc/parseint.h>
+#include <isc/portset.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/resource.h>
+#include <isc/result.h>
+#include <isc/siphash.h>
+#include <isc/stat.h>
+#include <isc/stats.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/badcache.h>
+#include <dns/cache.h>
+#include <dns/catz.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/dlz.h>
+#include <dns/dns64.h>
+#include <dns/dnsrps.h>
+#include <dns/dnssec.h>
+#include <dns/dyndb.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/forward.h>
+#include <dns/geoip.h>
+#include <dns/journal.h>
+#include <dns/kasp.h>
+#include <dns/keymgr.h>
+#include <dns/keytable.h>
+#include <dns/keyvalues.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/nsec3.h>
+#include <dns/nta.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/private.h>
+#include <dns/rbt.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/rootns.h>
+#include <dns/rriterator.h>
+#include <dns/secalg.h>
+#include <dns/soa.h>
+#include <dns/stats.h>
+#include <dns/time.h>
+#include <dns/tkey.h>
+#include <dns/tsig.h>
+#include <dns/ttl.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <dst/dst.h>
+
+#include <isccfg/grammar.h>
+#include <isccfg/kaspconf.h>
+#include <isccfg/namedconf.h>
+
+#include <ns/client.h>
+#include <ns/hooks.h>
+#include <ns/interfacemgr.h>
+#include <ns/listenlist.h>
+
+#include <bind9/check.h>
+
+#include <named/config.h>
+#include <named/control.h>
+#if defined(HAVE_GEOIP2)
+#include <named/geoip.h>
+#endif /* HAVE_GEOIP2 */
+#include <named/log.h>
+#include <named/logconf.h>
+#include <named/main.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/statschannel.h>
+#include <named/tkeyconf.h>
+#include <named/transportconf.h>
+#include <named/tsigconf.h>
+#include <named/zoneconf.h>
+#ifdef HAVE_LIBSCF
+#include <stdlib.h>
+
+#include <named/smf_globals.h>
+#endif /* ifdef HAVE_LIBSCF */
+
+#ifdef HAVE_LMDB
+#include <lmdb.h>
+#define count_newzones count_newzones_db
+#define configure_newzones configure_newzones_db
+#define dumpzone dumpzone_db
+#else /* HAVE_LMDB */
+#define count_newzones count_newzones_file
+#define configure_newzones configure_newzones_file
+#define dumpzone dumpzone_file
+#endif /* HAVE_LMDB */
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif /* ifndef SIZE_MAX */
+
+#ifndef SIZE_AS_PERCENT
+#define SIZE_AS_PERCENT ((size_t)-2)
+#endif /* ifndef SIZE_AS_PERCENT */
+
+#ifdef TUNE_LARGE
+#define RESOLVER_NTASKS_PERCPU 32
+#else
+#define RESOLVER_NTASKS_PERCPU 8
+#endif /* TUNE_LARGE */
+
+/* RFC7828 defines timeout as 16-bit value specified in units of 100
+ * milliseconds, so the maximum and minimum advertised and keepalive
+ * timeouts are capped by the data type (it's ~109 minutes)
+ */
+#define MIN_INITIAL_TIMEOUT UINT32_C(2500) /* 2.5 seconds */
+#define MAX_INITIAL_TIMEOUT UINT32_C(120000) /* 2 minutes */
+#define MIN_IDLE_TIMEOUT UINT32_C(100) /* 0.1 seconds */
+#define MAX_IDLE_TIMEOUT UINT32_C(120000) /* 2 minutes */
+#define MIN_KEEPALIVE_TIMEOUT UINT32_C(100) /* 0.1 seconds */
+#define MAX_KEEPALIVE_TIMEOUT UINT32_C(UINT16_MAX * 100)
+#define MIN_ADVERTISED_TIMEOUT UINT32_C(0) /* No minimum */
+#define MAX_ADVERTISED_TIMEOUT UINT32_C(UINT16_MAX * 100)
+
+/*%
+ * Check an operation for failure. Assumes that the function
+ * using it has a 'result' variable and a 'cleanup' label.
+ */
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+#define TCHECK(op) \
+ do { \
+ tresult = (op); \
+ if (tresult != ISC_R_SUCCESS) { \
+ isc_buffer_clear(*text); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKM(op, msg) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, \
+ "%s: %s", msg, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKMF(op, msg, file) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, \
+ "%s '%s': %s", msg, file, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKFATAL(op, msg) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ fatal(server, msg, result); \
+ } while (0)
+
+/*%
+ * Maximum ADB size for views that share a cache. Use this limit to suppress
+ * the total of memory footprint, which should be the main reason for sharing
+ * a cache. Only effective when a finite max-cache-size is specified.
+ * This is currently defined to be 8MB.
+ */
+#define MAX_ADB_SIZE_FOR_CACHESHARE 8388608U
+
+struct named_dispatch {
+ isc_sockaddr_t addr;
+ unsigned int dispatchgen;
+ dns_dispatch_t *dispatch;
+ ISC_LINK(struct named_dispatch) link;
+};
+
+struct named_cache {
+ dns_cache_t *cache;
+ dns_view_t *primaryview;
+ bool needflush;
+ bool adbsizeadjusted;
+ dns_rdataclass_t rdclass;
+ ISC_LINK(named_cache_t) link;
+};
+
+struct dumpcontext {
+ isc_mem_t *mctx;
+ bool dumpcache;
+ bool dumpzones;
+ bool dumpadb;
+ bool dumpbad;
+ bool dumpexpired;
+ bool dumpfail;
+ FILE *fp;
+ ISC_LIST(struct viewlistentry) viewlist;
+ struct viewlistentry *view;
+ struct zonelistentry *zone;
+ dns_dumpctx_t *mdctx;
+ dns_db_t *db;
+ dns_db_t *cache;
+ isc_task_t *task;
+ dns_dbversion_t *version;
+};
+
+struct viewlistentry {
+ dns_view_t *view;
+ ISC_LINK(struct viewlistentry) link;
+ ISC_LIST(struct zonelistentry) zonelist;
+};
+
+struct zonelistentry {
+ dns_zone_t *zone;
+ ISC_LINK(struct zonelistentry) link;
+};
+
+/*%
+ * Configuration context to retain for each view that allows
+ * new zones to be added at runtime.
+ */
+typedef struct ns_cfgctx {
+ isc_mem_t *mctx;
+ cfg_parser_t *conf_parser;
+ cfg_parser_t *add_parser;
+ cfg_obj_t *config;
+ cfg_obj_t *vconfig;
+ cfg_obj_t *nzf_config;
+ cfg_aclconfctx_t *actx;
+} ns_cfgctx_t;
+
+/*%
+ * A function to write out added-zone configuration to the new_zone_file
+ * specified in 'view'. Maybe called by delete_zoneconf().
+ */
+typedef isc_result_t (*nzfwriter_t)(const cfg_obj_t *config, dns_view_t *view);
+
+/*%
+ * Holds state information for the initial zone loading process.
+ * Uses the isc_refcount structure to count the number of views
+ * with pending zone loads, dereferencing as each view finishes.
+ */
+typedef struct {
+ named_server_t *server;
+ bool reconfig;
+ isc_refcount_t refs;
+} ns_zoneload_t;
+
+typedef struct {
+ named_server_t *server;
+} catz_cb_data_t;
+
+typedef struct catz_chgzone_event {
+ ISC_EVENT_COMMON(struct catz_chgzone_event);
+ dns_catz_entry_t *entry;
+ dns_catz_zone_t *origin;
+ dns_view_t *view;
+ catz_cb_data_t *cbd;
+ bool mod;
+} catz_chgzone_event_t;
+
+typedef struct {
+ unsigned int magic;
+#define DZARG_MAGIC ISC_MAGIC('D', 'z', 'a', 'r')
+ isc_buffer_t **text;
+ isc_result_t result;
+} ns_dzarg_t;
+
+/*
+ * These zones should not leak onto the Internet.
+ */
+const char *empty_zones[] = {
+ /* RFC 1918 */
+ "10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA",
+ "18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA",
+ "21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA",
+ "24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA",
+ "27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA",
+ "30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA",
+
+ /* RFC 6598 */
+ "64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA",
+ "67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA",
+ "70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA",
+ "73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA",
+ "76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA",
+ "79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA",
+ "82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA",
+ "85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA",
+ "88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA",
+ "91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA",
+ "94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA",
+ "97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA",
+ "100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA", "102.100.IN-ADDR.ARPA",
+ "103.100.IN-ADDR.ARPA", "104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA",
+ "106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA", "108.100.IN-ADDR.ARPA",
+ "109.100.IN-ADDR.ARPA", "110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA",
+ "112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA", "114.100.IN-ADDR.ARPA",
+ "115.100.IN-ADDR.ARPA", "116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA",
+ "118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA", "120.100.IN-ADDR.ARPA",
+ "121.100.IN-ADDR.ARPA", "122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA",
+ "124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA", "126.100.IN-ADDR.ARPA",
+ "127.100.IN-ADDR.ARPA",
+
+ /* RFC 5735 and RFC 5737 */
+ "0.IN-ADDR.ARPA", /* THIS NETWORK */
+ "127.IN-ADDR.ARPA", /* LOOPBACK */
+ "254.169.IN-ADDR.ARPA", /* LINK LOCAL */
+ "2.0.192.IN-ADDR.ARPA", /* TEST NET */
+ "100.51.198.IN-ADDR.ARPA", /* TEST NET 2 */
+ "113.0.203.IN-ADDR.ARPA", /* TEST NET 3 */
+ "255.255.255.255.IN-ADDR.ARPA", /* BROADCAST */
+
+ /* Local IPv6 Unicast Addresses */
+ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6."
+ "ARPA",
+ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6."
+ "ARPA",
+ /* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */
+ "D.F.IP6.ARPA", "8.E.F.IP6.ARPA", /* LINK LOCAL */
+ "9.E.F.IP6.ARPA", /* LINK LOCAL */
+ "A.E.F.IP6.ARPA", /* LINK LOCAL */
+ "B.E.F.IP6.ARPA", /* LINK LOCAL */
+
+ /* Example Prefix, RFC 3849. */
+ "8.B.D.0.1.0.0.2.IP6.ARPA",
+
+ /* RFC 7534 */
+ "EMPTY.AS112.ARPA",
+
+ /* RFC 8375 */
+ "HOME.ARPA",
+
+ NULL
+};
+
+noreturn static void
+fatal(named_server_t *server, const char *msg, isc_result_t result);
+
+static void
+named_server_reload(isc_task_t *task, isc_event_t *event);
+
+#ifdef HAVE_LIBNGHTTP2
+static isc_result_t
+listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
+ const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
+ isc_mem_t *mctx, ns_listenelt_t **target);
+#endif
+
+static isc_result_t
+listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target);
+
+static isc_result_t
+listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
+ ns_listenlist_t **target);
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view,
+ const dns_name_t *origin, const cfg_obj_t *forwarders,
+ const cfg_obj_t *forwardtype);
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates);
+
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ dns_viewlist_t *viewlist, dns_kasplist_t *kasplist,
+ cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok,
+ bool modify);
+
+static void
+configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig,
+ dns_view_t *view);
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx);
+
+static isc_result_t
+add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
+
+static void
+end_reserved_dispatches(named_server_t *server, bool all);
+
+static void
+newzone_cfgctx_destroy(void **cfgp);
+
+static isc_result_t
+putstr(isc_buffer_t **b, const char *str);
+
+static isc_result_t
+putmem(isc_buffer_t **b, const char *str, size_t len);
+
+static isc_result_t
+putuint8(isc_buffer_t **b, uint8_t val);
+
+static isc_result_t
+putnull(isc_buffer_t **b);
+
+static int
+count_zones(const cfg_obj_t *conf);
+
+#ifdef HAVE_LMDB
+static isc_result_t
+migrate_nzf(dns_view_t *view);
+
+static isc_result_t
+nzd_writable(dns_view_t *view);
+
+static isc_result_t
+nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi);
+
+static isc_result_t
+nzd_env_reopen(dns_view_t *view);
+
+static void
+nzd_env_close(dns_view_t *view);
+
+static isc_result_t
+nzd_close(MDB_txn **txnp, bool commit);
+
+static isc_result_t
+nzd_count(dns_view_t *view, int *countp);
+#else /* ifdef HAVE_LMDB */
+static isc_result_t
+nzf_append(dns_view_t *view, const cfg_obj_t *zconfig);
+#endif /* ifdef HAVE_LMDB */
+
+/*%
+ * Configure a single view ACL at '*aclp'. Get its configuration from
+ * 'vconfig' (for per-view configuration) and maybe from 'config'
+ */
+static isc_result_t
+configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const cfg_obj_t *gconfig, const char *aclname,
+ const char *acltuplename, cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, dns_acl_t **aclp) {
+ isc_result_t result;
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+
+ if (*aclp != NULL) {
+ dns_acl_detach(aclp);
+ }
+ if (vconfig != NULL) {
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ }
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ if (gconfig != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(gconfig, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ maps[i] = NULL;
+
+ (void)named_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL) {
+ /*
+ * No value available. *aclp == NULL.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ if (acltuplename != NULL) {
+ /*
+ * If the ACL is given in an optional tuple, retrieve it.
+ * The parser should have ensured that a valid object be
+ * returned.
+ */
+ aclobj = cfg_tuple_get(aclobj, acltuplename);
+ }
+
+ result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, mctx, 0,
+ aclp);
+
+ return (result);
+}
+
+/*%
+ * Configure a sortlist at '*aclp'. Essentially the same as
+ * configure_view_acl() except it calls cfg_acl_fromconfig with a
+ * nest_level value of 2.
+ */
+static isc_result_t
+configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx,
+ dns_acl_t **aclp) {
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+
+ if (*aclp != NULL) {
+ dns_acl_detach(aclp);
+ }
+ if (vconfig != NULL) {
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ }
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ maps[i] = NULL;
+
+ (void)named_config_get(maps, "sortlist", &aclobj);
+ if (aclobj == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Use a nest level of 3 for the "top level" of the sortlist;
+ * this means each entry in the top three levels will be stored
+ * as lists of separate, nested ACLs, rather than merged together
+ * into IP tables as is usually done with ACLs.
+ */
+ result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, mctx, 3,
+ aclp);
+
+ return (result);
+}
+
+static isc_result_t
+configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const char *confname, const char *conftuplename,
+ isc_mem_t *mctx, dns_rbt_t **rbtp) {
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *obj = NULL;
+ const cfg_listelt_t *element;
+ int i = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const char *str;
+ const cfg_obj_t *nameobj;
+
+ if (*rbtp != NULL) {
+ dns_rbt_destroy(rbtp);
+ }
+ if (vconfig != NULL) {
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ }
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ maps[i] = NULL;
+
+ (void)named_config_get(maps, confname, &obj);
+ if (obj == NULL) {
+ /*
+ * No value available. *rbtp == NULL.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ if (conftuplename != NULL) {
+ obj = cfg_tuple_get(obj, conftuplename);
+ if (cfg_obj_isvoid(obj)) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ result = dns_rbt_create(mctx, NULL, NULL, rbtp);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ name = dns_fixedname_initname(&fixed);
+ for (element = cfg_list_first(obj); element != NULL;
+ element = cfg_list_next(element))
+ {
+ nameobj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(nameobj);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ /*
+ * We don't need the node data, but need to set dummy data to
+ * avoid a partial match with an empty node. For example, if
+ * we have foo.example.com and bar.example.com, we'd get a match
+ * for baz.example.com, which is not the expected result.
+ * We simply use (void *)1 as the dummy data.
+ */
+ result = dns_rbt_addname(*rbtp, name, (void *)1);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(nameobj, named_g_lctx, ISC_LOG_ERROR,
+ "failed to add %s for %s: %s", str,
+ confname, isc_result_totext(result));
+ goto cleanup;
+ }
+ }
+
+ return (result);
+
+cleanup:
+ dns_rbt_destroy(rbtp);
+ return (result);
+}
+
+static isc_result_t
+ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp,
+ unsigned char *digest, dns_rdata_ds_t *ds) {
+ isc_result_t result;
+ dns_rdata_dnskey_t keystruct;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ uint32_t rdata1, rdata2, rdata3;
+ const char *datastr = NULL, *namestr = NULL;
+ unsigned char data[4096];
+ isc_buffer_t databuf;
+ unsigned char rrdata[4096];
+ isc_buffer_t rrdatabuf;
+ isc_region_t r;
+ dns_fixedname_t fname;
+ dns_name_t *name = NULL;
+ isc_buffer_t namebuf;
+ const char *atstr = NULL;
+ enum {
+ INIT_DNSKEY,
+ STATIC_DNSKEY,
+ INIT_DS,
+ STATIC_DS,
+ TRUSTED
+ } anchortype;
+
+ REQUIRE(namestrp != NULL && *namestrp == NULL);
+ REQUIRE(ds != NULL);
+
+ /* if DNSKEY, flags; if DS, key tag */
+ rdata1 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata1"));
+
+ /* if DNSKEY, protocol; if DS, algorithm */
+ rdata2 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata2"));
+
+ /* if DNSKEY, algorithm; if DS, digest type */
+ rdata3 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata3"));
+
+ namestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
+ *namestrp = namestr;
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_constinit(&namebuf, namestr, strlen(namestr));
+ isc_buffer_add(&namebuf, strlen(namestr));
+ CHECK(dns_name_fromtext(name, &namebuf, dns_rootname, 0, NULL));
+
+ if (*initialp) {
+ atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype"));
+
+ if (strcasecmp(atstr, "static-key") == 0) {
+ *initialp = false;
+ anchortype = STATIC_DNSKEY;
+ } else if (strcasecmp(atstr, "static-ds") == 0) {
+ *initialp = false;
+ anchortype = STATIC_DS;
+ } else if (strcasecmp(atstr, "initial-key") == 0) {
+ anchortype = INIT_DNSKEY;
+ } else if (strcasecmp(atstr, "initial-ds") == 0) {
+ anchortype = INIT_DS;
+ } else {
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR,
+ "key '%s': "
+ "invalid initialization method '%s'",
+ namestr, atstr);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ } else {
+ anchortype = TRUSTED;
+ }
+
+ isc_buffer_init(&databuf, data, sizeof(data));
+ isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
+
+ *ds = (dns_rdata_ds_t){ .common.rdclass = dns_rdataclass_in,
+ .common.rdtype = dns_rdatatype_ds };
+
+ ISC_LINK_INIT(&ds->common, link);
+
+ switch (anchortype) {
+ case INIT_DNSKEY:
+ case STATIC_DNSKEY:
+ case TRUSTED:
+ /*
+ * This function should never be reached for view
+ * class other than IN
+ */
+ keystruct.common.rdclass = dns_rdataclass_in;
+ keystruct.common.rdtype = dns_rdatatype_dnskey;
+
+ /*
+ * The key data in keystruct is not dynamically allocated.
+ */
+ keystruct.mctx = NULL;
+
+ ISC_LINK_INIT(&keystruct.common, link);
+
+ if (rdata1 > 0xffff) {
+ CHECKM(ISC_R_RANGE, "key flags");
+ }
+ if (rdata1 & DNS_KEYFLAG_REVOKE) {
+ CHECKM(DST_R_BADKEYTYPE, "key flags revoke bit set");
+ }
+ if (rdata2 > 0xff) {
+ CHECKM(ISC_R_RANGE, "key protocol");
+ }
+ if (rdata3 > 0xff) {
+ CHECKM(ISC_R_RANGE, "key algorithm");
+ }
+
+ keystruct.flags = (uint16_t)rdata1;
+ keystruct.protocol = (uint8_t)rdata2;
+ keystruct.algorithm = (uint8_t)rdata3;
+
+ if (!dst_algorithm_supported(keystruct.algorithm)) {
+ CHECK(DST_R_UNSUPPORTEDALG);
+ }
+
+ datastr = cfg_obj_asstring(cfg_tuple_get(key, "data"));
+ CHECK(isc_base64_decodestring(datastr, &databuf));
+ isc_buffer_usedregion(&databuf, &r);
+ keystruct.datalen = r.length;
+ keystruct.data = r.base;
+
+ CHECK(dns_rdata_fromstruct(&rdata, keystruct.common.rdclass,
+ keystruct.common.rdtype, &keystruct,
+ &rrdatabuf));
+ CHECK(dns_ds_fromkeyrdata(name, &rdata, DNS_DSDIGEST_SHA256,
+ digest, ds));
+ break;
+
+ case INIT_DS:
+ case STATIC_DS:
+ if (rdata1 > 0xffff) {
+ CHECKM(ISC_R_RANGE, "key tag");
+ }
+ if (rdata2 > 0xff) {
+ CHECKM(ISC_R_RANGE, "key algorithm");
+ }
+ if (rdata3 > 0xff) {
+ CHECKM(ISC_R_RANGE, "digest type");
+ }
+
+ ds->key_tag = (uint16_t)rdata1;
+ ds->algorithm = (uint8_t)rdata2;
+ ds->digest_type = (uint8_t)rdata3;
+
+ datastr = cfg_obj_asstring(cfg_tuple_get(key, "data"));
+ CHECK(isc_hex_decodestring(datastr, &databuf));
+ isc_buffer_usedregion(&databuf, &r);
+
+ switch (ds->digest_type) {
+ case DNS_DSDIGEST_SHA1:
+ if (r.length != ISC_SHA1_DIGESTLENGTH) {
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ break;
+ case DNS_DSDIGEST_SHA256:
+ if (r.length != ISC_SHA256_DIGESTLENGTH) {
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ break;
+ case DNS_DSDIGEST_SHA384:
+ if (r.length != ISC_SHA384_DIGESTLENGTH) {
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ break;
+ default:
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR,
+ "key '%s': "
+ "unknown ds digest type %u",
+ namestr, ds->digest_type);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ break;
+ }
+
+ ds->length = r.length;
+ ds->digest = digest;
+ memmove(ds->digest, r.base, r.length);
+
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ return (result);
+}
+
+static void
+sfd_add(const dns_name_t *name, void *arg) {
+ if (arg != NULL) {
+ dns_view_sfd_add(arg, name);
+ }
+}
+
+/*%
+ * Parse 'key' in the context of view configuration 'vconfig'. If successful,
+ * add the key to 'secroots' if both of the following conditions are true:
+ *
+ * - 'keyname_match' is NULL or it matches the owner name of 'key',
+ * - support for the algorithm used by 'key' is not disabled by 'resolver'
+ * for the owner name of 'key'.
+ *
+ * 'managed' is true for managed keys and false for trusted keys. 'mctx' is
+ * the memory context to use for allocating memory.
+ */
+static isc_result_t
+process_key(const cfg_obj_t *key, dns_keytable_t *secroots,
+ const dns_name_t *keyname_match, dns_view_t *view, bool managed) {
+ dns_fixedname_t fkeyname;
+ dns_name_t *keyname = NULL;
+ const char *namestr = NULL;
+ dns_rdata_ds_t ds;
+ isc_result_t result;
+ bool initializing = managed;
+ unsigned char digest[ISC_MAX_MD_SIZE];
+ isc_buffer_t b;
+
+ result = ta_fromconfig(key, &initializing, &namestr, digest, &ds);
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ /*
+ * Trust anchor was parsed correctly.
+ */
+ isc_buffer_constinit(&b, namestr, strlen(namestr));
+ isc_buffer_add(&b, strlen(namestr));
+ keyname = dns_fixedname_initname(&fkeyname);
+ result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ break;
+ case DST_R_UNSUPPORTEDALG:
+ case DST_R_BADKEYTYPE:
+ /*
+ * Key was parsed correctly, but it cannot be used; this is not
+ * a fatal error - log a warning about this key being ignored,
+ * but do not prevent any further ones from being processed.
+ */
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING,
+ "ignoring %s for '%s': %s",
+ initializing ? "initial-key" : "static-key",
+ namestr, isc_result_totext(result));
+ return (ISC_R_SUCCESS);
+ case DST_R_NOCRYPTO:
+ /*
+ * Crypto support is not available.
+ */
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR,
+ "ignoring %s for '%s': no crypto support",
+ initializing ? "initial-key" : "static-key",
+ namestr);
+ return (result);
+ default:
+ /*
+ * Something unexpected happened; we have no choice but to
+ * indicate an error so that the configuration loading process
+ * is interrupted.
+ */
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR,
+ "configuring %s for '%s': %s",
+ initializing ? "initial-key" : "static-key",
+ namestr, isc_result_totext(result));
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * If the caller requested to only load keys for a specific name and
+ * the owner name of this key does not match the requested name, do not
+ * load it.
+ */
+ if (keyname_match != NULL && !dns_name_equal(keyname_match, keyname)) {
+ goto done;
+ }
+
+ /*
+ * Ensure that 'resolver' allows using the algorithm of this key for
+ * its owner name. If it does not, do not load the key and log a
+ * warning, but do not prevent further keys from being processed.
+ */
+ if (!dns_resolver_algorithm_supported(view->resolver, keyname,
+ ds.algorithm))
+ {
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING,
+ "ignoring %s for '%s': algorithm is disabled",
+ initializing ? "initial-key" : "static-key",
+ namestr);
+ goto done;
+ }
+
+ /*
+ * Add the key to 'secroots'. Keys from a "trust-anchors" or
+ * "managed-keys" statement may be either static or initializing
+ * keys. If it's not initializing, we don't want to treat it as
+ * managed, so we use 'initializing' twice here, for both the
+ * 'managed' and 'initializing' arguments to dns_keytable_add().
+ */
+ result = dns_keytable_add(secroots, initializing, initializing, keyname,
+ &ds, sfd_add, view);
+
+done:
+ return (result);
+}
+
+/*
+ * Load keys from configuration into key table. If 'keyname' is specified,
+ * only load keys matching that name. If 'managed' is true, load the key as
+ * an initializing key.
+ */
+static isc_result_t
+load_view_keys(const cfg_obj_t *keys, dns_view_t *view, bool managed,
+ const dns_name_t *keyname) {
+ const cfg_listelt_t *elt, *elt2;
+ const cfg_obj_t *keylist;
+ isc_result_t result;
+ dns_keytable_t *secroots = NULL;
+
+ CHECK(dns_view_getsecroots(view, &secroots));
+
+ for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt))
+ {
+ keylist = cfg_listelt_value(elt);
+
+ for (elt2 = cfg_list_first(keylist); elt2 != NULL;
+ elt2 = cfg_list_next(elt2))
+ {
+ CHECK(process_key(cfg_listelt_value(elt2), secroots,
+ keyname, view, managed));
+ }
+ }
+
+cleanup:
+ if (secroots != NULL) {
+ dns_keytable_detach(&secroots);
+ }
+ if (result == DST_R_NOCRYPTO) {
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+/*%
+ * Check whether a key has been successfully loaded.
+ */
+static bool
+keyloaded(dns_view_t *view, const dns_name_t *name) {
+ isc_result_t result;
+ dns_keytable_t *secroots = NULL;
+ dns_keynode_t *keynode = NULL;
+
+ result = dns_view_getsecroots(view, &secroots);
+ if (result != ISC_R_SUCCESS) {
+ return (false);
+ }
+
+ result = dns_keytable_find(secroots, name, &keynode);
+
+ if (keynode != NULL) {
+ dns_keytable_detachkeynode(secroots, &keynode);
+ }
+ if (secroots != NULL) {
+ dns_keytable_detach(&secroots);
+ }
+
+ return (result == ISC_R_SUCCESS);
+}
+
+/*%
+ * Configure DNSSEC keys for a view.
+ *
+ * The per-view configuration values and the server-global defaults are read
+ * from 'vconfig' and 'config'.
+ */
+static isc_result_t
+configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
+ const cfg_obj_t *config, const cfg_obj_t *bindkeys,
+ bool auto_root, isc_mem_t *mctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_obj_t *view_keys = NULL;
+ const cfg_obj_t *global_keys = NULL;
+ const cfg_obj_t *view_managed_keys = NULL;
+ const cfg_obj_t *view_trust_anchors = NULL;
+ const cfg_obj_t *global_managed_keys = NULL;
+ const cfg_obj_t *global_trust_anchors = NULL;
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *voptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *directory;
+ int i = 0;
+
+ /* We don't need trust anchors for the _bind view */
+ if (strcmp(view->name, "_bind") == 0 &&
+ view->rdclass == dns_rdataclass_chaos)
+ {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ if (voptions != NULL) {
+ (void)cfg_map_get(voptions, "trusted-keys", &view_keys);
+
+ /* managed-keys and trust-anchors are synonyms. */
+ (void)cfg_map_get(voptions, "managed-keys",
+ &view_managed_keys);
+ (void)cfg_map_get(voptions, "trust-anchors",
+ &view_trust_anchors);
+
+ maps[i++] = voptions;
+ }
+ }
+
+ if (config != NULL) {
+ (void)cfg_map_get(config, "trusted-keys", &global_keys);
+
+ /* managed-keys and trust-anchors are synonyms. */
+ (void)cfg_map_get(config, "managed-keys", &global_managed_keys);
+ (void)cfg_map_get(config, "trust-anchors",
+ &global_trust_anchors);
+
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ result = dns_view_initsecroots(view, mctx);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "couldn't create keytable");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = dns_view_initntatable(view, named_g_taskmgr, named_g_timermgr);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "couldn't create NTA table");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ if (auto_root && view->rdclass == dns_rdataclass_in) {
+ const cfg_obj_t *builtin_keys = NULL;
+
+ /*
+ * If bind.keys exists and is populated, it overrides
+ * the trust-anchors clause hard-coded in named_g_config.
+ */
+ if (bindkeys != NULL) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "obtaining root key for view %s "
+ "from '%s'",
+ view->name, named_g_server->bindkeysfile);
+
+ (void)cfg_map_get(bindkeys, "trust-anchors",
+ &builtin_keys);
+
+ if (builtin_keys == NULL) {
+ isc_log_write(
+ named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "dnssec-validation auto: "
+ "WARNING: root zone key "
+ "not found");
+ }
+ }
+
+ if (builtin_keys == NULL) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using built-in root key for view %s",
+ view->name);
+
+ (void)cfg_map_get(named_g_config, "trust-anchors",
+ &builtin_keys);
+ }
+
+ if (builtin_keys != NULL) {
+ CHECK(load_view_keys(builtin_keys, view, true,
+ dns_rootname));
+ }
+
+ if (!keyloaded(view, dns_rootname)) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "root key not loaded");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ if (view->rdclass == dns_rdataclass_in) {
+ CHECK(load_view_keys(view_keys, view, false, NULL));
+ CHECK(load_view_keys(view_trust_anchors, view, true, NULL));
+ CHECK(load_view_keys(view_managed_keys, view, true, NULL));
+
+ CHECK(load_view_keys(global_keys, view, false, NULL));
+ CHECK(load_view_keys(global_trust_anchors, view, true, NULL));
+ CHECK(load_view_keys(global_managed_keys, view, true, NULL));
+ }
+
+ /*
+ * Add key zone for managed keys.
+ */
+ obj = NULL;
+ (void)named_config_get(maps, "managed-keys-directory", &obj);
+ directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL);
+ if (directory != NULL) {
+ result = isc_file_isdirectory(directory);
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "invalid managed-keys-directory %s: %s",
+ directory, isc_result_totext(result));
+ goto cleanup;
+ } else if (directory != NULL) {
+ if (!isc_file_isdirwritable(directory)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "managed-keys-directory '%s' "
+ "is not writable",
+ directory);
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+ }
+
+ CHECK(add_keydata_zone(view, directory, named_g_mctx));
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) {
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ bool value;
+ isc_result_t result;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&fixed);
+ for (element = cfg_list_first(mbs); element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
+ CHECK(dns_resolver_setmustbesecure(resolver, name, value));
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ return (result);
+}
+
+/*%
+ * Get a dispatch appropriate for the resolver of a given view.
+ */
+static isc_result_t
+get_view_querysource_dispatch(const cfg_obj_t **maps, int af,
+ dns_dispatch_t **dispatchp, bool is_firstview) {
+ isc_result_t result = ISC_R_FAILURE;
+ dns_dispatch_t *disp = NULL;
+ isc_sockaddr_t sa;
+ const cfg_obj_t *obj = NULL;
+
+ switch (af) {
+ case AF_INET:
+ result = named_config_get(maps, "query-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ case AF_INET6:
+ result = named_config_get(maps, "query-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ sa = *(cfg_obj_assockaddr(obj));
+ INSIST(isc_sockaddr_pf(&sa) == af);
+
+ /*
+ * If we don't support this address family, we're done!
+ */
+ switch (af) {
+ case AF_INET:
+ result = isc_net_probeipv4();
+ break;
+ case AF_INET6:
+ result = isc_net_probeipv6();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Try to find a dispatcher that we can share.
+ */
+ if (isc_sockaddr_getport(&sa) != 0) {
+ INSIST(obj != NULL);
+ if (is_firstview) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO,
+ "using specific query-source port "
+ "suppresses port randomization and can be "
+ "insecure.");
+ }
+ }
+
+ result = dns_dispatch_createudp(named_g_dispatchmgr, &sa, &disp);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_t any;
+ char buf[ISC_SOCKADDR_FORMATSIZE];
+
+ switch (af) {
+ case AF_INET:
+ isc_sockaddr_any(&any);
+ break;
+ case AF_INET6:
+ isc_sockaddr_any6(&any);
+ break;
+ }
+ if (isc_sockaddr_equal(&sa, &any)) {
+ return (ISC_R_SUCCESS);
+ }
+ isc_sockaddr_format(&sa, buf, sizeof(buf));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not get query source dispatcher (%s)",
+ buf);
+ return (result);
+ }
+
+ *dispatchp = disp;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_order(dns_order_t *order, const cfg_obj_t *ent) {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t rdtype;
+ const cfg_obj_t *obj;
+ dns_fixedname_t fixed;
+ unsigned int mode = 0;
+ const char *str;
+ isc_buffer_t b;
+ isc_result_t result;
+ bool addroot;
+
+ result = named_config_getclass(cfg_tuple_get(ent, "class"),
+ dns_rdataclass_any, &rdclass);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = named_config_gettype(cfg_tuple_get(ent, "type"),
+ dns_rdatatype_any, &rdtype);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = cfg_tuple_get(ent, "name");
+ if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ } else {
+ str = "*";
+ }
+ addroot = (strcmp(str, "*") == 0);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ dns_fixedname_init(&fixed);
+ result = dns_name_fromtext(dns_fixedname_name(&fixed), &b, dns_rootname,
+ 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = cfg_tuple_get(ent, "ordering");
+ INSIST(cfg_obj_isstring(obj));
+ str = cfg_obj_asstring(obj);
+ if (!strcasecmp(str, "fixed")) {
+#if DNS_RDATASET_FIXED
+ mode = DNS_RDATASETATTR_FIXEDORDER;
+#else /* if DNS_RDATASET_FIXED */
+ mode = DNS_RDATASETATTR_CYCLIC;
+#endif /* DNS_RDATASET_FIXED */
+ } else if (!strcasecmp(str, "random")) {
+ mode = DNS_RDATASETATTR_RANDOMIZE;
+ } else if (!strcasecmp(str, "cyclic")) {
+ mode = DNS_RDATASETATTR_CYCLIC;
+ } else if (!strcasecmp(str, "none")) {
+ mode = DNS_RDATASETATTR_NONE;
+ } else {
+ UNREACHABLE();
+ }
+
+ /*
+ * "*" should match everything including the root (BIND 8 compat).
+ * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
+ * explicit entry for "." when the name is "*".
+ */
+ if (addroot) {
+ result = dns_order_add(order, dns_rootname, rdtype, rdclass,
+ mode);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ return (dns_order_add(order, dns_fixedname_name(&fixed), rdtype,
+ rdclass, mode));
+}
+
+static isc_result_t
+configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
+ isc_netaddr_t na;
+ dns_peer_t *peer;
+ const cfg_obj_t *obj;
+ const char *str;
+ isc_result_t result;
+ unsigned int prefixlen;
+
+ cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
+
+ peer = NULL;
+ result = dns_peer_newprefix(mctx, &na, prefixlen, &peer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "bogus", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "provide-ixfr", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-expire", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setrequestexpire(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-ixfr", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-nsid", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "send-cookie", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setsendcookie(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns-udp-size", &obj);
+ if (obj != NULL) {
+ uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512U) {
+ udpsize = 512U;
+ }
+ if (udpsize > 4096U) {
+ udpsize = 4096U;
+ }
+ CHECK(dns_peer_setudpsize(peer, (uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns-version", &obj);
+ if (obj != NULL) {
+ uint32_t ednsversion = cfg_obj_asuint32(obj);
+ if (ednsversion > 255U) {
+ ednsversion = 255U;
+ }
+ CHECK(dns_peer_setednsversion(peer, (uint8_t)ednsversion));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "max-udp-size", &obj);
+ if (obj != NULL) {
+ uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512U) {
+ udpsize = 512U;
+ }
+ if (udpsize > 4096U) {
+ udpsize = 4096U;
+ }
+ CHECK(dns_peer_setmaxudp(peer, (uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "padding", &obj);
+ if (obj != NULL) {
+ uint32_t padding = cfg_obj_asuint32(obj);
+ if (padding > 512U) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "server padding value cannot "
+ "exceed 512: lowering");
+ padding = 512U;
+ }
+ CHECK(dns_peer_setpadding(peer, (uint16_t)padding));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "tcp-only", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_setforcetcp(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "tcp-keepalive", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_settcpkeepalive(peer, cfg_obj_asboolean(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfers", &obj);
+ if (obj != NULL) {
+ CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfer-format", &obj);
+ if (obj != NULL) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0) {
+ CHECK(dns_peer_settransferformat(peer,
+ dns_many_answers));
+ } else if (strcasecmp(str, "one-answer") == 0) {
+ CHECK(dns_peer_settransferformat(peer, dns_one_answer));
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "keys", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET) {
+ (void)cfg_map_get(cpeer, "transfer-source", &obj);
+ } else {
+ (void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
+ }
+ if (obj != NULL) {
+ result = dns_peer_settransfersource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET) {
+ (void)cfg_map_get(cpeer, "notify-source", &obj);
+ } else {
+ (void)cfg_map_get(cpeer, "notify-source-v6", &obj);
+ }
+ if (obj != NULL) {
+ result = dns_peer_setnotifysource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET) {
+ (void)cfg_map_get(cpeer, "query-source", &obj);
+ } else {
+ (void)cfg_map_get(cpeer, "query-source-v6", &obj);
+ }
+ if (obj != NULL) {
+ result = dns_peer_setquerysource(peer, cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+ }
+
+ *peerp = peer;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dns_peer_detach(&peer);
+ return (result);
+}
+
+static isc_result_t
+configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx,
+ const dns_dyndbctx_t *dctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_obj_t *obj;
+ const char *name, *library;
+
+ /* Get the name of the dyndb instance and the library path . */
+ name = cfg_obj_asstring(cfg_tuple_get(dyndb, "name"));
+ library = cfg_obj_asstring(cfg_tuple_get(dyndb, "library"));
+
+ obj = cfg_tuple_get(dyndb, "parameters");
+ if (obj != NULL) {
+ result = dns_dyndb_load(library, name, cfg_obj_asstring(obj),
+ cfg_obj_file(obj), cfg_obj_line(obj),
+ mctx, dctx);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dynamic database '%s' configuration failed: %s",
+ name, isc_result_totext(result));
+ }
+ return (result);
+}
+
+static isc_result_t
+disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
+ isc_result_t result;
+ const cfg_obj_t *algorithms;
+ const cfg_listelt_t *element;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+
+ algorithms = cfg_tuple_get(disabled, "algorithms");
+ for (element = cfg_list_first(algorithms); element != NULL;
+ element = cfg_list_next(element))
+ {
+ isc_textregion_t r;
+ dns_secalg_t alg;
+
+ DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
+ r.length = strlen(r.base);
+
+ result = dns_secalg_fromtext(&alg, &r);
+ if (result != ISC_R_SUCCESS) {
+ uint8_t ui;
+ result = isc_parse_uint8(&ui, r.base, 10);
+ alg = ui;
+ }
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(cfg_listelt_value(element), named_g_lctx,
+ ISC_LOG_ERROR, "invalid algorithm");
+ CHECK(result);
+ }
+ CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
+ }
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+disable_ds_digests(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
+ isc_result_t result;
+ const cfg_obj_t *digests;
+ const cfg_listelt_t *element;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+
+ digests = cfg_tuple_get(disabled, "digests");
+ for (element = cfg_list_first(digests); element != NULL;
+ element = cfg_list_next(element))
+ {
+ isc_textregion_t r;
+ dns_dsdigest_t digest;
+
+ DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
+ r.length = strlen(r.base);
+
+ /* disable_ds_digests handles numeric values. */
+ result = dns_dsdigest_fromtext(&digest, &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(cfg_listelt_value(element), named_g_lctx,
+ ISC_LOG_ERROR, "invalid algorithm");
+ CHECK(result);
+ }
+ CHECK(dns_resolver_disable_ds_digest(resolver, name, digest));
+ }
+cleanup:
+ return (result);
+}
+
+static bool
+on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
+ const cfg_listelt_t *element;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_result_t result;
+ const cfg_obj_t *value;
+ const char *str;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&fixed);
+
+ for (element = cfg_list_first(disablelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ value = cfg_listelt_value(element);
+ str = cfg_obj_asstring(value);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_equal(name, zonename)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+static isc_result_t
+check_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv,
+ isc_mem_t *mctx) {
+ char **argv = NULL;
+ unsigned int i;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ CHECK(dns_zone_getdbtype(zone, &argv, mctx));
+
+ /*
+ * Check that all the arguments match.
+ */
+ for (i = 0; i < dbtypec; i++) {
+ if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
+ CHECK(ISC_R_FAILURE);
+
+ /*
+ * Check that there are not extra arguments.
+ */
+ }
+ }
+
+ /*
+ * Check that there are not extra arguments.
+ */
+ if (i == dbtypec && argv[i] != NULL) {
+ result = ISC_R_FAILURE;
+ }
+
+cleanup:
+ isc_mem_free(mctx, argv);
+ return (result);
+}
+
+static isc_result_t
+setquerystats(dns_zone_t *zone, isc_mem_t *mctx, dns_zonestat_level_t level) {
+ isc_result_t result;
+ isc_stats_t *zoneqrystats;
+
+ dns_zone_setstatlevel(zone, level);
+
+ zoneqrystats = NULL;
+ if (level == dns_zonestat_full) {
+ result = isc_stats_create(mctx, &zoneqrystats,
+ ns_statscounter_max);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ if (zoneqrystats != NULL) {
+ isc_stats_detach(&zoneqrystats);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static named_cache_t *
+cachelist_find(named_cachelist_t *cachelist, const char *cachename,
+ dns_rdataclass_t rdclass) {
+ named_cache_t *nsc;
+
+ for (nsc = ISC_LIST_HEAD(*cachelist); nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link))
+ {
+ if (nsc->rdclass == rdclass &&
+ strcmp(dns_cache_getname(nsc->cache), cachename) == 0)
+ {
+ return (nsc);
+ }
+ }
+
+ return (NULL);
+}
+
+static bool
+cache_reusable(dns_view_t *originview, dns_view_t *view,
+ bool new_zero_no_soattl) {
+ if (originview->rdclass != view->rdclass ||
+ originview->checknames != view->checknames ||
+ dns_resolver_getzeronosoattl(originview->resolver) !=
+ new_zero_no_soattl ||
+ originview->acceptexpired != view->acceptexpired ||
+ originview->enablevalidation != view->enablevalidation ||
+ originview->maxcachettl != view->maxcachettl ||
+ originview->maxncachettl != view->maxncachettl)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+cache_sharable(dns_view_t *originview, dns_view_t *view,
+ bool new_zero_no_soattl, uint64_t new_max_cache_size,
+ uint32_t new_stale_ttl, uint32_t new_stale_refresh_time) {
+ /*
+ * If the cache cannot even reused for the same view, it cannot be
+ * shared with other views.
+ */
+ if (!cache_reusable(originview, view, new_zero_no_soattl)) {
+ return (false);
+ }
+
+ /*
+ * Check other cache related parameters that must be consistent among
+ * the sharing views.
+ */
+ if (dns_cache_getservestalettl(originview->cache) != new_stale_ttl ||
+ dns_cache_getservestalerefresh(originview->cache) !=
+ new_stale_refresh_time ||
+ dns_cache_getcachesize(originview->cache) != new_max_cache_size)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+/*
+ * Callback from DLZ configure when the driver sets up a writeable zone
+ */
+static isc_result_t
+dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) {
+ dns_name_t *origin = dns_zone_getorigin(zone);
+ dns_rdataclass_t zclass = view->rdclass;
+ isc_result_t result;
+
+ result = dns_zonemgr_managezone(named_g_server->zonemgr, zone);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ dns_zone_setstats(zone, named_g_server->zonestats);
+
+ return (named_zone_configure_writeable_dlz(dlzdb, zone, zclass,
+ origin));
+}
+
+static isc_result_t
+dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na,
+ unsigned int prefixlen, const char *server, const char *contact) {
+ char reverse[48 + sizeof("ip6.arpa.")] = { 0 };
+ char buf[sizeof("x.x.")];
+ const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." };
+ const char *sep = ": view ";
+ const char *viewname = view->name;
+ const unsigned char *s6;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_zone_t *zone = NULL;
+ int dns64_dbtypec = 4;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
+ prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ }
+
+ /*
+ * Construct the reverse name of the zone.
+ */
+ s6 = na->type.in6.s6_addr;
+ while (prefixlen > 0) {
+ prefixlen -= 8;
+ snprintf(buf, sizeof(buf), "%x.%x.", s6[prefixlen / 8] & 0xf,
+ (s6[prefixlen / 8] >> 4) & 0xf);
+ strlcat(reverse, buf, sizeof(reverse));
+ }
+ strlcat(reverse, "ip6.arpa.", sizeof(reverse));
+
+ /*
+ * Create the actual zone.
+ */
+ if (server != NULL) {
+ dns64_dbtype[2] = server;
+ }
+ if (contact != NULL) {
+ dns64_dbtype[3] = contact;
+ }
+ name = dns_fixedname_initname(&fixed);
+ isc_buffer_constinit(&b, reverse, strlen(reverse));
+ isc_buffer_add(&b, strlen(reverse));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, name));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_primary);
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype);
+ if (view->queryacl != NULL) {
+ dns_zone_setqueryacl(zone, view->queryacl);
+ }
+ if (view->queryonacl != NULL) {
+ dns_zone_setqueryonacl(zone, view->queryonacl);
+ }
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ CHECK(setquerystats(zone, mctx, dns_zonestat_none)); /* XXXMPA */
+ CHECK(dns_view_addzone(view, zone));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dns64 reverse zone%s%s: %s", sep, viewname, reverse);
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ return (result);
+}
+
+#ifdef USE_DNSRPS
+typedef struct conf_dnsrps_ctx conf_dnsrps_ctx_t;
+struct conf_dnsrps_ctx {
+ isc_result_t result;
+ char *cstr;
+ size_t cstr_size;
+ isc_mem_t *mctx;
+};
+
+/*
+ * Add to the DNSRPS configuration string.
+ */
+static bool
+conf_dnsrps_sadd(conf_dnsrps_ctx_t *ctx, const char *p, ...) {
+ size_t new_len, cur_len, new_cstr_size;
+ char *new_cstr;
+ va_list args;
+
+ if (ctx->cstr == NULL) {
+ ctx->cstr = isc_mem_get(ctx->mctx, 256);
+ ctx->cstr[0] = '\0';
+ ctx->cstr_size = 256;
+ }
+
+ cur_len = strlen(ctx->cstr);
+ va_start(args, p);
+ new_len = vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p,
+ args) +
+ 1;
+ va_end(args);
+
+ if (cur_len + new_len <= ctx->cstr_size) {
+ return (true);
+ }
+
+ new_cstr_size = ((cur_len + new_len) / 256 + 1) * 256;
+ new_cstr = isc_mem_get(ctx->mctx, new_cstr_size);
+
+ memmove(new_cstr, ctx->cstr, cur_len);
+ isc_mem_put(ctx->mctx, ctx->cstr, ctx->cstr_size);
+ ctx->cstr_size = new_cstr_size;
+ ctx->cstr = new_cstr;
+
+ /* cannot use args twice after a single va_start()on some systems */
+ va_start(args, p);
+ vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p, args);
+ va_end(args);
+ return (true);
+}
+
+/*
+ * Get an DNSRPS configuration value using the global and view options
+ * for the default. Return false upon failure.
+ */
+static bool
+conf_dnsrps_get(const cfg_obj_t **sub_obj, const cfg_obj_t **maps,
+ const cfg_obj_t *obj, const char *name,
+ conf_dnsrps_ctx_t *ctx) {
+ if (ctx != NULL && ctx->result != ISC_R_SUCCESS) {
+ *sub_obj = NULL;
+ return (false);
+ }
+
+ *sub_obj = cfg_tuple_get(obj, name);
+ if (cfg_obj_isvoid(*sub_obj)) {
+ *sub_obj = NULL;
+ if (maps != NULL &&
+ ISC_R_SUCCESS != named_config_get(maps, name, sub_obj))
+ {
+ *sub_obj = NULL;
+ }
+ }
+ return (true);
+}
+
+/*
+ * Handle a DNSRPS boolean configuration value with the global and view
+ * options providing the default.
+ */
+static void
+conf_dnsrps_yes_no(const cfg_obj_t *obj, const char *name,
+ conf_dnsrps_ctx_t *ctx) {
+ const cfg_obj_t *sub_obj;
+
+ if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx)) {
+ return;
+ }
+ if (sub_obj == NULL) {
+ return;
+ }
+ if (ctx == NULL) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "\"%s\" without \"dnsrps-enable yes\"", name);
+ return;
+ }
+
+ conf_dnsrps_sadd(ctx, " %s %s", name,
+ cfg_obj_asboolean(sub_obj) ? "yes" : "no");
+}
+
+static void
+conf_dnsrps_num(const cfg_obj_t *obj, const char *name,
+ conf_dnsrps_ctx_t *ctx) {
+ const cfg_obj_t *sub_obj;
+
+ if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx)) {
+ return;
+ }
+ if (sub_obj == NULL) {
+ return;
+ }
+ if (ctx == NULL) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "\"%s\" without \"dnsrps-enable yes\"", name);
+ return;
+ }
+
+ if (cfg_obj_isduration(sub_obj)) {
+ conf_dnsrps_sadd(ctx, " %s %d", name,
+ cfg_obj_asduration(sub_obj));
+ } else {
+ conf_dnsrps_sadd(ctx, " %s %d", name,
+ cfg_obj_asuint32(sub_obj));
+ }
+}
+
+/*
+ * Convert the parsed RPZ configuration statement to a string for
+ * dns_rpz_new_zones().
+ */
+static isc_result_t
+conf_dnsrps(dns_view_t *view, const cfg_obj_t **maps, bool nsip_enabled,
+ bool nsdname_enabled, dns_rpz_zbits_t *nsip_on,
+ dns_rpz_zbits_t *nsdname_on, char **rps_cstr, size_t *rps_cstr_size,
+ const cfg_obj_t *rpz_obj, const cfg_listelt_t *zone_element) {
+ conf_dnsrps_ctx_t ctx;
+ const cfg_obj_t *zone_obj, *obj;
+ dns_rpz_num_t rpz_num;
+ bool on;
+ const char *s;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.result = ISC_R_SUCCESS;
+ ctx.mctx = view->mctx;
+
+ for (rpz_num = 0; zone_element != NULL && ctx.result == ISC_R_SUCCESS;
+ ++rpz_num)
+ {
+ zone_obj = cfg_listelt_value(zone_element);
+
+ s = cfg_obj_asstring(cfg_tuple_get(zone_obj, "zone name"));
+ conf_dnsrps_sadd(&ctx, "zone \"%s\"", s);
+
+ obj = cfg_tuple_get(zone_obj, "policy");
+ if (!cfg_obj_isvoid(obj)) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+ conf_dnsrps_sadd(&ctx, " policy %s", s);
+ if (strcasecmp(s, "cname") == 0) {
+ s = cfg_obj_asstring(
+ cfg_tuple_get(obj, "cname"));
+ conf_dnsrps_sadd(&ctx, " %s", s);
+ }
+ }
+
+ conf_dnsrps_yes_no(zone_obj, "recursive-only", &ctx);
+ conf_dnsrps_yes_no(zone_obj, "log", &ctx);
+ conf_dnsrps_num(zone_obj, "max-policy-ttl", &ctx);
+ obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+ if (!cfg_obj_isvoid(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ *nsip_on |= DNS_RPZ_ZBIT(rpz_num);
+ } else {
+ *nsip_on &= ~DNS_RPZ_ZBIT(rpz_num);
+ }
+ }
+ on = ((*nsip_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+ if (nsip_enabled != on) {
+ conf_dnsrps_sadd(&ctx, on ? " nsip-enable yes "
+ : " nsip-enable no ");
+ }
+ obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+ if (!cfg_obj_isvoid(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ *nsdname_on |= DNS_RPZ_ZBIT(rpz_num);
+ } else {
+ *nsdname_on &= ~DNS_RPZ_ZBIT(rpz_num);
+ }
+ }
+ on = ((*nsdname_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+ if (nsdname_enabled != on) {
+ conf_dnsrps_sadd(&ctx, on ? " nsdname-enable yes "
+ : " nsdname-enable no ");
+ }
+ conf_dnsrps_sadd(&ctx, ";\n");
+ zone_element = cfg_list_next(zone_element);
+ }
+
+ conf_dnsrps_yes_no(rpz_obj, "recursive-only", &ctx);
+ conf_dnsrps_num(rpz_obj, "max-policy-ttl", &ctx);
+ conf_dnsrps_num(rpz_obj, "min-ns-dots", &ctx);
+ conf_dnsrps_yes_no(rpz_obj, "qname-wait-recurse", &ctx);
+ conf_dnsrps_yes_no(rpz_obj, "break-dnssec", &ctx);
+ if (!nsip_enabled) {
+ conf_dnsrps_sadd(&ctx, " nsip-enable no ");
+ }
+ if (!nsdname_enabled) {
+ conf_dnsrps_sadd(&ctx, " nsdname-enable no ");
+ }
+
+ /*
+ * Get the general dnsrpzd parameters from the response-policy
+ * statement in the view and the general options.
+ */
+ if (conf_dnsrps_get(&obj, maps, rpz_obj, "dnsrps-options", &ctx) &&
+ obj != NULL)
+ {
+ conf_dnsrps_sadd(&ctx, " %s\n", cfg_obj_asstring(obj));
+ }
+
+ if (ctx.result == ISC_R_SUCCESS) {
+ *rps_cstr = ctx.cstr;
+ *rps_cstr_size = ctx.cstr_size;
+ } else {
+ if (ctx.cstr != NULL) {
+ isc_mem_put(ctx.mctx, ctx.cstr, ctx.cstr_size);
+ }
+ *rps_cstr = NULL;
+ *rps_cstr_size = 0;
+ }
+ return (ctx.result);
+}
+#endif /* ifdef USE_DNSRPS */
+
+static isc_result_t
+configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const char *msg) {
+ isc_result_t result;
+
+ result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid %s '%s'", msg, str);
+ }
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const dns_name_t *origin) {
+ isc_result_t result;
+
+ result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE,
+ view->mctx);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid zone '%s'", str);
+ }
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
+ bool recursive_only_default, bool add_soa_default,
+ dns_ttl_t ttl_default, uint32_t minupdateinterval_default,
+ const dns_rpz_zone_t *old, bool *old_rpz_okp) {
+ const cfg_obj_t *rpz_obj, *obj;
+ const char *str;
+ dns_rpz_zone_t *zone = NULL;
+ isc_result_t result;
+ dns_rpz_num_t rpz_num;
+
+ REQUIRE(old != NULL || !*old_rpz_okp);
+
+ rpz_obj = cfg_listelt_value(element);
+
+ if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "limit of %d response policy zones exceeded",
+ DNS_RPZ_MAX_ZONES);
+ return (ISC_R_FAILURE);
+ }
+
+ result = dns_rpz_new_zone(view->rpzs, &zone);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "Error creating new RPZ zone : %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "recursive-only");
+ if (cfg_obj_isvoid(obj) ? recursive_only_default
+ : cfg_obj_asboolean(obj))
+ {
+ view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(zone->num);
+ } else {
+ view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(zone->num);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "log");
+ if (!cfg_obj_isvoid(obj) && !cfg_obj_asboolean(obj)) {
+ view->rpzs->p.no_log |= DNS_RPZ_ZBIT(zone->num);
+ } else {
+ view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(zone->num);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+ if (cfg_obj_isduration(obj)) {
+ zone->max_policy_ttl = cfg_obj_asduration(obj);
+ } else {
+ zone->max_policy_ttl = ttl_default;
+ }
+ if (*old_rpz_okp && zone->max_policy_ttl != old->max_policy_ttl) {
+ *old_rpz_okp = false;
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "min-update-interval");
+ if (cfg_obj_isduration(obj)) {
+ zone->min_update_interval = cfg_obj_asduration(obj);
+ } else {
+ zone->min_update_interval = minupdateinterval_default;
+ }
+ if (*old_rpz_okp &&
+ zone->min_update_interval != old->min_update_interval)
+ {
+ *old_rpz_okp = false;
+ }
+
+ str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
+ result = configure_rpz_name(view, rpz_obj, &zone->origin, str, "zone");
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (dns_name_equal(&zone->origin, dns_rootname)) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid zone name '%s'", str);
+ return (DNS_R_EMPTYLABEL);
+ }
+ if (!view->rpzs->p.dnsrps_enabled) {
+ for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones - 1;
+ ++rpz_num)
+ {
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
+ &zone->origin))
+ {
+ cfg_obj_log(rpz_obj, named_g_lctx,
+ DNS_RPZ_ERROR_LEVEL,
+ "duplicate '%s'", str);
+ result = DNS_R_DUPLICATE;
+ return (result);
+ }
+ }
+ }
+ if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin)) {
+ *old_rpz_okp = false;
+ }
+
+ result = configure_rpz_name2(view, rpz_obj, &zone->client_ip,
+ DNS_RPZ_CLIENT_IP_ZONE, &zone->origin);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name2(view, rpz_obj, &zone->ip, DNS_RPZ_IP_ZONE,
+ &zone->origin);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name2(view, rpz_obj, &zone->nsdname,
+ DNS_RPZ_NSDNAME_ZONE, &zone->origin);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name2(view, rpz_obj, &zone->nsip,
+ DNS_RPZ_NSIP_ZONE, &zone->origin);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name(view, rpz_obj, &zone->passthru,
+ DNS_RPZ_PASSTHRU_NAME, "name");
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name(view, rpz_obj, &zone->drop,
+ DNS_RPZ_DROP_NAME, "name");
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = configure_rpz_name(view, rpz_obj, &zone->tcp_only,
+ DNS_RPZ_TCP_ONLY_NAME, "name");
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "policy");
+ if (cfg_obj_isvoid(obj)) {
+ zone->policy = DNS_RPZ_POLICY_GIVEN;
+ } else {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+ zone->policy = dns_rpz_str2policy(str);
+ INSIST(zone->policy != DNS_RPZ_POLICY_ERROR);
+ if (zone->policy == DNS_RPZ_POLICY_CNAME) {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
+ result = configure_rpz_name(view, rpz_obj, &zone->cname,
+ str, "cname");
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ }
+ if (*old_rpz_okp && (zone->policy != old->policy ||
+ !dns_name_equal(&old->cname, &zone->cname)))
+ {
+ *old_rpz_okp = false;
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "add-soa");
+ if (cfg_obj_isvoid(obj)) {
+ zone->addsoa = add_soa_default;
+ } else {
+ zone->addsoa = cfg_obj_asboolean(obj);
+ }
+ if (*old_rpz_okp && zone->addsoa != old->addsoa) {
+ *old_rpz_okp = false;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t **maps,
+ const cfg_obj_t *rpz_obj, bool *old_rpz_okp) {
+ bool dnsrps_enabled;
+ const cfg_listelt_t *zone_element;
+ char *rps_cstr;
+ size_t rps_cstr_size;
+ const cfg_obj_t *sub_obj;
+ bool recursive_only_default, add_soa_default;
+ bool nsip_enabled, nsdname_enabled;
+ dns_rpz_zbits_t nsip_on, nsdname_on;
+ dns_ttl_t ttl_default;
+ uint32_t minupdateinterval_default;
+ dns_rpz_zones_t *zones;
+ const dns_rpz_zones_t *old;
+ bool pview_must_detach = false;
+ const dns_rpz_zone_t *old_zone;
+ isc_result_t result;
+ int i;
+
+ *old_rpz_okp = false;
+
+ zone_element = cfg_list_first(cfg_tuple_get(rpz_obj, "zone list"));
+ if (zone_element == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ nsip_enabled = true;
+ sub_obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+ if (!cfg_obj_isvoid(sub_obj)) {
+ nsip_enabled = cfg_obj_asboolean(sub_obj);
+ }
+ nsip_on = nsip_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+ nsdname_enabled = true;
+ sub_obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+ if (!cfg_obj_isvoid(sub_obj)) {
+ nsdname_enabled = cfg_obj_asboolean(sub_obj);
+ }
+ nsdname_on = nsdname_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+ /*
+ * "dnsrps-enable yes|no" can be either a global or response-policy
+ * clause.
+ */
+ dnsrps_enabled = false;
+ rps_cstr = NULL;
+ rps_cstr_size = 0;
+ sub_obj = NULL;
+ (void)named_config_get(maps, "dnsrps-enable", &sub_obj);
+ if (sub_obj != NULL) {
+ dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+ }
+ sub_obj = cfg_tuple_get(rpz_obj, "dnsrps-enable");
+ if (!cfg_obj_isvoid(sub_obj)) {
+ dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+ }
+#ifndef USE_DNSRPS
+ if (dnsrps_enabled) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "\"dnsrps-enable yes\" but"
+ " without `./configure --enable-dnsrps`");
+ return (ISC_R_FAILURE);
+ }
+#else /* ifndef USE_DNSRPS */
+ if (dnsrps_enabled) {
+ if (librpz == NULL) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "\"dnsrps-enable yes\" but %s",
+ librpz_lib_open_emsg.c);
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * Generate the DNS Response Policy Service
+ * configuration string.
+ */
+ result = conf_dnsrps(view, maps, nsip_enabled, nsdname_enabled,
+ &nsip_on, &nsdname_on, &rps_cstr,
+ &rps_cstr_size, rpz_obj, zone_element);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+#endif /* ifndef USE_DNSRPS */
+
+ result = dns_rpz_new_zones(view->mctx, named_g_taskmgr,
+ named_g_timermgr, rps_cstr, rps_cstr_size,
+ &view->rpzs);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ zones = view->rpzs;
+
+ zones->p.nsip_on = nsip_on;
+ zones->p.nsdname_on = nsdname_on;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
+ if (!cfg_obj_isvoid(sub_obj) && !cfg_obj_asboolean(sub_obj)) {
+ recursive_only_default = false;
+ } else {
+ recursive_only_default = true;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "add-soa");
+ if (!cfg_obj_isvoid(sub_obj) && !cfg_obj_asboolean(sub_obj)) {
+ add_soa_default = false;
+ } else {
+ add_soa_default = true;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec");
+ if (!cfg_obj_isvoid(sub_obj) && cfg_obj_asboolean(sub_obj)) {
+ zones->p.break_dnssec = true;
+ } else {
+ zones->p.break_dnssec = false;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+ if (cfg_obj_isduration(sub_obj)) {
+ ttl_default = cfg_obj_asduration(sub_obj);
+ } else {
+ ttl_default = DNS_RPZ_MAX_TTL_DEFAULT;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "min-update-interval");
+ if (cfg_obj_isduration(sub_obj)) {
+ minupdateinterval_default = cfg_obj_asduration(sub_obj);
+ } else {
+ minupdateinterval_default = DNS_RPZ_MINUPDATEINTERVAL_DEFAULT;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots");
+ if (cfg_obj_isuint32(sub_obj)) {
+ zones->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1;
+ } else {
+ zones->p.min_ns_labels = 2;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) {
+ zones->p.qname_wait_recurse = true;
+ } else {
+ zones->p.qname_wait_recurse = false;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "nsdname-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) {
+ zones->p.nsdname_wait_recurse = true;
+ } else {
+ zones->p.nsdname_wait_recurse = false;
+ }
+
+ sub_obj = cfg_tuple_get(rpz_obj, "nsip-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) {
+ zones->p.nsip_wait_recurse = true;
+ } else {
+ zones->p.nsip_wait_recurse = false;
+ }
+
+ if (pview != NULL) {
+ old = pview->rpzs;
+ } else {
+ result = dns_viewlist_find(&named_g_server->viewlist,
+ view->name, view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS) {
+ pview_must_detach = true;
+ old = pview->rpzs;
+ } else {
+ old = NULL;
+ }
+ }
+
+ if (old == NULL) {
+ *old_rpz_okp = false;
+ } else {
+ *old_rpz_okp = true;
+ }
+
+ for (i = 0; zone_element != NULL;
+ ++i, zone_element = cfg_list_next(zone_element))
+ {
+ INSIST(!*old_rpz_okp || old != NULL);
+ if (*old_rpz_okp && i < old->p.num_zones) {
+ old_zone = old->zones[i];
+ } else {
+ *old_rpz_okp = false;
+ old_zone = NULL;
+ }
+ result = configure_rpz_zone(
+ view, zone_element, recursive_only_default,
+ add_soa_default, ttl_default, minupdateinterval_default,
+ old_zone, old_rpz_okp);
+ if (result != ISC_R_SUCCESS) {
+ if (pview_must_detach) {
+ dns_view_detach(&pview);
+ }
+ return (result);
+ }
+ }
+
+ /*
+ * If this is a reloading and the parameters and list of policy
+ * zones are unchanged, then use the same policy data.
+ * Data for individual zones that must be reloaded will be merged.
+ */
+ if (*old_rpz_okp) {
+ if (old != NULL &&
+ memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0)
+ {
+ *old_rpz_okp = false;
+ } else if ((old == NULL || old->rps_cstr == NULL) !=
+ (zones->rps_cstr == NULL))
+ {
+ *old_rpz_okp = false;
+ } else if (old != NULL && zones->rps_cstr != NULL &&
+ strcmp(old->rps_cstr, zones->rps_cstr) != 0)
+ {
+ *old_rpz_okp = false;
+ }
+ }
+
+ if (*old_rpz_okp) {
+ dns_rpz_shutdown_rpzs(view->rpzs);
+ dns_rpz_detach_rpzs(&view->rpzs);
+ dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
+ dns_rpz_detach_rpzs(&pview->rpzs);
+ } else if (old != NULL && pview != NULL) {
+ ++pview->rpzs->rpz_ver;
+ view->rpzs->rpz_ver = pview->rpzs->rpz_ver;
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_DEBUG_LEVEL1,
+ "updated RPZ policy: version %d",
+ view->rpzs->rpz_ver);
+ }
+
+ if (pview_must_detach) {
+ dns_view_detach(&pview);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+ catz_chgzone_event_t *ev = (catz_chgzone_event_t *)event0;
+ isc_result_t result;
+ dns_forwarders_t *dnsforwarders = NULL;
+ dns_name_t *name = NULL;
+ isc_buffer_t namebuf;
+ isc_buffer_t *confbuf;
+ char nameb[DNS_NAME_FORMATSIZE];
+ const cfg_obj_t *zlist = NULL;
+ cfg_obj_t *zoneconf = NULL;
+ cfg_obj_t *zoneobj = NULL;
+ ns_cfgctx_t *cfg;
+ dns_zone_t *zone = NULL;
+
+ cfg = (ns_cfgctx_t *)ev->view->new_zone_config;
+ if (cfg == NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: allow-new-zones statement missing from "
+ "config; cannot add zone from the catalog");
+ goto cleanup;
+ }
+
+ name = dns_catz_entry_getname(ev->entry);
+
+ isc_buffer_init(&namebuf, nameb, DNS_NAME_FORMATSIZE);
+ dns_name_totext(name, true, &namebuf);
+ isc_buffer_putuint8(&namebuf, 0);
+
+ result = dns_fwdtable_find(ev->view->fwdtable, name, NULL,
+ &dnsforwarders);
+ if (result == ISC_R_SUCCESS &&
+ dnsforwarders->fwdpolicy == dns_fwdpolicy_only)
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_addmodzone_taskaction: "
+ "zone '%s' will not be processed because of the "
+ "explicitly configured forwarding for that zone",
+ nameb);
+ goto cleanup;
+ }
+
+ result = dns_zt_find(ev->view->zonetable, name, 0, NULL, &zone);
+
+ if (ev->mod) {
+ dns_catz_zone_t *parentcatz;
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: error \"%s\" while trying to "
+ "modify zone '%s'",
+ isc_result_totext(result), nameb);
+ goto cleanup;
+ }
+
+ if (!dns_zone_getadded(zone)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_addmodzone_taskaction: "
+ "zone '%s' is not a dynamically "
+ "added zone",
+ nameb);
+ goto cleanup;
+ }
+
+ parentcatz = dns_zone_get_parentcatz(zone);
+
+ if (parentcatz == NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_addmodzone_taskaction: "
+ "zone '%s' exists and is not added by "
+ "a catalog zone, so won't be modified",
+ nameb);
+ goto cleanup;
+ }
+ if (parentcatz != ev->origin) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_addmodzone_taskaction: "
+ "zone '%s' exists in multiple "
+ "catalog zones",
+ nameb);
+ goto cleanup;
+ }
+
+ dns_zone_detach(&zone);
+ } else {
+ /* Zone shouldn't already exist when adding */
+ if (result == ISC_R_SUCCESS) {
+ if (dns_zone_get_parentcatz(zone) == NULL) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: "
+ "catz_addmodzone_taskaction: "
+ "zone '%s' will not be added "
+ "because it is an explicitly "
+ "configured zone",
+ nameb);
+ } else {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: "
+ "catz_addmodzone_taskaction: "
+ "zone '%s' will not be added "
+ "because another catalog zone "
+ "already contains an entry with "
+ "that zone",
+ nameb);
+ }
+ goto cleanup;
+ } else if (result != ISC_R_NOTFOUND &&
+ result != DNS_R_PARTIALMATCH)
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: error \"%s\" while trying to "
+ "add zone '%s'",
+ isc_result_totext(result), nameb);
+ goto cleanup;
+ } else { /* this can happen in case of DNS_R_PARTIALMATCH */
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ }
+ }
+ RUNTIME_CHECK(zone == NULL);
+ /* Create a config for new zone */
+ confbuf = NULL;
+ result = dns_catz_generate_zonecfg(ev->origin, ev->entry, &confbuf);
+ if (result == ISC_R_SUCCESS) {
+ cfg_parser_reset(cfg->add_parser);
+ result = cfg_parse_buffer(cfg->add_parser, confbuf, "catz", 0,
+ &cfg_type_addzoneconf, 0, &zoneconf);
+ isc_buffer_free(&confbuf);
+ }
+ /*
+ * Fail if either dns_catz_generate_zonecfg() or cfg_parse_buffer3()
+ * failed.
+ */
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: error \"%s\" while trying to generate "
+ "config for zone '%s'",
+ isc_result_totext(result), nameb);
+ goto cleanup;
+ }
+ CHECK(cfg_map_get(zoneconf, "zone", &zlist));
+ if (!cfg_obj_islist(zlist)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* For now we only support adding one zone at a time */
+ zoneobj = cfg_listelt_value(cfg_list_first(zlist));
+
+ /* Mark view unfrozen so that zone can be added */
+
+ result = isc_task_beginexclusive(task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_view_thaw(ev->view);
+ result = configure_zone(
+ cfg->config, zoneobj, cfg->vconfig, ev->cbd->server->mctx,
+ ev->view, &ev->cbd->server->viewlist,
+ &ev->cbd->server->kasplist, cfg->actx, true, false, ev->mod);
+ dns_view_freeze(ev->view);
+ isc_task_endexclusive(task);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: failed to configure zone '%s' - %d", nameb,
+ result);
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ CHECK(dns_zt_find(ev->view->zonetable, name, 0, NULL, &zone));
+
+ /*
+ * Load the zone from the master file. If this fails, we'll
+ * need to undo the configuration we've done already.
+ */
+ result = dns_zone_load(zone, true);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: dns_zone_load() failed "
+ "with %s; reverting.",
+ isc_result_totext(result));
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(ev->view->zonetable, zone);
+ goto cleanup;
+ }
+
+ /* Flag the zone as having been added at runtime */
+ dns_zone_setadded(zone, true);
+ dns_zone_set_parentcatz(zone, ev->origin);
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(cfg->add_parser, &zoneconf);
+ }
+ dns_catz_entry_detach(ev->origin, &ev->entry);
+ dns_catz_detach_catz(&ev->origin);
+ dns_view_detach(&ev->view);
+ isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static void
+catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+ catz_chgzone_event_t *ev = (catz_chgzone_event_t *)event0;
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_db_t *dbp = NULL;
+ char cname[DNS_NAME_FORMATSIZE];
+ const char *file;
+
+ result = isc_task_beginexclusive(task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_name_format(dns_catz_entry_getname(ev->entry), cname,
+ DNS_NAME_FORMATSIZE);
+ result = dns_zt_find(ev->view->zonetable,
+ dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' not found",
+ cname);
+ goto cleanup;
+ }
+
+ if (!dns_zone_getadded(zone)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' is not a dynamically added zone",
+ cname);
+ goto cleanup;
+ }
+
+ if (dns_zone_get_parentcatz(zone) != ev->origin) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: zone "
+ "'%s' exists in multiple catalog zones",
+ cname);
+ goto cleanup;
+ }
+
+ /* Stop answering for this zone */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ CHECK(dns_zt_unmount(ev->view->zonetable, zone));
+ file = dns_zone_getfile(zone);
+ if (file != NULL) {
+ isc_file_remove(file);
+ file = dns_zone_getjournal(zone);
+ if (file != NULL) {
+ isc_file_remove(file);
+ }
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' deleted",
+ cname);
+cleanup:
+ isc_task_endexclusive(task);
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ dns_catz_entry_detach(ev->origin, &ev->entry);
+ dns_catz_detach_catz(&ev->origin);
+ dns_view_detach(&ev->view);
+ isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static isc_result_t
+catz_create_chg_task(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+ dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata,
+ isc_eventtype_t type) {
+ catz_chgzone_event_t *event = NULL;
+ isc_task_t *task = NULL;
+ isc_result_t result;
+ isc_taskaction_t action = NULL;
+
+ result = isc_taskmgr_excltask(taskmgr, &task);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ switch (type) {
+ case DNS_EVENT_CATZADDZONE:
+ case DNS_EVENT_CATZMODZONE:
+ action = catz_addmodzone_taskaction;
+ break;
+ case DNS_EVENT_CATZDELZONE:
+ action = catz_delzone_taskaction;
+ break;
+ default:
+ REQUIRE(0);
+ UNREACHABLE();
+ }
+
+ event = (catz_chgzone_event_t *)isc_event_allocate(
+ view->mctx, origin, type, action, NULL, sizeof(*event));
+
+ event->cbd = (catz_cb_data_t *)udata;
+ event->entry = NULL;
+ event->origin = NULL;
+ event->view = NULL;
+ event->mod = (type == DNS_EVENT_CATZMODZONE);
+
+ dns_catz_entry_attach(entry, &event->entry);
+ dns_catz_attach_catz(origin, &event->origin);
+ dns_view_attach(view, &event->view);
+
+ isc_task_send(task, ISC_EVENT_PTR(&event));
+ isc_task_detach(&task);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_addzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view,
+ isc_taskmgr_t *taskmgr, void *udata) {
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZADDZONE));
+}
+
+static isc_result_t
+catz_delzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view,
+ isc_taskmgr_t *taskmgr, void *udata) {
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZDELZONE));
+}
+
+static isc_result_t
+catz_modzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view,
+ isc_taskmgr_t *taskmgr, void *udata) {
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZMODZONE));
+}
+
+static isc_result_t
+configure_catz_zone(dns_view_t *view, dns_view_t *pview,
+ const cfg_obj_t *config, const cfg_listelt_t *element) {
+ const cfg_obj_t *catz_obj, *obj;
+ dns_catz_zone_t *zone = NULL;
+ const char *str;
+ isc_result_t result;
+ dns_name_t origin;
+ dns_catz_options_t *opts;
+
+ dns_name_init(&origin, NULL);
+ catz_obj = cfg_listelt_value(element);
+
+ str = cfg_obj_asstring(cfg_tuple_get(catz_obj, "zone name"));
+
+ result = dns_name_fromstring(&origin, str, DNS_NAME_DOWNCASE,
+ view->mctx);
+ if (result == ISC_R_SUCCESS && dns_name_equal(&origin, dns_rootname)) {
+ result = DNS_R_EMPTYLABEL;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: invalid zone name '%s'", str);
+ goto cleanup;
+ }
+
+ result = dns_catz_add_zone(view->catzs, &origin, &zone);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+ cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: unable to create catalog zone '%s', "
+ "error %s",
+ str, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ if (result == ISC_R_EXISTS) {
+ isc_ht_iter_t *it = NULL;
+
+ RUNTIME_CHECK(pview != NULL);
+
+ /*
+ * xxxwpk todo: reconfigure the zone!!!!
+ */
+ cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: catalog zone '%s' will not be reconfigured",
+ str);
+ /*
+ * We have to walk through all the member zones and attach
+ * them to current view
+ */
+ dns_catz_get_iterator(zone, &it);
+
+ for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
+ result = isc_ht_iter_next(it))
+ {
+ dns_name_t *name = NULL;
+ dns_zone_t *dnszone = NULL;
+ dns_catz_entry_t *entry = NULL;
+ isc_result_t tresult;
+
+ isc_ht_iter_current(it, (void **)&entry);
+ name = dns_catz_entry_getname(entry);
+
+ tresult = dns_view_findzone(pview, name, &dnszone);
+ if (tresult != ISC_R_SUCCESS) {
+ continue;
+ }
+
+ dns_zone_setview(dnszone, view);
+ dns_view_addzone(view, dnszone);
+
+ /*
+ * The dns_view_findzone() call above increments the
+ * zone's reference count, which we need to decrement
+ * back. However, as dns_zone_detach() sets the
+ * supplied pointer to NULL, calling it is deferred
+ * until the dnszone variable is no longer used.
+ */
+ dns_zone_detach(&dnszone);
+ }
+
+ isc_ht_iter_destroy(&it);
+
+ result = ISC_R_SUCCESS;
+ }
+
+ dns_catz_zone_resetdefoptions(zone);
+ opts = dns_catz_zone_getdefoptions(zone);
+
+ obj = cfg_tuple_get(catz_obj, "default-masters");
+ if (obj == NULL || !cfg_obj_istuple(obj)) {
+ obj = cfg_tuple_get(catz_obj, "default-primaries");
+ }
+ if (obj != NULL && cfg_obj_istuple(obj)) {
+ result = named_config_getipandkeylist(
+ config, "primaries", obj, view->mctx, &opts->masters);
+ }
+
+ obj = cfg_tuple_get(catz_obj, "in-memory");
+ if (obj != NULL && cfg_obj_isboolean(obj)) {
+ opts->in_memory = cfg_obj_asboolean(obj);
+ }
+
+ obj = cfg_tuple_get(catz_obj, "zone-directory");
+ if (!opts->in_memory && obj != NULL && cfg_obj_isstring(obj)) {
+ opts->zonedir = isc_mem_strdup(view->mctx,
+ cfg_obj_asstring(obj));
+ if (isc_file_isdirectory(opts->zonedir) != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: zone-directory '%s' "
+ "not found; zone files will not be "
+ "saved",
+ opts->zonedir);
+ opts->in_memory = true;
+ }
+ }
+
+ obj = cfg_tuple_get(catz_obj, "min-update-interval");
+ if (obj != NULL && cfg_obj_isduration(obj)) {
+ opts->min_update_interval = cfg_obj_asduration(obj);
+ }
+
+cleanup:
+ dns_name_free(&origin, view->mctx);
+
+ return (result);
+}
+
+static catz_cb_data_t ns_catz_cbdata;
+static dns_catz_zonemodmethods_t ns_catz_zonemodmethods = {
+ catz_addzone, catz_modzone, catz_delzone, &ns_catz_cbdata
+};
+
+static isc_result_t
+configure_catz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t *config,
+ const cfg_obj_t *catz_obj) {
+ const cfg_listelt_t *zone_element = NULL;
+ const dns_catz_zones_t *old = NULL;
+ bool pview_must_detach = false;
+ isc_result_t result;
+
+ /* xxxwpk TODO do it cleaner, once, somewhere */
+ ns_catz_cbdata.server = named_g_server;
+
+ zone_element = cfg_list_first(cfg_tuple_get(catz_obj, "zone list"));
+ if (zone_element == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ CHECK(dns_catz_new_zones(view->mctx, named_g_taskmgr, named_g_timermgr,
+ &view->catzs, &ns_catz_zonemodmethods));
+
+ if (pview != NULL) {
+ old = pview->catzs;
+ } else {
+ result = dns_viewlist_find(&named_g_server->viewlist,
+ view->name, view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS) {
+ pview_must_detach = true;
+ old = pview->catzs;
+ }
+ }
+
+ if (old != NULL) {
+ dns_catz_shutdown_catzs(view->catzs);
+ dns_catz_detach_catzs(&view->catzs);
+ dns_catz_attach_catzs(pview->catzs, &view->catzs);
+ dns_catz_detach_catzs(&pview->catzs);
+ dns_catz_prereconfig(view->catzs);
+ }
+
+ while (zone_element != NULL) {
+ CHECK(configure_catz_zone(view, pview, config, zone_element));
+ zone_element = cfg_list_next(zone_element);
+ }
+
+ if (old != NULL) {
+ dns_catz_postreconfig(view->catzs);
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (pview_must_detach) {
+ dns_view_detach(&pview);
+ }
+
+ return (result);
+}
+
+#define CHECK_RRL(cond, pat, val1, val2) \
+ do { \
+ if (!(cond)) { \
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, pat, \
+ val1, val2); \
+ result = ISC_R_RANGE; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECK_RRL_RATE(rate, def, max_rate, name) \
+ do { \
+ obj = NULL; \
+ rrl->rate.str = name; \
+ result = cfg_map_get(map, name, &obj); \
+ if (result == ISC_R_SUCCESS) { \
+ rrl->rate.r = cfg_obj_asuint32(obj); \
+ CHECK_RRL(rrl->rate.r <= max_rate, name " %d > %d", \
+ rrl->rate.r, max_rate); \
+ } else { \
+ rrl->rate.r = def; \
+ } \
+ rrl->rate.scaled = rrl->rate.r; \
+ } while (0)
+
+static isc_result_t
+configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
+ const cfg_obj_t *obj;
+ dns_rrl_t *rrl;
+ isc_result_t result;
+ int min_entries, i, j;
+
+ /*
+ * Most DNS servers have few clients, but intentinally open
+ * recursive and authoritative servers often have many.
+ * So start with a small number of entries unless told otherwise
+ * to reduce cold-start costs.
+ */
+ min_entries = 500;
+ obj = NULL;
+ result = cfg_map_get(map, "min-table-size", &obj);
+ if (result == ISC_R_SUCCESS) {
+ min_entries = cfg_obj_asuint32(obj);
+ if (min_entries < 1) {
+ min_entries = 1;
+ }
+ }
+ result = dns_rrl_init(&rrl, view, min_entries);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ i = ISC_MAX(20000, min_entries);
+ obj = NULL;
+ result = cfg_map_get(map, "max-table-size", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= min_entries,
+ "max-table-size %d < min-table-size %d", i,
+ min_entries);
+ }
+ rrl->max_entries = i;
+
+ CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
+ "responses-per-second");
+ CHECK_RRL_RATE(referrals_per_second, rrl->responses_per_second.r,
+ DNS_RRL_MAX_RATE, "referrals-per-second");
+ CHECK_RRL_RATE(nodata_per_second, rrl->responses_per_second.r,
+ DNS_RRL_MAX_RATE, "nodata-per-second");
+ CHECK_RRL_RATE(nxdomains_per_second, rrl->responses_per_second.r,
+ DNS_RRL_MAX_RATE, "nxdomains-per-second");
+ CHECK_RRL_RATE(errors_per_second, rrl->responses_per_second.r,
+ DNS_RRL_MAX_RATE, "errors-per-second");
+
+ CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE, "all-per-second");
+
+ CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP, "slip");
+
+ i = 15;
+ obj = NULL;
+ result = cfg_map_get(map, "window", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
+ "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
+ }
+ rrl->window = i;
+
+ i = 0;
+ obj = NULL;
+ result = cfg_map_get(map, "qps-scale", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
+ }
+ rrl->qps_scale = i;
+ rrl->qps = 1.0;
+
+ i = 24;
+ obj = NULL;
+ result = cfg_map_get(map, "ipv4-prefix-length", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 8 && i <= 32,
+ "invalid 'ipv4-prefix-length %d'%s", i, "");
+ }
+ rrl->ipv4_prefixlen = i;
+ if (i == 32) {
+ rrl->ipv4_mask = 0xffffffff;
+ } else {
+ rrl->ipv4_mask = htonl(0xffffffff << (32 - i));
+ }
+
+ i = 56;
+ obj = NULL;
+ result = cfg_map_get(map, "ipv6-prefix-length", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
+ "ipv6-prefix-length %d < 16 or > %d", i,
+ DNS_RRL_MAX_PREFIX);
+ }
+ rrl->ipv6_prefixlen = i;
+ for (j = 0; j < 4; ++j) {
+ if (i <= 0) {
+ rrl->ipv6_mask[j] = 0;
+ } else if (i < 32) {
+ rrl->ipv6_mask[j] = htonl(0xffffffff << (32 - i));
+ } else {
+ rrl->ipv6_mask[j] = 0xffffffff;
+ }
+ i -= 32;
+ }
+
+ obj = NULL;
+ result = cfg_map_get(map, "exempt-clients", &obj);
+ if (result == ISC_R_SUCCESS) {
+ result = cfg_acl_fromconfig(obj, config, named_g_lctx,
+ named_g_aclconfctx, named_g_mctx, 0,
+ &rrl->exempt);
+ CHECK_RRL(result == ISC_R_SUCCESS, "invalid %s%s",
+ "address match list", "");
+ }
+
+ obj = NULL;
+ result = cfg_map_get(map, "log-only", &obj);
+ if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj)) {
+ rrl->log_only = true;
+ } else {
+ rrl->log_only = false;
+ }
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dns_rrl_view_destroy(view);
+ return (result);
+}
+
+static isc_result_t
+add_soa(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name,
+ const dns_name_t *origin, const dns_name_t *contact) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+
+ CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), 0, 28800,
+ 7200, 604800, 86400, buf, &rdata));
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.type = rdata.type;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+
+cleanup:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ return (result);
+}
+
+static isc_result_t
+add_ns(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name,
+ const dns_name_t *nsname) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_ns_t ns;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_buffer_t b;
+ unsigned char buf[DNS_NAME_MAXWIRE];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ ns.common.rdtype = dns_rdatatype_ns;
+ ns.common.rdclass = dns_db_class(db);
+ ns.mctx = NULL;
+ dns_name_init(&ns.name, NULL);
+ dns_name_clone(nsname, &ns.name);
+ CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns,
+ &ns, &b));
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.type = rdata.type;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+
+cleanup:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ return (result);
+}
+
+static isc_result_t
+create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view,
+ const cfg_obj_t *zonelist, const char **empty_dbtype,
+ int empty_dbtypec, dns_zonestat_level_t statlevel) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj;
+ const cfg_obj_t *zconfig;
+ const cfg_obj_t *zoptions;
+ const char *rbt_dbtype[4] = { "rbt" };
+ const char *sep = ": view ";
+ const char *str;
+ const char *viewname = view->name;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_fixedname_t cfixed;
+ dns_fixedname_t fixed;
+ dns_fixedname_t nsfixed;
+ dns_name_t *contact;
+ dns_name_t *ns;
+ dns_name_t *zname;
+ dns_zone_t *zone = NULL;
+ int rbt_dbtypec = 1;
+ isc_result_t result;
+ dns_namereln_t namereln;
+ int order;
+ unsigned int nlabels;
+
+ zname = dns_fixedname_initname(&fixed);
+ ns = dns_fixedname_initname(&nsfixed);
+ contact = dns_fixedname_initname(&cfixed);
+
+ /*
+ * Look for forward "zones" beneath this empty zone and if so
+ * create a custom db for the empty zone.
+ */
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ zconfig = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ CHECK(dns_name_fromstring(zname, str, 0, NULL));
+ namereln = dns_name_fullcompare(zname, name, &order, &nlabels);
+ if (namereln != dns_namereln_subdomain) {
+ continue;
+ }
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "type", &obj);
+ if (obj != NULL &&
+ strcasecmp(cfg_obj_asstring(obj), "forward") == 0)
+ {
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "forward", &obj);
+ if (obj == NULL) {
+ continue;
+ }
+ if (strcasecmp(cfg_obj_asstring(obj), "only") != 0) {
+ continue;
+ }
+ }
+ if (db == NULL) {
+ CHECK(dns_db_create(view->mctx, "rbt", name,
+ dns_dbtype_zone, view->rdclass, 0,
+ NULL, &db));
+ CHECK(dns_db_newversion(db, &version));
+ if (strcmp(empty_dbtype[2], "@") == 0) {
+ dns_name_clone(name, ns);
+ } else {
+ CHECK(dns_name_fromstring(ns, empty_dbtype[2],
+ 0, NULL));
+ }
+ CHECK(dns_name_fromstring(contact, empty_dbtype[3], 0,
+ NULL));
+ CHECK(add_soa(db, version, name, ns, contact));
+ CHECK(add_ns(db, version, name, ns));
+ }
+ CHECK(add_ns(db, version, zname, dns_rootname));
+ }
+
+ /*
+ * Is the existing zone the ok to use?
+ */
+ if (pzone != NULL) {
+ unsigned int typec;
+ const char **dbargv;
+
+ if (db != NULL) {
+ typec = rbt_dbtypec;
+ dbargv = rbt_dbtype;
+ } else {
+ typec = empty_dbtypec;
+ dbargv = empty_dbtype;
+ }
+
+ result = check_dbtype(pzone, typec, dbargv, view->mctx);
+ if (result != ISC_R_SUCCESS) {
+ pzone = NULL;
+ }
+
+ if (pzone != NULL &&
+ dns_zone_gettype(pzone) != dns_zone_primary)
+ {
+ pzone = NULL;
+ }
+ if (pzone != NULL && dns_zone_getfile(pzone) != NULL) {
+ pzone = NULL;
+ }
+ if (pzone != NULL) {
+ dns_zone_getraw(pzone, &zone);
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ pzone = NULL;
+ }
+ }
+ }
+
+ if (pzone == NULL) {
+ CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone));
+ CHECK(dns_zone_setorigin(zone, name));
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
+ if (db == NULL) {
+ dns_zone_setdbtype(zone, empty_dbtypec, empty_dbtype);
+ }
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_primary);
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ } else {
+ dns_zone_attach(pzone, &zone);
+ }
+
+ dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, false);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setautomatic(zone, true);
+ if (view->queryacl != NULL) {
+ dns_zone_setqueryacl(zone, view->queryacl);
+ } else {
+ dns_zone_clearqueryacl(zone);
+ }
+ if (view->queryonacl != NULL) {
+ dns_zone_setqueryonacl(zone, view->queryonacl);
+ } else {
+ dns_zone_clearqueryonacl(zone);
+ }
+ dns_zone_clearupdateacl(zone);
+ if (view->transferacl != NULL) {
+ dns_zone_setxfracl(zone, view->transferacl);
+ } else {
+ dns_zone_clearxfracl(zone);
+ }
+
+ CHECK(setquerystats(zone, view->mctx, statlevel));
+ if (db != NULL) {
+ dns_db_closeversion(db, &version, true);
+ CHECK(dns_zone_replacedb(zone, db, false));
+ }
+ dns_zone_setoption(zone, DNS_ZONEOPT_AUTOEMPTY, true);
+ dns_zone_setview(zone, view);
+ CHECK(dns_view_addzone(view, zone));
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ }
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_ZONELOAD,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "automatic empty zone%s%s: %s", sep, viewname, namebuf);
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, false);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+
+ INSIST(version == NULL);
+
+ return (result);
+}
+
+static isc_result_t
+create_ipv4only_zone(dns_zone_t *pzone, dns_view_t *view,
+ const dns_name_t *name, const char *type, isc_mem_t *mctx,
+ const char *server, const char *contact) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ const char *dbtype[4] = { "_builtin", NULL, "@", "." };
+ const char *sep = ": view ";
+ const char *viewname = view->name;
+ dns_zone_t *zone = NULL;
+ int dbtypec = 4;
+ isc_result_t result;
+
+ REQUIRE(type != NULL);
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ }
+
+ dbtype[1] = type;
+ if (server != NULL) {
+ dbtype[2] = server;
+ }
+ if (contact != NULL) {
+ dbtype[3] = contact;
+ }
+
+ if (pzone != NULL) {
+ result = check_dbtype(pzone, dbtypec, dbtype, view->mctx);
+ if (result != ISC_R_SUCCESS) {
+ pzone = NULL;
+ }
+ }
+
+ if (pzone == NULL) {
+ /*
+ * Create the actual zone.
+ */
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, name));
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_primary);
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ dns_zone_setdbtype(zone, dbtypec, dbtype);
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setautomatic(zone, true);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ } else {
+ dns_zone_attach(pzone, &zone);
+ }
+ if (view->queryacl != NULL) {
+ dns_zone_setqueryacl(zone, view->queryacl);
+ } else {
+ dns_zone_clearqueryacl(zone);
+ }
+ if (view->queryonacl != NULL) {
+ dns_zone_setqueryonacl(zone, view->queryonacl);
+ } else {
+ dns_zone_clearqueryonacl(zone);
+ }
+ dns_zone_setview(zone, view);
+ CHECK(dns_view_addzone(view, zone));
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "automatic ipv4only zone%s%s: %s", sep, viewname,
+ namebuf);
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ return (result);
+}
+
+#ifdef HAVE_DNSTAP
+static isc_result_t
+configure_dnstap(const cfg_obj_t **maps, dns_view_t *view) {
+ isc_result_t result;
+ const cfg_obj_t *obj, *obj2;
+ const cfg_listelt_t *element;
+ const char *dpath;
+ const cfg_obj_t *dlist = NULL;
+ dns_dtmsgtype_t dttypes = 0;
+ unsigned int i;
+ struct fstrm_iothr_options *fopt = NULL;
+
+ result = named_config_get(maps, "dnstap", &dlist);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_SUCCESS);
+ }
+
+ for (element = cfg_list_first(dlist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *str;
+ dns_dtmsgtype_t dt = 0;
+
+ obj = cfg_listelt_value(element);
+ obj2 = cfg_tuple_get(obj, "type");
+ str = cfg_obj_asstring(obj2);
+ if (strcasecmp(str, "client") == 0) {
+ dt |= DNS_DTTYPE_CQ | DNS_DTTYPE_CR;
+ } else if (strcasecmp(str, "auth") == 0) {
+ dt |= DNS_DTTYPE_AQ | DNS_DTTYPE_AR;
+ } else if (strcasecmp(str, "resolver") == 0) {
+ dt |= DNS_DTTYPE_RQ | DNS_DTTYPE_RR;
+ } else if (strcasecmp(str, "forwarder") == 0) {
+ dt |= DNS_DTTYPE_FQ | DNS_DTTYPE_FR;
+ } else if (strcasecmp(str, "update") == 0) {
+ dt |= DNS_DTTYPE_UQ | DNS_DTTYPE_UR;
+ } else if (strcasecmp(str, "all") == 0) {
+ dt |= DNS_DTTYPE_CQ | DNS_DTTYPE_CR | DNS_DTTYPE_AQ |
+ DNS_DTTYPE_AR | DNS_DTTYPE_RQ | DNS_DTTYPE_RR |
+ DNS_DTTYPE_FQ | DNS_DTTYPE_FR | DNS_DTTYPE_UQ |
+ DNS_DTTYPE_UR;
+ }
+
+ obj2 = cfg_tuple_get(obj, "mode");
+ if (obj2 == NULL || cfg_obj_isvoid(obj2)) {
+ dttypes |= dt;
+ continue;
+ }
+
+ str = cfg_obj_asstring(obj2);
+ if (strcasecmp(str, "query") == 0) {
+ dt &= ~DNS_DTTYPE_RESPONSE;
+ } else if (strcasecmp(str, "response") == 0) {
+ dt &= ~DNS_DTTYPE_QUERY;
+ }
+
+ dttypes |= dt;
+ }
+
+ if (named_g_server->dtenv == NULL && dttypes != 0) {
+ dns_dtmode_t dmode;
+ uint64_t max_size = 0;
+ uint32_t rolls = 0;
+ isc_log_rollsuffix_t suffix = isc_log_rollsuffix_increment;
+
+ obj = NULL;
+ CHECKM(named_config_get(maps, "dnstap-output", &obj),
+ "'dnstap-output' must be set if 'dnstap' is set");
+
+ obj2 = cfg_tuple_get(obj, "mode");
+ if (obj2 == NULL) {
+ CHECKM(ISC_R_FAILURE, "dnstap-output mode not found");
+ }
+ if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0) {
+ dmode = dns_dtmode_file;
+ } else {
+ dmode = dns_dtmode_unix;
+ }
+
+ obj2 = cfg_tuple_get(obj, "path");
+ if (obj2 == NULL) {
+ CHECKM(ISC_R_FAILURE, "dnstap-output path not found");
+ }
+
+ dpath = cfg_obj_asstring(obj2);
+
+ obj2 = cfg_tuple_get(obj, "size");
+ if (obj2 != NULL && cfg_obj_isuint64(obj2)) {
+ max_size = cfg_obj_asuint64(obj2);
+ if (max_size > SIZE_MAX) {
+ cfg_obj_log(obj2, named_g_lctx, ISC_LOG_WARNING,
+ "'dnstap-output size "
+ "%" PRIu64 "' "
+ "is too large for this "
+ "system; reducing to %lu",
+ max_size, (unsigned long)SIZE_MAX);
+ max_size = SIZE_MAX;
+ }
+ }
+
+ obj2 = cfg_tuple_get(obj, "versions");
+ if (obj2 != NULL && cfg_obj_isuint32(obj2)) {
+ rolls = cfg_obj_asuint32(obj2);
+ } else {
+ rolls = ISC_LOG_ROLLINFINITE;
+ }
+
+ obj2 = cfg_tuple_get(obj, "suffix");
+ if (obj2 != NULL && cfg_obj_isstring(obj2) &&
+ strcasecmp(cfg_obj_asstring(obj2), "timestamp") == 0)
+ {
+ suffix = isc_log_rollsuffix_timestamp;
+ }
+
+ fopt = fstrm_iothr_options_init();
+ /*
+ * Both network threads and worker threads may log dnstap data.
+ */
+ fstrm_iothr_options_set_num_input_queues(fopt,
+ 2 * named_g_cpus);
+ fstrm_iothr_options_set_queue_model(
+ fopt, FSTRM_IOTHR_QUEUE_MODEL_MPSC);
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-buffer-hint", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_buffer_hint(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-flush-timeout",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_flush_timeout(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-input-queue-size",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_input_queue_size(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(
+ maps, "fstrm-set-output-notify-threshold", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_queue_notify_threshold(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-output-queue-model",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ if (strcasecmp(cfg_obj_asstring(obj), "spsc") == 0) {
+ i = FSTRM_IOTHR_QUEUE_MODEL_SPSC;
+ } else {
+ i = FSTRM_IOTHR_QUEUE_MODEL_MPSC;
+ }
+ fstrm_iothr_options_set_queue_model(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-output-queue-size",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_output_queue_size(fopt, i);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fstrm-set-reopen-interval",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asduration(obj);
+ fstrm_iothr_options_set_reopen_interval(fopt, i);
+ }
+
+ CHECKM(dns_dt_create(named_g_mctx, dmode, dpath, &fopt,
+ named_g_server->task,
+ &named_g_server->dtenv),
+ "unable to create dnstap environment");
+
+ CHECKM(dns_dt_setupfile(named_g_server->dtenv, max_size, rolls,
+ suffix),
+ "unable to set up dnstap logfile");
+ }
+
+ if (named_g_server->dtenv == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "dnstap-version", &obj);
+ if (result != ISC_R_SUCCESS) {
+ /* not specified; use the product and version */
+ dns_dt_setversion(named_g_server->dtenv, PACKAGE_STRING);
+ } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ /* Quoted string */
+ dns_dt_setversion(named_g_server->dtenv, cfg_obj_asstring(obj));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "dnstap-identity", &obj);
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ /* "hostname" is interpreted as boolean true */
+ char buf[256];
+ if (gethostname(buf, sizeof(buf)) == 0) {
+ dns_dt_setidentity(named_g_server->dtenv, buf);
+ }
+ } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ /* Quoted string */
+ dns_dt_setidentity(named_g_server->dtenv,
+ cfg_obj_asstring(obj));
+ }
+
+ dns_dt_attach(named_g_server->dtenv, &view->dtenv);
+ view->dttypes = dttypes;
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (fopt != NULL) {
+ fstrm_iothr_options_destroy(&fopt);
+ }
+
+ return (result);
+}
+#endif /* HAVE_DNSTAP */
+
+static isc_result_t
+create_mapped_acl(void) {
+ isc_result_t result;
+ dns_acl_t *acl = NULL;
+ struct in6_addr in6 = IN6ADDR_V4MAPPED_INIT;
+ isc_netaddr_t addr;
+
+ isc_netaddr_fromin6(&addr, &in6);
+
+ result = dns_acl_create(named_g_mctx, 1, &acl);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = dns_iptable_addprefix(acl->iptable, &addr, 96, true);
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_attach(acl, &named_g_mapped);
+ }
+ dns_acl_detach(&acl);
+ return (result);
+}
+
+/*%
+ * A callback for the cfg_pluginlist_foreach() call in configure_view() below.
+ * If registering any plugin fails, registering subsequent ones is not
+ * attempted.
+ */
+static isc_result_t
+register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
+ const char *plugin_path, const char *parameters,
+ void *callback_data) {
+ dns_view_t *view = callback_data;
+ char full_path[PATH_MAX];
+ isc_result_t result;
+
+ result = ns_plugin_expandpath(plugin_path, full_path,
+ sizeof(full_path));
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "%s: plugin configuration failed: "
+ "unable to get full plugin path: %s",
+ plugin_path, isc_result_totext(result));
+ return (result);
+ }
+
+ result = ns_plugin_register(full_path, parameters, config,
+ cfg_obj_file(obj), cfg_obj_line(obj),
+ named_g_mctx, named_g_lctx,
+ named_g_aclconfctx, view);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "%s: plugin configuration failed: %s", full_path,
+ isc_result_totext(result));
+ }
+
+ return (result);
+}
+
+/*
+ * Determine if a minimal-sized cache can be used for a given view, according
+ * to 'maps' (implicit defaults, global options, view options) and 'optionmaps'
+ * (global options, view options). This is only allowed for views which have
+ * recursion disabled and do not have "max-cache-size" set explicitly. Using
+ * minimal-sized caches prevents a situation in which all explicitly configured
+ * and built-in views inherit the default "max-cache-size 90%;" setting, which
+ * could lead to memory exhaustion with multiple views configured.
+ */
+static bool
+minimal_cache_allowed(const cfg_obj_t *maps[4],
+ const cfg_obj_t *optionmaps[3]) {
+ const cfg_obj_t *obj;
+
+ /*
+ * Do not use a minimal-sized cache for a view with recursion enabled.
+ */
+ obj = NULL;
+ (void)named_config_get(maps, "recursion", &obj);
+ INSIST(obj != NULL);
+ if (cfg_obj_asboolean(obj)) {
+ return (false);
+ }
+
+ /*
+ * Do not use a minimal-sized cache if a specific size was requested.
+ */
+ obj = NULL;
+ (void)named_config_get(optionmaps, "max-cache-size", &obj);
+ if (obj != NULL) {
+ return (false);
+ }
+
+ return (true);
+}
+
+static const char *const response_synonyms[] = { "response", NULL };
+
+static const dns_name_t *
+algorithm_name(unsigned int alg) {
+ switch (alg) {
+ case DST_ALG_HMACMD5:
+ return (dns_tsig_hmacmd5_name);
+ case DST_ALG_HMACSHA1:
+ return (dns_tsig_hmacsha1_name);
+ case DST_ALG_HMACSHA224:
+ return (dns_tsig_hmacsha224_name);
+ case DST_ALG_HMACSHA256:
+ return (dns_tsig_hmacsha256_name);
+ case DST_ALG_HMACSHA384:
+ return (dns_tsig_hmacsha384_name);
+ case DST_ALG_HMACSHA512:
+ return (dns_tsig_hmacsha512_name);
+ case DST_ALG_GSSAPI:
+ return (dns_tsig_gssapi_name);
+ default:
+ UNREACHABLE();
+ }
+}
+
+/*
+ * Configure 'view' according to 'vconfig', taking defaults from
+ * 'config' where values are missing in 'vconfig'.
+ *
+ * When configuring the default view, 'vconfig' will be NULL and the
+ * global defaults in 'config' used exclusively.
+ */
+static isc_result_t
+configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
+ cfg_obj_t *vconfig, named_cachelist_t *cachelist,
+ dns_kasplist_t *kasplist, const cfg_obj_t *bindkeys,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx, bool need_hints) {
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *cfgmaps[3];
+ const cfg_obj_t *optionmaps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *voptions = NULL;
+ const cfg_obj_t *forwardtype;
+ const cfg_obj_t *forwarders;
+ const cfg_obj_t *alternates;
+ const cfg_obj_t *zonelist;
+ const cfg_obj_t *dlzlist;
+ const cfg_obj_t *dlz;
+ const cfg_obj_t *prefetch_trigger;
+ const cfg_obj_t *prefetch_eligible;
+ unsigned int dlzargc;
+ char **dlzargv;
+ const cfg_obj_t *dyndb_list, *plugin_list;
+ const cfg_obj_t *disabled;
+ const cfg_obj_t *obj, *obj2;
+ const cfg_listelt_t *element = NULL;
+ const cfg_listelt_t *zone_element_latest = NULL;
+ in_port_t port;
+ dns_cache_t *cache = NULL;
+ isc_result_t result;
+ size_t max_cache_size;
+ uint32_t max_cache_size_percent = 0;
+ size_t max_adb_size;
+ uint32_t lame_ttl, fail_ttl;
+ uint32_t max_stale_ttl = 0;
+ uint32_t stale_refresh_time = 0;
+ dns_tsig_keyring_t *ring = NULL;
+ dns_transport_list_t *transports = NULL;
+ dns_view_t *pview = NULL; /* Production view */
+ isc_mem_t *cmctx = NULL, *hmctx = NULL;
+ dns_dispatch_t *dispatch4 = NULL;
+ dns_dispatch_t *dispatch6 = NULL;
+ bool rpz_configured = false;
+ bool catz_configured = false;
+ bool shared_cache = false;
+ int i = 0, j = 0, k = 0;
+ const char *str;
+ const char *cachename = NULL;
+ dns_order_t *order = NULL;
+ uint32_t udpsize;
+ uint32_t maxbits;
+ unsigned int resopts = 0;
+ dns_zone_t *zone = NULL;
+ uint32_t max_clients_per_query;
+ bool empty_zones_enable;
+ const cfg_obj_t *disablelist = NULL;
+ isc_stats_t *resstats = NULL;
+ dns_stats_t *resquerystats = NULL;
+ bool auto_root = false;
+ named_cache_t *nsc;
+ bool zero_no_soattl;
+ dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
+ unsigned int query_timeout, ndisp;
+ bool old_rpz_ok = false;
+ dns_dyndbctx_t *dctx = NULL;
+ unsigned int resolver_param;
+ dns_ntatable_t *ntatable = NULL;
+ const char *qminmode = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (config != NULL) {
+ (void)cfg_map_get(config, "options", &options);
+ }
+
+ /*
+ * maps: view options, options, defaults
+ * cfgmaps: view options, config
+ * optionmaps: view options, options
+ */
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ maps[i++] = voptions;
+ optionmaps[j++] = voptions;
+ cfgmaps[k++] = voptions;
+ }
+ if (options != NULL) {
+ maps[i++] = options;
+ optionmaps[j++] = options;
+ }
+
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+ optionmaps[j] = NULL;
+ if (config != NULL) {
+ cfgmaps[k++] = config;
+ }
+ cfgmaps[k] = NULL;
+
+ /*
+ * Set the view's port number for outgoing queries.
+ */
+ CHECKM(named_config_getport(config, "port", &port), "port");
+ dns_view_setdstport(view, port);
+
+ /*
+ * Make the list of response policy zone names for a view that
+ * is used for real lookups and so cares about hints.
+ */
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in && need_hints &&
+ named_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS)
+ {
+ CHECK(configure_rpz(view, NULL, maps, obj, &old_rpz_ok));
+ rpz_configured = true;
+ }
+
+ obj = NULL;
+ if (view->rdclass != dns_rdataclass_in && need_hints &&
+ named_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS)
+ {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "'catalog-zones' option is only supported "
+ "for views with class IN");
+ }
+
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in && need_hints &&
+ named_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS)
+ {
+ CHECK(configure_catz(view, NULL, config, obj));
+ catz_configured = true;
+ }
+
+ /*
+ * Configure the zones.
+ */
+ zonelist = NULL;
+ if (voptions != NULL) {
+ (void)cfg_map_get(voptions, "zone", &zonelist);
+ } else {
+ (void)cfg_map_get(config, "zone", &zonelist);
+ }
+
+ /*
+ * Load zone configuration
+ */
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
+ viewlist, kasplist, actx, false,
+ old_rpz_ok, false));
+ zone_element_latest = element;
+ }
+
+ /*
+ * Check that a primary or secondary zone was found for each
+ * zone named in the response policy statement, unless we are
+ * using RPZ service interface.
+ */
+ if (view->rpzs != NULL && !view->rpzs->p.dnsrps_enabled) {
+ dns_rpz_num_t n;
+
+ for (n = 0; n < view->rpzs->p.num_zones; ++n) {
+ if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&view->rpzs->zones[n]->origin,
+ namebuf, sizeof(namebuf));
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ DNS_RPZ_ERROR_LEVEL,
+ "rpz '%s' is not a primary or a "
+ "secondary zone",
+ namebuf);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+ }
+ }
+
+ /*
+ * If we're allowing added zones, then load zone configuration
+ * from the newzone file for zones that were added during previous
+ * runs.
+ */
+ CHECK(configure_newzones(view, config, vconfig, mctx, actx));
+
+ /*
+ * Create Dynamically Loadable Zone driver.
+ */
+ dlzlist = NULL;
+ if (voptions != NULL) {
+ (void)cfg_map_get(voptions, "dlz", &dlzlist);
+ } else {
+ (void)cfg_map_get(config, "dlz", &dlzlist);
+ }
+
+ for (element = cfg_list_first(dlzlist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ dlz = cfg_listelt_value(element);
+
+ obj = NULL;
+ (void)cfg_map_get(dlz, "database", &obj);
+ if (obj != NULL) {
+ dns_dlzdb_t *dlzdb = NULL;
+ const cfg_obj_t *name, *search = NULL;
+ char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+
+ if (s == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = isc_commandline_strtoargv(mctx, s, &dlzargc,
+ &dlzargv, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_free(mctx, s);
+ goto cleanup;
+ }
+
+ name = cfg_map_getname(dlz);
+ result = dns_dlzcreate(mctx, cfg_obj_asstring(name),
+ dlzargv[0], dlzargc, dlzargv,
+ &dlzdb);
+ isc_mem_free(mctx, s);
+ isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /*
+ * If the DLZ backend supports configuration,
+ * and is searchable, then call its configure
+ * method now. If not searchable, we'll take
+ * care of it when we process the zone statement.
+ */
+ (void)cfg_map_get(dlz, "search", &search);
+ if (search == NULL || cfg_obj_asboolean(search)) {
+ dlzdb->search = true;
+ result = dns_dlzconfigure(
+ view, dlzdb, dlzconfigure_callback);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(view->dlz_searched, dlzdb,
+ link);
+ } else {
+ dlzdb->search = false;
+ ISC_LIST_APPEND(view->dlz_unsearched, dlzdb,
+ link);
+ }
+ }
+ }
+
+ /*
+ * Obtain configuration parameters that affect the decision of whether
+ * we can reuse/share an existing cache.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "max-cache-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ /*
+ * If "-T maxcachesize=..." is in effect, it overrides any other
+ * "max-cache-size" setting found in configuration, either implicit or
+ * explicit. For simplicity, the value passed to that command line
+ * option is always treated as the number of bytes to set
+ * "max-cache-size" to.
+ */
+ if (named_g_maxcachesize != 0) {
+ max_cache_size = named_g_maxcachesize;
+ } else if (minimal_cache_allowed(maps, optionmaps)) {
+ /*
+ * dns_cache_setcachesize() will adjust this to the smallest
+ * allowed value.
+ */
+ max_cache_size = 1;
+ } else if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ max_cache_size = 0;
+ } else if (cfg_obj_ispercentage(obj)) {
+ max_cache_size = SIZE_AS_PERCENT;
+ max_cache_size_percent = cfg_obj_aspercentage(obj);
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > SIZE_MAX) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "'max-cache-size "
+ "%" PRIu64 "' "
+ "is too large for this "
+ "system; reducing to %lu",
+ value, (unsigned long)SIZE_MAX);
+ value = SIZE_MAX;
+ }
+ max_cache_size = (size_t)value;
+ }
+
+ if (max_cache_size == SIZE_AS_PERCENT) {
+ uint64_t totalphys = isc_meminfo_totalphys();
+
+ max_cache_size =
+ (size_t)(totalphys * max_cache_size_percent / 100);
+ if (totalphys == 0) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "Unable to determine amount of physical "
+ "memory, setting 'max-cache-size' to "
+ "unlimited");
+ } else {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO,
+ "'max-cache-size %d%%' "
+ "- setting to %" PRIu64 "MB "
+ "(out of %" PRIu64 "MB)",
+ max_cache_size_percent,
+ (uint64_t)(max_cache_size / (1024 * 1024)),
+ totalphys / (1024 * 1024));
+ }
+ }
+
+ /* Check-names. */
+ obj = NULL;
+ result = named_checknames_get(maps, response_synonyms, &obj);
+ INSIST(result == ISC_R_SUCCESS);
+
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "fail") == 0) {
+ resopts |= DNS_RESOLVER_CHECKNAMES |
+ DNS_RESOLVER_CHECKNAMESFAIL;
+ view->checknames = true;
+ } else if (strcasecmp(str, "warn") == 0) {
+ resopts |= DNS_RESOLVER_CHECKNAMES;
+ view->checknames = false;
+ } else if (strcasecmp(str, "ignore") == 0) {
+ view->checknames = false;
+ } else {
+ UNREACHABLE();
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "zero-no-soa-ttl-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ zero_no_soattl = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "dns64", &obj);
+ if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") &&
+ strcmp(view->name, "_meta"))
+ {
+ isc_netaddr_t na, suffix, *sp;
+ unsigned int prefixlen;
+ const char *server, *contact;
+ const cfg_obj_t *myobj;
+
+ myobj = NULL;
+ result = named_config_get(maps, "dns64-server", &myobj);
+ if (result == ISC_R_SUCCESS) {
+ server = cfg_obj_asstring(myobj);
+ } else {
+ server = NULL;
+ }
+
+ myobj = NULL;
+ result = named_config_get(maps, "dns64-contact", &myobj);
+ if (result == ISC_R_SUCCESS) {
+ contact = cfg_obj_asstring(myobj);
+ } else {
+ contact = NULL;
+ }
+
+ for (element = cfg_list_first(obj); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *map = cfg_listelt_value(element);
+ dns_dns64_t *dns64 = NULL;
+ unsigned int dns64options = 0;
+
+ cfg_obj_asnetprefix(cfg_map_getname(map), &na,
+ &prefixlen);
+
+ obj = NULL;
+ (void)cfg_map_get(map, "suffix", &obj);
+ if (obj != NULL) {
+ sp = &suffix;
+ isc_netaddr_fromsockaddr(
+ sp, cfg_obj_assockaddr(obj));
+ } else {
+ sp = NULL;
+ }
+
+ clients = mapped = excluded = NULL;
+ obj = NULL;
+ (void)cfg_map_get(map, "clients", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ named_g_lctx, actx,
+ mctx, 0, &clients);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ obj = NULL;
+ (void)cfg_map_get(map, "mapped", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ named_g_lctx, actx,
+ mctx, 0, &mapped);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ obj = NULL;
+ (void)cfg_map_get(map, "exclude", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ named_g_lctx, actx,
+ mctx, 0, &excluded);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ } else {
+ if (named_g_mapped == NULL) {
+ result = create_mapped_acl();
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ dns_acl_attach(named_g_mapped, &excluded);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(map, "recursive-only", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj)) {
+ dns64options |= DNS_DNS64_RECURSIVE_ONLY;
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(map, "break-dnssec", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj)) {
+ dns64options |= DNS_DNS64_BREAK_DNSSEC;
+ }
+
+ result = dns_dns64_create(mctx, &na, prefixlen, sp,
+ clients, mapped, excluded,
+ dns64options, &dns64);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ dns_dns64_append(&view->dns64, dns64);
+ view->dns64cnt++;
+ result = dns64_reverse(view, mctx, &na, prefixlen,
+ server, contact);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (clients != NULL) {
+ dns_acl_detach(&clients);
+ }
+ if (mapped != NULL) {
+ dns_acl_detach(&mapped);
+ }
+ if (excluded != NULL) {
+ dns_acl_detach(&excluded);
+ }
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-accept-expired", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->acceptexpired = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ /* 'optionmaps', not 'maps': don't check named_g_defaults yet */
+ (void)named_config_get(optionmaps, "dnssec-validation", &obj);
+ if (obj == NULL) {
+ /*
+ * Default to VALIDATION_DEFAULT as set in config.c.
+ */
+ (void)cfg_map_get(named_g_defaults, "dnssec-validation", &obj);
+ INSIST(obj != NULL);
+ }
+ if (obj != NULL) {
+ if (cfg_obj_isboolean(obj)) {
+ view->enablevalidation = cfg_obj_asboolean(obj);
+ } else {
+ /*
+ * If dnssec-validation is set but not boolean,
+ * then it must be "auto"
+ */
+ view->enablevalidation = true;
+ auto_root = true;
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "max-cache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxcachettl = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-ncache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxncachettl = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "min-cache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->mincachettl = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "min-ncache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->minncachettl = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "synth-from-dnssec", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->synthfromdnssec = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "stale-cache-enable", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_asboolean(obj)) {
+ obj = NULL;
+ result = named_config_get(maps, "max-stale-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1);
+ }
+ /*
+ * If 'stale-cache-enable' is false, max_stale_ttl is set to 0,
+ * meaning keeping stale RRsets in cache is disabled.
+ */
+
+ obj = NULL;
+ result = named_config_get(maps, "stale-answer-enable", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->staleanswersenable = cfg_obj_asboolean(obj);
+
+ result = dns_viewlist_find(&named_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS) {
+ view->staleanswersok = pview->staleanswersok;
+ dns_view_detach(&pview);
+ } else {
+ view->staleanswersok = dns_stale_answer_conf;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "stale-answer-client-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ /*
+ * The only string values available for this option
+ * are "disabled" and "off".
+ * We use (uint32_t) -1 to represent disabled since
+ * a value of zero means that stale data can be used
+ * to promptly answer the query, while an attempt to
+ * refresh the RRset will still be made in background.
+ */
+ view->staleanswerclienttimeout = (uint32_t)-1;
+ } else {
+ view->staleanswerclienttimeout = cfg_obj_asuint32(obj);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "stale-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ stale_refresh_time = cfg_obj_asduration(obj);
+
+ /*
+ * Configure the view's cache.
+ *
+ * First, check to see if there are any attach-cache options. If yes,
+ * attempt to lookup an existing cache at attach it to the view. If
+ * there is not one, then try to reuse an existing cache if possible;
+ * otherwise create a new cache.
+ *
+ * Note that the ADB is not preserved or shared in either case.
+ *
+ * When a matching view is found, the associated statistics are also
+ * retrieved and reused.
+ *
+ * XXX Determining when it is safe to reuse or share a cache is tricky.
+ * When the view's configuration changes, the cached data may become
+ * invalid because it reflects our old view of the world. We check
+ * some of the configuration parameters that could invalidate the cache
+ * or otherwise make it unshareable, but there are other configuration
+ * options that should be checked. For example, if a view uses a
+ * forwarder, changes in the forwarder configuration may invalidate
+ * the cache. At the moment, it's the administrator's responsibility to
+ * ensure these configuration options don't invalidate reusing/sharing.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "attach-cache", &obj);
+ if (result == ISC_R_SUCCESS) {
+ cachename = cfg_obj_asstring(obj);
+ } else {
+ cachename = view->name;
+ }
+ cache = NULL;
+ nsc = cachelist_find(cachelist, cachename, view->rdclass);
+ if (nsc != NULL) {
+ if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
+ max_cache_size, max_stale_ttl,
+ stale_refresh_time))
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "views %s and %s can't share the cache "
+ "due to configuration parameter mismatch",
+ nsc->primaryview->name, view->name);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ dns_cache_attach(nsc->cache, &cache);
+ shared_cache = true;
+ } else {
+ if (strcmp(cachename, view->name) == 0) {
+ result = dns_viewlist_find(&named_g_server->viewlist,
+ cachename, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ {
+ goto cleanup;
+ }
+ if (pview != NULL) {
+ if (!cache_reusable(pview, view,
+ zero_no_soattl))
+ {
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(1),
+ "cache cannot be reused "
+ "for view %s due to "
+ "configuration parameter "
+ "mismatch",
+ view->name);
+ } else {
+ INSIST(pview->cache != NULL);
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(3),
+ "reusing existing cache");
+ dns_cache_attach(pview->cache, &cache);
+ }
+ dns_view_getresstats(pview, &resstats);
+ dns_view_getresquerystats(pview,
+ &resquerystats);
+ dns_view_detach(&pview);
+ }
+ }
+ if (cache == NULL) {
+ /*
+ * Create a cache with the desired name. This normally
+ * equals the view name, but may also be a forward
+ * reference to a view that share the cache with this
+ * view but is not yet configured. If it is not the
+ * view name but not a forward reference either, then it
+ * is simply a named cache that is not shared.
+ *
+ * We use two separate memory contexts for the
+ * cache, for the main cache memory and the heap
+ * memory.
+ */
+ isc_mem_create(&cmctx);
+ isc_mem_setname(cmctx, "cache");
+ isc_mem_create(&hmctx);
+ isc_mem_setname(hmctx, "cache_heap");
+ CHECK(dns_cache_create(cmctx, hmctx, named_g_taskmgr,
+ named_g_timermgr, view->rdclass,
+ cachename, "rbt", 0, NULL,
+ &cache));
+ isc_mem_detach(&cmctx);
+ isc_mem_detach(&hmctx);
+ }
+ nsc = isc_mem_get(mctx, sizeof(*nsc));
+ nsc->cache = NULL;
+ dns_cache_attach(cache, &nsc->cache);
+ nsc->primaryview = view;
+ nsc->needflush = false;
+ nsc->adbsizeadjusted = false;
+ nsc->rdclass = view->rdclass;
+ ISC_LINK_INIT(nsc, link);
+ ISC_LIST_APPEND(*cachelist, nsc, link);
+ }
+ dns_view_setcache(view, cache, shared_cache);
+
+ dns_cache_setcachesize(cache, max_cache_size);
+ dns_cache_setservestalettl(cache, max_stale_ttl);
+ dns_cache_setservestalerefresh(cache, stale_refresh_time);
+
+ dns_cache_detach(&cache);
+
+ obj = NULL;
+ result = named_config_get(maps, "stale-answer-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->staleanswerttl = ISC_MAX(cfg_obj_asduration(obj), 1);
+
+ /*
+ * Resolver.
+ */
+ CHECK(get_view_querysource_dispatch(
+ maps, AF_INET, &dispatch4,
+ (ISC_LIST_PREV(view, link) == NULL)));
+ CHECK(get_view_querysource_dispatch(
+ maps, AF_INET6, &dispatch6,
+ (ISC_LIST_PREV(view, link) == NULL)));
+ if (dispatch4 == NULL && dispatch6 == NULL) {
+ UNEXPECTED_ERROR("unable to obtain either an IPv4 or"
+ " an IPv6 dispatch");
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ if (resstats == NULL) {
+ CHECK(isc_stats_create(mctx, &resstats,
+ dns_resstatscounter_max));
+ }
+ dns_view_setresstats(view, resstats);
+ if (resquerystats == NULL) {
+ CHECK(dns_rdatatypestats_create(mctx, &resquerystats));
+ }
+ dns_view_setresquerystats(view, resquerystats);
+
+ ndisp = 4 * ISC_MIN(named_g_udpdisp, MAX_UDP_DISPATCH);
+ CHECK(dns_view_createresolver(
+ view, named_g_taskmgr, RESOLVER_NTASKS_PERCPU * named_g_cpus,
+ ndisp, named_g_netmgr, named_g_timermgr, resopts,
+ named_g_dispatchmgr, dispatch4, dispatch6));
+
+ /*
+ * Set the ADB cache size to 1/8th of the max-cache-size or
+ * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
+ */
+ max_adb_size = 0;
+ if (max_cache_size != 0U) {
+ max_adb_size = max_cache_size / 8;
+ if (max_adb_size == 0U) {
+ max_adb_size = 1; /* Force minimum. */
+ }
+ if (view != nsc->primaryview &&
+ max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE)
+ {
+ max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
+ if (!nsc->adbsizeadjusted) {
+ dns_adb_setadbsize(nsc->primaryview->adb,
+ MAX_ADB_SIZE_FOR_CACHESHARE);
+ nsc->adbsizeadjusted = true;
+ }
+ }
+ }
+ dns_adb_setadbsize(view->adb, max_adb_size);
+
+ /*
+ * Set up ADB quotas
+ */
+ {
+ uint32_t fps, freq;
+ double low, high, discount;
+
+ obj = NULL;
+ result = named_config_get(maps, "fetches-per-server", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ obj2 = cfg_tuple_get(obj, "fetches");
+ fps = cfg_obj_asuint32(obj2);
+ obj2 = cfg_tuple_get(obj, "response");
+ if (!cfg_obj_isvoid(obj2)) {
+ const char *resp = cfg_obj_asstring(obj2);
+ isc_result_t r = DNS_R_SERVFAIL;
+
+ if (strcasecmp(resp, "drop") == 0) {
+ r = DNS_R_DROP;
+ } else if (strcasecmp(resp, "fail") == 0) {
+ r = DNS_R_SERVFAIL;
+ } else {
+ UNREACHABLE();
+ }
+
+ dns_resolver_setquotaresponse(view->resolver,
+ dns_quotatype_server, r);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "fetch-quota-params", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+
+ obj2 = cfg_tuple_get(obj, "frequency");
+ freq = cfg_obj_asuint32(obj2);
+
+ obj2 = cfg_tuple_get(obj, "low");
+ low = (double)cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ obj2 = cfg_tuple_get(obj, "high");
+ high = (double)cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ obj2 = cfg_tuple_get(obj, "discount");
+ discount = (double)cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ dns_adb_setquota(view->adb, fps, freq, low, high, discount);
+ }
+
+ /*
+ * Set resolver's lame-ttl.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "lame-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ lame_ttl = cfg_obj_asduration(obj);
+ if (lame_ttl > 0) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "disabling lame cache despite lame-ttl > 0 as it "
+ "may cause performance issues");
+ lame_ttl = 0;
+ }
+ dns_resolver_setlamettl(view->resolver, lame_ttl);
+
+ /*
+ * Set the resolver's query timeout.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "resolver-query-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ query_timeout = cfg_obj_asuint32(obj);
+ dns_resolver_settimeout(view->resolver, query_timeout);
+
+ /*
+ * Adjust stale-answer-client-timeout upper bound
+ * to be resolver-query-timeout - 1s.
+ * This assignment is safe as dns_resolver_settimeout()
+ * ensures that resolver->querytimeout value will be in the
+ * [MINIMUM_QUERY_TIMEOUT, MAXIMUM_QUERY_TIMEOUT] range and
+ * MINIMUM_QUERY_TIMEOUT is > 1000 (in ms).
+ */
+ if (view->staleanswerclienttimeout != (uint32_t)-1 &&
+ view->staleanswerclienttimeout >
+ (dns_resolver_gettimeout(view->resolver) - 1000))
+ {
+ view->staleanswerclienttimeout =
+ dns_resolver_gettimeout(view->resolver) - 1000;
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "stale-answer-client-timeout adjusted to %" PRIu32,
+ view->staleanswerclienttimeout);
+ }
+
+ /* Specify whether to use 0-TTL for negative response for SOA query */
+ dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
+
+ /*
+ * Set the resolver's EDNS UDP size.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512) {
+ udpsize = 512;
+ }
+ if (udpsize > 4096) {
+ udpsize = 4096;
+ }
+ dns_resolver_setudpsize(view->resolver, (uint16_t)udpsize);
+
+ /*
+ * Set the maximum UDP response size.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "max-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512) {
+ udpsize = 512;
+ }
+ if (udpsize > 4096) {
+ udpsize = 4096;
+ }
+ view->maxudp = udpsize;
+
+ /*
+ * Set the maximum UDP when a COOKIE is not provided.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "nocookie-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 128) {
+ udpsize = 128;
+ }
+ if (udpsize > view->maxudp) {
+ udpsize = view->maxudp;
+ }
+ view->nocookieudp = udpsize;
+
+ /*
+ * Set the maximum rsa exponent bits.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "max-rsa-exponent-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ maxbits = cfg_obj_asuint32(obj);
+ if (maxbits != 0 && maxbits < 35) {
+ maxbits = 35;
+ }
+ if (maxbits > 4096) {
+ maxbits = 4096;
+ }
+ view->maxbits = maxbits;
+
+ /*
+ * Set resolver retry parameters.
+ */
+ obj = NULL;
+ CHECK(named_config_get(maps, "resolver-retry-interval", &obj));
+ resolver_param = cfg_obj_asuint32(obj);
+ if (resolver_param > 0) {
+ dns_resolver_setretryinterval(view->resolver, resolver_param);
+ }
+
+ obj = NULL;
+ CHECK(named_config_get(maps, "resolver-nonbackoff-tries", &obj));
+ resolver_param = cfg_obj_asuint32(obj);
+ if (resolver_param > 0) {
+ dns_resolver_setnonbackofftries(view->resolver, resolver_param);
+ }
+
+ /*
+ * Set supported DNSSEC algorithms.
+ */
+ dns_resolver_reset_algorithms(view->resolver);
+ disabled = NULL;
+ (void)named_config_get(maps, "disable-algorithms", &disabled);
+ if (disabled != NULL) {
+ for (element = cfg_list_first(disabled); element != NULL;
+ element = cfg_list_next(element))
+ {
+ CHECK(disable_algorithms(cfg_listelt_value(element),
+ view->resolver));
+ }
+ }
+
+ /*
+ * Set supported DS digest types.
+ */
+ dns_resolver_reset_ds_digests(view->resolver);
+ disabled = NULL;
+ (void)named_config_get(maps, "disable-ds-digests", &disabled);
+ if (disabled != NULL) {
+ for (element = cfg_list_first(disabled); element != NULL;
+ element = cfg_list_next(element))
+ {
+ CHECK(disable_ds_digests(cfg_listelt_value(element),
+ view->resolver));
+ }
+ }
+
+ /*
+ * A global or view "forwarders" option, if present,
+ * creates an entry for "." in the forwarding table.
+ */
+ forwardtype = NULL;
+ forwarders = NULL;
+ (void)named_config_get(maps, "forward", &forwardtype);
+ (void)named_config_get(maps, "forwarders", &forwarders);
+ if (forwarders != NULL) {
+ CHECK(configure_forward(config, view, dns_rootname, forwarders,
+ forwardtype));
+ }
+
+ /*
+ * Dual Stack Servers.
+ */
+ alternates = NULL;
+ (void)named_config_get(maps, "dual-stack-servers", &alternates);
+ if (alternates != NULL) {
+ CHECK(configure_alternates(config, view, alternates));
+ }
+
+ /*
+ * We have default hints for class IN if we need them.
+ */
+ if (view->rdclass == dns_rdataclass_in && view->hints == NULL) {
+ dns_view_sethints(view, named_g_server->in_roothints);
+ }
+
+ /*
+ * If we still have no hints, this is a non-IN view with no
+ * "hints zone" configured. Issue a warning, except if this
+ * is a root server. Root servers never need to consult
+ * their hints, so it's no point requiring users to configure
+ * them.
+ */
+ if (view->hints == NULL) {
+ dns_zone_t *rootzone = NULL;
+ (void)dns_view_findzone(view, dns_rootname, &rootzone);
+ if (rootzone != NULL) {
+ dns_zone_detach(&rootzone);
+ need_hints = false;
+ }
+ if (need_hints) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "no root hints for view '%s'",
+ view->name);
+ }
+ }
+
+ /*
+ * Configure the view's transports (DoT/DoH)
+ */
+ CHECK(named_transports_fromconfig(config, vconfig, view->mctx,
+ &transports));
+ dns_view_settransports(view, transports);
+ dns_transport_list_detach(&transports);
+
+ /*
+ * Configure the view's TSIG keys.
+ */
+ CHECK(named_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
+ if (named_g_server->sessionkey != NULL) {
+ dns_tsigkey_t *tsigkey = NULL;
+ result = dns_tsigkey_createfromkey(
+ named_g_server->session_keyname,
+ algorithm_name(named_g_server->session_keyalg),
+ named_g_server->sessionkey, false, NULL, 0, 0, mctx,
+ NULL, &tsigkey);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_tsigkeyring_add(
+ ring, named_g_server->session_keyname, tsigkey);
+ dns_tsigkey_detach(&tsigkey);
+ }
+ CHECK(result);
+ }
+ dns_view_setkeyring(view, ring);
+ dns_tsigkeyring_detach(&ring);
+
+ /*
+ * See if we can re-use a dynamic key ring.
+ */
+ result = dns_viewlist_find(&named_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (pview != NULL) {
+ dns_view_getdynamickeyring(pview, &ring);
+ if (ring != NULL) {
+ dns_view_setdynamickeyring(view, ring);
+ }
+ dns_tsigkeyring_detach(&ring);
+ dns_view_detach(&pview);
+ } else {
+ dns_view_restorekeyring(view);
+ }
+
+ /*
+ * Configure the view's peer list.
+ */
+ {
+ const cfg_obj_t *peers = NULL;
+ dns_peerlist_t *newpeers = NULL;
+
+ (void)named_config_get(cfgmaps, "server", &peers);
+ CHECK(dns_peerlist_new(mctx, &newpeers));
+ for (element = cfg_list_first(peers); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *cpeer = cfg_listelt_value(element);
+ dns_peer_t *peer;
+
+ CHECK(configure_peer(cpeer, mctx, &peer));
+ dns_peerlist_addpeer(newpeers, peer);
+ dns_peer_detach(&peer);
+ }
+ dns_peerlist_detach(&view->peers);
+ view->peers = newpeers; /* Transfer ownership. */
+ }
+
+ /*
+ * Configure the views rrset-order.
+ */
+ {
+ const cfg_obj_t *rrsetorder = NULL;
+
+ (void)named_config_get(maps, "rrset-order", &rrsetorder);
+ CHECK(dns_order_create(mctx, &order));
+ for (element = cfg_list_first(rrsetorder); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *ent = cfg_listelt_value(element);
+
+ CHECK(configure_order(order, ent));
+ }
+ if (view->order != NULL) {
+ dns_order_detach(&view->order);
+ }
+ dns_order_attach(order, &view->order);
+ dns_order_detach(&order);
+ }
+ /*
+ * Copy the aclenv object.
+ */
+ dns_aclenv_copy(view->aclenv, ns_interfacemgr_getaclenv(
+ named_g_server->interfacemgr));
+
+ /*
+ * Configure the "match-clients" and "match-destinations" ACL.
+ * (These are only meaningful at the view level, but 'config'
+ * must be passed so that named ACLs defined at the global level
+ * can be retrieved.)
+ */
+ CHECK(configure_view_acl(vconfig, config, NULL, "match-clients", NULL,
+ actx, named_g_mctx, &view->matchclients));
+ CHECK(configure_view_acl(vconfig, config, NULL, "match-destinations",
+ NULL, actx, named_g_mctx,
+ &view->matchdestinations));
+
+ /*
+ * Configure the "match-recursive-only" option.
+ */
+ obj = NULL;
+ (void)named_config_get(maps, "match-recursive-only", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj)) {
+ view->matchrecursiveonly = true;
+ } else {
+ view->matchrecursiveonly = false;
+ }
+
+ /*
+ * Configure other configurable data.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "recursion", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->recursion = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "qname-minimization", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ qminmode = cfg_obj_asstring(obj);
+ INSIST(qminmode != NULL);
+ if (!strcmp(qminmode, "strict")) {
+ view->qminimization = true;
+ view->qmin_strict = true;
+ } else if (!strcmp(qminmode, "relaxed")) {
+ view->qminimization = true;
+ view->qmin_strict = false;
+ } else { /* "disabled" or "off" */
+ view->qminimization = false;
+ view->qmin_strict = false;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "auth-nxdomain", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->auth_nxdomain = cfg_obj_asboolean(obj);
+
+ /* deprecated */
+ obj = NULL;
+ result = named_config_get(maps, "glue-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->use_glue_cache = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "minimal-any", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->minimal_any = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "minimal-responses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ view->minimalresponses = dns_minimal_yes;
+ } else {
+ view->minimalresponses = dns_minimal_no;
+ }
+ } else {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "no-auth") == 0) {
+ view->minimalresponses = dns_minimal_noauth;
+ } else if (strcasecmp(str, "no-auth-recursive") == 0) {
+ view->minimalresponses = dns_minimal_noauthrec;
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "transfer-format", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0) {
+ view->transfer_format = dns_many_answers;
+ } else if (strcasecmp(str, "one-answer") == 0) {
+ view->transfer_format = dns_one_answer;
+ } else {
+ UNREACHABLE();
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "trust-anchor-telemetry", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->trust_anchor_telemetry = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "root-key-sentinel", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->root_key_sentinel = cfg_obj_asboolean(obj);
+
+ /*
+ * Set the "allow-query", "allow-query-cache", "allow-recursion",
+ * "allow-recursion-on" and "allow-query-cache-on" ACLs if
+ * configured in named.conf, but NOT from the global defaults.
+ * This is done by leaving the third argument to configure_view_acl()
+ * NULL.
+ *
+ * We ignore the global defaults here because these ACLs
+ * can inherit from each other. If any are still unset after
+ * applying the inheritance rules, we'll look up the defaults at
+ * that time.
+ */
+
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL, "allow-query", NULL,
+ actx, named_g_mctx, &view->queryacl));
+
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache",
+ NULL, actx, named_g_mctx, &view->cacheacl));
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache-on",
+ NULL, actx, named_g_mctx, &view->cacheonacl));
+
+ if (strcmp(view->name, "_bind") != 0 &&
+ view->rdclass != dns_rdataclass_chaos)
+ {
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-recursion", NULL, actx,
+ named_g_mctx, &view->recursionacl));
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-recursion-on", NULL, actx,
+ named_g_mctx, &view->recursiononacl));
+ }
+
+ if (view->recursion) {
+ /*
+ * "allow-query-cache" inherits from "allow-recursion" if set,
+ * otherwise from "allow-query" if set.
+ */
+ if (view->cacheacl == NULL) {
+ if (view->recursionacl != NULL) {
+ dns_acl_attach(view->recursionacl,
+ &view->cacheacl);
+ } else if (view->queryacl != NULL) {
+ dns_acl_attach(view->queryacl, &view->cacheacl);
+ }
+ }
+
+ /*
+ * "allow-recursion" inherits from "allow-query-cache" if set,
+ * otherwise from "allow-query" if set.
+ */
+ if (view->recursionacl == NULL) {
+ if (view->cacheacl != NULL) {
+ dns_acl_attach(view->cacheacl,
+ &view->recursionacl);
+ } else if (view->queryacl != NULL) {
+ dns_acl_attach(view->queryacl,
+ &view->recursionacl);
+ }
+ }
+
+ /*
+ * "allow-query-cache-on" inherits from "allow-recursion-on"
+ * if set.
+ */
+ if (view->cacheonacl == NULL) {
+ if (view->recursiononacl != NULL) {
+ dns_acl_attach(view->recursiononacl,
+ &view->cacheonacl);
+ }
+ }
+
+ /*
+ * "allow-recursion-on" inherits from "allow-query-cache-on"
+ * if set.
+ */
+ if (view->recursiononacl == NULL) {
+ if (view->cacheonacl != NULL) {
+ dns_acl_attach(view->cacheonacl,
+ &view->recursiononacl);
+ }
+ }
+
+ /*
+ * If any are still unset at this point, we now get default
+ * values for from the global config.
+ */
+
+ if (view->recursionacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(
+ NULL, NULL, named_g_config, "allow-recursion",
+ NULL, actx, named_g_mctx, &view->recursionacl));
+ }
+ if (view->recursiononacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, named_g_config,
+ "allow-recursion-on", NULL,
+ actx, named_g_mctx,
+ &view->recursiononacl));
+ }
+ if (view->cacheacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(
+ NULL, NULL, named_g_config, "allow-query-cache",
+ NULL, actx, named_g_mctx, &view->cacheacl));
+ }
+ if (view->cacheonacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, named_g_config,
+ "allow-query-cache-on", NULL,
+ actx, named_g_mctx,
+ &view->cacheonacl));
+ }
+ } else {
+ /*
+ * We're not recursive; if the query-cache ACLs haven't
+ * been set at the options/view level, set them to none.
+ */
+ if (view->cacheacl == NULL) {
+ CHECK(dns_acl_none(mctx, &view->cacheacl));
+ }
+ if (view->cacheonacl == NULL) {
+ CHECK(dns_acl_none(mctx, &view->cacheonacl));
+ }
+ }
+
+ /*
+ * Finished setting recursion and query-cache ACLs, so now we
+ * can get the allow-query default if it wasn't set in named.conf
+ */
+ if (view->queryacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, named_g_config,
+ "allow-query", NULL, actx,
+ named_g_mctx, &view->queryacl));
+ }
+
+ /*
+ * Ignore case when compressing responses to the specified
+ * clients. This causes case not always to be preserved,
+ * and is needed by some broken clients.
+ */
+ CHECK(configure_view_acl(vconfig, config, named_g_config,
+ "no-case-compress", NULL, actx, named_g_mctx,
+ &view->nocasecompress));
+
+ /*
+ * Disable name compression completely, this is a tradeoff
+ * between CPU and network usage.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "message-compression", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->msgcompression = cfg_obj_asboolean(obj);
+
+ /*
+ * Filter setting on addresses in the answer section.
+ */
+ CHECK(configure_view_acl(vconfig, config, named_g_config,
+ "deny-answer-addresses", "acl", actx,
+ named_g_mctx, &view->denyansweracl));
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
+ "except-from", named_g_mctx,
+ &view->answeracl_exclude));
+
+ /*
+ * Filter setting on names (CNAME/DNAME targets) in the answer section.
+ */
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
+ "name", named_g_mctx,
+ &view->denyanswernames));
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
+ "except-from", named_g_mctx,
+ &view->answernames_exclude));
+
+ /*
+ * Configure sortlist, if set
+ */
+ CHECK(configure_view_sortlist(vconfig, config, actx, named_g_mctx,
+ &view->sortlist));
+
+ /*
+ * Configure default allow-update and allow-update-forwarding ACLs,
+ * so they can be inherited by zones. (XXX: These are not
+ * read from the options/view level here. However, they may be
+ * read from there in zoneconf.c:configure_zone_acl() later.)
+ */
+ if (view->updateacl == NULL) {
+ CHECK(configure_view_acl(NULL, NULL, named_g_config,
+ "allow-update", NULL, actx,
+ named_g_mctx, &view->updateacl));
+ }
+ if (view->upfwdacl == NULL) {
+ CHECK(configure_view_acl(NULL, NULL, named_g_config,
+ "allow-update-forwarding", NULL, actx,
+ named_g_mctx, &view->upfwdacl));
+ }
+
+ /*
+ * Configure default allow-transfer and allow-notify ACLs so they
+ * can be inherited by zones.
+ */
+ if (view->transferacl == NULL) {
+ CHECK(configure_view_acl(vconfig, config, named_g_config,
+ "allow-transfer", NULL, actx,
+ named_g_mctx, &view->transferacl));
+ }
+ if (view->notifyacl == NULL) {
+ CHECK(configure_view_acl(vconfig, config, named_g_config,
+ "allow-notify", NULL, actx,
+ named_g_mctx, &view->notifyacl));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "provide-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->provideixfr = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "request-nsid", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requestnsid = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "send-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->sendcookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ if (view->pad_acl != NULL) {
+ dns_acl_detach(&view->pad_acl);
+ }
+ result = named_config_get(optionmaps, "response-padding", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_obj_t *padobj = cfg_tuple_get(obj, "block-size");
+ const cfg_obj_t *aclobj = cfg_tuple_get(obj, "acl");
+ uint32_t padding = cfg_obj_asuint32(padobj);
+
+ if (padding > 512U) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "response-padding block-size cannot "
+ "exceed 512: lowering");
+ padding = 512U;
+ }
+ view->padding = (uint16_t)padding;
+ CHECK(cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx,
+ named_g_mctx, 0, &view->pad_acl));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "require-server-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requireservercookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "v6-bias", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->v6bias = cfg_obj_asuint32(obj) * 1000;
+
+ obj = NULL;
+ result = named_config_get(maps, "max-clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ max_clients_per_query = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj),
+ max_clients_per_query);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-recursion-depth", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "max-recursion-queries", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "fetches-per-zone", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ obj2 = cfg_tuple_get(obj, "fetches");
+ dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj2));
+ obj2 = cfg_tuple_get(obj, "response");
+ if (!cfg_obj_isvoid(obj2)) {
+ const char *resp = cfg_obj_asstring(obj2);
+ isc_result_t r = DNS_R_SERVFAIL;
+
+ if (strcasecmp(resp, "drop") == 0) {
+ r = DNS_R_DROP;
+ } else if (strcasecmp(resp, "fail") == 0) {
+ r = DNS_R_SERVFAIL;
+ } else {
+ UNREACHABLE();
+ }
+
+ dns_resolver_setquotaresponse(view->resolver,
+ dns_quotatype_zone, r);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "prefetch", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ prefetch_trigger = cfg_tuple_get(obj, "trigger");
+ view->prefetch_trigger = cfg_obj_asuint32(prefetch_trigger);
+ if (view->prefetch_trigger > 10) {
+ view->prefetch_trigger = 10;
+ }
+ prefetch_eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isvoid(prefetch_eligible)) {
+ int m;
+ for (m = 1; maps[m] != NULL; m++) {
+ obj = NULL;
+ result = named_config_get(&maps[m], "prefetch", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ prefetch_eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isuint32(prefetch_eligible)) {
+ break;
+ }
+ }
+ INSIST(cfg_obj_isuint32(prefetch_eligible));
+ }
+ view->prefetch_eligible = cfg_obj_asuint32(prefetch_eligible);
+ if (view->prefetch_eligible < view->prefetch_trigger + 6) {
+ view->prefetch_eligible = view->prefetch_trigger + 6;
+ }
+
+ /*
+ * For now, there is only one kind of trusted keys, the
+ * "security roots".
+ */
+ CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
+ auto_root, mctx));
+ dns_resolver_resetmustbesecure(view->resolver);
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-must-be-secure", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECK(mustbesecure(obj, view->resolver));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "nta-recheck", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_recheck = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "nta-lifetime", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_lifetime = cfg_obj_asduration(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "preferred-glue", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "a") == 0) {
+ view->preferred_glue = dns_rdatatype_a;
+ } else if (strcasecmp(str, "aaaa") == 0) {
+ view->preferred_glue = dns_rdatatype_aaaa;
+ } else {
+ view->preferred_glue = 0;
+ }
+ } else {
+ view->preferred_glue = 0;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "root-delegation-only", &obj);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_setrootdelonly(view, true);
+ }
+ if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ const cfg_obj_t *exclude;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ name = dns_fixedname_initname(&fixed);
+ for (element = cfg_list_first(obj); element != NULL;
+ element = cfg_list_next(element))
+ {
+ exclude = cfg_listelt_value(element);
+ CHECK(dns_name_fromstring(
+ name, cfg_obj_asstring(exclude), 0, NULL));
+ dns_view_excludedelegationonly(view, name);
+ }
+ } else {
+ dns_view_setrootdelonly(view, false);
+ }
+
+ /*
+ * Load DynDB modules.
+ */
+ dyndb_list = NULL;
+ if (voptions != NULL) {
+ (void)cfg_map_get(voptions, "dyndb", &dyndb_list);
+ } else {
+ (void)cfg_map_get(config, "dyndb", &dyndb_list);
+ }
+
+ for (element = cfg_list_first(dyndb_list); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *dyndb = cfg_listelt_value(element);
+
+ if (dctx == NULL) {
+ const void *hashinit = isc_hash_get_initializer();
+ CHECK(dns_dyndb_createctx(mctx, hashinit, named_g_lctx,
+ view, named_g_server->zonemgr,
+ named_g_server->task,
+ named_g_timermgr, &dctx));
+ }
+
+ CHECK(configure_dyndb(dyndb, mctx, dctx));
+ }
+
+ /*
+ * Load plugins.
+ */
+ plugin_list = NULL;
+ if (voptions != NULL) {
+ (void)cfg_map_get(voptions, "plugin", &plugin_list);
+ } else {
+ (void)cfg_map_get(config, "plugin", &plugin_list);
+ }
+
+ if (plugin_list != NULL) {
+ INSIST(view->hooktable == NULL);
+ CHECK(ns_hooktable_create(view->mctx,
+ (ns_hooktable_t **)&view->hooktable));
+ view->hooktable_free = ns_hooktable_free;
+
+ ns_plugins_create(view->mctx, (ns_plugins_t **)&view->plugins);
+ view->plugins_free = ns_plugins_free;
+
+ CHECK(cfg_pluginlist_foreach(config, plugin_list, named_g_lctx,
+ register_one_plugin, view));
+ }
+
+ /*
+ * Setup automatic empty zones. If recursion is off then
+ * they are disabled by default.
+ */
+ obj = NULL;
+ (void)named_config_get(maps, "empty-zones-enable", &obj);
+ (void)named_config_get(maps, "disable-empty-zone", &disablelist);
+ if (obj == NULL && disablelist == NULL &&
+ view->rdclass == dns_rdataclass_in)
+ {
+ empty_zones_enable = view->recursion;
+ } else if (view->rdclass == dns_rdataclass_in) {
+ if (obj != NULL) {
+ empty_zones_enable = cfg_obj_asboolean(obj);
+ } else {
+ empty_zones_enable = view->recursion;
+ }
+ } else {
+ empty_zones_enable = false;
+ }
+
+ if (empty_zones_enable) {
+ const char *empty;
+ int empty_zone = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t buffer;
+ char server[DNS_NAME_FORMATSIZE + 1];
+ char contact[DNS_NAME_FORMATSIZE + 1];
+ const char *empty_dbtype[4] = { "_builtin", "empty", NULL,
+ NULL };
+ int empty_dbtypec = 4;
+ dns_zonestat_level_t statlevel = dns_zonestat_none;
+
+ name = dns_fixedname_initname(&fixed);
+
+ obj = NULL;
+ result = named_config_get(maps, "empty-server", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj),
+ 0, NULL));
+ isc_buffer_init(&buffer, server, sizeof(server) - 1);
+ CHECK(dns_name_totext(name, false, &buffer));
+ server[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[2] = server;
+ } else {
+ empty_dbtype[2] = "@";
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "empty-contact", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj),
+ 0, NULL));
+ isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
+ CHECK(dns_name_totext(name, false, &buffer));
+ contact[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[3] = contact;
+ } else {
+ empty_dbtype[3] = ".";
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ statlevel = dns_zonestat_full;
+ } else {
+ statlevel = dns_zonestat_none;
+ }
+ } else {
+ const char *levelstr = cfg_obj_asstring(obj);
+ if (strcasecmp(levelstr, "full") == 0) {
+ statlevel = dns_zonestat_full;
+ } else if (strcasecmp(levelstr, "terse") == 0) {
+ statlevel = dns_zonestat_terse;
+ } else if (strcasecmp(levelstr, "none") == 0) {
+ statlevel = dns_zonestat_none;
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ for (empty = empty_zones[empty_zone]; empty != NULL;
+ empty = empty_zones[++empty_zone])
+ {
+ dns_forwarders_t *dnsforwarders = NULL;
+
+ /*
+ * Look for zone on drop list.
+ */
+ CHECK(dns_name_fromstring(name, empty, 0, NULL));
+ if (disablelist != NULL &&
+ on_disable_list(disablelist, name))
+ {
+ continue;
+ }
+
+ /*
+ * This zone already exists.
+ */
+ (void)dns_view_findzone(view, name, &zone);
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ continue;
+ }
+
+ /*
+ * If we would forward this name don't add a
+ * empty zone for it.
+ */
+ result = dns_fwdtable_find(view->fwdtable, name, NULL,
+ &dnsforwarders);
+ if ((result == ISC_R_SUCCESS ||
+ result == DNS_R_PARTIALMATCH) &&
+ dnsforwarders->fwdpolicy == dns_fwdpolicy_only)
+ {
+ continue;
+ }
+
+ /*
+ * See if we can re-use a existing zone.
+ */
+ result = dns_viewlist_find(&named_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ {
+ goto cleanup;
+ }
+
+ if (pview != NULL) {
+ (void)dns_view_findzone(pview, name, &zone);
+ dns_view_detach(&pview);
+ }
+
+ CHECK(create_empty_zone(zone, name, view, zonelist,
+ empty_dbtype, empty_dbtypec,
+ statlevel));
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ }
+ }
+
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in) {
+ (void)named_config_get(maps, "ipv4only-enable", &obj);
+ }
+ if (view->rdclass == dns_rdataclass_in && (obj != NULL)
+ ? cfg_obj_asboolean(obj)
+ : !ISC_LIST_EMPTY(view->dns64))
+ {
+ const char *server, *contact;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ struct {
+ const char *name;
+ const char *type;
+ } zones[] = {
+ { "ipv4only.arpa", "ipv4only" },
+ { "170.0.0.192.in-addr.arpa", "ipv4reverse" },
+ { "171.0.0.192.in-addr.arpa", "ipv4reverse" },
+ };
+ size_t ipv4only_zone;
+
+ obj = NULL;
+ result = named_config_get(maps, "ipv4only-server", &obj);
+ if (result == ISC_R_SUCCESS) {
+ server = cfg_obj_asstring(obj);
+ } else {
+ server = NULL;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "ipv4only-contact", &obj);
+ if (result == ISC_R_SUCCESS) {
+ contact = cfg_obj_asstring(obj);
+ } else {
+ contact = NULL;
+ }
+
+ name = dns_fixedname_initname(&fixed);
+ for (ipv4only_zone = 0; ipv4only_zone < ARRAY_SIZE(zones);
+ ipv4only_zone++)
+ {
+ dns_forwarders_t *dnsforwarders = NULL;
+
+ CHECK(dns_name_fromstring(
+ name, zones[ipv4only_zone].name, 0, NULL));
+
+ (void)dns_view_findzone(view, name, &zone);
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ continue;
+ }
+
+ /*
+ * If we would forward this name don't add it.
+ */
+ result = dns_fwdtable_find(view->fwdtable, name, NULL,
+ &dnsforwarders);
+ if ((result == ISC_R_SUCCESS ||
+ result == DNS_R_PARTIALMATCH) &&
+ dnsforwarders->fwdpolicy == dns_fwdpolicy_only)
+ {
+ continue;
+ }
+
+ /*
+ * See if we can re-use a existing zone.
+ */
+ result = dns_viewlist_find(&named_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ {
+ goto cleanup;
+ }
+
+ if (pview != NULL) {
+ (void)dns_view_findzone(pview, name, &zone);
+ dns_view_detach(&pview);
+ }
+
+ CHECK(create_ipv4only_zone(zone, view, name,
+ zones[ipv4only_zone].type,
+ mctx, server, contact));
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "rate-limit", &obj);
+ if (result == ISC_R_SUCCESS) {
+ result = configure_rrl(view, config, obj);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Set the servfail-ttl.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "servfail-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ fail_ttl = cfg_obj_asduration(obj);
+ if (fail_ttl > 30) {
+ fail_ttl = 30;
+ }
+ dns_view_setfailttl(view, fail_ttl);
+
+ /*
+ * Name space to look up redirect information in.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "nxdomain-redirect", &obj);
+ if (result == ISC_R_SUCCESS) {
+ dns_name_t *name = dns_fixedname_name(&view->redirectfixed);
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj), 0,
+ NULL));
+ view->redirectzone = name;
+ } else {
+ view->redirectzone = NULL;
+ }
+
+ /*
+ * Exceptions to DNSSEC validation.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "validate-except", &obj);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_view_getntatable(view, &ntatable);
+ }
+ if (result == ISC_R_SUCCESS) {
+ for (element = cfg_list_first(obj); element != NULL;
+ element = cfg_list_next(element))
+ {
+ dns_fixedname_t fntaname;
+ dns_name_t *ntaname;
+
+ ntaname = dns_fixedname_initname(&fntaname);
+ obj = cfg_listelt_value(element);
+ CHECK(dns_name_fromstring(
+ ntaname, cfg_obj_asstring(obj), 0, NULL));
+ CHECK(dns_ntatable_add(ntatable, ntaname, true, 0,
+ 0xffffffffU));
+ }
+ }
+
+#ifdef HAVE_DNSTAP
+ /*
+ * Set up the dnstap environment and configure message
+ * types to log.
+ */
+ CHECK(configure_dnstap(maps, view));
+#endif /* HAVE_DNSTAP */
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ /*
+ * Revert to the old view if there was an error.
+ */
+ if (result != ISC_R_SUCCESS) {
+ isc_result_t result2;
+
+ result2 = dns_viewlist_find(&named_g_server->viewlist,
+ view->name, view->rdclass, &pview);
+ if (result2 == ISC_R_SUCCESS) {
+ dns_view_thaw(pview);
+
+ obj = NULL;
+ if (rpz_configured &&
+ pview->rdclass == dns_rdataclass_in && need_hints &&
+ named_config_get(maps, "response-policy", &obj) ==
+ ISC_R_SUCCESS)
+ {
+ /*
+ * We are swapping the places of the `view` and
+ * `pview` in the function's parameters list
+ * because we are reverting the same operation
+ * done previously in the "correct" order.
+ */
+ result2 = configure_rpz(pview, view, maps, obj,
+ &old_rpz_ok);
+ if (result2 != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "rpz configuration "
+ "revert failed for view "
+ "'%s'",
+ pview->name);
+ }
+ }
+
+ obj = NULL;
+ if (catz_configured &&
+ pview->rdclass == dns_rdataclass_in && need_hints &&
+ named_config_get(maps, "catalog-zones", &obj) ==
+ ISC_R_SUCCESS)
+ {
+ /*
+ * We are swapping the places of the `view` and
+ * `pview` in the function's parameters list
+ * because we are reverting the same operation
+ * done previously in the "correct" order.
+ */
+ result2 = configure_catz(pview, view, config,
+ obj);
+ if (result2 != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "catz configuration "
+ "revert failed for view "
+ "'%s'",
+ pview->name);
+ }
+ }
+
+ dns_view_freeze(pview);
+ }
+
+ if (pview != NULL) {
+ dns_view_detach(&pview);
+ }
+
+ if (zone_element_latest != NULL) {
+ for (element = cfg_list_first(zonelist);
+ element != NULL; element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig =
+ cfg_listelt_value(element);
+ configure_zone_setviewcommit(result, zconfig,
+ view);
+ if (element == zone_element_latest) {
+ /*
+ * This was the latest element that was
+ * successfully configured earlier.
+ */
+ break;
+ }
+ }
+ }
+ }
+
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ if (clients != NULL) {
+ dns_acl_detach(&clients);
+ }
+ if (mapped != NULL) {
+ dns_acl_detach(&mapped);
+ }
+ if (excluded != NULL) {
+ dns_acl_detach(&excluded);
+ }
+ if (ring != NULL) {
+ dns_tsigkeyring_detach(&ring);
+ }
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ if (dispatch4 != NULL) {
+ dns_dispatch_detach(&dispatch4);
+ }
+ if (dispatch6 != NULL) {
+ dns_dispatch_detach(&dispatch6);
+ }
+ if (resstats != NULL) {
+ isc_stats_detach(&resstats);
+ }
+ if (resquerystats != NULL) {
+ dns_stats_detach(&resquerystats);
+ }
+ if (order != NULL) {
+ dns_order_detach(&order);
+ }
+ if (cmctx != NULL) {
+ isc_mem_detach(&cmctx);
+ }
+ if (hmctx != NULL) {
+ isc_mem_detach(&hmctx);
+ }
+ if (cache != NULL) {
+ dns_cache_detach(&cache);
+ }
+ if (dctx != NULL) {
+ dns_dyndb_destroyctx(&dctx);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+configure_hints(dns_view_t *view, const char *filename) {
+ isc_result_t result;
+ dns_db_t *db;
+
+ db = NULL;
+ result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_sethints(view, db);
+ dns_db_detach(&db);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates) {
+ const cfg_obj_t *portobj;
+ const cfg_obj_t *addresses;
+ const cfg_listelt_t *element;
+ isc_result_t result = ISC_R_SUCCESS;
+ in_port_t port;
+
+ /*
+ * Determine which port to send requests to.
+ */
+ CHECKM(named_config_getport(config, "port", &port), "port");
+
+ if (alternates != NULL) {
+ portobj = cfg_tuple_get(alternates, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx,
+ ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)val;
+ }
+ }
+
+ addresses = NULL;
+ if (alternates != NULL) {
+ addresses = cfg_tuple_get(alternates, "addresses");
+ }
+
+ for (element = cfg_list_first(addresses); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *alternate = cfg_listelt_value(element);
+ isc_sockaddr_t sa;
+
+ if (!cfg_obj_issockaddr(alternate)) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ const char *str = cfg_obj_asstring(
+ cfg_tuple_get(alternate, "name"));
+ isc_buffer_t buffer;
+ in_port_t myport = port;
+
+ isc_buffer_constinit(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ name = dns_fixedname_initname(&fixed);
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
+ NULL));
+
+ portobj = cfg_tuple_get(alternate, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx,
+ ISC_LOG_ERROR,
+ "port '%u' out of range",
+ val);
+ return (ISC_R_RANGE);
+ }
+ myport = (in_port_t)val;
+ }
+ dns_resolver_addalternate(view->resolver, NULL, name,
+ myport);
+ continue;
+ }
+
+ sa = *cfg_obj_assockaddr(alternate);
+ if (isc_sockaddr_getport(&sa) == 0) {
+ isc_sockaddr_setport(&sa, port);
+ }
+ dns_resolver_addalternate(view->resolver, &sa, NULL, 0);
+ }
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view,
+ const dns_name_t *origin, const cfg_obj_t *forwarders,
+ const cfg_obj_t *forwardtype) {
+ const cfg_obj_t *portobj = NULL;
+ const cfg_obj_t *faddresses = NULL;
+ const cfg_listelt_t *element = NULL;
+ dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
+ dns_forwarderlist_t fwdlist;
+ dns_forwarder_t *fwd = NULL;
+ isc_result_t result;
+ in_port_t port;
+
+ ISC_LIST_INIT(fwdlist);
+
+ /*
+ * Determine which port to send forwarded requests to.
+ */
+ CHECKM(named_config_getport(config, "port", &port), "port");
+
+ if (forwarders != NULL) {
+ portobj = cfg_tuple_get(forwarders, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > UINT16_MAX) {
+ cfg_obj_log(portobj, named_g_lctx,
+ ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)val;
+ }
+ }
+
+ faddresses = NULL;
+ if (forwarders != NULL) {
+ faddresses = cfg_tuple_get(forwarders, "addresses");
+ }
+
+ for (element = cfg_list_first(faddresses); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *forwarder = cfg_listelt_value(element);
+ fwd = isc_mem_get(view->mctx, sizeof(dns_forwarder_t));
+ fwd->addr = *cfg_obj_assockaddr(forwarder);
+ if (isc_sockaddr_getport(&fwd->addr) == 0) {
+ isc_sockaddr_setport(&fwd->addr, port);
+ }
+ ISC_LINK_INIT(fwd, link);
+ ISC_LIST_APPEND(fwdlist, fwd, link);
+ }
+
+ if (ISC_LIST_EMPTY(fwdlist)) {
+ if (forwardtype != NULL) {
+ cfg_obj_log(forwardtype, named_g_lctx, ISC_LOG_WARNING,
+ "no forwarders seen; disabling "
+ "forwarding");
+ }
+ fwdpolicy = dns_fwdpolicy_none;
+ } else {
+ if (forwardtype == NULL) {
+ fwdpolicy = dns_fwdpolicy_first;
+ } else {
+ const char *forwardstr = cfg_obj_asstring(forwardtype);
+ if (strcasecmp(forwardstr, "first") == 0) {
+ fwdpolicy = dns_fwdpolicy_first;
+ } else if (strcasecmp(forwardstr, "only") == 0) {
+ fwdpolicy = dns_fwdpolicy_only;
+ } else {
+ UNREACHABLE();
+ }
+ }
+ }
+
+ result = dns_fwdtable_addfwd(view->fwdtable, origin, &fwdlist,
+ fwdpolicy);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(origin, namebuf, sizeof(namebuf));
+ cfg_obj_log(forwarders, named_g_lctx, ISC_LOG_WARNING,
+ "could not set up forwarding for domain '%s': %s",
+ namebuf, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ if (fwdpolicy == dns_fwdpolicy_only) {
+ dns_view_sfd_add(view, origin);
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+
+ while (!ISC_LIST_EMPTY(fwdlist)) {
+ fwd = ISC_LIST_HEAD(fwdlist);
+ ISC_LIST_UNLINK(fwdlist, fwd, link);
+ isc_mem_put(view->mctx, fwd, sizeof(dns_forwarder_t));
+ }
+
+ return (result);
+}
+
+static isc_result_t
+get_viewinfo(const cfg_obj_t *vconfig, const char **namep,
+ dns_rdataclass_t *classp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ const char *viewname;
+ dns_rdataclass_t viewclass;
+
+ REQUIRE(namep != NULL && *namep == NULL);
+ REQUIRE(classp != NULL);
+
+ if (vconfig != NULL) {
+ const cfg_obj_t *classobj = NULL;
+
+ viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
+ classobj = cfg_tuple_get(vconfig, "class");
+ CHECK(named_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass));
+ if (dns_rdataclass_ismeta(viewclass)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "view '%s': class must not be meta",
+ viewname);
+ CHECK(ISC_R_FAILURE);
+ }
+ } else {
+ viewname = "_default";
+ viewclass = dns_rdataclass_in;
+ }
+
+ *namep = viewname;
+ *classp = viewclass;
+
+cleanup:
+ return (result);
+}
+
+/*
+ * Find a view based on its configuration info and attach to it.
+ *
+ * If 'vconfig' is NULL, attach to the default view.
+ */
+static isc_result_t
+find_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
+ dns_view_t **viewp) {
+ isc_result_t result;
+ const char *viewname = NULL;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ result = get_viewinfo(vconfig, &viewname, &viewclass);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ *viewp = view;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Create a new view and add it to the list.
+ *
+ * If 'vconfig' is NULL, create the default view.
+ *
+ * The view created is attached to '*viewp'.
+ */
+static isc_result_t
+create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
+ dns_view_t **viewp) {
+ isc_result_t result;
+ const char *viewname = NULL;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ result = get_viewinfo(vconfig, &viewname, &viewclass);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
+ if (result == ISC_R_SUCCESS) {
+ return (ISC_R_EXISTS);
+ }
+ if (result != ISC_R_NOTFOUND) {
+ return (result);
+ }
+ INSIST(view == NULL);
+
+ result = dns_view_create(named_g_mctx, viewclass, viewname, &view);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ isc_nonce_buf(view->secret, sizeof(view->secret));
+
+ ISC_LIST_APPEND(*viewlist, view, link);
+ dns_view_attach(view, viewp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Configure or reconfigure a zone.
+ */
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ dns_viewlist_t *viewlist, dns_kasplist_t *kasplist,
+ cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok,
+ bool modify) {
+ dns_view_t *pview = NULL; /* Production view */
+ dns_zone_t *zone = NULL; /* New or reused zone */
+ dns_zone_t *raw = NULL; /* New or reused raw zone */
+ dns_zone_t *dupzone = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *typeobj = NULL;
+ const cfg_obj_t *forwarders = NULL;
+ const cfg_obj_t *forwardtype = NULL;
+ const cfg_obj_t *ixfrfromdiffs = NULL;
+ const cfg_obj_t *only = NULL;
+ const cfg_obj_t *viewobj = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_result_t tresult;
+ isc_buffer_t buffer;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ const char *ztypestr;
+ dns_rpz_num_t rpz_num;
+ bool zone_is_catz = false;
+ bool zone_maybe_inline = false;
+ bool inline_signing = false;
+ bool fullsign = false;
+
+ options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ /*
+ * Get the zone origin as a dns_name_t.
+ */
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ isc_buffer_constinit(&buffer, zname, strlen(zname));
+ isc_buffer_add(&buffer, strlen(zname));
+ dns_fixedname_init(&fixorigin);
+ CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer,
+ dns_rootname, 0, NULL));
+ origin = dns_fixedname_name(&fixorigin);
+
+ CHECK(named_config_getclass(cfg_tuple_get(zconfig, "class"),
+ view->rdclass, &zclass));
+ if (zclass != view->rdclass) {
+ const char *vname = NULL;
+ if (vconfig != NULL) {
+ vname = cfg_obj_asstring(
+ cfg_tuple_get(vconfig, "name"));
+ } else {
+ vname = "<default view>";
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': wrong class for view '%s'", zname,
+ vname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ (void)cfg_map_get(zoptions, "in-view", &viewobj);
+ if (viewobj != NULL) {
+ const char *inview = cfg_obj_asstring(viewobj);
+ dns_view_t *otherview = NULL;
+
+ if (viewlist == NULL) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "'in-view' option is not permitted in "
+ "dynamically added zones");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ result = dns_viewlist_find(viewlist, inview, view->rdclass,
+ &otherview);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "view '%s' is not yet defined.", inview);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ result = dns_view_findzone(otherview, origin, &zone);
+ dns_view_detach(&otherview);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' not defined in view '%s'", zname,
+ inview);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ CHECK(dns_view_addzone(view, zone));
+ dns_zone_detach(&zone);
+
+ /*
+ * If the zone contains a 'forwarders' statement, configure
+ * selective forwarding. Note: this is not inherited from the
+ * other view.
+ */
+ forwarders = NULL;
+ result = cfg_map_get(zoptions, "forwarders", &forwarders);
+ if (result == ISC_R_SUCCESS) {
+ forwardtype = NULL;
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ CHECK(configure_forward(config, view, origin,
+ forwarders, forwardtype));
+ }
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ (void)cfg_map_get(zoptions, "type", &typeobj);
+ if (typeobj == NULL) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' 'type' not specified", zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ ztypestr = cfg_obj_asstring(typeobj);
+
+ /*
+ * "hints zones" aren't zones. If we've got one,
+ * configure it and return.
+ */
+ if (strcasecmp(ztypestr, "hint") == 0) {
+ const cfg_obj_t *fileobj = NULL;
+ if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'file' not specified", zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ if (dns_name_equal(origin, dns_rootname)) {
+ const char *hintsfile = cfg_obj_asstring(fileobj);
+
+ CHECK(configure_hints(view, hintsfile));
+
+ /*
+ * Hint zones may also refer to delegation only points.
+ */
+ only = NULL;
+ tresult = cfg_map_get(zoptions, "delegation-only",
+ &only);
+ if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
+ {
+ dns_view_adddelegationonly(view, origin);
+ }
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "ignoring non-root hint zone '%s'",
+ zname);
+ result = ISC_R_SUCCESS;
+ }
+ /* Skip ordinary zone processing. */
+ goto cleanup;
+ }
+
+ /*
+ * "forward zones" aren't zones either. Translate this syntax into
+ * the appropriate selective forwarding configuration and return.
+ */
+ if (strcasecmp(ztypestr, "forward") == 0) {
+ forwardtype = NULL;
+ forwarders = NULL;
+
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ (void)cfg_map_get(zoptions, "forwarders", &forwarders);
+ CHECK(configure_forward(config, view, origin, forwarders,
+ forwardtype));
+
+ /*
+ * Forward zones may also set delegation only.
+ */
+ only = NULL;
+ tresult = cfg_map_get(zoptions, "delegation-only", &only);
+ if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) {
+ dns_view_adddelegationonly(view, origin);
+ }
+ goto cleanup;
+ }
+
+ /*
+ * "delegation-only zones" aren't zones either.
+ */
+ if (strcasecmp(ztypestr, "delegation-only") == 0) {
+ dns_view_adddelegationonly(view, origin);
+ goto cleanup;
+ }
+
+ /*
+ * Redirect zones only require minimal configuration.
+ */
+ if (strcasecmp(ztypestr, "redirect") == 0) {
+ if (view->redirect != NULL) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "redirect zone already exists");
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ result = dns_viewlist_find(viewlist, view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (pview != NULL && pview->redirect != NULL) {
+ dns_zone_attach(pview->redirect, &zone);
+ dns_zone_setview(zone, view);
+ } else {
+ CHECK(dns_zonemgr_createzone(named_g_server->zonemgr,
+ &zone));
+ CHECK(dns_zone_setorigin(zone, origin));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr,
+ zone));
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ }
+ CHECK(named_zone_configure(config, vconfig, zconfig, aclconf,
+ kasplist, zone, NULL));
+ dns_zone_attach(zone, &view->redirect);
+ goto cleanup;
+ }
+
+ if (!modify) {
+ /*
+ * Check for duplicates in the new zone table.
+ */
+ result = dns_view_findzone(view, origin, &dupzone);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We already have this zone!
+ */
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' already exists", zname);
+ dns_zone_detach(&dupzone);
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ INSIST(dupzone == NULL);
+ }
+
+ /*
+ * Note whether this is a response policy zone and which one if so,
+ * unless we are using RPZ service interface. In that case, the
+ * BIND zone database has nothing to do with rpz and so we don't care.
+ */
+ for (rpz_num = 0;; ++rpz_num) {
+ if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones ||
+ view->rpzs->p.dnsrps_enabled)
+ {
+ rpz_num = DNS_RPZ_INVALID_NUM;
+ break;
+ }
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, origin))
+ {
+ break;
+ }
+ }
+
+ if (view->catzs != NULL &&
+ dns_catz_get_zone(view->catzs, origin) != NULL)
+ {
+ zone_is_catz = true;
+ }
+
+ /*
+ * See if we can reuse an existing zone. This is
+ * only possible if all of these are true:
+ * - The zone's view exists
+ * - A zone with the right name exists in the view
+ * - The zone is compatible with the config
+ * options (e.g., an existing primary zone cannot
+ * be reused if the options specify a secondary zone)
+ * - The zone was not and is still not a response policy zone
+ * or the zone is a policy zone with an unchanged number
+ * and we are using the old policy zone summary data.
+ */
+ result = dns_viewlist_find(&named_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (pview != NULL) {
+ result = dns_view_findzone(pview, origin, &zone);
+ }
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (zone != NULL && !named_zone_reusable(zone, zconfig)) {
+ dns_zone_detach(&zone);
+ fullsign = true;
+ }
+
+ if (zone != NULL && (rpz_num != dns_zone_get_rpz_num(zone) ||
+ (rpz_num != DNS_RPZ_INVALID_NUM && !old_rpz_ok)))
+ {
+ dns_zone_detach(&zone);
+ }
+
+ if (zone != NULL) {
+ /*
+ * We found a reusable zone. Make it use the
+ * new view.
+ */
+ dns_zone_setview(zone, view);
+ } else {
+ /*
+ * We cannot reuse an existing zone, we have
+ * to create a new one.
+ */
+ CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone));
+ CHECK(dns_zone_setorigin(zone, origin));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ }
+ if (rpz_num != DNS_RPZ_INVALID_NUM) {
+ result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': incompatible"
+ " masterfile-format or database"
+ " for a response policy zone",
+ zname);
+ goto cleanup;
+ }
+ }
+
+ if (zone_is_catz) {
+ dns_zone_catz_enable(zone, view->catzs);
+ } else if (dns_zone_catz_is_enabled(zone)) {
+ dns_zone_catz_disable(zone);
+ }
+
+ /*
+ * If the zone contains a 'forwarders' statement, configure
+ * selective forwarding.
+ */
+ forwarders = NULL;
+ if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) {
+ forwardtype = NULL;
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ CHECK(configure_forward(config, view, origin, forwarders,
+ forwardtype));
+ }
+
+ /*
+ * Stub and forward zones may also refer to delegation only points.
+ */
+ only = NULL;
+ if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS) {
+ if (cfg_obj_asboolean(only)) {
+ dns_view_adddelegationonly(view, origin);
+ }
+ }
+
+ /*
+ * Mark whether the zone was originally added at runtime or not
+ */
+ dns_zone_setadded(zone, added);
+
+ /*
+ * Determine if we need to set up inline signing.
+ */
+ zone_maybe_inline = ((strcasecmp(ztypestr, "primary") == 0 ||
+ strcasecmp(ztypestr, "master") == 0 ||
+ strcasecmp(ztypestr, "secondary") == 0 ||
+ strcasecmp(ztypestr, "slave") == 0));
+
+ if (zone_maybe_inline) {
+ inline_signing = named_zone_inlinesigning(zconfig);
+ }
+ if (inline_signing) {
+ dns_zone_getraw(zone, &raw);
+ if (raw == NULL) {
+ CHECK(dns_zone_create(&raw, mctx));
+ CHECK(dns_zone_setorigin(raw, origin));
+ dns_zone_setview(raw, view);
+ dns_zone_setstats(raw, named_g_server->zonestats);
+ CHECK(dns_zone_link(zone, raw));
+ }
+ if (cfg_map_get(zoptions, "ixfr-from-differences",
+ &ixfrfromdiffs) == ISC_R_SUCCESS)
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "zone '%s': 'ixfr-from-differences' is "
+ "ignored for inline-signed zones",
+ zname);
+ }
+ }
+
+ /*
+ * Configure the zone.
+ */
+ CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, kasplist,
+ zone, raw));
+
+ /*
+ * Add the zone to its view in the new view list.
+ */
+ if (!modify) {
+ CHECK(dns_view_addzone(view, zone));
+ }
+
+ if (zone_is_catz) {
+ /*
+ * force catz reload if the zone is loaded;
+ * if it's not it'll get reloaded on zone load
+ */
+ dns_db_t *db = NULL;
+
+ tresult = dns_zone_getdb(zone, &db);
+ if (tresult == ISC_R_SUCCESS) {
+ dns_catz_dbupdate_callback(db, view->catzs);
+ dns_db_detach(&db);
+ }
+ }
+
+ /*
+ * Ensure that zone keys are reloaded on reconfig
+ */
+ if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0) {
+ dns_zone_rekey(zone, fullsign);
+ }
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+ if (pview != NULL) {
+ dns_view_detach(&pview);
+ }
+
+ return (result);
+}
+
+/*
+ * Configure built-in zone for storing managed-key data.
+ */
+static isc_result_t
+add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
+ isc_result_t result;
+ dns_view_t *pview = NULL;
+ dns_zone_t *zone = NULL;
+ dns_acl_t *none = NULL;
+ char filename[PATH_MAX];
+ bool defaultview;
+
+ REQUIRE(view != NULL);
+
+ /* See if we can re-use an existing keydata zone. */
+ result = dns_viewlist_find(&named_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (pview != NULL) {
+ if (pview->managed_keys != NULL) {
+ dns_zone_attach(pview->managed_keys,
+ &view->managed_keys);
+ dns_zone_setview(pview->managed_keys, view);
+ dns_zone_setviewcommit(pview->managed_keys);
+ dns_view_detach(&pview);
+ dns_zone_synckeyzone(view->managed_keys);
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_view_detach(&pview);
+ }
+
+ /* No existing keydata zone was found; create one */
+ CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone));
+ CHECK(dns_zone_setorigin(zone, dns_rootname));
+
+ defaultview = (strcmp(view->name, "_default") == 0);
+ CHECK(isc_file_sanitize(
+ directory, defaultview ? "managed-keys" : view->name,
+ defaultview ? "bind" : "mkeys", filename, sizeof(filename)));
+ CHECK(dns_zone_setfile(zone, filename, dns_masterformat_text,
+ &dns_master_style_default));
+
+ dns_zone_setview(zone, view);
+ dns_zone_settype(zone, dns_zone_key);
+ dns_zone_setclass(zone, view->rdclass);
+
+ CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone));
+
+ CHECK(dns_acl_none(mctx, &none));
+ dns_zone_setqueryacl(zone, none);
+ dns_zone_setqueryonacl(zone, none);
+ dns_acl_detach(&none);
+
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ dns_zone_setjournalsize(zone, 0);
+
+ dns_zone_setstats(zone, named_g_server->zonestats);
+ CHECK(setquerystats(zone, mctx, dns_zonestat_none));
+
+ if (view->managed_keys != NULL) {
+ dns_zone_detach(&view->managed_keys);
+ }
+ dns_zone_attach(zone, &view->managed_keys);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "set up managed keys zone for view %s, file '%s'",
+ view->name, filename);
+
+cleanup:
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ if (none != NULL) {
+ dns_acl_detach(&none);
+ }
+
+ return (result);
+}
+
+/*
+ * Configure a single server quota.
+ */
+static void
+configure_server_quota(const cfg_obj_t **maps, const char *name,
+ isc_quota_t *quota) {
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = named_config_get(maps, name, &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ isc_quota_max(quota, cfg_obj_asuint32(obj));
+}
+
+/*
+ * This function is called as soon as the 'directory' statement has been
+ * parsed. This can be extended to support other options if necessary.
+ */
+static isc_result_t
+directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
+ isc_result_t result;
+ const char *directory;
+
+ REQUIRE(strcasecmp("directory", clausename) == 0);
+
+ UNUSED(arg);
+ UNUSED(clausename);
+
+ /*
+ * Change directory.
+ */
+ directory = cfg_obj_asstring(obj);
+
+ if (!isc_file_ischdiridempotent(directory)) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "option 'directory' contains relative path '%s'",
+ directory);
+ }
+
+ if (!isc_file_isdirwritable(directory)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "directory '%s' is not writable", directory);
+ return (ISC_R_NOPERM);
+ }
+
+ result = isc_dir_chdir(directory);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "change directory to '%s' failed: %s", directory,
+ isc_result_totext(result));
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This event callback is invoked to do periodic network interface
+ * scanning.
+ */
+
+static void
+interface_timer_tick(isc_task_t *task, isc_event_t *event) {
+ named_server_t *server = (named_server_t *)event->ev_arg;
+ INSIST(task == server->task);
+ UNUSED(task);
+
+ isc_event_free(&event);
+ ns_interfacemgr_scan(server->interfacemgr, false, false);
+}
+
+static void
+heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
+ named_server_t *server = (named_server_t *)event->ev_arg;
+ dns_view_t *view;
+
+ UNUSED(task);
+ isc_event_free(&event);
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ dns_view_dialup(view);
+ view = ISC_LIST_NEXT(view, link);
+ }
+}
+
+typedef struct {
+ isc_mem_t *mctx;
+ isc_task_t *task;
+ dns_fetch_t *fetch;
+ dns_view_t *view;
+ dns_fixedname_t tatname;
+ dns_fixedname_t keyname;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t sigrdataset;
+} ns_tat_t;
+
+static int
+cid(const void *a, const void *b) {
+ const uint16_t ida = *(const uint16_t *)a;
+ const uint16_t idb = *(const uint16_t *)b;
+ if (ida < idb) {
+ return (-1);
+ } else if (ida > idb) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+static void
+tat_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent;
+ ns_tat_t *tat;
+
+ INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE);
+ INSIST(event->ev_arg != NULL);
+
+ UNUSED(task);
+
+ tat = event->ev_arg;
+ devent = (dns_fetchevent_t *)event;
+
+ /* Free resources which are not of interest */
+ if (devent->node != NULL) {
+ dns_db_detachnode(devent->db, &devent->node);
+ }
+ if (devent->db != NULL) {
+ dns_db_detach(&devent->db);
+ }
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&tat->fetch);
+ if (dns_rdataset_isassociated(&tat->rdataset)) {
+ dns_rdataset_disassociate(&tat->rdataset);
+ }
+ if (dns_rdataset_isassociated(&tat->sigrdataset)) {
+ dns_rdataset_disassociate(&tat->sigrdataset);
+ }
+ dns_view_detach(&tat->view);
+ isc_task_detach(&tat->task);
+ isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat));
+}
+
+struct dotat_arg {
+ dns_view_t *view;
+ isc_task_t *task;
+};
+
+/*%
+ * Prepare the QNAME for the TAT query to be sent by processing the trust
+ * anchors present at 'keynode' of 'keytable'. Store the result in 'dst' and
+ * the domain name which 'keynode' is associated with in 'origin'.
+ *
+ * A maximum of 12 key IDs can be reported in a single TAT query due to the
+ * 63-octet length limit for any single label in a domain name. If there are
+ * more than 12 keys configured at 'keynode', only the first 12 will be
+ * reported in the TAT query.
+ */
+static isc_result_t
+get_tat_qname(dns_name_t *target, dns_name_t *keyname, dns_keynode_t *keynode) {
+ dns_rdataset_t dsset;
+ unsigned int i, n = 0;
+ uint16_t ids[12];
+ isc_textregion_t r;
+ char label[64];
+ int m;
+
+ dns_rdataset_init(&dsset);
+ if (dns_keynode_dsset(keynode, &dsset)) {
+ isc_result_t result;
+
+ for (result = dns_rdataset_first(&dsset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&dsset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_ds_t ds;
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&dsset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ds, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (n < (sizeof(ids) / sizeof(ids[0]))) {
+ ids[n] = ds.key_tag;
+ n++;
+ }
+ }
+ dns_rdataset_disassociate(&dsset);
+ }
+
+ if (n == 0) {
+ return (DNS_R_EMPTYNAME);
+ }
+
+ if (n > 1) {
+ qsort(ids, n, sizeof(ids[0]), cid);
+ }
+
+ /*
+ * Encoded as "_ta-xxxx\(-xxxx\)*" where xxxx is the hex version of
+ * of the keyid.
+ */
+ label[0] = 0;
+ r.base = label;
+ r.length = sizeof(label);
+ m = snprintf(r.base, r.length, "_ta");
+ if (m < 0 || (unsigned)m > r.length) {
+ return (ISC_R_FAILURE);
+ }
+ isc_textregion_consume(&r, m);
+ for (i = 0; i < n; i++) {
+ m = snprintf(r.base, r.length, "-%04x", ids[i]);
+ if (m < 0 || (unsigned)m > r.length) {
+ return (ISC_R_FAILURE);
+ }
+ isc_textregion_consume(&r, m);
+ }
+
+ return (dns_name_fromstring2(target, label, keyname, 0, NULL));
+}
+
+static void
+tat_send(isc_task_t *task, isc_event_t *event) {
+ ns_tat_t *tat;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fdomain;
+ dns_name_t *domain;
+ dns_rdataset_t nameservers;
+ isc_result_t result;
+ dns_name_t *keyname;
+ dns_name_t *tatname;
+
+ INSIST(event != NULL && event->ev_type == NAMED_EVENT_TATSEND);
+ INSIST(event->ev_arg != NULL);
+
+ UNUSED(task);
+
+ tat = event->ev_arg;
+
+ keyname = dns_fixedname_name(&tat->keyname);
+ tatname = dns_fixedname_name(&tat->tatname);
+
+ dns_name_format(tatname, namebuf, sizeof(namebuf));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s: sending trust-anchor-telemetry query '%s/NULL'",
+ tat->view->name, namebuf);
+
+ /*
+ * TAT queries should be sent to the authoritative servers for a given
+ * zone. If this function is called for a keytable node corresponding
+ * to a locally served zone, calling dns_resolver_createfetch() with
+ * NULL 'domain' and 'nameservers' arguments will cause 'tatname' to be
+ * resolved locally, without sending any TAT queries upstream.
+ *
+ * Work around this issue by calling dns_view_findzonecut() first. If
+ * the zone is served locally, the NS RRset for the given domain name
+ * will be retrieved from local data; if it is not, the deepest zone
+ * cut we have for it will be retrieved from cache. In either case,
+ * passing the results to dns_resolver_createfetch() will prevent it
+ * from returning NXDOMAIN for 'tatname' while still allowing it to
+ * chase down any potential delegations returned by upstream servers in
+ * order to eventually find the destination host to send the TAT query
+ * to.
+ *
+ * After the dns_view_findzonecut() call, 'domain' will hold the
+ * deepest zone cut we can find for 'keyname' while 'nameservers' will
+ * hold the NS RRset at that zone cut.
+ */
+ domain = dns_fixedname_initname(&fdomain);
+ dns_rdataset_init(&nameservers);
+ result = dns_view_findzonecut(tat->view, keyname, domain, NULL, 0, 0,
+ true, true, &nameservers, NULL);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_resolver_createfetch(
+ tat->view->resolver, tatname, dns_rdatatype_null,
+ domain, &nameservers, NULL, NULL, 0, 0, 0, NULL,
+ tat->task, tat_done, tat, &tat->rdataset,
+ &tat->sigrdataset, &tat->fetch);
+ }
+
+ /*
+ * 'domain' holds the dns_name_t pointer inside a dst_key_t structure.
+ * dns_resolver_createfetch() creates its own copy of 'domain' if it
+ * succeeds. Thus, 'domain' is not freed here.
+ *
+ * Even if dns_view_findzonecut() returned something else than
+ * ISC_R_SUCCESS, it still could have associated 'nameservers'.
+ * dns_resolver_createfetch() creates its own copy of 'nameservers' if
+ * it succeeds. Thus, we need to check whether 'nameservers' is
+ * associated and release it if it is.
+ */
+ if (dns_rdataset_isassociated(&nameservers)) {
+ dns_rdataset_disassociate(&nameservers);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&tat->view);
+ isc_task_detach(&tat->task);
+ isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat));
+ }
+ isc_event_free(&event);
+}
+
+static void
+dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, dns_name_t *keyname,
+ void *arg) {
+ struct dotat_arg *dotat_arg = arg;
+ isc_result_t result;
+ dns_view_t *view;
+ isc_task_t *task;
+ ns_tat_t *tat;
+ isc_event_t *event;
+
+ REQUIRE(keytable != NULL);
+ REQUIRE(keynode != NULL);
+ REQUIRE(dotat_arg != NULL);
+
+ view = dotat_arg->view;
+ task = dotat_arg->task;
+
+ tat = isc_mem_get(dotat_arg->view->mctx, sizeof(*tat));
+
+ tat->fetch = NULL;
+ tat->mctx = NULL;
+ tat->task = NULL;
+ tat->view = NULL;
+ dns_rdataset_init(&tat->rdataset);
+ dns_rdataset_init(&tat->sigrdataset);
+ dns_name_copy(keyname, dns_fixedname_initname(&tat->keyname));
+ result = get_tat_qname(dns_fixedname_initname(&tat->tatname), keyname,
+ keynode);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(dotat_arg->view->mctx, tat, sizeof(*tat));
+ return;
+ }
+ isc_mem_attach(dotat_arg->view->mctx, &tat->mctx);
+ isc_task_attach(task, &tat->task);
+ dns_view_attach(view, &tat->view);
+
+ /*
+ * We don't want to be holding the keytable lock when calling
+ * dns_view_findzonecut() as it creates a lock order loop so
+ * call dns_view_findzonecut() in a event handler.
+ *
+ * zone->lock (dns_zone_setviewcommit) while holding view->lock
+ * (dns_view_setviewcommit)
+ *
+ * keytable->lock (dns_keytable_find) while holding zone->lock
+ * (zone_asyncload)
+ *
+ * view->lock (dns_view_findzonecut) while holding keytable->lock
+ * (dns_keytable_forall)
+ */
+ event = isc_event_allocate(tat->mctx, keytable, NAMED_EVENT_TATSEND,
+ tat_send, tat, sizeof(isc_event_t));
+ isc_task_send(task, &event);
+}
+
+static void
+tat_timer_tick(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ named_server_t *server = (named_server_t *)event->ev_arg;
+ struct dotat_arg arg;
+ dns_view_t *view;
+ dns_keytable_t *secroots = NULL;
+
+ isc_event_free(&event);
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (!view->trust_anchor_telemetry || !view->enablevalidation) {
+ continue;
+ }
+
+ result = dns_view_getsecroots(view, &secroots);
+ if (result != ISC_R_SUCCESS) {
+ continue;
+ }
+
+ arg.view = view;
+ arg.task = task;
+ (void)dns_keytable_forall(secroots, dotat, &arg);
+ dns_keytable_detach(&secroots);
+ }
+}
+
+static void
+pps_timer_tick(isc_task_t *task, isc_event_t *event) {
+ static unsigned int oldrequests = 0;
+ unsigned int requests = atomic_load_relaxed(&ns_client_requests);
+
+ UNUSED(task);
+ isc_event_free(&event);
+
+ /*
+ * Don't worry about wrapping as the overflow result will be right.
+ */
+ dns_pps = (requests - oldrequests) / 1200;
+ oldrequests = requests;
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with a dynamically allocated copy of the
+ * null-terminated string pointed to by 'value', or NULL.
+ */
+static isc_result_t
+setstring(named_server_t *server, char **field, const char *value) {
+ char *copy;
+
+ if (value != NULL) {
+ copy = isc_mem_strdup(server->mctx, value);
+ } else {
+ copy = NULL;
+ }
+
+ if (*field != NULL) {
+ isc_mem_free(server->mctx, *field);
+ }
+
+ *field = copy;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with another dynamically allocated string
+ * or NULL if whether 'obj' is a string or void value, respectively.
+ */
+static isc_result_t
+setoptstring(named_server_t *server, char **field, const cfg_obj_t *obj) {
+ if (cfg_obj_isvoid(obj)) {
+ return (setstring(server, field, NULL));
+ } else {
+ return (setstring(server, field, cfg_obj_asstring(obj)));
+ }
+}
+
+static void
+set_limit(const cfg_obj_t **maps, const char *configname,
+ const char *description, isc_resource_t resourceid,
+ isc_resourcevalue_t defaultvalue) {
+ const cfg_obj_t *obj = NULL;
+ const char *resource;
+ isc_resourcevalue_t value;
+ isc_result_t result;
+
+ if (named_config_get(maps, configname, &obj) != ISC_R_SUCCESS) {
+ return;
+ }
+
+ if (cfg_obj_isstring(obj)) {
+ resource = cfg_obj_asstring(obj);
+ if (strcasecmp(resource, "unlimited") == 0) {
+ value = ISC_RESOURCE_UNLIMITED;
+ } else {
+ INSIST(strcasecmp(resource, "default") == 0);
+ value = defaultvalue;
+ }
+ } else {
+ value = cfg_obj_asuint64(obj);
+ }
+
+ result = isc_resource_setlimit(resourceid, value);
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+ result == ISC_R_SUCCESS ? ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
+ "set maximum %s to %" PRIu64 ": %s", description, value,
+ isc_result_totext(result));
+}
+
+#define SETLIMIT(cfgvar, resource, description) \
+ set_limit(maps, cfgvar, description, isc_resource_##resource, \
+ named_g_init##resource)
+
+static void
+set_limits(const cfg_obj_t **maps) {
+ SETLIMIT("stacksize", stacksize, "stack size");
+ SETLIMIT("datasize", datasize, "data size");
+ SETLIMIT("coresize", coresize, "core size");
+ SETLIMIT("files", openfiles, "open files");
+}
+
+static void
+portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
+ bool positive) {
+ const cfg_listelt_t *element;
+
+ for (element = cfg_list_first(ports); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *obj = cfg_listelt_value(element);
+
+ if (cfg_obj_isuint32(obj)) {
+ in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
+
+ if (positive) {
+ isc_portset_add(portset, port);
+ } else {
+ isc_portset_remove(portset, port);
+ }
+ } else {
+ const cfg_obj_t *obj_loport, *obj_hiport;
+ in_port_t loport, hiport;
+
+ obj_loport = cfg_tuple_get(obj, "loport");
+ loport = (in_port_t)cfg_obj_asuint32(obj_loport);
+ obj_hiport = cfg_tuple_get(obj, "hiport");
+ hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
+
+ if (positive) {
+ isc_portset_addrange(portset, loport, hiport);
+ } else {
+ isc_portset_removerange(portset, loport,
+ hiport);
+ }
+ }
+ }
+}
+
+static isc_result_t
+removed(dns_zone_t *zone, void *uap) {
+ if (dns_zone_getview(zone) != uap) {
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed",
+ dns_zonetype_name(dns_zone_gettype(zone)));
+ return (ISC_R_SUCCESS);
+}
+
+static void
+cleanup_session_key(named_server_t *server, isc_mem_t *mctx) {
+ if (server->session_keyfile != NULL) {
+ isc_file_remove(server->session_keyfile);
+ isc_mem_free(mctx, server->session_keyfile);
+ server->session_keyfile = NULL;
+ }
+
+ if (server->session_keyname != NULL) {
+ if (dns_name_dynamic(server->session_keyname)) {
+ dns_name_free(server->session_keyname, mctx);
+ }
+ isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
+ server->session_keyname = NULL;
+ }
+
+ if (server->sessionkey != NULL) {
+ dst_key_free(&server->sessionkey);
+ }
+
+ server->session_keyalg = DST_ALG_UNKNOWN;
+ server->session_keybits = 0;
+}
+
+static isc_result_t
+generate_session_key(const char *filename, const char *keynamestr,
+ const dns_name_t *keyname, const char *algstr,
+ unsigned int algtype, uint16_t bits, isc_mem_t *mctx,
+ bool first_time, dst_key_t **keyp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dst_key_t *key = NULL;
+ isc_buffer_t key_txtbuffer;
+ isc_buffer_t key_rawbuffer;
+ char key_txtsecret[256];
+ char key_rawsecret[64];
+ isc_region_t key_rawregion;
+ FILE *fp = NULL;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "generating session key for dynamic DNS");
+
+ /* generate key */
+ result = dst_key_generate(keyname, algtype, bits, 1, 0,
+ DNS_KEYPROTO_ANY, dns_rdataclass_in, mctx,
+ &key, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Dump the key to the buffer for later use.
+ */
+ isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
+ CHECK(dst_key_tobuffer(key, &key_rawbuffer));
+
+ isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
+ isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
+ CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
+
+ /* Dump the key to the key file. */
+ fp = named_os_openfile(filename, S_IRUSR | S_IWUSR, first_time);
+ if (fp == NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not create %s", filename);
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+
+ fprintf(fp,
+ "key \"%s\" {\n"
+ "\talgorithm %s;\n"
+ "\tsecret \"%.*s\";\n};\n",
+ keynamestr, algstr, (int)isc_buffer_usedlength(&key_txtbuffer),
+ (char *)isc_buffer_base(&key_txtbuffer));
+
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed to generate session key "
+ "for dynamic DNS: %s",
+ isc_result_totext(result));
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ (void)isc_file_remove(filename);
+ }
+ if (key != NULL) {
+ dst_key_free(&key);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+configure_session_key(const cfg_obj_t **maps, named_server_t *server,
+ isc_mem_t *mctx, bool first_time) {
+ const char *keyfile, *keynamestr, *algstr;
+ unsigned int algtype;
+ dns_fixedname_t fname;
+ dns_name_t *keyname;
+ const dns_name_t *algname;
+ isc_buffer_t buffer;
+ uint16_t bits;
+ const cfg_obj_t *obj;
+ bool need_deleteold = false;
+ bool need_createnew = false;
+ isc_result_t result;
+
+ obj = NULL;
+ result = named_config_get(maps, "session-keyfile", &obj);
+ if (result == ISC_R_SUCCESS) {
+ if (cfg_obj_isvoid(obj)) {
+ keyfile = NULL; /* disable it */
+ } else {
+ keyfile = cfg_obj_asstring(obj);
+ }
+ } else {
+ keyfile = named_g_defaultsessionkeyfile;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "session-keyname", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ keynamestr = cfg_obj_asstring(obj);
+ isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr));
+ isc_buffer_add(&buffer, strlen(keynamestr));
+ keyname = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "session-keyalg", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ algstr = cfg_obj_asstring(obj);
+ algname = NULL;
+ result = named_config_getkeyalgorithm2(algstr, &algname, &algtype,
+ &bits);
+ if (result != ISC_R_SUCCESS) {
+ const char *s = " (keeping current key)";
+
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "session-keyalg: "
+ "unsupported or unknown algorithm '%s'%s",
+ algstr, server->session_keyfile != NULL ? s : "");
+ return (result);
+ }
+
+ /* See if we need to (re)generate a new key. */
+ if (keyfile == NULL) {
+ if (server->session_keyfile != NULL) {
+ need_deleteold = true;
+ }
+ } else if (server->session_keyfile == NULL) {
+ need_createnew = true;
+ } else if (strcmp(keyfile, server->session_keyfile) != 0 ||
+ !dns_name_equal(server->session_keyname, keyname) ||
+ server->session_keyalg != algtype ||
+ server->session_keybits != bits)
+ {
+ need_deleteold = true;
+ need_createnew = true;
+ }
+
+ if (need_deleteold) {
+ INSIST(server->session_keyfile != NULL);
+ INSIST(server->session_keyname != NULL);
+ INSIST(server->sessionkey != NULL);
+
+ cleanup_session_key(server, mctx);
+ }
+
+ if (need_createnew) {
+ INSIST(server->sessionkey == NULL);
+ INSIST(server->session_keyfile == NULL);
+ INSIST(server->session_keyname == NULL);
+ INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
+ INSIST(server->session_keybits == 0);
+
+ server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
+ dns_name_init(server->session_keyname, NULL);
+ dns_name_dup(keyname, mctx, server->session_keyname);
+
+ server->session_keyfile = isc_mem_strdup(mctx, keyfile);
+
+ server->session_keyalg = algtype;
+ server->session_keybits = bits;
+
+ CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
+ algtype, bits, mctx, first_time,
+ &server->sessionkey));
+ }
+
+ return (result);
+
+cleanup:
+ cleanup_session_key(server, mctx);
+ return (result);
+}
+
+#ifndef HAVE_LMDB
+static isc_result_t
+count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
+ isc_result_t result;
+
+ /* The new zone file may not exist. That is OK. */
+ if (!isc_file_exists(view->new_zone_file)) {
+ *num_zonesp = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * In the case of NZF files, we also parse the configuration in
+ * the file at this stage.
+ *
+ * This may be called in multiple views, so we reset
+ * the parser each time.
+ */
+ cfg_parser_reset(named_g_addparser);
+ result = cfg_parse_file(named_g_addparser, view->new_zone_file,
+ &cfg_type_addzoneconf, &nzcfg->nzf_config);
+ if (result == ISC_R_SUCCESS) {
+ int num_zones;
+
+ num_zones = count_zones(nzcfg->nzf_config);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "NZF file '%s' contains %d zones",
+ view->new_zone_file, num_zones);
+ if (num_zonesp != NULL) {
+ *num_zonesp = num_zones;
+ }
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error parsing NZF file '%s': %s",
+ view->new_zone_file, isc_result_totext(result));
+ }
+
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static isc_result_t
+count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
+ isc_result_t result;
+ int n;
+
+ UNUSED(nzcfg);
+
+ REQUIRE(num_zonesp != NULL);
+
+ LOCK(&view->new_zone_lock);
+
+ CHECK(migrate_nzf(view));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading NZD zone count from '%s' "
+ "for view '%s'",
+ view->new_zone_db, view->name);
+
+ CHECK(nzd_count(view, &n));
+
+ *num_zonesp = n;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "NZD database '%s' contains %d zones", view->new_zone_db,
+ n);
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ *num_zonesp = 0;
+ }
+
+ UNLOCK(&view->new_zone_lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LMDB */
+
+static isc_result_t
+setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx,
+ int *num_zones) {
+ isc_result_t result = ISC_R_SUCCESS;
+ bool allow = false;
+ ns_cfgctx_t *nzcfg = NULL;
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *options = NULL, *voptions = NULL;
+ const cfg_obj_t *nz = NULL;
+ const cfg_obj_t *nzdir = NULL;
+ const char *dir = NULL;
+ const cfg_obj_t *obj = NULL;
+ int i = 0;
+ uint64_t mapsize = 0ULL;
+
+ REQUIRE(config != NULL);
+
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ }
+ if (voptions != NULL) {
+ maps[i++] = voptions;
+ }
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS) {
+ maps[i++] = options;
+ }
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ result = named_config_get(maps, "allow-new-zones", &nz);
+ if (result == ISC_R_SUCCESS) {
+ allow = cfg_obj_asboolean(nz);
+ }
+ result = named_config_get(maps, "new-zones-directory", &nzdir);
+ if (result == ISC_R_SUCCESS) {
+ dir = cfg_obj_asstring(nzdir);
+ result = isc_file_isdirectory(dir);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "invalid new-zones-directory %s: %s", dir,
+ isc_result_totext(result));
+ return (result);
+ }
+ if (!isc_file_isdirwritable(dir)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "new-zones-directory '%s' "
+ "is not writable",
+ dir);
+ return (ISC_R_NOPERM);
+ }
+
+ dns_view_setnewzonedir(view, dir);
+ }
+
+#ifdef HAVE_LMDB
+ result = named_config_get(maps, "lmdb-mapsize", &obj);
+ if (result == ISC_R_SUCCESS && obj != NULL) {
+ mapsize = cfg_obj_asuint64(obj);
+ if (mapsize < (1ULL << 20)) { /* 1 megabyte */
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "'lmdb-mapsize "
+ "%" PRId64 "' "
+ "is too small",
+ mapsize);
+ return (ISC_R_FAILURE);
+ } else if (mapsize > (1ULL << 40)) { /* 1 terabyte */
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "'lmdb-mapsize "
+ "%" PRId64 "' "
+ "is too large",
+ mapsize);
+ return (ISC_R_FAILURE);
+ }
+ }
+#else /* ifdef HAVE_LMDB */
+ UNUSED(obj);
+#endif /* HAVE_LMDB */
+
+ /*
+ * A non-empty catalog-zones statement implies allow-new-zones
+ */
+ if (!allow) {
+ const cfg_obj_t *cz = NULL;
+ result = named_config_get(maps, "catalog-zones", &cz);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_listelt_t *e =
+ cfg_list_first(cfg_tuple_get(cz, "zone list"));
+ if (e != NULL) {
+ allow = true;
+ }
+ }
+ }
+
+ if (!allow) {
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ if (num_zones != NULL) {
+ *num_zones = 0;
+ }
+ return (ISC_R_SUCCESS);
+ }
+
+ nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
+
+ /*
+ * We attach the parser that was used for config as well
+ * as the one that will be used for added zones, to avoid
+ * a shutdown race later.
+ */
+ memset(nzcfg, 0, sizeof(*nzcfg));
+ cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
+ cfg_parser_attach(named_g_addparser, &nzcfg->add_parser);
+ isc_mem_attach(view->mctx, &nzcfg->mctx);
+ cfg_aclconfctx_attach(actx, &nzcfg->actx);
+
+ result = dns_view_setnewzones(view, true, nzcfg, newzone_cfgctx_destroy,
+ mapsize);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ return (result);
+ }
+
+ cfg_obj_attach(config, &nzcfg->config);
+ if (vconfig != NULL) {
+ cfg_obj_attach(vconfig, &nzcfg->vconfig);
+ }
+
+ result = count_newzones(view, nzcfg, num_zones);
+ return (result);
+}
+
+static void
+configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig,
+ dns_view_t *view) {
+ const char *zname;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+ isc_result_t result2;
+ dns_view_t *pview = NULL;
+ dns_zone_t *zone = NULL;
+
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ origin = dns_fixedname_initname(&fixorigin);
+
+ result2 = dns_name_fromstring(origin, zname, 0, NULL);
+ if (result2 != ISC_R_SUCCESS) {
+ return;
+ }
+
+ result2 = dns_viewlist_find(&named_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result2 != ISC_R_SUCCESS) {
+ return;
+ }
+
+ result2 = dns_view_findzone(pview, origin, &zone);
+ if (result2 != ISC_R_SUCCESS) {
+ dns_view_detach(&pview);
+ return;
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_zone_setviewcommit(zone);
+ } else {
+ dns_zone_setviewrevert(zone);
+ }
+
+ dns_zone_detach(&zone);
+ dns_view_detach(&pview);
+}
+
+#ifndef HAVE_LMDB
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx) {
+ isc_result_t result;
+ ns_cfgctx_t *nzctx;
+ const cfg_obj_t *zonelist;
+ const cfg_listelt_t *element;
+
+ nzctx = view->new_zone_config;
+ if (nzctx == NULL || nzctx->nzf_config == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading additional zones for view '%s'", view->name);
+
+ zonelist = NULL;
+ cfg_map_get(nzctx->nzf_config, "zone", &zonelist);
+
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
+ &named_g_server->viewlist,
+ &named_g_server->kasplist, actx, true,
+ false, false));
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ configure_zone_setviewcommit(result, zconfig, view);
+ }
+
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static isc_result_t
+data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data, isc_buffer_t **text,
+ cfg_obj_t **zoneconfig) {
+ isc_result_t result;
+ const char *zone_name;
+ size_t zone_name_len;
+ const char *zone_config;
+ size_t zone_config_len;
+ cfg_obj_t *zoneconf = NULL;
+ char bufname[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(view != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE(text != NULL);
+ REQUIRE(zoneconfig != NULL && *zoneconfig == NULL);
+
+ if (*text == NULL) {
+ isc_buffer_allocate(view->mctx, text, 256);
+ } else {
+ isc_buffer_clear(*text);
+ }
+
+ zone_name = (const char *)key->mv_data;
+ zone_name_len = key->mv_size;
+ INSIST(zone_name != NULL && zone_name_len > 0);
+
+ zone_config = (const char *)data->mv_data;
+ zone_config_len = data->mv_size;
+ INSIST(zone_config != NULL && zone_config_len > 0);
+
+ /* zone zonename { config; }; */
+ result = isc_buffer_reserve(text, 6 + zone_name_len + 2 +
+ zone_config_len + 2);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ CHECK(putstr(text, "zone \""));
+ CHECK(putmem(text, (const void *)zone_name, zone_name_len));
+ CHECK(putstr(text, "\" "));
+ CHECK(putmem(text, (const void *)zone_config, zone_config_len));
+ CHECK(putstr(text, ";\n"));
+
+ snprintf(bufname, sizeof(bufname), "%.*s", (int)zone_name_len,
+ zone_name);
+
+ cfg_parser_reset(named_g_addparser);
+ result = cfg_parse_buffer(named_g_addparser, *text, bufname, 0,
+ &cfg_type_addzoneconf, 0, &zoneconf);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "parsing config for zone '%.*s' in "
+ "NZD database '%s' failed",
+ (int)zone_name_len, zone_name, view->new_zone_db);
+ goto cleanup;
+ }
+
+ *zoneconfig = zoneconf;
+ zoneconf = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(named_g_addparser, &zoneconf);
+ }
+
+ return (result);
+}
+
+/*%
+ * Prototype for a callback which can be used with for_all_newzone_cfgs().
+ */
+typedef isc_result_t (*newzone_cfg_cb_t)(const cfg_obj_t *zconfig,
+ cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx);
+
+/*%
+ * For each zone found in a NZD opened by the caller, create an object
+ * representing its configuration and invoke "callback" with the created
+ * object, "config", "vconfig", "mctx", "view" and "actx" as arguments (all
+ * these are non-global variables required to invoke configure_zone()).
+ * Immediately interrupt processing if an error is encountered while
+ * transforming NZD data into a zone configuration object or if "callback"
+ * returns an error.
+ *
+ * Caller must hold 'view->new_zone_lock'.
+ */
+static isc_result_t
+for_all_newzone_cfgs(newzone_cfg_cb_t callback, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx, MDB_txn *txn, MDB_dbi dbi) {
+ const cfg_obj_t *zconfig, *zlist;
+ isc_result_t result = ISC_R_SUCCESS;
+ cfg_obj_t *zconfigobj = NULL;
+ isc_buffer_t *text = NULL;
+ MDB_cursor *cursor = NULL;
+ MDB_val data, key;
+ int status;
+
+ status = mdb_cursor_open(txn, dbi, &cursor);
+ if (status != MDB_SUCCESS) {
+ return (ISC_R_FAILURE);
+ }
+
+ for (status = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
+ status == MDB_SUCCESS;
+ status = mdb_cursor_get(cursor, &key, &data, MDB_NEXT))
+ {
+ /*
+ * Create a configuration object from data fetched from NZD.
+ */
+ result = data_to_cfg(view, &key, &data, &text, &zconfigobj);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Extract zone configuration from configuration object.
+ */
+ zlist = NULL;
+ result = cfg_map_get(zconfigobj, "zone", &zlist);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ } else if (!cfg_obj_islist(zlist)) {
+ result = ISC_R_FAILURE;
+ break;
+ }
+ zconfig = cfg_listelt_value(cfg_list_first(zlist));
+
+ /*
+ * Invoke callback.
+ */
+ result = callback(zconfig, config, vconfig, mctx, view, actx);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Destroy the configuration object created in this iteration.
+ */
+ cfg_obj_destroy(named_g_addparser, &zconfigobj);
+ }
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+ if (zconfigobj != NULL) {
+ cfg_obj_destroy(named_g_addparser, &zconfigobj);
+ }
+ mdb_cursor_close(cursor);
+
+ return (result);
+}
+
+/*%
+ * Attempt to configure a zone found in NZD and return the result.
+ */
+static isc_result_t
+configure_newzone(const cfg_obj_t *zconfig, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx) {
+ return (configure_zone(
+ config, zconfig, vconfig, mctx, view, &named_g_server->viewlist,
+ &named_g_server->kasplist, actx, true, false, false));
+}
+
+/*%
+ * Revert new view assignment for a zone found in NZD.
+ */
+static isc_result_t
+configure_newzone_revert(const cfg_obj_t *zconfig, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx) {
+ UNUSED(config);
+ UNUSED(vconfig);
+ UNUSED(mctx);
+ UNUSED(actx);
+
+ configure_zone_setviewcommit(ISC_R_FAILURE, zconfig, view);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx) {
+ isc_result_t result;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+
+ if (view->new_zone_config == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ LOCK(&view->new_zone_lock);
+
+ result = nzd_open(view, MDB_RDONLY, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&view->new_zone_lock);
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading NZD configs from '%s' "
+ "for view '%s'",
+ view->new_zone_db, view->name);
+
+ result = for_all_newzone_cfgs(configure_newzone, config, vconfig, mctx,
+ view, actx, txn, dbi);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * An error was encountered while attempting to configure zones
+ * found in NZD. As this error may have been caused by a
+ * configure_zone() failure, try restoring a sane configuration
+ * by reattaching all zones found in NZD to the old view. If
+ * this also fails, too bad, there is nothing more we can do in
+ * terms of trying to make things right.
+ */
+ (void)for_all_newzone_cfgs(configure_newzone_revert, config,
+ vconfig, mctx, view, actx, txn, dbi);
+ }
+
+ (void)nzd_close(&txn, false);
+
+ UNLOCK(&view->new_zone_lock);
+
+ return (result);
+}
+
+static isc_result_t
+get_newzone_config(dns_view_t *view, const char *zonename,
+ cfg_obj_t **zoneconfig) {
+ isc_result_t result;
+ int status;
+ cfg_obj_t *zoneconf = NULL;
+ isc_buffer_t *text = NULL;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ char zname[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ INSIST(zoneconfig != NULL && *zoneconfig == NULL);
+
+ LOCK(&view->new_zone_lock);
+
+ CHECK(nzd_open(view, MDB_RDONLY, &txn, &dbi));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading NZD config from '%s' "
+ "for zone '%s'",
+ view->new_zone_db, zonename);
+
+ /* Normalize zone name */
+ isc_buffer_constinit(&b, zonename, strlen(zonename));
+ isc_buffer_add(&b, strlen(zonename));
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, DNS_NAME_DOWNCASE,
+ NULL));
+ dns_name_format(name, zname, sizeof(zname));
+
+ key.mv_data = zname;
+ key.mv_size = strlen(zname);
+
+ status = mdb_get(txn, dbi, &key, &data);
+ if (status != MDB_SUCCESS) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(data_to_cfg(view, &key, &data, &text, &zoneconf));
+
+ *zoneconfig = zoneconf;
+ zoneconf = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ (void)nzd_close(&txn, false);
+
+ UNLOCK(&view->new_zone_lock);
+
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(named_g_addparser, &zoneconf);
+ }
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ return (result);
+}
+
+#endif /* HAVE_LMDB */
+
+static int
+count_zones(const cfg_obj_t *conf) {
+ const cfg_obj_t *zonelist = NULL;
+ const cfg_listelt_t *element;
+ int n = 0;
+
+ REQUIRE(conf != NULL);
+
+ cfg_map_get(conf, "zone", &zonelist);
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ n++;
+ }
+
+ return (n);
+}
+
+static isc_result_t
+check_lockfile(named_server_t *server, const cfg_obj_t *config,
+ bool first_time) {
+ isc_result_t result;
+ const char *filename = NULL;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *options;
+ const cfg_obj_t *obj;
+ int i;
+
+ i = 0;
+ options = NULL;
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS) {
+ maps[i++] = options;
+ }
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ obj = NULL;
+ (void)named_config_get(maps, "lock-file", &obj);
+
+ if (!first_time) {
+ if (obj != NULL && !cfg_obj_isstring(obj) &&
+ server->lockfile != NULL &&
+ strcmp(cfg_obj_asstring(obj), server->lockfile) != 0)
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "changing 'lock-file' "
+ "has no effect until the "
+ "server is restarted");
+ }
+
+ return (ISC_R_SUCCESS);
+ }
+
+ if (obj != NULL) {
+ if (cfg_obj_isvoid(obj)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "skipping lock-file check ");
+ return (ISC_R_SUCCESS);
+ } else if (named_g_forcelock) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "'lock-file' has no effect "
+ "because the server was run with -X");
+ server->lockfile = isc_mem_strdup(
+ server->mctx, named_g_defaultlockfile);
+ } else {
+ filename = cfg_obj_asstring(obj);
+ server->lockfile = isc_mem_strdup(server->mctx,
+ filename);
+ }
+
+ if (server->lockfile == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ }
+
+ if (named_g_forcelock && named_g_defaultlockfile != NULL) {
+ INSIST(server->lockfile == NULL);
+ server->lockfile = isc_mem_strdup(server->mctx,
+ named_g_defaultlockfile);
+ }
+
+ if (server->lockfile == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (named_os_issingleton(server->lockfile)) {
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not lock %s; another named "
+ "process may be running",
+ server->lockfile);
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+load_configuration(const char *filename, named_server_t *server,
+ bool first_time) {
+ cfg_obj_t *config = NULL, *bindkeys = NULL;
+ cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *builtin_views;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *obj;
+ const cfg_obj_t *options;
+ const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
+ const cfg_obj_t *kasps;
+ dns_kasp_t *kasp = NULL;
+ dns_kasp_t *kasp_next = NULL;
+ dns_kasp_t *default_kasp = NULL;
+ dns_kasplist_t tmpkasplist, kasplist;
+ const cfg_obj_t *views;
+ dns_view_t *view = NULL;
+ dns_view_t *view_next = NULL;
+ dns_viewlist_t tmpviewlist;
+ dns_viewlist_t viewlist, builtin_viewlist;
+ in_port_t listen_port, udpport_low, udpport_high;
+ int i, backlog;
+ int num_zones = 0;
+ bool exclusive = false;
+ isc_interval_t interval;
+ isc_logconfig_t *logc = NULL;
+ isc_portset_t *v4portset = NULL;
+ isc_portset_t *v6portset = NULL;
+ isc_result_t result, tresult;
+ uint32_t heartbeat_interval;
+ uint32_t interface_interval;
+ uint32_t udpsize;
+ uint32_t transfer_message_size;
+ uint32_t recv_tcp_buffer_size;
+ uint32_t send_tcp_buffer_size;
+ uint32_t recv_udp_buffer_size;
+ uint32_t send_udp_buffer_size;
+ named_cache_t *nsc;
+ named_cachelist_t cachelist, tmpcachelist;
+ ns_altsecret_t *altsecret;
+ ns_altsecretlist_t altsecrets, tmpaltsecrets;
+ uint32_t softquota = 0;
+ uint32_t max;
+ uint64_t initial, idle, keepalive, advertised;
+ bool loadbalancesockets;
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
+
+ ISC_LIST_INIT(kasplist);
+ ISC_LIST_INIT(viewlist);
+ ISC_LIST_INIT(builtin_viewlist);
+ ISC_LIST_INIT(cachelist);
+ ISC_LIST_INIT(altsecrets);
+
+ /* Create the ACL configuration context */
+ if (named_g_aclconfctx != NULL) {
+ cfg_aclconfctx_detach(&named_g_aclconfctx);
+ }
+ CHECK(cfg_aclconfctx_create(named_g_mctx, &named_g_aclconfctx));
+
+ /*
+ * Shut down all dyndb instances.
+ */
+ dns_dyndb_cleanup(false);
+
+ /*
+ * Parse the global default pseudo-config file.
+ */
+ if (first_time) {
+ result = named_config_parsedefaults(named_g_parser,
+ &named_g_config);
+ if (result != ISC_R_SUCCESS) {
+ named_main_earlyfatal("unable to load "
+ "internal defaults: %s",
+ isc_result_totext(result));
+ }
+ RUNTIME_CHECK(cfg_map_get(named_g_config, "options",
+ &named_g_defaults) == ISC_R_SUCCESS);
+ }
+
+ /*
+ * Parse the configuration file using the new config code.
+ */
+ config = NULL;
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading configuration from '%s'", filename);
+ CHECK(cfg_parser_create(named_g_mctx, named_g_lctx, &conf_parser));
+ cfg_parser_setcallback(conf_parser, directory_callback, NULL);
+ result = cfg_parse_file(conf_parser, filename, &cfg_type_namedconf,
+ &config);
+
+ CHECK(result);
+
+ /*
+ * Check the validity of the configuration.
+ *
+ * (Ignore plugin parameters for now; they will be
+ * checked later when the modules are actually loaded and
+ * registered.)
+ */
+ CHECK(bind9_check_namedconf(config, false, false, named_g_lctx,
+ named_g_mctx));
+
+ /* Let's recreate the TLS context cache */
+ if (server->tlsctx_server_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_server_cache);
+ }
+
+ isc_tlsctx_cache_create(named_g_mctx, &server->tlsctx_server_cache);
+
+ if (server->tlsctx_client_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_client_cache);
+ }
+
+ isc_tlsctx_cache_create(named_g_mctx, &server->tlsctx_client_cache);
+
+ dns_zonemgr_set_tlsctx_cache(server->zonemgr,
+ server->tlsctx_client_cache);
+
+ /*
+ * Fill in the maps array, used for resolving defaults.
+ */
+ i = 0;
+ options = NULL;
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS) {
+ maps[i++] = options;
+ }
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+#if HAVE_LIBNGHTTP2
+ obj = NULL;
+ result = named_config_get(maps, "http-port", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ named_g_httpport = (in_port_t)cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "https-port", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "http-listener-clients", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ named_g_http_listener_clients = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "http-streams-per-connection", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ named_g_http_streams_per_conn = cfg_obj_asuint32(obj);
+#endif
+
+ /*
+ * If bind.keys exists, load it. If "dnssec-validation auto"
+ * is turned on, the root key found there will be used as a
+ * default trust anchor.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "bindkeys-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->bindkeysfile, cfg_obj_asstring(obj)),
+ "strdup");
+ INSIST(server->bindkeysfile != NULL);
+
+ if (access(server->bindkeysfile, R_OK) == 0) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reading built-in trust anchors "
+ "from file '%s'",
+ server->bindkeysfile);
+
+ CHECK(cfg_parser_create(named_g_mctx, named_g_lctx,
+ &bindkeys_parser));
+
+ result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
+ &cfg_type_bindkeys, &bindkeys);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "unable to parse '%s' error '%s'; using "
+ "built-in keys instead",
+ server->bindkeysfile,
+ isc_result_totext(result));
+ }
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "unable to open '%s'; using built-in keys "
+ "instead",
+ server->bindkeysfile);
+ }
+
+ /* Ensure exclusive access to configuration data. */
+ if (!exclusive) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+ }
+
+ /*
+ * Set process limits, which (usually) needs to be done as root.
+ */
+ set_limits(maps);
+
+ /*
+ * Check the process lockfile.
+ */
+ CHECK(check_lockfile(server, config, first_time));
+
+#if defined(HAVE_GEOIP2)
+ /*
+ * Release any previously opened GeoIP2 databases.
+ */
+ named_geoip_unload();
+
+ /*
+ * Initialize GeoIP databases from the configured location.
+ * This should happen before configuring any ACLs, so that we
+ * know what databases are available and can reject any GeoIP
+ * ACLs that can't work.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "geoip-directory", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ char *dir;
+ DE_CONST(cfg_obj_asstring(obj), dir);
+ named_geoip_load(dir);
+ }
+ named_g_aclconfctx->geoip = named_g_geoip;
+#endif /* HAVE_GEOIP2 */
+
+ /*
+ * Configure various server options.
+ */
+ configure_server_quota(maps, "transfers-out",
+ &server->sctx->xfroutquota);
+ configure_server_quota(maps, "tcp-clients", &server->sctx->tcpquota);
+ configure_server_quota(maps, "recursive-clients",
+ &server->sctx->recursionquota);
+ configure_server_quota(maps, "update-quota", &server->sctx->updquota);
+
+ max = isc_quota_getmax(&server->sctx->recursionquota);
+ if (max > 1000) {
+ unsigned margin = ISC_MAX(100, named_g_cpus + 1);
+ if (margin + 100 > max) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "'recursive-clients %d' too low when "
+ "running with %d worker threads",
+ max, named_g_cpus);
+ CHECK(ISC_R_RANGE);
+ }
+ softquota = max - margin;
+ } else {
+ softquota = (max * 90) / 100;
+ }
+
+ isc_quota_soft(&server->sctx->recursionquota, softquota);
+
+ /*
+ * Set "blackhole". Only legal at options level; there is
+ * no default.
+ */
+ CHECK(configure_view_acl(NULL, config, NULL, "blackhole", NULL,
+ named_g_aclconfctx, named_g_mctx,
+ &server->sctx->blackholeacl));
+ if (server->sctx->blackholeacl != NULL) {
+ dns_dispatchmgr_setblackhole(named_g_dispatchmgr,
+ server->sctx->blackholeacl);
+ }
+
+ /*
+ * Set "keep-response-order". Only legal at options or
+ * global defaults level.
+ */
+ CHECK(configure_view_acl(NULL, config, named_g_config,
+ "keep-response-order", NULL,
+ named_g_aclconfctx, named_g_mctx,
+ &server->sctx->keepresporder));
+
+ obj = NULL;
+ result = named_config_get(maps, "match-mapped-addresses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ env->match_mapped = cfg_obj_asboolean(obj);
+
+ CHECKM(named_statschannels_configure(named_g_server, config,
+ named_g_aclconfctx),
+ "configuring statistics server(s)");
+
+ /*
+ * Configure the network manager
+ */
+ obj = NULL;
+ result = named_config_get(maps, "tcp-initial-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ initial = cfg_obj_asuint32(obj) * 100;
+ if (initial > MAX_INITIAL_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-initial-timeout value is out of range: "
+ "lowering to %" PRIu32,
+ MAX_INITIAL_TIMEOUT / 100);
+ initial = MAX_INITIAL_TIMEOUT;
+ } else if (initial < MIN_INITIAL_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-initial-timeout value is out of range: "
+ "raising to %" PRIu32,
+ MIN_INITIAL_TIMEOUT / 100);
+ initial = MIN_INITIAL_TIMEOUT;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "tcp-idle-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ idle = cfg_obj_asuint32(obj) * 100;
+ if (idle > MAX_IDLE_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-idle-timeout value is out of range: "
+ "lowering to %" PRIu32,
+ MAX_IDLE_TIMEOUT / 100);
+ idle = MAX_IDLE_TIMEOUT;
+ } else if (idle < MIN_IDLE_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-idle-timeout value is out of range: "
+ "raising to %" PRIu32,
+ MIN_IDLE_TIMEOUT / 100);
+ idle = MIN_IDLE_TIMEOUT;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "tcp-keepalive-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ keepalive = cfg_obj_asuint32(obj) * 100;
+ if (keepalive > MAX_KEEPALIVE_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-keepalive-timeout value is out of range: "
+ "lowering to %" PRIu32,
+ MAX_KEEPALIVE_TIMEOUT / 100);
+ keepalive = MAX_KEEPALIVE_TIMEOUT;
+ } else if (keepalive < MIN_KEEPALIVE_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-keepalive-timeout value is out of range: "
+ "raising to %" PRIu32,
+ MIN_KEEPALIVE_TIMEOUT / 100);
+ keepalive = MIN_KEEPALIVE_TIMEOUT;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "tcp-advertised-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ advertised = cfg_obj_asuint32(obj) * 100;
+ if (advertised > MAX_ADVERTISED_TIMEOUT) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "tcp-advertized-timeout value is out of range: "
+ "lowering to %" PRIu32,
+ MAX_ADVERTISED_TIMEOUT / 100);
+ advertised = MAX_ADVERTISED_TIMEOUT;
+ }
+
+ isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive,
+ advertised);
+
+#define CAP_IF_NOT_ZERO(v, min, max) \
+ if (v > 0 && v < min) { \
+ v = min; \
+ } else if (v > max) { \
+ v = max; \
+ }
+
+ /* Set the kernel send and receive buffer sizes */
+ obj = NULL;
+ result = named_config_get(maps, "tcp-receive-buffer", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ recv_tcp_buffer_size = cfg_obj_asuint32(obj);
+ CAP_IF_NOT_ZERO(recv_tcp_buffer_size, 4096, INT32_MAX);
+
+ obj = NULL;
+ result = named_config_get(maps, "tcp-send-buffer", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ send_tcp_buffer_size = cfg_obj_asuint32(obj);
+ CAP_IF_NOT_ZERO(send_tcp_buffer_size, 4096, INT32_MAX);
+
+ obj = NULL;
+ result = named_config_get(maps, "udp-receive-buffer", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ recv_udp_buffer_size = cfg_obj_asuint32(obj);
+ CAP_IF_NOT_ZERO(recv_udp_buffer_size, 4096, INT32_MAX);
+
+ obj = NULL;
+ result = named_config_get(maps, "udp-send-buffer", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ send_udp_buffer_size = cfg_obj_asuint32(obj);
+ CAP_IF_NOT_ZERO(send_udp_buffer_size, 4096, INT32_MAX);
+
+ isc_nm_setnetbuffers(named_g_netmgr, recv_tcp_buffer_size,
+ send_tcp_buffer_size, recv_udp_buffer_size,
+ send_udp_buffer_size);
+
+#undef CAP_IF_NOT_ZERO
+
+ /*
+ * Configure sets of UDP query source ports.
+ */
+ CHECKM(isc_portset_create(named_g_mctx, &v4portset), "creating UDP "
+ "port set");
+ CHECKM(isc_portset_create(named_g_mctx, &v6portset), "creating UDP "
+ "port set");
+
+ usev4ports = NULL;
+ usev6ports = NULL;
+ avoidv4ports = NULL;
+ avoidv6ports = NULL;
+
+ (void)named_config_get(maps, "use-v4-udp-ports", &usev4ports);
+ if (usev4ports != NULL) {
+ portset_fromconf(v4portset, usev4ports, true);
+ } else {
+ CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
+ &udpport_high),
+ "get the default UDP/IPv4 port range");
+ if (udpport_low == udpport_high) {
+ isc_portset_add(v4portset, udpport_low);
+ } else {
+ isc_portset_addrange(v4portset, udpport_low,
+ udpport_high);
+ }
+ if (!ns_server_getoption(server->sctx, NS_SERVER_DISABLE4)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using default UDP/IPv4 port range: "
+ "[%d, %d]",
+ udpport_low, udpport_high);
+ }
+ }
+ (void)named_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
+ if (avoidv4ports != NULL) {
+ portset_fromconf(v4portset, avoidv4ports, false);
+ }
+
+ (void)named_config_get(maps, "use-v6-udp-ports", &usev6ports);
+ if (usev6ports != NULL) {
+ portset_fromconf(v6portset, usev6ports, true);
+ } else {
+ CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
+ &udpport_high),
+ "get the default UDP/IPv6 port range");
+ if (udpport_low == udpport_high) {
+ isc_portset_add(v6portset, udpport_low);
+ } else {
+ isc_portset_addrange(v6portset, udpport_low,
+ udpport_high);
+ }
+ if (!ns_server_getoption(server->sctx, NS_SERVER_DISABLE6)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using default UDP/IPv6 port range: "
+ "[%d, %d]",
+ udpport_low, udpport_high);
+ }
+ }
+ (void)named_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
+ if (avoidv6ports != NULL) {
+ portset_fromconf(v6portset, avoidv6ports, false);
+ }
+
+ dns_dispatchmgr_setavailports(named_g_dispatchmgr, v4portset,
+ v6portset);
+
+ /*
+ * Set the EDNS UDP size when we don't match a view.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512) {
+ udpsize = 512;
+ }
+ if (udpsize > 4096) {
+ udpsize = 4096;
+ }
+ server->sctx->udpsize = (uint16_t)udpsize;
+
+ /* Set the transfer message size for TCP */
+ obj = NULL;
+ result = named_config_get(maps, "transfer-message-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ transfer_message_size = cfg_obj_asuint32(obj);
+ if (transfer_message_size < 512) {
+ transfer_message_size = 512;
+ } else if (transfer_message_size > 65535) {
+ transfer_message_size = 65535;
+ }
+ server->sctx->transfer_tcp_message_size =
+ (uint16_t)transfer_message_size;
+
+ /*
+ * Configure the zone manager.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "transfers-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "transfers-per-ns", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "notify-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setnotifyrate(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "startup-notify-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setstartupnotifyrate(server->zonemgr,
+ cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "serial-query-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
+
+ /*
+ * Determine which port to use for listening for incoming connections.
+ */
+ if (named_g_port != 0) {
+ listen_port = named_g_port;
+ } else {
+ CHECKM(named_config_getport(config, "port", &listen_port),
+ "port");
+ }
+
+ /*
+ * Find the listen queue depth.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "tcp-listen-queue", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ backlog = cfg_obj_asuint32(obj);
+ if ((backlog > 0) && (backlog < 10)) {
+ backlog = 10;
+ }
+ ns_interfacemgr_setbacklog(server->interfacemgr, backlog);
+
+ obj = NULL;
+ result = named_config_get(maps, "reuseport", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ loadbalancesockets = cfg_obj_asboolean(obj);
+#if HAVE_SO_REUSEPORT_LB
+ if (first_time) {
+ isc_nm_setloadbalancesockets(named_g_netmgr,
+ cfg_obj_asboolean(obj));
+ } else if (loadbalancesockets !=
+ isc_nm_getloadbalancesockets(named_g_netmgr))
+ {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "changing reuseport value requires server restart");
+ }
+#else
+ if (loadbalancesockets) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "reuseport has no effect on this system");
+ }
+#endif
+
+ /*
+ * Configure the interface manager according to the "listen-on"
+ * statement.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ clistenon = NULL;
+ /*
+ * Even though listen-on is present in the default
+ * configuration, this way is easier.
+ */
+ if (options != NULL) {
+ (void)cfg_map_get(options, "listen-on", &clistenon);
+ }
+ if (clistenon != NULL) {
+ CHECK(listenlist_fromconfig(
+ clistenon, config, named_g_aclconfctx,
+ named_g_mctx, AF_INET,
+ server->tlsctx_server_cache, &listenon));
+ } else {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(named_g_mctx, listen_port,
+ true, AF_INET, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon4(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+ /*
+ * Ditto for IPv6.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ if (options != NULL) {
+ (void)cfg_map_get(options, "listen-on-v6", &clistenon);
+ }
+ if (clistenon != NULL) {
+ CHECK(listenlist_fromconfig(
+ clistenon, config, named_g_aclconfctx,
+ named_g_mctx, AF_INET6,
+ server->tlsctx_server_cache, &listenon));
+ } else {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(named_g_mctx, listen_port,
+ true, AF_INET6, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon6(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+
+ /*
+ * Rescan the interface list to pick up changes in the
+ * listen-on option. It's important that we do this before we try
+ * to configure the query source, since the dispatcher we use might
+ * be shared with an interface.
+ */
+ result = ns_interfacemgr_scan(server->interfacemgr, true, true);
+
+ /*
+ * Check that named is able to TCP listen on at least one
+ * interface. Otherwise, another named process could be running
+ * and we should fail.
+ */
+ if (first_time && (result == ISC_R_ADDRINUSE)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to listen on any configured interfaces");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * Arrange for further interface scanning to occur periodically
+ * as specified by the "interface-interval" option.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "interface-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ interface_interval = cfg_obj_asduration(obj);
+ if (interface_interval == 0) {
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_inactive, NULL, NULL,
+ true));
+ } else if (server->interface_interval != interface_interval) {
+ isc_interval_set(&interval, interface_interval, 0);
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_ticker, NULL, &interval,
+ false));
+ }
+ server->interface_interval = interface_interval;
+
+ /*
+ * Enable automatic interface scans.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "automatic-interface-scan", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->sctx->interface_auto = cfg_obj_asboolean(obj);
+
+ /*
+ * Configure the dialup heartbeat timer.
+ */
+ obj = NULL;
+ result = named_config_get(maps, "heartbeat-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ heartbeat_interval = cfg_obj_asuint32(obj) * 60;
+ if (heartbeat_interval == 0) {
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_inactive, NULL, NULL,
+ true));
+ } else if (server->heartbeat_interval != heartbeat_interval) {
+ isc_interval_set(&interval, heartbeat_interval, 0);
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_ticker, NULL, &interval,
+ false));
+ }
+ server->heartbeat_interval = heartbeat_interval;
+
+ isc_interval_set(&interval, 1200, 0);
+ CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
+ &interval, false));
+
+ isc_interval_set(&interval, named_g_tat_interval, 0);
+ CHECK(isc_timer_reset(server->tat_timer, isc_timertype_ticker, NULL,
+ &interval, false));
+
+ /*
+ * Write the PID file.
+ */
+ obj = NULL;
+ if (named_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS) {
+ if (cfg_obj_isvoid(obj)) {
+ named_os_writepidfile(NULL, first_time);
+ } else {
+ named_os_writepidfile(cfg_obj_asstring(obj),
+ first_time);
+ }
+ } else {
+ named_os_writepidfile(named_g_defaultpidfile, first_time);
+ }
+
+ /*
+ * Configure the server-wide session key. This must be done before
+ * configure views because zone configuration may need to know
+ * session-keyname.
+ *
+ * Failure of session key generation isn't fatal at this time; if it
+ * turns out that a session key is really needed but doesn't exist,
+ * we'll treat it as a fatal error then.
+ */
+ (void)configure_session_key(maps, server, named_g_mctx, first_time);
+
+ /*
+ * Create the built-in kasp policies ("default", "insecure").
+ */
+ kasps = NULL;
+ (void)cfg_map_get(named_g_config, "dnssec-policy", &kasps);
+ for (element = cfg_list_first(kasps); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *kconfig = cfg_listelt_value(element);
+
+ kasp = NULL;
+ CHECK(cfg_kasp_fromconfig(kconfig, default_kasp, named_g_mctx,
+ named_g_lctx, &kasplist, &kasp));
+ INSIST(kasp != NULL);
+ dns_kasp_freeze(kasp);
+
+ /* Insist that the first built-in policy is the default one. */
+ if (default_kasp == NULL) {
+ INSIST(strcmp(dns_kasp_getname(kasp), "default") == 0);
+ dns_kasp_attach(kasp, &default_kasp);
+ }
+
+ dns_kasp_detach(&kasp);
+ }
+ INSIST(default_kasp != NULL);
+
+ /*
+ * Create the DNSSEC key and signing policies (KASP).
+ */
+ kasps = NULL;
+ (void)cfg_map_get(config, "dnssec-policy", &kasps);
+ for (element = cfg_list_first(kasps); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *kconfig = cfg_listelt_value(element);
+ kasp = NULL;
+ CHECK(cfg_kasp_fromconfig(kconfig, default_kasp, named_g_mctx,
+ named_g_lctx, &kasplist, &kasp));
+ INSIST(kasp != NULL);
+ dns_kasp_freeze(kasp);
+ dns_kasp_detach(&kasp);
+ }
+
+ dns_kasp_detach(&default_kasp);
+ tmpkasplist = server->kasplist;
+ server->kasplist = kasplist;
+ kasplist = tmpkasplist;
+
+ /*
+ * Configure the views.
+ */
+ views = NULL;
+ (void)cfg_map_get(config, "view", &views);
+
+ /*
+ * Create the views and count all the configured zones in
+ * order to correctly size the zone manager's task table.
+ * (We only count zones for configured views; the built-in
+ * "bind" view can be ignored as it only adds a negligible
+ * number of zones.)
+ *
+ * If we're allowing new zones, we need to be able to find the
+ * new zone file and count those as well. So we setup the new
+ * zone configuration context, but otherwise view configuration
+ * waits until after the zone manager's task list has been sized.
+ */
+ for (element = cfg_list_first(views); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+ const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
+ int nzf_num_zones;
+
+ view = NULL;
+
+ CHECK(create_view(vconfig, &viewlist, &view));
+ INSIST(view != NULL);
+
+ num_zones += count_zones(voptions);
+
+ CHECK(setup_newzones(view, config, vconfig, conf_parser,
+ named_g_aclconfctx, &nzf_num_zones));
+ num_zones += nzf_num_zones;
+
+ dns_view_detach(&view);
+ }
+
+ /*
+ * If there were no explicit views then we do the default
+ * view here.
+ */
+ if (views == NULL) {
+ int nzf_num_zones;
+
+ CHECK(create_view(NULL, &viewlist, &view));
+ INSIST(view != NULL);
+
+ num_zones = count_zones(config);
+
+ CHECK(setup_newzones(view, config, NULL, conf_parser,
+ named_g_aclconfctx, &nzf_num_zones));
+ num_zones += nzf_num_zones;
+
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Zones have been counted; set the zone manager task pool size.
+ */
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "sizing zone task pool based on %d zones", num_zones);
+ CHECK(dns_zonemgr_setsize(named_g_server->zonemgr, num_zones));
+
+ /*
+ * Configure and freeze all explicit views. Explicit
+ * views that have zones were already created at parsing
+ * time, but views with no zones must be created here.
+ */
+ for (element = cfg_list_first(views); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+
+ view = NULL;
+ CHECK(find_view(vconfig, &viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, vconfig,
+ &cachelist, &server->kasplist, bindkeys,
+ named_g_mctx, named_g_aclconfctx, true));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Make sure we have a default view if and only if there
+ * were no explicit views.
+ */
+ if (views == NULL) {
+ view = NULL;
+ CHECK(find_view(NULL, &viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, NULL, &cachelist,
+ &server->kasplist, bindkeys, named_g_mctx,
+ named_g_aclconfctx, true));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Create (or recreate) the built-in views.
+ */
+ builtin_views = NULL;
+ RUNTIME_CHECK(cfg_map_get(named_g_config, "view", &builtin_views) ==
+ ISC_R_SUCCESS);
+ for (element = cfg_list_first(builtin_views); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+
+ CHECK(create_view(vconfig, &builtin_viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, vconfig,
+ &cachelist, &server->kasplist, bindkeys,
+ named_g_mctx, named_g_aclconfctx, false));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ view = NULL;
+ }
+
+ /* Now combine the two viewlists into one */
+ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
+
+ /*
+ * Commit any dns_zone_setview() calls on all zones in the new
+ * view.
+ */
+ for (view = ISC_LIST_HEAD(viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ dns_view_setviewcommit(view);
+ }
+
+ /* Swap our new view list with the production one. */
+ tmpviewlist = server->viewlist;
+ server->viewlist = viewlist;
+ viewlist = tmpviewlist;
+
+ /* Make the view list available to each of the views */
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ view->viewlist = &server->viewlist;
+ view = ISC_LIST_NEXT(view, link);
+ }
+
+ /* Swap our new cache list with the production one. */
+ tmpcachelist = server->cachelist;
+ server->cachelist = cachelist;
+ cachelist = tmpcachelist;
+
+ /* Load the TKEY information from the configuration. */
+ if (options != NULL) {
+ dns_tkeyctx_t *t = NULL;
+ CHECKM(named_tkeyctx_fromconfig(options, named_g_mctx, &t),
+ "configuring TKEY");
+ if (server->sctx->tkeyctx != NULL) {
+ dns_tkeyctx_destroy(&server->sctx->tkeyctx);
+ }
+ server->sctx->tkeyctx = t;
+ }
+
+ /*
+ * Bind the control port(s).
+ */
+ CHECKM(named_controls_configure(named_g_server->controls, config,
+ named_g_aclconfctx),
+ "binding control channel(s)");
+
+#ifdef HAVE_LMDB
+ /*
+ * If we're using LMDB, we may have created newzones databases
+ * as root, making it impossible to reopen them later after
+ * switching to a new userid. We close them now, and reopen
+ * after relinquishing privileges them.
+ */
+ if (first_time) {
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ nzd_env_close(view);
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ /*
+ * Relinquish root privileges.
+ */
+ if (first_time) {
+ named_os_changeuser();
+ }
+
+ /*
+ * Check that the working directory is writable.
+ */
+ if (!isc_file_isdirwritable(".")) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "the working directory is not writable");
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+
+#ifdef HAVE_LMDB
+ /*
+ * Reopen NZD databases.
+ */
+ if (first_time) {
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ nzd_env_reopen(view);
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ /*
+ * Configure the logging system.
+ *
+ * Do this after changing UID to make sure that any log
+ * files specified in named.conf get created by the
+ * unprivileged user, not root.
+ */
+ if (named_g_logstderr) {
+ const cfg_obj_t *logobj = NULL;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "not using config file logging "
+ "statement for logging due to "
+ "-g option");
+
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ result = named_logconfig(NULL, logobj);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "checking logging configuration "
+ "failed: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ }
+ } else {
+ const cfg_obj_t *logobj = NULL;
+
+ isc_logconfig_create(named_g_lctx, &logc);
+
+ logobj = NULL;
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ CHECKM(named_logconfig(logc, logobj),
+ "configuring logging");
+ } else {
+ named_log_setdefaultchannels(logc);
+ named_log_setdefaultsslkeylogfile(logc);
+ CHECKM(named_log_setunmatchedcategory(logc),
+ "setting up default 'category unmatched'");
+ CHECKM(named_log_setdefaultcategory(logc),
+ "setting up default 'category default'");
+ }
+
+ isc_logconfig_use(named_g_lctx, logc);
+ logc = NULL;
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "now using logging configuration from "
+ "config file");
+ }
+
+ /*
+ * Set the default value of the query logging flag depending
+ * whether a "queries" category has been defined. This is
+ * a disgusting hack, but we need to do this for BIND 8
+ * compatibility.
+ */
+ if (first_time) {
+ const cfg_obj_t *logobj = NULL;
+ const cfg_obj_t *categories = NULL;
+
+ obj = NULL;
+ if (named_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
+ ns_server_setoption(server->sctx, NS_SERVER_LOGQUERIES,
+ cfg_obj_asboolean(obj));
+ } else {
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ (void)cfg_map_get(logobj, "category",
+ &categories);
+ }
+ if (categories != NULL) {
+ for (element = cfg_list_first(categories);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *catobj;
+ const char *str;
+
+ obj = cfg_listelt_value(element);
+ catobj = cfg_tuple_get(obj, "name");
+ str = cfg_obj_asstring(catobj);
+ if (strcasecmp(str, "queries") == 0) {
+ ns_server_setoption(
+ server->sctx,
+ NS_SERVER_LOGQUERIES,
+ true);
+ }
+ }
+ }
+ }
+ }
+
+ obj = NULL;
+ if (options != NULL &&
+ cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
+ {
+ named_g_memstatistics = cfg_obj_asboolean(obj);
+ } else {
+ named_g_memstatistics =
+ ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
+ }
+
+ obj = NULL;
+ if (named_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
+ {
+ named_main_setmemstats(cfg_obj_asstring(obj));
+ } else if (named_g_memstatistics) {
+ named_main_setmemstats("named.memstats");
+ } else {
+ named_main_setmemstats(NULL);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "statistics-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = named_config_get(maps, "dump-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = named_config_get(maps, "secroots-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = named_config_get(maps, "recursing-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = named_config_get(maps, "version", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->version, obj), "strdup");
+ server->version_set = true;
+ } else {
+ server->version_set = false;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "hostname", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
+ server->hostname_set = true;
+ } else {
+ server->hostname_set = false;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "server-id", &obj);
+ server->sctx->usehostname = false;
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ /* The parser translates "hostname" to true */
+ server->sctx->usehostname = true;
+ result = ns_server_setserverid(server->sctx, NULL);
+ } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ /* Found a quoted string */
+ result = ns_server_setserverid(server->sctx,
+ cfg_obj_asstring(obj));
+ } else {
+ result = ns_server_setserverid(server->sctx, NULL);
+ }
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ obj = NULL;
+ result = named_config_get(maps, "flush-zones-on-shutdown", &obj);
+ if (result == ISC_R_SUCCESS) {
+ server->flushonshutdown = cfg_obj_asboolean(obj);
+ } else {
+ server->flushonshutdown = false;
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "answer-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->sctx->answercookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "cookie-algorithm", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (strcasecmp(cfg_obj_asstring(obj), "siphash24") == 0) {
+ server->sctx->cookiealg = ns_cookiealg_siphash24;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "aes") == 0) {
+ server->sctx->cookiealg = ns_cookiealg_aes;
+ } else {
+ UNREACHABLE();
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "cookie-secret", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *str;
+ bool first = true;
+ isc_buffer_t b;
+ unsigned int usedlength;
+ unsigned int expectedlength;
+
+ for (element = cfg_list_first(obj); element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+
+ if (first) {
+ memset(server->sctx->secret, 0,
+ sizeof(server->sctx->secret));
+ isc_buffer_init(&b, server->sctx->secret,
+ sizeof(server->sctx->secret));
+ result = isc_hex_decodestring(str, &b);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_NOSPACE)
+ {
+ goto cleanup;
+ }
+ first = false;
+ } else {
+ altsecret = isc_mem_get(server->sctx->mctx,
+ sizeof(*altsecret));
+ isc_buffer_init(&b, altsecret->secret,
+ sizeof(altsecret->secret));
+ result = isc_hex_decodestring(str, &b);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_NOSPACE)
+ {
+ isc_mem_put(server->sctx->mctx,
+ altsecret,
+ sizeof(*altsecret));
+ goto cleanup;
+ }
+ ISC_LIST_INITANDAPPEND(altsecrets, altsecret,
+ link);
+ }
+
+ usedlength = isc_buffer_usedlength(&b);
+ switch (server->sctx->cookiealg) {
+ case ns_cookiealg_siphash24:
+ expectedlength = ISC_SIPHASH24_KEY_LENGTH;
+ if (usedlength != expectedlength) {
+ CHECKM(ISC_R_RANGE, "SipHash-2-4 "
+ "cookie-secret "
+ "must be 128 bits");
+ }
+ break;
+ case ns_cookiealg_aes:
+ expectedlength = ISC_AES128_KEYLENGTH;
+ if (usedlength != expectedlength) {
+ CHECKM(ISC_R_RANGE, "AES cookie-secret "
+ "must be 128 bits");
+ }
+ break;
+ }
+ }
+ } else {
+ isc_nonce_buf(server->sctx->secret,
+ sizeof(server->sctx->secret));
+ }
+
+ /*
+ * Swap altsecrets lists.
+ */
+ tmpaltsecrets = server->sctx->altsecrets;
+ server->sctx->altsecrets = altsecrets;
+ altsecrets = tmpaltsecrets;
+
+ (void)named_server_loadnta(server);
+
+#ifdef USE_DNSRPS
+ /*
+ * Start and connect to the DNS Response Policy Service
+ * daemon, dnsrpzd, for each view that uses DNSRPS.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ result = dns_dnsrps_connect(view->rpzs);
+ if (result != ISC_R_SUCCESS) {
+ view = NULL;
+ goto cleanup;
+ }
+ }
+#endif /* ifdef USE_DNSRPS */
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (logc != NULL) {
+ isc_logconfig_destroy(&logc);
+ }
+
+ if (v4portset != NULL) {
+ isc_portset_destroy(named_g_mctx, &v4portset);
+ }
+
+ if (v6portset != NULL) {
+ isc_portset_destroy(named_g_mctx, &v6portset);
+ }
+
+ if (conf_parser != NULL) {
+ if (config != NULL) {
+ cfg_obj_destroy(conf_parser, &config);
+ }
+ cfg_parser_destroy(&conf_parser);
+ }
+
+ if (bindkeys_parser != NULL) {
+ if (bindkeys != NULL) {
+ cfg_obj_destroy(bindkeys_parser, &bindkeys);
+ }
+ cfg_parser_destroy(&bindkeys_parser);
+ }
+
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+
+ if (kasp != NULL) {
+ dns_kasp_detach(&kasp);
+ }
+
+ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
+
+ /*
+ * This cleans up either the old production view list
+ * or our temporary list depending on whether they
+ * were swapped above or not.
+ */
+ for (view = ISC_LIST_HEAD(viewlist); view != NULL; view = view_next) {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(viewlist, view, link);
+ if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0)
+ {
+ dns_view_setviewrevert(view);
+ (void)dns_zt_apply(view->zonetable, isc_rwlocktype_read,
+ false, NULL, removed, view);
+ }
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Same cleanup for kasp list.
+ */
+ for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) {
+ kasp_next = ISC_LIST_NEXT(kasp, link);
+ ISC_LIST_UNLINK(kasplist, kasp, link);
+ dns_kasp_detach(&kasp);
+ }
+
+ /* Same cleanup for cache list. */
+ while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
+ ISC_LIST_UNLINK(cachelist, nsc, link);
+ dns_cache_detach(&nsc->cache);
+ isc_mem_put(server->mctx, nsc, sizeof(*nsc));
+ }
+
+ /* Cleanup for altsecrets list. */
+ while ((altsecret = ISC_LIST_HEAD(altsecrets)) != NULL) {
+ ISC_LIST_UNLINK(altsecrets, altsecret, link);
+ isc_mem_put(server->sctx->mctx, altsecret, sizeof(*altsecret));
+ }
+
+ /*
+ * Record the time of most recent configuration
+ */
+ tresult = isc_time_now(&named_g_configtime);
+ if (tresult != ISC_R_SUCCESS) {
+ named_main_earlyfatal("isc_time_now() failed: %s",
+ isc_result_totext(result));
+ }
+
+ /* Relinquish exclusive access to configuration data. */
+ if (exclusive) {
+ isc_task_endexclusive(server->task);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "load_configuration: %s", isc_result_totext(result));
+
+ return (result);
+}
+
+static isc_result_t
+view_loaded(void *arg) {
+ isc_result_t result;
+ ns_zoneload_t *zl = (ns_zoneload_t *)arg;
+
+ /*
+ * Force zone maintenance. Do this after loading
+ * so that we know when we need to force AXFR of
+ * secondary zones whose master files are missing.
+ *
+ * We use the zoneload reference counter to let us
+ * know when all views are finished.
+ */
+ if (isc_refcount_decrement(&zl->refs) == 1) {
+ named_server_t *server = zl->server;
+ bool reconfig = zl->reconfig;
+ dns_view_t *view = NULL;
+
+ isc_refcount_destroy(&zl->refs);
+ isc_mem_put(server->mctx, zl, sizeof(*zl));
+
+ /*
+ * To maintain compatibility with log parsing tools that might
+ * be looking for this string after "rndc reconfig", we keep it
+ * as it is
+ */
+ if (reconfig) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "any newly configured zones are now "
+ "loaded");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "all zones loaded");
+ }
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->managed_keys != NULL) {
+ result = dns_zone_synckeyzone(
+ view->managed_keys);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx,
+ DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_DNSSEC,
+ ISC_LOG_ERROR,
+ "failed to initialize "
+ "managed-keys for view %s "
+ "(%s): DNSSEC validation is "
+ "at risk",
+ view->name,
+ isc_result_totext(result));
+ }
+ }
+ }
+
+ CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr),
+ "forcing zone maintenance");
+
+ named_os_started();
+
+#ifdef HAVE_FIPS_MODE
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "FIPS mode is %s",
+ FIPS_mode() ? "enabled" : "disabled");
+#endif /* ifdef HAVE_FIPS_MODE */
+ atomic_store(&server->reload_status, NAMED_RELOAD_DONE);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "running");
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_zones(named_server_t *server, bool init, bool reconfig) {
+ isc_result_t result;
+ isc_taskmgr_t *taskmgr = dns_zonemgr_gettaskmgr(server->zonemgr);
+ ns_zoneload_t *zl = NULL;
+ dns_view_t *view = NULL;
+
+ zl = isc_mem_get(server->mctx, sizeof(*zl));
+ zl->server = server;
+ zl->reconfig = reconfig;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_refcount_init(&zl->refs, 1);
+
+ /*
+ * Schedule zones to be loaded from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->managed_keys != NULL) {
+ result = dns_zone_load(view->managed_keys, false);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UPTODATE &&
+ result != DNS_R_CONTINUE)
+ {
+ goto cleanup;
+ }
+ }
+ if (view->redirect != NULL) {
+ result = dns_zone_load(view->redirect, false);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UPTODATE &&
+ result != DNS_R_CONTINUE)
+ {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * 'dns_view_asyncload' calls view_loaded if there are no
+ * zones.
+ */
+ isc_refcount_increment(&zl->refs);
+ result = dns_view_asyncload(view, reconfig, view_loaded, zl);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_decrement1(&zl->refs);
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (isc_refcount_decrement(&zl->refs) == 1) {
+ isc_refcount_destroy(&zl->refs);
+ isc_mem_put(server->mctx, zl, sizeof(*zl));
+ }
+
+ if (init) {
+ /*
+ * If we're setting up the server for the first time, set
+ * the task manager into privileged mode; this ensures
+ * that no other tasks will begin to run until after zone
+ * loading is complete. We won't return from exclusive mode
+ * until the loading is finished; we can then drop out of
+ * privileged mode.
+ *
+ * We do *not* want to do this in the case of reload or
+ * reconfig, as loading a large zone could cause the server
+ * to be inactive for too long a time.
+ */
+ isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged);
+ isc_task_endexclusive(server->task);
+ isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_normal);
+ } else {
+ isc_task_endexclusive(server->task);
+ }
+
+ return (result);
+}
+
+static void
+run_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ named_server_t *server = (named_server_t *)event->ev_arg;
+ dns_geoip_databases_t *geoip;
+
+ INSIST(task == server->task);
+
+ isc_event_free(&event);
+
+ CHECKFATAL(dns_dispatchmgr_create(named_g_mctx, named_g_netmgr,
+ &named_g_dispatchmgr),
+ "creating dispatch manager");
+
+ dns_dispatchmgr_setstats(named_g_dispatchmgr, server->resolverstats);
+
+#if defined(HAVE_GEOIP2)
+ geoip = named_g_geoip;
+#else /* if defined(HAVE_GEOIP2) */
+ geoip = NULL;
+#endif /* if defined(HAVE_GEOIP2) */
+
+ CHECKFATAL(ns_interfacemgr_create(named_g_mctx, server->sctx,
+ named_g_taskmgr, named_g_timermgr,
+ named_g_netmgr, named_g_dispatchmgr,
+ server->task, geoip, named_g_cpus,
+ true, &server->interfacemgr),
+ "creating interface manager");
+
+ CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ interface_timer_tick, server,
+ &server->interface_timer),
+ "creating interface timer");
+
+ CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ heartbeat_timer_tick, server,
+ &server->heartbeat_timer),
+ "creating heartbeat timer");
+
+ CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task, tat_timer_tick,
+ server, &server->tat_timer),
+ "creating trust anchor telemetry timer");
+
+ CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task, pps_timer_tick,
+ server, &server->pps_timer),
+ "creating pps timer");
+
+ CHECKFATAL(
+ cfg_parser_create(named_g_mctx, named_g_lctx, &named_g_parser),
+ "creating default configuration parser");
+
+ CHECKFATAL(cfg_parser_create(named_g_mctx, named_g_lctx,
+ &named_g_addparser),
+ "creating additional configuration parser");
+
+ CHECKFATAL(load_configuration(named_g_conffile, server, true),
+ "loading configuration");
+
+ CHECKFATAL(load_zones(server, true, false), "loading zones");
+#ifdef ENABLE_AFL
+ named_g_run_done = true;
+#endif /* ifdef ENABLE_AFL */
+}
+
+void
+named_server_flushonshutdown(named_server_t *server, bool flush) {
+ REQUIRE(NAMED_SERVER_VALID(server));
+
+ server->flushonshutdown = flush;
+}
+
+static void
+shutdown_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_view_t *view, *view_next = NULL;
+ dns_kasp_t *kasp, *kasp_next = NULL;
+ named_server_t *server = (named_server_t *)event->ev_arg;
+ bool flush = server->flushonshutdown;
+ named_cache_t *nsc;
+
+ UNUSED(task);
+ INSIST(task == server->task);
+
+ /*
+ * We need to shutdown the interface before going
+ * exclusive (which would pause the netmgr).
+ */
+ ns_interfacemgr_shutdown(server->interfacemgr);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, "shutting down%s",
+ flush ? ": flushing changes" : "");
+
+ named_statschannels_shutdown(server);
+ named_controls_shutdown(server->controls);
+ end_reserved_dispatches(server, true);
+ cleanup_session_key(server, server->mctx);
+
+ if (named_g_aclconfctx != NULL) {
+ cfg_aclconfctx_detach(&named_g_aclconfctx);
+ }
+
+ cfg_obj_destroy(named_g_parser, &named_g_config);
+ cfg_parser_destroy(&named_g_parser);
+ cfg_parser_destroy(&named_g_addparser);
+
+ (void)named_server_saventa(server);
+
+ for (kasp = ISC_LIST_HEAD(server->kasplist); kasp != NULL;
+ kasp = kasp_next)
+ {
+ kasp_next = ISC_LIST_NEXT(kasp, link);
+ ISC_LIST_UNLINK(server->kasplist, kasp, link);
+ dns_kasp_detach(&kasp);
+ }
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = view_next)
+ {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(server->viewlist, view, link);
+ if (flush) {
+ dns_view_flushanddetach(&view);
+ } else {
+ dns_view_detach(&view);
+ }
+ }
+
+ /*
+ * Shut down all dyndb instances.
+ */
+ dns_dyndb_cleanup(true);
+
+ while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
+ ISC_LIST_UNLINK(server->cachelist, nsc, link);
+ dns_cache_detach(&nsc->cache);
+ isc_mem_put(server->mctx, nsc, sizeof(*nsc));
+ }
+
+ isc_timer_destroy(&server->interface_timer);
+ isc_timer_destroy(&server->heartbeat_timer);
+ isc_timer_destroy(&server->pps_timer);
+ isc_timer_destroy(&server->tat_timer);
+
+ ns_interfacemgr_detach(&server->interfacemgr);
+
+ dns_dispatchmgr_detach(&named_g_dispatchmgr);
+
+ dns_zonemgr_shutdown(server->zonemgr);
+
+ if (named_g_sessionkey != NULL) {
+ dns_tsigkey_detach(&named_g_sessionkey);
+ dns_name_free(&named_g_sessionkeyname, server->mctx);
+ }
+#if defined(HAVE_GEOIP2)
+ named_geoip_shutdown();
+#endif /* HAVE_GEOIP2 */
+
+ dns_db_detach(&server->in_roothints);
+
+ isc_task_endexclusive(server->task);
+
+ isc_task_detach(&server->task);
+
+ isc_event_free(&event);
+}
+
+/*%
+ * Find a view that matches the source and destination addresses of a query.
+ */
+static isc_result_t
+get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
+ dns_message_t *message, dns_aclenv_t *env,
+ isc_result_t *sigresult, dns_view_t **viewp) {
+ dns_view_t *view;
+
+ REQUIRE(message != NULL);
+ REQUIRE(sigresult != NULL);
+ REQUIRE(viewp != NULL && *viewp == NULL);
+
+ for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (message->rdclass == view->rdclass ||
+ message->rdclass == dns_rdataclass_any)
+ {
+ const dns_name_t *tsig = NULL;
+
+ *sigresult = dns_message_rechecksig(message, view);
+ if (*sigresult == ISC_R_SUCCESS) {
+ dns_tsigkey_t *tsigkey;
+
+ tsigkey = message->tsigkey;
+ tsig = dns_tsigkey_identity(tsigkey);
+ }
+
+ if (dns_acl_allowed(srcaddr, tsig, view->matchclients,
+ env) &&
+ dns_acl_allowed(destaddr, tsig,
+ view->matchdestinations, env) &&
+ !(view->matchrecursiveonly &&
+ (message->flags & DNS_MESSAGEFLAG_RD) == 0))
+ {
+ dns_view_attach(view, viewp);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+void
+named_server_create(isc_mem_t *mctx, named_server_t **serverp) {
+ isc_result_t result;
+ named_server_t *server = isc_mem_get(mctx, sizeof(*server));
+
+ *server = (named_server_t){
+ .mctx = mctx,
+ .statsfile = isc_mem_strdup(mctx, "named.stats"),
+ .bindkeysfile = isc_mem_strdup(mctx, named_g_defaultbindkeys),
+ .dumpfile = isc_mem_strdup(mctx, "named_dump.db"),
+ .secrootsfile = isc_mem_strdup(mctx, "named.secroots"),
+ .recfile = isc_mem_strdup(mctx, "named.recursing"),
+ };
+
+#ifdef USE_DNSRPS
+ CHECKFATAL(dns_dnsrps_server_create(), "initializing RPZ service "
+ "interface");
+#endif /* ifdef USE_DNSRPS */
+
+ /* Initialize server data structures. */
+ ISC_LIST_INIT(server->kasplist);
+ ISC_LIST_INIT(server->viewlist);
+
+ /* Must be first. */
+ CHECKFATAL(dst_lib_init(named_g_mctx, named_g_engine), "initializing "
+ "DST");
+
+ CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
+ &server->in_roothints),
+ "setting up root hints");
+
+ atomic_init(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
+
+ /*
+ * Setup the server task, which is responsible for coordinating
+ * startup and shutdown of the server, as well as all exclusive
+ * tasks.
+ */
+ CHECKFATAL(isc_task_create_bound(named_g_taskmgr, 0, &server->task, 0),
+ "creating server task");
+ isc_task_setname(server->task, "server", server);
+ isc_taskmgr_setexcltask(named_g_taskmgr, server->task);
+
+ CHECKFATAL(ns_server_create(mctx, get_matching_view, &server->sctx),
+ "creating server context");
+
+#if defined(HAVE_GEOIP2)
+ /*
+ * GeoIP must be initialized before the interface
+ * manager (which includes the ACL environment)
+ * is created.
+ */
+ named_geoip_init();
+#endif /* HAVE_GEOIP2 */
+
+#ifdef ENABLE_AFL
+ server->sctx->fuzztype = named_g_fuzz_type;
+ server->sctx->fuzznotify = named_fuzz_notify;
+#endif /* ifdef ENABLE_AFL */
+
+ CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
+ "isc_task_onshutdown");
+ CHECKFATAL(
+ isc_app_onrun(named_g_mctx, server->task, run_server, server),
+ "isc_app_onrun");
+
+ CHECKFATAL(dns_zonemgr_create(named_g_mctx, named_g_taskmgr,
+ named_g_timermgr, named_g_netmgr,
+ &server->zonemgr),
+ "dns_zonemgr_create");
+ CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000), "dns_zonemgr_"
+ "setsize");
+
+ CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
+ isc_sockstatscounter_max),
+ "isc_stats_create");
+ isc_nm_setstats(named_g_netmgr, server->sockstats);
+
+ CHECKFATAL(isc_stats_create(named_g_mctx, &server->zonestats,
+ dns_zonestatscounter_max),
+ "dns_stats_create (zone)");
+
+ CHECKFATAL(isc_stats_create(named_g_mctx, &server->resolverstats,
+ dns_resstatscounter_max),
+ "dns_stats_create (resolver)");
+
+ CHECKFATAL(named_controls_create(server, &server->controls),
+ "named_controls_create");
+
+ ISC_LIST_INIT(server->dispatches);
+
+ ISC_LIST_INIT(server->statschannels);
+
+ ISC_LIST_INIT(server->cachelist);
+
+ server->magic = NAMED_SERVER_MAGIC;
+
+ *serverp = server;
+}
+
+void
+named_server_destroy(named_server_t **serverp) {
+ named_server_t *server = *serverp;
+ REQUIRE(NAMED_SERVER_VALID(server));
+
+#ifdef HAVE_DNSTAP
+ if (server->dtenv != NULL) {
+ dns_dt_detach(&server->dtenv);
+ }
+#endif /* HAVE_DNSTAP */
+
+#ifdef USE_DNSRPS
+ dns_dnsrps_server_destroy();
+#endif /* ifdef USE_DNSRPS */
+
+ named_controls_destroy(&server->controls);
+
+ isc_stats_detach(&server->zonestats);
+ isc_stats_detach(&server->sockstats);
+ isc_stats_detach(&server->resolverstats);
+
+ if (server->sctx != NULL) {
+ ns_server_detach(&server->sctx);
+ }
+
+ isc_mem_free(server->mctx, server->statsfile);
+ isc_mem_free(server->mctx, server->bindkeysfile);
+ isc_mem_free(server->mctx, server->dumpfile);
+ isc_mem_free(server->mctx, server->secrootsfile);
+ isc_mem_free(server->mctx, server->recfile);
+
+ if (server->version != NULL) {
+ isc_mem_free(server->mctx, server->version);
+ }
+ if (server->hostname != NULL) {
+ isc_mem_free(server->mctx, server->hostname);
+ }
+ if (server->lockfile != NULL) {
+ isc_mem_free(server->mctx, server->lockfile);
+ }
+
+ if (server->zonemgr != NULL) {
+ dns_zonemgr_detach(&server->zonemgr);
+ }
+
+ dst_lib_destroy();
+
+ INSIST(ISC_LIST_EMPTY(server->kasplist));
+ INSIST(ISC_LIST_EMPTY(server->viewlist));
+ INSIST(ISC_LIST_EMPTY(server->cachelist));
+
+ if (server->tlsctx_server_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_server_cache);
+ }
+
+ if (server->tlsctx_client_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_client_cache);
+ }
+
+ server->magic = 0;
+ isc_mem_put(server->mctx, server, sizeof(*server));
+ *serverp = NULL;
+}
+
+static void
+fatal(named_server_t *server, const char *msg, isc_result_t result) {
+ if (server != NULL && server->task != NULL) {
+ /*
+ * Prevent races between the OpenSSL on_exit registered
+ * function and any other OpenSSL calls from other tasks
+ * by requesting exclusive access to the task manager.
+ */
+ (void)isc_task_beginexclusive(server->task);
+ }
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "%s: %s", msg,
+ isc_result_totext(result));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_CRITICAL,
+ "exiting (due to fatal error)");
+ named_os_shutdown();
+ exit(1);
+}
+
+static void
+start_reserved_dispatches(named_server_t *server) {
+ REQUIRE(NAMED_SERVER_VALID(server));
+
+ server->dispatchgen++;
+}
+
+static void
+end_reserved_dispatches(named_server_t *server, bool all) {
+ named_dispatch_t *dispatch, *nextdispatch;
+
+ REQUIRE(NAMED_SERVER_VALID(server));
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL;
+ dispatch = nextdispatch)
+ {
+ nextdispatch = ISC_LIST_NEXT(dispatch, link);
+ if (!all && server->dispatchgen == dispatch->dispatchgen) {
+ continue;
+ }
+ ISC_LIST_UNLINK(server->dispatches, dispatch, link);
+ dns_dispatch_detach(&dispatch->dispatch);
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ }
+}
+
+void
+named_add_reserved_dispatch(named_server_t *server,
+ const isc_sockaddr_t *addr) {
+ named_dispatch_t *dispatch;
+ in_port_t port;
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+
+ REQUIRE(NAMED_SERVER_VALID(server));
+
+ port = isc_sockaddr_getport(addr);
+ if (port == 0 || port >= 1024) {
+ return;
+ }
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL;
+ dispatch = ISC_LIST_NEXT(dispatch, link))
+ {
+ if (isc_sockaddr_equal(&dispatch->addr, addr)) {
+ break;
+ }
+ }
+ if (dispatch != NULL) {
+ dispatch->dispatchgen = server->dispatchgen;
+ return;
+ }
+
+ dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
+
+ dispatch->addr = *addr;
+ dispatch->dispatchgen = server->dispatchgen;
+ dispatch->dispatch = NULL;
+
+ result = dns_dispatch_createudp(named_g_dispatchmgr, &dispatch->addr,
+ &dispatch->dispatch);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
+
+ return;
+
+cleanup:
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "unable to create dispatch for reserved port %s: %s",
+ addrbuf, isc_result_totext(result));
+}
+
+static isc_result_t
+loadconfig(named_server_t *server) {
+ isc_result_t result;
+ start_reserved_dispatches(server);
+ result = load_configuration(named_g_conffile, server, false);
+ if (result == ISC_R_SUCCESS) {
+ end_reserved_dispatches(server, false);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reloading configuration succeeded");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading configuration failed: %s",
+ isc_result_totext(result));
+ atomic_store(&server->reload_status, NAMED_RELOAD_FAILED);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+reload(named_server_t *server) {
+ isc_result_t result;
+
+ atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
+
+ CHECK(loadconfig(server));
+
+ result = load_zones(server, false, false);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reloading zones succeeded");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading zones failed: %s",
+ isc_result_totext(result));
+ atomic_store(&server->reload_status, NAMED_RELOAD_FAILED);
+ }
+cleanup:
+ return (result);
+}
+
+/*
+ * Handle a reload event (from SIGHUP).
+ */
+static void
+named_server_reload(isc_task_t *task, isc_event_t *event) {
+ named_server_t *server = (named_server_t *)event->ev_sender;
+
+ INSIST(task == server->task);
+ UNUSED(task);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "received SIGHUP signal to reload zones");
+ (void)reload(server);
+
+ isc_event_free(&event);
+}
+
+void
+named_server_reloadwanted(named_server_t *server) {
+ isc_event_t *event = isc_event_allocate(
+ named_g_mctx, server, NAMED_EVENT_RELOAD, named_server_reload,
+ NULL, sizeof(isc_event_t));
+ isc_task_send(server->task, &event);
+}
+
+void
+named_server_scan_interfaces(named_server_t *server) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "automatic interface rescan");
+
+ ns_interfacemgr_scan(server->interfacemgr, true, false);
+}
+
+/*
+ * Get the next token from lexer 'lex'.
+ *
+ * NOTE: the token value for string tokens always uses the same pointer
+ * value. Multiple calls to this function on the same lexer will always
+ * return either that value (lex->data) or NULL. It is necessary to copy
+ * the token into local storage if it needs to be referenced after the next
+ * call to next_token().
+ */
+static char *
+next_token(isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ isc_token_t token;
+
+ token.type = isc_tokentype_unknown;
+ result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF | ISC_LEXOPT_QSTRING,
+ &token);
+
+ switch (result) {
+ case ISC_R_NOMORE:
+ (void)isc_lex_close(lex);
+ break;
+ case ISC_R_SUCCESS:
+ if (token.type == isc_tokentype_eof) {
+ (void)isc_lex_close(lex);
+ }
+ break;
+ case ISC_R_NOSPACE:
+ if (text != NULL) {
+ (void)putstr(text, "token too large");
+ (void)putnull(text);
+ }
+ return (NULL);
+ default:
+ if (text != NULL) {
+ (void)putstr(text, isc_result_totext(result));
+ (void)putnull(text);
+ }
+ return (NULL);
+ }
+
+ if (token.type == isc_tokentype_string ||
+ token.type == isc_tokentype_qstring)
+ {
+ return (token.value.as_textregion.base);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Find the zone specified in the control channel command, if any.
+ * If a zone is specified, point '*zonep' at it, otherwise
+ * set '*zonep' to NULL, and f 'zonename' is not NULL, copy
+ * the zone name into it (N.B. 'zonename' must have space to hold
+ * a full DNS name).
+ *
+ * If 'zonetxt' is set, the caller has already pulled a token
+ * off the command line that is to be used as the zone name. (This
+ * is sometimes done when it's necessary to check for an optional
+ * argument before the zone name, as in "rndc sync [-clean] zone".)
+ */
+static isc_result_t
+zone_from_args(named_server_t *server, isc_lex_t *lex, const char *zonetxt,
+ dns_zone_t **zonep, char *zonename, isc_buffer_t **text,
+ bool skip) {
+ char *ptr;
+ char *classtxt;
+ const char *viewtxt = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+ char problem[DNS_NAME_FORMATSIZE + 500] = "";
+ char zonebuf[DNS_NAME_FORMATSIZE];
+ bool redirect = false;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ if (skip) {
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ }
+
+ /* Look for the zone name. */
+ if (zonetxt == NULL) {
+ zonetxt = next_token(lex, text);
+ }
+ if (zonetxt == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Copy zonetxt because it'll be overwritten by next_token() */
+ /* To locate a zone named "-redirect" use "-redirect." */
+ if (strcmp(zonetxt, "-redirect") == 0) {
+ redirect = true;
+ strlcpy(zonebuf, ".", DNS_NAME_FORMATSIZE);
+ } else {
+ strlcpy(zonebuf, zonetxt, DNS_NAME_FORMATSIZE);
+ }
+ if (zonename != NULL) {
+ strlcpy(zonename, redirect ? "." : zonetxt,
+ DNS_NAME_FORMATSIZE);
+ }
+
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromstring(name, zonebuf, 0, NULL));
+
+ /* Look for the optional class name. */
+ classtxt = next_token(lex, text);
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ CHECK(dns_rdataclass_fromtext(&rdclass, &r));
+
+ /* Look for the optional view name. */
+ viewtxt = next_token(lex, text);
+ } else {
+ rdclass = dns_rdataclass_in;
+ }
+
+ if (viewtxt == NULL) {
+ if (redirect) {
+ result = dns_viewlist_find(&server->viewlist,
+ "_default",
+ dns_rdataclass_in, &view);
+ if (result != ISC_R_SUCCESS || view->redirect == NULL) {
+ result = ISC_R_NOTFOUND;
+ snprintf(problem, sizeof(problem),
+ "redirect zone not found in "
+ "_default view");
+ } else {
+ dns_zone_attach(view->redirect, zonep);
+ result = ISC_R_SUCCESS;
+ }
+ } else {
+ result = dns_viewlist_findzone(&server->viewlist, name,
+ (classtxt == NULL),
+ rdclass, zonep);
+ if (result == ISC_R_NOTFOUND) {
+ snprintf(problem, sizeof(problem),
+ "no matching zone '%s' in any view",
+ zonebuf);
+ } else if (result == ISC_R_MULTIPLE) {
+ snprintf(problem, sizeof(problem),
+ "zone '%s' was found in multiple "
+ "views",
+ zonebuf);
+ }
+ }
+ } else {
+ result = dns_viewlist_find(&server->viewlist, viewtxt, rdclass,
+ &view);
+ if (result != ISC_R_SUCCESS) {
+ snprintf(problem, sizeof(problem),
+ "no matching view '%s'", viewtxt);
+ goto report;
+ }
+
+ if (redirect) {
+ if (view->redirect != NULL) {
+ dns_zone_attach(view->redirect, zonep);
+ result = ISC_R_SUCCESS;
+ } else {
+ result = ISC_R_NOTFOUND;
+ }
+ } else {
+ result = dns_zt_find(view->zonetable, name, 0, NULL,
+ zonep);
+ }
+ if (result != ISC_R_SUCCESS) {
+ snprintf(problem, sizeof(problem),
+ "no matching zone '%s' in view '%s'", zonebuf,
+ viewtxt);
+ }
+ }
+
+ /* Partial match? */
+ if (result != ISC_R_SUCCESS && *zonep != NULL) {
+ dns_zone_detach(zonep);
+ }
+ if (result == DNS_R_PARTIALMATCH) {
+ result = ISC_R_NOTFOUND;
+ }
+report:
+ if (result != ISC_R_SUCCESS) {
+ isc_result_t tresult;
+
+ tresult = putstr(text, problem);
+ if (tresult == ISC_R_SUCCESS) {
+ (void)putnull(text);
+ }
+ }
+
+cleanup:
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+
+ return (result);
+}
+
+/*
+ * Act on a "retransfer" command from the command channel.
+ */
+isc_result_t
+named_server_retransfercommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zone_t *raw = NULL;
+ dns_zonetype_t type;
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL, text, true);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (zone == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&zone);
+ dns_zone_attach(raw, &zone);
+ dns_zone_detach(&raw);
+ }
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_secondary || type == dns_zone_mirror ||
+ type == dns_zone_stub ||
+ (type == dns_zone_redirect &&
+ dns_zone_getredirecttype(zone) == dns_zone_secondary))
+ {
+ dns_zone_forcereload(zone);
+ } else {
+ (void)putstr(text, "retransfer: inappropriate zone type: ");
+ (void)putstr(text, dns_zonetype_name(type));
+ if (type == dns_zone_redirect) {
+ type = dns_zone_getredirecttype(zone);
+ (void)putstr(text, "(");
+ (void)putstr(text, dns_zonetype_name(type));
+ (void)putstr(text, ")");
+ }
+ (void)putnull(text);
+ result = ISC_R_FAILURE;
+ }
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "reload" command from the command channel.
+ */
+isc_result_t
+named_server_reloadcommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ const char *msg = NULL;
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL, text, true);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (zone == NULL) {
+ result = reload(server);
+ if (result == ISC_R_SUCCESS) {
+ msg = "server reload successful";
+ }
+ } else {
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_secondary || type == dns_zone_mirror ||
+ type == dns_zone_stub)
+ {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ msg = "zone refresh queued";
+ } else {
+ result = dns_zone_load(zone, false);
+ dns_zone_detach(&zone);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ msg = "zone reload successful";
+ break;
+ case DNS_R_CONTINUE:
+ msg = "zone reload queued";
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_UPTODATE:
+ msg = "zone reload up-to-date";
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ /* failure message will be generated by rndc */
+ break;
+ }
+ }
+ }
+ if (msg != NULL) {
+ (void)putstr(text, msg);
+ (void)putnull(text);
+ }
+ return (result);
+}
+
+/*
+ * Act on a "reconfig" command from the command channel.
+ */
+isc_result_t
+named_server_reconfigcommand(named_server_t *server) {
+ isc_result_t result;
+ atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
+
+ CHECK(loadconfig(server));
+
+ result = load_zones(server, false, true);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "scheduled loading new zones");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "loading new zones failed: %s",
+ isc_result_totext(result));
+ atomic_store(&server->reload_status, NAMED_RELOAD_FAILED);
+ }
+cleanup:
+ return (result);
+}
+
+/*
+ * Act on a "notify" command from the command channel.
+ */
+isc_result_t
+named_server_notifycommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const char msg[] = "zone notify queued";
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL, text, true);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (zone == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ dns_zone_notify(zone);
+ dns_zone_detach(&zone);
+ (void)putstr(text, msg);
+ (void)putnull(text);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "refresh" command from the command channel.
+ */
+isc_result_t
+named_server_refreshcommand(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL, *raw = NULL;
+ const char msg1[] = "zone refresh queued";
+ const char msg2[] = "not a secondary, mirror, or stub zone";
+ dns_zonetype_t type;
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL, text, true);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (zone == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&zone);
+ dns_zone_attach(raw, &zone);
+ dns_zone_detach(&raw);
+ }
+
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_secondary || type == dns_zone_mirror ||
+ type == dns_zone_stub)
+ {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ (void)putstr(text, msg1);
+ (void)putnull(text);
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_detach(&zone);
+ (void)putstr(text, msg2);
+ (void)putnull(text);
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) {
+ bool prev, value;
+ char *ptr;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ prev = ns_server_getoption(server->sctx, NS_SERVER_LOGQUERIES);
+
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ value = !prev;
+ } else if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+ {
+ value = true;
+ } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+ {
+ value = false;
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ if (value == prev) {
+ return (ISC_R_SUCCESS);
+ }
+
+ ns_server_setoption(server->sctx, NS_SERVER_LOGQUERIES, value);
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "query logging is now %s", value ? "on" : "off");
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
+ ns_listenlist_t **target) {
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ ns_listenlist_t *dlist = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ result = ns_listenlist_create(mctx, &dlist);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ for (element = cfg_list_first(listenlist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ ns_listenelt_t *delt = NULL;
+ const cfg_obj_t *listener = cfg_listelt_value(element);
+ result = listenelt_fromconfig(listener, config, actx, mctx,
+ family, tlsctx_cache, &delt);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(dlist->elts, delt, link);
+ }
+ *target = dlist;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ ns_listenlist_detach(&dlist);
+ return (result);
+}
+
+static const cfg_obj_t *
+find_maplist(const cfg_obj_t *config, const char *listname, const char *name) {
+ isc_result_t result;
+ const cfg_obj_t *maplist = NULL;
+ const cfg_listelt_t *elt = NULL;
+
+ REQUIRE(config != NULL);
+ REQUIRE(name != NULL);
+
+ result = cfg_map_get(config, listname, &maplist);
+ if (result != ISC_R_SUCCESS) {
+ return (NULL);
+ }
+
+ for (elt = cfg_list_first(maplist); elt != NULL;
+ elt = cfg_list_next(elt))
+ {
+ const cfg_obj_t *map = cfg_listelt_value(elt);
+ if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) ==
+ 0)
+ {
+ return (map);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Create a listen list from the corresponding configuration
+ * data structure.
+ */
+static isc_result_t
+listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
+ ns_listenelt_t **target) {
+ isc_result_t result;
+ const cfg_obj_t *ltup = NULL;
+ const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
+ const cfg_obj_t *portobj = NULL;
+ const cfg_obj_t *http_server = NULL;
+ in_port_t port = 0;
+ const char *key = NULL, *cert = NULL, *ca_file = NULL,
+ *dhparam_file = NULL, *ciphers = NULL;
+ bool tls_prefer_server_ciphers = false,
+ tls_prefer_server_ciphers_set = false;
+ bool tls_session_tickets = false, tls_session_tickets_set = false;
+ bool do_tls = false, no_tls = false, http = false;
+ ns_listenelt_t *delt = NULL;
+ uint32_t tls_protos = 0;
+ ns_listen_tls_params_t tls_params = { 0 };
+ const char *tlsname = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ ltup = cfg_tuple_get(listener, "tuple");
+ RUNTIME_CHECK(ltup != NULL);
+
+ tlsobj = cfg_tuple_get(ltup, "tls");
+ if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) {
+ tlsname = cfg_obj_asstring(tlsobj);
+
+ if (strcasecmp(tlsname, "none") == 0) {
+ no_tls = true;
+ } else if (strcasecmp(tlsname, "ephemeral") == 0) {
+ do_tls = true;
+ } else {
+ const cfg_obj_t *keyobj = NULL, *certobj = NULL,
+ *ca_obj = NULL, *dhparam_obj = NULL;
+ const cfg_obj_t *tlsmap = NULL;
+ const cfg_obj_t *tls_proto_list = NULL;
+ const cfg_obj_t *ciphers_obj = NULL;
+ const cfg_obj_t *prefer_server_ciphers_obj = NULL;
+ const cfg_obj_t *session_tickets_obj = NULL;
+
+ do_tls = true;
+
+ tlsmap = find_maplist(config, "tls", tlsname);
+ if (tlsmap == NULL) {
+ cfg_obj_log(tlsobj, named_g_lctx, ISC_LOG_ERROR,
+ "tls '%s' is not defined",
+ cfg_obj_asstring(tlsobj));
+ return (ISC_R_FAILURE);
+ }
+
+ CHECK(cfg_map_get(tlsmap, "key-file", &keyobj));
+ key = cfg_obj_asstring(keyobj);
+
+ CHECK(cfg_map_get(tlsmap, "cert-file", &certobj));
+ cert = cfg_obj_asstring(certobj);
+
+ if (cfg_map_get(tlsmap, "ca-file", &ca_obj) ==
+ ISC_R_SUCCESS)
+ {
+ ca_file = cfg_obj_asstring(ca_obj);
+ }
+
+ if (cfg_map_get(tlsmap, "protocols", &tls_proto_list) ==
+ ISC_R_SUCCESS)
+ {
+ const cfg_listelt_t *proto = NULL;
+ INSIST(tls_proto_list != NULL);
+ for (proto = cfg_list_first(tls_proto_list);
+ proto != 0; proto = cfg_list_next(proto))
+ {
+ const cfg_obj_t *tls_proto_obj =
+ cfg_listelt_value(proto);
+ const char *tls_sver =
+ cfg_obj_asstring(tls_proto_obj);
+ const isc_tls_protocol_version_t ver =
+ isc_tls_protocol_name_to_version(
+ tls_sver);
+
+ INSIST(ver !=
+ ISC_TLS_PROTO_VER_UNDEFINED);
+ INSIST(isc_tls_protocol_supported(ver));
+ tls_protos |= ver;
+ }
+ }
+
+ if (cfg_map_get(tlsmap, "dhparam-file", &dhparam_obj) ==
+ ISC_R_SUCCESS)
+ {
+ dhparam_file = cfg_obj_asstring(dhparam_obj);
+ }
+
+ if (cfg_map_get(tlsmap, "ciphers", &ciphers_obj) ==
+ ISC_R_SUCCESS)
+ {
+ ciphers = cfg_obj_asstring(ciphers_obj);
+ }
+
+ if (cfg_map_get(tlsmap, "prefer-server-ciphers",
+ &prefer_server_ciphers_obj) ==
+ ISC_R_SUCCESS)
+ {
+ tls_prefer_server_ciphers = cfg_obj_asboolean(
+ prefer_server_ciphers_obj);
+ tls_prefer_server_ciphers_set = true;
+ }
+
+ if (cfg_map_get(tlsmap, "session-tickets",
+ &session_tickets_obj) == ISC_R_SUCCESS)
+ {
+ tls_session_tickets =
+ cfg_obj_asboolean(session_tickets_obj);
+ tls_session_tickets_set = true;
+ }
+ }
+ }
+
+ tls_params = (ns_listen_tls_params_t){
+ .name = tlsname,
+ .key = key,
+ .cert = cert,
+ .ca_file = ca_file,
+ .protocols = tls_protos,
+ .dhparam_file = dhparam_file,
+ .ciphers = ciphers,
+ .prefer_server_ciphers = tls_prefer_server_ciphers,
+ .prefer_server_ciphers_set = tls_prefer_server_ciphers_set,
+ .session_tickets = tls_session_tickets,
+ .session_tickets_set = tls_session_tickets_set
+ };
+
+ httpobj = cfg_tuple_get(ltup, "http");
+ if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
+ const char *httpname = cfg_obj_asstring(httpobj);
+
+ if (!do_tls && !no_tls) {
+ return (ISC_R_FAILURE);
+ }
+
+ http_server = find_maplist(config, "http", httpname);
+ if (http_server == NULL && strcasecmp(httpname, "default") != 0)
+ {
+ cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR,
+ "http '%s' is not defined",
+ cfg_obj_asstring(httpobj));
+ return (ISC_R_FAILURE);
+ }
+
+ http = true;
+ }
+
+ portobj = cfg_tuple_get(ltup, "port");
+ if (!cfg_obj_isuint32(portobj)) {
+ if (http && do_tls) {
+ if (named_g_httpsport != 0) {
+ port = named_g_httpsport;
+ } else {
+ result = named_config_getport(
+ config, "https-port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ } else if (http && !do_tls) {
+ if (named_g_httpport != 0) {
+ port = named_g_httpport;
+ } else {
+ result = named_config_getport(
+ config, "http-port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ } else if (do_tls) {
+ if (named_g_tlsport != 0) {
+ port = named_g_tlsport;
+ } else {
+ result = named_config_getport(
+ config, "tls-port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ } else {
+ if (named_g_port != 0) {
+ port = named_g_port;
+ } else {
+ result = named_config_getport(config, "port",
+ &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ }
+ } else {
+ if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)cfg_obj_asuint32(portobj);
+ }
+
+#ifdef HAVE_LIBNGHTTP2
+ if (http) {
+ CHECK(listenelt_http(http_server, family, do_tls, &tls_params,
+ tlsctx_cache, port, mctx, &delt));
+ }
+#endif /* HAVE_LIBNGHTTP2 */
+
+ if (!http) {
+ CHECK(ns_listenelt_create(mctx, port, NULL, family, do_tls,
+ &tls_params, tlsctx_cache, &delt));
+ }
+
+ result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config,
+ named_g_lctx, actx, mctx, 0, family,
+ &delt->acl);
+ if (result != ISC_R_SUCCESS) {
+ ns_listenelt_destroy(delt);
+ return (result);
+ }
+ *target = delt;
+
+cleanup:
+ return (result);
+}
+
+#ifdef HAVE_LIBNGHTTP2
+static isc_result_t
+listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
+ const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
+ isc_mem_t *mctx, ns_listenelt_t **target) {
+ isc_result_t result = ISC_R_SUCCESS;
+ ns_listenelt_t *delt = NULL;
+ char **endpoints = NULL;
+ const cfg_obj_t *eplist = NULL;
+ const cfg_listelt_t *elt = NULL;
+ size_t len = 1, i = 0;
+ uint32_t max_clients = named_g_http_listener_clients;
+ uint32_t max_streams = named_g_http_streams_per_conn;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ if (tls) {
+ INSIST(tls_params != NULL);
+ INSIST((tls_params->key == NULL) == (tls_params->cert == NULL));
+ }
+
+ if (port == 0) {
+ port = tls ? named_g_httpsport : named_g_httpport;
+ }
+
+ /*
+ * If "default" was used, we set up the default endpoint
+ * of "/dns-query".
+ */
+ if (http != NULL) {
+ const cfg_obj_t *cfg_max_clients = NULL;
+ const cfg_obj_t *cfg_max_streams = NULL;
+
+ if (cfg_map_get(http, "endpoints", &eplist) == ISC_R_SUCCESS) {
+ INSIST(eplist != NULL);
+ len = cfg_list_length(eplist, false);
+ }
+
+ if (cfg_map_get(http, "listener-clients", &cfg_max_clients) ==
+ ISC_R_SUCCESS)
+ {
+ INSIST(cfg_max_clients != NULL);
+ max_clients = cfg_obj_asuint32(cfg_max_clients);
+ }
+
+ if (cfg_map_get(http, "streams-per-connection",
+ &cfg_max_streams) == ISC_R_SUCCESS)
+ {
+ INSIST(cfg_max_streams != NULL);
+ max_streams = cfg_obj_asuint32(cfg_max_streams);
+ }
+ }
+
+ endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len);
+
+ if (http != NULL && eplist != NULL) {
+ for (elt = cfg_list_first(eplist); elt != NULL;
+ elt = cfg_list_next(elt))
+ {
+ const cfg_obj_t *ep = cfg_listelt_value(elt);
+ const char *path = cfg_obj_asstring(ep);
+ endpoints[i++] = isc_mem_strdup(mctx, path);
+ }
+ } else {
+ endpoints[i++] = isc_mem_strdup(mctx, ISC_NM_HTTP_DEFAULT_PATH);
+ }
+
+ INSIST(i == len);
+
+ result = ns_listenelt_create_http(mctx, port, NULL, family, tls,
+ tls_params, tlsctx_cache, endpoints,
+ len, max_clients, max_streams, &delt);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ *target = delt;
+
+ return (result);
+error:
+ if (delt != NULL) {
+ ns_listenelt_destroy(delt);
+ }
+ return (result);
+}
+#endif /* HAVE_LIBNGHTTP2 */
+
+isc_result_t
+named_server_dumpstats(named_server_t *server) {
+ isc_result_t result;
+ FILE *fp = NULL;
+
+ CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
+ "could not open statistics dump file", server->statsfile);
+
+ result = named_stats_dump(server, fp);
+
+cleanup:
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ }
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpstats complete");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpstats failed: %s",
+ isc_result_totext(result));
+ }
+ return (result);
+}
+
+static isc_result_t
+add_zone_tolist(dns_zone_t *zone, void *uap) {
+ struct dumpcontext *dctx = uap;
+ struct zonelistentry *zle;
+
+ zle = isc_mem_get(dctx->mctx, sizeof *zle);
+ zle->zone = NULL;
+ dns_zone_attach(zone, &zle->zone);
+ ISC_LINK_INIT(zle, link);
+ ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
+ struct viewlistentry *vle;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ /*
+ * Prevent duplicate views.
+ */
+ for (vle = ISC_LIST_HEAD(dctx->viewlist); vle != NULL;
+ vle = ISC_LIST_NEXT(vle, link))
+ {
+ if (vle->view == view) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ vle = isc_mem_get(dctx->mctx, sizeof *vle);
+ vle->view = NULL;
+ dns_view_attach(view, &vle->view);
+ ISC_LINK_INIT(vle, link);
+ ISC_LIST_INIT(vle->zonelist);
+ ISC_LIST_APPEND(dctx->viewlist, vle, link);
+ if (dctx->dumpzones) {
+ result = dns_zt_apply(view->zonetable, isc_rwlocktype_read,
+ true, NULL, add_zone_tolist, dctx);
+ }
+ return (result);
+}
+
+static void
+dumpcontext_destroy(struct dumpcontext *dctx) {
+ struct viewlistentry *vle;
+ struct zonelistentry *zle;
+
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ while (vle != NULL) {
+ ISC_LIST_UNLINK(dctx->viewlist, vle, link);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ while (zle != NULL) {
+ ISC_LIST_UNLINK(vle->zonelist, zle, link);
+ dns_zone_detach(&zle->zone);
+ isc_mem_put(dctx->mctx, zle, sizeof *zle);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ }
+ dns_view_detach(&vle->view);
+ isc_mem_put(dctx->mctx, vle, sizeof *vle);
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ }
+ if (dctx->version != NULL) {
+ dns_db_closeversion(dctx->db, &dctx->version, false);
+ }
+ if (dctx->db != NULL) {
+ dns_db_detach(&dctx->db);
+ }
+ if (dctx->cache != NULL) {
+ dns_db_detach(&dctx->cache);
+ }
+ if (dctx->task != NULL) {
+ isc_task_detach(&dctx->task);
+ }
+ if (dctx->fp != NULL) {
+ (void)isc_stdio_close(dctx->fp);
+ }
+ if (dctx->mdctx != NULL) {
+ dns_dumpctx_detach(&dctx->mdctx);
+ }
+ isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
+}
+
+static void
+dumpdone(void *arg, isc_result_t result) {
+ struct dumpcontext *dctx = arg;
+ char buf[1024 + 32];
+ const dns_master_style_t *style;
+
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (dctx->mdctx != NULL) {
+ dns_dumpctx_detach(&dctx->mdctx);
+ }
+ if (dctx->view == NULL) {
+ dctx->view = ISC_LIST_HEAD(dctx->viewlist);
+ if (dctx->view == NULL) {
+ goto done;
+ }
+ INSIST(dctx->zone == NULL);
+ } else {
+ goto resume;
+ }
+nextview:
+ fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
+resume:
+ if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
+ fprintf(dctx->fp, ";\n; Cache of view '%s' is shared as '%s'\n",
+ dctx->view->view->name,
+ dns_cache_getname(dctx->view->view->cache));
+ } else if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache)
+ {
+ if (dctx->dumpexpired) {
+ style = &dns_master_style_cache_with_expired;
+ } else {
+ style = &dns_master_style_cache;
+ }
+ /* start cache dump */
+ if (dctx->view->view->cachedb != NULL) {
+ dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+ }
+ if (dctx->cache != NULL) {
+ fprintf(dctx->fp,
+ ";\n; Cache dump of view '%s' (cache %s)\n;\n",
+ dctx->view->view->name,
+ dns_cache_getname(dctx->view->view->cache));
+ result = dns_master_dumptostreamasync(
+ dctx->mctx, dctx->cache, NULL, style, dctx->fp,
+ dctx->task, dumpdone, dctx, &dctx->mdctx);
+ if (result == DNS_R_CONTINUE) {
+ return;
+ }
+ if (result == ISC_R_NOTIMPLEMENTED) {
+ fprintf(dctx->fp, "; %s\n",
+ isc_result_totext(result));
+ } else if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ }
+
+ if ((dctx->dumpadb || dctx->dumpbad || dctx->dumpfail) &&
+ dctx->cache == NULL && dctx->view->view->cachedb != NULL)
+ {
+ dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+ }
+
+ if (dctx->cache != NULL) {
+ if (dctx->dumpadb) {
+ dns_adb_dump(dctx->view->view->adb, dctx->fp);
+ }
+ if (dctx->dumpbad) {
+ dns_resolver_printbadcache(dctx->view->view->resolver,
+ dctx->fp);
+ }
+ if (dctx->dumpfail) {
+ dns_badcache_print(dctx->view->view->failcache,
+ "SERVFAIL cache", dctx->fp);
+ }
+ dns_db_detach(&dctx->cache);
+ }
+ if (dctx->dumpzones) {
+ style = &dns_master_style_full;
+ nextzone:
+ if (dctx->version != NULL) {
+ dns_db_closeversion(dctx->db, &dctx->version, false);
+ }
+ if (dctx->db != NULL) {
+ dns_db_detach(&dctx->db);
+ }
+ if (dctx->zone == NULL) {
+ dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
+ } else {
+ dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
+ }
+ if (dctx->zone != NULL) {
+ /* start zone dump */
+ dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
+ fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
+ result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
+ if (result != ISC_R_SUCCESS) {
+ fprintf(dctx->fp, "; %s\n",
+ isc_result_totext(result));
+ goto nextzone;
+ }
+ dns_db_currentversion(dctx->db, &dctx->version);
+ result = dns_master_dumptostreamasync(
+ dctx->mctx, dctx->db, dctx->version, style,
+ dctx->fp, dctx->task, dumpdone, dctx,
+ &dctx->mdctx);
+ if (result == DNS_R_CONTINUE) {
+ return;
+ }
+ if (result == ISC_R_NOTIMPLEMENTED) {
+ fprintf(dctx->fp, "; %s\n",
+ isc_result_totext(result));
+ result = ISC_R_SUCCESS;
+ POST(result);
+ goto nextzone;
+ }
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ }
+ if (dctx->view != NULL) {
+ dctx->view = ISC_LIST_NEXT(dctx->view, link);
+ if (dctx->view != NULL) {
+ goto nextview;
+ }
+ }
+done:
+ fprintf(dctx->fp, "; Dump complete\n");
+ result = isc_stdio_flush(dctx->fp);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb complete");
+ }
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpdb failed: %s", isc_result_totext(result));
+ }
+ dumpcontext_destroy(dctx);
+}
+
+isc_result_t
+named_server_dumpdb(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ struct dumpcontext *dctx = NULL;
+ dns_view_t *view;
+ isc_result_t result;
+ char *ptr;
+ const char *sep;
+ bool found;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ dctx = isc_mem_get(server->mctx, sizeof(*dctx));
+
+ dctx->mctx = server->mctx;
+ dctx->dumpcache = true;
+ dctx->dumpadb = true;
+ dctx->dumpbad = true;
+ dctx->dumpexpired = false;
+ dctx->dumpfail = true;
+ dctx->dumpzones = false;
+ dctx->fp = NULL;
+ ISC_LIST_INIT(dctx->viewlist);
+ dctx->view = NULL;
+ dctx->zone = NULL;
+ dctx->cache = NULL;
+ dctx->mdctx = NULL;
+ dctx->db = NULL;
+ dctx->cache = NULL;
+ dctx->task = NULL;
+ dctx->version = NULL;
+ isc_task_attach(server->task, &dctx->task);
+
+ CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
+ "could not open dump file", server->dumpfile);
+
+ ptr = next_token(lex, NULL);
+ sep = (ptr == NULL) ? "" : ": ";
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb started%s%s", sep, (ptr != NULL) ? ptr : "");
+
+ if (ptr != NULL && strcmp(ptr, "-all") == 0) {
+ /* also dump zones */
+ dctx->dumpzones = true;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
+ /* this is the default */
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-expired") == 0) {
+ /* this is the same as -cache but includes expired data */
+ dctx->dumpexpired = true;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
+ /* only dump zones, suppress caches */
+ dctx->dumpadb = false;
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ dctx->dumpzones = true;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-adb") == 0) {
+ /* only dump adb, suppress other caches */
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-bad") == 0) {
+ /* only dump badcache, suppress other caches */
+ dctx->dumpadb = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-fail") == 0) {
+ /* only dump servfail cache, suppress other caches */
+ dctx->dumpadb = false;
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ ptr = next_token(lex, NULL);
+ }
+
+nextview:
+ found = false;
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0) {
+ continue;
+ }
+ found = true;
+ CHECK(add_view_tolist(dctx, view));
+ }
+ if (ptr != NULL) {
+ if (!found) {
+ CHECK(putstr(text, "view '"));
+ CHECK(putstr(text, ptr));
+ CHECK(putstr(text, "' not found"));
+ CHECK(putnull(text));
+ result = ISC_R_NOTFOUND;
+ dumpdone(dctx, result);
+ return (result);
+ }
+ ptr = next_token(lex, NULL);
+ if (ptr != NULL) {
+ goto nextview;
+ }
+ }
+ dumpdone(dctx, ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dumpcontext_destroy(dctx);
+ return (result);
+}
+
+isc_result_t
+named_server_dumpsecroots(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ dns_view_t *view;
+ dns_keytable_t *secroots = NULL;
+ dns_ntatable_t *ntatable = NULL;
+ isc_result_t result;
+ char *ptr;
+ FILE *fp = NULL;
+ isc_time_t now;
+ char tbuf[64];
+ unsigned int used = isc_buffer_usedlength(*text);
+ bool first = true;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* "-" here means print the output instead of dumping to file */
+ ptr = next_token(lex, text);
+ if (ptr != NULL && strcmp(ptr, "-") == 0) {
+ ptr = next_token(lex, text);
+ } else {
+ result = isc_stdio_open(server->secrootsfile, "w", &fp);
+ if (result != ISC_R_SUCCESS) {
+ (void)putstr(text, "could not open ");
+ (void)putstr(text, server->secrootsfile);
+ CHECKMF(result, "could not open secroots dump file",
+ server->secrootsfile);
+ }
+ }
+
+ TIME_NOW(&now);
+ isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
+ CHECK(putstr(text, "secure roots as of "));
+ CHECK(putstr(text, tbuf));
+ CHECK(putstr(text, ":\n"));
+ used = isc_buffer_usedlength(*text);
+
+ do {
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0) {
+ continue;
+ }
+ if (secroots != NULL) {
+ dns_keytable_detach(&secroots);
+ }
+ result = dns_view_getsecroots(view, &secroots);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ if (first || used != isc_buffer_usedlength(*text)) {
+ CHECK(putstr(text, "\n"));
+ first = false;
+ }
+ CHECK(putstr(text, " Start view "));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, "\n Secure roots:\n\n"));
+ used = isc_buffer_usedlength(*text);
+ CHECK(dns_keytable_totext(secroots, text));
+
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ if (used != isc_buffer_usedlength(*text)) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, " Negative trust anchors:\n\n"));
+ used = isc_buffer_usedlength(*text);
+ CHECK(dns_ntatable_totext(ntatable, NULL, text));
+ }
+
+ if (ptr != NULL) {
+ ptr = next_token(lex, text);
+ }
+ } while (ptr != NULL);
+
+cleanup:
+ if (secroots != NULL) {
+ dns_keytable_detach(&secroots);
+ }
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+
+ if (fp != NULL) {
+ if (used != isc_buffer_usedlength(*text)) {
+ (void)putstr(text, "\n");
+ }
+ fprintf(fp, "%.*s", (int)isc_buffer_usedlength(*text),
+ (char *)isc_buffer_base(*text));
+ isc_buffer_clear(*text);
+ (void)isc_stdio_close(fp);
+ } else if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpsecroots complete");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpsecroots failed: %s",
+ isc_result_totext(result));
+ }
+ return (result);
+}
+
+isc_result_t
+named_server_dumprecursing(named_server_t *server) {
+ FILE *fp = NULL;
+ dns_view_t *view;
+ isc_result_t result;
+
+ CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
+ "could not open dump file", server->recfile);
+ fprintf(fp, ";\n; Recursing Queries\n;\n");
+ ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ fprintf(fp, ";\n; Active fetch domains [view: %s]\n;\n",
+ view->name);
+ dns_resolver_dumpfetches(view->resolver, isc_statsformat_file,
+ fp);
+ }
+
+ fprintf(fp, "; Dump complete\n");
+
+cleanup:
+ if (fp != NULL) {
+ result = isc_stdio_close(fp);
+ }
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumprecursing complete");
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumprecursing failed: %s",
+ isc_result_totext(result));
+ }
+ return (result);
+}
+
+isc_result_t
+named_server_setdebuglevel(named_server_t *server, isc_lex_t *lex) {
+ char *ptr;
+ char *endp;
+ long newlevel;
+
+ UNUSED(server);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Look for the new level name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ if (named_g_debuglevel < 99) {
+ named_g_debuglevel++;
+ }
+ } else {
+ newlevel = strtol(ptr, &endp, 10);
+ if (*endp != '\0' || newlevel < 0 || newlevel > 99) {
+ return (ISC_R_RANGE);
+ }
+ named_g_debuglevel = (unsigned int)newlevel;
+ }
+ isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "debug level is now %u", named_g_debuglevel);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_server_validation(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ char *ptr;
+ dns_view_t *view;
+ bool changed = false;
+ isc_result_t result;
+ bool enable = true, set = true, first = true;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+ {
+ enable = true;
+ } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+ {
+ enable = false;
+ } else if (!strcasecmp(ptr, "check") || !strcasecmp(ptr, "status")) {
+ set = false;
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ /* Look for the view name. */
+ ptr = next_token(lex, text);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if ((ptr != NULL && strcasecmp(ptr, view->name) != 0) ||
+ strcasecmp("_bind", view->name) == 0)
+ {
+ continue;
+ }
+
+ if (set) {
+ CHECK(dns_view_flushcache(view, false));
+ view->enablevalidation = enable;
+ changed = true;
+ } else {
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, "DNSSEC validation is "));
+ CHECK(putstr(text, view->enablevalidation
+ ? "enabled"
+ : "disabled"));
+ CHECK(putstr(text, " (view "));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, ")"));
+ first = false;
+ }
+ }
+ CHECK(putnull(text));
+
+ if (!set) {
+ result = ISC_R_SUCCESS;
+ } else if (changed) {
+ result = ISC_R_SUCCESS;
+ } else {
+ result = ISC_R_FAILURE;
+ }
+cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+named_server_flushcache(named_server_t *server, isc_lex_t *lex) {
+ char *ptr;
+ dns_view_t *view;
+ bool flushed;
+ bool found;
+ isc_result_t result;
+ named_cache_t *nsc;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Look for the view name. */
+ ptr = next_token(lex, NULL);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = true;
+ found = false;
+
+ /*
+ * Flushing a cache is tricky when caches are shared by multiple views.
+ * We first identify which caches should be flushed in the local cache
+ * list, flush these caches, and then update other views that refer to
+ * the flushed cache DB.
+ */
+ if (ptr != NULL) {
+ /*
+ * Mark caches that need to be flushed. This is an O(#view^2)
+ * operation in the very worst case, but should be normally
+ * much more lightweight because only a few (most typically just
+ * one) views will match.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (strcasecmp(ptr, view->name) != 0) {
+ continue;
+ }
+ found = true;
+ for (nsc = ISC_LIST_HEAD(server->cachelist);
+ nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link))
+ {
+ if (nsc->cache == view->cache) {
+ break;
+ }
+ }
+ INSIST(nsc != NULL);
+ nsc->needflush = true;
+ }
+ } else {
+ found = true;
+ }
+
+ /* Perform flush */
+ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link))
+ {
+ if (ptr != NULL && !nsc->needflush) {
+ continue;
+ }
+ nsc->needflush = true;
+ result = dns_view_flushcache(nsc->primaryview, false);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing cache in view '%s' failed: %s",
+ nsc->primaryview->name,
+ isc_result_totext(result));
+ }
+ }
+
+ /*
+ * Fix up views that share a flushed cache: let the views update the
+ * cache DB they're referring to. This could also be an expensive
+ * operation, but should typically be marginal: the inner loop is only
+ * necessary for views that share a cache, and if there are many such
+ * views the number of shared cache should normally be small.
+ * A worst case is that we have n views and n/2 caches, each shared by
+ * two views. Then this will be a O(n^2/4) operation.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (!dns_view_iscacheshared(view)) {
+ continue;
+ }
+ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link))
+ {
+ if (!nsc->needflush || nsc->cache != view->cache) {
+ continue;
+ }
+ result = dns_view_flushcache(view, true);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "fixing cache in view '%s' "
+ "failed: %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+ }
+
+ /* Cleanup the cache list. */
+ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link))
+ {
+ nsc->needflush = false;
+ }
+
+ if (flushed && found) {
+ if (ptr != NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing cache in view '%s' succeeded",
+ ptr);
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing caches in all views succeeded");
+ }
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing cache in view '%s' failed: "
+ "view not found",
+ ptr);
+ result = ISC_R_NOTFOUND;
+ } else {
+ result = ISC_R_FAILURE;
+ }
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+named_server_flushnode(named_server_t *server, isc_lex_t *lex, bool tree) {
+ char *ptr, *viewname;
+ char target[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ bool flushed;
+ bool found;
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Find the domain name to flush. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ strlcpy(target, ptr, DNS_NAME_FORMATSIZE);
+ isc_buffer_constinit(&b, target, strlen(target));
+ isc_buffer_add(&b, strlen(target));
+ name = dns_fixedname_initname(&fixed);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /* Look for the view name. */
+ viewname = next_token(lex, NULL);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = true;
+ found = false;
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0) {
+ continue;
+ }
+ found = true;
+ /*
+ * It's a little inefficient to try flushing name for all views
+ * if some of the views share a single cache. But since the
+ * operation is lightweight we prefer simplicity here.
+ */
+ result = dns_view_flushnode(view, name, tree);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing %s '%s' in cache view '%s' "
+ "failed: %s",
+ tree ? "tree" : "name", target,
+ view->name, isc_result_totext(result));
+ }
+ }
+ if (flushed && found) {
+ if (viewname != NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing %s '%s' in cache view '%s' "
+ "succeeded",
+ tree ? "tree" : "name", target, viewname);
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing %s '%s' in all cache views "
+ "succeeded",
+ tree ? "tree" : "name", target);
+ }
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing %s '%s' in cache view '%s' "
+ "failed: view not found",
+ tree ? "tree" : "name", target, viewname);
+ }
+ result = ISC_R_FAILURE;
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+named_server_status(named_server_t *server, isc_buffer_t **text) {
+ isc_result_t result;
+ unsigned int zonecount, xferrunning, xferdeferred, soaqueries;
+ unsigned int automatic;
+ const char *ob = "", *cb = "", *alt = "";
+ char boottime[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char configtime[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char line[1024], hostname[256];
+ named_reload_t reload_status;
+
+ REQUIRE(text != NULL);
+
+ if (named_g_server->version_set) {
+ ob = " (";
+ cb = ")";
+ if (named_g_server->version == NULL) {
+ alt = "version.bind/txt/ch disabled";
+ } else {
+ alt = named_g_server->version;
+ }
+ }
+ zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
+ xferrunning = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERRUNNING);
+ xferdeferred = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERDEFERRED);
+ soaqueries = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_SOAQUERY);
+ automatic = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_AUTOMATIC);
+
+ isc_time_formathttptimestamp(&named_g_boottime, boottime,
+ sizeof(boottime));
+ isc_time_formathttptimestamp(&named_g_configtime, configtime,
+ sizeof(configtime));
+
+ snprintf(line, sizeof(line), "version: %s%s <id:%s>%s%s%s\n",
+ PACKAGE_STRING, PACKAGE_DESCRIPTION, PACKAGE_SRCID, ob, alt,
+ cb);
+ CHECK(putstr(text, line));
+
+ if (gethostname(hostname, sizeof(hostname)) == 0) {
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
+ snprintf(line, sizeof(line), "running on %s: %s\n", hostname,
+ named_os_uname());
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "boot time: %s\n", boottime);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "last configured: %s\n", configtime);
+ CHECK(putstr(text, line));
+
+ if (named_g_chrootdir != NULL) {
+ snprintf(line, sizeof(line), "configuration file: %s (%s%s)\n",
+ named_g_conffile, named_g_chrootdir, named_g_conffile);
+ } else {
+ snprintf(line, sizeof(line), "configuration file: %s\n",
+ named_g_conffile);
+ }
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "CPUs found: %u\n", named_g_cpus_detected);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "worker threads: %u\n", named_g_cpus);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "UDP listeners per interface: %u\n",
+ named_g_udpdisp);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "number of zones: %u (%u automatic)\n",
+ zonecount, automatic);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "debug level: %u\n", named_g_debuglevel);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "xfers running: %u\n", xferrunning);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "xfers deferred: %u\n", xferdeferred);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "soa queries in progress: %u\n",
+ soaqueries);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "query logging is %s\n",
+ ns_server_getoption(server->sctx, NS_SERVER_LOGQUERIES)
+ ? "ON"
+ : "OFF");
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "recursive clients: %u/%u/%u\n",
+ isc_quota_getused(&server->sctx->recursionquota),
+ isc_quota_getsoft(&server->sctx->recursionquota),
+ isc_quota_getmax(&server->sctx->recursionquota));
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "tcp clients: %u/%u\n",
+ isc_quota_getused(&server->sctx->tcpquota),
+ isc_quota_getmax(&server->sctx->tcpquota));
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "TCP high-water: %u\n",
+ (unsigned)ns_stats_get_counter(server->sctx->nsstats,
+ ns_statscounter_tcphighwater));
+ CHECK(putstr(text, line));
+
+ reload_status = atomic_load(&server->reload_status);
+ if (reload_status != NAMED_RELOAD_DONE) {
+ snprintf(line, sizeof(line), "reload/reconfig %s\n",
+ (reload_status == NAMED_RELOAD_FAILED
+ ? "failed"
+ : "in progress"));
+ CHECK(putstr(text, line));
+ }
+
+ CHECK(putstr(text, "server is up and running"));
+ CHECK(putnull(text));
+
+ return (ISC_R_SUCCESS);
+cleanup:
+ return (result);
+}
+
+isc_result_t
+named_server_testgen(isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ char *ptr;
+ unsigned long count;
+ unsigned long i;
+ const unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ count = 26;
+ } else {
+ count = strtoul(ptr, NULL, 10);
+ }
+
+ CHECK(isc_buffer_reserve(text, count));
+ for (i = 0; i < count; i++) {
+ CHECK(putuint8(text, chars[i % (sizeof(chars) - 1)]));
+ }
+
+ CHECK(putnull(text));
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+delete_keynames(dns_tsig_keyring_t *ring, char *target,
+ unsigned int *foundkeys) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+ dns_rbtnodechain_t chain;
+ dns_name_t foundname;
+ dns_fixedname_t fixedorigin;
+ dns_name_t *origin;
+ dns_rbtnode_t *node;
+ dns_tsigkey_t *tkey;
+
+ dns_name_init(&foundname, NULL);
+ origin = dns_fixedname_initname(&fixedorigin);
+
+again:
+ dns_rbtnodechain_init(&chain);
+ result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
+ if (result == ISC_R_NOTFOUND) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+
+ if (tkey != NULL) {
+ if (!tkey->generated) {
+ goto nextkey;
+ }
+
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ if (strcmp(namestr, target) == 0) {
+ (*foundkeys)++;
+ dns_rbtnodechain_invalidate(&chain);
+ (void)dns_rbt_deletename(ring->keys,
+ &tkey->name, false);
+ goto again;
+ }
+ }
+
+ nextkey:
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (result == ISC_R_NOMORE) {
+ break;
+ }
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_server_tsigdelete(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_view_t *view;
+ unsigned int foundkeys = 0;
+ char *ptr, *viewname;
+ char target[DNS_NAME_FORMATSIZE];
+ char fbuf[16];
+
+ REQUIRE(text != NULL);
+
+ (void)next_token(lex, text); /* skip command name */
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ strlcpy(target, ptr, DNS_NAME_FORMATSIZE);
+
+ viewname = next_token(lex, text);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname == NULL || strcmp(view->name, viewname) == 0) {
+ RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
+ result = delete_keynames(view->dynamickeys, target,
+ &foundkeys);
+ RWUNLOCK(&view->dynamickeys->lock,
+ isc_rwlocktype_write);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ return (result);
+ }
+ }
+ }
+ isc_task_endexclusive(server->task);
+
+ snprintf(fbuf, sizeof(fbuf), "%u", foundkeys);
+
+ CHECK(putstr(text, fbuf));
+ CHECK(putstr(text, " tsig keys deleted."));
+ CHECK(putnull(text));
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t **text,
+ unsigned int *foundkeys) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char creatorstr[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+ dns_rbtnodechain_t chain;
+ dns_name_t foundname;
+ dns_fixedname_t fixedorigin;
+ dns_name_t *origin;
+ dns_rbtnode_t *node;
+ dns_tsigkey_t *tkey;
+ const char *viewname;
+
+ if (view != NULL) {
+ viewname = view->name;
+ } else {
+ viewname = "(global)";
+ }
+
+ dns_name_init(&foundname, NULL);
+ origin = dns_fixedname_initname(&fixedorigin);
+ dns_rbtnodechain_init(&chain);
+ result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
+ if (result == ISC_R_NOTFOUND) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+
+ if (tkey != NULL) {
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ if (tkey->generated) {
+ dns_name_format(tkey->creator, creatorstr,
+ sizeof(creatorstr));
+ if (*foundkeys != 0) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, "view \""));
+ CHECK(putstr(text, viewname));
+ CHECK(putstr(text, "\"; type \"dynamic\"; key "
+ "\""));
+ CHECK(putstr(text, namestr));
+ CHECK(putstr(text, "\"; creator \""));
+ CHECK(putstr(text, creatorstr));
+ CHECK(putstr(text, "\";"));
+ } else {
+ if (*foundkeys != 0) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, "view \""));
+ CHECK(putstr(text, viewname));
+ CHECK(putstr(text, "\"; type \"static\"; key "
+ "\""));
+ CHECK(putstr(text, namestr));
+ CHECK(putstr(text, "\";"));
+ }
+ (*foundkeys)++;
+ }
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (result == ISC_R_NOMORE || result == DNS_R_NEWORIGIN) {
+ break;
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+}
+
+isc_result_t
+named_server_tsiglist(named_server_t *server, isc_buffer_t **text) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_view_t *view;
+ unsigned int foundkeys = 0;
+
+ REQUIRE(text != NULL);
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
+ result = list_keynames(view, view->statickeys, text,
+ &foundkeys);
+ RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
+ result = list_keynames(view, view->dynamickeys, text,
+ &foundkeys);
+ RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ if (foundkeys == 0) {
+ CHECK(putstr(text, "no tsig keys found."));
+ }
+
+ if (isc_buffer_usedlength(*text) > 0) {
+ CHECK(putnull(text));
+ }
+
+cleanup:
+ return (result);
+}
+
+/*
+ * Act on a "sign" or "loadkeys" command from the command channel.
+ */
+isc_result_t
+named_server_rekey(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ uint16_t keyopts;
+ bool fullsign = false;
+ char *ptr;
+
+ REQUIRE(text != NULL);
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcasecmp(ptr, NAMED_COMMAND_SIGN) == 0) {
+ fullsign = true;
+ }
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL, text, false);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (zone == NULL) {
+ return (ISC_R_UNEXPECTEDEND); /* XXX: or do all zones? */
+ }
+
+ type = dns_zone_gettype(zone);
+ if (type != dns_zone_primary) {
+ dns_zone_detach(&zone);
+ return (DNS_R_NOTPRIMARY);
+ }
+
+ keyopts = dns_zone_getkeyopts(zone);
+
+ /*
+ * "rndc loadkeys" requires "auto-dnssec maintain"
+ * or a "dnssec-policy".
+ */
+ if ((keyopts & DNS_ZONEKEY_ALLOW) == 0) {
+ result = ISC_R_NOPERM;
+ } else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign) {
+ result = ISC_R_NOPERM;
+ } else {
+ dns_zone_rekey(zone, fullsign);
+ }
+
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "sync" command from the command channel.
+ */
+static isc_result_t
+synczone(dns_zone_t *zone, void *uap) {
+ bool cleanup = *(bool *)uap;
+ isc_result_t result;
+ dns_zone_t *raw = NULL;
+ char *journal;
+
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ synczone(raw, uap);
+ dns_zone_detach(&raw);
+ }
+
+ result = dns_zone_flush(zone);
+ if (result != ISC_R_SUCCESS) {
+ cleanup = false;
+ }
+ if (cleanup) {
+ journal = dns_zone_getjournal(zone);
+ if (journal != NULL) {
+ (void)isc_file_remove(journal);
+ }
+ }
+
+ return (result);
+}
+
+isc_result_t
+named_server_sync(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_view_t *view;
+ dns_zone_t *zone = NULL;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ const char *vname, *sep, *arg;
+ bool cleanup = false;
+
+ REQUIRE(text != NULL);
+
+ (void)next_token(lex, text);
+
+ arg = next_token(lex, text);
+ if (arg != NULL &&
+ (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0))
+ {
+ cleanup = true;
+ arg = next_token(lex, text);
+ }
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, arg, &zone, NULL, text, false);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (zone == NULL) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ tresult = ISC_R_SUCCESS;
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ result = dns_zt_apply(view->zonetable,
+ isc_rwlocktype_none, false, NULL,
+ synczone, &cleanup);
+ if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS)
+ {
+ tresult = result;
+ }
+ }
+ isc_task_endexclusive(server->task);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumping all zones%s: %s",
+ cleanup ? ", removing journal files" : "",
+ isc_result_totext(result));
+ return (tresult);
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = synczone(zone, &cleanup);
+ isc_task_endexclusive(server->task);
+
+ view = dns_zone_getview(zone);
+ if (strcmp(view->name, "_default") == 0 ||
+ strcmp(view->name, "_bind") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(zone), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "sync: dumping zone '%s/%s'%s%s%s: %s", zonename,
+ classstr, sep, vname, cleanup ? ", removing journal file" : "",
+ isc_result_totext(result));
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "freeze" or "thaw" command from the command channel.
+ */
+isc_result_t
+named_server_freeze(named_server_t *server, bool freeze, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_zone_t *mayberaw = NULL, *raw = NULL;
+ dns_zonetype_t type;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ const char *vname, *sep;
+ bool frozen;
+ const char *msg = NULL;
+
+ REQUIRE(text != NULL);
+
+ result = zone_from_args(server, lex, NULL, &mayberaw, NULL, text, true);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (mayberaw == NULL) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ tresult = ISC_R_SUCCESS;
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ result = dns_view_freezezones(view, freeze);
+ if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS)
+ {
+ tresult = result;
+ }
+ }
+ isc_task_endexclusive(server->task);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s all zones: %s",
+ freeze ? "freezing" : "thawing",
+ isc_result_totext(tresult));
+ return (tresult);
+ }
+ dns_zone_getraw(mayberaw, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&mayberaw);
+ dns_zone_attach(raw, &mayberaw);
+ dns_zone_detach(&raw);
+ }
+ type = dns_zone_gettype(mayberaw);
+ if (type != dns_zone_primary) {
+ dns_zone_detach(&mayberaw);
+ return (DNS_R_NOTPRIMARY);
+ }
+
+ if (freeze && !dns_zone_isdynamic(mayberaw, true)) {
+ dns_zone_detach(&mayberaw);
+ return (DNS_R_NOTDYNAMIC);
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ frozen = dns_zone_getupdatedisabled(mayberaw);
+ if (freeze) {
+ if (frozen) {
+ msg = "WARNING: The zone was already frozen.\n"
+ "Someone else may be editing it or "
+ "it may still be re-loading.";
+ result = DNS_R_FROZEN;
+ }
+ if (result == ISC_R_SUCCESS) {
+ result = dns_zone_flush(mayberaw);
+ if (result != ISC_R_SUCCESS) {
+ msg = "Flushing the zone updates to "
+ "disk failed.";
+ }
+ }
+ if (result == ISC_R_SUCCESS) {
+ dns_zone_setupdatedisabled(mayberaw, freeze);
+ }
+ } else {
+ if (frozen) {
+ result = dns_zone_loadandthaw(mayberaw);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_UPTODATE:
+ msg = "The zone reload and thaw was "
+ "successful.";
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_CONTINUE:
+ msg = "A zone reload and thaw was started.\n"
+ "Check the logs to see the result.";
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ isc_task_endexclusive(server->task);
+
+ if (msg != NULL) {
+ (void)putstr(text, msg);
+ (void)putnull(text);
+ }
+
+ view = dns_zone_getview(mayberaw);
+ if (strcmp(view->name, "_default") == 0 ||
+ strcmp(view->name, "_bind") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(mayberaw), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(mayberaw), zonename,
+ sizeof(zonename));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s zone '%s/%s'%s%s: %s",
+ freeze ? "freezing" : "thawing", zonename, classstr, sep,
+ vname, isc_result_totext(result));
+ dns_zone_detach(&mayberaw);
+ return (result);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * This function adds a message for rndc to echo if named
+ * is managed by smf and is also running chroot.
+ */
+isc_result_t
+named_smf_add_message(isc_buffer_t **text) {
+ REQUIRE(text != NULL);
+
+ return (putstr(text, "use svcadm(1M) to manage named"));
+}
+#endif /* HAVE_LIBSCF */
+
+#ifndef HAVE_LMDB
+
+/*
+ * Emit a comment at the top of the nzf file containing the viewname
+ * Expects the fp to already be open for writing
+ */
+#define HEADER1 "# New zone file for view: "
+#define HEADER2 \
+ "\n# This file contains configuration for zones added by\n" \
+ "# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
+static isc_result_t
+add_comment(FILE *fp, const char *viewname) {
+ isc_result_t result;
+ CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
+ CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
+ CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
+cleanup:
+ return (result);
+}
+
+static void
+dumpzone(void *arg, const char *buf, int len) {
+ FILE *fp = arg;
+
+ (void)isc_stdio_write(buf, len, 1, fp, NULL);
+}
+
+static isc_result_t
+nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) {
+ isc_result_t result;
+ off_t offset;
+ FILE *fp = NULL;
+ bool offsetok = false;
+
+ LOCK(&view->new_zone_lock);
+
+ CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
+ CHECK(isc_stdio_seek(fp, 0, SEEK_END));
+
+ CHECK(isc_stdio_tell(fp, &offset));
+ offsetok = true;
+ if (offset == 0) {
+ CHECK(add_comment(fp, view->name));
+ }
+
+ CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
+ CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+
+cleanup:
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ if (offsetok) {
+ isc_result_t result2;
+
+ result2 = isc_file_truncate(view->new_zone_file,
+ offset);
+ if (result2 != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error truncating NZF file '%s' "
+ "during rollback from append: "
+ "%s",
+ view->new_zone_file,
+ isc_result_totext(result2));
+ }
+ }
+ }
+ UNLOCK(&view->new_zone_lock);
+ return (result);
+}
+
+static isc_result_t
+nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) {
+ const cfg_obj_t *zl = NULL;
+ cfg_list_t *list;
+ const cfg_listelt_t *elt;
+
+ FILE *fp = NULL;
+ char tmp[1024];
+ isc_result_t result;
+
+ result = isc_file_template(view->new_zone_file, "nzf-XXXXXXXX", tmp,
+ sizeof(tmp));
+ if (result == ISC_R_SUCCESS) {
+ result = isc_file_openunique(tmp, &fp);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ cfg_map_get(config, "zone", &zl);
+ if (!cfg_obj_islist(zl)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ DE_CONST(&zl->value.list, list);
+
+ CHECK(add_comment(fp, view->name)); /* force a comment */
+
+ for (elt = ISC_LIST_HEAD(*list); elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(elt);
+
+ CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
+ CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
+ }
+
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ CHECK(isc_file_rename(tmp, view->new_zone_file));
+ return (result);
+
+cleanup:
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ }
+ (void)isc_file_remove(tmp);
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static void
+nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) {
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ dns_name_downcase(name, dns_fixedname_name(&fixed), NULL);
+ dns_name_format(dns_fixedname_name(&fixed), namebuf, buflen);
+
+ key->mv_data = namebuf;
+ key->mv_size = strlen(namebuf);
+}
+
+static void
+dumpzone(void *arg, const char *buf, int len) {
+ ns_dzarg_t *dzarg = arg;
+ isc_result_t result;
+
+ REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC));
+
+ result = putmem(dzarg->text, buf, len);
+ if (result != ISC_R_SUCCESS && dzarg->result == ISC_R_SUCCESS) {
+ dzarg->result = result;
+ }
+}
+
+static isc_result_t
+nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone,
+ const cfg_obj_t *zconfig) {
+ isc_result_t result;
+ int status;
+ dns_view_t *view;
+ bool commit = false;
+ isc_buffer_t *text = NULL;
+ char namebuf[1024];
+ MDB_val key, data;
+ ns_dzarg_t dzarg;
+
+ view = dns_zone_getview(zone);
+
+ nzd_setkey(&key, dns_zone_getorigin(zone), namebuf, sizeof(namebuf));
+
+ if (zconfig == NULL) {
+ /* We're deleting the zone from the database */
+ status = mdb_del(*txnp, dbi, &key, NULL);
+ if (status != MDB_SUCCESS && status != MDB_NOTFOUND) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error deleting zone %s "
+ "from NZD database: %s",
+ namebuf, mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ } else if (status != MDB_NOTFOUND) {
+ commit = true;
+ }
+ } else {
+ /* We're creating or overwriting the zone */
+ const cfg_obj_t *zoptions;
+
+ isc_buffer_allocate(view->mctx, &text, 256);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+ if (zoptions == NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Unable to get options from config in "
+ "nzd_save()");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ dzarg.magic = DZARG_MAGIC;
+ dzarg.text = &text;
+ dzarg.result = ISC_R_SUCCESS;
+ cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg);
+ if (dzarg.result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error writing zone config to "
+ "buffer in nzd_save(): %s",
+ isc_result_totext(dzarg.result));
+ result = dzarg.result;
+ goto cleanup;
+ }
+
+ data.mv_data = isc_buffer_base(text);
+ data.mv_size = isc_buffer_usedlength(text);
+
+ status = mdb_put(*txnp, dbi, &key, &data, 0);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error inserting zone in "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ commit = true;
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (!commit || result != ISC_R_SUCCESS) {
+ (void)mdb_txn_abort(*txnp);
+ } else {
+ status = mdb_txn_commit(*txnp);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error committing "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ }
+ }
+ *txnp = NULL;
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ return (result);
+}
+
+/*
+ * Check whether the new zone database for 'view' can be opened for writing.
+ *
+ * Caller must hold 'view->new_zone_lock'.
+ */
+static isc_result_t
+nzd_writable(dns_view_t *view) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int status;
+ MDB_dbi dbi;
+ MDB_txn *txn = NULL;
+
+ REQUIRE(view != NULL);
+
+ status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, 0, &txn);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "mdb_txn_begin: %s", mdb_strerror(status));
+ return (ISC_R_FAILURE);
+ }
+
+ status = mdb_dbi_open(txn, NULL, 0, &dbi);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "mdb_dbi_open: %s", mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ }
+
+ mdb_txn_abort(txn);
+ return (result);
+}
+
+/*
+ * Open the new zone database for 'view' and start a transaction for it.
+ *
+ * Caller must hold 'view->new_zone_lock'.
+ */
+static isc_result_t
+nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) {
+ int status;
+ MDB_txn *txn = NULL;
+
+ REQUIRE(view != NULL);
+ REQUIRE(txnp != NULL && *txnp == NULL);
+ REQUIRE(dbi != NULL);
+
+ status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, flags, &txn);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "mdb_txn_begin: %s", mdb_strerror(status));
+ goto cleanup;
+ }
+
+ status = mdb_dbi_open(txn, NULL, 0, dbi);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "mdb_dbi_open: %s", mdb_strerror(status));
+ goto cleanup;
+ }
+
+ *txnp = txn;
+
+cleanup:
+ if (status != MDB_SUCCESS) {
+ if (txn != NULL) {
+ mdb_txn_abort(txn);
+ }
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * nzd_env_close() and nzd_env_reopen are a kluge to address the
+ * problem of an NZD file possibly being created before we drop
+ * root privileges.
+ */
+static void
+nzd_env_close(dns_view_t *view) {
+ const char *dbpath = NULL;
+ char dbpath_copy[PATH_MAX];
+ char lockpath[PATH_MAX];
+ int status, ret;
+
+ if (view->new_zone_dbenv == NULL) {
+ return;
+ }
+
+ status = mdb_env_get_path(view->new_zone_dbenv, &dbpath);
+ INSIST(status == MDB_SUCCESS);
+ snprintf(lockpath, sizeof(lockpath), "%s-lock", dbpath);
+ strlcpy(dbpath_copy, dbpath, sizeof(dbpath_copy));
+ mdb_env_close((MDB_env *)view->new_zone_dbenv);
+
+ /*
+ * Database files must be owned by the eventual user, not by root.
+ */
+ ret = chown(dbpath_copy, ns_os_uid(), -1);
+ UNUSED(ret);
+
+ /*
+ * Some platforms need the lockfile not to exist when we reopen the
+ * environment.
+ */
+ (void)isc_file_remove(lockpath);
+
+ view->new_zone_dbenv = NULL;
+}
+
+static isc_result_t
+nzd_env_reopen(dns_view_t *view) {
+ isc_result_t result;
+ MDB_env *env = NULL;
+ int status;
+
+ if (view->new_zone_db == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ nzd_env_close(view);
+
+ status = mdb_env_create(&env);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_create failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (view->new_zone_mapsize != 0ULL) {
+ status = mdb_env_set_mapsize(env, view->new_zone_mapsize);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_set_mapsize failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+ }
+
+ status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_open of '%s' failed: %s",
+ view->new_zone_db, mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ view->new_zone_dbenv = env;
+ env = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (env != NULL) {
+ mdb_env_close(env);
+ }
+ return (result);
+}
+
+/*
+ * If 'commit' is true, commit the new zone database transaction pointed to by
+ * 'txnp'; otherwise, abort that transaction.
+ *
+ * Caller must hold 'view->new_zone_lock' for the view that the transaction
+ * pointed to by 'txnp' was started for.
+ */
+static isc_result_t
+nzd_close(MDB_txn **txnp, bool commit) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int status;
+
+ REQUIRE(txnp != NULL);
+
+ if (*txnp != NULL) {
+ if (commit) {
+ status = mdb_txn_commit(*txnp);
+ if (status != MDB_SUCCESS) {
+ result = ISC_R_FAILURE;
+ }
+ } else {
+ mdb_txn_abort(*txnp);
+ }
+ *txnp = NULL;
+ }
+
+ return (result);
+}
+
+/*
+ * Count the zones configured in the new zone database for 'view' and store the
+ * result in 'countp'.
+ *
+ * Caller must hold 'view->new_zone_lock'.
+ */
+static isc_result_t
+nzd_count(dns_view_t *view, int *countp) {
+ isc_result_t result;
+ int status;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_stat statbuf;
+
+ REQUIRE(countp != NULL);
+
+ result = nzd_open(view, MDB_RDONLY, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = mdb_stat(txn, dbi, &statbuf);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "mdb_stat: %s", mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ *countp = statbuf.ms_entries;
+
+cleanup:
+ (void)nzd_close(&txn, false);
+
+ return (result);
+}
+
+/*
+ * Migrate zone configuration from an NZF file to an NZD database.
+ * Caller must hold view->new_zone_lock.
+ */
+static isc_result_t
+migrate_nzf(dns_view_t *view) {
+ isc_result_t result;
+ cfg_obj_t *nzf_config = NULL;
+ int status, n;
+ isc_buffer_t *text = NULL;
+ bool commit = false;
+ const cfg_obj_t *zonelist;
+ const cfg_listelt_t *element;
+ char tempname[PATH_MAX];
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ ns_dzarg_t dzarg;
+
+ /*
+ * If NZF file doesn't exist, or NZD DB exists and already
+ * has data, return without attempting migration.
+ */
+ if (!isc_file_exists(view->new_zone_file)) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ result = nzd_count(view, &n);
+ if (result == ISC_R_SUCCESS && n > 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "Migrating zones from NZF file '%s' to "
+ "NZD database '%s'",
+ view->new_zone_file, view->new_zone_db);
+ /*
+ * Instead of blindly copying lines, we parse the NZF file using
+ * the configuration parser, because it validates it against the
+ * config type, giving us a guarantee that valid configuration
+ * will be written to DB.
+ */
+ cfg_parser_reset(named_g_addparser);
+ result = cfg_parse_file(named_g_addparser, view->new_zone_file,
+ &cfg_type_addzoneconf, &nzf_config);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error parsing NZF file '%s': %s",
+ view->new_zone_file, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ zonelist = NULL;
+ CHECK(cfg_map_get(nzf_config, "zone", &zonelist));
+ if (!cfg_obj_islist(zonelist)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+
+ isc_buffer_allocate(view->mctx, &text, 256);
+
+ for (element = cfg_list_first(zonelist); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig;
+ const cfg_obj_t *zoptions;
+ char zname[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ const char *origin;
+ isc_buffer_t b;
+
+ zconfig = cfg_listelt_value(element);
+
+ origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ if (origin == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Normalize zone name */
+ isc_buffer_constinit(&b, origin, strlen(origin));
+ isc_buffer_add(&b, strlen(origin));
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ DNS_NAME_DOWNCASE, NULL));
+ dns_name_format(name, zname, sizeof(zname));
+
+ key.mv_data = zname;
+ key.mv_size = strlen(zname);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+ if (zoptions == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ isc_buffer_clear(text);
+ dzarg.magic = DZARG_MAGIC;
+ dzarg.text = &text;
+ dzarg.result = ISC_R_SUCCESS;
+ cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg);
+ if (dzarg.result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error writing zone config to "
+ "buffer in migrate_nzf(): %s",
+ isc_result_totext(result));
+ result = dzarg.result;
+ goto cleanup;
+ }
+
+ data.mv_data = isc_buffer_base(text);
+ data.mv_size = isc_buffer_usedlength(text);
+
+ status = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Error inserting zone in "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ commit = true;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ /*
+ * Leaving the NZF file in place is harmless as we won't use it
+ * if an NZD database is found for the view. But we rename NZF file
+ * to a backup name here.
+ */
+ strlcpy(tempname, view->new_zone_file, sizeof(tempname));
+ if (strlen(tempname) < sizeof(tempname) - 1) {
+ strlcat(tempname, "~", sizeof(tempname));
+ isc_file_rename(view->new_zone_file, tempname);
+ }
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ (void)nzd_close(&txn, false);
+ } else {
+ result = nzd_close(&txn, commit);
+ }
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ if (nzf_config != NULL) {
+ cfg_obj_destroy(named_g_addparser, &nzf_config);
+ }
+
+ return (result);
+}
+
+#endif /* HAVE_LMDB */
+
+static isc_result_t
+newzone_parse(named_server_t *server, char *command, dns_view_t **viewp,
+ cfg_obj_t **zoneconfp, const cfg_obj_t **zoneobjp,
+ bool *redirectp, isc_buffer_t **text) {
+ isc_result_t result;
+ isc_buffer_t argbuf;
+ bool redirect = false;
+ cfg_obj_t *zoneconf = NULL;
+ const cfg_obj_t *zlist = NULL;
+ const cfg_obj_t *zoneobj = NULL;
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *viewname = NULL;
+ dns_rdataclass_t rdclass;
+ dns_view_t *view = NULL;
+ const char *bn = NULL;
+
+ REQUIRE(viewp != NULL && *viewp == NULL);
+ REQUIRE(zoneobjp != NULL && *zoneobjp == NULL);
+ REQUIRE(zoneconfp != NULL && *zoneconfp == NULL);
+ REQUIRE(redirectp != NULL);
+
+ /* Try to parse the argument string */
+ isc_buffer_init(&argbuf, command, (unsigned int)strlen(command));
+ isc_buffer_add(&argbuf, strlen(command));
+
+ if (strncasecmp(command, "add", 3) == 0) {
+ bn = "addzone";
+ } else if (strncasecmp(command, "mod", 3) == 0) {
+ bn = "modzone";
+ } else {
+ UNREACHABLE();
+ }
+
+ /*
+ * Convert the "addzone" or "modzone" to just "zone", for
+ * the benefit of the parser
+ */
+ isc_buffer_forward(&argbuf, 3);
+
+ cfg_parser_reset(named_g_addparser);
+ CHECK(cfg_parse_buffer(named_g_addparser, &argbuf, bn, 0,
+ &cfg_type_addzoneconf, 0, &zoneconf));
+ CHECK(cfg_map_get(zoneconf, "zone", &zlist));
+ if (!cfg_obj_islist(zlist)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* For now we only support adding one zone at a time */
+ zoneobj = cfg_listelt_value(cfg_list_first(zlist));
+
+ /* Check the zone type for ones that are not supported by addzone. */
+ zoptions = cfg_tuple_get(zoneobj, "options");
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "type", &obj);
+ if (obj == NULL) {
+ (void)cfg_map_get(zoptions, "in-view", &obj);
+ if (obj != NULL) {
+ (void)putstr(text, "'in-view' zones not supported by ");
+ (void)putstr(text, bn);
+ } else {
+ (void)putstr(text, "zone type not specified");
+ }
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(cfg_obj_asstring(obj), "hint") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "forward") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "delegation-only") == 0)
+ {
+ (void)putstr(text, "'");
+ (void)putstr(text, cfg_obj_asstring(obj));
+ (void)putstr(text, "' zones not supported by ");
+ (void)putstr(text, bn);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(cfg_obj_asstring(obj), "redirect") == 0) {
+ redirect = true;
+ }
+
+ /* Make sense of optional class argument */
+ obj = cfg_tuple_get(zoneobj, "class");
+ CHECK(named_config_getclass(obj, dns_rdataclass_in, &rdclass));
+
+ /* Make sense of optional view argument */
+ obj = cfg_tuple_get(zoneobj, "view");
+ if (obj && cfg_obj_isstring(obj)) {
+ viewname = cfg_obj_asstring(obj);
+ }
+ if (viewname == NULL || *viewname == '\0') {
+ viewname = "_default";
+ }
+ result = dns_viewlist_find(&server->viewlist, viewname, rdclass, &view);
+ if (result == ISC_R_NOTFOUND) {
+ (void)putstr(text, "no matching view found for '");
+ (void)putstr(text, viewname);
+ (void)putstr(text, "'");
+ goto cleanup;
+ } else if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ *viewp = view;
+ *zoneobjp = zoneobj;
+ *zoneconfp = zoneconf;
+ *redirectp = redirect;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(named_g_addparser, &zoneconf);
+ }
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+delete_zoneconf(dns_view_t *view, cfg_parser_t *pctx, const cfg_obj_t *config,
+ const dns_name_t *zname, nzfwriter_t nzfwriter) {
+ isc_result_t result = ISC_R_NOTFOUND;
+ const cfg_listelt_t *elt = NULL;
+ const cfg_obj_t *zl = NULL;
+ cfg_list_t *list;
+ dns_fixedname_t myfixed;
+ dns_name_t *myname;
+
+ REQUIRE(view != NULL);
+ REQUIRE(pctx != NULL);
+ REQUIRE(config != NULL);
+ REQUIRE(zname != NULL);
+
+ LOCK(&view->new_zone_lock);
+
+ cfg_map_get(config, "zone", &zl);
+
+ if (!cfg_obj_islist(zl)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ DE_CONST(&zl->value.list, list);
+
+ myname = dns_fixedname_initname(&myfixed);
+
+ for (elt = ISC_LIST_HEAD(*list); elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ const cfg_obj_t *zconf = cfg_listelt_value(elt);
+ const char *zn;
+ cfg_listelt_t *e;
+
+ zn = cfg_obj_asstring(cfg_tuple_get(zconf, "name"));
+ result = dns_name_fromstring(myname, zn, 0, NULL);
+ if (result != ISC_R_SUCCESS || !dns_name_equal(zname, myname)) {
+ continue;
+ }
+
+ DE_CONST(elt, e);
+ ISC_LIST_UNLINK(*list, e, link);
+ cfg_obj_destroy(pctx, &e->obj);
+ isc_mem_put(pctx->mctx, e, sizeof(*e));
+ result = ISC_R_SUCCESS;
+ break;
+ }
+
+ /*
+ * Write config to NZF file if appropriate
+ */
+ if (nzfwriter != NULL && view->new_zone_file != NULL) {
+ result = nzfwriter(config, view);
+ }
+
+cleanup:
+ UNLOCK(&view->new_zone_lock);
+ return (result);
+}
+
+static isc_result_t
+do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
+ dns_name_t *name, cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj,
+ bool redirect, isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+#ifndef HAVE_LMDB
+ FILE *fp = NULL;
+ bool cleanup_config = false;
+#else /* HAVE_LMDB */
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ bool locked = false;
+
+ UNUSED(zoneconf);
+#endif
+
+ /* Zone shouldn't already exist */
+ if (redirect) {
+ result = (view->redirect != NULL) ? ISC_R_SUCCESS
+ : ISC_R_NOTFOUND;
+ } else {
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ }
+ if (result == ISC_R_SUCCESS) {
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ } else if (result == DNS_R_PARTIALMATCH) {
+ /* Create our sub-zone anyway */
+ dns_zone_detach(&zone);
+ zone = NULL;
+ } else if (result != ISC_R_NOTFOUND) {
+ goto cleanup;
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+#ifndef HAVE_LMDB
+ /*
+ * Make sure we can open the configuration save file
+ */
+ result = isc_stdio_open(view->new_zone_file, "a", &fp);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ TCHECK(putstr(text, "unable to create '"));
+ TCHECK(putstr(text, view->new_zone_file));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ (void)isc_stdio_close(fp);
+ fp = NULL;
+#else /* HAVE_LMDB */
+ LOCK(&view->new_zone_lock);
+ locked = true;
+ /* Make sure we can open the NZD database */
+ result = nzd_writable(view);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ TCHECK(putstr(text, "unable to open NZD database for '"));
+ TCHECK(putstr(text, view->new_zone_db));
+ TCHECK(putstr(text, "'"));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ /* Mark view unfrozen and configure zone */
+ dns_view_thaw(view);
+ result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+ server->mctx, view, &server->viewlist,
+ &server->kasplist, cfg->actx, true, false,
+ false);
+ dns_view_freeze(view);
+
+ isc_task_endexclusive(server->task);
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "configure_zone failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ if (redirect) {
+ if (view->redirect == NULL) {
+ CHECK(ISC_R_NOTFOUND);
+ }
+ dns_zone_attach(view->redirect, &zone);
+ } else {
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "added new zone was not found: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ }
+
+#ifndef HAVE_LMDB
+ /*
+ * If there wasn't a previous newzone config, just save the one
+ * we've created. If there was a previous one, merge the new
+ * zone into it.
+ */
+ if (cfg->nzf_config == NULL) {
+ cfg_obj_attach(zoneconf, &cfg->nzf_config);
+ } else {
+ cfg_obj_t *z;
+ DE_CONST(zoneobj, z);
+ CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z,
+ "zone"));
+ }
+ cleanup_config = true;
+#endif /* HAVE_LMDB */
+
+ /*
+ * Load the zone from the master file. If this fails, we'll
+ * need to undo the configuration we've done already.
+ */
+ result = dns_zone_load(zone, true);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+
+ TCHECK(putstr(text, "dns_zone_loadnew failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "addzone failed; reverting.");
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(view->zonetable, zone);
+ goto cleanup;
+ }
+
+ /* Flag the zone as having been added at runtime */
+ dns_zone_setadded(zone, true);
+
+#ifdef HAVE_LMDB
+ /* Save the new zone configuration into the NZD */
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+ CHECK(nzd_save(&txn, dbi, zone, zoneobj));
+#else /* ifdef HAVE_LMDB */
+ /* Append the zone configuration to the NZF */
+ result = nzf_append(view, zoneobj);
+#endif /* HAVE_LMDB */
+
+cleanup:
+
+#ifndef HAVE_LMDB
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ }
+ if (result != ISC_R_SUCCESS && cleanup_config) {
+ tresult = delete_zoneconf(view, cfg->add_parser,
+ cfg->nzf_config, name, NULL);
+ RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+ }
+#else /* HAVE_LMDB */
+ if (txn != NULL) {
+ (void)nzd_close(&txn, false);
+ }
+ if (locked) {
+ UNLOCK(&view->new_zone_lock);
+ }
+#endif /* HAVE_LMDB */
+
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
+ dns_name_t *name, const char *zname, const cfg_obj_t *zoneobj,
+ bool redirect, isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ bool added;
+ bool exclusive = false;
+#ifndef HAVE_LMDB
+ FILE *fp = NULL;
+ cfg_obj_t *z;
+#else /* HAVE_LMDB */
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ bool locked = false;
+#endif /* HAVE_LMDB */
+
+ /* Zone must already exist */
+ if (redirect) {
+ if (view->redirect != NULL) {
+ dns_zone_attach(view->redirect, &zone);
+ result = ISC_R_SUCCESS;
+ } else {
+ result = ISC_R_NOTFOUND;
+ }
+ } else {
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ }
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ added = dns_zone_getadded(zone);
+ dns_zone_detach(&zone);
+
+#ifndef HAVE_LMDB
+ cfg = (ns_cfgctx_t *)view->new_zone_config;
+ if (cfg == NULL) {
+ TCHECK(putstr(text, "new zone config is not set"));
+ CHECK(ISC_R_FAILURE);
+ }
+#endif /* ifndef HAVE_LMDB */
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+
+#ifndef HAVE_LMDB
+ /* Make sure we can open the configuration save file */
+ result = isc_stdio_open(view->new_zone_file, "a", &fp);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to open '"));
+ TCHECK(putstr(text, view->new_zone_file));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ (void)isc_stdio_close(fp);
+ fp = NULL;
+#else /* HAVE_LMDB */
+ LOCK(&view->new_zone_lock);
+ locked = true;
+ /* Make sure we can open the NZD database */
+ result = nzd_writable(view);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to open NZD database for '"));
+ TCHECK(putstr(text, view->new_zone_db));
+ TCHECK(putstr(text, "'"));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ /* Reconfigure the zone */
+ dns_view_thaw(view);
+ result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+ server->mctx, view, &server->viewlist,
+ &server->kasplist, cfg->actx, true, false,
+ true);
+ dns_view_freeze(view);
+
+ exclusive = false;
+ isc_task_endexclusive(server->task);
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "configure_zone failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ if (redirect) {
+ if (view->redirect == NULL) {
+ CHECK(ISC_R_NOTFOUND);
+ }
+ dns_zone_attach(view->redirect, &zone);
+ } else {
+ CHECK(dns_zt_find(view->zonetable, name, 0, NULL, &zone));
+ }
+
+#ifndef HAVE_LMDB
+ /* Remove old zone from configuration (and NZF file if applicable) */
+ if (added) {
+ result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
+ dns_zone_getorigin(zone),
+ nzf_writeconf);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "former zone configuration "
+ "not deleted: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ if (!added) {
+ if (cfg->vconfig == NULL) {
+ result = delete_zoneconf(
+ view, cfg->conf_parser, cfg->config,
+ dns_zone_getorigin(zone), NULL);
+ } else {
+ const cfg_obj_t *voptions = cfg_tuple_get(cfg->vconfig,
+ "options");
+ result = delete_zoneconf(
+ view, cfg->conf_parser, voptions,
+ dns_zone_getorigin(zone), NULL);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "former zone configuration "
+ "not deleted: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ }
+
+ /* Load the zone from the master file if it needs reloading. */
+ result = dns_zone_load(zone, true);
+
+ /*
+ * Dynamic zones need no reloading, so we can pass this result.
+ */
+ if (result == DNS_R_DYNAMIC) {
+ result = ISC_R_SUCCESS;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+
+ TCHECK(putstr(text, "failed to load zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ TCHECK(putstr(text, "\nThe zone is no longer being served. "));
+ TCHECK(putstr(text, "Use 'rndc addzone' to correct\n"));
+ TCHECK(putstr(text, "the problem and restore service."));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "modzone failed; removing zone.");
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(view->zonetable, zone);
+ goto cleanup;
+ }
+
+#ifndef HAVE_LMDB
+ /* Store the new zone configuration; also in NZF if applicable */
+ DE_CONST(zoneobj, z);
+ CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, "zone"));
+#endif /* HAVE_LMDB */
+
+ if (added) {
+#ifdef HAVE_LMDB
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+ CHECK(nzd_save(&txn, dbi, zone, zoneobj));
+#else /* ifdef HAVE_LMDB */
+ result = nzf_append(view, zoneobj);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "\nNew zone config not saved: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "' reconfigured."));
+ } else {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "' must also be reconfigured in\n"));
+ TCHECK(putstr(text, "named.conf to make changes permanent."));
+ }
+
+cleanup:
+ if (exclusive) {
+ isc_task_endexclusive(server->task);
+ }
+
+#ifndef HAVE_LMDB
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ }
+#else /* HAVE_LMDB */
+ if (txn != NULL) {
+ (void)nzd_close(&txn, false);
+ }
+ if (locked) {
+ UNLOCK(&view->new_zone_lock);
+ }
+#endif /* HAVE_LMDB */
+
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+
+ return (result);
+}
+
+/*
+ * Act on an "addzone" or "modzone" command from the command channel.
+ */
+isc_result_t
+named_server_changezone(named_server_t *server, char *command,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ bool addzone;
+ bool redirect = false;
+ ns_cfgctx_t *cfg = NULL;
+ cfg_obj_t *zoneconf = NULL;
+ const cfg_obj_t *zoneobj = NULL;
+ const char *zonename;
+ dns_view_t *view = NULL;
+ isc_buffer_t buf;
+ dns_fixedname_t fname;
+ dns_name_t *dnsname;
+
+ REQUIRE(text != NULL);
+
+ if (strncasecmp(command, "add", 3) == 0) {
+ addzone = true;
+ } else {
+ INSIST(strncasecmp(command, "mod", 3) == 0);
+ addzone = false;
+ }
+
+ CHECK(newzone_parse(server, command, &view, &zoneconf, &zoneobj,
+ &redirect, text));
+
+ /* Are we accepting new zones in this view? */
+#ifdef HAVE_LMDB
+ if (view->new_zone_db == NULL)
+#else /* ifdef HAVE_LMDB */
+ if (view->new_zone_file == NULL)
+#endif /* HAVE_LMDB */
+ {
+ (void)putstr(text, "Not allowing new zones in view '");
+ (void)putstr(text, view->name);
+ (void)putstr(text, "'");
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+
+ cfg = (ns_cfgctx_t *)view->new_zone_config;
+ if (cfg == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ zonename = cfg_obj_asstring(cfg_tuple_get(zoneobj, "name"));
+ isc_buffer_constinit(&buf, zonename, strlen(zonename));
+ isc_buffer_add(&buf, strlen(zonename));
+
+ dnsname = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, 0, NULL));
+
+ if (redirect) {
+ if (!dns_name_equal(dnsname, dns_rootname)) {
+ (void)putstr(text, "redirect zones must be called "
+ "\".\"");
+ CHECK(ISC_R_FAILURE);
+ }
+ }
+
+ if (addzone) {
+ CHECK(do_addzone(server, cfg, view, dnsname, zoneconf, zoneobj,
+ redirect, text));
+ } else {
+ CHECK(do_modzone(server, cfg, view, dnsname, zonename, zoneobj,
+ redirect, text));
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s zone %s in view %s via %s",
+ addzone ? "added" : "updated", zonename, view->name,
+ addzone ? NAMED_COMMAND_ADDZONE : NAMED_COMMAND_MODZONE);
+
+ /* Changing a zone counts as reconfiguration */
+ CHECK(isc_time_now(&named_g_configtime));
+
+cleanup:
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(named_g_addparser, &zoneconf);
+ }
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+
+ return (result);
+}
+
+static bool
+inuse(const char *file, bool first, isc_buffer_t **text) {
+ if (file != NULL && isc_file_exists(file)) {
+ if (first) {
+ (void)putstr(text, "The following files were in use "
+ "and may now be removed:\n");
+ } else {
+ (void)putstr(text, "\n");
+ }
+ (void)putstr(text, file);
+ (void)putnull(text);
+ return (false);
+ }
+ return (first);
+}
+
+typedef struct {
+ dns_zone_t *zone;
+ bool cleanup;
+} ns_dzctx_t;
+
+/*
+ * Carry out a zone deletion scheduled by named_server_delzone().
+ */
+static void
+rmzone(isc_task_t *task, isc_event_t *event) {
+ ns_dzctx_t *dz = (ns_dzctx_t *)event->ev_arg;
+ dns_zone_t *zone = NULL, *raw = NULL, *mayberaw = NULL;
+ dns_catz_zone_t *catz = NULL;
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view = NULL;
+ ns_cfgctx_t *cfg = NULL;
+ dns_db_t *dbp = NULL;
+ bool added;
+ isc_result_t result;
+#ifdef HAVE_LMDB
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+#endif /* ifdef HAVE_LMDB */
+
+ REQUIRE(dz != NULL);
+
+ isc_event_free(&event);
+
+ /* Dig out configuration for this zone */
+ zone = dz->zone;
+ view = dns_zone_getview(zone);
+ cfg = (ns_cfgctx_t *)view->new_zone_config;
+ dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "deleting zone %s in view %s via delzone", zonename,
+ view->name);
+
+ /*
+ * Remove the zone from configuration (and NZF file if applicable)
+ * (If this is a catalog zone member then nzf_config can be NULL)
+ */
+ added = dns_zone_getadded(zone);
+ catz = dns_zone_get_parentcatz(zone);
+
+ if (added && catz == NULL && cfg != NULL) {
+#ifdef HAVE_LMDB
+ /* Make sure we can open the NZD database */
+ LOCK(&view->new_zone_lock);
+ result = nzd_open(view, 0, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to open NZD database for '%s'",
+ view->new_zone_db);
+ } else {
+ result = nzd_save(&txn, dbi, zone, NULL);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+
+ if (txn != NULL) {
+ (void)nzd_close(&txn, false);
+ }
+ UNLOCK(&view->new_zone_lock);
+#else /* ifdef HAVE_LMDB */
+ result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
+ dns_zone_getorigin(zone),
+ nzf_writeconf);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+#endif /* HAVE_LMDB */
+ }
+
+ if (!added && cfg != NULL) {
+ if (cfg->vconfig != NULL) {
+ const cfg_obj_t *voptions = cfg_tuple_get(cfg->vconfig,
+ "options");
+ result = delete_zoneconf(
+ view, cfg->conf_parser, voptions,
+ dns_zone_getorigin(zone), NULL);
+ } else {
+ result = delete_zoneconf(
+ view, cfg->conf_parser, cfg->config,
+ dns_zone_getorigin(zone), NULL);
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+ }
+
+ /* Unload zone database */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Clean up stub/secondary zone files if requested to do so */
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+
+ if (added && dz->cleanup) {
+ const char *file;
+
+ file = dns_zone_getfile(mayberaw);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+
+ file = dns_zone_getjournal(mayberaw);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+
+ if (zone != mayberaw) {
+ file = dns_zone_getfile(zone);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+
+ file = dns_zone_getjournal(zone);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+ }
+ }
+
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+ dns_zone_detach(&zone);
+ isc_mem_put(named_g_mctx, dz, sizeof(*dz));
+ isc_task_detach(&task);
+}
+
+/*
+ * Act on a "delzone" command from the command channel.
+ */
+isc_result_t
+named_server_delzone(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ dns_zone_t *raw = NULL;
+ dns_zone_t *mayberaw;
+ dns_view_t *view = NULL;
+ char zonename[DNS_NAME_FORMATSIZE];
+ bool cleanup = false;
+ const char *ptr;
+ bool added;
+ ns_dzctx_t *dz = NULL;
+ isc_event_t *dzevent = NULL;
+ isc_task_t *task = NULL;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcmp(ptr, "-clean") == 0 || strcmp(ptr, "-clear") == 0) {
+ cleanup = true;
+ ptr = next_token(lex, text);
+ }
+
+ CHECK(zone_from_args(server, lex, ptr, &zone, zonename, text, false));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ INSIST(zonename != NULL);
+
+ /* Is this a policy zone? */
+ if (dns_zone_get_rpz_num(zone) != DNS_RPZ_INVALID_NUM) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text,
+ "' cannot be deleted: response-policy zone."));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ view = dns_zone_getview(zone);
+ if (dns_zone_gettype(zone) == dns_zone_redirect) {
+ dns_zone_detach(&view->redirect);
+ } else {
+ CHECK(dns_zt_unmount(view->zonetable, zone));
+ }
+
+ /* Send cleanup event */
+ dz = isc_mem_get(named_g_mctx, sizeof(*dz));
+
+ dz->cleanup = cleanup;
+ dz->zone = NULL;
+ dns_zone_attach(zone, &dz->zone);
+ dzevent = isc_event_allocate(named_g_mctx, server, NAMED_EVENT_DELZONE,
+ rmzone, dz, sizeof(isc_event_t));
+
+ dns_zone_gettask(zone, &task);
+ isc_task_send(task, &dzevent);
+ dz = NULL;
+
+ /* Inform user about cleaning up stub/secondary zone files */
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+
+ added = dns_zone_getadded(zone);
+ if (!added) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text, "' is no longer active and will be "
+ "deleted.\n"));
+ TCHECK(putstr(text, "To keep it from returning "));
+ TCHECK(putstr(text, "when the server is restarted, it\n"));
+ TCHECK(putstr(text, "must also be removed from named.conf."));
+ } else if (cleanup) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text, "' and associated files will be deleted."));
+ } else if (dns_zone_gettype(mayberaw) == dns_zone_secondary ||
+ dns_zone_gettype(mayberaw) == dns_zone_mirror ||
+ dns_zone_gettype(mayberaw) == dns_zone_stub)
+ {
+ bool first;
+ const char *file;
+
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text, "' will be deleted."));
+
+ file = dns_zone_getfile(mayberaw);
+ first = inuse(file, true, text);
+
+ file = dns_zone_getjournal(mayberaw);
+ first = inuse(file, first, text);
+
+ if (zone != mayberaw) {
+ file = dns_zone_getfile(zone);
+ first = inuse(file, first, text);
+
+ file = dns_zone_getjournal(zone);
+ (void)inuse(file, first, text);
+ }
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "zone %s scheduled for removal via delzone", zonename);
+
+ /* Removing a zone counts as reconfiguration */
+ CHECK(isc_time_now(&named_g_configtime));
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+
+ return (result);
+}
+
+static const cfg_obj_t *
+find_name_in_list_from_map(const cfg_obj_t *config,
+ const char *map_key_for_list, const char *name,
+ bool redirect) {
+ const cfg_obj_t *list = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj = NULL;
+ dns_fixedname_t fixed1, fixed2;
+ dns_name_t *name1 = NULL, *name2 = NULL;
+ isc_result_t result;
+
+ if (strcmp(map_key_for_list, "zone") == 0) {
+ name1 = dns_fixedname_initname(&fixed1);
+ name2 = dns_fixedname_initname(&fixed2);
+ result = dns_name_fromstring(name1, name, 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ cfg_map_get(config, map_key_for_list, &list);
+ for (element = cfg_list_first(list); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *vname;
+
+ obj = cfg_listelt_value(element);
+ INSIST(obj != NULL);
+ vname = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ if (vname == NULL) {
+ obj = NULL;
+ continue;
+ }
+
+ if (name1 != NULL) {
+ result = dns_name_fromstring(name2, vname, 0, NULL);
+ if (result == ISC_R_SUCCESS &&
+ dns_name_equal(name1, name2))
+ {
+ const cfg_obj_t *zoptions;
+ const cfg_obj_t *typeobj = NULL;
+ zoptions = cfg_tuple_get(obj, "options");
+
+ if (zoptions != NULL) {
+ cfg_map_get(zoptions, "type", &typeobj);
+ }
+ if (redirect && typeobj != NULL &&
+ strcasecmp(cfg_obj_asstring(typeobj),
+ "redirect") == 0)
+ {
+ break;
+ } else if (!redirect) {
+ break;
+ }
+ }
+ } else if (strcasecmp(vname, name) == 0) {
+ break;
+ }
+
+ obj = NULL;
+ }
+
+ return (obj);
+}
+
+static void
+emitzone(void *arg, const char *buf, int len) {
+ ns_dzarg_t *dzarg = arg;
+ isc_result_t result;
+
+ REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC));
+ result = putmem(dzarg->text, buf, len);
+ if (result != ISC_R_SUCCESS && dzarg->result == ISC_R_SUCCESS) {
+ dzarg->result = result;
+ }
+}
+
+/*
+ * Act on a "showzone" command from the command channel.
+ */
+isc_result_t
+named_server_showzone(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result;
+ const cfg_obj_t *vconfig = NULL, *zconfig = NULL;
+ char zonename[DNS_NAME_FORMATSIZE];
+ const cfg_obj_t *map;
+ dns_view_t *view = NULL;
+ dns_zone_t *zone = NULL;
+ ns_cfgctx_t *cfg = NULL;
+#ifdef HAVE_LMDB
+ cfg_obj_t *nzconfig = NULL;
+#endif /* HAVE_LMDB */
+ bool added, redirect;
+ ns_dzarg_t dzarg;
+
+ REQUIRE(text != NULL);
+
+ /* Parse parameters */
+ CHECK(zone_from_args(server, lex, NULL, &zone, zonename, text, true));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ redirect = dns_zone_gettype(zone) == dns_zone_redirect;
+ added = dns_zone_getadded(zone);
+ view = dns_zone_getview(zone);
+ dns_zone_detach(&zone);
+
+ cfg = (ns_cfgctx_t *)view->new_zone_config;
+ if (cfg == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ if (!added) {
+ /* Find the view statement */
+ vconfig = find_name_in_list_from_map(cfg->config, "view",
+ view->name, false);
+
+ /* Find the zone statement */
+ if (vconfig != NULL) {
+ map = cfg_tuple_get(vconfig, "options");
+ } else {
+ map = cfg->config;
+ }
+
+ zconfig = find_name_in_list_from_map(map, "zone", zonename,
+ redirect);
+ }
+
+#ifndef HAVE_LMDB
+ if (zconfig == NULL && cfg->nzf_config != NULL) {
+ zconfig = find_name_in_list_from_map(cfg->nzf_config, "zone",
+ zonename, redirect);
+ }
+#else /* HAVE_LMDB */
+ if (zconfig == NULL) {
+ const cfg_obj_t *zlist = NULL;
+ CHECK(get_newzone_config(view, zonename, &nzconfig));
+ CHECK(cfg_map_get(nzconfig, "zone", &zlist));
+ if (!cfg_obj_islist(zlist)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ zconfig = cfg_listelt_value(cfg_list_first(zlist));
+ }
+#endif /* HAVE_LMDB */
+
+ if (zconfig == NULL) {
+ CHECK(ISC_R_NOTFOUND);
+ }
+
+ CHECK(putstr(text, "zone "));
+ dzarg.magic = DZARG_MAGIC;
+ dzarg.text = text;
+ dzarg.result = ISC_R_SUCCESS;
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, emitzone, &dzarg);
+ CHECK(dzarg.result);
+
+ CHECK(putstr(text, ";"));
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+#ifdef HAVE_LMDB
+ if (nzconfig != NULL) {
+ cfg_obj_destroy(named_g_addparser, &nzconfig);
+ }
+#endif /* HAVE_LMDB */
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+
+ return (result);
+}
+
+static void
+newzone_cfgctx_destroy(void **cfgp) {
+ ns_cfgctx_t *cfg;
+
+ REQUIRE(cfgp != NULL && *cfgp != NULL);
+
+ cfg = *cfgp;
+
+ if (cfg->conf_parser != NULL) {
+ if (cfg->config != NULL) {
+ cfg_obj_destroy(cfg->conf_parser, &cfg->config);
+ }
+ if (cfg->vconfig != NULL) {
+ cfg_obj_destroy(cfg->conf_parser, &cfg->vconfig);
+ }
+ cfg_parser_destroy(&cfg->conf_parser);
+ }
+ if (cfg->add_parser != NULL) {
+ if (cfg->nzf_config != NULL) {
+ cfg_obj_destroy(cfg->add_parser, &cfg->nzf_config);
+ }
+ cfg_parser_destroy(&cfg->add_parser);
+ }
+
+ if (cfg->actx != NULL) {
+ cfg_aclconfctx_detach(&cfg->actx);
+ }
+
+ isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
+ *cfgp = NULL;
+}
+
+isc_result_t
+named_server_signing(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *zone = NULL;
+ dns_name_t *origin;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdatatype_t privatetype;
+ dns_rdataset_t privset;
+ bool first = true;
+ bool list = false, clear = false;
+ bool chain = false;
+ bool setserial = false;
+ bool resalt = false;
+ uint32_t serial = 0;
+ char keystr[DNS_SECALG_FORMATSIZE + 7]; /* <5-digit keyid>/<alg> */
+ unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0;
+ unsigned char salt[255];
+ const char *ptr;
+ size_t n;
+
+ REQUIRE(text != NULL);
+
+ dns_rdataset_init(&privset);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcasecmp(ptr, "-list") == 0) {
+ list = true;
+ } else if ((strcasecmp(ptr, "-clear") == 0) ||
+ (strcasecmp(ptr, "-clean") == 0))
+ {
+ clear = true;
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ strlcpy(keystr, ptr, sizeof(keystr));
+ } else if (strcasecmp(ptr, "-nsec3param") == 0) {
+ char hashbuf[64], flagbuf[64], iterbuf[64];
+ char nbuf[256];
+
+ chain = true;
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcasecmp(ptr, "none") == 0) {
+ hash = 0;
+ } else {
+ strlcpy(hashbuf, ptr, sizeof(hashbuf));
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ strlcpy(flagbuf, ptr, sizeof(flagbuf));
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ strlcpy(iterbuf, ptr, sizeof(iterbuf));
+ n = snprintf(nbuf, sizeof(nbuf), "%s %s %s", hashbuf,
+ flagbuf, iterbuf);
+ if (n == sizeof(nbuf)) {
+ return (ISC_R_NOSPACE);
+ }
+ n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter);
+ if (n != 3U) {
+ return (ISC_R_BADNUMBER);
+ }
+
+ if (hash > 0xffU || flags > 0xffU ||
+ iter > dns_nsec3_maxiterations())
+ {
+ return (ISC_R_RANGE);
+ }
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ } else if (strcasecmp(ptr, "auto") == 0) {
+ /* Auto-generate a random salt.
+ * XXXMUKS: This currently uses the
+ * minimum recommended length by RFC
+ * 5155 (64 bits). It should be made
+ * configurable.
+ */
+ saltlen = 8;
+ resalt = true;
+ } else if (strcmp(ptr, "-") != 0) {
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, salt, sizeof(salt));
+ CHECK(isc_hex_decodestring(ptr, &buf));
+ saltlen = isc_buffer_usedlength(&buf);
+ }
+ }
+ } else if (strcasecmp(ptr, "-serial") == 0) {
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(isc_parse_uint32(&serial, ptr, 10));
+ setserial = true;
+ } else {
+ CHECK(DNS_R_SYNTAX);
+ }
+
+ CHECK(zone_from_args(server, lex, NULL, &zone, NULL, text, false));
+ if (zone == NULL) {
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ if (dns_zone_getkasp(zone) != NULL) {
+ (void)putstr(text, "zone uses dnssec-policy, use rndc dnssec "
+ "command instead");
+ (void)putnull(text);
+ goto cleanup;
+ }
+
+ if (clear) {
+ CHECK(dns_zone_keydone(zone, keystr));
+ (void)putstr(text, "request queued");
+ (void)putnull(text);
+ } else if (chain) {
+ CHECK(dns_zone_setnsec3param(
+ zone, (uint8_t)hash, (uint8_t)flags, iter,
+ (uint8_t)saltlen, salt, true, resalt));
+ (void)putstr(text, "nsec3param request queued");
+ (void)putnull(text);
+ } else if (setserial) {
+ CHECK(dns_zone_setserial(zone, serial));
+ (void)putstr(text, "serial request queued");
+ (void)putnull(text);
+ } else if (list) {
+ privatetype = dns_zone_getprivatetype(zone);
+ origin = dns_zone_getorigin(zone);
+ CHECK(dns_zone_getdb(zone, &db));
+ CHECK(dns_db_findnode(db, origin, false, &node));
+ dns_db_currentversion(db, &version);
+
+ result = dns_db_findrdataset(db, node, version, privatetype,
+ dns_rdatatype_none, 0, &privset,
+ NULL);
+ if (result == ISC_R_NOTFOUND) {
+ (void)putstr(text, "No signing records found");
+ (void)putnull(text);
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ for (result = dns_rdataset_first(&privset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&privset))
+ {
+ dns_rdata_t priv = DNS_RDATA_INIT;
+ /*
+ * In theory, the output buffer could hold a full RDATA
+ * record which is 16-bit and then some text around
+ * it
+ */
+ char output[UINT16_MAX + BUFSIZ];
+ isc_buffer_t buf;
+
+ dns_rdataset_current(&privset, &priv);
+
+ isc_buffer_init(&buf, output, sizeof(output));
+ CHECK(dns_private_totext(&priv, &buf));
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, output));
+ first = false;
+ }
+ if (!first) {
+ CHECK(putnull(text));
+ }
+
+ if (result == ISC_R_NOMORE) {
+ result = ISC_R_SUCCESS;
+ }
+ }
+
+cleanup:
+ if (dns_rdataset_isassociated(&privset)) {
+ dns_rdataset_disassociate(&privset);
+ }
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, false);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+
+ return (result);
+}
+
+static bool
+argcheck(char *cmd, const char *full) {
+ size_t l;
+
+ if (cmd == NULL || cmd[0] != '-') {
+ return (false);
+ }
+
+ cmd++;
+ l = strlen(cmd);
+ if (l > strlen(full) || strncasecmp(cmd, full, l) != 0) {
+ return (false);
+ }
+
+ return (true);
+}
+
+isc_result_t
+named_server_dnssec(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *zone = NULL;
+ dns_kasp_t *kasp = NULL;
+ dns_dnsseckeylist_t keys;
+ dns_dnsseckey_t *key;
+ char *ptr, *zonetext = NULL;
+ const char *msg = NULL;
+ /* variables for -checkds */
+ bool checkds = false, dspublish = false;
+ /* variables for -rollover */
+ bool rollover = false;
+ /* variables for -key */
+ bool use_keyid = false;
+ dns_keytag_t keyid = 0;
+ uint8_t algorithm = 0;
+ /* variables for -status */
+ bool status = false;
+ char output[4096];
+ isc_stdtime_t now, when;
+ isc_time_t timenow, timewhen;
+ const char *dir;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Initialize current time and key list. */
+ TIME_NOW(&timenow);
+ now = isc_time_seconds(&timenow);
+ when = now;
+
+ ISC_LIST_INIT(keys);
+
+ if (strcasecmp(ptr, "-status") == 0) {
+ status = true;
+ } else if (strcasecmp(ptr, "-rollover") == 0) {
+ rollover = true;
+ } else if (strcasecmp(ptr, "-checkds") == 0) {
+ checkds = true;
+ } else {
+ CHECK(DNS_R_SYNTAX);
+ }
+
+ if (rollover || checkds) {
+ /* Check for options */
+ for (;;) {
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "Bad format";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ } else if (argcheck(ptr, "alg")) {
+ isc_consttextregion_t alg;
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No key algorithm specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ alg.base = ptr;
+ alg.length = strlen(alg.base);
+ result = dns_secalg_fromtext(
+ &algorithm, (isc_textregion_t *)&alg);
+ if (result != ISC_R_SUCCESS) {
+ msg = "Bad algorithm";
+ CHECK(DNS_R_SYNTAX);
+ }
+ continue;
+ } else if (argcheck(ptr, "key")) {
+ uint16_t id;
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No key identifier specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(isc_parse_uint16(&id, ptr, 10));
+ keyid = (dns_keytag_t)id;
+ use_keyid = true;
+ continue;
+ } else if (argcheck(ptr, "when")) {
+ uint32_t tw;
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No time specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(dns_time32_fromtext(ptr, &tw));
+ when = (isc_stdtime_t)tw;
+ continue;
+ } else if (ptr[0] == '-') {
+ msg = "Unknown option";
+ CHECK(DNS_R_SYNTAX);
+ } else if (checkds) {
+ /*
+ * No arguments provided, so we must be
+ * parsing "published|withdrawn".
+ */
+ if (strcasecmp(ptr, "published") == 0) {
+ dspublish = true;
+ } else if (strcasecmp(ptr, "withdrawn") != 0) {
+ CHECK(DNS_R_SYNTAX);
+ }
+ } else if (rollover) {
+ /*
+ * No arguments provided, so we must be
+ * parsing the zone.
+ */
+ zonetext = ptr;
+ }
+ break;
+ }
+
+ if (rollover && !use_keyid) {
+ msg = "Key id is required when scheduling rollover";
+ CHECK(DNS_R_SYNTAX);
+ }
+
+ if (algorithm > 0 && !use_keyid) {
+ msg = "Key id is required when setting algorithm";
+ CHECK(DNS_R_SYNTAX);
+ }
+ }
+
+ /* Get zone. */
+ CHECK(zone_from_args(server, lex, zonetext, &zone, NULL, text, false));
+ if (zone == NULL) {
+ msg = "Zone not found";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Trailing garbage? */
+ ptr = next_token(lex, text);
+ if (ptr != NULL) {
+ msg = "Too many arguments";
+ CHECK(DNS_R_SYNTAX);
+ }
+
+ /* Get dnssec-policy. */
+ kasp = dns_zone_getkasp(zone);
+ if (kasp == NULL) {
+ msg = "Zone does not have dnssec-policy";
+ goto cleanup;
+ }
+
+ /* Get DNSSEC keys. */
+ dir = dns_zone_getkeydirectory(zone);
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_db_currentversion(db, &version);
+ LOCK(&kasp->lock);
+ result = dns_zone_getdnsseckeys(zone, db, version, now, &keys);
+ UNLOCK(&kasp->lock);
+ if (result != ISC_R_SUCCESS) {
+ if (result != ISC_R_NOTFOUND) {
+ goto cleanup;
+ }
+ }
+
+ if (status) {
+ /*
+ * Output the DNSSEC status of the key and signing policy.
+ */
+ LOCK(&kasp->lock);
+ dns_keymgr_status(kasp, &keys, now, &output[0], sizeof(output));
+ UNLOCK(&kasp->lock);
+ CHECK(putstr(text, output));
+ } else if (checkds) {
+ /*
+ * Mark DS record has been seen, so it may move to the
+ * rumoured state.
+ */
+ char whenbuf[80];
+ isc_time_set(&timewhen, when, 0);
+ isc_time_formattimestamp(&timewhen, whenbuf, sizeof(whenbuf));
+ isc_result_t ret;
+
+ LOCK(&kasp->lock);
+ if (use_keyid) {
+ result = dns_keymgr_checkds_id(kasp, &keys, dir, now,
+ when, dspublish, keyid,
+ (unsigned int)algorithm);
+ } else {
+ result = dns_keymgr_checkds(kasp, &keys, dir, now, when,
+ dspublish);
+ }
+ UNLOCK(&kasp->lock);
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ /*
+ * Rekey after checkds command because the next key
+ * event may have changed.
+ */
+ dns_zone_rekey(zone, false);
+
+ if (use_keyid) {
+ char tagbuf[6];
+ snprintf(tagbuf, sizeof(tagbuf), "%u", keyid);
+ CHECK(putstr(text, "KSK "));
+ CHECK(putstr(text, tagbuf));
+ CHECK(putstr(text, ": "));
+ }
+ CHECK(putstr(text, "Marked DS as "));
+ if (dspublish) {
+ CHECK(putstr(text, "published "));
+ } else {
+ CHECK(putstr(text, "withdrawn "));
+ }
+ CHECK(putstr(text, "since "));
+ CHECK(putstr(text, whenbuf));
+ break;
+ case DNS_R_TOOMANYKEYS:
+ CHECK(putstr(text,
+ "Error: multiple possible keys found, "
+ "retry command with -key id"));
+ break;
+ default:
+ ret = result;
+ CHECK(putstr(text,
+ "Error executing checkds command: "));
+ CHECK(putstr(text, isc_result_totext(ret)));
+ break;
+ }
+ } else if (rollover) {
+ /*
+ * Manually rollover a key.
+ */
+ char whenbuf[80];
+ isc_time_set(&timewhen, when, 0);
+ isc_time_formattimestamp(&timewhen, whenbuf, sizeof(whenbuf));
+ isc_result_t ret;
+
+ LOCK(&kasp->lock);
+ result = dns_keymgr_rollover(kasp, &keys, dir, now, when, keyid,
+ (unsigned int)algorithm);
+ UNLOCK(&kasp->lock);
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ /*
+ * Rekey after rollover command because the next key
+ * event may have changed.
+ */
+ dns_zone_rekey(zone, false);
+
+ if (use_keyid) {
+ char tagbuf[6];
+ snprintf(tagbuf, sizeof(tagbuf), "%u", keyid);
+ CHECK(putstr(text, "Key "));
+ CHECK(putstr(text, tagbuf));
+ CHECK(putstr(text, ": "));
+ }
+ CHECK(putstr(text, "Rollover scheduled on "));
+ CHECK(putstr(text, whenbuf));
+ break;
+ case DNS_R_TOOMANYKEYS:
+ CHECK(putstr(text,
+ "Error: multiple possible keys found, "
+ "retry command with -alg algorithm"));
+ break;
+ default:
+ ret = result;
+ CHECK(putstr(text,
+ "Error executing rollover command: "));
+ CHECK(putstr(text, isc_result_totext(ret)));
+ break;
+ }
+ }
+ CHECK(putnull(text));
+
+cleanup:
+ if (msg != NULL) {
+ (void)putstr(text, msg);
+ (void)putnull(text);
+ }
+
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, false);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+
+ while (!ISC_LIST_EMPTY(keys)) {
+ key = ISC_LIST_HEAD(keys);
+ ISC_LIST_UNLINK(keys, key, link);
+ dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key);
+ }
+
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+putmem(isc_buffer_t **b, const char *str, size_t len) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, (unsigned int)len);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+
+ isc_buffer_putmem(*b, (const unsigned char *)str, (unsigned int)len);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+putstr(isc_buffer_t **b, const char *str) {
+ return (putmem(b, str, strlen(str)));
+}
+
+static isc_result_t
+putuint8(isc_buffer_t **b, uint8_t val) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, 1);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+
+ isc_buffer_putuint8(*b, val);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+putnull(isc_buffer_t **b) {
+ return (putuint8(b, 0));
+}
+
+isc_result_t
+named_server_zonestatus(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *zone = NULL, *raw = NULL, *mayberaw = NULL;
+ const char *type, *file;
+ char zonename[DNS_NAME_FORMATSIZE];
+ uint32_t serial, signed_serial, nodes;
+ char serbuf[16], sserbuf[16], nodebuf[16];
+ char resignbuf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + 2];
+ char lbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char xbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char rbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char kbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char rtbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_t loadtime, expiretime, refreshtime;
+ isc_time_t refreshkeytime, resigntime;
+ dns_zonetype_t zonetype;
+ bool dynamic = false, frozen = false;
+ bool hasraw = false;
+ bool secure, maintain, allow;
+ dns_db_t *db = NULL, *rawdb = NULL;
+ char **incfiles = NULL;
+ int nfiles = 0;
+
+ REQUIRE(text != NULL);
+
+ isc_time_settoepoch(&loadtime);
+ isc_time_settoepoch(&refreshtime);
+ isc_time_settoepoch(&expiretime);
+ isc_time_settoepoch(&refreshkeytime);
+ isc_time_settoepoch(&resigntime);
+
+ CHECK(zone_from_args(server, lex, NULL, &zone, zonename, text, true));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ /* Inline signing? */
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_zone_getraw(zone, &raw);
+ hasraw = (raw != NULL);
+ if (hasraw) {
+ mayberaw = raw;
+ zonetype = dns_zone_gettype(raw);
+ CHECK(dns_zone_getdb(raw, &rawdb));
+ } else {
+ mayberaw = zone;
+ zonetype = dns_zone_gettype(zone);
+ }
+
+ type = dns_zonetype_name(zonetype);
+
+ /* Serial number */
+ result = dns_zone_getserial(mayberaw, &serial);
+
+ /* This is to mirror old behavior with dns_zone_getserial */
+ if (result != ISC_R_SUCCESS) {
+ serial = 0;
+ }
+
+ snprintf(serbuf, sizeof(serbuf), "%u", serial);
+ if (hasraw) {
+ result = dns_zone_getserial(zone, &signed_serial);
+ if (result != ISC_R_SUCCESS) {
+ serial = 0;
+ }
+ snprintf(sserbuf, sizeof(sserbuf), "%u", signed_serial);
+ }
+
+ /* Database node count */
+ nodes = dns_db_nodecount(hasraw ? rawdb : db, dns_dbtree_main);
+ snprintf(nodebuf, sizeof(nodebuf), "%u", nodes);
+
+ /* Security */
+ secure = dns_db_issecure(db);
+ allow = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_ALLOW) != 0);
+ maintain = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0);
+
+ /* Master files */
+ file = dns_zone_getfile(mayberaw);
+ nfiles = dns_zone_getincludes(mayberaw, &incfiles);
+
+ /* Load time */
+ dns_zone_getloadtime(zone, &loadtime);
+ isc_time_formathttptimestamp(&loadtime, lbuf, sizeof(lbuf));
+
+ /* Refresh/expire times */
+ if (zonetype == dns_zone_secondary || zonetype == dns_zone_mirror ||
+ zonetype == dns_zone_stub || zonetype == dns_zone_redirect)
+ {
+ dns_zone_getexpiretime(mayberaw, &expiretime);
+ isc_time_formathttptimestamp(&expiretime, xbuf, sizeof(xbuf));
+ dns_zone_getrefreshtime(mayberaw, &refreshtime);
+ isc_time_formathttptimestamp(&refreshtime, rbuf, sizeof(rbuf));
+ }
+
+ /* Key refresh time */
+ if (zonetype == dns_zone_primary ||
+ (zonetype == dns_zone_secondary && hasraw))
+ {
+ dns_zone_getrefreshkeytime(zone, &refreshkeytime);
+ isc_time_formathttptimestamp(&refreshkeytime, kbuf,
+ sizeof(kbuf));
+ }
+
+ /* Dynamic? */
+ if (zonetype == dns_zone_primary) {
+ dynamic = dns_zone_isdynamic(mayberaw, true);
+ frozen = dynamic && !dns_zone_isdynamic(mayberaw, false);
+ }
+
+ /* Next resign event */
+ if (secure &&
+ (zonetype == dns_zone_primary ||
+ (zonetype == dns_zone_secondary && hasraw)) &&
+ ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_NORESIGN) == 0))
+ {
+ dns_name_t *name;
+ dns_fixedname_t fixed;
+ dns_rdataset_t next;
+
+ dns_rdataset_init(&next);
+ name = dns_fixedname_initname(&fixed);
+
+ result = dns_db_getsigningtime(db, &next, name);
+ if (result == ISC_R_SUCCESS) {
+ isc_stdtime_t timenow;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ isc_stdtime_get(&timenow);
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(next.covers, typebuf,
+ sizeof(typebuf));
+ snprintf(resignbuf, sizeof(resignbuf), "%s/%s", namebuf,
+ typebuf);
+ isc_time_set(
+ &resigntime,
+ next.resign -
+ dns_zone_getsigresigninginterval(zone),
+ 0);
+ isc_time_formathttptimestamp(&resigntime, rtbuf,
+ sizeof(rtbuf));
+ dns_rdataset_disassociate(&next);
+ }
+ }
+
+ /* Create text */
+ CHECK(putstr(text, "name: "));
+ CHECK(putstr(text, zonename));
+
+ CHECK(putstr(text, "\ntype: "));
+ CHECK(putstr(text, type));
+
+ if (file != NULL) {
+ int i;
+ CHECK(putstr(text, "\nfiles: "));
+ CHECK(putstr(text, file));
+ for (i = 0; i < nfiles; i++) {
+ CHECK(putstr(text, ", "));
+ if (incfiles[i] != NULL) {
+ CHECK(putstr(text, incfiles[i]));
+ }
+ }
+ }
+
+ CHECK(putstr(text, "\nserial: "));
+ CHECK(putstr(text, serbuf));
+ if (hasraw) {
+ CHECK(putstr(text, "\nsigned serial: "));
+ CHECK(putstr(text, sserbuf));
+ }
+
+ CHECK(putstr(text, "\nnodes: "));
+ CHECK(putstr(text, nodebuf));
+
+ if (!isc_time_isepoch(&loadtime)) {
+ CHECK(putstr(text, "\nlast loaded: "));
+ CHECK(putstr(text, lbuf));
+ }
+
+ if (!isc_time_isepoch(&refreshtime)) {
+ CHECK(putstr(text, "\nnext refresh: "));
+ CHECK(putstr(text, rbuf));
+ }
+
+ if (!isc_time_isepoch(&expiretime)) {
+ CHECK(putstr(text, "\nexpires: "));
+ CHECK(putstr(text, xbuf));
+ }
+
+ if (secure) {
+ CHECK(putstr(text, "\nsecure: yes"));
+ if (hasraw) {
+ CHECK(putstr(text, "\ninline signing: yes"));
+ } else {
+ CHECK(putstr(text, "\ninline signing: no"));
+ }
+ } else {
+ CHECK(putstr(text, "\nsecure: no"));
+ }
+
+ if (maintain) {
+ CHECK(putstr(text, "\nkey maintenance: automatic"));
+ if (!isc_time_isepoch(&refreshkeytime)) {
+ CHECK(putstr(text, "\nnext key event: "));
+ CHECK(putstr(text, kbuf));
+ }
+ } else if (allow) {
+ CHECK(putstr(text, "\nkey maintenance: on command"));
+ } else if (secure || hasraw) {
+ CHECK(putstr(text, "\nkey maintenance: none"));
+ }
+
+ if (!isc_time_isepoch(&resigntime)) {
+ CHECK(putstr(text, "\nnext resign node: "));
+ CHECK(putstr(text, resignbuf));
+ CHECK(putstr(text, "\nnext resign time: "));
+ CHECK(putstr(text, rtbuf));
+ }
+
+ if (dynamic) {
+ CHECK(putstr(text, "\ndynamic: yes"));
+ if (frozen) {
+ CHECK(putstr(text, "\nfrozen: yes"));
+ } else {
+ CHECK(putstr(text, "\nfrozen: no"));
+ }
+ } else {
+ CHECK(putstr(text, "\ndynamic: no"));
+ }
+
+ CHECK(putstr(text, "\nreconfigurable via modzone: "));
+ CHECK(putstr(text, dns_zone_getadded(zone) ? "yes" : "no"));
+
+cleanup:
+ /* Indicate truncated output if possible. */
+ if (result == ISC_R_NOSPACE) {
+ (void)putstr(text, "\n...");
+ }
+ if ((result == ISC_R_SUCCESS || result == ISC_R_NOSPACE)) {
+ (void)putnull(text);
+ }
+
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+ if (rawdb != NULL) {
+ dns_db_detach(&rawdb);
+ }
+ if (incfiles != NULL && mayberaw != NULL) {
+ int i;
+ isc_mem_t *mctx = dns_zone_getmctx(mayberaw);
+
+ for (i = 0; i < nfiles; i++) {
+ if (incfiles[i] != NULL) {
+ isc_mem_free(mctx, incfiles[i]);
+ }
+ }
+ isc_mem_free(mctx, incfiles);
+ }
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ return (result);
+}
+
+isc_result_t
+named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly,
+ isc_buffer_t **text) {
+ dns_view_t *view;
+ dns_ntatable_t *ntatable = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ char *ptr, *nametext = NULL, *viewname;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char viewbuf[DNS_NAME_FORMATSIZE];
+ isc_stdtime_t now, when;
+ isc_time_t t;
+ char tbuf[64];
+ const char *msg = NULL;
+ bool dump = false, force = false;
+ dns_fixedname_t fn;
+ const dns_name_t *ntaname;
+ dns_name_t *fname;
+ dns_ttl_t ntattl;
+ bool ttlset = false, excl = false, viewfound = false;
+ dns_rdataclass_t rdclass = dns_rdataclass_in;
+ bool first = true;
+
+ REQUIRE(text != NULL);
+
+ UNUSED(force);
+
+ fname = dns_fixedname_initname(&fn);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ for (;;) {
+ /* Check for options */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcmp(ptr, "--") == 0) {
+ break;
+ } else if (argcheck(ptr, "dump")) {
+ dump = true;
+ } else if (argcheck(ptr, "remove")) {
+ ntattl = 0;
+ ttlset = true;
+ } else if (argcheck(ptr, "force")) {
+ force = true;
+ continue;
+ } else if (argcheck(ptr, "lifetime")) {
+ isc_textregion_t tr;
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No lifetime specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ tr.base = ptr;
+ tr.length = strlen(ptr);
+ result = dns_ttl_fromtext(&tr, &ntattl);
+ if (result != ISC_R_SUCCESS) {
+ msg = "could not parse NTA lifetime";
+ CHECK(result);
+ }
+
+ if (ntattl > 604800) {
+ msg = "NTA lifetime cannot exceed one week";
+ CHECK(ISC_R_RANGE);
+ }
+
+ ttlset = true;
+ continue;
+ } else if (argcheck(ptr, "class")) {
+ isc_textregion_t tr;
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No class specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ tr.base = ptr;
+ tr.length = strlen(ptr);
+ CHECK(dns_rdataclass_fromtext(&rdclass, &tr));
+ continue;
+ } else if (ptr[0] == '-') {
+ msg = "Unknown option";
+ CHECK(DNS_R_SYNTAX);
+ } else {
+ nametext = ptr;
+ }
+
+ break;
+ }
+
+ /*
+ * If -dump was specified, list NTA's and return
+ */
+ if (dump) {
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ continue;
+ }
+
+ CHECK(dns_ntatable_totext(ntatable, view->name, text));
+ }
+ CHECK(putnull(text));
+
+ goto cleanup;
+ }
+
+ if (readonly) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_INFO,
+ "rejecting restricted control channel "
+ "NTA command");
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* Get the NTA name if not found above. */
+ if (nametext == NULL) {
+ nametext = next_token(lex, text);
+ }
+ if (nametext == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Copy nametext as it'll be overwritten by next_token() */
+ strlcpy(namebuf, nametext, DNS_NAME_FORMATSIZE);
+
+ if (strcmp(namebuf, ".") == 0) {
+ ntaname = dns_rootname;
+ } else {
+ isc_buffer_t b;
+ isc_buffer_init(&b, namebuf, strlen(namebuf));
+ isc_buffer_add(&b, strlen(namebuf));
+ CHECK(dns_name_fromtext(fname, &b, dns_rootname, 0, NULL));
+ ntaname = fname;
+ }
+
+ /* Look for the view name. */
+ viewname = next_token(lex, text);
+ if (viewname != NULL) {
+ strlcpy(viewbuf, viewname, DNS_NAME_FORMATSIZE);
+ viewname = viewbuf;
+ }
+
+ if (next_token(lex, text) != NULL) {
+ CHECK(DNS_R_SYNTAX);
+ }
+
+ isc_stdtime_get(&now);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ excl = true;
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcmp(view->name, viewname) != 0) {
+ continue;
+ }
+ viewfound = true;
+
+ if (view->rdclass != rdclass && rdclass != dns_rdataclass_any) {
+ continue;
+ }
+
+ if (view->nta_lifetime == 0) {
+ continue;
+ }
+
+ if (!ttlset) {
+ ntattl = view->nta_lifetime;
+ }
+
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+
+ result = dns_view_flushnode(view, ntaname, true);
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flush tree '%s' in cache view '%s': %s", namebuf,
+ view->name, isc_result_totext(result));
+
+ if (ntattl != 0) {
+ CHECK(dns_ntatable_add(ntatable, ntaname, force, now,
+ ntattl));
+
+ when = now + ntattl;
+ isc_time_set(&t, when, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ first = false;
+
+ CHECK(putstr(text, "Negative trust anchor added: "));
+ CHECK(putstr(text, namebuf));
+ CHECK(putstr(text, "/"));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, ", expires "));
+ CHECK(putstr(text, tbuf));
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "added NTA '%s' (%d sec) in view '%s'",
+ namebuf, ntattl, view->name);
+ } else {
+ bool wasremoved;
+
+ result = dns_ntatable_delete(ntatable, ntaname);
+ if (result == ISC_R_SUCCESS) {
+ wasremoved = true;
+ } else if (result == ISC_R_NOTFOUND) {
+ wasremoved = false;
+ } else {
+ goto cleanup;
+ }
+
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ first = false;
+
+ CHECK(putstr(text, "Negative trust anchor "));
+ CHECK(putstr(text,
+ wasremoved ? "removed: " : "not found: "));
+ CHECK(putstr(text, namebuf));
+ CHECK(putstr(text, "/"));
+ CHECK(putstr(text, view->name));
+
+ if (wasremoved) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "removed NTA '%s' in view %s", namebuf,
+ view->name);
+ }
+ }
+
+ result = dns_view_saventa(view);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error writing NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ if (!viewfound) {
+ msg = "No such view";
+ CHECK(ISC_R_NOTFOUND);
+ }
+
+ (void)putnull(text);
+
+cleanup:
+ if (msg != NULL) {
+ (void)putstr(text, msg);
+ (void)putnull(text);
+ }
+
+ if (excl) {
+ isc_task_endexclusive(server->task);
+ }
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ return (result);
+}
+
+isc_result_t
+named_server_saventa(named_server_t *server) {
+ dns_view_t *view;
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ isc_result_t result = dns_view_saventa(view);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error writing NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_server_loadnta(named_server_t *server) {
+ dns_view_t *view;
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ isc_result_t result = dns_view_loadnta(view);
+
+ if ((result != ISC_R_SUCCESS) &&
+ (result != ISC_R_FILENOTFOUND) &&
+ (result != ISC_R_NOTFOUND))
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error loading NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+mkey_refresh(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ char msg[DNS_NAME_FORMATSIZE + 500] = "";
+
+ snprintf(msg, sizeof(msg), "refreshing managed keys for '%s'",
+ view->name);
+ CHECK(putstr(text, msg));
+ CHECK(dns_zone_synckeyzone(view->managed_keys));
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+mkey_destroy(named_server_t *server, dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ char msg[DNS_NAME_FORMATSIZE + 500] = "";
+ bool exclusive = false;
+ const char *file = NULL;
+ dns_db_t *dbp = NULL;
+ dns_zone_t *mkzone = NULL;
+ bool removed_a_file = false;
+
+ if (view->managed_keys == NULL) {
+ CHECK(ISC_R_NOTFOUND);
+ }
+
+ snprintf(msg, sizeof(msg), "destroying managed-keys database for '%s'",
+ view->name);
+ CHECK(putstr(text, msg));
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+
+ /* Remove and clean up managed keys zone from view */
+ mkzone = view->managed_keys;
+ view->managed_keys = NULL;
+ (void)dns_zone_flush(mkzone);
+
+ /* Unload zone database */
+ if (dns_zone_getdb(mkzone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(mkzone);
+ }
+
+ /* Delete files */
+ file = dns_zone_getfile(mkzone);
+ result = isc_file_remove(file);
+ if (result == ISC_R_SUCCESS) {
+ removed_a_file = true;
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+
+ file = dns_zone_getjournal(mkzone);
+ result = isc_file_remove(file);
+ if (result == ISC_R_SUCCESS) {
+ removed_a_file = true;
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s", file,
+ isc_result_totext(result));
+ }
+
+ if (!removed_a_file) {
+ CHECK(putstr(text, "error: no files could be removed"));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ dns_zone_detach(&mkzone);
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (exclusive) {
+ isc_task_endexclusive(server->task);
+ }
+ return (result);
+}
+
+static isc_result_t
+mkey_dumpzone(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rriterator_t rrit;
+ isc_stdtime_t now;
+ dns_name_t *prevname = NULL;
+
+ isc_stdtime_get(&now);
+
+ CHECK(dns_zone_getdb(view->managed_keys, &db));
+ dns_db_currentversion(db, &ver);
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit); result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit))
+ {
+ char buf[DNS_NAME_FORMATSIZE + 500];
+ dns_name_t *name = NULL;
+ dns_rdataset_t *kdset = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t kd;
+ uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &name, &ttl, &kdset, NULL);
+ if (kdset == NULL || kdset->type != dns_rdatatype_keydata ||
+ !dns_rdataset_isassociated(kdset))
+ {
+ continue;
+ }
+
+ if (name != prevname) {
+ char nbuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ snprintf(buf, sizeof(buf), "\n\n name: %s", nbuf);
+ CHECK(putstr(text, buf));
+ }
+
+ for (result = dns_rdataset_first(kdset);
+ result == ISC_R_SUCCESS; result = dns_rdataset_next(kdset))
+ {
+ char alg[DNS_SECALG_FORMATSIZE];
+ char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ dns_keytag_t keyid;
+ isc_region_t r;
+ isc_time_t t;
+ bool revoked;
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(kdset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &kd, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_rdata_toregion(&rdata, &r);
+ isc_region_consume(&r, 12);
+ keyid = dst_region_computeid(&r);
+
+ snprintf(buf, sizeof(buf), "\n keyid: %u", keyid);
+ CHECK(putstr(text, buf));
+
+ dns_secalg_format(kd.algorithm, alg, sizeof(alg));
+ snprintf(buf, sizeof(buf), "\n\talgorithm: %s", alg);
+ CHECK(putstr(text, buf));
+
+ revoked = ((kd.flags & DNS_KEYFLAG_REVOKE) != 0);
+ snprintf(buf, sizeof(buf), "\n\tflags:%s%s%s",
+ revoked ? " REVOKE" : "",
+ ((kd.flags & DNS_KEYFLAG_KSK) != 0) ? " SEP"
+ : "",
+ (kd.flags == 0) ? " (none)" : "");
+ CHECK(putstr(text, buf));
+
+ isc_time_set(&t, kd.refresh, 0);
+ isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf));
+ snprintf(buf, sizeof(buf), "\n\tnext refresh: %s",
+ tbuf);
+ CHECK(putstr(text, buf));
+
+ if (kd.removehd != 0) {
+ isc_time_set(&t, kd.removehd, 0);
+ isc_time_formathttptimestamp(&t, tbuf,
+ sizeof(tbuf));
+ snprintf(buf, sizeof(buf), "\n\tremove at: %s",
+ tbuf);
+ CHECK(putstr(text, buf));
+ }
+
+ isc_time_set(&t, kd.addhd, 0);
+ isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf));
+ if (kd.addhd == 0) {
+ snprintf(buf, sizeof(buf), "\n\tno trust");
+ } else if (revoked) {
+ snprintf(buf, sizeof(buf), "\n\ttrust revoked");
+ } else if (kd.addhd <= now) {
+ snprintf(buf, sizeof(buf),
+ "\n\ttrusted since: %s", tbuf);
+ } else if (kd.addhd > now) {
+ snprintf(buf, sizeof(buf),
+ "\n\ttrust pending: %s", tbuf);
+ }
+ CHECK(putstr(text, buf));
+ }
+ }
+
+ if (result == ISC_R_NOMORE) {
+ result = ISC_R_SUCCESS;
+ }
+
+cleanup:
+ if (ver != NULL) {
+ dns_rriterator_destroy(&rrit);
+ dns_db_closeversion(db, &ver, false);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+mkey_status(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ char msg[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_t t;
+
+ CHECK(putstr(text, "view: "));
+ CHECK(putstr(text, view->name));
+
+ CHECK(putstr(text, "\nnext scheduled event: "));
+
+ dns_zone_getrefreshkeytime(view->managed_keys, &t);
+ if (isc_time_isepoch(&t)) {
+ CHECK(putstr(text, "never"));
+ } else {
+ isc_time_formathttptimestamp(&t, msg, sizeof(msg));
+ CHECK(putstr(text, msg));
+ }
+
+ CHECK(mkey_dumpzone(view, text));
+
+cleanup:
+ return (result);
+}
+
+isc_result_t
+named_server_mkeys(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ char *cmd, *classtxt, *viewtxt = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+ char msg[DNS_NAME_FORMATSIZE + 500] = "";
+ enum { NONE, STATUS, REFRESH, SYNC, DESTROY } opt = NONE;
+ bool found = false;
+ bool first = true;
+
+ REQUIRE(text != NULL);
+
+ /* Skip rndc command name */
+ cmd = next_token(lex, text);
+ if (cmd == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Get managed-keys subcommand */
+ cmd = next_token(lex, text);
+ if (cmd == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (strcasecmp(cmd, "status") == 0) {
+ opt = STATUS;
+ } else if (strcasecmp(cmd, "refresh") == 0) {
+ opt = REFRESH;
+ } else if (strcasecmp(cmd, "sync") == 0) {
+ opt = SYNC;
+ } else if (strcasecmp(cmd, "destroy") == 0) {
+ opt = DESTROY;
+ } else {
+ snprintf(msg, sizeof(msg), "unknown command '%s'", cmd);
+ (void)putstr(text, msg);
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ /* Look for the optional class name. */
+ classtxt = next_token(lex, text);
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS) {
+ snprintf(msg, sizeof(msg), "unknown class '%s'",
+ classtxt);
+ (void)putstr(text, msg);
+ goto cleanup;
+ }
+ viewtxt = next_token(lex, text);
+ }
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewtxt != NULL && (rdclass != view->rdclass ||
+ strcmp(view->name, viewtxt) != 0))
+ {
+ continue;
+ }
+
+ if (view->managed_keys == NULL) {
+ if (viewtxt != NULL) {
+ snprintf(msg, sizeof(msg),
+ "view '%s': no managed keys", viewtxt);
+ CHECK(putstr(text, msg));
+ goto cleanup;
+ } else {
+ continue;
+ }
+ }
+
+ found = true;
+
+ switch (opt) {
+ case REFRESH:
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(mkey_refresh(view, text));
+ break;
+ case STATUS:
+ if (!first) {
+ CHECK(putstr(text, "\n\n"));
+ }
+ CHECK(mkey_status(view, text));
+ break;
+ case SYNC:
+ CHECK(dns_zone_flush(view->managed_keys));
+ break;
+ case DESTROY:
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(mkey_destroy(server, view, text));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (viewtxt != NULL) {
+ break;
+ }
+ first = false;
+ }
+
+ if (!found) {
+ CHECK(putstr(text, "no views with managed keys"));
+ }
+
+cleanup:
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+
+ return (result);
+}
+
+isc_result_t
+named_server_dnstap(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+#ifdef HAVE_DNSTAP
+ char *ptr;
+ isc_result_t result;
+ bool reopen = false;
+ int backups = 0;
+
+ REQUIRE(text != NULL);
+
+ if (server->dtenv == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ /* Check the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* "dnstap-reopen" was used in 9.11.0b1 */
+ if (strcasecmp(ptr, "dnstap-reopen") == 0) {
+ reopen = true;
+ } else {
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ }
+
+ if (reopen || strcasecmp(ptr, "-reopen") == 0) {
+ backups = ISC_LOG_ROLLNEVER;
+ } else if ((strcasecmp(ptr, "-roll") == 0)) {
+ unsigned int n;
+ ptr = next_token(lex, text);
+ if (ptr != NULL) {
+ unsigned int u;
+ n = sscanf(ptr, "%u", &u);
+ if (n != 1U || u > INT_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ backups = u;
+ } else {
+ backups = ISC_LOG_ROLLINFINITE;
+ }
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ result = dns_dt_reopen(server->dtenv, backups);
+ return (result);
+#else /* ifdef HAVE_DNSTAP */
+ UNUSED(server);
+ UNUSED(lex);
+ UNUSED(text);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif /* ifdef HAVE_DNSTAP */
+}
+
+isc_result_t
+named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text) {
+ char *ptr;
+ isc_result_t result = ISC_R_SUCCESS;
+ uint32_t initial, idle, keepalive, advertised;
+ char msg[128];
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ isc_nm_gettimeouts(named_g_netmgr, &initial, &idle, &keepalive,
+ &advertised);
+
+ /* Look for optional arguments. */
+ ptr = next_token(lex, NULL);
+ if (ptr != NULL) {
+ CHECK(isc_parse_uint32(&initial, ptr, 10));
+ initial *= 100;
+ if (initial > MAX_INITIAL_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+ if (initial < MIN_INITIAL_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(isc_parse_uint32(&idle, ptr, 10));
+ idle *= 100;
+ if (idle > MAX_IDLE_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+ if (idle < MIN_IDLE_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(isc_parse_uint32(&keepalive, ptr, 10));
+ keepalive *= 100;
+ if (keepalive > MAX_KEEPALIVE_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+ if (keepalive < MIN_KEEPALIVE_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ CHECK(isc_parse_uint32(&advertised, ptr, 10));
+ advertised *= 100;
+ if (advertised > MAX_ADVERTISED_TIMEOUT) {
+ CHECK(ISC_R_RANGE);
+ }
+
+ result = isc_task_beginexclusive(named_g_server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive,
+ advertised);
+
+ isc_task_endexclusive(named_g_server->task);
+ }
+
+ snprintf(msg, sizeof(msg), "tcp-initial-timeout=%u\n", initial / 100);
+ CHECK(putstr(text, msg));
+ snprintf(msg, sizeof(msg), "tcp-idle-timeout=%u\n", idle / 100);
+ CHECK(putstr(text, msg));
+ snprintf(msg, sizeof(msg), "tcp-keepalive-timeout=%u\n",
+ keepalive / 100);
+ CHECK(putstr(text, msg));
+ snprintf(msg, sizeof(msg), "tcp-advertised-timeout=%u",
+ advertised / 100);
+ CHECK(putstr(text, msg));
+
+cleanup:
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+
+ return (result);
+}
+
+isc_result_t
+named_server_servestale(named_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text) {
+ char *ptr, *classtxt, *viewtxt = NULL;
+ char msg[128];
+ dns_rdataclass_t rdclass = dns_rdataclass_in;
+ dns_view_t *view;
+ bool found = false;
+ dns_stale_answer_t staleanswersok = dns_stale_answer_conf;
+ bool wantstatus = false;
+ isc_result_t result = ISC_R_SUCCESS;
+ bool exclusive = false;
+
+ REQUIRE(text != NULL);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+ {
+ staleanswersok = dns_stale_answer_yes;
+ } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+ {
+ staleanswersok = dns_stale_answer_no;
+ } else if (strcasecmp(ptr, "reset") == 0) {
+ staleanswersok = dns_stale_answer_conf;
+ } else if (!strcasecmp(ptr, "check") || !strcasecmp(ptr, "status")) {
+ wantstatus = true;
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ /* Look for the optional class name. */
+ classtxt = next_token(lex, text);
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+
+ /* Look for the optional view name. */
+ viewtxt = next_token(lex, text);
+
+ /*
+ * If 'classtext' is not a valid class then it us a view name.
+ */
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS) {
+ if (viewtxt != NULL) {
+ snprintf(msg, sizeof(msg), "unknown class '%s'",
+ classtxt);
+ (void)putstr(text, msg);
+ goto cleanup;
+ }
+
+ viewtxt = classtxt;
+ classtxt = NULL;
+ }
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ dns_ttl_t stale_ttl = 0;
+ uint32_t stale_refresh = 0;
+ dns_db_t *db = NULL;
+
+ if (classtxt != NULL && rdclass != view->rdclass) {
+ continue;
+ }
+
+ if (viewtxt != NULL && strcmp(view->name, viewtxt) != 0) {
+ continue;
+ }
+
+ if (!wantstatus) {
+ view->staleanswersok = staleanswersok;
+ found = true;
+ continue;
+ }
+
+ db = NULL;
+ dns_db_attach(view->cachedb, &db);
+ (void)dns_db_getservestalettl(db, &stale_ttl);
+ (void)dns_db_getservestalerefresh(db, &stale_refresh);
+ dns_db_detach(&db);
+ if (found) {
+ CHECK(putstr(text, "\n"));
+ }
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, ": "));
+ switch (view->staleanswersok) {
+ case dns_stale_answer_yes:
+ if (stale_ttl > 0) {
+ CHECK(putstr(text, "stale cache enabled; stale "
+ "answers enabled"));
+ } else {
+ CHECK(putstr(text,
+ "stale cache disabled; stale "
+ "answers unavailable"));
+ }
+ break;
+ case dns_stale_answer_no:
+ if (stale_ttl > 0) {
+ CHECK(putstr(text, "stale cache enabled; stale "
+ "answers disabled"));
+ } else {
+ CHECK(putstr(text,
+ "stale cache disabled; stale "
+ "answers unavailable"));
+ }
+ break;
+ case dns_stale_answer_conf:
+ if (view->staleanswersenable && stale_ttl > 0) {
+ CHECK(putstr(text, "stale cache enabled; stale "
+ "answers enabled"));
+ } else if (stale_ttl > 0) {
+ CHECK(putstr(text, "stale cache enabled; stale "
+ "answers disabled"));
+ } else {
+ CHECK(putstr(text,
+ "stale cache disabled; stale "
+ "answers unavailable"));
+ }
+ break;
+ }
+ if (stale_ttl > 0) {
+ snprintf(msg, sizeof(msg),
+ " (stale-answer-ttl=%u max-stale-ttl=%u "
+ "stale-refresh-time=%u)",
+ view->staleanswerttl, stale_ttl,
+ stale_refresh);
+ CHECK(putstr(text, msg));
+ }
+ found = true;
+ }
+
+ if (!found) {
+ result = ISC_R_NOTFOUND;
+ }
+
+cleanup:
+ if (exclusive) {
+ isc_task_endexclusive(named_g_server->task);
+ }
+
+ if (isc_buffer_usedlength(*text) > 0) {
+ (void)putnull(text);
+ }
+
+ return (result);
+}
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
new file mode 100644
index 0000000..2c4760c
--- /dev/null
+++ b/bin/named/statschannel.c
@@ -0,0 +1,4084 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/httpd.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/stats.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/stats.h>
+#include <dns/view.h>
+#include <dns/zt.h>
+
+#include <ns/stats.h>
+
+#include <named/log.h>
+#include <named/server.h>
+#include <named/statschannel.h>
+
+#if HAVE_JSON_C
+#include <json_object.h>
+#include <linkhash.h>
+#endif /* HAVE_JSON_C */
+
+#if HAVE_LIBXML2
+#include <libxml/xmlwriter.h>
+#define ISC_XMLCHAR (const xmlChar *)
+#endif /* HAVE_LIBXML2 */
+
+#include "xsl_p.h"
+
+#define STATS_XML_VERSION_MAJOR "3"
+#define STATS_XML_VERSION_MINOR "13"
+#define STATS_XML_VERSION STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR
+
+#define STATS_JSON_VERSION_MAJOR "1"
+#define STATS_JSON_VERSION_MINOR "7"
+#define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR
+
+#define CHECK(m) \
+ do { \
+ result = (m); \
+ if (result != ISC_R_SUCCESS) { \
+ goto cleanup; \
+ } \
+ } while (0)
+
+struct named_statschannel {
+ /* Unlocked */
+ isc_httpdmgr_t *httpdmgr;
+ isc_sockaddr_t address;
+ isc_mem_t *mctx;
+
+ /*
+ * Locked by channel lock: can be referenced and modified by both
+ * the server task and the channel task.
+ */
+ isc_mutex_t lock;
+ dns_acl_t *acl;
+
+ /* Locked by server task */
+ ISC_LINK(struct named_statschannel) link;
+};
+
+typedef struct stats_dumparg {
+ isc_statsformat_t type;
+ void *arg; /* type dependent argument */
+ int ncounters; /* for general statistics */
+ int *counterindices; /* for general statistics */
+ uint64_t *countervalues; /* for general statistics */
+ isc_result_t result;
+} stats_dumparg_t;
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
+#define EXTENDED_STATS
+#else /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
+#undef EXTENDED_STATS
+#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
+
+#ifdef EXTENDED_STATS
+static const char *
+user_zonetype(dns_zone_t *zone) {
+ dns_zonetype_t ztype;
+ dns_view_t *view;
+ static const struct zt {
+ const dns_zonetype_t type;
+ const char *const string;
+ } typemap[] = { { dns_zone_none, "none" },
+ { dns_zone_primary, "primary" },
+ { dns_zone_secondary, "secondary" },
+ { dns_zone_mirror, "mirror" },
+ { dns_zone_stub, "stub" },
+ { dns_zone_staticstub, "static-stub" },
+ { dns_zone_key, "key" },
+ { dns_zone_dlz, "dlz" },
+ { dns_zone_redirect, "redirect" },
+ { 0, NULL } };
+ const struct zt *tp;
+
+ if ((dns_zone_getoptions(zone) & DNS_ZONEOPT_AUTOEMPTY) != 0) {
+ return ("builtin");
+ }
+
+ view = dns_zone_getview(zone);
+ if (view != NULL && strcmp(view->name, "_bind") == 0) {
+ return ("builtin");
+ }
+
+ ztype = dns_zone_gettype(zone);
+ for (tp = typemap; tp->string != NULL && tp->type != ztype; tp++) {
+ /* empty */
+ }
+ return (tp->string);
+}
+#endif /* ifdef EXTENDED_STATS */
+
+/*%
+ * Statistics descriptions. These could be statistically initialized at
+ * compile time, but we configure them run time in the init_desc() function
+ * below so that they'll be less susceptible to counter name changes.
+ */
+static const char *nsstats_desc[ns_statscounter_max];
+static const char *resstats_desc[dns_resstatscounter_max];
+static const char *adbstats_desc[dns_adbstats_max];
+static const char *zonestats_desc[dns_zonestatscounter_max];
+static const char *sockstats_desc[isc_sockstatscounter_max];
+static const char *dnssecstats_desc[dns_dnssecstats_max];
+static const char *udpinsizestats_desc[dns_sizecounter_in_max];
+static const char *udpoutsizestats_desc[dns_sizecounter_out_max];
+static const char *tcpinsizestats_desc[dns_sizecounter_in_max];
+static const char *tcpoutsizestats_desc[dns_sizecounter_out_max];
+static const char *dnstapstats_desc[dns_dnstapcounter_max];
+static const char *gluecachestats_desc[dns_gluecachestatscounter_max];
+#if defined(EXTENDED_STATS)
+static const char *nsstats_xmldesc[ns_statscounter_max];
+static const char *resstats_xmldesc[dns_resstatscounter_max];
+static const char *adbstats_xmldesc[dns_adbstats_max];
+static const char *zonestats_xmldesc[dns_zonestatscounter_max];
+static const char *sockstats_xmldesc[isc_sockstatscounter_max];
+static const char *dnssecstats_xmldesc[dns_dnssecstats_max];
+static const char *udpinsizestats_xmldesc[dns_sizecounter_in_max];
+static const char *udpoutsizestats_xmldesc[dns_sizecounter_out_max];
+static const char *tcpinsizestats_xmldesc[dns_sizecounter_in_max];
+static const char *tcpoutsizestats_xmldesc[dns_sizecounter_out_max];
+static const char *dnstapstats_xmldesc[dns_dnstapcounter_max];
+static const char *gluecachestats_xmldesc[dns_gluecachestatscounter_max];
+#else /* if defined(EXTENDED_STATS) */
+#define nsstats_xmldesc NULL
+#define resstats_xmldesc NULL
+#define adbstats_xmldesc NULL
+#define zonestats_xmldesc NULL
+#define sockstats_xmldesc NULL
+#define dnssecstats_xmldesc NULL
+#define udpinsizestats_xmldesc NULL
+#define udpoutsizestats_xmldesc NULL
+#define tcpinsizestats_xmldesc NULL
+#define tcpoutsizestats_xmldesc NULL
+#define dnstapstats_xmldesc NULL
+#define gluecachestats_xmldesc NULL
+#endif /* EXTENDED_STATS */
+
+#define TRY0(a) \
+ do { \
+ xmlrc = (a); \
+ if (xmlrc < 0) \
+ goto cleanup; \
+ } while (0)
+
+/*%
+ * Mapping arrays to represent statistics counters in the order of our
+ * preference, regardless of the order of counter indices. For example,
+ * nsstats_desc[nsstats_index[0]] will be the description that is shown first.
+ */
+static int nsstats_index[ns_statscounter_max];
+static int resstats_index[dns_resstatscounter_max];
+static int adbstats_index[dns_adbstats_max];
+static int zonestats_index[dns_zonestatscounter_max];
+static int sockstats_index[isc_sockstatscounter_max];
+static int dnssecstats_index[dns_dnssecstats_max];
+static int udpinsizestats_index[dns_sizecounter_in_max];
+static int udpoutsizestats_index[dns_sizecounter_out_max];
+static int tcpinsizestats_index[dns_sizecounter_in_max];
+static int tcpoutsizestats_index[dns_sizecounter_out_max];
+static int dnstapstats_index[dns_dnstapcounter_max];
+static int gluecachestats_index[dns_gluecachestatscounter_max];
+
+static void
+set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
+ const char *xdesc, const char **xdescs) {
+ REQUIRE(counter < maxcounter);
+ REQUIRE(fdescs != NULL && fdescs[counter] == NULL);
+#if defined(EXTENDED_STATS)
+ REQUIRE(xdescs != NULL && xdescs[counter] == NULL);
+#endif /* if defined(EXTENDED_STATS) */
+
+ fdescs[counter] = fdesc;
+#if defined(EXTENDED_STATS)
+ xdescs[counter] = xdesc;
+#else /* if defined(EXTENDED_STATS) */
+ UNUSED(xdesc);
+ UNUSED(xdescs);
+#endif /* if defined(EXTENDED_STATS) */
+}
+
+static void
+init_desc(void) {
+ int i;
+
+ /* Initialize name server statistics */
+ for (i = 0; i < ns_statscounter_max; i++) {
+ nsstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < ns_statscounter_max; i++) {
+ nsstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_NSSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(ns_statscounter_##counterid, ns_statscounter_max, \
+ desc, nsstats_desc, xmldesc, nsstats_xmldesc); \
+ nsstats_index[i++] = ns_statscounter_##counterid; \
+ } while (0)
+
+ i = 0;
+ SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4");
+ SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6");
+ SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0");
+ SET_NSSTATDESC(badednsver,
+ "requests with unsupported EDNS version received",
+ "ReqBadEDNSVer");
+ SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG");
+ SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
+ SET_NSSTATDESC(invalidsig, "requests with invalid signature",
+ "ReqBadSIG");
+ SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP");
+ SET_NSSTATDESC(tcphighwater, "TCP connection high-water",
+ "TCPConnHighWater");
+ SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
+ SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
+ SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
+ SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej");
+ SET_NSSTATDESC(response, "responses sent", "Response");
+ SET_NSSTATDESC(truncatedresp, "truncated responses sent",
+ "TruncatedResp");
+ SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0");
+ SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG");
+ SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0");
+ SET_NSSTATDESC(success, "queries resulted in successful answer",
+ "QrySuccess");
+ SET_NSSTATDESC(authans, "queries resulted in authoritative answer",
+ "QryAuthAns");
+ SET_NSSTATDESC(nonauthans,
+ "queries resulted in non authoritative answer",
+ "QryNoauthAns");
+ SET_NSSTATDESC(referral, "queries resulted in referral answer",
+ "QryReferral");
+ SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset");
+ SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL");
+ SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR");
+ SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN");
+ SET_NSSTATDESC(recursion, "queries caused recursion", "QryRecursion");
+ SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate");
+ SET_NSSTATDESC(dropped, "queries dropped", "QryDropped");
+ SET_NSSTATDESC(failure, "other query failures", "QryFailure");
+ SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone");
+ SET_NSSTATDESC(updatereqfwd, "update requests forwarded",
+ "UpdateReqFwd");
+ SET_NSSTATDESC(updaterespfwd, "update responses forwarded",
+ "UpdateRespFwd");
+ SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail");
+ SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone");
+ SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail");
+ SET_NSSTATDESC(updatebadprereq,
+ "updates rejected due to prerequisite failure",
+ "UpdateBadPrereq");
+ SET_NSSTATDESC(recursclients, "recursing clients", "RecursClients");
+ SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64");
+ SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
+ "RateDropped");
+ SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
+ "RateSlipped");
+ SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
+ "RPZRewrites");
+ SET_NSSTATDESC(udp, "UDP queries received", "QryUDP");
+ SET_NSSTATDESC(tcp, "TCP queries received", "QryTCP");
+ SET_NSSTATDESC(nsidopt, "NSID option received", "NSIDOpt");
+ SET_NSSTATDESC(expireopt, "Expire option received", "ExpireOpt");
+ SET_NSSTATDESC(keepaliveopt, "EDNS TCP keepalive option received",
+ "KeepAliveOpt");
+ SET_NSSTATDESC(padopt, "EDNS padding option received", "PadOpt");
+ SET_NSSTATDESC(otheropt, "Other EDNS option received", "OtherOpt");
+ SET_NSSTATDESC(cookiein, "COOKIE option received", "CookieIn");
+ SET_NSSTATDESC(cookienew, "COOKIE - client only", "CookieNew");
+ SET_NSSTATDESC(cookiebadsize, "COOKIE - bad size", "CookieBadSize");
+ SET_NSSTATDESC(cookiebadtime, "COOKIE - bad time", "CookieBadTime");
+ SET_NSSTATDESC(cookienomatch, "COOKIE - no match", "CookieNoMatch");
+ SET_NSSTATDESC(cookiematch, "COOKIE - match", "CookieMatch");
+ SET_NSSTATDESC(ecsopt, "EDNS client subnet option received", "ECSOpt");
+ SET_NSSTATDESC(nxdomainredirect,
+ "queries resulted in NXDOMAIN that were redirected",
+ "QryNXRedir");
+ SET_NSSTATDESC(nxdomainredirect_rlookup,
+ "queries resulted in NXDOMAIN that were redirected and "
+ "resulted in a successful remote lookup",
+ "QryNXRedirRLookup");
+ SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE");
+ SET_NSSTATDESC(nxdomainsynth, "synthesized a NXDOMAIN response",
+ "SynthNXDOMAIN");
+ SET_NSSTATDESC(nodatasynth, "synthesized a no-data response",
+ "SynthNODATA");
+ SET_NSSTATDESC(wildcardsynth, "synthesized a wildcard response",
+ "SynthWILDCARD");
+ SET_NSSTATDESC(trystale,
+ "attempts to use stale cache data after lookup failure",
+ "QryTryStale");
+ SET_NSSTATDESC(usedstale,
+ "successful uses of stale cache data after lookup "
+ "failure",
+ "QryUsedStale");
+ SET_NSSTATDESC(prefetch, "queries triggered prefetch", "Prefetch");
+ SET_NSSTATDESC(keytagopt, "Keytag option received", "KeyTagOpt");
+ SET_NSSTATDESC(reclimitdropped,
+ "queries dropped due to recursive client limit",
+ "RecLimitDropped");
+ SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota");
+
+ INSIST(i == ns_statscounter_max);
+
+ /* Initialize resolver statistics */
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ resstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ resstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_RESSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_resstatscounter_##counterid, \
+ dns_resstatscounter_max, desc, resstats_desc, \
+ xmldesc, resstats_xmldesc); \
+ resstats_index[i++] = dns_resstatscounter_##counterid; \
+ } while (0)
+
+ i = 0;
+ SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4");
+ SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6");
+ SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4");
+ SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6");
+ SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN");
+ SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL");
+ SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR");
+ SET_RESSTATDESC(othererror, "other errors received", "OtherError");
+ SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail");
+ SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch");
+ SET_RESSTATDESC(truncated, "truncated responses received", "Truncated");
+ SET_RESSTATDESC(lame, "lame delegations received", "Lame");
+ SET_RESSTATDESC(retry, "query retries", "Retry");
+ SET_RESSTATDESC(dispabort, "queries aborted due to quota",
+ "QueryAbort");
+ SET_RESSTATDESC(dispsockfail, "failures in opening query sockets",
+ "QuerySockFail");
+ SET_RESSTATDESC(disprequdp, "UDP queries in progress", "QueryCurUDP");
+ SET_RESSTATDESC(dispreqtcp, "TCP queries in progress", "QueryCurTCP");
+ SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout");
+ SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4");
+ SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6");
+ SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed",
+ "GlueFetchv4Fail");
+ SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed",
+ "GlueFetchv6Fail");
+ SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt");
+ SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk");
+ SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded",
+ "ValNegOk");
+ SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail");
+ SET_RESSTATDESC(queryrtt0,
+ "queries with RTT < " DNS_RESOLVER_QRYRTTCLASS0STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
+ SET_RESSTATDESC(queryrtt1,
+ "queries with RTT " DNS_RESOLVER_QRYRTTCLASS0STR
+ "-" DNS_RESOLVER_QRYRTTCLASS1STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
+ SET_RESSTATDESC(queryrtt2,
+ "queries with RTT " DNS_RESOLVER_QRYRTTCLASS1STR
+ "-" DNS_RESOLVER_QRYRTTCLASS2STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
+ SET_RESSTATDESC(queryrtt3,
+ "queries with RTT " DNS_RESOLVER_QRYRTTCLASS2STR
+ "-" DNS_RESOLVER_QRYRTTCLASS3STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
+ SET_RESSTATDESC(queryrtt4,
+ "queries with RTT " DNS_RESOLVER_QRYRTTCLASS3STR
+ "-" DNS_RESOLVER_QRYRTTCLASS4STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
+ SET_RESSTATDESC(queryrtt5,
+ "queries with RTT > " DNS_RESOLVER_QRYRTTCLASS4STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
+ SET_RESSTATDESC(nfetch, "active fetches", "NumFetch");
+ SET_RESSTATDESC(buckets, "bucket size", "BucketSize");
+ SET_RESSTATDESC(refused, "REFUSED received", "REFUSED");
+ SET_RESSTATDESC(cookienew, "COOKIE send with client cookie only",
+ "ClientCookieOut");
+ SET_RESSTATDESC(cookieout, "COOKIE sent with client and server cookie",
+ "ServerCookieOut");
+ SET_RESSTATDESC(cookiein, "COOKIE replies received", "CookieIn");
+ SET_RESSTATDESC(cookieok, "COOKIE client ok", "CookieClientOk");
+ SET_RESSTATDESC(badvers, "bad EDNS version", "BadEDNSVersion");
+ SET_RESSTATDESC(badcookie, "bad cookie rcode", "BadCookieRcode");
+ SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota");
+ SET_RESSTATDESC(serverquota, "spilled due to server quota",
+ "ServerQuota");
+ SET_RESSTATDESC(clientquota, "spilled due to clients per query quota",
+ "ClientQuota");
+ SET_RESSTATDESC(nextitem, "waited for next item", "NextItem");
+ SET_RESSTATDESC(priming, "priming queries", "Priming");
+
+ INSIST(i == dns_resstatscounter_max);
+
+ /* Initialize adb statistics */
+ for (i = 0; i < dns_adbstats_max; i++) {
+ adbstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_adbstats_max; i++) {
+ adbstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_ADBSTATDESC(id, desc, xmldesc) \
+ do { \
+ set_desc(dns_adbstats_##id, dns_adbstats_max, desc, \
+ adbstats_desc, xmldesc, adbstats_xmldesc); \
+ adbstats_index[i++] = dns_adbstats_##id; \
+ } while (0)
+ i = 0;
+ SET_ADBSTATDESC(nentries, "Address hash table size", "nentries");
+ SET_ADBSTATDESC(entriescnt, "Addresses in hash table", "entriescnt");
+ SET_ADBSTATDESC(nnames, "Name hash table size", "nnames");
+ SET_ADBSTATDESC(namescnt, "Names in hash table", "namescnt");
+
+ INSIST(i == dns_adbstats_max);
+
+ /* Initialize zone statistics */
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ zonestats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ zonestats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_ZONESTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_zonestatscounter_##counterid, \
+ dns_zonestatscounter_max, desc, zonestats_desc, \
+ xmldesc, zonestats_xmldesc); \
+ zonestats_index[i++] = dns_zonestatscounter_##counterid; \
+ } while (0)
+
+ i = 0;
+ SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4");
+ SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6");
+ SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4");
+ SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6");
+ SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej");
+ SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4");
+ SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6");
+ SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4");
+ SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6");
+ SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4");
+ SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6");
+ SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded",
+ "XfrSuccess");
+ SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail");
+ INSIST(i == dns_zonestatscounter_max);
+
+ /* Initialize socket statistics */
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ sockstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ sockstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_SOCKSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(isc_sockstatscounter_##counterid, \
+ isc_sockstatscounter_max, desc, sockstats_desc, \
+ xmldesc, sockstats_xmldesc); \
+ sockstats_index[i++] = isc_sockstatscounter_##counterid; \
+ } while (0)
+
+ i = 0;
+ SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open");
+ SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open");
+ SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open");
+ SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open");
+ SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen");
+ SET_SOCKSTATDESC(rawopen, "Raw sockets opened", "RawOpen");
+ SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures",
+ "UDP4OpenFail");
+ SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures",
+ "UDP6OpenFail");
+ SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures",
+ "TCP4OpenFail");
+ SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures",
+ "TCP6OpenFail");
+ SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures",
+ "UnixOpenFail");
+ SET_SOCKSTATDESC(rawopenfail, "Raw socket open failures",
+ "RawOpenFail");
+ SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close");
+ SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close");
+ SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close");
+ SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close");
+ SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose");
+ SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed",
+ "FDWatchClose");
+ SET_SOCKSTATDESC(rawclose, "Raw sockets closed", "RawClose");
+ SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures",
+ "UDP4BindFail");
+ SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures",
+ "UDP6BindFail");
+ SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures",
+ "TCP4BindFail");
+ SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures",
+ "TCP6BindFail");
+ SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures",
+ "UnixBindFail");
+ SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures",
+ "FdwatchBindFail");
+ SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures",
+ "UDP4ConnFail");
+ SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures",
+ "UDP6ConnFail");
+ SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures",
+ "TCP4ConnFail");
+ SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures",
+ "TCP6ConnFail");
+ SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures",
+ "UnixConnFail");
+ SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures",
+ "FDwatchConnFail");
+ SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established",
+ "UDP4Conn");
+ SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established",
+ "UDP6Conn");
+ SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established",
+ "TCP4Conn");
+ SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established",
+ "TCP6Conn");
+ SET_SOCKSTATDESC(unixconnect, "Unix domain connections established",
+ "UnixConn");
+ SET_SOCKSTATDESC(fdwatchconnect,
+ "FDwatch domain connections established",
+ "FDwatchConn");
+ SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures",
+ "TCP4AcceptFail");
+ SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures",
+ "TCP6AcceptFail");
+ SET_SOCKSTATDESC(unixacceptfail,
+ "Unix domain connection accept failures",
+ "UnixAcceptFail");
+ SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted",
+ "TCP4Accept");
+ SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted",
+ "TCP6Accept");
+ SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted",
+ "UnixAccept");
+ SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr");
+ SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr");
+ SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr");
+ SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr");
+ SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors",
+ "UnixSendErr");
+ SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors",
+ "FDwatchSendErr");
+ SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr");
+ SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr");
+ SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr");
+ SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr");
+ SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors",
+ "UnixRecvErr");
+ SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors",
+ "FDwatchRecvErr");
+ SET_SOCKSTATDESC(rawrecvfail, "Raw recv errors", "RawRecvErr");
+ SET_SOCKSTATDESC(udp4active, "UDP/IPv4 sockets active", "UDP4Active");
+ SET_SOCKSTATDESC(udp6active, "UDP/IPv6 sockets active", "UDP6Active");
+ SET_SOCKSTATDESC(tcp4active, "TCP/IPv4 sockets active", "TCP4Active");
+ SET_SOCKSTATDESC(tcp6active, "TCP/IPv6 sockets active", "TCP6Active");
+ SET_SOCKSTATDESC(unixactive, "Unix domain sockets active",
+ "UnixActive");
+ SET_SOCKSTATDESC(rawactive, "Raw sockets active", "RawActive");
+ INSIST(i == isc_sockstatscounter_max);
+
+ /* Initialize DNSSEC statistics */
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ dnssecstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ dnssecstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_DNSSECSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_dnssecstats_##counterid, dns_dnssecstats_max, \
+ desc, dnssecstats_desc, xmldesc, \
+ dnssecstats_xmldesc); \
+ dnssecstats_index[i++] = dns_dnssecstats_##counterid; \
+ } while (0)
+
+ i = 0;
+ SET_DNSSECSTATDESC(asis,
+ "dnssec validation success with signer "
+ "\"as is\"",
+ "DNSSECasis");
+ SET_DNSSECSTATDESC(downcase,
+ "dnssec validation success with signer "
+ "lower cased",
+ "DNSSECdowncase");
+ SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature",
+ "DNSSECwild");
+ SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail");
+ INSIST(i == dns_dnssecstats_max);
+
+ /* Initialize dnstap statistics */
+ for (i = 0; i < dns_dnstapcounter_max; i++) {
+ dnstapstats_desc[i] = NULL;
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_dnstapcounter_max; i++) {
+ dnstapstats_xmldesc[i] = NULL;
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+#define SET_DNSTAPSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_dnstapcounter_##counterid, dns_dnstapcounter_max, \
+ desc, dnstapstats_desc, xmldesc, \
+ dnstapstats_xmldesc); \
+ dnstapstats_index[i++] = dns_dnstapcounter_##counterid; \
+ } while (0)
+ i = 0;
+ SET_DNSTAPSTATDESC(success, "dnstap messages written", "DNSTAPsuccess");
+ SET_DNSTAPSTATDESC(drop, "dnstap messages dropped", "DNSTAPdropped");
+ INSIST(i == dns_dnstapcounter_max);
+
+#define SET_GLUECACHESTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_gluecachestatscounter_##counterid, \
+ dns_gluecachestatscounter_max, desc, \
+ gluecachestats_desc, xmldesc, \
+ gluecachestats_xmldesc); \
+ gluecachestats_index[i++] = \
+ dns_gluecachestatscounter_##counterid; \
+ } while (0)
+ i = 0;
+ SET_GLUECACHESTATDESC(hits_present, "Hits for present glue (cached)",
+ "GLUECACHEhitspresent");
+ SET_GLUECACHESTATDESC(hits_absent,
+ "Hits for non-existent glue (cached)",
+ "GLUECACHEhitsabsent");
+ SET_GLUECACHESTATDESC(inserts_present,
+ "Miss-plus-cache-inserts for present glue",
+ "GLUECACHEinsertspresent");
+ SET_GLUECACHESTATDESC(inserts_absent,
+ "Miss-plus-cache-inserts for non-existent glue",
+ "GLUECACHEinsertsabsent");
+ INSIST(i == dns_gluecachestatscounter_max);
+
+ /* Sanity check */
+ for (i = 0; i < ns_statscounter_max; i++) {
+ INSIST(nsstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ INSIST(resstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_adbstats_max; i++) {
+ INSIST(adbstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ INSIST(zonestats_desc[i] != NULL);
+ }
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ INSIST(sockstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ INSIST(dnssecstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnstapcounter_max; i++) {
+ INSIST(dnstapstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_gluecachestatscounter_max; i++) {
+ INSIST(gluecachestats_desc[i] != NULL);
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < ns_statscounter_max; i++) {
+ INSIST(nsstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ INSIST(resstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_adbstats_max; i++) {
+ INSIST(adbstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ INSIST(zonestats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ INSIST(sockstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ INSIST(dnssecstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnstapcounter_max; i++) {
+ INSIST(dnstapstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_gluecachestatscounter_max; i++) {
+ INSIST(gluecachestats_xmldesc[i] != NULL);
+ }
+#endif /* if defined(EXTENDED_STATS) */
+
+ /* Initialize traffic size statistics */
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ udpinsizestats_desc[i] = NULL;
+ tcpinsizestats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ udpinsizestats_xmldesc[i] = NULL;
+ tcpinsizestats_xmldesc[i] = NULL;
+#endif /* if defined(EXTENDED_STATS) */
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ udpoutsizestats_desc[i] = NULL;
+ tcpoutsizestats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ udpoutsizestats_xmldesc[i] = NULL;
+ tcpoutsizestats_xmldesc[i] = NULL;
+#endif /* if defined(EXTENDED_STATS) */
+ }
+
+#define SET_SIZESTATDESC(counterid, desc, xmldesc, inout) \
+ do { \
+ set_desc(dns_sizecounter_##inout##_##counterid, \
+ dns_sizecounter_##inout##_max, desc, \
+ udp##inout##sizestats_desc, xmldesc, \
+ udp##inout##sizestats_xmldesc); \
+ set_desc(dns_sizecounter_##inout##_##counterid, \
+ dns_sizecounter_##inout##_max, desc, \
+ tcp##inout##sizestats_desc, xmldesc, \
+ tcp##inout##sizestats_xmldesc); \
+ udp##inout##sizestats_index[i] = \
+ dns_sizecounter_##inout##_##counterid; \
+ tcp##inout##sizestats_index[i] = \
+ dns_sizecounter_##inout##_##counterid; \
+ i++; \
+ } while (0)
+
+ i = 0;
+ SET_SIZESTATDESC(0, "requests received 0-15 bytes", "0-15", in);
+ SET_SIZESTATDESC(16, "requests received 16-31 bytes", "16-31", in);
+ SET_SIZESTATDESC(32, "requests received 32-47 bytes", "32-47", in);
+ SET_SIZESTATDESC(48, "requests received 48-63 bytes", "48-63", in);
+ SET_SIZESTATDESC(64, "requests received 64-79 bytes", "64-79", in);
+ SET_SIZESTATDESC(80, "requests received 80-95 bytes", "80-95", in);
+ SET_SIZESTATDESC(96, "requests received 96-111 bytes", "96-111", in);
+ SET_SIZESTATDESC(112, "requests received 112-127 bytes", "112-127", in);
+ SET_SIZESTATDESC(128, "requests received 128-143 bytes", "128-143", in);
+ SET_SIZESTATDESC(144, "requests received 144-159 bytes", "144-159", in);
+ SET_SIZESTATDESC(160, "requests received 160-175 bytes", "160-175", in);
+ SET_SIZESTATDESC(176, "requests received 176-191 bytes", "176-191", in);
+ SET_SIZESTATDESC(192, "requests received 192-207 bytes", "192-207", in);
+ SET_SIZESTATDESC(208, "requests received 208-223 bytes", "208-223", in);
+ SET_SIZESTATDESC(224, "requests received 224-239 bytes", "224-239", in);
+ SET_SIZESTATDESC(240, "requests received 240-255 bytes", "240-255", in);
+ SET_SIZESTATDESC(256, "requests received 256-271 bytes", "256-271", in);
+ SET_SIZESTATDESC(272, "requests received 272-287 bytes", "272-287", in);
+ SET_SIZESTATDESC(288, "requests received 288+ bytes", "288+", in);
+ INSIST(i == dns_sizecounter_in_max);
+
+ i = 0;
+ SET_SIZESTATDESC(0, "responses sent 0-15 bytes", "0-15", out);
+ SET_SIZESTATDESC(16, "responses sent 16-31 bytes", "16-31", out);
+ SET_SIZESTATDESC(32, "responses sent 32-47 bytes", "32-47", out);
+ SET_SIZESTATDESC(48, "responses sent 48-63 bytes", "48-63", out);
+ SET_SIZESTATDESC(64, "responses sent 64-79 bytes", "64-79", out);
+ SET_SIZESTATDESC(80, "responses sent 80-95 bytes", "80-95", out);
+ SET_SIZESTATDESC(96, "responses sent 96-111 bytes", "96-111", out);
+ SET_SIZESTATDESC(112, "responses sent 112-127 bytes", "112-127", out);
+ SET_SIZESTATDESC(128, "responses sent 128-143 bytes", "128-143", out);
+ SET_SIZESTATDESC(144, "responses sent 144-159 bytes", "144-159", out);
+ SET_SIZESTATDESC(160, "responses sent 160-175 bytes", "160-175", out);
+ SET_SIZESTATDESC(176, "responses sent 176-191 bytes", "176-191", out);
+ SET_SIZESTATDESC(192, "responses sent 192-207 bytes", "192-207", out);
+ SET_SIZESTATDESC(208, "responses sent 208-223 bytes", "208-223", out);
+ SET_SIZESTATDESC(224, "responses sent 224-239 bytes", "224-239", out);
+ SET_SIZESTATDESC(240, "responses sent 240-255 bytes", "240-255", out);
+ SET_SIZESTATDESC(256, "responses sent 256-271 bytes", "256-271", out);
+ SET_SIZESTATDESC(272, "responses sent 272-287 bytes", "272-287", out);
+ SET_SIZESTATDESC(288, "responses sent 288-303 bytes", "288-303", out);
+ SET_SIZESTATDESC(304, "responses sent 304-319 bytes", "304-319", out);
+ SET_SIZESTATDESC(320, "responses sent 320-335 bytes", "320-335", out);
+ SET_SIZESTATDESC(336, "responses sent 336-351 bytes", "336-351", out);
+ SET_SIZESTATDESC(352, "responses sent 352-367 bytes", "352-367", out);
+ SET_SIZESTATDESC(368, "responses sent 368-383 bytes", "368-383", out);
+ SET_SIZESTATDESC(384, "responses sent 384-399 bytes", "384-399", out);
+ SET_SIZESTATDESC(400, "responses sent 400-415 bytes", "400-415", out);
+ SET_SIZESTATDESC(416, "responses sent 416-431 bytes", "416-431", out);
+ SET_SIZESTATDESC(432, "responses sent 432-447 bytes", "432-447", out);
+ SET_SIZESTATDESC(448, "responses sent 448-463 bytes", "448-463", out);
+ SET_SIZESTATDESC(464, "responses sent 464-479 bytes", "464-479", out);
+ SET_SIZESTATDESC(480, "responses sent 480-495 bytes", "480-495", out);
+ SET_SIZESTATDESC(496, "responses sent 496-511 bytes", "496-511", out);
+ SET_SIZESTATDESC(512, "responses sent 512-527 bytes", "512-527", out);
+ SET_SIZESTATDESC(528, "responses sent 528-543 bytes", "528-543", out);
+ SET_SIZESTATDESC(544, "responses sent 544-559 bytes", "544-559", out);
+ SET_SIZESTATDESC(560, "responses sent 560-575 bytes", "560-575", out);
+ SET_SIZESTATDESC(576, "responses sent 576-591 bytes", "576-591", out);
+ SET_SIZESTATDESC(592, "responses sent 592-607 bytes", "592-607", out);
+ SET_SIZESTATDESC(608, "responses sent 608-623 bytes", "608-623", out);
+ SET_SIZESTATDESC(624, "responses sent 624-639 bytes", "624-639", out);
+ SET_SIZESTATDESC(640, "responses sent 640-655 bytes", "640-655", out);
+ SET_SIZESTATDESC(656, "responses sent 656-671 bytes", "656-671", out);
+ SET_SIZESTATDESC(672, "responses sent 672-687 bytes", "672-687", out);
+ SET_SIZESTATDESC(688, "responses sent 688-703 bytes", "688-703", out);
+ SET_SIZESTATDESC(704, "responses sent 704-719 bytes", "704-719", out);
+ SET_SIZESTATDESC(720, "responses sent 720-735 bytes", "720-735", out);
+ SET_SIZESTATDESC(736, "responses sent 736-751 bytes", "736-751", out);
+ SET_SIZESTATDESC(752, "responses sent 752-767 bytes", "752-767", out);
+ SET_SIZESTATDESC(768, "responses sent 768-783 bytes", "768-783", out);
+ SET_SIZESTATDESC(784, "responses sent 784-799 bytes", "784-799", out);
+ SET_SIZESTATDESC(800, "responses sent 800-815 bytes", "800-815", out);
+ SET_SIZESTATDESC(816, "responses sent 816-831 bytes", "816-831", out);
+ SET_SIZESTATDESC(832, "responses sent 832-847 bytes", "832-847", out);
+ SET_SIZESTATDESC(848, "responses sent 848-863 bytes", "848-863", out);
+ SET_SIZESTATDESC(864, "responses sent 864-879 bytes", "864-879", out);
+ SET_SIZESTATDESC(880, "responses sent 880-895 bytes", "880-895", out);
+ SET_SIZESTATDESC(896, "responses sent 896-911 bytes", "896-911", out);
+ SET_SIZESTATDESC(912, "responses sent 912-927 bytes", "912-927", out);
+ SET_SIZESTATDESC(928, "responses sent 928-943 bytes", "928-943", out);
+ SET_SIZESTATDESC(944, "responses sent 944-959 bytes", "944-959", out);
+ SET_SIZESTATDESC(960, "responses sent 960-975 bytes", "960-975", out);
+ SET_SIZESTATDESC(976, "responses sent 976-991 bytes", "976-991", out);
+ SET_SIZESTATDESC(992, "responses sent 992-1007 bytes", "992-1007", out);
+ SET_SIZESTATDESC(1008, "responses sent 1008-1023 bytes", "1008-1023",
+ out);
+ SET_SIZESTATDESC(1024, "responses sent 1024-1039 bytes", "1024-1039",
+ out);
+ SET_SIZESTATDESC(1040, "responses sent 1040-1055 bytes", "1040-1055",
+ out);
+ SET_SIZESTATDESC(1056, "responses sent 1056-1071 bytes", "1056-1071",
+ out);
+ SET_SIZESTATDESC(1072, "responses sent 1072-1087 bytes", "1072-1087",
+ out);
+ SET_SIZESTATDESC(1088, "responses sent 1088-1103 bytes", "1088-1103",
+ out);
+ SET_SIZESTATDESC(1104, "responses sent 1104-1119 bytes", "1104-1119",
+ out);
+ SET_SIZESTATDESC(1120, "responses sent 1120-1135 bytes", "1120-1135",
+ out);
+ SET_SIZESTATDESC(1136, "responses sent 1136-1151 bytes", "1136-1151",
+ out);
+ SET_SIZESTATDESC(1152, "responses sent 1152-1167 bytes", "1152-1167",
+ out);
+ SET_SIZESTATDESC(1168, "responses sent 1168-1183 bytes", "1168-1183",
+ out);
+ SET_SIZESTATDESC(1184, "responses sent 1184-1199 bytes", "1184-1199",
+ out);
+ SET_SIZESTATDESC(1200, "responses sent 1200-1215 bytes", "1200-1215",
+ out);
+ SET_SIZESTATDESC(1216, "responses sent 1216-1231 bytes", "1216-1231",
+ out);
+ SET_SIZESTATDESC(1232, "responses sent 1232-1247 bytes", "1232-1247",
+ out);
+ SET_SIZESTATDESC(1248, "responses sent 1248-1263 bytes", "1248-1263",
+ out);
+ SET_SIZESTATDESC(1264, "responses sent 1264-1279 bytes", "1264-1279",
+ out);
+ SET_SIZESTATDESC(1280, "responses sent 1280-1295 bytes", "1280-1295",
+ out);
+ SET_SIZESTATDESC(1296, "responses sent 1296-1311 bytes", "1296-1311",
+ out);
+ SET_SIZESTATDESC(1312, "responses sent 1312-1327 bytes", "1312-1327",
+ out);
+ SET_SIZESTATDESC(1328, "responses sent 1328-1343 bytes", "1328-1343",
+ out);
+ SET_SIZESTATDESC(1344, "responses sent 1344-1359 bytes", "1344-1359",
+ out);
+ SET_SIZESTATDESC(1360, "responses sent 1360-1375 bytes", "1360-1375",
+ out);
+ SET_SIZESTATDESC(1376, "responses sent 1376-1391 bytes", "1376-1391",
+ out);
+ SET_SIZESTATDESC(1392, "responses sent 1392-1407 bytes", "1392-1407",
+ out);
+ SET_SIZESTATDESC(1408, "responses sent 1408-1423 bytes", "1408-1423",
+ out);
+ SET_SIZESTATDESC(1424, "responses sent 1424-1439 bytes", "1424-1439",
+ out);
+ SET_SIZESTATDESC(1440, "responses sent 1440-1455 bytes", "1440-1455",
+ out);
+ SET_SIZESTATDESC(1456, "responses sent 1456-1471 bytes", "1456-1471",
+ out);
+ SET_SIZESTATDESC(1472, "responses sent 1472-1487 bytes", "1472-1487",
+ out);
+ SET_SIZESTATDESC(1488, "responses sent 1488-1503 bytes", "1488-1503",
+ out);
+ SET_SIZESTATDESC(1504, "responses sent 1504-1519 bytes", "1504-1519",
+ out);
+ SET_SIZESTATDESC(1520, "responses sent 1520-1535 bytes", "1520-1535",
+ out);
+ SET_SIZESTATDESC(1536, "responses sent 1536-1551 bytes", "1536-1551",
+ out);
+ SET_SIZESTATDESC(1552, "responses sent 1552-1567 bytes", "1552-1567",
+ out);
+ SET_SIZESTATDESC(1568, "responses sent 1568-1583 bytes", "1568-1583",
+ out);
+ SET_SIZESTATDESC(1584, "responses sent 1584-1599 bytes", "1584-1599",
+ out);
+ SET_SIZESTATDESC(1600, "responses sent 1600-1615 bytes", "1600-1615",
+ out);
+ SET_SIZESTATDESC(1616, "responses sent 1616-1631 bytes", "1616-1631",
+ out);
+ SET_SIZESTATDESC(1632, "responses sent 1632-1647 bytes", "1632-1647",
+ out);
+ SET_SIZESTATDESC(1648, "responses sent 1648-1663 bytes", "1648-1663",
+ out);
+ SET_SIZESTATDESC(1664, "responses sent 1664-1679 bytes", "1664-1679",
+ out);
+ SET_SIZESTATDESC(1680, "responses sent 1680-1695 bytes", "1680-1695",
+ out);
+ SET_SIZESTATDESC(1696, "responses sent 1696-1711 bytes", "1696-1711",
+ out);
+ SET_SIZESTATDESC(1712, "responses sent 1712-1727 bytes", "1712-1727",
+ out);
+ SET_SIZESTATDESC(1728, "responses sent 1728-1743 bytes", "1728-1743",
+ out);
+ SET_SIZESTATDESC(1744, "responses sent 1744-1759 bytes", "1744-1759",
+ out);
+ SET_SIZESTATDESC(1760, "responses sent 1760-1775 bytes", "1760-1775",
+ out);
+ SET_SIZESTATDESC(1776, "responses sent 1776-1791 bytes", "1776-1791",
+ out);
+ SET_SIZESTATDESC(1792, "responses sent 1792-1807 bytes", "1792-1807",
+ out);
+ SET_SIZESTATDESC(1808, "responses sent 1808-1823 bytes", "1808-1823",
+ out);
+ SET_SIZESTATDESC(1824, "responses sent 1824-1839 bytes", "1824-1839",
+ out);
+ SET_SIZESTATDESC(1840, "responses sent 1840-1855 bytes", "1840-1855",
+ out);
+ SET_SIZESTATDESC(1856, "responses sent 1856-1871 bytes", "1856-1871",
+ out);
+ SET_SIZESTATDESC(1872, "responses sent 1872-1887 bytes", "1872-1887",
+ out);
+ SET_SIZESTATDESC(1888, "responses sent 1888-1903 bytes", "1888-1903",
+ out);
+ SET_SIZESTATDESC(1904, "responses sent 1904-1919 bytes", "1904-1919",
+ out);
+ SET_SIZESTATDESC(1920, "responses sent 1920-1935 bytes", "1920-1935",
+ out);
+ SET_SIZESTATDESC(1936, "responses sent 1936-1951 bytes", "1936-1951",
+ out);
+ SET_SIZESTATDESC(1952, "responses sent 1952-1967 bytes", "1952-1967",
+ out);
+ SET_SIZESTATDESC(1968, "responses sent 1968-1983 bytes", "1968-1983",
+ out);
+ SET_SIZESTATDESC(1984, "responses sent 1984-1999 bytes", "1984-1999",
+ out);
+ SET_SIZESTATDESC(2000, "responses sent 2000-2015 bytes", "2000-2015",
+ out);
+ SET_SIZESTATDESC(2016, "responses sent 2016-2031 bytes", "2016-2031",
+ out);
+ SET_SIZESTATDESC(2032, "responses sent 2032-2047 bytes", "2032-2047",
+ out);
+ SET_SIZESTATDESC(2048, "responses sent 2048-2063 bytes", "2048-2063",
+ out);
+ SET_SIZESTATDESC(2064, "responses sent 2064-2079 bytes", "2064-2079",
+ out);
+ SET_SIZESTATDESC(2080, "responses sent 2080-2095 bytes", "2080-2095",
+ out);
+ SET_SIZESTATDESC(2096, "responses sent 2096-2111 bytes", "2096-2111",
+ out);
+ SET_SIZESTATDESC(2112, "responses sent 2112-2127 bytes", "2112-2127",
+ out);
+ SET_SIZESTATDESC(2128, "responses sent 2128-2143 bytes", "2128-2143",
+ out);
+ SET_SIZESTATDESC(2144, "responses sent 2144-2159 bytes", "2144-2159",
+ out);
+ SET_SIZESTATDESC(2160, "responses sent 2160-2175 bytes", "2160-2175",
+ out);
+ SET_SIZESTATDESC(2176, "responses sent 2176-2191 bytes", "2176-2191",
+ out);
+ SET_SIZESTATDESC(2192, "responses sent 2192-2207 bytes", "2192-2207",
+ out);
+ SET_SIZESTATDESC(2208, "responses sent 2208-2223 bytes", "2208-2223",
+ out);
+ SET_SIZESTATDESC(2224, "responses sent 2224-2239 bytes", "2224-2239",
+ out);
+ SET_SIZESTATDESC(2240, "responses sent 2240-2255 bytes", "2240-2255",
+ out);
+ SET_SIZESTATDESC(2256, "responses sent 2256-2271 bytes", "2256-2271",
+ out);
+ SET_SIZESTATDESC(2272, "responses sent 2272-2287 bytes", "2272-2287",
+ out);
+ SET_SIZESTATDESC(2288, "responses sent 2288-2303 bytes", "2288-2303",
+ out);
+ SET_SIZESTATDESC(2304, "responses sent 2304-2319 bytes", "2304-2319",
+ out);
+ SET_SIZESTATDESC(2320, "responses sent 2320-2335 bytes", "2320-2335",
+ out);
+ SET_SIZESTATDESC(2336, "responses sent 2336-2351 bytes", "2336-2351",
+ out);
+ SET_SIZESTATDESC(2352, "responses sent 2352-2367 bytes", "2352-2367",
+ out);
+ SET_SIZESTATDESC(2368, "responses sent 2368-2383 bytes", "2368-2383",
+ out);
+ SET_SIZESTATDESC(2384, "responses sent 2384-2399 bytes", "2384-2399",
+ out);
+ SET_SIZESTATDESC(2400, "responses sent 2400-2415 bytes", "2400-2415",
+ out);
+ SET_SIZESTATDESC(2416, "responses sent 2416-2431 bytes", "2416-2431",
+ out);
+ SET_SIZESTATDESC(2432, "responses sent 2432-2447 bytes", "2432-2447",
+ out);
+ SET_SIZESTATDESC(2448, "responses sent 2448-2463 bytes", "2448-2463",
+ out);
+ SET_SIZESTATDESC(2464, "responses sent 2464-2479 bytes", "2464-2479",
+ out);
+ SET_SIZESTATDESC(2480, "responses sent 2480-2495 bytes", "2480-2495",
+ out);
+ SET_SIZESTATDESC(2496, "responses sent 2496-2511 bytes", "2496-2511",
+ out);
+ SET_SIZESTATDESC(2512, "responses sent 2512-2527 bytes", "2512-2527",
+ out);
+ SET_SIZESTATDESC(2528, "responses sent 2528-2543 bytes", "2528-2543",
+ out);
+ SET_SIZESTATDESC(2544, "responses sent 2544-2559 bytes", "2544-2559",
+ out);
+ SET_SIZESTATDESC(2560, "responses sent 2560-2575 bytes", "2560-2575",
+ out);
+ SET_SIZESTATDESC(2576, "responses sent 2576-2591 bytes", "2576-2591",
+ out);
+ SET_SIZESTATDESC(2592, "responses sent 2592-2607 bytes", "2592-2607",
+ out);
+ SET_SIZESTATDESC(2608, "responses sent 2608-2623 bytes", "2608-2623",
+ out);
+ SET_SIZESTATDESC(2624, "responses sent 2624-2639 bytes", "2624-2639",
+ out);
+ SET_SIZESTATDESC(2640, "responses sent 2640-2655 bytes", "2640-2655",
+ out);
+ SET_SIZESTATDESC(2656, "responses sent 2656-2671 bytes", "2656-2671",
+ out);
+ SET_SIZESTATDESC(2672, "responses sent 2672-2687 bytes", "2672-2687",
+ out);
+ SET_SIZESTATDESC(2688, "responses sent 2688-2703 bytes", "2688-2703",
+ out);
+ SET_SIZESTATDESC(2704, "responses sent 2704-2719 bytes", "2704-2719",
+ out);
+ SET_SIZESTATDESC(2720, "responses sent 2720-2735 bytes", "2720-2735",
+ out);
+ SET_SIZESTATDESC(2736, "responses sent 2736-2751 bytes", "2736-2751",
+ out);
+ SET_SIZESTATDESC(2752, "responses sent 2752-2767 bytes", "2752-2767",
+ out);
+ SET_SIZESTATDESC(2768, "responses sent 2768-2783 bytes", "2768-2783",
+ out);
+ SET_SIZESTATDESC(2784, "responses sent 2784-2799 bytes", "2784-2799",
+ out);
+ SET_SIZESTATDESC(2800, "responses sent 2800-2815 bytes", "2800-2815",
+ out);
+ SET_SIZESTATDESC(2816, "responses sent 2816-2831 bytes", "2816-2831",
+ out);
+ SET_SIZESTATDESC(2832, "responses sent 2832-2847 bytes", "2832-2847",
+ out);
+ SET_SIZESTATDESC(2848, "responses sent 2848-2863 bytes", "2848-2863",
+ out);
+ SET_SIZESTATDESC(2864, "responses sent 2864-2879 bytes", "2864-2879",
+ out);
+ SET_SIZESTATDESC(2880, "responses sent 2880-2895 bytes", "2880-2895",
+ out);
+ SET_SIZESTATDESC(2896, "responses sent 2896-2911 bytes", "2896-2911",
+ out);
+ SET_SIZESTATDESC(2912, "responses sent 2912-2927 bytes", "2912-2927",
+ out);
+ SET_SIZESTATDESC(2928, "responses sent 2928-2943 bytes", "2928-2943",
+ out);
+ SET_SIZESTATDESC(2944, "responses sent 2944-2959 bytes", "2944-2959",
+ out);
+ SET_SIZESTATDESC(2960, "responses sent 2960-2975 bytes", "2960-2975",
+ out);
+ SET_SIZESTATDESC(2976, "responses sent 2976-2991 bytes", "2976-2991",
+ out);
+ SET_SIZESTATDESC(2992, "responses sent 2992-3007 bytes", "2992-3007",
+ out);
+ SET_SIZESTATDESC(3008, "responses sent 3008-3023 bytes", "3008-3023",
+ out);
+ SET_SIZESTATDESC(3024, "responses sent 3024-3039 bytes", "3024-3039",
+ out);
+ SET_SIZESTATDESC(3040, "responses sent 3040-3055 bytes", "3040-3055",
+ out);
+ SET_SIZESTATDESC(3056, "responses sent 3056-3071 bytes", "3056-3071",
+ out);
+ SET_SIZESTATDESC(3072, "responses sent 3072-3087 bytes", "3072-3087",
+ out);
+ SET_SIZESTATDESC(3088, "responses sent 3088-3103 bytes", "3088-3103",
+ out);
+ SET_SIZESTATDESC(3104, "responses sent 3104-3119 bytes", "3104-3119",
+ out);
+ SET_SIZESTATDESC(3120, "responses sent 3120-3135 bytes", "3120-3135",
+ out);
+ SET_SIZESTATDESC(3136, "responses sent 3136-3151 bytes", "3136-3151",
+ out);
+ SET_SIZESTATDESC(3152, "responses sent 3152-3167 bytes", "3152-3167",
+ out);
+ SET_SIZESTATDESC(3168, "responses sent 3168-3183 bytes", "3168-3183",
+ out);
+ SET_SIZESTATDESC(3184, "responses sent 3184-3199 bytes", "3184-3199",
+ out);
+ SET_SIZESTATDESC(3200, "responses sent 3200-3215 bytes", "3200-3215",
+ out);
+ SET_SIZESTATDESC(3216, "responses sent 3216-3231 bytes", "3216-3231",
+ out);
+ SET_SIZESTATDESC(3232, "responses sent 3232-3247 bytes", "3232-3247",
+ out);
+ SET_SIZESTATDESC(3248, "responses sent 3248-3263 bytes", "3248-3263",
+ out);
+ SET_SIZESTATDESC(3264, "responses sent 3264-3279 bytes", "3264-3279",
+ out);
+ SET_SIZESTATDESC(3280, "responses sent 3280-3295 bytes", "3280-3295",
+ out);
+ SET_SIZESTATDESC(3296, "responses sent 3296-3311 bytes", "3296-3311",
+ out);
+ SET_SIZESTATDESC(3312, "responses sent 3312-3327 bytes", "3312-3327",
+ out);
+ SET_SIZESTATDESC(3328, "responses sent 3328-3343 bytes", "3328-3343",
+ out);
+ SET_SIZESTATDESC(3344, "responses sent 3344-3359 bytes", "3344-3359",
+ out);
+ SET_SIZESTATDESC(3360, "responses sent 3360-3375 bytes", "3360-3375",
+ out);
+ SET_SIZESTATDESC(3376, "responses sent 3376-3391 bytes", "3376-3391",
+ out);
+ SET_SIZESTATDESC(3392, "responses sent 3392-3407 bytes", "3392-3407",
+ out);
+ SET_SIZESTATDESC(3408, "responses sent 3408-3423 bytes", "3408-3423",
+ out);
+ SET_SIZESTATDESC(3424, "responses sent 3424-3439 bytes", "3424-3439",
+ out);
+ SET_SIZESTATDESC(3440, "responses sent 3440-3455 bytes", "3440-3455",
+ out);
+ SET_SIZESTATDESC(3456, "responses sent 3456-3471 bytes", "3456-3471",
+ out);
+ SET_SIZESTATDESC(3472, "responses sent 3472-3487 bytes", "3472-3487",
+ out);
+ SET_SIZESTATDESC(3488, "responses sent 3488-3503 bytes", "3488-3503",
+ out);
+ SET_SIZESTATDESC(3504, "responses sent 3504-3519 bytes", "3504-3519",
+ out);
+ SET_SIZESTATDESC(3520, "responses sent 3520-3535 bytes", "3520-3535",
+ out);
+ SET_SIZESTATDESC(3536, "responses sent 3536-3551 bytes", "3536-3551",
+ out);
+ SET_SIZESTATDESC(3552, "responses sent 3552-3567 bytes", "3552-3567",
+ out);
+ SET_SIZESTATDESC(3568, "responses sent 3568-3583 bytes", "3568-3583",
+ out);
+ SET_SIZESTATDESC(3584, "responses sent 3584-3599 bytes", "3584-3599",
+ out);
+ SET_SIZESTATDESC(3600, "responses sent 3600-3615 bytes", "3600-3615",
+ out);
+ SET_SIZESTATDESC(3616, "responses sent 3616-3631 bytes", "3616-3631",
+ out);
+ SET_SIZESTATDESC(3632, "responses sent 3632-3647 bytes", "3632-3647",
+ out);
+ SET_SIZESTATDESC(3648, "responses sent 3648-3663 bytes", "3648-3663",
+ out);
+ SET_SIZESTATDESC(3664, "responses sent 3664-3679 bytes", "3664-3679",
+ out);
+ SET_SIZESTATDESC(3680, "responses sent 3680-3695 bytes", "3680-3695",
+ out);
+ SET_SIZESTATDESC(3696, "responses sent 3696-3711 bytes", "3696-3711",
+ out);
+ SET_SIZESTATDESC(3712, "responses sent 3712-3727 bytes", "3712-3727",
+ out);
+ SET_SIZESTATDESC(3728, "responses sent 3728-3743 bytes", "3728-3743",
+ out);
+ SET_SIZESTATDESC(3744, "responses sent 3744-3759 bytes", "3744-3759",
+ out);
+ SET_SIZESTATDESC(3760, "responses sent 3760-3775 bytes", "3760-3775",
+ out);
+ SET_SIZESTATDESC(3776, "responses sent 3776-3791 bytes", "3776-3791",
+ out);
+ SET_SIZESTATDESC(3792, "responses sent 3792-3807 bytes", "3792-3807",
+ out);
+ SET_SIZESTATDESC(3808, "responses sent 3808-3823 bytes", "3808-3823",
+ out);
+ SET_SIZESTATDESC(3824, "responses sent 3824-3839 bytes", "3824-3839",
+ out);
+ SET_SIZESTATDESC(3840, "responses sent 3840-3855 bytes", "3840-3855",
+ out);
+ SET_SIZESTATDESC(3856, "responses sent 3856-3871 bytes", "3856-3871",
+ out);
+ SET_SIZESTATDESC(3872, "responses sent 3872-3887 bytes", "3872-3887",
+ out);
+ SET_SIZESTATDESC(3888, "responses sent 3888-3903 bytes", "3888-3903",
+ out);
+ SET_SIZESTATDESC(3904, "responses sent 3904-3919 bytes", "3904-3919",
+ out);
+ SET_SIZESTATDESC(3920, "responses sent 3920-3935 bytes", "3920-3935",
+ out);
+ SET_SIZESTATDESC(3936, "responses sent 3936-3951 bytes", "3936-3951",
+ out);
+ SET_SIZESTATDESC(3952, "responses sent 3952-3967 bytes", "3952-3967",
+ out);
+ SET_SIZESTATDESC(3968, "responses sent 3968-3983 bytes", "3968-3983",
+ out);
+ SET_SIZESTATDESC(3984, "responses sent 3984-3999 bytes", "3984-3999",
+ out);
+ SET_SIZESTATDESC(4000, "responses sent 4000-4015 bytes", "4000-4015",
+ out);
+ SET_SIZESTATDESC(4016, "responses sent 4016-4031 bytes", "4016-4031",
+ out);
+ SET_SIZESTATDESC(4032, "responses sent 4032-4047 bytes", "4032-4047",
+ out);
+ SET_SIZESTATDESC(4048, "responses sent 4048-4063 bytes", "4048-4063",
+ out);
+ SET_SIZESTATDESC(4064, "responses sent 4064-4079 bytes", "4064-4079",
+ out);
+ SET_SIZESTATDESC(4080, "responses sent 4080-4095 bytes", "4080-4095",
+ out);
+ SET_SIZESTATDESC(4096, "responses sent 4096+ bytes", "4096+", out);
+ INSIST(i == dns_sizecounter_out_max);
+
+ /* Sanity check */
+ for (i = 0; i < ns_statscounter_max; i++) {
+ INSIST(nsstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ INSIST(resstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_adbstats_max; i++) {
+ INSIST(adbstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ INSIST(zonestats_desc[i] != NULL);
+ }
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ INSIST(sockstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ INSIST(dnssecstats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ INSIST(udpinsizestats_desc[i] != NULL);
+ INSIST(tcpinsizestats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ INSIST(udpoutsizestats_desc[i] != NULL);
+ INSIST(tcpoutsizestats_desc[i] != NULL);
+ }
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < ns_statscounter_max; i++) {
+ INSIST(nsstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_resstatscounter_max; i++) {
+ INSIST(resstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_adbstats_max; i++) {
+ INSIST(adbstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_zonestatscounter_max; i++) {
+ INSIST(zonestats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < isc_sockstatscounter_max; i++) {
+ INSIST(sockstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_dnssecstats_max; i++) {
+ INSIST(dnssecstats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ INSIST(udpinsizestats_xmldesc[i] != NULL);
+ INSIST(tcpinsizestats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ INSIST(udpoutsizestats_xmldesc[i] != NULL);
+ INSIST(tcpoutsizestats_xmldesc[i] != NULL);
+ }
+#endif /* if defined(EXTENDED_STATS) */
+}
+
+/*%
+ * Dump callback functions.
+ */
+static void
+generalstat_dump(isc_statscounter_t counter, uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+
+ REQUIRE(counter < dumparg->ncounters);
+ dumparg->countervalues[counter] = val;
+}
+
+static isc_result_t
+dump_counters(isc_stats_t *stats, isc_statsformat_t type, void *arg,
+ const char *category, const char **desc, int ncounters,
+ int *indices, uint64_t *values, int options) {
+ int i, idx;
+ uint64_t value;
+ stats_dumparg_t dumparg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ void *writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *job, *cat, *counter;
+#endif /* ifdef HAVE_JSON_C */
+
+#if !defined(EXTENDED_STATS)
+ UNUSED(category);
+#endif /* if !defined(EXTENDED_STATS) */
+
+ dumparg.type = type;
+ dumparg.ncounters = ncounters;
+ dumparg.counterindices = indices;
+ dumparg.countervalues = values;
+
+ memset(values, 0, sizeof(values[0]) * ncounters);
+ isc_stats_dump(stats, generalstat_dump, &dumparg, options);
+
+#ifdef HAVE_JSON_C
+ cat = job = (json_object *)arg;
+ if (ncounters > 0 && type == isc_statsformat_json) {
+ if (category != NULL) {
+ cat = json_object_new_object();
+ if (cat == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ json_object_object_add(job, category, cat);
+ }
+ }
+#endif /* ifdef HAVE_JSON_C */
+
+ for (i = 0; i < ncounters; i++) {
+ idx = indices[i];
+ value = values[idx];
+
+ if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0) {
+ continue;
+ }
+
+ switch (dumparg.type) {
+ case isc_statsformat_file:
+ fp = arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", value, desc[idx]);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = arg;
+
+ if (category != NULL) {
+ /* <NameOfCategory> */
+ TRY0(xmlTextWriterStartElement(
+ writer, ISC_XMLCHAR category));
+
+ /* <name> inside category */
+ TRY0(xmlTextWriterStartElement(
+ writer, ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteString(
+ writer, ISC_XMLCHAR desc[idx]));
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </name> */
+
+ /* <counter> */
+ TRY0(xmlTextWriterStartElement(
+ writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64, value));
+
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </counter> */
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </NameOfCategory> */
+ } else {
+ TRY0(xmlTextWriterStartElement(
+ writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(
+ writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR desc[idx]));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64, value));
+ TRY0(xmlTextWriterEndElement(writer));
+ /* counter */
+ }
+
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ counter = json_object_new_int64(value);
+ if (counter == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ json_object_object_add(cat, desc[idx], counter);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ }
+ return (ISC_R_SUCCESS);
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at dump_counters()");
+ return (ISC_R_FAILURE);
+#endif /* ifdef HAVE_LIBXML2 */
+}
+
+static void
+rdtypestat_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
+ char typebuf[64];
+ const char *typestr;
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ void *writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *zoneobj, *obj;
+#endif /* ifdef HAVE_JSON_C */
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) &
+ DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) == 0)
+ {
+ dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
+ sizeof(typebuf));
+ typestr = typebuf;
+ } else {
+ typestr = "Others";
+ }
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, typestr);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR typestr));
+
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* type */
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ zoneobj = (json_object *)dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL) {
+ return;
+ }
+ json_object_object_add(zoneobj, typestr, obj);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ return;
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rdtypestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif /* ifdef HAVE_LIBXML2 */
+}
+
+static bool
+rdatastatstype_attr(dns_rdatastatstype_t type, unsigned int attr) {
+ return ((DNS_RDATASTATSTYPE_ATTR(type) & attr) != 0);
+}
+
+static void
+rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+ char typebuf[64];
+ const char *typestr;
+ bool nxrrset = false;
+ bool stale = false;
+ bool ancient = false;
+#ifdef HAVE_LIBXML2
+ void *writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *zoneobj, *obj;
+ char buf[1024];
+#endif /* ifdef HAVE_JSON_C */
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) &
+ DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0)
+ {
+ typestr = "NXDOMAIN";
+ } else if ((DNS_RDATASTATSTYPE_ATTR(type) &
+ DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0)
+ {
+ typestr = "Others";
+ } else {
+ dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
+ sizeof(typebuf));
+ typestr = typebuf;
+ }
+
+ nxrrset = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_NXRRSET);
+ stale = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_STALE);
+ ancient = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_ANCIENT);
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s%s%s%s\n", val, ancient ? "~" : "",
+ stale ? "#" : "", nxrrset ? "!" : "", typestr);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%s%s%s%s", ancient ? "~" : "",
+ stale ? "#" : "", nxrrset ? "!" : "", typestr));
+ TRY0(xmlTextWriterEndElement(writer)); /* name */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* rrset */
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ zoneobj = (json_object *)dumparg->arg;
+ snprintf(buf, sizeof(buf), "%s%s%s%s", ancient ? "~" : "",
+ stale ? "#" : "", nxrrset ? "!" : "", typestr);
+ obj = json_object_new_int64(val);
+ if (obj == NULL) {
+ return;
+ }
+ json_object_object_add(zoneobj, buf, obj);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ return;
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rdatasetstats_dump()");
+ dumparg->result = ISC_R_FAILURE;
+#endif /* ifdef HAVE_LIBXML2 */
+}
+
+static void
+opcodestat_dump(dns_opcode_t code, uint64_t val, void *arg) {
+ FILE *fp;
+ isc_buffer_t b;
+ char codebuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ void *writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *zoneobj, *obj;
+#endif /* ifdef HAVE_JSON_C */
+
+ isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
+ dns_opcode_totext(code, &b);
+ codebuf[isc_buffer_usedlength(&b)] = '\0';
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR codebuf));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ zoneobj = (json_object *)dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL) {
+ return;
+ }
+ json_object_object_add(zoneobj, codebuf, obj);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ return;
+
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at opcodestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif /* ifdef HAVE_LIBXML2 */
+}
+
+static void
+rcodestat_dump(dns_rcode_t code, uint64_t val, void *arg) {
+ FILE *fp;
+ isc_buffer_t b;
+ char codebuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ void *writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *zoneobj, *obj;
+#endif /* ifdef HAVE_JSON_C */
+
+ isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
+ dns_rcode_totext(code, &b);
+ codebuf[isc_buffer_usedlength(&b)] = '\0';
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR codebuf));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ zoneobj = (json_object *)dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL) {
+ return;
+ }
+ json_object_object_add(zoneobj, codebuf, obj);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ return;
+
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rcodestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif /* ifdef HAVE_LIBXML2 */
+}
+
+#if defined(EXTENDED_STATS)
+static void
+dnssecsignstat_dump(dns_keytag_t tag, uint64_t val, void *arg) {
+ FILE *fp;
+ char tagbuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ json_object *zoneobj, *obj;
+#endif /* ifdef HAVE_JSON_C */
+
+ snprintf(tagbuf, sizeof(tagbuf), "%u", tag);
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, tagbuf);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR tagbuf));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+#endif /* ifdef HAVE_LIBXML2 */
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON_C
+ zoneobj = (json_object *)dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL) {
+ return;
+ }
+ json_object_object_add(zoneobj, tagbuf, obj);
+#endif /* ifdef HAVE_JSON_C */
+ break;
+ }
+ return;
+#ifdef HAVE_LIBXML2
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at dnssecsignstat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif /* ifdef HAVE_LIBXML2 */
+}
+#endif /* defined(EXTENDED_STATS) */
+
+#ifdef HAVE_LIBXML2
+/*
+ * Which statistics to include when rendering to XML
+ */
+#define STATS_XML_STATUS 0x00 /* display only common statistics */
+#define STATS_XML_SERVER 0x01
+#define STATS_XML_ZONES 0x02
+#define STATS_XML_TASKS 0x04
+#define STATS_XML_NET 0x08
+#define STATS_XML_MEM 0x10
+#define STATS_XML_TRAFFIC 0x20
+#define STATS_XML_ALL 0xff
+
+static isc_result_t
+zone_xmlrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ dns_rdataclass_t rdclass;
+ uint32_t serial;
+ xmlTextWriterPtr writer = arg;
+ dns_zonestat_level_t statlevel;
+ int xmlrc;
+ stats_dumparg_t dumparg;
+ const char *ztype;
+ isc_time_t timestamp;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none) {
+ return (ISC_R_SUCCESS);
+ }
+
+ dumparg.type = isc_statsformat_xml;
+ dumparg.arg = writer;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone"));
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR buf));
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "rdataclass",
+ ISC_XMLCHAR buf));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
+ ztype = user_zonetype(zone);
+ if (ztype != NULL) {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "unknown"));
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* type */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
+ if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) {
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
+ } else {
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* serial */
+
+ /*
+ * Export zone timers to the statistics channel in XML format. For
+ * primary zones, only include the loaded time. For secondary zones,
+ * also include the expire and refresh times.
+ */
+ CHECK(dns_zone_getloadtime(zone, &timestamp));
+
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "loaded"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
+ TRY0(xmlTextWriterEndElement(writer));
+
+ if (dns_zone_gettype(zone) == dns_zone_secondary) {
+ CHECK(dns_zone_getexpiretime(zone, &timestamp));
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "expires"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
+ TRY0(xmlTextWriterEndElement(writer));
+
+ CHECK(dns_zone_getrefreshtime(zone, &timestamp));
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refresh"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf));
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ if (statlevel == dns_zonestat_full) {
+ isc_stats_t *zonestats;
+ isc_stats_t *gluecachestats;
+ dns_stats_t *rcvquerystats;
+ dns_stats_t *dnssecsignstats;
+ uint64_t nsstat_values[ns_statscounter_max];
+ uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
+
+ zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "type",
+ ISC_XMLCHAR "rcode"));
+
+ CHECK(dump_counters(zonestats, isc_statsformat_xml,
+ writer, NULL, nsstats_xmldesc,
+ ns_statscounter_max, nsstats_index,
+ nsstat_values,
+ ISC_STATSDUMP_VERBOSE));
+ /* counters type="rcode"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ gluecachestats = dns_zone_getgluecachestats(zone);
+ if (gluecachestats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(
+ writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "gluecache"));
+
+ CHECK(dump_counters(
+ gluecachestats, isc_statsformat_xml, writer,
+ NULL, gluecachestats_xmldesc,
+ dns_gluecachestatscounter_max,
+ gluecachestats_index, gluecachestats_values,
+ ISC_STATSDUMP_VERBOSE));
+ /* counters type="rcode"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ rcvquerystats = dns_zone_getrcvquerystats(zone);
+ if (rcvquerystats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "type",
+ ISC_XMLCHAR "qtype"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ CHECK(dumparg.result);
+
+ /* counters type="qtype"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ dnssecsignstats = dns_zone_getdnssecsignstats(zone);
+ if (dnssecsignstats != NULL) {
+ /* counters type="dnssec-sign"*/
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(
+ writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "dnssec-sign"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_dnssecsignstats_dump(
+ dnssecsignstats, dns_dnssecsignstats_sign,
+ dnssecsignstat_dump, &dumparg, 0);
+ CHECK(dumparg.result);
+
+ /* counters type="dnssec-sign"*/
+ TRY0(xmlTextWriterEndElement(writer));
+
+ /* counters type="dnssec-refresh"*/
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(
+ writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "dnssec-refresh"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_dnssecsignstats_dump(
+ dnssecsignstats, dns_dnssecsignstats_refresh,
+ dnssecsignstat_dump, &dumparg, 0);
+ CHECK(dumparg.result);
+
+ /* counters type="dnssec-refresh"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+ }
+
+ TRY0(xmlTextWriterEndElement(writer)); /* zone */
+
+ return (ISC_R_SUCCESS);
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "Failed at zone_xmlrender()");
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+generatexml(named_server_t *server, uint32_t flags, int *buflen,
+ xmlChar **buf) {
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ isc_time_t now;
+ xmlTextWriterPtr writer = NULL;
+ xmlDocPtr doc = NULL;
+ int xmlrc;
+ dns_view_t *view;
+ stats_dumparg_t dumparg;
+ dns_stats_t *cacherrstats;
+ uint64_t nsstat_values[ns_statscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+ uint64_t udpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t udpoutsizestat_values[dns_sizecounter_out_max];
+ uint64_t tcpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t tcpoutsizestat_values[dns_sizecounter_out_max];
+#ifdef HAVE_DNSTAP
+ uint64_t dnstapstat_values[dns_dnstapcounter_max];
+#endif /* ifdef HAVE_DNSTAP */
+ isc_result_t result;
+
+ isc_time_now(&now);
+ isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof boottime);
+ isc_time_formatISO8601ms(&named_g_configtime, configtime,
+ sizeof configtime);
+ isc_time_formatISO8601ms(&now, nowstr, sizeof nowstr);
+
+ writer = xmlNewTextWriterDoc(&doc, 0);
+ if (writer == NULL) {
+ goto cleanup;
+ }
+ 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 "statistics"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR STATS_XML_VERSION));
+
+ /* Set common fields for statistics dump */
+ dumparg.type = isc_statsformat_xml;
+ dumparg.arg = writer;
+
+ /* Render server information */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime));
+ TRY0(xmlTextWriterEndElement(writer)); /* boot-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "config-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR configtime));
+ TRY0(xmlTextWriterEndElement(writer)); /* config-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr));
+ TRY0(xmlTextWriterEndElement(writer)); /* current-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "version"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR PACKAGE_VERSION));
+ TRY0(xmlTextWriterEndElement(writer)); /* version */
+
+ if ((flags & STATS_XML_SERVER) != 0) {
+ dumparg.result = ISC_R_SUCCESS;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "opcode"));
+
+ dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ CHECK(dumparg.result);
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "rcode"));
+
+ dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ CHECK(dumparg.result);
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "qtype"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(server->sctx->rcvquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ CHECK(dumparg.result);
+
+ TRY0(xmlTextWriterEndElement(writer)); /* counters */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "nsstat"));
+
+ CHECK(dump_counters(ns_stats_get(server->sctx->nsstats),
+ isc_statsformat_xml, writer, NULL,
+ nsstats_xmldesc, ns_statscounter_max,
+ nsstats_index, nsstat_values,
+ ISC_STATSDUMP_VERBOSE));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /nsstat */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "zonestat"));
+
+ CHECK(dump_counters(server->zonestats, isc_statsformat_xml,
+ writer, NULL, zonestats_xmldesc,
+ dns_zonestatscounter_max, zonestats_index,
+ zonestat_values, ISC_STATSDUMP_VERBOSE));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /zonestat */
+
+ /*
+ * Most of the common resolver statistics entries are 0, so
+ * we don't use the verbose dump here.
+ */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resstat"));
+ CHECK(dump_counters(server->resolverstats, isc_statsformat_xml,
+ writer, NULL, resstats_xmldesc,
+ dns_resstatscounter_max, resstats_index,
+ resstat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* resstat */
+
+#ifdef HAVE_DNSTAP
+ if (server->dtenv != NULL) {
+ isc_stats_t *dnstapstats = NULL;
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "type",
+ ISC_XMLCHAR "dnstap"));
+ dns_dt_getstats(named_g_server->dtenv, &dnstapstats);
+ result = dump_counters(
+ dnstapstats, isc_statsformat_xml, writer, NULL,
+ dnstapstats_xmldesc, dns_dnstapcounter_max,
+ dnstapstats_index, dnstapstat_values, 0);
+ isc_stats_detach(&dnstapstats);
+ CHECK(result);
+
+ TRY0(xmlTextWriterEndElement(writer)); /* dnstap */
+ }
+#endif /* ifdef HAVE_DNSTAP */
+ }
+
+ if ((flags & STATS_XML_NET) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "sockstat"));
+
+ CHECK(dump_counters(server->sockstats, isc_statsformat_xml,
+ writer, NULL, sockstats_xmldesc,
+ isc_sockstatscounter_max, sockstats_index,
+ sockstat_values, ISC_STATSDUMP_VERBOSE));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /sockstat */
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* /server */
+
+ if ((flags & STATS_XML_TRAFFIC) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "traffic"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv4"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ CHECK(dump_counters(
+ server->sctx->udpinstats4, isc_statsformat_xml, writer,
+ NULL, udpinsizestats_xmldesc, dns_sizecounter_in_max,
+ udpinsizestats_index, udpinsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ CHECK(dump_counters(
+ server->sctx->udpoutstats4, isc_statsformat_xml, writer,
+ NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max,
+ udpoutsizestats_index, udpoutsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ CHECK(dump_counters(
+ server->sctx->tcpinstats4, isc_statsformat_xml, writer,
+ NULL, tcpinsizestats_xmldesc, dns_sizecounter_in_max,
+ tcpinsizestats_index, tcpinsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ CHECK(dump_counters(
+ server->sctx->tcpoutstats4, isc_statsformat_xml, writer,
+ NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max,
+ tcpoutsizestats_index, tcpoutsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </ipv4> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv6"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ CHECK(dump_counters(
+ server->sctx->udpinstats6, isc_statsformat_xml, writer,
+ NULL, udpinsizestats_xmldesc, dns_sizecounter_in_max,
+ udpinsizestats_index, udpinsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ CHECK(dump_counters(
+ server->sctx->udpoutstats6, isc_statsformat_xml, writer,
+ NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max,
+ udpoutsizestats_index, udpoutsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ CHECK(dump_counters(
+ server->sctx->tcpinstats6, isc_statsformat_xml, writer,
+ NULL, tcpinsizestats_xmldesc, dns_sizecounter_in_max,
+ tcpinsizestats_index, tcpinsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ CHECK(dump_counters(
+ server->sctx->tcpoutstats6, isc_statsformat_xml, writer,
+ NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max,
+ tcpoutsizestats_index, tcpoutsizestat_values, 0));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </ipv6> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </traffic> */
+ }
+
+ /*
+ * Render views. 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 &&
+ ((flags & (STATS_XML_SERVER | STATS_XML_ZONES)) != 0))
+ {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR view->name));
+
+ if ((flags & STATS_XML_ZONES) != 0) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "zones"));
+ CHECK(dns_zt_apply(view->zonetable, isc_rwlocktype_read,
+ true, NULL, zone_xmlrender, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /zones */
+ }
+
+ if ((flags & STATS_XML_SERVER) == 0) {
+ TRY0(xmlTextWriterEndElement(writer)); /* /view */
+ view = ISC_LIST_NEXT(view, link);
+ continue;
+ }
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resqtype"));
+
+ if (view->resquerystats != NULL) {
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(view->resquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ CHECK(dumparg.result);
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ /* <resstats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resstats"));
+ if (view->resstats != NULL) {
+ CHECK(dump_counters(view->resstats, isc_statsformat_xml,
+ writer, NULL, resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index, resstat_values,
+ ISC_STATSDUMP_VERBOSE));
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* </resstats> */
+
+ cacherrstats = dns_db_getrrsetstats(view->cachedb);
+ if (cacherrstats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "cache"));
+ TRY0(xmlTextWriterWriteAttribute(
+ writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR dns_cache_getname(view->cache)));
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
+ &dumparg, 0);
+ CHECK(dumparg.result);
+ TRY0(xmlTextWriterEndElement(writer)); /* cache */
+ }
+
+ /* <adbstats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "adbstat"));
+ if (view->adbstats != NULL) {
+ CHECK(dump_counters(view->adbstats, isc_statsformat_xml,
+ writer, NULL, adbstats_xmldesc,
+ dns_adbstats_max, adbstats_index,
+ adbstat_values,
+ ISC_STATSDUMP_VERBOSE));
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* </adbstats> */
+
+ /* <cachestats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "cachestats"));
+ TRY0(dns_cache_renderxml(view->cache, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* </cachestats> */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* view */
+
+ view = ISC_LIST_NEXT(view, link);
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* /views */
+
+ if ((flags & STATS_XML_TASKS) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr"));
+ TRY0(isc_taskmgr_renderxml(named_g_taskmgr, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /taskmgr */
+ }
+
+ if ((flags & STATS_XML_MEM) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
+ TRY0(isc_mem_renderxml(writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /memory */
+ }
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /statistics */
+ TRY0(xmlTextWriterEndDocument(writer));
+
+ xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 0);
+ if (*buf == NULL) {
+ goto cleanup;
+ }
+
+ xmlFreeTextWriter(writer);
+ xmlFreeDoc(doc);
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed generating XML response");
+ if (writer != NULL) {
+ xmlFreeTextWriter(writer);
+ }
+ if (doc != NULL) {
+ xmlFreeDoc(doc);
+ }
+ return (ISC_R_FAILURE);
+}
+
+static void
+wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
+ UNUSED(arg);
+
+ xmlFree(isc_buffer_base(buffer));
+}
+
+static isc_result_t
+render_xml(uint32_t flags, 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 = NULL;
+ int msglen;
+ named_server_t *server = arg;
+ isc_result_t result;
+
+ result = generatexml(server, flags, &msglen, &msg);
+
+ if (result == ISC_R_SUCCESS) {
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xml";
+ isc_buffer_reinit(b, msg, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_xmlfree;
+ *freecb_args = NULL;
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rendering XML()");
+ }
+
+ return (result);
+}
+
+static isc_result_t
+render_xml_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_ALL, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_STATUS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_SERVER, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_ZONES, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_NET, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_TASKS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_MEM, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_TRAFFIC, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+/*
+ * Which statistics to include when rendering to JSON
+ */
+#define STATS_JSON_STATUS 0x00 /* display only common statistics */
+#define STATS_JSON_SERVER 0x01
+#define STATS_JSON_ZONES 0x02
+#define STATS_JSON_TASKS 0x04
+#define STATS_JSON_NET 0x08
+#define STATS_JSON_MEM 0x10
+#define STATS_JSON_TRAFFIC 0x20
+#define STATS_JSON_ALL 0xff
+
+#define CHECKMEM(m) \
+ do { \
+ if (m == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+static void
+wrap_jsonfree(isc_buffer_t *buffer, void *arg) {
+ json_object_put(isc_buffer_base(buffer));
+ if (arg != NULL) {
+ json_object_put((json_object *)arg);
+ }
+}
+
+static json_object *
+addzone(char *name, char *classname, const char *ztype, uint32_t serial,
+ bool add_serial) {
+ json_object *node = json_object_new_object();
+
+ if (node == NULL) {
+ return (NULL);
+ }
+
+ json_object_object_add(node, "name", json_object_new_string(name));
+ json_object_object_add(node, "class",
+ json_object_new_string(classname));
+ if (add_serial) {
+ json_object_object_add(node, "serial",
+ json_object_new_int64(serial));
+ }
+ if (ztype != NULL) {
+ json_object_object_add(node, "type",
+ json_object_new_string(ztype));
+ }
+ return (node);
+}
+
+static isc_result_t
+zone_jsonrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result = ISC_R_SUCCESS;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ char classbuf[64]; /* sufficiently large for class */
+ char *zone_name_only = NULL;
+ char *class_only = NULL;
+ dns_rdataclass_t rdclass;
+ uint32_t serial;
+ json_object *zonearray = (json_object *)arg;
+ json_object *zoneobj = NULL;
+ dns_zonestat_level_t statlevel;
+ isc_time_t timestamp;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none) {
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ zone_name_only = buf;
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ class_only = classbuf;
+
+ if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) {
+ zoneobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), 0, false);
+ } else {
+ zoneobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), serial, true);
+ }
+
+ if (zoneobj == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ /*
+ * Export zone timers to the statistics channel in JSON format.
+ * For primary zones, only include the loaded time. For secondary
+ * zones, also include the expire and refresh times.
+ */
+
+ CHECK(dns_zone_getloadtime(zone, &timestamp));
+
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ json_object_object_add(zoneobj, "loaded", json_object_new_string(buf));
+
+ if (dns_zone_gettype(zone) == dns_zone_secondary) {
+ CHECK(dns_zone_getexpiretime(zone, &timestamp));
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ json_object_object_add(zoneobj, "expires",
+ json_object_new_string(buf));
+
+ CHECK(dns_zone_getrefreshtime(zone, &timestamp));
+ isc_time_formatISO8601(&timestamp, buf, 64);
+ json_object_object_add(zoneobj, "refresh",
+ json_object_new_string(buf));
+ }
+
+ if (statlevel == dns_zonestat_full) {
+ isc_stats_t *zonestats;
+ isc_stats_t *gluecachestats;
+ dns_stats_t *rcvquerystats;
+ dns_stats_t *dnssecsignstats;
+ uint64_t nsstat_values[ns_statscounter_max];
+ uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
+
+ zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ json_object *counters = json_object_new_object();
+ if (counters == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dump_counters(zonestats, isc_statsformat_json,
+ counters, NULL, nsstats_xmldesc,
+ ns_statscounter_max,
+ nsstats_index, nsstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(zoneobj, "rcodes",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+ }
+
+ gluecachestats = dns_zone_getgluecachestats(zone);
+ if (gluecachestats != NULL) {
+ json_object *counters = json_object_new_object();
+ if (counters == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dump_counters(
+ gluecachestats, isc_statsformat_json, counters,
+ NULL, gluecachestats_xmldesc,
+ dns_gluecachestatscounter_max,
+ gluecachestats_index, gluecachestats_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(zoneobj, "gluecache",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+ }
+
+ rcvquerystats = dns_zone_getrcvquerystats(zone);
+ if (rcvquerystats != NULL) {
+ stats_dumparg_t dumparg;
+ json_object *counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(zoneobj, "qtypes",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+ }
+
+ dnssecsignstats = dns_zone_getdnssecsignstats(zone);
+ if (dnssecsignstats != NULL) {
+ stats_dumparg_t dumparg;
+ json_object *sign_counters = json_object_new_object();
+ CHECKMEM(sign_counters);
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = sign_counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_dnssecsignstats_dump(
+ dnssecsignstats, dns_dnssecsignstats_sign,
+ dnssecsignstat_dump, &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(sign_counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(sign_counters)->count != 0) {
+ json_object_object_add(zoneobj, "dnssec-sign",
+ sign_counters);
+ } else {
+ json_object_put(sign_counters);
+ }
+
+ json_object *refresh_counters =
+ json_object_new_object();
+ CHECKMEM(refresh_counters);
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = refresh_counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_dnssecsignstats_dump(
+ dnssecsignstats, dns_dnssecsignstats_refresh,
+ dnssecsignstat_dump, &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(refresh_counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(refresh_counters)->count !=
+ 0)
+ {
+ json_object_object_add(zoneobj,
+ "dnssec-refresh",
+ refresh_counters);
+ } else {
+ json_object_put(refresh_counters);
+ }
+ }
+ }
+
+ json_object_array_add(zonearray, zoneobj);
+ zoneobj = NULL;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (zoneobj != NULL) {
+ json_object_put(zoneobj);
+ }
+ return (result);
+}
+
+static isc_result_t
+generatejson(named_server_t *server, size_t *msglen, const char **msg,
+ json_object **rootp, uint32_t flags) {
+ dns_view_t *view;
+ isc_result_t result = ISC_R_SUCCESS;
+ json_object *bindstats, *viewlist, *counters, *obj;
+ json_object *traffic = NULL;
+ json_object *udpreq4 = NULL, *udpresp4 = NULL;
+ json_object *tcpreq4 = NULL, *tcpresp4 = NULL;
+ json_object *udpreq6 = NULL, *udpresp6 = NULL;
+ json_object *tcpreq6 = NULL, *tcpresp6 = NULL;
+ uint64_t nsstat_values[ns_statscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+ uint64_t udpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t udpoutsizestat_values[dns_sizecounter_out_max];
+ uint64_t tcpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t tcpoutsizestat_values[dns_sizecounter_out_max];
+#ifdef HAVE_DNSTAP
+ uint64_t dnstapstat_values[dns_dnstapcounter_max];
+#endif /* ifdef HAVE_DNSTAP */
+ stats_dumparg_t dumparg;
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ isc_time_t now;
+
+ REQUIRE(msglen != NULL);
+ REQUIRE(msg != NULL && *msg == NULL);
+ REQUIRE(rootp == NULL || *rootp == NULL);
+
+ bindstats = json_object_new_object();
+ if (bindstats == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ /*
+ * These statistics are included no matter which URL we use.
+ */
+ obj = json_object_new_string(STATS_JSON_VERSION);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "json-stats-version", obj);
+
+ isc_time_now(&now);
+ isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof(boottime));
+ isc_time_formatISO8601ms(&named_g_configtime, configtime,
+ sizeof configtime);
+ isc_time_formatISO8601ms(&now, nowstr, sizeof(nowstr));
+
+ obj = json_object_new_string(boottime);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "boot-time", obj);
+
+ obj = json_object_new_string(configtime);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "config-time", obj);
+
+ obj = json_object_new_string(nowstr);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "current-time", obj);
+ obj = json_object_new_string(PACKAGE_VERSION);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "version", obj);
+
+ if ((flags & STATS_JSON_SERVER) != 0) {
+ /* OPCODE counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+
+ dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "opcodes", counters);
+ } else {
+ json_object_put(counters);
+ }
+
+ /* OPCODE counters */
+ counters = json_object_new_object();
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+
+ dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "rcodes", counters);
+ } else {
+ json_object_put(counters);
+ }
+
+ /* QTYPE counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ dns_rdatatypestats_dump(server->sctx->rcvquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "qtypes", counters);
+ } else {
+ json_object_put(counters);
+ }
+
+ /* server stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(ns_stats_get(server->sctx->nsstats),
+ isc_statsformat_json, counters, NULL,
+ nsstats_xmldesc, ns_statscounter_max,
+ nsstats_index, nsstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "nsstats", counters);
+ } else {
+ json_object_put(counters);
+ }
+
+ /* zone stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->zonestats, isc_statsformat_json,
+ counters, NULL, zonestats_xmldesc,
+ dns_zonestatscounter_max,
+ zonestats_index, zonestat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "zonestats",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+
+ /* resolver stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(
+ server->resolverstats, isc_statsformat_json, counters,
+ NULL, resstats_xmldesc, dns_resstatscounter_max,
+ resstats_index, resstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "resstats", counters);
+ } else {
+ json_object_put(counters);
+ }
+
+#ifdef HAVE_DNSTAP
+ /* dnstap stat counters */
+ if (named_g_server->dtenv != NULL) {
+ isc_stats_t *dnstapstats = NULL;
+ dns_dt_getstats(named_g_server->dtenv, &dnstapstats);
+ counters = json_object_new_object();
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+ result = dump_counters(
+ dnstapstats, isc_statsformat_json, counters,
+ NULL, dnstapstats_xmldesc,
+ dns_dnstapcounter_max, dnstapstats_index,
+ dnstapstat_values, 0);
+ isc_stats_detach(&dnstapstats);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "dnstapstats",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+ }
+#endif /* ifdef HAVE_DNSTAP */
+ }
+
+ if ((flags & (STATS_JSON_ZONES | STATS_JSON_SERVER)) != 0) {
+ viewlist = json_object_new_object();
+ CHECKMEM(viewlist);
+
+ json_object_object_add(bindstats, "views", viewlist);
+
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ json_object *za, *v = json_object_new_object();
+
+ CHECKMEM(v);
+ json_object_object_add(viewlist, view->name, v);
+
+ za = json_object_new_array();
+ CHECKMEM(za);
+
+ if ((flags & STATS_JSON_ZONES) != 0) {
+ CHECK(dns_zt_apply(view->zonetable,
+ isc_rwlocktype_read, true,
+ NULL, zone_jsonrender, za));
+ }
+
+ if (json_object_array_length(za) != 0) {
+ json_object_object_add(v, "zones", za);
+ } else {
+ json_object_put(za);
+ }
+
+ if ((flags & STATS_JSON_SERVER) != 0) {
+ json_object *res;
+ dns_stats_t *dstats;
+ isc_stats_t *istats;
+
+ res = json_object_new_object();
+ CHECKMEM(res);
+ json_object_object_add(v, "resolver", res);
+
+ istats = view->resstats;
+ if (istats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dump_counters(
+ istats, isc_statsformat_json,
+ counters, NULL,
+ resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index, resstat_values,
+ 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto cleanup;
+ }
+
+ json_object_object_add(res, "stats",
+ counters);
+ }
+
+ dstats = view->resquerystats;
+ if (dstats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(dstats,
+ rdtypestat_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto cleanup;
+ }
+
+ json_object_object_add(res, "qtypes",
+ counters);
+ }
+
+ dstats = dns_db_getrrsetstats(view->cachedb);
+ if (dstats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatasetstats_dump(
+ dstats, rdatasetstats_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto cleanup;
+ }
+
+ json_object_object_add(res, "cache",
+ counters);
+ }
+
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dns_cache_renderjson(view->cache,
+ counters);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ json_object_object_add(res, "cachestats",
+ counters);
+
+ istats = view->adbstats;
+ if (istats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dump_counters(
+ istats, isc_statsformat_json,
+ counters, NULL,
+ adbstats_xmldesc,
+ dns_adbstats_max,
+ adbstats_index, adbstat_values,
+ 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto cleanup;
+ }
+
+ json_object_object_add(res, "adb",
+ counters);
+ }
+ }
+
+ view = ISC_LIST_NEXT(view, link);
+ }
+ }
+
+ if ((flags & STATS_JSON_NET) != 0) {
+ /* socket stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->sockstats, isc_statsformat_json,
+ counters, NULL, sockstats_xmldesc,
+ isc_sockstatscounter_max,
+ sockstats_index, sockstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto cleanup;
+ }
+
+ if (json_object_get_object(counters)->count != 0) {
+ json_object_object_add(bindstats, "sockstats",
+ counters);
+ } else {
+ json_object_put(counters);
+ }
+ }
+
+ if ((flags & STATS_JSON_TASKS) != 0) {
+ json_object *tasks = json_object_new_object();
+ CHECKMEM(tasks);
+
+ result = isc_taskmgr_renderjson(named_g_taskmgr, tasks);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(tasks);
+ goto cleanup;
+ }
+
+ json_object_object_add(bindstats, "taskmgr", tasks);
+ }
+
+ if ((flags & STATS_JSON_MEM) != 0) {
+ json_object *memory = json_object_new_object();
+ CHECKMEM(memory);
+
+ result = isc_mem_renderjson(memory);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(memory);
+ goto cleanup;
+ }
+
+ json_object_object_add(bindstats, "memory", memory);
+ }
+
+ if ((flags & STATS_JSON_TRAFFIC) != 0) {
+ traffic = json_object_new_object();
+ CHECKMEM(traffic);
+
+ udpreq4 = json_object_new_object();
+ CHECKMEM(udpreq4);
+
+ udpresp4 = json_object_new_object();
+ CHECKMEM(udpresp4);
+
+ tcpreq4 = json_object_new_object();
+ CHECKMEM(tcpreq4);
+
+ tcpresp4 = json_object_new_object();
+ CHECKMEM(tcpresp4);
+
+ udpreq6 = json_object_new_object();
+ CHECKMEM(udpreq6);
+
+ udpresp6 = json_object_new_object();
+ CHECKMEM(udpresp6);
+
+ tcpreq6 = json_object_new_object();
+ CHECKMEM(tcpreq6);
+
+ tcpresp6 = json_object_new_object();
+ CHECKMEM(tcpresp6);
+
+ CHECK(dump_counters(
+ server->sctx->udpinstats4, isc_statsformat_json,
+ udpreq4, NULL, udpinsizestats_xmldesc,
+ dns_sizecounter_in_max, udpinsizestats_index,
+ udpinsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->udpoutstats4, isc_statsformat_json,
+ udpresp4, NULL, udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max, udpoutsizestats_index,
+ udpoutsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->tcpinstats4, isc_statsformat_json,
+ tcpreq4, NULL, tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max, tcpinsizestats_index,
+ tcpinsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->tcpoutstats4, isc_statsformat_json,
+ tcpresp4, NULL, tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max, tcpoutsizestats_index,
+ tcpoutsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->udpinstats6, isc_statsformat_json,
+ udpreq6, NULL, udpinsizestats_xmldesc,
+ dns_sizecounter_in_max, udpinsizestats_index,
+ udpinsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->udpoutstats6, isc_statsformat_json,
+ udpresp6, NULL, udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max, udpoutsizestats_index,
+ udpoutsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->tcpinstats6, isc_statsformat_json,
+ tcpreq6, NULL, tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max, tcpinsizestats_index,
+ tcpinsizestat_values, 0));
+
+ CHECK(dump_counters(
+ server->sctx->tcpoutstats6, isc_statsformat_json,
+ tcpresp6, NULL, tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max, tcpoutsizestats_index,
+ tcpoutsizestat_values, 0));
+
+ json_object_object_add(traffic,
+ "dns-udp-requests-sizes-received-ipv4",
+ udpreq4);
+ json_object_object_add(
+ traffic, "dns-udp-responses-sizes-sent-ipv4", udpresp4);
+ json_object_object_add(traffic,
+ "dns-tcp-requests-sizes-received-ipv4",
+ tcpreq4);
+ json_object_object_add(
+ traffic, "dns-tcp-responses-sizes-sent-ipv4", tcpresp4);
+ json_object_object_add(traffic,
+ "dns-udp-requests-sizes-received-ipv6",
+ udpreq6);
+ json_object_object_add(
+ traffic, "dns-udp-responses-sizes-sent-ipv6", udpresp6);
+ json_object_object_add(traffic,
+ "dns-tcp-requests-sizes-received-ipv6",
+ tcpreq6);
+ json_object_object_add(
+ traffic, "dns-tcp-responses-sizes-sent-ipv6", tcpresp6);
+ json_object_object_add(bindstats, "traffic", traffic);
+ udpreq4 = NULL;
+ udpresp4 = NULL;
+ tcpreq4 = NULL;
+ tcpresp4 = NULL;
+ udpreq6 = NULL;
+ udpresp6 = NULL;
+ tcpreq6 = NULL;
+ tcpresp6 = NULL;
+ traffic = NULL;
+ }
+
+ *msg = json_object_to_json_string_ext(bindstats,
+ JSON_C_TO_STRING_PRETTY);
+ *msglen = strlen(*msg);
+
+ if (rootp != NULL) {
+ *rootp = bindstats;
+ bindstats = NULL;
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (udpreq4 != NULL) {
+ json_object_put(udpreq4);
+ }
+ if (udpresp4 != NULL) {
+ json_object_put(udpresp4);
+ }
+ if (tcpreq4 != NULL) {
+ json_object_put(tcpreq4);
+ }
+ if (tcpresp4 != NULL) {
+ json_object_put(tcpresp4);
+ }
+ if (udpreq6 != NULL) {
+ json_object_put(udpreq6);
+ }
+ if (udpresp6 != NULL) {
+ json_object_put(udpresp6);
+ }
+ if (tcpreq6 != NULL) {
+ json_object_put(tcpreq6);
+ }
+ if (tcpresp6 != NULL) {
+ json_object_put(tcpresp6);
+ }
+ if (traffic != NULL) {
+ json_object_put(traffic);
+ }
+ if (bindstats != NULL) {
+ json_object_put(bindstats);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+render_json(uint32_t flags, void *arg, unsigned int *retcode,
+ const char **retmsg, const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ isc_result_t result;
+ json_object *bindstats = NULL;
+ named_server_t *server = arg;
+ const char *msg = NULL;
+ size_t msglen = 0;
+ char *p;
+
+ result = generatejson(server, &msglen, &msg, &bindstats, flags);
+ if (result == ISC_R_SUCCESS) {
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "application/json";
+ DE_CONST(msg, p);
+ isc_buffer_reinit(b, p, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_jsonfree;
+ *freecb_args = bindstats;
+ } else {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rendering JSON()");
+ }
+
+ return (result);
+}
+
+static isc_result_t
+render_json_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_ALL, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_STATUS, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_SERVER, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_ZONES, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_MEM, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_TASKS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_NET, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_TRAFFIC, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
+}
+
+#endif /* HAVE_JSON_C */
+
+static isc_result_t
+render_xsl(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
+ isc_result_t result;
+ char *p = NULL;
+
+ UNUSED(httpd);
+ UNUSED(args);
+
+ *freecb = NULL;
+ *freecb_args = NULL;
+ *mimetype = "text/xslt+xml";
+
+ if (isc_httpdurl_isstatic(urlinfo)) {
+ time_t t1, t2;
+ const isc_time_t *when;
+ const isc_time_t *loadtime;
+
+ when = isc_httpd_if_modified_since(httpd);
+
+ if (isc_time_isepoch(when)) {
+ goto send;
+ }
+
+ result = isc_time_secondsastimet(when, &t1);
+ if (result != ISC_R_SUCCESS) {
+ goto send;
+ }
+
+ loadtime = isc_httpdurl_loadtime(urlinfo);
+
+ result = isc_time_secondsastimet(loadtime, &t2);
+ if (result != ISC_R_SUCCESS) {
+ goto send;
+ }
+
+ if (t1 < t2) {
+ goto send;
+ }
+
+ *retcode = 304;
+ *retmsg = "Not modified";
+ goto end;
+ }
+
+send:
+ *retcode = 200;
+ *retmsg = "OK";
+ DE_CONST(xslmsg, p);
+ isc_buffer_reinit(b, p, strlen(xslmsg));
+ isc_buffer_add(b, strlen(xslmsg));
+end:
+ return (ISC_R_SUCCESS);
+}
+
+static void
+shutdown_listener(named_statschannel_t *listener) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&listener->address, socktext, sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "stopping statistics channel on %s", socktext);
+
+ isc_httpdmgr_shutdown(&listener->httpdmgr);
+}
+
+static bool
+client_ok(const isc_sockaddr_t *fromaddr, void *arg) {
+ named_statschannel_t *listener = arg;
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
+ isc_netaddr_t netaddr;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ int match;
+
+ REQUIRE(listener != NULL);
+
+ isc_netaddr_fromsockaddr(&netaddr, fromaddr);
+
+ LOCK(&listener->lock);
+ if ((dns_acl_match(&netaddr, NULL, listener->acl, env, &match, NULL) ==
+ ISC_R_SUCCESS) &&
+ match > 0)
+ {
+ UNLOCK(&listener->lock);
+ return (true);
+ }
+ UNLOCK(&listener->lock);
+
+ isc_sockaddr_format(fromaddr, socktext, sizeof(socktext));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "rejected statistics connection from %s", socktext);
+
+ return (false);
+}
+
+static void
+destroy_listener(void *arg) {
+ named_statschannel_t *listener = (named_statschannel_t *)arg;
+
+ REQUIRE(listener != NULL);
+ REQUIRE(!ISC_LINK_LINKED(listener, link));
+
+ /* We don't have to acquire the lock here since it's already unlinked */
+ dns_acl_detach(&listener->acl);
+
+ isc_mutex_destroy(&listener->lock);
+ isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
+}
+
+static isc_result_t
+add_listener(named_server_t *server, named_statschannel_t **listenerp,
+ const cfg_obj_t *listen_params, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext) {
+ isc_result_t result;
+ named_statschannel_t *listener = NULL;
+ const cfg_obj_t *allow = NULL;
+ dns_acl_t *new_acl = NULL;
+ int pf;
+
+ listener = isc_mem_get(server->mctx, sizeof(*listener));
+ *listener = (named_statschannel_t){ .address = *addr };
+ ISC_LINK_INIT(listener, link);
+ isc_mutex_init(&listener->lock);
+ isc_mem_attach(server->mctx, &listener->mctx);
+
+ allow = cfg_tuple_get(listen_params, "allow");
+ if (allow != NULL && cfg_obj_islist(allow)) {
+ result = cfg_acl_fromconfig(allow, config, named_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else {
+ result = dns_acl_any(listener->mctx, &new_acl);
+ }
+ CHECK(result);
+
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ pf = isc_sockaddr_pf(&listener->address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ {
+ CHECK(ISC_R_FAMILYNOSUPPORT);
+ }
+
+ CHECK(isc_httpdmgr_create(named_g_netmgr, server->mctx, addr, client_ok,
+ destroy_listener, listener,
+ &listener->httpdmgr));
+
+#ifdef HAVE_LIBXML2
+ isc_httpdmgr_addurl(listener->httpdmgr, "/", false, render_xml_all,
+ server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml", false, render_xml_all,
+ server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR, false,
+ render_xml_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/status", false,
+ render_xml_status, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/server", false,
+ render_xml_server, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/zones", false,
+ render_xml_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/net", false,
+ render_xml_net, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/tasks", false,
+ render_xml_tasks, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/mem", false,
+ render_xml_mem, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/xml/v" STATS_XML_VERSION_MAJOR "/traffic", false,
+ render_xml_traffic, server);
+#endif /* ifdef HAVE_LIBXML2 */
+#ifdef HAVE_JSON_C
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json", false, render_json_all,
+ server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR, false,
+ render_json_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/status", false,
+ render_json_status, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/server", false,
+ render_json_server, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/zones", false,
+ render_json_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/tasks", false,
+ render_json_tasks, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/net", false,
+ render_json_net, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/mem", false,
+ render_json_mem, server);
+ isc_httpdmgr_addurl(listener->httpdmgr,
+ "/json/v" STATS_JSON_VERSION_MAJOR "/traffic",
+ false, render_json_traffic, server);
+#endif /* ifdef HAVE_JSON_C */
+ isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", true, render_xsl,
+ server);
+
+ *listenerp = listener;
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "statistics channel listening on %s", socktext);
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (listener->acl != NULL) {
+ dns_acl_detach(&listener->acl);
+ }
+ isc_mutex_destroy(&listener->lock);
+ isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
+
+ return (result);
+}
+
+static void
+update_listener(named_server_t *server, named_statschannel_t **listenerp,
+ const cfg_obj_t *listen_params, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext) {
+ named_statschannel_t *listener;
+ const cfg_obj_t *allow = NULL;
+ dns_acl_t *new_acl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ {
+ if (isc_sockaddr_equal(addr, &listener->address)) {
+ break;
+ }
+ }
+
+ if (listener == NULL) {
+ *listenerp = NULL;
+ return;
+ }
+
+ /*
+ * Now, keep the old access list unless a new one can be made.
+ */
+ allow = cfg_tuple_get(listen_params, "allow");
+ if (allow != NULL && cfg_obj_islist(allow)) {
+ result = cfg_acl_fromconfig(allow, config, named_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else {
+ result = dns_acl_any(listener->mctx, &new_acl);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ LOCK(&listener->lock);
+
+ dns_acl_detach(&listener->acl);
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ UNLOCK(&listener->lock);
+ } else {
+ cfg_obj_log(listen_params, named_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "statistics channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ *listenerp = listener;
+}
+
+isc_result_t
+named_statschannels_configure(named_server_t *server, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx) {
+ named_statschannel_t *listener, *listener_next;
+ named_statschannellist_t new_listeners;
+ const cfg_obj_t *statschannellist = NULL;
+ const cfg_listelt_t *element, *element2;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
+
+ ISC_LIST_INIT(new_listeners);
+
+ /*
+ * Get the list of named.conf 'statistics-channels' statements.
+ */
+ (void)cfg_map_get(config, "statistics-channels", &statschannellist);
+
+ /*
+ * Run through the new address/port list, noting sockets that are
+ * already being listened on and moving them to the new list.
+ *
+ * Identifying duplicate addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ if (statschannellist != NULL) {
+#ifndef EXTENDED_STATS
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels specified but not effective "
+ "due to missing XML and/or JSON library");
+#else /* EXTENDED_STATS */
+#ifndef HAVE_LIBXML2
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels: XML library missing, "
+ "only JSON stats will be available");
+#endif /* !HAVE_LIBXML2 */
+#ifndef HAVE_JSON_C
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels: JSON library missing, "
+ "only XML stats will be available");
+#endif /* !HAVE_JSON_C */
+#endif /* EXTENDED_STATS */
+
+ for (element = cfg_list_first(statschannellist);
+ element != NULL; element = cfg_list_next(element))
+ {
+ const cfg_obj_t *statschannel;
+ const cfg_obj_t *listenercfg = NULL;
+
+ statschannel = cfg_listelt_value(element);
+ (void)cfg_map_get(statschannel, "inet", &listenercfg);
+ if (listenercfg == NULL) {
+ continue;
+ }
+
+ for (element2 = cfg_list_first(listenercfg);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *listen_params;
+ const cfg_obj_t *obj;
+ isc_sockaddr_t addr;
+
+ listen_params = cfg_listelt_value(element2);
+
+ obj = cfg_tuple_get(listen_params, "address");
+ addr = *cfg_obj_assockaddr(obj);
+ if (isc_sockaddr_getport(&addr) == 0) {
+ isc_sockaddr_setport(
+ &addr,
+ NAMED_STATSCHANNEL_HTTPPORT);
+ }
+
+ isc_sockaddr_format(&addr, socktext,
+ sizeof(socktext));
+
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(9),
+ "processing statistics "
+ "channel %s",
+ socktext);
+
+ update_listener(server, &listener,
+ listen_params, config, &addr,
+ aclconfctx, socktext);
+
+ if (listener != NULL) {
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(server->statschannels,
+ listener, link);
+ } else {
+ /*
+ * This is a new listener.
+ */
+ isc_result_t r;
+
+ r = add_listener(server, &listener,
+ listen_params, config,
+ &addr, aclconfctx,
+ socktext);
+ if (r != ISC_R_SUCCESS) {
+ cfg_obj_log(
+ listen_params,
+ named_g_lctx,
+ ISC_LOG_WARNING,
+ "couldn't allocate "
+ "statistics channel"
+ " %s: %s",
+ socktext,
+ isc_result_totext(r));
+ }
+ }
+
+ if (listener != NULL) {
+ ISC_LIST_APPEND(new_listeners, listener,
+ link);
+ }
+ }
+ }
+ }
+
+ for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL;
+ listener = listener_next)
+ {
+ listener_next = ISC_LIST_NEXT(listener, link);
+ ISC_LIST_UNLINK(server->statschannels, listener, link);
+ shutdown_listener(listener);
+ }
+
+ ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
+ return (ISC_R_SUCCESS);
+}
+
+void
+named_statschannels_shutdown(named_server_t *server) {
+ named_statschannel_t *listener;
+
+ while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) {
+ ISC_LIST_UNLINK(server->statschannels, listener, link);
+ shutdown_listener(listener);
+ }
+}
+
+isc_result_t
+named_stats_dump(named_server_t *server, FILE *fp) {
+ isc_stdtime_t now;
+ isc_result_t result;
+ dns_view_t *view;
+ dns_zone_t *zone, *next;
+ stats_dumparg_t dumparg;
+ uint64_t nsstat_values[ns_statscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+ uint64_t gluecachestats_values[dns_gluecachestatscounter_max];
+
+ RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
+
+ /* Set common fields */
+ dumparg.type = isc_statsformat_file;
+ dumparg.arg = fp;
+
+ isc_stdtime_get(&now);
+ fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
+
+ fprintf(fp, "++ Incoming Requests ++\n");
+ dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump,
+ &dumparg, 0);
+
+ fprintf(fp, "++ Incoming Queries ++\n");
+ dns_rdatatypestats_dump(server->sctx->rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+
+ fprintf(fp, "++ Outgoing Rcodes ++\n");
+ dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump, &dumparg,
+ 0);
+
+ fprintf(fp, "++ Outgoing Queries ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->resquerystats == NULL) {
+ continue;
+ }
+ if (strcmp(view->name, "_default") == 0) {
+ fprintf(fp, "[View: default]\n");
+ } else {
+ fprintf(fp, "[View: %s]\n", view->name);
+ }
+ dns_rdatatypestats_dump(view->resquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ }
+
+ fprintf(fp, "++ Name Server Statistics ++\n");
+ (void)dump_counters(ns_stats_get(server->sctx->nsstats),
+ isc_statsformat_file, fp, NULL, nsstats_desc,
+ ns_statscounter_max, nsstats_index, nsstat_values,
+ 0);
+
+ fprintf(fp, "++ Zone Maintenance Statistics ++\n");
+ (void)dump_counters(server->zonestats, isc_statsformat_file, fp, NULL,
+ zonestats_desc, dns_zonestatscounter_max,
+ zonestats_index, zonestat_values, 0);
+
+ fprintf(fp, "++ Resolver Statistics ++\n");
+ fprintf(fp, "[Common]\n");
+ (void)dump_counters(server->resolverstats, isc_statsformat_file, fp,
+ NULL, resstats_desc, dns_resstatscounter_max,
+ resstats_index, resstat_values, 0);
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->resstats == NULL) {
+ continue;
+ }
+ if (strcmp(view->name, "_default") == 0) {
+ fprintf(fp, "[View: default]\n");
+ } else {
+ fprintf(fp, "[View: %s]\n", view->name);
+ }
+ (void)dump_counters(view->resstats, isc_statsformat_file, fp,
+ NULL, resstats_desc,
+ dns_resstatscounter_max, resstats_index,
+ resstat_values, 0);
+ }
+
+ fprintf(fp, "++ Cache Statistics ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (strcmp(view->name, "_default") == 0) {
+ fprintf(fp, "[View: default]\n");
+ } else {
+ fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
+ dns_cache_getname(view->cache));
+ }
+ /*
+ * Avoid dumping redundant statistics when the cache is shared.
+ */
+ if (dns_view_iscacheshared(view)) {
+ continue;
+ }
+ dns_cache_dumpstats(view->cache, fp);
+ }
+
+ fprintf(fp, "++ Cache DB RRsets ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ dns_stats_t *cacherrstats;
+
+ cacherrstats = dns_db_getrrsetstats(view->cachedb);
+ if (cacherrstats == NULL) {
+ continue;
+ }
+ if (strcmp(view->name, "_default") == 0) {
+ fprintf(fp, "[View: default]\n");
+ } else {
+ fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
+ dns_cache_getname(view->cache));
+ }
+ if (dns_view_iscacheshared(view)) {
+ /*
+ * Avoid dumping redundant statistics when the cache is
+ * shared.
+ */
+ continue;
+ }
+ dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
+ &dumparg, 0);
+ }
+
+ fprintf(fp, "++ ADB stats ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->adbstats == NULL) {
+ continue;
+ }
+ if (strcmp(view->name, "_default") == 0) {
+ fprintf(fp, "[View: default]\n");
+ } else {
+ fprintf(fp, "[View: %s]\n", view->name);
+ }
+ (void)dump_counters(view->adbstats, isc_statsformat_file, fp,
+ NULL, adbstats_desc, dns_adbstats_max,
+ adbstats_index, adbstat_values, 0);
+ }
+
+ fprintf(fp, "++ Socket I/O Statistics ++\n");
+ (void)dump_counters(server->sockstats, isc_statsformat_file, fp, NULL,
+ sockstats_desc, isc_sockstatscounter_max,
+ sockstats_index, sockstat_values, 0);
+
+ fprintf(fp, "++ Per Zone Query Statistics ++\n");
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next)
+ {
+ isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ char zonename[DNS_NAME_FORMATSIZE];
+
+ view = dns_zone_getview(zone);
+ if (view == NULL) {
+ continue;
+ }
+
+ dns_name_format(dns_zone_getorigin(zone), zonename,
+ sizeof(zonename));
+ fprintf(fp, "[%s", zonename);
+ if (strcmp(view->name, "_default") != 0) {
+ fprintf(fp, " (view: %s)", view->name);
+ }
+ fprintf(fp, "]\n");
+
+ (void)dump_counters(zonestats, isc_statsformat_file, fp,
+ NULL, nsstats_desc,
+ ns_statscounter_max, nsstats_index,
+ nsstat_values, 0);
+ }
+ }
+
+ fprintf(fp, "++ Per Zone Glue Cache Statistics ++\n");
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next)
+ {
+ isc_stats_t *gluecachestats = dns_zone_getgluecachestats(zone);
+ if (gluecachestats != NULL) {
+ char zonename[DNS_NAME_FORMATSIZE];
+
+ view = dns_zone_getview(zone);
+ if (view == NULL) {
+ continue;
+ }
+
+ dns_name_format(dns_zone_getorigin(zone), zonename,
+ sizeof(zonename));
+ fprintf(fp, "[%s", zonename);
+ if (strcmp(view->name, "_default") != 0) {
+ fprintf(fp, " (view: %s)", view->name);
+ }
+ fprintf(fp, "]\n");
+
+ (void)dump_counters(
+ gluecachestats, isc_statsformat_file, fp, NULL,
+ gluecachestats_desc,
+ dns_gluecachestatscounter_max,
+ gluecachestats_index, gluecachestats_values, 0);
+ }
+ }
+
+ fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
+
+ return (ISC_R_SUCCESS); /* this function currently always succeeds */
+}
diff --git a/bin/named/tkeyconf.c b/bin/named/tkeyconf.c
new file mode 100644
index 0000000..03b198b
--- /dev/null
+++ b/bin/named/tkeyconf.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/name.h>
+#include <dns/tkey.h>
+
+#include <dst/gssapi.h>
+
+#include <isccfg/cfg.h>
+
+#include <named/tkeyconf.h>
+
+#define RETERR(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+#include <named/log.h>
+#define LOG(msg) \
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "%s", msg)
+
+isc_result_t
+named_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ dns_tkeyctx_t **tctxp) {
+ isc_result_t result;
+ dns_tkeyctx_t *tctx = NULL;
+ const char *s;
+ uint32_t n;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const cfg_obj_t *obj;
+ int type;
+
+ result = dns_tkeyctx_create(mctx, &tctx);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-dhkey", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ n = cfg_obj_asuint32(cfg_tuple_get(obj, "keyid"));
+ isc_buffer_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ type = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY;
+ RETERR(dst_key_fromfile(name, (dns_keytag_t)n, DNS_KEYALG_DH,
+ type, NULL, mctx, &tctx->dhkey));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-domain", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ isc_buffer_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ tctx->domain = isc_mem_get(mctx, sizeof(dns_name_t));
+ dns_name_init(tctx->domain, NULL);
+ dns_name_dup(name, mctx, tctx->domain);
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-gssapi-credential", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+
+ isc_buffer_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ RETERR(dst_gssapi_acquirecred(name, false, &tctx->gsscred));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-gssapi-keytab", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ tctx->gssapi_keytab = isc_mem_strdup(mctx, s);
+ }
+
+ *tctxp = tctx;
+ return (ISC_R_SUCCESS);
+
+failure:
+ dns_tkeyctx_destroy(&tctx);
+ return (result);
+}
diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c
new file mode 100644
index 0000000..f24aab1
--- /dev/null
+++ b/bin/named/transportconf.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/transport.h>
+
+#include <isccfg/cfg.h>
+
+#include <named/log.h>
+#include <named/transportconf.h>
+
+#define create_name(id, name) \
+ isc_buffer_t namesrc, namebuf; \
+ char namedata[DNS_NAME_FORMATSIZE + 1]; \
+ dns_name_init(name, NULL); \
+ isc_buffer_constinit(&namesrc, id, strlen(id)); \
+ isc_buffer_add(&namesrc, strlen(id)); \
+ isc_buffer_init(&namebuf, namedata, sizeof(namedata)); \
+ result = (dns_name_fromtext(name, &namesrc, dns_rootname, \
+ DNS_NAME_DOWNCASE, &namebuf)); \
+ if (result != ISC_R_SUCCESS) { \
+ goto failure; \
+ }
+
+#define parse_transport_option(map, transport, name, setter) \
+ { \
+ const cfg_obj_t *obj = NULL; \
+ cfg_map_get(map, name, &obj); \
+ if (obj != NULL) { \
+ setter(transport, cfg_obj_asstring(obj)); \
+ } \
+ }
+
+#define parse_transport_tls_versions(map, transport, name, setter) \
+ { \
+ const cfg_obj_t *obj = NULL; \
+ cfg_map_get(map, name, &obj); \
+ if (obj != NULL) { \
+ { \
+ uint32_t tls_protos = 0; \
+ const cfg_listelt_t *proto = NULL; \
+ INSIST(obj != NULL); \
+ for (proto = cfg_list_first(obj); proto != 0; \
+ proto = cfg_list_next(proto)) \
+ { \
+ const cfg_obj_t *tls_proto_obj = \
+ cfg_listelt_value(proto); \
+ const char *tls_sver = \
+ cfg_obj_asstring( \
+ tls_proto_obj); \
+ const isc_tls_protocol_version_t ver = \
+ isc_tls_protocol_name_to_version( \
+ tls_sver); \
+ INSIST(ver != \
+ ISC_TLS_PROTO_VER_UNDEFINED); \
+ INSIST(isc_tls_protocol_supported( \
+ ver)); \
+ tls_protos |= ver; \
+ } \
+ if (tls_protos != 0) { \
+ setter(transport, tls_protos); \
+ } \
+ } \
+ } \
+ }
+
+#define parse_transport_bool_option(map, transport, name, setter) \
+ { \
+ const cfg_obj_t *obj = NULL; \
+ cfg_map_get(map, name, &obj); \
+ if (obj != NULL) { \
+ setter(transport, cfg_obj_asboolean(obj)); \
+ } \
+ }
+
+static isc_result_t
+add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
+ const cfg_obj_t *doh = NULL;
+ const char *dohid = NULL;
+ isc_result_t result;
+
+ for (const cfg_listelt_t *element = cfg_list_first(transportlist);
+ element != NULL; element = cfg_list_next(element))
+ {
+ dns_name_t dohname;
+ dns_transport_t *transport;
+
+ doh = cfg_listelt_value(element);
+ dohid = cfg_obj_asstring(cfg_map_getname(doh));
+
+ create_name(dohid, &dohname);
+
+ transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP,
+ list);
+
+ dns_transport_set_tlsname(transport, dohid);
+ parse_transport_option(doh, transport, "key-file",
+ dns_transport_set_keyfile);
+ parse_transport_option(doh, transport, "cert-file",
+ dns_transport_set_certfile);
+ parse_transport_tls_versions(doh, transport, "protocols",
+ dns_transport_set_tls_versions);
+ parse_transport_option(doh, transport, "ciphers",
+ dns_transport_set_ciphers);
+ parse_transport_bool_option(
+ doh, transport, "prefer-server-ciphers",
+ dns_transport_set_prefer_server_ciphers)
+ parse_transport_option(doh, transport, "ca-file",
+ dns_transport_set_cafile);
+ parse_transport_option(doh, transport, "remote-hostname",
+ dns_transport_set_remote_hostname);
+ }
+
+ return (ISC_R_SUCCESS);
+failure:
+ cfg_obj_log(doh, named_g_lctx, ISC_LOG_ERROR,
+ "configuring DoH '%s': %s", dohid,
+ isc_result_totext(result));
+
+ return (result);
+}
+
+static isc_result_t
+add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
+ const cfg_obj_t *tls = NULL;
+ const char *tlsid = NULL;
+ isc_result_t result;
+
+ for (const cfg_listelt_t *element = cfg_list_first(transportlist);
+ element != NULL; element = cfg_list_next(element))
+ {
+ dns_name_t tlsname;
+ dns_transport_t *transport;
+
+ tls = cfg_listelt_value(element);
+ tlsid = cfg_obj_asstring(cfg_map_getname(tls));
+
+ if (!strcmp(tlsid, "ephemeral")) {
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto failure;
+ }
+
+ create_name(tlsid, &tlsname);
+
+ transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS,
+ list);
+
+ dns_transport_set_tlsname(transport, tlsid);
+ parse_transport_option(tls, transport, "key-file",
+ dns_transport_set_keyfile);
+ parse_transport_option(tls, transport, "cert-file",
+ dns_transport_set_certfile);
+ parse_transport_tls_versions(tls, transport, "protocols",
+ dns_transport_set_tls_versions);
+ parse_transport_option(tls, transport, "ciphers",
+ dns_transport_set_ciphers);
+ parse_transport_bool_option(
+ tls, transport, "prefer-server-ciphers",
+ dns_transport_set_prefer_server_ciphers)
+ parse_transport_option(tls, transport, "ca-file",
+ dns_transport_set_cafile);
+ parse_transport_option(tls, transport, "remote-hostname",
+ dns_transport_set_remote_hostname);
+ }
+
+ return (ISC_R_SUCCESS);
+failure:
+ cfg_obj_log(tls, named_g_lctx, ISC_LOG_ERROR,
+ "configuring tls '%s': %s", tlsid,
+ isc_result_totext(result));
+
+ return (result);
+}
+
+#define CHECK(f) \
+ if ((result = f) != ISC_R_SUCCESS) { \
+ goto failure; \
+ }
+
+static isc_result_t
+transport_list_fromconfig(const cfg_obj_t *config, dns_transport_list_t *list) {
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ if (result == ISC_R_SUCCESS &&
+ cfg_map_get(config, "tls", &obj) == ISC_R_SUCCESS)
+ {
+ result = add_tls_transports(obj, list);
+ obj = NULL;
+ }
+
+ if (result == ISC_R_SUCCESS &&
+ cfg_map_get(config, "doh", &obj) == ISC_R_SUCCESS)
+ {
+ result = add_doh_transports(obj, list);
+ obj = NULL;
+ }
+
+ return (result);
+}
+
+static void
+transport_list_add_ephemeral(dns_transport_list_t *list) {
+ isc_result_t result;
+ dns_name_t tlsname;
+ dns_transport_t *transport;
+
+ create_name("ephemeral", &tlsname);
+
+ transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list);
+ dns_transport_set_tlsname(transport, "ephemeral");
+
+ return;
+failure:
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+isc_result_t
+named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_transport_list_t **listp) {
+ isc_result_t result;
+ dns_transport_list_t *list = dns_transport_list_new(mctx);
+
+ REQUIRE(listp != NULL && *listp == NULL);
+
+ transport_list_add_ephemeral(list);
+
+ if (config != NULL) {
+ result = transport_list_fromconfig(config, list);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ }
+
+ if (vconfig != NULL) {
+ config = cfg_tuple_get(vconfig, "options");
+ transport_list_fromconfig(config, list);
+ }
+
+ *listp = list;
+ return (ISC_R_SUCCESS);
+failure:
+ dns_transport_list_detach(&list);
+ return (result);
+}
diff --git a/bin/named/tsigconf.c b/bin/named/tsigconf.c
new file mode 100644
index 0000000..6d596ab
--- /dev/null
+++ b/bin/named/tsigconf.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/tsig.h>
+
+#include <isccfg/cfg.h>
+
+#include <named/config.h>
+#include <named/log.h>
+#include <named/tsigconf.h>
+
+static isc_result_t
+add_initial_keys(const cfg_obj_t *list, dns_tsig_keyring_t *ring,
+ isc_mem_t *mctx) {
+ dns_tsigkey_t *tsigkey = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *key = NULL;
+ const char *keyid = NULL;
+ unsigned char *secret = NULL;
+ int secretalloc = 0;
+ int secretlen = 0;
+ isc_result_t ret;
+ isc_stdtime_t now;
+ uint16_t bits;
+
+ for (element = cfg_list_first(list); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ dns_name_t keyname;
+ const dns_name_t *alg;
+ const char *algstr;
+ char keynamedata[1024];
+ isc_buffer_t keynamesrc, keynamebuf;
+ const char *secretstr;
+ isc_buffer_t secretbuf;
+
+ key = cfg_listelt_value(element);
+ keyid = cfg_obj_asstring(cfg_map_getname(key));
+
+ algobj = NULL;
+ secretobj = NULL;
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ /*
+ * Create the key name.
+ */
+ dns_name_init(&keyname, NULL);
+ isc_buffer_constinit(&keynamesrc, keyid, strlen(keyid));
+ isc_buffer_add(&keynamesrc, strlen(keyid));
+ isc_buffer_init(&keynamebuf, keynamedata, sizeof(keynamedata));
+ ret = dns_name_fromtext(&keyname, &keynamesrc, dns_rootname,
+ DNS_NAME_DOWNCASE, &keynamebuf);
+ if (ret != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ /*
+ * Create the algorithm.
+ */
+ algstr = cfg_obj_asstring(algobj);
+ if (named_config_getkeyalgorithm(algstr, &alg, &bits) !=
+ ISC_R_SUCCESS)
+ {
+ cfg_obj_log(algobj, named_g_lctx, ISC_LOG_ERROR,
+ "key '%s': has a "
+ "unsupported algorithm '%s'",
+ keyid, algstr);
+ ret = DNS_R_BADALG;
+ goto failure;
+ }
+
+ secretstr = cfg_obj_asstring(secretobj);
+ secretalloc = secretlen = strlen(secretstr) * 3 / 4;
+ secret = isc_mem_get(mctx, secretlen);
+ isc_buffer_init(&secretbuf, secret, secretlen);
+ ret = isc_base64_decodestring(secretstr, &secretbuf);
+ if (ret != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ secretlen = isc_buffer_usedlength(&secretbuf);
+
+ isc_stdtime_get(&now);
+ ret = dns_tsigkey_create(&keyname, alg, secret, secretlen,
+ false, NULL, now, now, mctx, ring,
+ &tsigkey);
+ isc_mem_put(mctx, secret, secretalloc);
+ secret = NULL;
+ if (ret != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ /*
+ * Set digest bits.
+ */
+ dst_key_setbits(tsigkey->key, bits);
+ dns_tsigkey_detach(&tsigkey);
+ }
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR,
+ "configuring key '%s': %s", keyid, isc_result_totext(ret));
+
+ if (secret != NULL) {
+ isc_mem_put(mctx, secret, secretalloc);
+ }
+ return (ret);
+}
+
+isc_result_t
+named_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *keylist;
+ dns_tsig_keyring_t *ring = NULL;
+ isc_result_t result;
+ int i;
+
+ REQUIRE(ringp != NULL && *ringp == NULL);
+
+ i = 0;
+ if (config != NULL) {
+ maps[i++] = config;
+ }
+ if (vconfig != NULL) {
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ }
+ maps[i] = NULL;
+
+ result = dns_tsigkeyring_create(mctx, &ring);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ for (i = 0;; i++) {
+ if (maps[i] == NULL) {
+ break;
+ }
+ keylist = NULL;
+ result = cfg_map_get(maps[i], "key", &keylist);
+ if (result != ISC_R_SUCCESS) {
+ continue;
+ }
+ result = add_initial_keys(keylist, ring, mctx);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ }
+
+ *ringp = ring;
+ return (ISC_R_SUCCESS);
+
+failure:
+ dns_tsigkeyring_detach(&ring);
+ return (result);
+}
diff --git a/bin/named/xsl_p.h b/bin/named/xsl_p.h
new file mode 100644
index 0000000..5623534
--- /dev/null
+++ b/bin/named/xsl_p.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+extern const char xslmsg[];
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
new file mode 100644
index 0000000..44c2242
--- /dev/null
+++ b/bin/named/zoneconf.c
@@ -0,0 +1,2114 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/stats.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/ipkeylist.h>
+#include <dns/journal.h>
+#include <dns/kasp.h>
+#include <dns/log.h>
+#include <dns/masterdump.h>
+#include <dns/name.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatatype.h>
+#include <dns/sdlz.h>
+#include <dns/ssu.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <ns/client.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/zoneconf.h>
+
+/* ACLs associated with zone */
+typedef enum {
+ allow_notify,
+ allow_query,
+ allow_query_on,
+ allow_transfer,
+ allow_update,
+ allow_update_forwarding
+} acl_type_t;
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return ((_r)); \
+ } while (0)
+
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+/*%
+ * Convenience function for configuring a single zone ACL.
+ */
+static isc_result_t
+configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
+ const cfg_obj_t *config, acl_type_t acltype,
+ cfg_aclconfctx_t *actx, dns_zone_t *zone,
+ void (*setzacl)(dns_zone_t *, dns_acl_t *),
+ void (*clearzacl)(dns_zone_t *)) {
+ isc_result_t result;
+ const cfg_obj_t *maps[5] = { NULL, NULL, NULL, NULL, NULL };
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+ dns_acl_t **aclp = NULL, *acl = NULL;
+ const char *aclname;
+ dns_view_t *view;
+
+ view = dns_zone_getview(zone);
+
+ switch (acltype) {
+ case allow_notify:
+ if (view != NULL) {
+ aclp = &view->notifyacl;
+ }
+ aclname = "allow-notify";
+ break;
+ case allow_query:
+ if (view != NULL) {
+ aclp = &view->queryacl;
+ }
+ aclname = "allow-query";
+ break;
+ case allow_query_on:
+ if (view != NULL) {
+ aclp = &view->queryonacl;
+ }
+ aclname = "allow-query-on";
+ break;
+ case allow_transfer:
+ if (view != NULL) {
+ aclp = &view->transferacl;
+ }
+ aclname = "allow-transfer";
+ break;
+ case allow_update:
+ if (view != NULL) {
+ aclp = &view->updateacl;
+ }
+ aclname = "allow-update";
+ break;
+ case allow_update_forwarding:
+ if (view != NULL) {
+ aclp = &view->upfwdacl;
+ }
+ aclname = "allow-update-forwarding";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ /* First check to see if ACL is defined within the zone */
+ if (zconfig != NULL) {
+ maps[0] = cfg_tuple_get(zconfig, "options");
+ (void)named_config_get(maps, aclname, &aclobj);
+ if (aclobj != NULL) {
+ aclp = NULL;
+ goto parse_acl;
+ }
+ }
+
+ /* Failing that, see if there's a default ACL already in the view */
+ if (aclp != NULL && *aclp != NULL) {
+ (*setzacl)(zone, *aclp);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Check for default ACLs that haven't been parsed yet */
+ if (vconfig != NULL) {
+ const cfg_obj_t *options = cfg_tuple_get(vconfig, "options");
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ (void)named_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL) {
+ (*clearzacl)(zone);
+ return (ISC_R_SUCCESS);
+ }
+
+parse_acl:
+ result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx,
+ named_g_mctx, 0, &acl);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ (*setzacl)(zone, acl);
+
+ /* Set the view default now */
+ if (aclp != NULL) {
+ dns_acl_attach(acl, aclp);
+ }
+
+ dns_acl_detach(&acl);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Parse the zone update-policy statement.
+ */
+static isc_result_t
+configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ const char *zname) {
+ const cfg_obj_t *updatepolicy = NULL;
+ const cfg_listelt_t *element, *element2;
+ dns_ssutable_t *table = NULL;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ bool autoddns = false;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ (void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
+
+ if (updatepolicy == NULL) {
+ dns_zone_setssutable(zone, NULL);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (cfg_obj_isstring(updatepolicy) &&
+ strcmp("local", cfg_obj_asstring(updatepolicy)) == 0)
+ {
+ autoddns = true;
+ updatepolicy = NULL;
+ }
+
+ dns_ssutable_create(mctx, &table);
+
+ for (element = cfg_list_first(updatepolicy); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *stmt = cfg_listelt_value(element);
+ const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
+ const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
+ const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
+ const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
+ const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
+ const char *str;
+ bool grant = false;
+ bool usezone = false;
+ dns_ssumatchtype_t mtype = dns_ssumatchtype_name;
+ dns_fixedname_t fname, fident;
+ isc_buffer_t b;
+ dns_ssuruletype_t *types;
+ unsigned int i, n;
+
+ str = cfg_obj_asstring(mode);
+ if (strcasecmp(str, "grant") == 0) {
+ grant = true;
+ } else if (strcasecmp(str, "deny") == 0) {
+ grant = false;
+ } else {
+ UNREACHABLE();
+ }
+
+ str = cfg_obj_asstring(matchtype);
+ CHECK(dns_ssu_mtypefromstring(str, &mtype));
+ if (mtype == dns_ssumatchtype_subdomain &&
+ strcasecmp(str, "zonesub") == 0)
+ {
+ usezone = true;
+ }
+
+ dns_fixedname_init(&fident);
+ str = cfg_obj_asstring(identity);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
+ dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, named_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+
+ dns_fixedname_init(&fname);
+ if (usezone) {
+ dns_name_copy(dns_zone_getorigin(zone),
+ dns_fixedname_name(&fname));
+ } else {
+ str = cfg_obj_asstring(dname);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fname),
+ &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, named_g_lctx,
+ ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+ }
+
+ n = named_config_listcount(typelist);
+ if (n == 0) {
+ types = NULL;
+ } else {
+ types = isc_mem_get(mctx, n * sizeof(*types));
+ }
+
+ i = 0;
+ for (element2 = cfg_list_first(typelist); element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *typeobj;
+ const char *bracket;
+ isc_textregion_t r;
+ unsigned long max = 0;
+
+ INSIST(i < n);
+
+ typeobj = cfg_listelt_value(element2);
+ str = cfg_obj_asstring(typeobj);
+ DE_CONST(str, r.base);
+
+ bracket = strchr(str, '(' /*)*/);
+ if (bracket != NULL) {
+ char *end = NULL;
+ r.length = bracket - str;
+ max = strtoul(bracket + 1, &end, 10);
+ if (max > 0xffff || end[0] != /*(*/ ')' ||
+ end[1] != 0)
+ {
+ cfg_obj_log(identity, named_g_lctx,
+ ISC_LOG_ERROR,
+ "'%s' is not a valid count",
+ bracket);
+ isc_mem_put(mctx, types,
+ n * sizeof(*types));
+ goto cleanup;
+ }
+ } else {
+ r.length = strlen(str);
+ }
+ types[i].max = max;
+
+ result = dns_rdatatype_fromtext(&types[i++].type, &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, named_g_lctx,
+ ISC_LOG_ERROR,
+ "'%.*s' is not a valid type",
+ (int)r.length, str);
+ isc_mem_put(mctx, types, n * sizeof(*types));
+ goto cleanup;
+ }
+ }
+ INSIST(i == n);
+
+ dns_ssutable_addrule(table, grant, dns_fixedname_name(&fident),
+ mtype, dns_fixedname_name(&fname), n,
+ types);
+ if (types != NULL) {
+ isc_mem_put(mctx, types, n * sizeof(*types));
+ }
+ }
+
+ /*
+ * If "update-policy local;" and a session key exists,
+ * then use the default policy, which is equivalent to:
+ * update-policy { grant <session-keyname> zonesub any; };
+ */
+ if (autoddns) {
+ dns_ssuruletype_t any = { dns_rdatatype_any, 0 };
+
+ if (named_g_server->session_keyname == NULL) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed to enable auto DDNS policy "
+ "for zone %s: session key not found",
+ zname);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+
+ dns_ssutable_addrule(table, true,
+ named_g_server->session_keyname,
+ dns_ssumatchtype_local,
+ dns_zone_getorigin(zone), 1, &any);
+ }
+
+ dns_zone_setssutable(zone, table);
+
+cleanup:
+ dns_ssutable_detach(&table);
+ return (result);
+}
+
+/*
+ * This is the TTL used for internally generated RRsets for static-stub zones.
+ * The value doesn't matter because the mapping is static, but needs to be
+ * defined for the sake of implementation.
+ */
+#define STATICSTUB_SERVER_TTL 86400
+
+/*%
+ * Configure an apex NS with glues for a static-stub zone.
+ * For example, for the zone named "example.com", the following RRs will be
+ * added to the zone DB:
+ * example.com. NS example.com.
+ * example.com. A 192.0.2.1
+ * example.com. AAAA 2001:db8::1
+ */
+static isc_result_t
+configure_staticstub_serveraddrs(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ dns_rdatalist_t *rdatalist_ns,
+ dns_rdatalist_t *rdatalist_a,
+ dns_rdatalist_t *rdatalist_aaaa) {
+ const cfg_listelt_t *element;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ isc_region_t region, sregion;
+ dns_rdata_t *rdata;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (element = cfg_list_first(zconfig); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const isc_sockaddr_t *sa;
+ isc_netaddr_t na;
+ const cfg_obj_t *address = cfg_listelt_value(element);
+ dns_rdatalist_t *rdatalist;
+
+ sa = cfg_obj_assockaddr(address);
+ if (isc_sockaddr_getport(sa) != 0) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "port is not configurable for "
+ "static stub server-addresses");
+ return (ISC_R_FAILURE);
+ }
+ isc_netaddr_fromsockaddr(&na, sa);
+ if (isc_netaddr_getzone(&na) != 0) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "scoped address is not allowed "
+ "for static stub "
+ "server-addresses");
+ return (ISC_R_FAILURE);
+ }
+
+ switch (na.family) {
+ case AF_INET:
+ region.length = sizeof(na.type.in);
+ rdatalist = rdatalist_a;
+ break;
+ default:
+ INSIST(na.family == AF_INET6);
+ region.length = sizeof(na.type.in6);
+ rdatalist = rdatalist_aaaa;
+ break;
+ }
+
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + region.length);
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, &na.type, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
+ rdatalist->type, &region);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ }
+
+ /*
+ * If no address is specified (unlikely in this context, but possible),
+ * there's nothing to do anymore.
+ */
+ if (ISC_LIST_EMPTY(rdatalist_a->rdata) &&
+ ISC_LIST_EMPTY(rdatalist_aaaa->rdata))
+ {
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Add to the list an apex NS with the ns name being the origin name */
+ dns_name_toregion(dns_zone_getorigin(zone), &sregion);
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
+ region.length = sregion.length;
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, sregion.base, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone), dns_rdatatype_ns,
+ &region);
+ ISC_LIST_APPEND(rdatalist_ns->rdata, rdata, link);
+
+ return (result);
+}
+
+/*%
+ * Configure an apex NS with an out-of-zone NS names for a static-stub zone.
+ * For example, for the zone named "example.com", something like the following
+ * RRs will be added to the zone DB:
+ * example.com. NS ns.example.net.
+ */
+static isc_result_t
+configure_staticstub_servernames(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ dns_rdatalist_t *rdatalist,
+ const char *zname) {
+ const cfg_listelt_t *element;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_rdata_t *rdata;
+ isc_region_t sregion, region;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (element = cfg_list_first(zconfig); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *obj;
+ const char *str;
+ dns_fixedname_t fixed_name;
+ dns_name_t *nsname;
+ isc_buffer_t b;
+
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+
+ nsname = dns_fixedname_initname(&fixed_name);
+
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(nsname, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "server-name '%s' is not a valid "
+ "name",
+ str);
+ return (result);
+ }
+ if (dns_name_issubdomain(nsname, dns_zone_getorigin(zone))) {
+ cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
+ "server-name '%s' must not be a "
+ "subdomain of zone name '%s'",
+ str, zname);
+ return (ISC_R_FAILURE);
+ }
+
+ dns_name_toregion(nsname, &sregion);
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
+ region.length = sregion.length;
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, sregion.base, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
+ dns_rdatatype_ns, &region);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ }
+
+ return (result);
+}
+
+/*%
+ * Configure static-stub zone.
+ */
+static isc_result_t
+configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ const char *zname, const char *dbtype) {
+ int i = 0;
+ const cfg_obj_t *obj;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_db_t *db = NULL;
+ dns_dbversion_t *dbversion = NULL;
+ dns_dbnode_t *apexnode = NULL;
+ dns_name_t apexname;
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdatalist_t rdatalist_ns, rdatalist_a, rdatalist_aaaa;
+ dns_rdatalist_t *rdatalists[] = { &rdatalist_ns, &rdatalist_a,
+ &rdatalist_aaaa, NULL };
+ dns_rdata_t *rdata;
+ isc_region_t region;
+
+ /* Create the DB beforehand */
+ RETERR(dns_db_create(mctx, dbtype, dns_zone_getorigin(zone),
+ dns_dbtype_stub, dns_zone_getclass(zone), 0, NULL,
+ &db));
+
+ dns_rdataset_init(&rdataset);
+
+ dns_rdatalist_init(&rdatalist_ns);
+ rdatalist_ns.rdclass = dns_zone_getclass(zone);
+ rdatalist_ns.type = dns_rdatatype_ns;
+ rdatalist_ns.ttl = STATICSTUB_SERVER_TTL;
+
+ dns_rdatalist_init(&rdatalist_a);
+ rdatalist_a.rdclass = dns_zone_getclass(zone);
+ rdatalist_a.type = dns_rdatatype_a;
+ rdatalist_a.ttl = STATICSTUB_SERVER_TTL;
+
+ dns_rdatalist_init(&rdatalist_aaaa);
+ rdatalist_aaaa.rdclass = dns_zone_getclass(zone);
+ rdatalist_aaaa.type = dns_rdatatype_aaaa;
+ rdatalist_aaaa.ttl = STATICSTUB_SERVER_TTL;
+
+ /* Prepare zone RRs from the configuration */
+ obj = NULL;
+ result = cfg_map_get(zconfig, "server-addresses", &obj);
+ if (result == ISC_R_SUCCESS) {
+ INSIST(obj != NULL);
+ CHECK(configure_staticstub_serveraddrs(obj, zone, &rdatalist_ns,
+ &rdatalist_a,
+ &rdatalist_aaaa));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zconfig, "server-names", &obj);
+ if (result == ISC_R_SUCCESS) {
+ INSIST(obj != NULL);
+ CHECK(configure_staticstub_servernames(obj, zone, &rdatalist_ns,
+ zname));
+ }
+
+ /*
+ * Sanity check: there should be at least one NS RR at the zone apex
+ * to trigger delegation.
+ */
+ if (ISC_LIST_EMPTY(rdatalist_ns.rdata)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "No NS record is configured for a "
+ "static-stub zone '%s'",
+ zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * Now add NS and glue A/AAAA RRsets to the zone DB.
+ * First open a new version for the add operation and get a pointer
+ * to the apex node (all RRs are of the apex name).
+ */
+ CHECK(dns_db_newversion(db, &dbversion));
+
+ dns_name_init(&apexname, NULL);
+ dns_name_clone(dns_zone_getorigin(zone), &apexname);
+ CHECK(dns_db_findnode(db, &apexname, false, &apexnode));
+
+ /* Add NS RRset */
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_ns, &rdataset) ==
+ ISC_R_SUCCESS);
+ CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset, 0,
+ NULL));
+ dns_rdataset_disassociate(&rdataset);
+
+ /* Add glue A RRset, if any */
+ if (!ISC_LIST_EMPTY(rdatalist_a.rdata)) {
+ RUNTIME_CHECK(
+ dns_rdatalist_tordataset(&rdatalist_a, &rdataset) ==
+ ISC_R_SUCCESS);
+ CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
+ 0, NULL));
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ /* Add glue AAAA RRset, if any */
+ if (!ISC_LIST_EMPTY(rdatalist_aaaa.rdata)) {
+ RUNTIME_CHECK(
+ dns_rdatalist_tordataset(&rdatalist_aaaa, &rdataset) ==
+ ISC_R_SUCCESS);
+ CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
+ 0, NULL));
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ dns_db_closeversion(db, &dbversion, true);
+ dns_zone_setdb(zone, db);
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (dns_rdataset_isassociated(&rdataset)) {
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (apexnode != NULL) {
+ dns_db_detachnode(db, &apexnode);
+ }
+ if (dbversion != NULL) {
+ dns_db_closeversion(db, &dbversion, false);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+ for (i = 0; rdatalists[i] != NULL; i++) {
+ while ((rdata = ISC_LIST_HEAD(rdatalists[i]->rdata)) != NULL) {
+ ISC_LIST_UNLINK(rdatalists[i]->rdata, rdata, link);
+ dns_rdata_toregion(rdata, &region);
+ isc_mem_put(mctx, rdata,
+ sizeof(*rdata) + region.length);
+ }
+ }
+
+ INSIST(dbversion == NULL);
+
+ return (result);
+}
+
+/*%
+ * Convert a config file zone type into a server zone type.
+ */
+static dns_zonetype_t
+zonetype_fromconfig(const cfg_obj_t *map) {
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = cfg_map_get(map, "type", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ return (named_config_getzonetype(obj));
+}
+
+/*%
+ * Helper function for strtoargv(). Pardon the gratuitous recursion.
+ */
+static isc_result_t
+strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp,
+ unsigned int n) {
+ isc_result_t result;
+
+ /* Discard leading whitespace. */
+ while (*s == ' ' || *s == '\t') {
+ s++;
+ }
+
+ if (*s == '\0') {
+ /* We have reached the end of the string. */
+ *argcp = n;
+ *argvp = isc_mem_get(mctx, n * sizeof(char *));
+ } else {
+ char *p = s;
+ while (*p != ' ' && *p != '\t' && *p != '\0') {
+ p++;
+ }
+ if (*p != '\0') {
+ *p++ = '\0';
+ }
+
+ result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ (*argvp)[n] = s;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Tokenize the string "s" into whitespace-separated words,
+ * return the number of words in '*argcp' and an array
+ * of pointers to the words in '*argvp'. The caller
+ * must free the array using isc_mem_put(). The string
+ * is modified in-place.
+ */
+static isc_result_t
+strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
+ return (strtoargvsub(mctx, s, argcp, argvp, 0));
+}
+
+static const char *const primary_synonyms[] = { "primary", "master", NULL };
+
+static const char *const secondary_synonyms[] = { "secondary", "slave", NULL };
+
+static void
+checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
+ const cfg_obj_t **objp) {
+ isc_result_t result;
+
+ switch (ztype) {
+ case dns_zone_secondary:
+ case dns_zone_mirror:
+ result = named_checknames_get(maps, secondary_synonyms, objp);
+ break;
+ case dns_zone_primary:
+ result = named_checknames_get(maps, primary_synonyms, objp);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ INSIST(result == ISC_R_SUCCESS && objp != NULL && *objp != NULL);
+}
+
+/*
+ * Callback to see if a non-recursive query coming from 'srcaddr' to
+ * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
+ * delivered to 'myview'.
+ *
+ * We run this unlocked as both the view list and the interface list
+ * are updated when the appropriate task has exclusivity.
+ */
+static bool
+isself(dns_view_t *myview, dns_tsigkey_t *mykey, const isc_sockaddr_t *srcaddr,
+ const isc_sockaddr_t *dstaddr, dns_rdataclass_t rdclass, void *arg) {
+ dns_aclenv_t *env = NULL;
+ dns_view_t *view = NULL;
+ dns_tsigkey_t *key = NULL;
+ isc_netaddr_t netsrc;
+ isc_netaddr_t netdst;
+
+ UNUSED(arg);
+
+ /* interfacemgr can be destroyed only in exclusive mode. */
+ if (named_g_server->interfacemgr == NULL) {
+ return (true);
+ }
+
+ if (!ns_interfacemgr_listeningon(named_g_server->interfacemgr, dstaddr))
+ {
+ return (false);
+ }
+
+ isc_netaddr_fromsockaddr(&netsrc, srcaddr);
+ isc_netaddr_fromsockaddr(&netdst, dstaddr);
+ env = ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
+
+ for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ const dns_name_t *tsig = NULL;
+
+ if (view->matchrecursiveonly) {
+ continue;
+ }
+
+ if (rdclass != view->rdclass) {
+ continue;
+ }
+
+ if (mykey != NULL) {
+ bool match;
+ isc_result_t result;
+
+ result = dns_view_gettsig(view, &mykey->name, &key);
+ if (result != ISC_R_SUCCESS) {
+ continue;
+ }
+ match = dst_key_compare(mykey->key, key->key);
+ dns_tsigkey_detach(&key);
+ if (!match) {
+ continue;
+ }
+ tsig = dns_tsigkey_identity(mykey);
+ }
+
+ if (dns_acl_allowed(&netsrc, tsig, view->matchclients, env) &&
+ dns_acl_allowed(&netdst, tsig, view->matchdestinations,
+ env))
+ {
+ break;
+ }
+ }
+ return (view == myview);
+}
+
+/*%
+ * For mirror zones, change "notify yes;" to "notify explicit;", informing the
+ * user only if "notify" was explicitly configured rather than inherited from
+ * default configuration.
+ */
+static dns_notifytype_t
+process_notifytype(dns_notifytype_t ntype, dns_zonetype_t ztype,
+ const char *zname, const cfg_obj_t **maps) {
+ const cfg_obj_t *obj = NULL;
+
+ /*
+ * Return the original setting if this is not a mirror zone or if the
+ * zone is configured with something else than "notify yes;".
+ */
+ if (ztype != dns_zone_mirror || ntype != dns_notifytype_yes) {
+ return (ntype);
+ }
+
+ /*
+ * Only log a message if "notify" was set in the configuration
+ * hierarchy supplied in 'maps'.
+ */
+ if (named_config_get(maps, "notify", &obj) == ISC_R_SUCCESS) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO,
+ "'notify explicit;' will be used for mirror zone "
+ "'%s'",
+ zname);
+ }
+
+ return (dns_notifytype_explicit);
+}
+
+isc_result_t
+named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_kasplist_t *kasplist, dns_zone_t *zone,
+ dns_zone_t *raw) {
+ isc_result_t result;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ dns_rdataclass_t vclass;
+ const cfg_obj_t *maps[5];
+ const cfg_obj_t *nodefault[4];
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj;
+ const char *filename = NULL;
+ const char *kaspname = NULL;
+ const char *dupcheck;
+ dns_notifytype_t notifytype = dns_notifytype_yes;
+ uint32_t count;
+ unsigned int dbargc;
+ char **dbargv;
+ static char default_dbtype[] = "rbt";
+ static char dlz_dbtype[] = "dlz";
+ char *cpval = default_dbtype;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_dialuptype_t dialup = dns_dialuptype_no;
+ dns_zonetype_t ztype;
+ int i;
+ int32_t journal_size;
+ bool multi;
+ bool alt;
+ dns_view_t *view = NULL;
+ dns_kasp_t *kasp = NULL;
+ bool check = false, fail = false;
+ bool warn = false, ignore = false;
+ bool ixfrdiff;
+ bool use_kasp = false;
+ dns_masterformat_t masterformat;
+ const dns_master_style_t *masterstyle = &dns_master_style_default;
+ isc_stats_t *zoneqrystats;
+ dns_stats_t *rcvquerystats;
+ dns_stats_t *dnssecsignstats;
+ dns_zonestat_level_t statlevel = dns_zonestat_none;
+ int seconds;
+ dns_ttl_t maxttl = 0; /* unlimited */
+ dns_zone_t *mayberaw = (raw != NULL) ? raw : zone;
+ bool transferinsecs = ns_server_getoption(named_g_server->sctx,
+ NS_SERVER_TRANSFERINSECS);
+
+ i = 0;
+ if (zconfig != NULL) {
+ zoptions = cfg_tuple_get(zconfig, "options");
+ nodefault[i] = maps[i] = zoptions;
+ i++;
+ }
+ if (vconfig != NULL) {
+ nodefault[i] = maps[i] = cfg_tuple_get(vconfig, "options");
+ i++;
+ }
+ if (config != NULL) {
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ nodefault[i] = maps[i] = options;
+ i++;
+ }
+ }
+ nodefault[i] = NULL;
+ maps[i++] = named_g_defaults;
+ maps[i] = NULL;
+
+ if (vconfig != NULL) {
+ CHECK(named_config_getclass(cfg_tuple_get(vconfig, "class"),
+ dns_rdataclass_in, &vclass));
+ } else {
+ vclass = dns_rdataclass_in;
+ }
+
+ /*
+ * Configure values common to all zone types.
+ */
+
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+
+ CHECK(named_config_getclass(cfg_tuple_get(zconfig, "class"), vclass,
+ &zclass));
+ dns_zone_setclass(zone, zclass);
+ if (raw != NULL) {
+ dns_zone_setclass(raw, zclass);
+ }
+
+ ztype = zonetype_fromconfig(zoptions);
+ if (raw != NULL) {
+ dns_zone_settype(raw, ztype);
+ dns_zone_settype(zone, dns_zone_primary);
+ } else {
+ dns_zone_settype(zone, ztype);
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "database", &obj);
+ if (result == ISC_R_SUCCESS) {
+ cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+ }
+ if (cpval == NULL) {
+ CHECK(ISC_R_NOMEMORY);
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "dlz", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *dlzname = cfg_obj_asstring(obj);
+ size_t len;
+
+ if (cpval != default_dbtype) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': both 'database' and 'dlz' "
+ "specified",
+ zname);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ len = strlen(dlzname) + 5;
+ cpval = isc_mem_allocate(mctx, len);
+ snprintf(cpval, len, "dlz %s", dlzname);
+ }
+
+ result = strtoargv(mctx, cpval, &dbargc, &dbargv);
+ if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
+ isc_mem_free(mctx, cpval);
+ CHECK(result);
+ }
+
+ /*
+ * ANSI C is strange here. There is no logical reason why (char **)
+ * cannot be promoted automatically to (const char * const *) by the
+ * compiler w/o generating a warning.
+ */
+ dns_zone_setdbtype(zone, dbargc, (const char *const *)dbargv);
+ isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
+ if (cpval != default_dbtype && cpval != dlz_dbtype) {
+ isc_mem_free(mctx, cpval);
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "file", &obj);
+ if (result == ISC_R_SUCCESS) {
+ filename = cfg_obj_asstring(obj);
+ }
+
+ /*
+ * Unless we're using some alternative database, a primary zone
+ * will be needing a master file.
+ */
+ if (ztype == dns_zone_primary && cpval == default_dbtype &&
+ filename == NULL)
+ {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'file' not specified", zname);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) {
+ masterformat = dns_masterformat_raw;
+ } else {
+ masterformat = dns_masterformat_text;
+ }
+ obj = NULL;
+ result = named_config_get(maps, "masterfile-format", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *masterformatstr = cfg_obj_asstring(obj);
+
+ if (strcasecmp(masterformatstr, "text") == 0) {
+ masterformat = dns_masterformat_text;
+ } else if (strcasecmp(masterformatstr, "raw") == 0) {
+ masterformat = dns_masterformat_raw;
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "masterfile-style", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *masterstylestr = cfg_obj_asstring(obj);
+
+ if (masterformat != dns_masterformat_text) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "zone '%s': 'masterfile-style' "
+ "can only be used with "
+ "'masterfile-format text'",
+ zname);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(masterstylestr, "full") == 0) {
+ masterstyle = &dns_master_style_full;
+ } else if (strcasecmp(masterstylestr, "relative") == 0) {
+ masterstyle = &dns_master_style_default;
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "max-records", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj));
+ if (zone != mayberaw) {
+ dns_zone_setmaxrecords(zone, 0);
+ }
+
+ if (raw != NULL && filename != NULL) {
+#define SIGNED ".signed"
+ size_t signedlen = strlen(filename) + sizeof(SIGNED);
+ char *signedname;
+
+ CHECK(dns_zone_setfile(raw, filename, masterformat,
+ masterstyle));
+ signedname = isc_mem_get(mctx, signedlen);
+
+ (void)snprintf(signedname, signedlen, "%s" SIGNED, filename);
+ result = dns_zone_setfile(zone, signedname,
+ dns_masterformat_raw, NULL);
+ isc_mem_put(mctx, signedname, signedlen);
+ CHECK(result);
+ } else {
+ CHECK(dns_zone_setfile(zone, filename, masterformat,
+ masterstyle));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "journal", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECK(dns_zone_setjournal(mayberaw, cfg_obj_asstring(obj)));
+ }
+
+ /*
+ * Notify messages are processed by the raw zone if it exists.
+ */
+ if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) {
+ CHECK(configure_zone_acl(zconfig, vconfig, config, allow_notify,
+ ac, mayberaw, dns_zone_setnotifyacl,
+ dns_zone_clearnotifyacl));
+ }
+
+ /*
+ * XXXAG This probably does not make sense for stubs.
+ */
+ CHECK(configure_zone_acl(zconfig, vconfig, config, allow_query, ac,
+ zone, dns_zone_setqueryacl,
+ dns_zone_clearqueryacl));
+
+ CHECK(configure_zone_acl(zconfig, vconfig, config, allow_query_on, ac,
+ zone, dns_zone_setqueryonacl,
+ dns_zone_clearqueryonacl));
+
+ obj = NULL;
+ result = named_config_get(maps, "dialup", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ dialup = dns_dialuptype_yes;
+ } else {
+ dialup = dns_dialuptype_no;
+ }
+ } else {
+ const char *dialupstr = cfg_obj_asstring(obj);
+ if (strcasecmp(dialupstr, "notify") == 0) {
+ dialup = dns_dialuptype_notify;
+ } else if (strcasecmp(dialupstr, "notify-passive") == 0) {
+ dialup = dns_dialuptype_notifypassive;
+ } else if (strcasecmp(dialupstr, "refresh") == 0) {
+ dialup = dns_dialuptype_refresh;
+ } else if (strcasecmp(dialupstr, "passive") == 0) {
+ dialup = dns_dialuptype_passive;
+ } else {
+ UNREACHABLE();
+ }
+ }
+ if (raw != NULL) {
+ dns_zone_setdialup(raw, dialup);
+ }
+ dns_zone_setdialup(zone, dialup);
+
+ obj = NULL;
+ result = named_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ statlevel = dns_zonestat_full;
+ } else {
+ statlevel = dns_zonestat_none;
+ }
+ } else {
+ const char *levelstr = cfg_obj_asstring(obj);
+ if (strcasecmp(levelstr, "full") == 0) {
+ statlevel = dns_zonestat_full;
+ } else if (strcasecmp(levelstr, "terse") == 0) {
+ statlevel = dns_zonestat_terse;
+ } else if (strcasecmp(levelstr, "none") == 0) {
+ statlevel = dns_zonestat_none;
+ } else {
+ UNREACHABLE();
+ }
+ }
+ dns_zone_setstatlevel(zone, statlevel);
+
+ zoneqrystats = NULL;
+ rcvquerystats = NULL;
+ dnssecsignstats = NULL;
+ if (statlevel == dns_zonestat_full) {
+ CHECK(isc_stats_create(mctx, &zoneqrystats,
+ ns_statscounter_max));
+ CHECK(dns_rdatatypestats_create(mctx, &rcvquerystats));
+ CHECK(dns_dnssecsignstats_create(mctx, &dnssecsignstats));
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ dns_zone_setrcvquerystats(zone, rcvquerystats);
+ dns_zone_setdnssecsignstats(zone, dnssecsignstats);
+
+ if (zoneqrystats != NULL) {
+ isc_stats_detach(&zoneqrystats);
+ }
+
+ if (rcvquerystats != NULL) {
+ dns_stats_detach(&rcvquerystats);
+ }
+
+ if (dnssecsignstats != NULL) {
+ dns_stats_detach(&dnssecsignstats);
+ }
+
+ /*
+ * Configure authoritative zone functionality. This applies
+ * to primary servers (type "primary") and secondaries
+ * acting as primaries (type "secondary"), but not to stubs.
+ */
+ if (ztype != dns_zone_stub && ztype != dns_zone_staticstub &&
+ ztype != dns_zone_redirect)
+ {
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-policy", &obj);
+ if (result == ISC_R_SUCCESS) {
+ kaspname = cfg_obj_asstring(obj);
+ if (strcmp(kaspname, "none") != 0) {
+ result = dns_kasplist_find(kasplist, kaspname,
+ &kasp);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(
+ obj, named_g_lctx,
+ ISC_LOG_ERROR,
+ "dnssec-policy '%s' not found ",
+ kaspname);
+ CHECK(result);
+ }
+ dns_zone_setkasp(zone, kasp);
+ use_kasp = true;
+ }
+ }
+ if (!use_kasp) {
+ dns_zone_setkasp(zone, NULL);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "notify", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj)) {
+ notifytype = dns_notifytype_yes;
+ } else {
+ notifytype = dns_notifytype_no;
+ }
+ } else {
+ const char *str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "explicit") == 0) {
+ notifytype = dns_notifytype_explicit;
+ } else if (strcasecmp(str, "master-only") == 0 ||
+ strcasecmp(str, "primary-only") == 0)
+ {
+ notifytype = dns_notifytype_masteronly;
+ } else {
+ UNREACHABLE();
+ }
+ }
+ notifytype = process_notifytype(notifytype, ztype, zname,
+ nodefault);
+ if (raw != NULL) {
+ dns_zone_setnotifytype(raw, dns_notifytype_no);
+ }
+ dns_zone_setnotifytype(zone, notifytype);
+
+ obj = NULL;
+ result = named_config_get(maps, "also-notify", &obj);
+ if (result == ISC_R_SUCCESS &&
+ (notifytype == dns_notifytype_yes ||
+ notifytype == dns_notifytype_explicit ||
+ (notifytype == dns_notifytype_masteronly &&
+ ztype == dns_zone_primary)))
+ {
+ dns_ipkeylist_t ipkl;
+ dns_ipkeylist_init(&ipkl);
+
+ CHECK(named_config_getipandkeylist(config, "primaries",
+ obj, mctx, &ipkl));
+ dns_zone_setalsonotify(zone, ipkl.addrs, ipkl.keys,
+ ipkl.tlss, ipkl.count);
+ dns_ipkeylist_clear(mctx, &ipkl);
+ } else {
+ dns_zone_setalsonotify(zone, NULL, NULL, NULL, 0);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "parental-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+
+ CHECK(dns_zone_setparentalsrc4(zone, cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "parental-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+
+ CHECK(dns_zone_setparentalsrc6(zone, cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "notify-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "notify-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "notify-to-soa", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOTIFYTOSOA,
+ cfg_obj_asboolean(obj));
+
+ dns_zone_setisself(zone, isself, NULL);
+
+ CHECK(configure_zone_acl(
+ zconfig, vconfig, config, allow_transfer, ac, zone,
+ dns_zone_setxfracl, dns_zone_clearxfracl));
+
+ obj = NULL;
+ result = named_config_get(maps, "max-transfer-time-out", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxxfrout(
+ zone, transferinsecs ? cfg_obj_asuint32(obj)
+ : cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-transfer-idle-out", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setidleout(zone, transferinsecs
+ ? cfg_obj_asuint32(obj)
+ : cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-journal-size", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (raw != NULL) {
+ dns_zone_setjournalsize(raw, -1);
+ }
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "unlimited") == 0) {
+ journal_size = DNS_JOURNAL_SIZE_MAX;
+ } else {
+ INSIST(strcasecmp(str, "default") == 0);
+ journal_size = -1;
+ }
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > DNS_JOURNAL_SIZE_MAX) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" PRId64 "' "
+ "is too large",
+ value);
+ CHECK(ISC_R_RANGE);
+ }
+ journal_size = (uint32_t)value;
+ }
+ if (raw != NULL) {
+ dns_zone_setjournalsize(raw, journal_size);
+ }
+ dns_zone_setjournalsize(zone, journal_size);
+
+ obj = NULL;
+ result = named_config_get(maps, "ixfr-from-differences", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj)) {
+ ixfrdiff = cfg_obj_asboolean(obj);
+ } else if ((strcasecmp(cfg_obj_asstring(obj), "primary") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "master") == 0) &&
+ ztype == dns_zone_primary)
+ {
+ ixfrdiff = true;
+ } else if ((strcasecmp(cfg_obj_asstring(obj), "secondary") ==
+ 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "slave") == 0) &&
+ ztype == dns_zone_secondary)
+ {
+ ixfrdiff = true;
+ } else {
+ ixfrdiff = false;
+ }
+ if (raw != NULL) {
+ dns_zone_setoption(raw, DNS_ZONEOPT_IXFRFROMDIFFS,
+ true);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
+ false);
+ } else {
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
+ ixfrdiff);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "max-ixfr-ratio", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isstring(obj)) {
+ dns_zone_setixfrratio(zone, 0);
+ } else {
+ dns_zone_setixfrratio(zone, cfg_obj_aspercentage(obj));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "request-expire", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setrequestexpire(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "request-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setrequestixfr(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ checknames(ztype, maps, &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = false;
+ } else {
+ UNREACHABLE();
+ }
+ if (raw != NULL) {
+ dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMES, check);
+ dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMESFAIL,
+ fail);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, false);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL,
+ false);
+ } else {
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL,
+ fail);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "notify-delay", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "check-sibling", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSIBLING,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "check-spf", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ check = false;
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSPF, check);
+
+ obj = NULL;
+ result = named_config_get(maps, "zero-no-soa-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "nsec3-test-zone", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE,
+ cfg_obj_asboolean(obj));
+ } else if (ztype == dns_zone_redirect) {
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-journal-size", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "unlimited") == 0) {
+ journal_size = DNS_JOURNAL_SIZE_MAX;
+ } else {
+ INSIST(strcasecmp(str, "default") == 0);
+ journal_size = -1;
+ }
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > DNS_JOURNAL_SIZE_MAX) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" PRId64 "' "
+ "is too large",
+ value);
+ CHECK(ISC_R_RANGE);
+ }
+ journal_size = (uint32_t)value;
+ }
+ dns_zone_setjournalsize(zone, journal_size);
+ }
+
+ if (use_kasp) {
+ maxttl = dns_kasp_zonemaxttl(dns_zone_getkasp(zone), false);
+ } else {
+ obj = NULL;
+ result = named_config_get(maps, "max-zone-ttl", &obj);
+ if (result == ISC_R_SUCCESS) {
+ if (cfg_obj_isduration(obj)) {
+ maxttl = cfg_obj_asduration(obj);
+ }
+ }
+ }
+ dns_zone_setmaxttl(zone, maxttl);
+ if (raw != NULL) {
+ dns_zone_setmaxttl(raw, maxttl);
+ }
+
+ /*
+ * Configure update-related options. These apply to
+ * primary servers only.
+ */
+ if (ztype == dns_zone_primary) {
+ dns_acl_t *updateacl;
+
+ CHECK(configure_zone_acl(zconfig, vconfig, config, allow_update,
+ ac, mayberaw, dns_zone_setupdateacl,
+ dns_zone_clearupdateacl));
+
+ updateacl = dns_zone_getupdateacl(mayberaw);
+ if (updateacl != NULL && dns_acl_isinsecure(updateacl)) {
+ isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "zone '%s' allows unsigned updates "
+ "from remote hosts, which is insecure",
+ zname);
+ }
+
+ CHECK(configure_zone_ssutable(zoptions, mayberaw, zname));
+ }
+
+ /*
+ * Configure DNSSEC signing. These apply to primary zones or zones that
+ * use inline-signing (raw != NULL).
+ */
+ if (ztype == dns_zone_primary || raw != NULL) {
+ const cfg_obj_t *validity, *resign;
+ bool allow = false, maint = false;
+ bool sigvalinsecs;
+
+ if (use_kasp) {
+ if (dns_kasp_nsec3(kasp)) {
+ result = dns_zone_setnsec3param(
+ zone, 1, dns_kasp_nsec3flags(kasp),
+ dns_kasp_nsec3iter(kasp),
+ dns_kasp_nsec3saltlen(kasp), NULL, true,
+ false);
+ } else {
+ result = dns_zone_setnsec3param(
+ zone, 0, 0, 0, 0, NULL, true, false);
+ }
+ INSIST(result == ISC_R_SUCCESS);
+ }
+
+ if (use_kasp) {
+ seconds = (uint32_t)dns_kasp_sigvalidity_dnskey(kasp);
+ } else {
+ obj = NULL;
+ result = named_config_get(maps, "dnskey-sig-validity",
+ &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ seconds = cfg_obj_asuint32(obj) * 86400;
+ }
+ dns_zone_setkeyvalidityinterval(zone, seconds);
+
+ if (use_kasp) {
+ seconds = (uint32_t)dns_kasp_sigvalidity(kasp);
+ dns_zone_setsigvalidityinterval(zone, seconds);
+ seconds = (uint32_t)dns_kasp_sigrefresh(kasp);
+ dns_zone_setsigresigninginterval(zone, seconds);
+ } else {
+ obj = NULL;
+ result = named_config_get(maps, "sig-validity-interval",
+ &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+
+ sigvalinsecs = ns_server_getoption(
+ named_g_server->sctx, NS_SERVER_SIGVALINSECS);
+ validity = cfg_tuple_get(obj, "validity");
+ seconds = cfg_obj_asuint32(validity);
+ if (!sigvalinsecs) {
+ seconds *= 86400;
+ }
+ dns_zone_setsigvalidityinterval(zone, seconds);
+
+ resign = cfg_tuple_get(obj, "re-sign");
+ if (cfg_obj_isvoid(resign)) {
+ seconds /= 4;
+ } else if (!sigvalinsecs) {
+ uint32_t r = cfg_obj_asuint32(resign);
+ if (seconds > 7 * 86400) {
+ seconds = r * 86400;
+ } else {
+ seconds = r * 3600;
+ }
+ } else {
+ seconds = cfg_obj_asuint32(resign);
+ }
+ dns_zone_setsigresigninginterval(zone, seconds);
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "key-directory", &obj);
+ if (result == ISC_R_SUCCESS) {
+ filename = cfg_obj_asstring(obj);
+ CHECK(dns_zone_setkeydirectory(zone, filename));
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "sig-signing-signatures", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setsignatures(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "sig-signing-nodes", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setnodes(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "sig-signing-type", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setprivatetype(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "update-check-ksk", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_UPDATECHECKKSK,
+ cfg_obj_asboolean(obj));
+ /*
+ * This setting will be ignored if dnssec-policy is used.
+ * named-checkconf will error if both are configured.
+ */
+
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-dnskey-kskonly", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_DNSKEYKSKONLY,
+ cfg_obj_asboolean(obj));
+ /*
+ * This setting will be ignored if dnssec-policy is used.
+ * named-checkconf will error if both are configured.
+ */
+
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-loadkeys-interval",
+ &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setrefreshkeyinterval(zone,
+ cfg_obj_asuint32(obj)));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "auto-dnssec", &obj);
+ if (kasp != NULL) {
+ bool s2i = (strcmp(dns_kasp_getname(kasp),
+ "insecure") != 0);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, true);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, !s2i);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, true);
+ } else if (result == ISC_R_SUCCESS) {
+ const char *arg = cfg_obj_asstring(obj);
+ if (strcasecmp(arg, "allow") == 0) {
+ allow = true;
+ } else if (strcasecmp(arg, "maintain") == 0) {
+ allow = maint = true;
+ } else if (strcasecmp(arg, "off") == 0) {
+ /* Default */
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint);
+ }
+ }
+
+ if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) {
+ CHECK(configure_zone_acl(zconfig, vconfig, config,
+ allow_update_forwarding, ac, mayberaw,
+ dns_zone_setforwardacl,
+ dns_zone_clearforwardacl));
+ }
+
+ /*%
+ * Configure parental agents, applies to primary and secondary zones.
+ */
+ if (ztype == dns_zone_primary || ztype == dns_zone_secondary) {
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "parental-agents", &obj);
+ if (obj != NULL) {
+ dns_ipkeylist_t ipkl;
+ dns_ipkeylist_init(&ipkl);
+ CHECK(named_config_getipandkeylist(
+ config, "parental-agents", obj, mctx, &ipkl));
+ dns_zone_setparentals(zone, ipkl.addrs, ipkl.keys,
+ ipkl.tlss, ipkl.count);
+ dns_ipkeylist_clear(mctx, &ipkl);
+ } else {
+ dns_zone_setparentals(zone, NULL, NULL, NULL, 0);
+ }
+ }
+
+ /*%
+ * Configure primary zone functionality.
+ */
+ if (ztype == dns_zone_primary) {
+ obj = NULL;
+ result = named_config_get(maps, "check-wildcard", &obj);
+ if (result == ISC_R_SUCCESS) {
+ check = cfg_obj_asboolean(obj);
+ } else {
+ check = false;
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKWILDCARD, check);
+
+ obj = NULL;
+ result = named_config_get(maps, "check-dup-records", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dupcheck = cfg_obj_asstring(obj);
+ if (strcasecmp(dupcheck, "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(dupcheck, "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(dupcheck, "ignore") == 0) {
+ fail = check = false;
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRR, check);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRRFAIL, fail);
+
+ obj = NULL;
+ result = named_config_get(maps, "check-mx", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = false;
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMX, check);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMXFAIL, fail);
+
+ obj = NULL;
+ result = named_config_get(maps, "check-integrity", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKINTEGRITY,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "check-mx-cname", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = true;
+ ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = true;
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNMXCNAME, warn);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNOREMXCNAME, ignore);
+
+ obj = NULL;
+ result = named_config_get(maps, "check-srv-cname", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = true;
+ ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = true;
+ } else {
+ UNREACHABLE();
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNSRVCNAME, warn);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNORESRVCNAME,
+ ignore);
+
+ obj = NULL;
+ result = named_config_get(maps, "dnssec-secure-to-insecure",
+ &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_SECURETOINSECURE,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "dnssec-update-mode", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *arg = cfg_obj_asstring(obj);
+ if (strcasecmp(arg, "no-resign") == 0) {
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_NORESIGN,
+ true);
+ } else if (strcasecmp(arg, "maintain") == 0) {
+ /* Default */
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ obj = NULL;
+ result = named_config_get(maps, "serial-update-method", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "unixtime") == 0) {
+ dns_zone_setserialupdatemethod(
+ zone, dns_updatemethod_unixtime);
+ } else if (strcasecmp(cfg_obj_asstring(obj), "date") == 0) {
+ dns_zone_setserialupdatemethod(zone,
+ dns_updatemethod_date);
+ } else {
+ dns_zone_setserialupdatemethod(
+ zone, dns_updatemethod_increment);
+ }
+ }
+
+ /*
+ * Configure secondary zone functionality.
+ */
+ switch (ztype) {
+ case dns_zone_mirror:
+ /*
+ * Disable outgoing zone transfers for mirror zones unless they
+ * are explicitly enabled by zone configuration.
+ */
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "allow-transfer", &obj);
+ if (obj == NULL) {
+ dns_acl_t *none;
+ CHECK(dns_acl_none(mctx, &none));
+ dns_zone_setxfracl(zone, none);
+ dns_acl_detach(&none);
+ }
+ FALLTHROUGH;
+ case dns_zone_secondary:
+ case dns_zone_stub:
+ case dns_zone_redirect:
+ count = 0;
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "primaries", &obj);
+ if (obj == NULL) {
+ (void)cfg_map_get(zoptions, "masters", &obj);
+ }
+
+ /*
+ * Use the built-in primary server list if one was not
+ * explicitly specified and this is a root zone mirror.
+ */
+ if (obj == NULL && ztype == dns_zone_mirror &&
+ dns_name_equal(dns_zone_getorigin(zone), dns_rootname))
+ {
+ result = named_config_getremotesdef(
+ named_g_config, "primaries",
+ DEFAULT_IANA_ROOT_ZONE_PRIMARIES, &obj);
+ CHECK(result);
+ }
+ if (obj != NULL) {
+ dns_ipkeylist_t ipkl;
+ dns_ipkeylist_init(&ipkl);
+
+ CHECK(named_config_getipandkeylist(config, "primaries",
+ obj, mctx, &ipkl));
+ dns_zone_setprimaries(mayberaw, ipkl.addrs, ipkl.keys,
+ ipkl.tlss, ipkl.count);
+ count = ipkl.count;
+ dns_ipkeylist_clear(mctx, &ipkl);
+ } else {
+ dns_zone_setprimaries(mayberaw, NULL, NULL, NULL, 0);
+ }
+
+ multi = false;
+ if (count > 1) {
+ obj = NULL;
+ result = named_config_get(maps, "multi-master", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ multi = cfg_obj_asboolean(obj);
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_MULTIMASTER, multi);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-transfer-time-in", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxxfrin(
+ mayberaw, transferinsecs ? cfg_obj_asuint32(obj)
+ : cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-transfer-idle-in", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setidlein(mayberaw,
+ transferinsecs ? cfg_obj_asuint32(obj)
+ : cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = named_config_get(maps, "max-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxrefreshtime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "min-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setminrefreshtime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "max-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxretrytime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "min-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setminretrytime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setxfrsource4(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setxfrsource6(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ named_add_reserved_dispatch(named_g_server,
+ cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = named_config_get(maps, "alt-transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setaltxfrsource4(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ obj = NULL;
+ result = named_config_get(maps, "alt-transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ CHECK(dns_zone_setaltxfrsource6(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ obj = NULL;
+ (void)named_config_get(maps, "use-alt-transfer-source", &obj);
+ if (obj == NULL) {
+ /*
+ * Default off when views are in use otherwise
+ * on for BIND 8 compatibility.
+ */
+ view = dns_zone_getview(zone);
+ if (view != NULL && strcmp(view->name, "_default") == 0)
+ {
+ alt = true;
+ } else {
+ alt = false;
+ }
+ } else {
+ alt = cfg_obj_asboolean(obj);
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_USEALTXFRSRC, alt);
+
+ obj = NULL;
+ (void)named_config_get(maps, "try-tcp-refresh", &obj);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_TRYTCPREFRESH,
+ cfg_obj_asboolean(obj));
+ break;
+
+ case dns_zone_staticstub:
+ CHECK(configure_staticstub(zoptions, zone, zname,
+ default_dbtype));
+ break;
+
+ default:
+ break;
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (kasp != NULL) {
+ dns_kasp_detach(&kasp);
+ }
+ return (result);
+}
+
+/*
+ * Set up a DLZ zone as writeable
+ */
+isc_result_t
+named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone,
+ dns_rdataclass_t rdclass, dns_name_t *name) {
+ dns_db_t *db = NULL;
+ isc_time_t now;
+ isc_result_t result;
+
+ TIME_NOW(&now);
+
+ dns_zone_settype(zone, dns_zone_dlz);
+ result = dns_sdlz_setdb(dlzdatabase, rdclass, name, &db);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ result = dns_zone_dlzpostload(zone, db);
+ dns_db_detach(&db);
+ return (result);
+}
+
+bool
+named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) {
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *cfilename;
+ const char *zfilename;
+ dns_zone_t *raw = NULL;
+ bool has_raw, inline_signing;
+ dns_zonetype_t ztype;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ /*
+ * We always reconfigure a static-stub zone for simplicity, assuming
+ * the amount of data to be loaded is small.
+ */
+ if (zonetype_fromconfig(zoptions) == dns_zone_staticstub) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: staticstub");
+ return (false);
+ }
+
+ /* If there's a raw zone, use that for filename and type comparison */
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ zfilename = dns_zone_getfile(raw);
+ ztype = dns_zone_gettype(raw);
+ dns_zone_detach(&raw);
+ has_raw = true;
+ } else {
+ zfilename = dns_zone_getfile(zone);
+ ztype = dns_zone_gettype(zone);
+ has_raw = false;
+ }
+
+ inline_signing = named_zone_inlinesigning(zconfig);
+ if (!inline_signing && has_raw) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: old zone was inline-signing");
+ return (false);
+ } else if (inline_signing && !has_raw) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: old zone was not inline-signing");
+ return (false);
+ }
+
+ if (zonetype_fromconfig(zoptions) != ztype) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: type mismatch");
+ return (false);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "file", &obj);
+ if (obj != NULL) {
+ cfilename = cfg_obj_asstring(obj);
+ } else {
+ cfilename = NULL;
+ }
+ if (!((cfilename == NULL && zfilename == NULL) ||
+ (cfilename != NULL && zfilename != NULL &&
+ strcmp(cfilename, zfilename) == 0)))
+ {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: filename mismatch");
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+named_zone_inlinesigning(const cfg_obj_t *zconfig) {
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *signing = NULL;
+ bool inline_signing = false;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+ inline_signing = (cfg_map_get(zoptions, "inline-signing", &signing) ==
+ ISC_R_SUCCESS &&
+ cfg_obj_asboolean(signing));
+
+ return (inline_signing);
+}