diff options
Diffstat (limited to '')
-rw-r--r-- | lib/bind9/Makefile.am | 36 | ||||
-rw-r--r-- | lib/bind9/Makefile.in | 901 | ||||
-rw-r--r-- | lib/bind9/check.c | 6072 | ||||
-rw-r--r-- | lib/bind9/getaddresses.c | 164 | ||||
-rw-r--r-- | lib/bind9/include/bind9/check.h | 65 | ||||
-rw-r--r-- | lib/bind9/include/bind9/getaddresses.h | 51 |
6 files changed, 7289 insertions, 0 deletions
diff --git a/lib/bind9/Makefile.am b/lib/bind9/Makefile.am new file mode 100644 index 0000000..7ec5bdd --- /dev/null +++ b/lib/bind9/Makefile.am @@ -0,0 +1,36 @@ +include $(top_srcdir)/Makefile.top + +lib_LTLIBRARIES = libbind9.la + +libbind9_ladir = $(includedir)/bind9 +libbind9_la_HEADERS = \ + include/bind9/check.h \ + include/bind9/getaddresses.h + +libbind9_la_SOURCES = \ + $(libbind9_la_HEADERS) \ + check.c \ + getaddresses.c + +libbind9_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBNS_CFLAGS) \ + $(LIBBIND9_CFLAGS) \ + $(OPENSSL_CFLAGS) + +libbind9_la_LIBADD = \ + $(LIBNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISC_LIBS) + +libbind9_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -release "$(PACKAGE_VERSION)" + +if HAVE_DNSTAP +libbind9_la_CPPFLAGS += $(DNSTAP_CFLAGS) +endif diff --git a/lib/bind9/Makefile.in b/lib/bind9/Makefile.in new file mode 100644 index 0000000..cf12ed5 --- /dev/null +++ b/lib/bind9/Makefile.in @@ -0,0 +1,901 @@ +# 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_DNSTAP_TRUE@am__append_2 = $(DNSTAP_CFLAGS) +subdir = lib/bind9 +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 $(libbind9_la_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libbind9_ladir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libbind9_la_DEPENDENCIES = $(LIBNS_LIBS) $(LIBISCCFG_LIBS) \ + $(LIBDNS_LIBS) $(LIBISC_LIBS) +am__objects_1 = +am_libbind9_la_OBJECTS = $(am__objects_1) libbind9_la-check.lo \ + libbind9_la-getaddresses.lo +libbind9_la_OBJECTS = $(am_libbind9_la_OBJECTS) +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 = +libbind9_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libbind9_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/libbind9_la-check.Plo \ + ./$(DEPDIR)/libbind9_la-getaddresses.Plo +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 = $(libbind9_la_SOURCES) +DIST_SOURCES = $(libbind9_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libbind9_la_HEADERS) +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 + +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 + +lib_LTLIBRARIES = libbind9.la +libbind9_ladir = $(includedir)/bind9 +libbind9_la_HEADERS = \ + include/bind9/check.h \ + include/bind9/getaddresses.h + +libbind9_la_SOURCES = \ + $(libbind9_la_HEADERS) \ + check.c \ + getaddresses.c + +libbind9_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) $(LIBISCCFG_CFLAGS) $(LIBNS_CFLAGS) \ + $(LIBBIND9_CFLAGS) $(OPENSSL_CFLAGS) $(am__append_2) +libbind9_la_LIBADD = \ + $(LIBNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISC_LIBS) + +libbind9_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -release "$(PACKAGE_VERSION)" + +all: 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 lib/bind9/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/bind9/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-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libbind9.la: $(libbind9_la_OBJECTS) $(libbind9_la_DEPENDENCIES) $(EXTRA_libbind9_la_DEPENDENCIES) + $(AM_V_CCLD)$(libbind9_la_LINK) -rpath $(libdir) $(libbind9_la_OBJECTS) $(libbind9_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbind9_la-check.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbind9_la-getaddresses.Plo@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 $@ $< + +libbind9_la-check.lo: check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbind9_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbind9_la-check.lo -MD -MP -MF $(DEPDIR)/libbind9_la-check.Tpo -c -o libbind9_la-check.lo `test -f 'check.c' || echo '$(srcdir)/'`check.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbind9_la-check.Tpo $(DEPDIR)/libbind9_la-check.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='libbind9_la-check.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbind9_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbind9_la-check.lo `test -f 'check.c' || echo '$(srcdir)/'`check.c + +libbind9_la-getaddresses.lo: getaddresses.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbind9_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbind9_la-getaddresses.lo -MD -MP -MF $(DEPDIR)/libbind9_la-getaddresses.Tpo -c -o libbind9_la-getaddresses.lo `test -f 'getaddresses.c' || echo '$(srcdir)/'`getaddresses.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbind9_la-getaddresses.Tpo $(DEPDIR)/libbind9_la-getaddresses.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getaddresses.c' object='libbind9_la-getaddresses.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbind9_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbind9_la-getaddresses.lo `test -f 'getaddresses.c' || echo '$(srcdir)/'`getaddresses.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libbind9_laHEADERS: $(libbind9_la_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libbind9_la_HEADERS)'; test -n "$(libbind9_ladir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libbind9_ladir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libbind9_ladir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libbind9_ladir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libbind9_ladir)" || exit $$?; \ + done + +uninstall-libbind9_laHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libbind9_la_HEADERS)'; test -n "$(libbind9_ladir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libbind9_ladir)'; $(am__uninstall_files_from_dir) +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: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libbind9_ladir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: 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: + +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." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libbind9_la-check.Plo + -rm -f ./$(DEPDIR)/libbind9_la-getaddresses.Plo + -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-libbind9_laHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +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)/libbind9_la-check.Plo + -rm -f ./$(DEPDIR)/libbind9_la-getaddresses.Plo + -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-libLTLIBRARIES uninstall-libbind9_laHEADERS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES \ + install-libbind9_laHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am 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-libLTLIBRARIES uninstall-libbind9_laHEADERS unit-am \ + unit-local + +.PRECIOUS: Makefile + + +# 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/lib/bind9/check.c b/lib/bind9/check.c new file mode 100644 index 0000000..695090e --- /dev/null +++ b/lib/bind9/check.c @@ -0,0 +1,6072 @@ +/* + * 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 <stdbool.h> +#include <stdlib.h> + +#include <openssl/opensslv.h> + +#ifdef HAVE_DNSTAP +#include <fstrm.h> +#endif + +#include <isc/aes.h> +#include <isc/base64.h> +#include <isc/buffer.h> +#include <isc/dir.h> +#include <isc/file.h> +#include <isc/hex.h> +#include <isc/log.h> +#include <isc/md.h> +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/result.h> +#include <isc/siphash.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/symtab.h> +#include <isc/util.h> + +#include <dns/acl.h> +#include <dns/dnstap.h> +#include <dns/fixedname.h> +#include <dns/kasp.h> +#include <dns/keyvalues.h> +#include <dns/peer.h> +#include <dns/rbt.h> +#include <dns/rdataclass.h> +#include <dns/rdatatype.h> +#include <dns/rrl.h> +#include <dns/secalg.h> +#include <dns/ssu.h> + +#include <dst/dst.h> + +#include <isccfg/aclconf.h> +#include <isccfg/cfg.h> +#include <isccfg/grammar.h> +#include <isccfg/kaspconf.h> +#include <isccfg/namedconf.h> + +#include <ns/hooks.h> + +#include <bind9/check.h> + +static in_port_t dnsport = 53; + +static isc_result_t +fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable, + isc_log_t *logctxlogc); + +static isc_result_t +keydirexist(const cfg_obj_t *zcgf, const char *dir, const char *kaspnamestr, + isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx); +static void +freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { + UNUSED(type); + UNUSED(value); + isc_mem_free(userarg, key); +} + +static isc_result_t +check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_textregion_t r; + dns_fixedname_t fixed; + const cfg_obj_t *obj; + dns_rdataclass_t rdclass; + dns_rdatatype_t rdtype; + isc_buffer_t b; + const char *str; + + dns_fixedname_init(&fixed); + obj = cfg_tuple_get(ent, "class"); + if (cfg_obj_isstring(obj)) { + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + tresult = dns_rdataclass_fromtext(&rdclass, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid class '%s'", r.base); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + obj = cfg_tuple_get(ent, "type"); + if (cfg_obj_isstring(obj)) { + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + tresult = dns_rdatatype_fromtext(&rdtype, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid type '%s'", r.base); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + obj = cfg_tuple_get(ent, "name"); + if (cfg_obj_isstring(obj)) { + str = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b, + dns_rootname, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid name '%s'", str); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + obj = cfg_tuple_get(ent, "order"); + if (!cfg_obj_isstring(obj) || + strcasecmp("order", cfg_obj_asstring(obj)) != 0) + { + cfg_obj_log(ent, logctx, ISC_LOG_ERROR, + "rrset-order: keyword 'order' missing"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + obj = cfg_tuple_get(ent, "ordering"); + if (!cfg_obj_isstring(obj)) { + cfg_obj_log(ent, logctx, ISC_LOG_ERROR, + "rrset-order: missing ordering"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) { +#if !DNS_RDATASET_FIXED + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "rrset-order: order 'fixed' was disabled at " + "compilation time"); +#endif /* if !DNS_RDATASET_FIXED */ + } else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 && + strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0 && + strcasecmp(cfg_obj_asstring(obj), "none") != 0) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid order '%s'", + cfg_obj_asstring(obj)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + return (result); +} + +static isc_result_t +check_order(const cfg_obj_t *options, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const cfg_obj_t *obj = NULL; + + if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS) { + return (result); + } + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + tresult = check_orderent(cfg_listelt_value(element), logctx); + if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + return (result); +} + +static isc_result_t +check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) { + const cfg_listelt_t *element; + const cfg_obj_t *alternates = NULL; + const cfg_obj_t *value; + const cfg_obj_t *obj; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t buffer; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + + (void)cfg_map_get(options, "dual-stack-servers", &alternates); + + if (alternates == NULL) { + return (ISC_R_SUCCESS); + } + + obj = cfg_tuple_get(alternates, "port"); + if (cfg_obj_isuint32(obj)) { + uint32_t val = cfg_obj_asuint32(obj); + if (val > UINT16_MAX) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + obj = cfg_tuple_get(alternates, "addresses"); + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + value = cfg_listelt_value(element); + if (cfg_obj_issockaddr(value)) { + continue; + } + obj = cfg_tuple_get(value, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_constinit(&buffer, str, strlen(str)); + isc_buffer_add(&buffer, strlen(str)); + name = dns_fixedname_initname(&fixed); + tresult = dns_name_fromtext(name, &buffer, dns_rootname, 0, + NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad name '%s'", + str); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + obj = cfg_tuple_get(value, "port"); + if (cfg_obj_isuint32(obj)) { + uint32_t val = cfg_obj_asuint32(obj); + if (val > UINT16_MAX) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + } + return (result); +} + +static isc_result_t +check_forward(const cfg_obj_t *options, const cfg_obj_t *global, + isc_log_t *logctx) { + const cfg_obj_t *forward = NULL; + const cfg_obj_t *forwarders = NULL; + + (void)cfg_map_get(options, "forward", &forward); + (void)cfg_map_get(options, "forwarders", &forwarders); + + if (forwarders != NULL && global != NULL) { + const char *file = cfg_obj_file(global); + unsigned int line = cfg_obj_line(global); + cfg_obj_log(forwarders, logctx, ISC_LOG_ERROR, + "forwarders declared in root zone and " + "in general configuration: %s:%u", + file, line); + return (ISC_R_FAILURE); + } + if (forward != NULL && forwarders == NULL) { + cfg_obj_log(forward, logctx, ISC_LOG_ERROR, + "no matching 'forwarders' statement"); + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const char *str; + isc_buffer_t b; + dns_fixedname_t fixed; + dns_name_t *name; + const cfg_obj_t *obj; + + name = dns_fixedname_initname(&fixed); + obj = cfg_tuple_get(disabled, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'", + str); + result = tresult; + } + + obj = cfg_tuple_get(disabled, "algorithms"); + + for (element = cfg_list_first(obj); 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); + + tresult = dns_secalg_fromtext(&alg, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), logctx, + ISC_LOG_ERROR, "invalid algorithm '%s'", + r.base); + result = tresult; + } + } + return (result); +} + +static isc_result_t +disabled_ds_digests(const cfg_obj_t *disabled, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const char *str; + isc_buffer_t b; + dns_fixedname_t fixed; + dns_name_t *name; + const cfg_obj_t *obj; + + name = dns_fixedname_initname(&fixed); + obj = cfg_tuple_get(disabled, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'", + str); + result = tresult; + } + + obj = cfg_tuple_get(disabled, "digests"); + + for (element = cfg_list_first(obj); 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); + + /* works with a numeric argument too */ + tresult = dns_dsdigest_fromtext(&digest, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), logctx, + ISC_LOG_ERROR, "invalid digest type '%s'", + r.base); + result = tresult; + } + } + return (result); +} + +static isc_result_t +nameexist(const cfg_obj_t *obj, const char *name, int value, + isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx, + isc_mem_t *mctx) { + char *key; + const char *file; + unsigned int line; + isc_result_t result; + isc_symvalue_t symvalue; + + key = isc_mem_strdup(mctx, name); + symvalue.as_cpointer = obj; + result = isc_symtab_define(symtab, key, value, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value, + &symvalue) == ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + + if (file == NULL) { + file = "<unknown file>"; + } + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line); + isc_mem_free(mctx, key); + result = ISC_R_EXISTS; + } else if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, key); + } + return (result); +} + +static isc_result_t +mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx, + isc_mem_t *mctx) { + const cfg_obj_t *obj; + char namebuf[DNS_NAME_FORMATSIZE]; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + isc_result_t result = ISC_R_SUCCESS; + + name = dns_fixedname_initname(&fixed); + obj = cfg_tuple_get(secure, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'", + str); + } else { + dns_name_format(name, namebuf, sizeof(namebuf)); + result = nameexist(secure, namebuf, 1, symtab, + "dnssec-must-be-secure '%s': already " + "exists previous definition: %s:%u", + logctx, mctx); + } + return (result); +} + +static isc_result_t +checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig, + const cfg_obj_t *voptions, const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result; + const cfg_obj_t *aclobj = NULL; + const cfg_obj_t *options; + dns_acl_t *acl = NULL; + + if (zconfig != NULL) { + options = cfg_tuple_get(zconfig, "options"); + cfg_map_get(options, aclname, &aclobj); + } + if (voptions != NULL && aclobj == NULL) { + cfg_map_get(voptions, aclname, &aclobj); + } + if (config != NULL && aclobj == NULL) { + options = NULL; + cfg_map_get(config, "options", &options); + if (options != NULL) { + cfg_map_get(options, aclname, &aclobj); + } + } + if (aclobj == NULL) { + return (ISC_R_SUCCESS); + } + result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, 0, + &acl); + if (acl != NULL) { + dns_acl_detach(&acl); + } + + if (strcasecmp(aclname, "allow-transfer") == 0 && + cfg_obj_istuple(aclobj)) + { + const cfg_obj_t *obj_port = cfg_tuple_get( + cfg_tuple_get(aclobj, "port-transport"), "port"); + const cfg_obj_t *obj_proto = cfg_tuple_get( + cfg_tuple_get(aclobj, "port-transport"), "transport"); + + if (cfg_obj_isuint32(obj_port) && + cfg_obj_asuint32(obj_port) >= UINT16_MAX) + { + cfg_obj_log(obj_port, logctx, ISC_LOG_ERROR, + "port value '%u' is out of range", + + cfg_obj_asuint32(obj_port)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + if (cfg_obj_isstring(obj_proto)) { + const char *allowed[] = { "tcp", "tls" }; + const char *transport = cfg_obj_asstring(obj_proto); + bool found = false; + for (size_t i = 0; i < ARRAY_SIZE(allowed); i++) { + if (strcasecmp(transport, allowed[i]) == 0) { + found = true; + } + } + + if (!found) { + cfg_obj_log(obj_proto, logctx, ISC_LOG_ERROR, + "'%s' is not a valid transport " + "protocol for " + "zone " + "transfers. Please specify either " + "'tcp' or 'tls'", + transport); + result = ISC_R_FAILURE; + } + } + } + return (result); +} + +static isc_result_t +check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, + const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS, tresult; + int i = 0; + + static const char *acls[] = { + "allow-query", "allow-query-on", + "allow-query-cache", "allow-query-cache-on", + "blackhole", "keep-response-order", + "match-clients", "match-destinations", + "sortlist", NULL + }; + + while (acls[i] != NULL) { + tresult = checkacl(acls[i++], actx, NULL, voptions, config, + logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + return (result); +} + +static void +dns64_error(const cfg_obj_t *obj, isc_log_t *logctx, isc_netaddr_t *netaddr, + unsigned int prefixlen, const char *message) { + char buf[ISC_NETADDR_FORMATSIZE + 1]; + isc_netaddr_format(netaddr, buf, sizeof(buf)); + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "dns64 prefix %s/%u %s", buf, + prefixlen, message); +} + +static isc_result_t +check_dns64(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, + const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *dns64 = NULL; + const cfg_obj_t *options; + const cfg_listelt_t *element; + const cfg_obj_t *map, *obj; + isc_netaddr_t na, sa; + unsigned int prefixlen; + int nbytes; + int i; + + static const char *acls[] = { "clients", "exclude", "mapped", NULL }; + + if (voptions != NULL) { + cfg_map_get(voptions, "dns64", &dns64); + } + if (config != NULL && dns64 == NULL) { + options = NULL; + cfg_map_get(config, "options", &options); + if (options != NULL) { + cfg_map_get(options, "dns64", &dns64); + } + } + if (dns64 == NULL) { + return (ISC_R_SUCCESS); + } + + for (element = cfg_list_first(dns64); element != NULL; + element = cfg_list_next(element)) + { + map = cfg_listelt_value(element); + obj = cfg_map_getname(map); + + cfg_obj_asnetprefix(obj, &na, &prefixlen); + if (na.family != AF_INET6) { + dns64_error(map, logctx, &na, prefixlen, + "must be IPv6"); + result = ISC_R_FAILURE; + continue; + } + + if (na.type.in6.s6_addr[8] != 0) { + dns64_error(map, logctx, &na, prefixlen, + "bits [64..71] must be zero"); + result = ISC_R_FAILURE; + continue; + } + + if (prefixlen != 32 && prefixlen != 40 && prefixlen != 48 && + prefixlen != 56 && prefixlen != 64 && prefixlen != 96) + { + dns64_error(map, logctx, &na, prefixlen, + "length is not 32/40/48/56/64/96"); + result = ISC_R_FAILURE; + continue; + } + + for (i = 0; acls[i] != NULL; i++) { + obj = NULL; + (void)cfg_map_get(map, acls[i], &obj); + if (obj != NULL) { + dns_acl_t *acl = NULL; + isc_result_t tresult; + + tresult = cfg_acl_fromconfig(obj, config, + logctx, actx, mctx, + 0, &acl); + if (acl != NULL) { + dns_acl_detach(&acl); + } + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + obj = NULL; + (void)cfg_map_get(map, "suffix", &obj); + if (obj != NULL) { + static const unsigned char zeros[16]; + isc_netaddr_fromsockaddr(&sa, cfg_obj_assockaddr(obj)); + if (sa.family != AF_INET6) { + cfg_obj_log(map, logctx, ISC_LOG_ERROR, + "dns64 requires a IPv6 suffix"); + result = ISC_R_FAILURE; + continue; + } + nbytes = prefixlen / 8 + 4; + if (prefixlen <= 64) { + nbytes++; + } + if (memcmp(sa.type.in6.s6_addr, zeros, nbytes) != 0) { + char netaddrbuf[ISC_NETADDR_FORMATSIZE]; + isc_netaddr_format(&sa, netaddrbuf, + sizeof(netaddrbuf)); + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad suffix '%s' leading " + "%u octets not zeros", + netaddrbuf, nbytes); + result = ISC_R_FAILURE; + } + } + } + + return (result); +} + +#define CHECK_RRL(cond, pat, val1, val2) \ + do { \ + if (!(cond)) { \ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, pat, val1, \ + val2); \ + if (result == ISC_R_SUCCESS) \ + result = ISC_R_RANGE; \ + } \ + } while (0) + +#define CHECK_RRL_RATE(rate, def, max_rate, name) \ + do { \ + obj = NULL; \ + mresult = cfg_map_get(map, name, &obj); \ + if (mresult == ISC_R_SUCCESS) { \ + rate = cfg_obj_asuint32(obj); \ + CHECK_RRL(rate <= max_rate, name " %d > %d", rate, \ + max_rate); \ + } \ + } while (0) + +static isc_result_t +check_ratelimit(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, + const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t mresult; + const cfg_obj_t *map = NULL; + const cfg_obj_t *options; + const cfg_obj_t *obj; + int min_entries, i; + int all_per_second; + int errors_per_second; + int nodata_per_second; + int nxdomains_per_second; + int referrals_per_second; + int responses_per_second; + int slip; + + if (voptions != NULL) { + cfg_map_get(voptions, "rate-limit", &map); + } + if (config != NULL && map == NULL) { + options = NULL; + cfg_map_get(config, "options", &options); + if (options != NULL) { + cfg_map_get(options, "rate-limit", &map); + } + } + if (map == NULL) { + return (ISC_R_SUCCESS); + } + + min_entries = 500; + obj = NULL; + mresult = cfg_map_get(map, "min-table-size", &obj); + if (mresult == ISC_R_SUCCESS) { + min_entries = cfg_obj_asuint32(obj); + if (min_entries < 1) { + min_entries = 1; + } + } + + obj = NULL; + mresult = cfg_map_get(map, "max-table-size", &obj); + if (mresult == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= min_entries, + "max-table-size %d < min-table-size %d", i, + min_entries); + } + + CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE, + "responses-per-second"); + + CHECK_RRL_RATE(referrals_per_second, responses_per_second, + DNS_RRL_MAX_RATE, "referrals-per-second"); + CHECK_RRL_RATE(nodata_per_second, responses_per_second, + DNS_RRL_MAX_RATE, "nodata-per-second"); + CHECK_RRL_RATE(nxdomains_per_second, responses_per_second, + DNS_RRL_MAX_RATE, "nxdomains-per-second"); + CHECK_RRL_RATE(errors_per_second, responses_per_second, + 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"); + + obj = NULL; + mresult = cfg_map_get(map, "window", &obj); + if (mresult == 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); + } + + obj = NULL; + mresult = cfg_map_get(map, "qps-scale", &obj); + if (mresult == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, ""); + } + + obj = NULL; + mresult = cfg_map_get(map, "ipv4-prefix-length", &obj); + if (mresult == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 8 && i <= 32, + "invalid 'ipv4-prefix-length %d'%s", i, ""); + } + + obj = NULL; + mresult = cfg_map_get(map, "ipv6-prefix-length", &obj); + if (mresult == 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); + } + + obj = NULL; + (void)cfg_map_get(map, "exempt-clients", &obj); + if (obj != NULL) { + dns_acl_t *acl = NULL; + isc_result_t tresult; + + tresult = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0, + &acl); + if (acl != NULL) { + dns_acl_detach(&acl); + } + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + return (result); +} + +/* + * Check allow-recursion and allow-recursion-on acls, and also log a + * warning if they're inconsistent with the "recursion" option. + */ +static isc_result_t +check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, + const char *viewname, const cfg_obj_t *config, + isc_log_t *logctx, isc_mem_t *mctx) { + const cfg_obj_t *options, *aclobj, *obj = NULL; + dns_acl_t *acl = NULL; + isc_result_t result = ISC_R_SUCCESS, tresult; + bool recursion; + const char *forview = " for view "; + int i = 0; + + static const char *acls[] = { "allow-recursion", "allow-recursion-on", + NULL }; + + if (voptions != NULL) { + cfg_map_get(voptions, "recursion", &obj); + } + if (obj == NULL && config != NULL) { + options = NULL; + cfg_map_get(config, "options", &options); + if (options != NULL) { + cfg_map_get(options, "recursion", &obj); + } + } + if (obj == NULL) { + recursion = true; + } else { + recursion = cfg_obj_asboolean(obj); + } + + if (viewname == NULL) { + viewname = ""; + forview = ""; + } + + for (i = 0; acls[i] != NULL; i++) { + aclobj = options = NULL; + acl = NULL; + + if (voptions != NULL) { + cfg_map_get(voptions, acls[i], &aclobj); + } + if (config != NULL && aclobj == NULL) { + options = NULL; + cfg_map_get(config, "options", &options); + if (options != NULL) { + cfg_map_get(options, acls[i], &aclobj); + } + } + if (aclobj == NULL) { + continue; + } + + tresult = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, + 0, &acl); + + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + if (acl == NULL) { + continue; + } + + if (!recursion && !dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING, + "both \"recursion no;\" and " + "\"%s\" active%s%s", + acls[i], forview, viewname); + } + + if (acl != NULL) { + dns_acl_detach(&acl); + } + } + + return (result); +} + +typedef struct { + const char *name; + unsigned int scale; + unsigned int max; +} intervaltable; + +#ifdef HAVE_DNSTAP +typedef struct { + const char *name; + unsigned int min; + unsigned int max; +} fstrmtable; +#endif /* ifdef HAVE_DNSTAP */ + +typedef enum { + optlevel_config, + optlevel_options, + optlevel_view, + optlevel_zone +} optlevel_t; + +static isc_result_t +check_name(const char *str) { + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + return (dns_name_fromstring(dns_fixedname_name(&fixed), str, 0, NULL)); +} + +static bool +kasp_name_allowed(const cfg_listelt_t *element) { + const char *name = cfg_obj_asstring( + cfg_tuple_get(cfg_listelt_value(element), "name")); + + if (strcmp("none", name) == 0) { + return (false); + } + if (strcmp("default", name) == 0) { + return (false); + } + if (strcmp("insecure", name) == 0) { + return (false); + } + return (true); +} + +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); +} + +static isc_result_t +check_listener(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t tresult, result = ISC_R_SUCCESS; + 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; + bool do_tls = false, no_tls = false; + dns_acl_t *acl = NULL; + + ltup = cfg_tuple_get(listener, "tuple"); + RUNTIME_CHECK(ltup != NULL); + + tlsobj = cfg_tuple_get(ltup, "tls"); + if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { + const char *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 *tlsmap = NULL; + + do_tls = true; + + tlsmap = find_maplist(config, "tls", tlsname); + if (tlsmap == NULL) { + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls '%s' is not defined", + cfg_obj_asstring(tlsobj)); + result = ISC_R_FAILURE; + } + } + } + + 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) { + cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR, + "http must specify a 'tls' " + "statement, 'tls ephemeral', or " + "'tls none'"); + result = ISC_R_FAILURE; + } + + http_server = find_maplist(config, "http", httpname); + if (http_server == NULL && strcasecmp(httpname, "default") != 0) + { + cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR, + "http '%s' is not defined", + cfg_obj_asstring(httpobj)); + result = ISC_R_FAILURE; + } + } + + portobj = cfg_tuple_get(ltup, "port"); + if (cfg_obj_isuint32(portobj) && + cfg_obj_asuint32(portobj) >= UINT16_MAX) + { + cfg_obj_log(portobj, logctx, ISC_LOG_ERROR, + "port value '%u' is out of range", + + cfg_obj_asuint32(portobj)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + tresult = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config, + logctx, actx, mctx, 0, &acl); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +check_listeners(const cfg_obj_t *list, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t tresult, result = ISC_R_SUCCESS; + const cfg_listelt_t *elt = NULL; + + for (elt = cfg_list_first(list); elt != NULL; elt = cfg_list_next(elt)) + { + const cfg_obj_t *obj = cfg_listelt_value(elt); + tresult = check_listener(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + return (result); +} + +static isc_result_t +check_port(const cfg_obj_t *options, isc_log_t *logctx, const char *type, + in_port_t *portp) { + const cfg_obj_t *portobj = NULL; + isc_result_t result; + + result = cfg_map_get(options, type, &portobj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + if (cfg_obj_asuint32(portobj) >= UINT16_MAX) { + cfg_obj_log(portobj, logctx, ISC_LOG_ERROR, + "port '%u' out of range", + cfg_obj_asuint32(portobj)); + return (ISC_R_RANGE); + } + + if (portp != NULL) { + *portp = (in_port_t)cfg_obj_asuint32(portobj); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +check_options(const cfg_obj_t *options, const cfg_obj_t *config, + isc_log_t *logctx, isc_mem_t *mctx, optlevel_t optlevel) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + unsigned int i; + const cfg_obj_t *obj = NULL; + const cfg_obj_t *resignobj = NULL; + const cfg_listelt_t *element; + isc_symtab_t *symtab = NULL; + const char *str; + isc_buffer_t b; + uint32_t lifetime = 3600; + bool has_dnssecpolicy = false; + const char *ccalg = "siphash24"; + cfg_aclconfctx_t *actx = NULL; + static const char *sources[] = { + "query-source", + "query-source-v6", + }; + + /* + * { "name", scale, value } + * (scale * value) <= UINT32_MAX + */ + static intervaltable intervals[] = { + { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */ + { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */ + + /* minimum and maximum cache and negative cache TTLs */ + { "min-cache-ttl", 1, MAX_MIN_CACHE_TTL }, /* 90 secs */ + { "max-cache-ttl", 1, UINT32_MAX }, /* no limit */ + { "min-ncache-ttl", 1, MAX_MIN_NCACHE_TTL }, /* 90 secs */ + { "max-ncache-ttl", 1, MAX_MAX_NCACHE_TTL }, /* 7 days */ + }; + + static const char *server_contact[] = { "empty-server", "empty-contact", + "dns64-server", "dns64-contact", + NULL }; + +#ifdef HAVE_DNSTAP + static fstrmtable fstrm[] = { + { "fstrm-set-buffer-hint", FSTRM_IOTHR_BUFFER_HINT_MIN, + FSTRM_IOTHR_BUFFER_HINT_MAX }, + { "fstrm-set-flush-timeout", FSTRM_IOTHR_FLUSH_TIMEOUT_MIN, + FSTRM_IOTHR_FLUSH_TIMEOUT_MAX }, + { "fstrm-set-input-queue-size", + FSTRM_IOTHR_INPUT_QUEUE_SIZE_MIN, + FSTRM_IOTHR_INPUT_QUEUE_SIZE_MAX }, + { "fstrm-set-output-notify-threshold", + FSTRM_IOTHR_QUEUE_NOTIFY_THRESHOLD_MIN, 0 }, + { "fstrm-set-output-queue-size", + FSTRM_IOTHR_OUTPUT_QUEUE_SIZE_MIN, + FSTRM_IOTHR_OUTPUT_QUEUE_SIZE_MAX }, + { "fstrm-set-reopen-interval", FSTRM_IOTHR_REOPEN_INTERVAL_MIN, + FSTRM_IOTHR_REOPEN_INTERVAL_MAX } + }; +#endif /* ifdef HAVE_DNSTAP */ + + if (optlevel == optlevel_options) { + /* + * Check port values, and record "port" for later use. + */ + tresult = check_port(options, logctx, "port", &dnsport); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + tresult = check_port(options, logctx, "tls-port", NULL); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + tresult = check_port(options, logctx, "http-port", NULL); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + tresult = check_port(options, logctx, "https-port", NULL); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + if (optlevel == optlevel_options || optlevel == optlevel_view) { + /* + * Warn if query-source or query-source-v6 options specify + * a port, and fail if they specify the DNS port. + */ + for (i = 0; i < ARRAY_SIZE(sources); i++) { + obj = NULL; + (void)cfg_map_get(options, sources[i], &obj); + if (obj != NULL) { + const isc_sockaddr_t *sa = + cfg_obj_assockaddr(obj); + in_port_t port = isc_sockaddr_getport(sa); + if (port == dnsport) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'%s' cannot specify the " + "DNS listener port (%d)", + sources[i], port); + result = ISC_R_FAILURE; + } else if (port != 0) { + cfg_obj_log(obj, logctx, + ISC_LOG_WARNING, + "'%s': specifying a port " + "is not recommended", + sources[i]); + } + } + } + } + + /* + * Check that fields specified in units of time other than seconds + * have reasonable values. + */ + for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) { + uint32_t val; + obj = NULL; + (void)cfg_map_get(options, intervals[i].name, &obj); + if (obj == NULL) { + continue; + } + if (cfg_obj_isduration(obj)) { + val = cfg_obj_asduration(obj); + } else { + val = cfg_obj_asuint32(obj); + } + if (val > intervals[i].max) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (0..%u)", + intervals[i].name, val, intervals[i].max); + result = ISC_R_RANGE; + } else if (val > (UINT32_MAX / intervals[i].scale)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%d' is out of range", + intervals[i].name, val); + result = ISC_R_RANGE; + } + } + + /* + * Check dnssec-policy. + */ + obj = NULL; + (void)cfg_map_get(options, "dnssec-policy", &obj); + if (obj != NULL) { + bool bad_kasp = false; + bool bad_name = false; + + if (optlevel != optlevel_config && !cfg_obj_isstring(obj)) { + bad_kasp = true; + } else if (optlevel == optlevel_config) { + dns_kasplist_t list; + dns_kasp_t *kasp = NULL, *kasp_next = NULL; + + ISC_LIST_INIT(list); + + if (cfg_obj_islist(obj)) { + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + isc_result_t ret; + cfg_obj_t *kconfig = + cfg_listelt_value(element); + + if (!cfg_obj_istuple(kconfig)) { + bad_kasp = true; + continue; + } + if (!kasp_name_allowed(element)) { + bad_name = true; + continue; + } + + ret = cfg_kasp_fromconfig(kconfig, NULL, + mctx, logctx, + &list, &kasp); + if (ret != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = ret; + } + } + + if (kasp != NULL) { + dns_kasp_detach(&kasp); + } + } + } + + for (kasp = ISC_LIST_HEAD(list); kasp != NULL; + kasp = kasp_next) + { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(list, kasp, link); + dns_kasp_detach(&kasp); + } + } + + if (bad_kasp) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-policy may only be configured at " + "the top level, please use name reference " + "at the zone level"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else if (bad_name) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-policy name may not be 'insecure', " + "'none', or 'default' (which are built-in " + "policies)"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else { + has_dnssecpolicy = true; + } + } + + obj = NULL; + cfg_map_get(options, "max-rsa-exponent-size", &obj); + if (obj != NULL) { + uint32_t val; + + val = cfg_obj_asuint32(obj); + if (val != 0 && (val < 35 || val > 4096)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "max-rsa-exponent-size '%u' is out of " + "range (35..4096)", + val); + result = ISC_R_RANGE; + } + } + + obj = NULL; + cfg_map_get(options, "sig-validity-interval", &obj); + if (obj != NULL) { + uint32_t validity, resign = 0; + + validity = cfg_obj_asuint32(cfg_tuple_get(obj, "validity")); + resignobj = cfg_tuple_get(obj, "re-sign"); + if (!cfg_obj_isvoid(resignobj)) { + resign = cfg_obj_asuint32(resignobj); + } + + if (validity > 3660 || validity == 0) { /* 10 years */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (1..3660)", + "sig-validity-interval", validity); + result = ISC_R_RANGE; + } + + if (!cfg_obj_isvoid(resignobj)) { + if (resign > 3660 || resign == 0) { /* 10 years */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (1..3660)", + "sig-validity-interval (re-sign)", + validity); + result = ISC_R_RANGE; + } else if ((validity > 7 && validity < resign) || + (validity <= 7 && validity * 24 < resign)) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "validity interval (%u days) " + "less than re-signing interval " + "(%u %s)", + validity, resign, + (validity > 7) ? "days" : "hours"); + result = ISC_R_RANGE; + } + } + + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "sig-validity-interval: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + } + + obj = NULL; + cfg_map_get(options, "dnskey-sig-validity", &obj); + if (obj != NULL) { + uint32_t keyvalidity; + + keyvalidity = cfg_obj_asuint32(obj); + if (keyvalidity > 3660) { /* 10 years */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (0..3660)", + "dnskey-sig-validity", keyvalidity); + result = ISC_R_RANGE; + } + + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnskey-sig-validity: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + } + + obj = NULL; + (void)cfg_map_get(options, "preferred-glue", &obj); + if (obj != NULL) { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "a") != 0 && strcasecmp(str, "aaaa") != 0 && + strcasecmp(str, "none") != 0) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "preferred-glue unexpected value '%s'", + str); + } + } + + obj = NULL; + (void)cfg_map_get(options, "root-delegation-only", &obj); + if (obj != NULL) { + if (!cfg_obj_isvoid(obj)) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *exclude; + + exclude = cfg_listelt_value(element); + str = cfg_obj_asstring(exclude); + tresult = check_name(str); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", + str); + result = tresult; + } + } + } + } + + /* + * Set supported DNSSEC algorithms. + */ + obj = NULL; + (void)cfg_map_get(options, "disable-algorithms", &obj); + if (obj != NULL) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + tresult = disabled_algorithms(obj, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + /* + * Set supported DS digest types. + */ + obj = NULL; + (void)cfg_map_get(options, "disable-ds-digests", &obj); + if (obj != NULL) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + tresult = disabled_ds_digests(obj, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + /* + * Check auto-dnssec at the view/options level + */ + obj = NULL; + (void)cfg_map_get(options, "auto-dnssec", &obj); + if (obj != NULL) { + const char *arg = cfg_obj_asstring(obj); + if (optlevel != optlevel_zone && strcasecmp(arg, "off") != 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "auto-dnssec may only be activated at the " + "zone level"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + /* + * Check dnssec-must-be-secure. + */ + obj = NULL; + (void)cfg_map_get(options, "dnssec-must-be-secure", &obj); + if (obj != NULL) { + tresult = isc_symtab_create(mctx, 100, freekey, mctx, false, + &symtab); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } else { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + tresult = mustbesecure(obj, symtab, logctx, + mctx); + if (result == ISC_R_SUCCESS && + tresult != ISC_R_SUCCESS) + { + result = tresult; + } + } + } + if (symtab != NULL) { + isc_symtab_destroy(&symtab); + } + } + + /* + * Check server/contacts for syntactic validity. + */ + for (i = 0; server_contact[i] != NULL; i++) { + obj = NULL; + (void)cfg_map_get(options, server_contact[i], &obj); + if (obj != NULL) { + str = cfg_obj_asstring(obj); + if (check_name(str) != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s: invalid name '%s'", + server_contact[i], str); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + } + + /* + * Check empty zone configuration. + */ + obj = NULL; + (void)cfg_map_get(options, "disable-empty-zone", &obj); + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + if (check_name(str) != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "disable-empty-zone: invalid name '%s'", + str); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + /* + * Check that server-id is not too long. + * 1024 bytes should be big enough. + */ + obj = NULL; + (void)cfg_map_get(options, "server-id", &obj); + if (obj != NULL && cfg_obj_isstring(obj) && + strlen(cfg_obj_asstring(obj)) > 1024U) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'server-id' too big (>1024 bytes)"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + obj = NULL; + (void)cfg_map_get(options, "nta-lifetime", &obj); + if (obj != NULL) { + lifetime = cfg_obj_asduration(obj); + if (lifetime > 604800) { /* 7 days */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'nta-lifetime' cannot exceed one week"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } else if (lifetime == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'nta-lifetime' may not be zero"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + + obj = NULL; + (void)cfg_map_get(options, "nta-recheck", &obj); + if (obj != NULL) { + uint32_t recheck = cfg_obj_asduration(obj); + if (recheck > 604800) { /* 7 days */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'nta-recheck' cannot exceed one week"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + if (recheck > lifetime) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'nta-recheck' (%d seconds) is " + "greater than 'nta-lifetime' " + "(%d seconds)", + recheck, lifetime); + } + } + + obj = NULL; + (void)cfg_map_get(options, "cookie-algorithm", &obj); + if (obj != NULL) { + ccalg = cfg_obj_asstring(obj); + } + + obj = NULL; + (void)cfg_map_get(options, "cookie-secret", &obj); + if (obj != NULL) { + unsigned char secret[32]; + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + unsigned int usedlength; + + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + + memset(secret, 0, sizeof(secret)); + isc_buffer_init(&b, secret, sizeof(secret)); + tresult = isc_hex_decodestring(str, &b); + if (tresult == ISC_R_NOSPACE) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "cookie-secret: too long"); + } else if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "cookie-secret: invalid hex " + "string"); + } + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = tresult; + } + continue; + } + + usedlength = isc_buffer_usedlength(&b); + if (strcasecmp(ccalg, "aes") == 0 && + usedlength != ISC_AES128_KEYLENGTH) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "AES cookie-secret must be 128 " + "bits"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + if (strcasecmp(ccalg, "siphash24") == 0 && + usedlength != ISC_SIPHASH24_KEY_LENGTH) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "SipHash-2-4 cookie-secret must be " + "128 bits"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + } + +#ifdef HAVE_DNSTAP + for (i = 0; i < sizeof(fstrm) / sizeof(fstrm[0]); i++) { + uint32_t value; + + obj = NULL; + (void)cfg_map_get(options, fstrm[i].name, &obj); + if (obj == NULL) { + continue; + } + + if (cfg_obj_isduration(obj)) { + value = cfg_obj_asduration(obj); + } else { + value = cfg_obj_asuint32(obj); + } + if (value < fstrm[i].min || + (fstrm[i].max != 0U && value > fstrm[i].max)) + { + if (fstrm[i].max != 0U) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' out of range (%u..%u)", + fstrm[i].name, value, fstrm[i].min, + fstrm[i].max); + } else { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s out of range (%u < %u)", + fstrm[i].name, value, fstrm[i].min); + } + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + if (strcmp(fstrm[i].name, "fstrm-set-input-queue-size") == 0) { + int bits = 0; + do { + bits += value & 0x1; + value >>= 1; + } while (value != 0U); + if (bits != 1) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' not a power-of-2", + fstrm[i].name, + cfg_obj_asuint32(obj)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + } + + /* Check that dnstap-ouput values are consistent */ + obj = NULL; + (void)cfg_map_get(options, "dnstap-output", &obj); + if (obj != NULL) { + const cfg_obj_t *obj2; + dns_dtmode_t dmode; + + obj2 = cfg_tuple_get(obj, "mode"); + if (obj2 == NULL) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnstap-output mode not found"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else { + if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0) { + dmode = dns_dtmode_file; + } else { + dmode = dns_dtmode_unix; + } + + obj2 = cfg_tuple_get(obj, "size"); + if (obj2 != NULL && !cfg_obj_isvoid(obj2) && + dmode == dns_dtmode_unix) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnstap-output size " + "cannot be set with mode unix"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + obj2 = cfg_tuple_get(obj, "versions"); + if (obj2 != NULL && !cfg_obj_isvoid(obj2) && + dmode == dns_dtmode_unix) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnstap-output versions " + "cannot be set with mode unix"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + obj2 = cfg_tuple_get(obj, "suffix"); + if (obj2 != NULL && !cfg_obj_isvoid(obj2) && + dmode == dns_dtmode_unix) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnstap-output suffix " + "cannot be set with mode unix"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + } +#endif /* ifdef HAVE_DNSTAP */ + + obj = NULL; + (void)cfg_map_get(options, "lmdb-mapsize", &obj); + if (obj != NULL) { + uint64_t mapsize = cfg_obj_asuint64(obj); + + if (mapsize < (1ULL << 20)) { /* 1 megabyte */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'lmdb-mapsize " + "%" PRId64 "' " + "is too small", + mapsize); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } else if (mapsize > (1ULL << 40)) { /* 1 terabyte */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'lmdb-mapsize " + "%" PRId64 "' " + "is too large", + mapsize); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + } + + obj = NULL; + (void)cfg_map_get(options, "resolver-nonbackoff-tries", &obj); + if (obj != NULL && cfg_obj_asuint32(obj) == 0U) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'resolver-nonbackoff-tries' must be >= 1"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + obj = NULL; + (void)cfg_map_get(options, "max-ixfr-ratio", &obj); + if (obj != NULL && cfg_obj_ispercentage(obj)) { + uint32_t percent = cfg_obj_aspercentage(obj); + if (percent == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'ixfr-max-ratio' must be a nonzero " + "percentage or 'unlimited')"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } else if (percent > 100) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'ixfr-max-ratio %d%%' exceeds 100%%", + percent); + } + } + + obj = NULL; + (void)cfg_map_get(options, "check-names", &obj); + if (obj != NULL && !cfg_obj_islist(obj)) { + obj = NULL; + } + if (obj != NULL) { + /* Note: SEC is defined in <sys/time.h> on some platforms. */ + enum { MAS = 1, PRI = 2, SLA = 4, SCN = 8 } values = 0; + for (const cfg_listelt_t *el = cfg_list_first(obj); el != NULL; + el = cfg_list_next(el)) + { + const cfg_obj_t *tuple = cfg_listelt_value(el); + const cfg_obj_t *type = cfg_tuple_get(tuple, "type"); + const char *keyword = cfg_obj_asstring(type); + if (strcasecmp(keyword, "primary") == 0) { + if ((values & PRI) == PRI) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names primary' " + "duplicated"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + values |= PRI; + } else if (strcasecmp(keyword, "master") == 0) { + if ((values & MAS) == MAS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names master' " + "duplicated"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + values |= MAS; + } else if (strcasecmp(keyword, "secondary") == 0) { + if ((values & SCN) == SCN) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names secondary' " + "duplicated"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + values |= SCN; + } else if (strcasecmp(keyword, "slave") == 0) { + if ((values & SLA) == SLA) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names slave' " + "duplicated"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + values |= SLA; + } + } + + if ((values & (PRI | MAS)) == (PRI | MAS)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names' cannot take both " + "'primary' and 'master'"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + if ((values & (SCN | SLA)) == (SCN | SLA)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'check-names' cannot take both " + "'secondary' and 'slave'"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + + obj = NULL; + (void)cfg_map_get(options, "stale-refresh-time", &obj); + if (obj != NULL) { + uint32_t refresh_time = cfg_obj_asduration(obj); + if (refresh_time > 0 && refresh_time < 30) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'stale-refresh-time' should either be 0 " + "or otherwise 30 seconds or higher"); + } + } + + cfg_aclconfctx_create(mctx, &actx); + + obj = NULL; + (void)cfg_map_get(options, "listen-on", &obj); + if (obj != NULL) { + INSIST(config != NULL); + tresult = check_listeners(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + obj = NULL; + (void)cfg_map_get(options, "listen-on-v6", &obj); + if (obj != NULL) { + INSIST(config != NULL); + tresult = check_listeners(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + if (actx != NULL) { + cfg_aclconfctx_detach(&actx); + } + + return (result); +} + +/* + * Check "remote-servers" style list. + */ +static isc_result_t +bind9_check_remoteserverlist(const cfg_obj_t *cctx, const char *list, + isc_log_t *logctx, isc_symtab_t *symtab, + isc_mem_t *mctx) { + isc_symvalue_t symvalue; + isc_result_t result, tresult; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *elt; + + result = cfg_map_get(cctx, list, &obj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + elt = cfg_list_first(obj); + while (elt != NULL) { + char *tmp; + const char *name; + + obj = cfg_listelt_value(elt); + name = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + + tmp = isc_mem_strdup(mctx, name); + symvalue.as_cpointer = obj; + tresult = isc_symtab_define(symtab, tmp, 1, symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + const char *file = NULL; + unsigned int line; + + RUNTIME_CHECK( + isc_symtab_lookup(symtab, tmp, 1, &symvalue) == + ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + + if (file == NULL) { + file = "<unknown file>"; + } + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s list '%s' is duplicated: " + "also defined at %s:%u", + list, name, file, line); + isc_mem_free(mctx, tmp); + result = tresult; + break; + } else if (tresult != ISC_R_SUCCESS) { + isc_mem_free(mctx, tmp); + result = tresult; + break; + } + + elt = cfg_list_next(elt); + } + return (result); +} + +/* + * Check primaries lists for duplicates. + */ +static isc_result_t +bind9_check_primarylists(const cfg_obj_t *cctx, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result, tresult; + isc_symtab_t *symtab = NULL; + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + tresult = bind9_check_remoteserverlist(cctx, "primaries", logctx, + symtab, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + tresult = bind9_check_remoteserverlist(cctx, "masters", logctx, symtab, + mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + isc_symtab_destroy(&symtab); + return (result); +} + +/* + * Check parental-agents lists for duplicates. + */ +static isc_result_t +bind9_check_parentalagentlists(const cfg_obj_t *cctx, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result, tresult; + isc_symtab_t *symtab = NULL; + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + tresult = bind9_check_remoteserverlist(cctx, "parental-agents", logctx, + symtab, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + isc_symtab_destroy(&symtab); + return (result); +} + +#if HAVE_LIBNGHTTP2 +static isc_result_t +bind9_check_httpserver(const cfg_obj_t *http, isc_log_t *logctx, + isc_symtab_t *symtab) { + isc_result_t result, tresult; + const char *name = cfg_obj_asstring(cfg_map_getname(http)); + const cfg_obj_t *eps = NULL; + const cfg_listelt_t *elt = NULL; + isc_symvalue_t symvalue; + + if (strcasecmp(name, "default") == 0) { + cfg_obj_log(http, logctx, ISC_LOG_ERROR, + "'http' name cannot be '%s' (which is a " + "built-in configuration)", + name); + result = ISC_R_FAILURE; + } else { + /* Check for duplicates */ + symvalue.as_cpointer = http; + result = isc_symtab_define(symtab, name, 1, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + const char *file = NULL; + unsigned int line; + + tresult = isc_symtab_lookup(symtab, name, 1, &symvalue); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + + line = cfg_obj_line(symvalue.as_cpointer); + file = cfg_obj_file(symvalue.as_cpointer); + if (file == NULL) { + file = "<unknown file>"; + } + + cfg_obj_log(http, logctx, ISC_LOG_ERROR, + "http '%s' is duplicated: " + "also defined at %s:%u", + name, file, line); + } + } + + /* Check endpoints are valid */ + tresult = cfg_map_get(http, "endpoints", &eps); + if (tresult == ISC_R_SUCCESS) { + for (elt = cfg_list_first(eps); elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *ep = cfg_listelt_value(elt); + const char *path = cfg_obj_asstring(ep); + if (!isc_nm_http_path_isvalid(path)) { + cfg_obj_log(eps, logctx, ISC_LOG_ERROR, + "endpoint '%s' is not a " + "valid absolute HTTP path", + path); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + } + + return (result); +} + +static isc_result_t +bind9_check_httpservers(const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result, tresult; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *elt = NULL; + isc_symtab_t *symtab = NULL; + + result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = cfg_map_get(config, "http", &obj); + if (result != ISC_R_SUCCESS) { + result = ISC_R_SUCCESS; + goto done; + } + + for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) { + obj = cfg_listelt_value(elt); + tresult = bind9_check_httpserver(obj, logctx, symtab); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + +done: + isc_symtab_destroy(&symtab); + return (result); +} +#endif /* HAVE_LIBNGHTTP2 */ + +static isc_result_t +bind9_check_tls_defintion(const cfg_obj_t *tlsobj, const char *name, + isc_log_t *logctx, isc_symtab_t *symtab) { + isc_result_t result, tresult; + const cfg_obj_t *tls_proto_list = NULL, *tls_key = NULL, + *tls_cert = NULL, *tls_ciphers = NULL; + uint32_t tls_protos = 0; + isc_symvalue_t symvalue; + + if (strcasecmp(name, "ephemeral") == 0 || strcasecmp(name, "none") == 0) + { + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls clause name '%s' is reserved for internal use", + name); + result = ISC_R_FAILURE; + } else { + /* Check for duplicates */ + symvalue.as_cpointer = tlsobj; + result = isc_symtab_define(symtab, name, 1, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + const char *file = NULL; + unsigned int line; + + tresult = isc_symtab_lookup(symtab, name, 1, &symvalue); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + + line = cfg_obj_line(symvalue.as_cpointer); + file = cfg_obj_file(symvalue.as_cpointer); + if (file == NULL) { + file = "<unknown file>"; + } + + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls clause '%s' is duplicated: " + "also defined at %s:%u", + name, file, line); + } + } + + (void)cfg_map_get(tlsobj, "key-file", &tls_key); + (void)cfg_map_get(tlsobj, "cert-file", &tls_cert); + if ((tls_key == NULL && tls_cert != NULL) || + (tls_cert == NULL && tls_key != NULL)) + { + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls '%s': 'cert-file' and 'key-file' must " + "both be specified, or both omitted", + name); + result = ISC_R_FAILURE; + } + + /* Check protocols are valid */ + tresult = cfg_map_get(tlsobj, "protocols", &tls_proto_list); + if (tresult == 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); + + if (ver == ISC_TLS_PROTO_VER_UNDEFINED) { + cfg_obj_log(tls_proto_obj, logctx, + ISC_LOG_ERROR, + "'%s' is not a valid " + "TLS protocol version", + tls_sver); + result = ISC_R_FAILURE; + continue; + } else if (!isc_tls_protocol_supported(ver)) { + cfg_obj_log(tls_proto_obj, logctx, + ISC_LOG_ERROR, + "'%s' is not " + "supported by the " + "cryptographic library version in " + "use (%s)", + tls_sver, OPENSSL_VERSION_TEXT); + result = ISC_R_FAILURE; + } + + if ((tls_protos & ver) != 0) { + cfg_obj_log(tls_proto_obj, logctx, + ISC_LOG_WARNING, + "'%s' is specified more than once " + "in '%s'", + tls_sver, name); + result = ISC_R_FAILURE; + } + + tls_protos |= ver; + } + + if (tls_protos == 0) { + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls '%s' does not contain any valid " + "TLS protocol versions definitions", + name); + result = ISC_R_FAILURE; + } + } + + /* Check cipher list string is valid */ + tresult = cfg_map_get(tlsobj, "ciphers", &tls_ciphers); + if (tresult == ISC_R_SUCCESS) { + const char *ciphers = cfg_obj_asstring(tls_ciphers); + if (!isc_tls_cipherlist_valid(ciphers)) { + cfg_obj_log(tls_ciphers, logctx, ISC_LOG_ERROR, + "'ciphers' in the 'tls' clause '%s' is " + "not a " + "valid cipher list string", + name); + result = ISC_R_FAILURE; + } + } + + return (result); +} + +static isc_result_t +bind9_check_tls_definitions(const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result, tresult; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *elt = NULL; + isc_symtab_t *symtab = NULL; + + result = cfg_map_get(config, "tls", &obj); + if (result != ISC_R_SUCCESS) { + result = ISC_R_SUCCESS; + return (result); + } + + result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + + for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) { + const char *name; + obj = cfg_listelt_value(elt); + name = cfg_obj_asstring(cfg_map_getname(obj)); + tresult = bind9_check_tls_defintion(obj, name, logctx, symtab); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + isc_symtab_destroy(&symtab); + + return (result); +} + +static isc_result_t +get_remotes(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 = NULL; + + result = cfg_map_get(cctx, list, &obj); + if (result != ISC_R_SUCCESS) { + return (result); + } + + elt = cfg_list_first(obj); + while (elt != NULL) { + const char *listname; + + obj = cfg_listelt_value(elt); + listname = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + + if (strcasecmp(listname, name) == 0) { + *ret = obj; + return (ISC_R_SUCCESS); + } + + elt = cfg_list_next(elt); + } + + return (ISC_R_NOTFOUND); +} + +static isc_result_t +get_remoteservers_def(const char *list, const char *name, const cfg_obj_t *cctx, + const cfg_obj_t **ret) { + isc_result_t result = ISC_R_NOTFOUND; + + if (strcmp(list, "primaries") == 0) { + result = get_remotes(cctx, "primaries", name, ret); + if (result != ISC_R_SUCCESS) { + result = get_remotes(cctx, "masters", name, ret); + } + } else if (strcmp(list, "parental-agents") == 0) { + result = get_remotes(cctx, "parental-agents", name, ret); + } + return (result); +} + +static isc_result_t +validate_remotes(const char *list, const cfg_obj_t *obj, + const cfg_obj_t *config, uint32_t *countp, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + uint32_t count = 0; + isc_symtab_t *symtab = NULL; + isc_symvalue_t symvalue; + const cfg_listelt_t *element; + const cfg_listelt_t **stack = NULL; + uint32_t stackcount = 0, pushed = 0; + const cfg_obj_t *listobj; + + REQUIRE(countp != NULL); + result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab); + if (result != ISC_R_SUCCESS) { + *countp = count; + return (result); + } + +newlist: + listobj = cfg_tuple_get(obj, "addresses"); + element = cfg_list_first(listobj); +resume: + for (; element != NULL; element = cfg_list_next(element)) { + const char *listname; + 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)) { + count++; + if (cfg_obj_isstring(key)) { + const char *str = cfg_obj_asstring(key); + dns_fixedname_t fname; + dns_name_t *nm = dns_fixedname_initname(&fname); + tresult = dns_name_fromstring(nm, str, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "'%s' is not a valid name", + str); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + } + if (cfg_obj_isstring(tls)) { + const char *str = cfg_obj_asstring(tls); + dns_fixedname_t fname; + dns_name_t *nm = dns_fixedname_initname(&fname); + tresult = dns_name_fromstring(nm, str, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(tls, logctx, ISC_LOG_ERROR, + "'%s' is not a valid name", + str); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + if (strcasecmp(str, "ephemeral") != 0) { + const cfg_obj_t *tlsmap = NULL; + + tlsmap = find_maplist(config, "tls", + str); + if (tlsmap == NULL) { + cfg_obj_log( + tls, logctx, + ISC_LOG_ERROR, + "tls '%s' is not " + "defined", + cfg_obj_asstring(tls)); + result = ISC_R_FAILURE; + } + } + } + continue; + } + if (!cfg_obj_isvoid(key)) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "unexpected token '%s'", + cfg_obj_asstring(key)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + if (!cfg_obj_isvoid(tls)) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "unexpected token '%s'", + cfg_obj_asstring(tls)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + listname = cfg_obj_asstring(addr); + symvalue.as_cpointer = addr; + tresult = isc_symtab_define(symtab, listname, 1, symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + continue; + } + tresult = get_remoteservers_def(list, listname, config, &obj); + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = tresult; + } + cfg_obj_log(addr, logctx, ISC_LOG_ERROR, + "unable to find %s list '%s'", list, + listname); + continue; + } + /* Grow stack? */ + if (stackcount == pushed) { + void *newstack; + uint32_t newlen = stackcount + 16; + size_t newsize, oldsize; + + newsize = newlen * sizeof(*stack); + oldsize = stackcount * sizeof(*stack); + newstack = isc_mem_get(mctx, newsize); + if (stackcount != 0) { + void *ptr; + + DE_CONST(stack, ptr); + memmove(newstack, stack, oldsize); + isc_mem_put(mctx, ptr, oldsize); + } + stack = newstack; + stackcount = newlen; + } + stack[pushed++] = cfg_list_next(element); + goto newlist; + } + if (pushed != 0) { + element = stack[--pushed]; + goto resume; + } + if (stack != NULL) { + void *ptr; + + DE_CONST(stack, ptr); + isc_mem_put(mctx, ptr, stackcount * sizeof(*stack)); + } + isc_symtab_destroy(&symtab); + *countp = count; + return (result); +} + +static isc_result_t +check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const cfg_listelt_t *element2; + dns_fixedname_t fixed_id, fixed_name; + dns_name_t *id, *name; + const char *str; + isc_textregion_t r; + dns_rdatatype_t type; + + /* Check for "update-policy local;" */ + if (cfg_obj_isstring(policy) && + strcmp("local", cfg_obj_asstring(policy)) == 0) + { + return (ISC_R_SUCCESS); + } + + /* Now check the grant policy */ + for (element = cfg_list_first(policy); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *stmt = cfg_listelt_value(element); + 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"); + dns_ssumatchtype_t mtype; + + id = dns_fixedname_initname(&fixed_id); + name = dns_fixedname_initname(&fixed_name); + + tresult = dns_ssu_mtypefromstring(cfg_obj_asstring(matchtype), + &mtype); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "has a bad match-type"); + } + + str = cfg_obj_asstring(identity); + tresult = dns_name_fromstring(id, str, 1, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "'%s' is not a valid name", str); + result = tresult; + } + + /* + * There is no name field for subzone and dname is void + */ + if (mtype == dns_ssumatchtype_subdomain && + cfg_obj_isvoid(dname)) + { + str = "."; /* Use "." as a replacement. */ + } else { + str = cfg_obj_asstring(dname); + } + if (tresult == ISC_R_SUCCESS) { + tresult = dns_name_fromstring(name, str, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(dname, logctx, ISC_LOG_ERROR, + "'%s' is not a valid name", str); + result = tresult; + } + } + + if (tresult == ISC_R_SUCCESS && + mtype == dns_ssumatchtype_wildcard && + !dns_name_iswildcard(name)) + { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "'%s' is not a wildcard", str); + result = ISC_R_FAILURE; + } + + /* + * For some match types, the name should be a placeholder + * value, either "." or the same as identity. + */ + switch (mtype) { + case dns_ssumatchtype_self: + case dns_ssumatchtype_selfsub: + case dns_ssumatchtype_selfwild: + if (tresult == ISC_R_SUCCESS && + (!dns_name_equal(id, name) && + !dns_name_equal(dns_rootname, name))) + { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "identity and name fields are not " + "the same"); + result = ISC_R_FAILURE; + } + break; + case dns_ssumatchtype_selfkrb5: + case dns_ssumatchtype_selfms: + case dns_ssumatchtype_selfsubkrb5: + case dns_ssumatchtype_selfsubms: + case dns_ssumatchtype_tcpself: + case dns_ssumatchtype_6to4self: + if (tresult == ISC_R_SUCCESS && + !dns_name_equal(dns_rootname, name)) + { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "name field not set to " + "placeholder value '.'"); + result = ISC_R_FAILURE; + } + break; + case dns_ssumatchtype_name: + case dns_ssumatchtype_subdomain: /* also zonesub */ + case dns_ssumatchtype_subdomainms: + case dns_ssumatchtype_subdomainselfmsrhs: + case dns_ssumatchtype_subdomainkrb5: + case dns_ssumatchtype_subdomainselfkrb5rhs: + case dns_ssumatchtype_wildcard: + case dns_ssumatchtype_external: + case dns_ssumatchtype_local: + if (tresult == ISC_R_SUCCESS) { + DE_CONST(str, r.base); + r.length = strlen(str); + tresult = dns_rdatatype_fromtext(&type, &r); + } + if (tresult == ISC_R_SUCCESS) { + cfg_obj_log(identity, logctx, ISC_LOG_ERROR, + "missing name field type '%s' " + "found", + str); + result = ISC_R_FAILURE; + break; + } + break; + default: + UNREACHABLE(); + } + + for (element2 = cfg_list_first(typelist); element2 != NULL; + element2 = cfg_list_next(element2)) + { + const cfg_obj_t *typeobj; + const char *bracket; + + typeobj = cfg_listelt_value(element2); + DE_CONST(cfg_obj_asstring(typeobj), r.base); + + bracket = strchr(r.base, '(' /*)*/); + if (bracket != NULL) { + char *end = NULL; + unsigned long max; + + r.length = bracket - r.base; + max = strtoul(bracket + 1, &end, 10); + if (max > 0xffff || end[0] != /*(*/ ')' || + end[1] != 0) + { + cfg_obj_log(typeobj, logctx, + ISC_LOG_ERROR, + "'%s' is not a valid count", + bracket); + result = DNS_R_SYNTAX; + } + } else { + r.length = strlen(r.base); + } + + tresult = dns_rdatatype_fromtext(&type, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR, + "'%.*s' is not a valid type", + (int)r.length, r.base); + result = tresult; + } + } + } + return (result); +} + +typedef struct { + const char *name; + unsigned int allowed; +} optionstable; + +static isc_result_t +check_nonzero(const cfg_obj_t *options, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *obj = NULL; + unsigned int i; + + static const char *nonzero[] = { "max-retry-time", "min-retry-time", + "max-refresh-time", + "min-refresh-time" }; + /* + * Check if value is zero. + */ + for (i = 0; i < sizeof(nonzero) / sizeof(nonzero[0]); i++) { + obj = NULL; + if (cfg_map_get(options, nonzero[i], &obj) == ISC_R_SUCCESS && + cfg_obj_asuint32(obj) == 0) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'%s' must not be zero", nonzero[i]); + result = ISC_R_FAILURE; + } + } + return (result); +} + +/*% + * Check whether NOTIFY configuration at the zone level is acceptable for a + * mirror zone. Return true if it is; return false otherwise. + */ +static bool +check_mirror_zone_notify(const cfg_obj_t *zoptions, const char *znamestr, + isc_log_t *logctx) { + bool notify_configuration_ok = true; + const cfg_obj_t *obj = NULL; + + (void)cfg_map_get(zoptions, "notify", &obj); + if (obj == NULL) { + /* + * "notify" not set at zone level. This is fine. + */ + return (true); + } + + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + /* + * "notify yes;" set at zone level. This is an error. + */ + notify_configuration_ok = false; + } + } else { + const char *notifystr = cfg_obj_asstring(obj); + if (strcasecmp(notifystr, "explicit") != 0) { + /* + * Something else than "notify explicit;" set at zone + * level. This is an error. + */ + notify_configuration_ok = false; + } + } + + if (!notify_configuration_ok) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': mirror zones can only be used with " + "'notify no;' or 'notify explicit;'", + znamestr); + } + + return (notify_configuration_ok); +} + +/*% + * Try to determine whether recursion is available in a view without resorting + * to extraordinary measures: just check the "recursion" and "allow-recursion" + * settings. The point is to prevent accidental mirror zone misuse rather than + * to enforce some sort of policy. Recursion is assumed to be allowed by + * default if it is not explicitly disabled. + */ +static bool +check_recursion(const cfg_obj_t *config, const cfg_obj_t *voptions, + const cfg_obj_t *goptions, isc_log_t *logctx, + cfg_aclconfctx_t *actx, isc_mem_t *mctx) { + dns_acl_t *acl = NULL; + const cfg_obj_t *obj; + isc_result_t result; + bool retval = true; + + /* + * Check the "recursion" option first. + */ + obj = NULL; + result = ISC_R_NOTFOUND; + if (voptions != NULL) { + result = cfg_map_get(voptions, "recursion", &obj); + } + if (result != ISC_R_SUCCESS && goptions != NULL) { + result = cfg_map_get(goptions, "recursion", &obj); + } + if (result == ISC_R_SUCCESS && !cfg_obj_asboolean(obj)) { + retval = false; + goto cleanup; + } + + /* + * If recursion is not disabled by the "recursion" option, check + * whether it is disabled by the "allow-recursion" ACL. + */ + obj = NULL; + result = ISC_R_NOTFOUND; + if (voptions != NULL) { + result = cfg_map_get(voptions, "allow-recursion", &obj); + } + if (result != ISC_R_SUCCESS && goptions != NULL) { + result = cfg_map_get(goptions, "allow-recursion", &obj); + } + if (result == ISC_R_SUCCESS) { + result = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0, + &acl); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + retval = !dns_acl_isnone(acl); + } + +cleanup: + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (retval); +} + +static isc_result_t +check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, + const cfg_obj_t *config, isc_symtab_t *symtab, + isc_symtab_t *files, isc_symtab_t *keydirs, isc_symtab_t *inview, + const char *viewname, dns_rdataclass_t defclass, + bool nodeprecate, cfg_aclconfctx_t *actx, isc_log_t *logctx, + isc_mem_t *mctx) { + const char *znamestr; + const char *typestr = NULL; + const char *target = NULL; + unsigned int ztype; + const cfg_obj_t *zoptions, *goptions = NULL; + const cfg_obj_t *obj = NULL, *kasp = NULL; + const cfg_obj_t *inviewobj = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + unsigned int i; + dns_rdataclass_t zclass; + dns_fixedname_t fixedname; + dns_name_t *zname = NULL; /* NULL if parsing of zone name fails. */ + isc_buffer_t b; + bool root = false; + bool rfc1918 = false; + bool ula = false; + const cfg_listelt_t *element; + bool dlz; + bool ddns = false; + bool has_dnssecpolicy = false; + const void *clauses = NULL; + const char *option = NULL; + const char *kaspname = NULL; + const char *dir = NULL; + static const char *acls[] = { + "allow-notify", + "allow-transfer", + "allow-update", + "allow-update-forwarding", + }; + static optionstable dialups[] = { + { "notify", CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY }, + { "notify-passive", CFG_ZONE_SECONDARY }, + { "passive", CFG_ZONE_SECONDARY | CFG_ZONE_STUB }, + { "refresh", CFG_ZONE_SECONDARY | CFG_ZONE_STUB }, + }; + static const char *sources[] = { + "transfer-source", "transfer-source-v6", "notify-source", + "notify-source-v6", "parental-source", "parental-source-v6", + }; + + znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + + zoptions = cfg_tuple_get(zconfig, "options"); + + if (config != NULL) { + cfg_map_get(config, "options", &goptions); + } + + inviewobj = NULL; + (void)cfg_map_get(zoptions, "in-view", &inviewobj); + if (inviewobj != NULL) { + target = cfg_obj_asstring(inviewobj); + ztype = CFG_ZONE_INVIEW; + } else { + obj = NULL; + (void)cfg_map_get(zoptions, "type", &obj); + if (obj == NULL) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': type not present", znamestr); + return (ISC_R_FAILURE); + } + + typestr = cfg_obj_asstring(obj); + if (strcasecmp(typestr, "master") == 0 || + strcasecmp(typestr, "primary") == 0) + { + ztype = CFG_ZONE_PRIMARY; + } else if (strcasecmp(typestr, "slave") == 0 || + strcasecmp(typestr, "secondary") == 0) + { + ztype = CFG_ZONE_SECONDARY; + } else if (strcasecmp(typestr, "mirror") == 0) { + ztype = CFG_ZONE_MIRROR; + } else if (strcasecmp(typestr, "stub") == 0) { + ztype = CFG_ZONE_STUB; + } else if (strcasecmp(typestr, "static-stub") == 0) { + ztype = CFG_ZONE_STATICSTUB; + } else if (strcasecmp(typestr, "forward") == 0) { + ztype = CFG_ZONE_FORWARD; + } else if (strcasecmp(typestr, "hint") == 0) { + ztype = CFG_ZONE_HINT; + } else if (strcasecmp(typestr, "delegation-only") == 0) { + ztype = CFG_ZONE_DELEGATION; + if (!nodeprecate) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'type delegation-only' is " + "deprecated"); + } + } else if (strcasecmp(typestr, "redirect") == 0) { + ztype = CFG_ZONE_REDIRECT; + } else { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': invalid type %s", znamestr, + typestr); + return (ISC_R_FAILURE); + } + + if (ztype == CFG_ZONE_REDIRECT && strcmp(znamestr, ".") != 0) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "redirect zones must be called \".\""); + return (ISC_R_FAILURE); + } + } + + obj = cfg_tuple_get(zconfig, "class"); + if (cfg_obj_isstring(obj)) { + isc_textregion_t r; + + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + result = dns_rdataclass_fromtext(&zclass, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': invalid class %s", znamestr, + r.base); + return (ISC_R_FAILURE); + } + if (zclass != defclass) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': class '%s' does not " + "match view/default class", + znamestr, r.base); + return (ISC_R_FAILURE); + } + } else { + zclass = defclass; + } + + /* + * Look for an already existing zone. + * We need to make this canonical as isc_symtab_define() + * deals with strings. + */ + dns_fixedname_init(&fixedname); + isc_buffer_constinit(&b, znamestr, strlen(znamestr)); + isc_buffer_add(&b, strlen(znamestr)); + tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b, + dns_rootname, DNS_NAME_DOWNCASE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': is not a valid name", znamestr); + result = ISC_R_FAILURE; + } else { + char namebuf[DNS_NAME_FORMATSIZE + 128]; + char *tmp = namebuf; + size_t len = sizeof(namebuf); + + zname = dns_fixedname_name(&fixedname); + dns_name_format(zname, namebuf, sizeof(namebuf)); + tresult = nameexist(zconfig, namebuf, + ztype == CFG_ZONE_HINT ? 1 + : ztype == CFG_ZONE_REDIRECT ? 2 + : 3, + symtab, + "zone '%s': already exists " + "previous definition: %s:%u", + logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + if (dns_name_equal(zname, dns_rootname)) { + root = true; + } else if (dns_name_isrfc1918(zname)) { + rfc1918 = true; + } else if (dns_name_isula(zname)) { + ula = true; + } + len -= strlen(tmp); + tmp += strlen(tmp); + (void)snprintf(tmp, len, "%u/%s", zclass, + (ztype == CFG_ZONE_INVIEW) ? target + : (viewname != NULL) ? viewname + : "_default"); + switch (ztype) { + case CFG_ZONE_INVIEW: + tresult = isc_symtab_lookup(inview, namebuf, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(inviewobj, logctx, ISC_LOG_ERROR, + "'in-view' zone '%s' " + "does not exist in view '%s', " + "or view '%s' is not yet defined", + znamestr, target, target); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + break; + + case CFG_ZONE_FORWARD: + case CFG_ZONE_REDIRECT: + case CFG_ZONE_DELEGATION: + break; + + case CFG_ZONE_PRIMARY: + case CFG_ZONE_SECONDARY: + case CFG_ZONE_MIRROR: + case CFG_ZONE_HINT: + case CFG_ZONE_STUB: + case CFG_ZONE_STATICSTUB: + tmp = isc_mem_strdup(mctx, namebuf); + { + isc_symvalue_t symvalue; + symvalue.as_cpointer = NULL; + tresult = isc_symtab_define( + inview, tmp, 1, symvalue, + isc_symexists_replace); + if (tresult == ISC_R_NOMEMORY) { + isc_mem_free(mctx, tmp); + } + if (result == ISC_R_SUCCESS && + tresult != ISC_R_SUCCESS) + { + result = tresult; + } + } + break; + + default: + UNREACHABLE(); + } + } + + if (ztype == CFG_ZONE_INVIEW) { + const cfg_obj_t *fwd = NULL; + unsigned int maxopts = 1; + + (void)cfg_map_get(zoptions, "forward", &fwd); + if (fwd != NULL) { + maxopts++; + } + fwd = NULL; + (void)cfg_map_get(zoptions, "forwarders", &fwd); + if (fwd != NULL) { + maxopts++; + } + if (cfg_map_count(zoptions) > maxopts) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': 'in-view' used " + "with incompatible zone options", + znamestr); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + return (result); + } + + /* + * Check if value is zero. + */ + if (check_nonzero(zoptions, logctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + /* + * Check if a dnssec-policy is set. + */ + obj = NULL; + (void)cfg_map_get(zoptions, "dnssec-policy", &obj); + if (obj == NULL && voptions != NULL) { + (void)cfg_map_get(voptions, "dnssec-policy", &obj); + } + if (obj == NULL && goptions != NULL) { + (void)cfg_map_get(goptions, "dnssec-policy", &obj); + } + if (obj != NULL) { + const cfg_obj_t *kasps = NULL; + + kaspname = cfg_obj_asstring(obj); + if (strcmp(kaspname, "default") == 0) { + has_dnssecpolicy = true; + } else if (strcmp(kaspname, "insecure") == 0) { + has_dnssecpolicy = true; + } else if (strcmp(kaspname, "none") == 0) { + has_dnssecpolicy = false; + } else { + (void)cfg_map_get(config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *kobj = cfg_tuple_get( + cfg_listelt_value(element), "name"); + if (strcmp(kaspname, cfg_obj_asstring(kobj)) == + 0) + { + has_dnssecpolicy = true; + } + } + + if (!has_dnssecpolicy) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': option " + "'dnssec-policy %s' has no " + "matching dnssec-policy config", + znamestr, kaspname); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + if (has_dnssecpolicy) { + kasp = obj; + } + } + + /* + * Warn about zones with both dnssec-policy and max-zone-ttl + */ + if (has_dnssecpolicy) { + obj = NULL; + (void)cfg_map_get(zoptions, "max-zone-ttl", &obj); + if (obj == NULL && voptions != NULL) { + (void)cfg_map_get(voptions, "max-zone-ttl", &obj); + } + if (obj == NULL && goptions != NULL) { + (void)cfg_map_get(goptions, "max-zone-ttl", &obj); + } + if (obj != NULL) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "zone '%s': option 'max-zone-ttl' " + "is ignored when used together with " + "'dnssec-policy'", + znamestr); + } + } + + /* + * Check validity of the zone options. + */ + option = cfg_map_firstclause(&cfg_type_zoneopts, &clauses, &i); + while (option != NULL) { + obj = NULL; + if (cfg_map_get(zoptions, option, &obj) == ISC_R_SUCCESS && + obj != NULL && !cfg_clause_validforzone(option, ztype)) + { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "option '%s' is not allowed " + "in '%s' zone '%s'", + option, typestr, znamestr); + result = ISC_R_FAILURE; + } + option = cfg_map_nextclause(&cfg_type_zoneopts, &clauses, &i); + } + + /* + * Check that ACLs expand correctly. + */ + for (i = 0; i < ARRAY_SIZE(acls); i++) { + tresult = checkacl(acls[i], actx, zconfig, voptions, config, + logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + /* + * Only a limited subset of all possible "notify" settings can be used + * at the zone level for mirror zones. + */ + if (ztype == CFG_ZONE_MIRROR && + !check_mirror_zone_notify(zoptions, znamestr, logctx)) + { + result = ISC_R_FAILURE; + } + + /* + * Primary, secondary, and mirror zones may have an "also-notify" + * field, but shouldn't if notify is disabled. + */ + if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY || + ztype == CFG_ZONE_MIRROR) + { + bool donotify = true; + + obj = NULL; + tresult = cfg_map_get(zoptions, "notify", &obj); + if (tresult != ISC_R_SUCCESS && voptions != NULL) { + tresult = cfg_map_get(voptions, "notify", &obj); + } + if (tresult != ISC_R_SUCCESS && goptions != NULL) { + tresult = cfg_map_get(goptions, "notify", &obj); + } + if (tresult == ISC_R_SUCCESS) { + if (cfg_obj_isboolean(obj)) { + donotify = cfg_obj_asboolean(obj); + } else { + const char *str = cfg_obj_asstring(obj); + if (ztype != CFG_ZONE_PRIMARY && + (strcasecmp(str, "master-only") == 0 || + strcasecmp(str, "primary-only") == 0)) + { + donotify = false; + } + } + } + + obj = NULL; + tresult = cfg_map_get(zoptions, "also-notify", &obj); + if (tresult == ISC_R_SUCCESS && !donotify) { + cfg_obj_log(zoptions, logctx, ISC_LOG_WARNING, + "zone '%s': 'also-notify' set but " + "'notify' is disabled", + znamestr); + } + if (tresult != ISC_R_SUCCESS && voptions != NULL) { + tresult = cfg_map_get(voptions, "also-notify", &obj); + } + if (tresult != ISC_R_SUCCESS && goptions != NULL) { + tresult = cfg_map_get(goptions, "also-notify", &obj); + } + if (tresult == ISC_R_SUCCESS && donotify) { + uint32_t count; + tresult = validate_remotes("primaries", obj, config, + &count, logctx, mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + { + result = tresult; + } + } + } + + /* + * Secondary, mirror, and stub zones must have a "primaries" field, + * with one exception: when mirroring the root zone, a default, + * built-in primary server list is used in the absence of one + * explicitly specified. + */ + if (ztype == CFG_ZONE_SECONDARY || ztype == CFG_ZONE_STUB || + (ztype == CFG_ZONE_MIRROR && zname != NULL && + !dns_name_equal(zname, dns_rootname))) + { + obj = NULL; + (void)cfg_map_get(zoptions, "primaries", &obj); + if (obj == NULL) { + /* If "primaries" was unset, check for "masters" */ + (void)cfg_map_get(zoptions, "masters", &obj); + } else { + const cfg_obj_t *obj2 = NULL; + + /* ...bug if it was set, "masters" must not be. */ + (void)cfg_map_get(zoptions, "masters", &obj2); + if (obj2 != NULL) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'primaries' and 'masters' cannot " + "both be used in the same zone"); + result = ISC_R_FAILURE; + } + } + if (obj == NULL) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': missing 'primaries' entry", + znamestr); + result = ISC_R_FAILURE; + } else { + uint32_t count; + tresult = validate_remotes("primaries", obj, config, + &count, logctx, mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + { + result = tresult; + } + if (tresult == ISC_R_SUCCESS && count == 0) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': " + "empty 'primaries' entry", + znamestr); + result = ISC_R_FAILURE; + } + } + } + + /* + * Warn if *-source and *-source-v6 options specify a port, + * and fail if they specify the default listener port. + */ + for (i = 0; i < ARRAY_SIZE(sources); i++) { + obj = NULL; + (void)cfg_map_get(zoptions, sources[i], &obj); + if (obj == NULL && goptions != NULL) { + (void)cfg_map_get(goptions, sources[i], &obj); + } + if (obj != NULL) { + in_port_t port = + isc_sockaddr_getport(cfg_obj_assockaddr(obj)); + if (port == dnsport) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'%s' cannot specify the " + "DNS listener port (%d)", + sources[i], port); + result = ISC_R_FAILURE; + } else if (port != 0) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'%s': specifying a port is " + "not recommended", + sources[i]); + } + } + } + + /* + * Primary and secondary zones that have a "parental-agents" field, + * must have a corresponding "parental-agents" clause. + */ + if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) { + obj = NULL; + (void)cfg_map_get(zoptions, "parental-agents", &obj); + if (obj != NULL) { + uint32_t count; + tresult = validate_remotes("parental-agents", obj, + config, &count, logctx, + mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + { + result = tresult; + } + if (tresult == ISC_R_SUCCESS && count == 0) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': " + "empty 'parental-agents' entry", + znamestr); + result = ISC_R_FAILURE; + } + } + } + + /* + * Configuring a mirror zone and disabling recursion at the same time + * contradicts the purpose of the former. + */ + if (ztype == CFG_ZONE_MIRROR && + !check_recursion(config, voptions, goptions, logctx, actx, mctx)) + { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': mirror zones cannot be used if " + "recursion is disabled", + znamestr); + result = ISC_R_FAILURE; + } + + /* + * Primary zones can't have both "allow-update" and "update-policy". + */ + if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) { + bool signing = false; + isc_result_t res1, res2, res3; + const cfg_obj_t *au = NULL; + const char *arg; + + obj = NULL; + res1 = cfg_map_get(zoptions, "allow-update", &au); + obj = NULL; + res2 = cfg_map_get(zoptions, "update-policy", &obj); + if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': 'allow-update' is ignored " + "when 'update-policy' is present", + znamestr); + result = ISC_R_FAILURE; + } else if (res2 == ISC_R_SUCCESS) { + res3 = check_update_policy(obj, logctx); + if (res3 != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + /* + * To determine whether auto-dnssec is allowed, + * we should also check for allow-update at the + * view and options levels. + */ + if (res1 != ISC_R_SUCCESS && voptions != NULL) { + res1 = cfg_map_get(voptions, "allow-update", &au); + } + if (res1 != ISC_R_SUCCESS && goptions != NULL) { + res1 = cfg_map_get(goptions, "allow-update", &au); + } + + if (res2 == ISC_R_SUCCESS) { + ddns = true; + } else if (res1 == ISC_R_SUCCESS) { + dns_acl_t *acl = NULL; + res1 = cfg_acl_fromconfig(au, config, logctx, actx, + mctx, 0, &acl); + if (res1 != ISC_R_SUCCESS) { + cfg_obj_log(au, logctx, ISC_LOG_ERROR, + "acl expansion failed: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + } else if (acl != NULL) { + if (!dns_acl_isnone(acl)) { + ddns = true; + } + dns_acl_detach(&acl); + } + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "inline-signing", &obj); + if (res1 == ISC_R_SUCCESS) { + signing = cfg_obj_asboolean(obj); + } + + if (has_dnssecpolicy) { + if (!ddns && !signing) { + cfg_obj_log(kasp, logctx, ISC_LOG_ERROR, + "'inline-signing yes;' must also " + "be configured explicitly for " + "zones using dnssec-policy%s. See " + "https://kb.isc.org/docs/" + "dnssec-policy-requires-dynamic-" + "dns-or-inline-signing", + (ztype == CFG_ZONE_PRIMARY) + ? " without a configured " + "'allow-update' or " + "'update-policy'" + : ""); + result = ISC_R_FAILURE; + } + } + + obj = NULL; + arg = "off"; + res3 = cfg_map_get(zoptions, "auto-dnssec", &obj); + if (res3 == ISC_R_SUCCESS) { + arg = cfg_obj_asstring(obj); + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'auto-dnssec' option is deprecated and " + "will be removed in BIND 9.19. Please " + "migrate to dnssec-policy"); + } + if (strcasecmp(arg, "off") != 0) { + if (!ddns && !signing && !has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'auto-dnssec %s;' requires%s " + "inline-signing to be configured " + "for the zone", + arg, + (ztype == CFG_ZONE_PRIMARY) + ? " dynamic DNS or" + : ""); + result = ISC_R_FAILURE; + } + + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'auto-dnssec %s;' cannot be " + "configured if dnssec-policy is " + "also set", + arg); + result = ISC_R_FAILURE; + } + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "sig-signing-type", &obj); + if (res1 == ISC_R_SUCCESS) { + uint32_t type = cfg_obj_asuint32(obj); + if (type < 0xff00U || type > 0xffffU) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "sig-signing-type: %u out of " + "range [%u..%u]", + type, 0xff00U, 0xffffU); + result = ISC_R_FAILURE; + } + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-dnskey-kskonly", &obj); + if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY && + !signing) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-dnskey-kskonly: requires " + "inline-signing when used in secondary " + "zone"); + result = ISC_R_FAILURE; + } + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-dnskey-kskonly: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-secure-to-insecure", &obj); + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-secure-to-insecure: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj); + if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY && + !signing) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-loadkeys-interval: requires " + "inline-signing when used in secondary " + "zone"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "update-check-ksk", &obj); + if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY && + !signing) + { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "update-check-ksk: requires " + "inline-signing when used in secondary " + "zone"); + result = ISC_R_FAILURE; + } + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "update-check-ksk: cannot be configured " + "if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-update-mode", &obj); + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-update-mode: cannot be configured " + "if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + } + + /* + * Check the excessively complicated "dialup" option. + */ + if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY || + ztype == CFG_ZONE_STUB) + { + obj = NULL; + (void)cfg_map_get(zoptions, "dialup", &obj); + if (obj != NULL && cfg_obj_isstring(obj)) { + const char *str = cfg_obj_asstring(obj); + for (i = 0; i < sizeof(dialups) / sizeof(dialups[0]); + i++) + { + if (strcasecmp(dialups[i].name, str) != 0) { + continue; + } + if ((dialups[i].allowed & ztype) == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dialup type '%s' is not " + "allowed in '%s' " + "zone '%s'", + str, typestr, znamestr); + result = ISC_R_FAILURE; + } + break; + } + if (i == sizeof(dialups) / sizeof(dialups[0])) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "invalid dialup type '%s' in zone " + "'%s'", + str, znamestr); + result = ISC_R_FAILURE; + } + } + } + + /* + * Check that forwarding is reasonable. + */ + obj = NULL; + if (root) { + if (voptions != NULL) { + (void)cfg_map_get(voptions, "forwarders", &obj); + } + if (obj == NULL && goptions != NULL) { + (void)cfg_map_get(goptions, "forwarders", &obj); + } + } + if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + /* + * Check that a RFC 1918 / ULA reverse zone is not forward first + * unless explicitly configured to be so. + */ + if (ztype == CFG_ZONE_FORWARD && (rfc1918 || ula)) { + obj = NULL; + (void)cfg_map_get(zoptions, "forward", &obj); + if (obj == NULL) { + /* + * Forward mode not explicitly configured. + */ + if (voptions != NULL) { + cfg_map_get(voptions, "forward", &obj); + } + if (obj == NULL && goptions != NULL) { + cfg_map_get(goptions, "forward", &obj); + } + if (obj == NULL || + strcasecmp(cfg_obj_asstring(obj), "first") == 0) + { + cfg_obj_log(zconfig, logctx, ISC_LOG_WARNING, + "inherited 'forward first;' for " + "%s zone '%s' - did you want " + "'forward only;'?", + rfc1918 ? "rfc1918" : "ula", + znamestr); + } + } + } + + /* + * Check validity of static stub server addresses. + */ + obj = NULL; + (void)cfg_map_get(zoptions, "server-addresses", &obj); + if (ztype == CFG_ZONE_STATICSTUB && obj != NULL) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + isc_sockaddr_t sa; + isc_netaddr_t na; + obj = cfg_listelt_value(element); + sa = *cfg_obj_assockaddr(obj); + + isc_netaddr_fromsockaddr(&na, &sa); + if (isc_netaddr_getzone(&na) != 0) { + result = ISC_R_FAILURE; + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "scoped address is not allowed " + "for static stub " + "server-addresses"); + } + } + } + + /* + * Check validity of static stub server names. + */ + obj = NULL; + (void)cfg_map_get(zoptions, "server-names", &obj); + if (zname != NULL && ztype == CFG_ZONE_STATICSTUB && obj != NULL) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + const char *snamestr; + dns_fixedname_t fixed_sname; + isc_buffer_t b2; + dns_name_t *sname; + + obj = cfg_listelt_value(element); + snamestr = cfg_obj_asstring(obj); + + isc_buffer_constinit(&b2, snamestr, strlen(snamestr)); + isc_buffer_add(&b2, strlen(snamestr)); + sname = dns_fixedname_initname(&fixed_sname); + tresult = dns_name_fromtext(sname, &b2, dns_rootname, 0, + NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "server-name '%s' is not a valid " + "name", + snamestr); + result = ISC_R_FAILURE; + } else if (dns_name_issubdomain(sname, zname)) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "server-name '%s' must not be a " + "subdomain of zone name '%s'", + snamestr, znamestr); + result = ISC_R_FAILURE; + } + } + } + + /* + * Warn if key-directory doesn't exist + */ + obj = NULL; + (void)cfg_map_get(zoptions, "key-directory", &obj); + if (obj == NULL && voptions != NULL) { + (void)cfg_map_get(voptions, "key-directory", &obj); + } + if (obj == NULL && goptions != NULL) { + (void)cfg_map_get(goptions, "key-directory", &obj); + } + if (obj != NULL) { + dir = cfg_obj_asstring(obj); + + tresult = isc_file_isdirectory(dir); + switch (tresult) { + case ISC_R_SUCCESS: + break; + case ISC_R_FILENOTFOUND: + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "key-directory: '%s' does not exist", dir); + break; + case ISC_R_INVALIDFILE: + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "key-directory: '%s' is not a directory", + dir); + break; + default: + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "key-directory: '%s' %s", dir, + isc_result_totext(tresult)); + result = tresult; + } + } + + /* + * Make sure there is no other zone with the same + * key-directory and a different dnssec-policy. + */ + if (zname != NULL) { + char keydirbuf[DNS_NAME_FORMATSIZE + 128]; + char *tmp = keydirbuf; + size_t len = sizeof(keydirbuf); + dns_name_format(zname, keydirbuf, sizeof(keydirbuf)); + len -= strlen(tmp); + tmp += strlen(tmp); + (void)snprintf(tmp, len, "/%s", (dir == NULL) ? "(null)" : dir); + tresult = keydirexist(zconfig, (const char *)keydirbuf, + kaspname, keydirs, logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + /* + * Check various options. + */ + tresult = check_options(zoptions, config, logctx, mctx, optlevel_zone); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + /* + * If the zone type is rbt/rbt64 then primary/hint zones require file + * clauses. If inline-signing is used, then secondary zones require a + * file clause as well. + */ + obj = NULL; + dlz = false; + tresult = cfg_map_get(zoptions, "dlz", &obj); + if (tresult == ISC_R_SUCCESS) { + dlz = true; + } + + obj = NULL; + tresult = cfg_map_get(zoptions, "database", &obj); + if (dlz && tresult == ISC_R_SUCCESS) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': cannot specify both 'dlz' " + "and 'database'", + znamestr); + result = ISC_R_FAILURE; + } else if (!dlz && (tresult == ISC_R_NOTFOUND || + (tresult == ISC_R_SUCCESS && + (strcmp("rbt", cfg_obj_asstring(obj)) == 0 || + strcmp("rbt64", cfg_obj_asstring(obj)) == 0)))) + { + isc_result_t res1; + const cfg_obj_t *fileobj = NULL; + tresult = cfg_map_get(zoptions, "file", &fileobj); + obj = NULL; + res1 = cfg_map_get(zoptions, "inline-signing", &obj); + if ((tresult != ISC_R_SUCCESS && + (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_HINT || + (ztype == CFG_ZONE_SECONDARY && res1 == ISC_R_SUCCESS && + cfg_obj_asboolean(obj))))) + { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': missing 'file' entry", + znamestr); + result = tresult; + } else if (tresult == ISC_R_SUCCESS && + (ztype == CFG_ZONE_SECONDARY || + ztype == CFG_ZONE_MIRROR || ddns || + has_dnssecpolicy)) + { + tresult = fileexist(fileobj, files, true, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } else if (tresult == ISC_R_SUCCESS && + (ztype == CFG_ZONE_PRIMARY || + ztype == CFG_ZONE_HINT)) + { + tresult = fileexist(fileobj, files, false, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + return (result); +} + +typedef struct keyalgorithms { + const char *name; + uint16_t size; +} algorithmtable; + +isc_result_t +bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) { + const cfg_obj_t *algobj = NULL; + const cfg_obj_t *secretobj = NULL; + const char *keyname = cfg_obj_asstring(cfg_map_getname(key)); + const char *algorithm; + int i; + size_t len = 0; + isc_result_t result; + isc_buffer_t buf; + unsigned char secretbuf[1024]; + static const algorithmtable algorithms[] = { + { "hmac-md5", 128 }, + { "hmac-md5.sig-alg.reg.int", 0 }, + { "hmac-md5.sig-alg.reg.int.", 0 }, + { "hmac-sha1", 160 }, + { "hmac-sha224", 224 }, + { "hmac-sha256", 256 }, + { "hmac-sha384", 384 }, + { "hmac-sha512", 512 }, + { NULL, 0 } + }; + + (void)cfg_map_get(key, "algorithm", &algobj); + (void)cfg_map_get(key, "secret", &secretobj); + if (secretobj == NULL || algobj == NULL) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s' must have both 'secret' and " + "'algorithm' defined", + keyname); + return (ISC_R_FAILURE); + } + + isc_buffer_init(&buf, secretbuf, sizeof(secretbuf)); + result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR, "bad secret '%s'", + isc_result_totext(result)); + return (result); + } + + algorithm = cfg_obj_asstring(algobj); + for (i = 0; algorithms[i].name != NULL; i++) { + len = strlen(algorithms[i].name); + if (strncasecmp(algorithms[i].name, algorithm, len) == 0 && + (algorithm[len] == '\0' || + (algorithms[i].size != 0 && algorithm[len] == '-'))) + { + break; + } + } + if (algorithms[i].name == NULL) { + cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, + "unknown algorithm '%s'", algorithm); + return (ISC_R_NOTFOUND); + } + if (algorithm[len] == '-') { + uint16_t digestbits; + result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10); + if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) { + if (result == ISC_R_RANGE || + digestbits > algorithms[i].size) + { + cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, + "key '%s' digest-bits too large " + "[%u..%u]", + keyname, algorithms[i].size / 2, + algorithms[i].size); + return (ISC_R_RANGE); + } + if ((digestbits % 8) != 0) { + cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, + "key '%s' digest-bits not multiple" + " of 8", + keyname); + return (ISC_R_RANGE); + } + /* + * Recommended minima for hmac algorithms. + */ + if ((digestbits < (algorithms[i].size / 2U) || + (digestbits < 80U))) + { + cfg_obj_log(algobj, logctx, ISC_LOG_WARNING, + "key '%s' digest-bits too small " + "[<%u]", + keyname, algorithms[i].size / 2); + } + } else { + cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, + "key '%s': unable to parse digest-bits", + keyname); + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable, + isc_log_t *logctx) { + isc_result_t result; + isc_symvalue_t symvalue; + unsigned int line; + const char *file; + + result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 0, &symvalue); + if (result == ISC_R_SUCCESS) { + if (writeable) { + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "writeable file '%s': already in use: " + "%s:%u", + cfg_obj_asstring(obj), file, line); + return (ISC_R_EXISTS); + } + result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 2, + &symvalue); + if (result == ISC_R_SUCCESS) { + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "writeable file '%s': already in use: " + "%s:%u", + cfg_obj_asstring(obj), file, line); + return (ISC_R_EXISTS); + } + return (ISC_R_SUCCESS); + } + + symvalue.as_cpointer = obj; + result = isc_symtab_define(symtab, cfg_obj_asstring(obj), + writeable ? 2 : 1, symvalue, + isc_symexists_reject); + return (result); +} + +static isc_result_t +keydirexist(const cfg_obj_t *zcfg, const char *keydir, const char *kaspnamestr, + isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t result; + isc_symvalue_t symvalue; + char *symkey; + + if (kaspnamestr == NULL || strcmp(kaspnamestr, "none") == 0) { + return (ISC_R_SUCCESS); + } + + result = isc_symtab_lookup(symtab, keydir, 0, &symvalue); + if (result == ISC_R_SUCCESS) { + const cfg_obj_t *kasp = NULL; + const cfg_obj_t *exist = symvalue.as_cpointer; + const char *file = cfg_obj_file(exist); + unsigned int line = cfg_obj_line(exist); + + /* + * Having the same key-directory for the same zone is fine + * iff the zone is using the same policy, or has no policy. + */ + (void)cfg_map_get(cfg_tuple_get(exist, "options"), + "dnssec-policy", &kasp); + if (kasp == NULL || + strcmp(cfg_obj_asstring(kasp), "none") == 0 || + strcmp(cfg_obj_asstring(kasp), kaspnamestr) == 0) + { + return (ISC_R_SUCCESS); + } + + cfg_obj_log(zcfg, logctx, ISC_LOG_ERROR, + "key-directory '%s' already in use by zone %s with " + "policy %s: %s:%u", + keydir, + cfg_obj_asstring(cfg_tuple_get(exist, "name")), + cfg_obj_asstring(kasp), file, line); + return (ISC_R_EXISTS); + } + + /* + * Add the new zone plus key-directory. + */ + symkey = isc_mem_strdup(mctx, keydir); + symvalue.as_cpointer = zcfg; + result = isc_symtab_define(symtab, symkey, 2, symvalue, + isc_symexists_reject); + return (result); +} + +/* + * Check key list for duplicates key names and that the key names + * are valid domain names as these keys are used for TSIG. + * + * Check the key contents for validity. + */ +static isc_result_t +check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, isc_mem_t *mctx, + isc_log_t *logctx) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + + name = dns_fixedname_initname(&fname); + for (element = cfg_list_first(keys); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *key = cfg_listelt_value(element); + const char *keyid = cfg_obj_asstring(cfg_map_getname(key)); + isc_symvalue_t symvalue; + isc_buffer_t b; + char *keyname; + + isc_buffer_constinit(&b, keyid, strlen(keyid)); + isc_buffer_add(&b, strlen(keyid)); + tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s': bad key name", keyid); + result = tresult; + continue; + } + tresult = bind9_check_key(key, logctx); + if (tresult != ISC_R_SUCCESS) { + return (tresult); + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + keyname = isc_mem_strdup(mctx, namebuf); + symvalue.as_cpointer = key; + tresult = isc_symtab_define(symtab, keyname, 1, symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + const char *file; + unsigned int line; + + RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname, 1, + &symvalue) == + ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + + if (file == NULL) { + file = "<unknown file>"; + } + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s': already exists " + "previous definition: %s:%u", + keyid, file, line); + isc_mem_free(mctx, keyname); + result = tresult; + } else if (tresult != ISC_R_SUCCESS) { + isc_mem_free(mctx, keyname); + return (tresult); + } + } + return (result); +} + +/* + * RNDC keys are not normalised unlike TSIG keys. + * + * "foo." is different to "foo". + */ +static bool +rndckey_exists(const cfg_obj_t *keylist, const char *keyname) { + const cfg_listelt_t *element; + const cfg_obj_t *obj; + const char *str; + + if (keylist == NULL) { + return (false); + } + + 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)) { + return (true); + } + } + return (false); +} + +static struct { + const char *v4; + const char *v6; +} sources[] = { { "transfer-source", "transfer-source-v6" }, + { "notify-source", "notify-source-v6" }, + { "parental-source", "parental-source-v6" }, + { "query-source", "query-source-v6" }, + { NULL, NULL } }; + +static struct { + const char *name; + isc_result_t (*set)(dns_peer_t *peer, bool newval); +} bools[] = { + { "bogus", dns_peer_setbogus }, + { "edns", dns_peer_setsupportedns }, + { "provide-ixfr", dns_peer_setprovideixfr }, + { "request-expire", dns_peer_setrequestexpire }, + { "request-ixfr", dns_peer_setrequestixfr }, + { "request-nsid", dns_peer_setrequestnsid }, + { "send-cookie", dns_peer_setsendcookie }, + { "tcp-keepalive", dns_peer_settcpkeepalive }, + { "tcp-only", dns_peer_setforcetcp }, +}; + +static isc_result_t +check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions, + isc_symtab_t *symtab, isc_mem_t *mctx, isc_log_t *logctx) { + dns_fixedname_t fname; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *e1, *e2; + const cfg_obj_t *v1, *v2, *keys; + const cfg_obj_t *servers; + isc_netaddr_t n1, n2; + unsigned int p1, p2; + const cfg_obj_t *obj; + char buf[ISC_NETADDR_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + const char *xfr; + const char *keyval; + isc_buffer_t b; + int source; + dns_name_t *keyname; + + servers = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "server", &servers); + } + if (servers == NULL) { + (void)cfg_map_get(config, "server", &servers); + } + if (servers == NULL) { + return (ISC_R_SUCCESS); + } + + for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) { + dns_peer_t *peer = NULL; + size_t i; + v1 = cfg_listelt_value(e1); + cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1); + /* + * Check that unused bits are zero. + */ + tresult = isc_netaddr_prefixok(&n1, p1); + if (tresult != ISC_R_SUCCESS) { + INSIST(tresult == ISC_R_FAILURE); + isc_netaddr_format(&n1, buf, sizeof(buf)); + cfg_obj_log(v1, logctx, ISC_LOG_ERROR, + "server '%s/%u': invalid prefix " + "(extra bits specified)", + buf, p1); + result = tresult; + } + source = 0; + do { + /* + * For a v6 server we can't specify a v4 source, + * and vice versa. + */ + obj = NULL; + if (n1.family == AF_INET) { + xfr = sources[source].v6; + } else { + xfr = sources[source].v4; + } + (void)cfg_map_get(v1, xfr, &obj); + if (obj != NULL) { + isc_netaddr_format(&n1, buf, sizeof(buf)); + cfg_obj_log(v1, logctx, ISC_LOG_ERROR, + "server '%s/%u': %s not legal", buf, + p1, xfr); + result = ISC_R_FAILURE; + } + + /* + * Check that we aren't using the DNS + * listener port (i.e. 53, or whatever was set + * as "port" in options) as a source port. + */ + obj = NULL; + if (n1.family == AF_INET) { + xfr = sources[source].v4; + } else { + xfr = sources[source].v6; + } + (void)cfg_map_get(v1, xfr, &obj); + if (obj != NULL) { + const isc_sockaddr_t *sa = + cfg_obj_assockaddr(obj); + in_port_t port = isc_sockaddr_getport(sa); + if (port == dnsport) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'%s' cannot specify the " + "DNS listener port (%d)", + xfr, port); + result = ISC_R_FAILURE; + } + } + } while (sources[++source].v4 != NULL); + e2 = e1; + while ((e2 = cfg_list_next(e2)) != NULL) { + v2 = cfg_listelt_value(e2); + cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2); + if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) { + const char *file = cfg_obj_file(v1); + unsigned int line = cfg_obj_line(v1); + + if (file == NULL) { + file = "<unknown file>"; + } + + isc_netaddr_format(&n2, buf, sizeof(buf)); + cfg_obj_log(v2, logctx, ISC_LOG_ERROR, + "server '%s/%u': already exists " + "previous definition: %s:%u", + buf, p2, file, line); + result = ISC_R_FAILURE; + } + } + keys = NULL; + cfg_map_get(v1, "keys", &keys); + if (keys != NULL) { + /* + * Normalize key name. + */ + keyval = cfg_obj_asstring(keys); + isc_buffer_constinit(&b, keyval, strlen(keyval)); + isc_buffer_add(&b, strlen(keyval)); + keyname = dns_fixedname_initname(&fname); + tresult = dns_name_fromtext(keyname, &b, dns_rootname, + 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(keys, logctx, ISC_LOG_ERROR, + "bad key name '%s'", keyval); + result = ISC_R_FAILURE; + continue; + } + dns_name_format(keyname, namebuf, sizeof(namebuf)); + tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(keys, logctx, ISC_LOG_ERROR, + "unknown key '%s'", keyval); + result = ISC_R_FAILURE; + } + } + (void)dns_peer_newprefix(mctx, &n1, p1, &peer); + for (i = 0; i < ARRAY_SIZE(bools); i++) { + const cfg_obj_t *opt = NULL; + cfg_map_get(v1, bools[i].name, &opt); + if (opt != NULL) { + tresult = (bools[i].set)( + peer, cfg_obj_asboolean(opt)); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(opt, logctx, ISC_LOG_ERROR, + "setting server option " + "'%s' failed: %s", + bools[i].name, + isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } + } + } + dns_peer_detach(&peer); + } + return (result); +} + +#define ROOT_KSK_STATIC 0x01 +#define ROOT_KSK_MANAGED 0x02 +#define ROOT_KSK_ANY 0x03 +#define ROOT_KSK_2010 0x04 +#define ROOT_KSK_2017 0x08 + +static isc_result_t +check_trust_anchor(const cfg_obj_t *key, bool managed, unsigned int *flagsp, + isc_log_t *logctx) { + const char *str = NULL, *namestr = NULL; + dns_fixedname_t fkeyname; + dns_name_t *keyname = NULL; + isc_buffer_t b; + isc_region_t r; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + uint32_t rdata1, rdata2, rdata3; + unsigned char data[4096]; + const char *atstr = NULL; + enum { + INIT_DNSKEY, + STATIC_DNSKEY, + INIT_DS, + STATIC_DS, + TRUSTED + } anchortype; + + /* + * The 2010 and 2017 IANA root keys - these are used below + * to check the contents of trusted, initial and + * static trust anchor configurations. + */ + static const unsigned char root_ksk_2010[] = { + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, 0x55, 0x66, + 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4, + 0x7e, 0xf5, 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, + 0x2c, 0xec, 0x90, 0x6d, 0x21, 0x16, 0xd0, 0xef, 0x20, 0x70, + 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, 0xe7, 0xc7, + 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, 0xc0, + 0x71, 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, + 0x83, 0xbc, 0x83, 0x43, 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, + 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, 0x51, 0xe5, 0x4f, + 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, 0x80, 0x25, + 0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, + 0x3d, 0xe8, 0xcf, 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, + 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, 0x5f, 0xa4, 0x52, 0xe8, + 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, 0xcf, 0x56, 0x34, + 0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, + 0xf5, 0xd9, 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, + 0x1b, 0x6e, 0x03, 0xa1, 0xb7, 0x2d, 0x0a, 0x73, 0x5b, 0x98, + 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, 0x23, 0x24, 0xf2, 0x7c, + 0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, + 0x38, 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, + 0xce, 0xc9, 0x07, 0x57, 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52, + 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, 0x01, 0xd4, 0xd3, 0x27, + 0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1, + 0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, + 0x75, 0xfc, 0x21, 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e, + 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, 0x4d, 0x62, 0x87, 0x3d + }; + static const unsigned char root_ksk_2017[] = { + 0x03, 0x01, 0x00, 0x01, 0xac, 0xff, 0xb4, 0x09, 0xbc, 0xc9, + 0x39, 0xf8, 0x31, 0xf7, 0xa1, 0xe5, 0xec, 0x88, 0xf7, 0xa5, + 0x92, 0x55, 0xec, 0x53, 0x04, 0x0b, 0xe4, 0x32, 0x02, 0x73, + 0x90, 0xa4, 0xce, 0x89, 0x6d, 0x6f, 0x90, 0x86, 0xf3, 0xc5, + 0xe1, 0x77, 0xfb, 0xfe, 0x11, 0x81, 0x63, 0xaa, 0xec, 0x7a, + 0xf1, 0x46, 0x2c, 0x47, 0x94, 0x59, 0x44, 0xc4, 0xe2, 0xc0, + 0x26, 0xbe, 0x5e, 0x98, 0xbb, 0xcd, 0xed, 0x25, 0x97, 0x82, + 0x72, 0xe1, 0xe3, 0xe0, 0x79, 0xc5, 0x09, 0x4d, 0x57, 0x3f, + 0x0e, 0x83, 0xc9, 0x2f, 0x02, 0xb3, 0x2d, 0x35, 0x13, 0xb1, + 0x55, 0x0b, 0x82, 0x69, 0x29, 0xc8, 0x0d, 0xd0, 0xf9, 0x2c, + 0xac, 0x96, 0x6d, 0x17, 0x76, 0x9f, 0xd5, 0x86, 0x7b, 0x64, + 0x7c, 0x3f, 0x38, 0x02, 0x9a, 0xbd, 0xc4, 0x81, 0x52, 0xeb, + 0x8f, 0x20, 0x71, 0x59, 0xec, 0xc5, 0xd2, 0x32, 0xc7, 0xc1, + 0x53, 0x7c, 0x79, 0xf4, 0xb7, 0xac, 0x28, 0xff, 0x11, 0x68, + 0x2f, 0x21, 0x68, 0x1b, 0xf6, 0xd6, 0xab, 0xa5, 0x55, 0x03, + 0x2b, 0xf6, 0xf9, 0xf0, 0x36, 0xbe, 0xb2, 0xaa, 0xa5, 0xb3, + 0x77, 0x8d, 0x6e, 0xeb, 0xfb, 0xa6, 0xbf, 0x9e, 0xa1, 0x91, + 0xbe, 0x4a, 0xb0, 0xca, 0xea, 0x75, 0x9e, 0x2f, 0x77, 0x3a, + 0x1f, 0x90, 0x29, 0xc7, 0x3e, 0xcb, 0x8d, 0x57, 0x35, 0xb9, + 0x32, 0x1d, 0xb0, 0x85, 0xf1, 0xb8, 0xe2, 0xd8, 0x03, 0x8f, + 0xe2, 0x94, 0x19, 0x92, 0x54, 0x8c, 0xee, 0x0d, 0x67, 0xdd, + 0x45, 0x47, 0xe1, 0x1d, 0xd6, 0x3a, 0xf9, 0xc9, 0xfc, 0x1c, + 0x54, 0x66, 0xfb, 0x68, 0x4c, 0xf0, 0x09, 0xd7, 0x19, 0x7c, + 0x2c, 0xf7, 0x9e, 0x79, 0x2a, 0xb5, 0x01, 0xe6, 0xa8, 0xa1, + 0xca, 0x51, 0x9a, 0xf2, 0xcb, 0x9b, 0x5f, 0x63, 0x67, 0xe9, + 0x4c, 0x0d, 0x47, 0x50, 0x24, 0x51, 0x35, 0x7b, 0xe1, 0xb5 + }; + static const unsigned char root_ds_1_2017[] = { + 0xae, 0x1e, 0xa5, 0xb9, 0x74, 0xd4, 0xc8, 0x58, 0xb7, 0x40, + 0xbd, 0x03, 0xe3, 0xce, 0xd7, 0xeb, 0xfc, 0xbd, 0x17, 0x24 + }; + static const unsigned char root_ds_2_2017[] = { + 0xe0, 0x6d, 0x44, 0xb8, 0x0b, 0x8f, 0x1d, 0x39, + 0xa9, 0x5c, 0x0b, 0x0d, 0x7c, 0x65, 0xd0, 0x84, + 0x58, 0xe8, 0x80, 0x40, 0x9b, 0xbc, 0x68, 0x34, + 0x57, 0x10, 0x42, 0x37, 0xc7, 0xf8, 0xec, 0x8D + }; + + /* 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")); + + keyname = dns_fixedname_initname(&fkeyname); + isc_buffer_constinit(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_WARNING, "bad key name: %s\n", + isc_result_totext(result)); + result = ISC_R_FAILURE; + } + + if (managed) { + atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); + + if (strcasecmp(atstr, "static-key") == 0) { + managed = false; + anchortype = STATIC_DNSKEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + managed = 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, logctx, ISC_LOG_ERROR, + "key '%s': " + "invalid initialization method '%s'", + namestr, atstr); + result = ISC_R_FAILURE; + + /* + * We can't interpret the trust anchor, so + * we skip all other checks. + */ + goto cleanup; + } + } else { + atstr = "trusted-key"; + anchortype = TRUSTED; + } + + switch (anchortype) { + case INIT_DNSKEY: + case STATIC_DNSKEY: + case TRUSTED: + if (rdata1 > 0xffff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "flags too big: %u", rdata1); + result = ISC_R_RANGE; + } + if (rdata1 & DNS_KEYFLAG_REVOKE) { + cfg_obj_log(key, logctx, ISC_LOG_WARNING, + "key flags revoke bit set"); + } + if (rdata2 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "protocol too big: %u", rdata2); + result = ISC_R_RANGE; + } + if (rdata3 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "algorithm too big: %u\n", rdata3); + result = ISC_R_RANGE; + } + + isc_buffer_init(&b, data, sizeof(data)); + + str = cfg_obj_asstring(cfg_tuple_get(key, "data")); + tresult = isc_base64_decodestring(str, &b); + + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, "%s", + isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } else { + isc_buffer_usedregion(&b, &r); + + if ((rdata3 == DST_ALG_RSASHA1) && r.length > 1 && + r.base[0] == 1 && r.base[1] == 3) + { + cfg_obj_log(key, logctx, ISC_LOG_WARNING, + "%s '%s' has a weak exponent", + atstr, namestr); + } + } + + if (result == ISC_R_SUCCESS && + dns_name_equal(keyname, dns_rootname)) + { + /* + * Flag any use of a root key, regardless of content. + */ + *flagsp |= (managed ? ROOT_KSK_MANAGED + : ROOT_KSK_STATIC); + + if (rdata1 == 257 && rdata2 == 3 && rdata3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2010)) && + memcmp(data, root_ksk_2010, + sizeof(root_ksk_2010)) == 0) + { + *flagsp |= ROOT_KSK_2010; + } + + if (rdata1 == 257 && rdata2 == 3 && rdata3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2017)) && + memcmp(data, root_ksk_2017, + sizeof(root_ksk_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + } + break; + + case INIT_DS: + case STATIC_DS: + if (rdata1 > 0xffff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key tag too big: %u", rdata1); + result = ISC_R_RANGE; + } + if (rdata2 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "algorithm too big: %u\n", rdata2); + result = ISC_R_RANGE; + } + if (rdata3 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "digest type too big: %u", rdata3); + result = ISC_R_RANGE; + } + + isc_buffer_init(&b, data, sizeof(data)); + + str = cfg_obj_asstring(cfg_tuple_get(key, "data")); + tresult = isc_hex_decodestring(str, &b); + + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, "%s", + isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } + if (result == ISC_R_SUCCESS && + dns_name_equal(keyname, dns_rootname)) + { + /* + * Flag any use of a root key, regardless of content. + */ + *flagsp |= (managed ? ROOT_KSK_MANAGED + : ROOT_KSK_STATIC); + + if (rdata1 == 20326 && rdata2 == 8 && rdata3 == 1 && + (isc_buffer_usedlength(&b) == + sizeof(root_ds_1_2017)) && + memcmp(data, root_ds_1_2017, + sizeof(root_ds_1_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + + if (rdata1 == 20326 && rdata2 == 8 && rdata3 == 2 && + (isc_buffer_usedlength(&b) == + sizeof(root_ds_2_2017)) && + memcmp(data, root_ds_2_2017, + sizeof(root_ds_2_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + } + break; + } + +cleanup: + return (result); +} + +static isc_result_t +record_static_keys(isc_symtab_t *symtab, isc_mem_t *mctx, + const cfg_obj_t *keylist, isc_log_t *logctx, + bool autovalidation) { + isc_result_t result, ret = ISC_R_SUCCESS; + const cfg_listelt_t *elt; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE], *p = NULL; + + name = dns_fixedname_initname(&fixed); + + for (elt = cfg_list_first(keylist); elt != NULL; + elt = cfg_list_next(elt)) + { + const char *initmethod; + const cfg_obj_t *init = NULL; + const cfg_obj_t *obj = cfg_listelt_value(elt); + const char *str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + isc_symvalue_t symvalue; + + result = dns_name_fromstring(name, str, 0, NULL); + if (result != ISC_R_SUCCESS) { + continue; + } + + init = cfg_tuple_get(obj, "anchortype"); + if (!cfg_obj_isvoid(init)) { + initmethod = cfg_obj_asstring(init); + if (strcasecmp(initmethod, "initial-key") == 0) { + /* initializing key, skip it */ + continue; + } + if (strcasecmp(initmethod, "initial-ds") == 0) { + /* initializing key, skip it */ + continue; + } + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + symvalue.as_cpointer = obj; + p = isc_mem_strdup(mctx, namebuf); + result = isc_symtab_define(symtab, p, 1, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + isc_mem_free(mctx, p); + } else if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, p); + ret = result; + continue; + } + + if (autovalidation && dns_name_equal(name, dns_rootname)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "static trust anchor for root zone " + "cannot be used with " + "'dnssec-validation auto'."); + ret = ISC_R_FAILURE; + continue; + } + } + + return (ret); +} + +static isc_result_t +check_initializing_keys(isc_symtab_t *symtab, const cfg_obj_t *keylist, + isc_log_t *logctx) { + isc_result_t result, ret = ISC_R_SUCCESS; + const cfg_listelt_t *elt; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE]; + + name = dns_fixedname_initname(&fixed); + + for (elt = cfg_list_first(keylist); elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *obj = cfg_listelt_value(elt); + const cfg_obj_t *init = NULL; + const char *str; + isc_symvalue_t symvalue; + + init = cfg_tuple_get(obj, "anchortype"); + if (cfg_obj_isvoid(init) || + strcasecmp(cfg_obj_asstring(init), "static-key") == 0 || + strcasecmp(cfg_obj_asstring(init), "static-ds") == 0) + { + /* static key, skip it */ + continue; + } + + str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + result = dns_name_fromstring(name, str, 0, NULL); + if (result != ISC_R_SUCCESS) { + continue; + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + result = isc_symtab_lookup(symtab, namebuf, 1, &symvalue); + if (result == ISC_R_SUCCESS) { + const char *file = cfg_obj_file(symvalue.as_cpointer); + unsigned int line = cfg_obj_line(symvalue.as_cpointer); + if (file == NULL) { + file = "<unknown file>"; + } + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "static and initializing keys " + "cannot be used for the " + "same domain. " + "static key defined at " + "%s:%u", + file, line); + + ret = ISC_R_FAILURE; + } + } + + return (ret); +} + +static isc_result_t +record_ds_keys(isc_symtab_t *symtab, isc_mem_t *mctx, + const cfg_obj_t *keylist) { + isc_result_t result, ret = ISC_R_SUCCESS; + const cfg_listelt_t *elt; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE], *p = NULL; + + name = dns_fixedname_initname(&fixed); + + for (elt = cfg_list_first(keylist); elt != NULL; + elt = cfg_list_next(elt)) + { + const char *initmethod; + const cfg_obj_t *init = NULL; + const cfg_obj_t *obj = cfg_listelt_value(elt); + const char *str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + isc_symvalue_t symvalue; + + result = dns_name_fromstring(name, str, 0, NULL); + if (result != ISC_R_SUCCESS) { + continue; + } + + init = cfg_tuple_get(obj, "anchortype"); + if (!cfg_obj_isvoid(init)) { + initmethod = cfg_obj_asstring(init); + if (strcasecmp(initmethod, "initial-key") == 0 || + strcasecmp(initmethod, "static-key") == 0) + { + /* Key-style key, skip it */ + continue; + } + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + symvalue.as_cpointer = obj; + p = isc_mem_strdup(mctx, namebuf); + result = isc_symtab_define(symtab, p, 1, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + isc_mem_free(mctx, p); + } else if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, p); + ret = result; + continue; + } + } + + return (ret); +} + +/* + * Check for conflicts between static and initialiizing keys. + */ +static isc_result_t +check_ta_conflicts(const cfg_obj_t *global_ta, const cfg_obj_t *view_ta, + const cfg_obj_t *global_tkeys, const cfg_obj_t *view_tkeys, + bool autovalidation, isc_mem_t *mctx, isc_log_t *logctx) { + isc_result_t result, tresult; + const cfg_listelt_t *elt = NULL; + const cfg_obj_t *keylist = NULL; + isc_symtab_t *statictab = NULL, *dstab = NULL; + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &statictab); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &dstab); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * First we record all the static keys (i.e., old-style + * trusted-keys and trust-anchors configured with "static-key"), + * and all the DS-style trust anchors. + */ + for (elt = cfg_list_first(global_ta); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = record_static_keys(statictab, mctx, keylist, logctx, + autovalidation); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + tresult = record_ds_keys(dstab, mctx, keylist); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + for (elt = cfg_list_first(view_ta); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = record_static_keys(statictab, mctx, keylist, logctx, + autovalidation); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + tresult = record_ds_keys(dstab, mctx, keylist); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + for (elt = cfg_list_first(global_tkeys); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = record_static_keys(statictab, mctx, keylist, logctx, + autovalidation); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + for (elt = cfg_list_first(view_tkeys); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = record_static_keys(statictab, mctx, keylist, logctx, + autovalidation); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + /* + * Next, ensure that there's no conflict between the + * static keys and the trust-anchors configured with "initial-key". + */ + for (elt = cfg_list_first(global_ta); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = check_initializing_keys(statictab, keylist, logctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + for (elt = cfg_list_first(view_ta); elt != NULL; + elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + tresult = check_initializing_keys(statictab, keylist, logctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + +cleanup: + if (statictab != NULL) { + isc_symtab_destroy(&statictab); + } + if (dstab != NULL) { + isc_symtab_destroy(&dstab); + } + return (result); +} + +typedef enum { special_zonetype_rpz, special_zonetype_catz } special_zonetype_t; + +static isc_result_t +check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj, + const char *viewname, isc_symtab_t *symtab, isc_log_t *logctx, + special_zonetype_t specialzonetype) { + const cfg_listelt_t *element; + const cfg_obj_t *obj, *nameobj, *zoneobj; + const char *zonename, *zonetype; + const char *forview = " for view "; + isc_symvalue_t value; + isc_result_t result, tresult; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE]; + unsigned int num_zones = 0; + + if (viewname == NULL) { + viewname = ""; + forview = ""; + } + result = ISC_R_SUCCESS; + + name = dns_fixedname_initname(&fixed); + obj = cfg_tuple_get(rpz_obj, "zone list"); + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + nameobj = cfg_tuple_get(obj, "zone name"); + zonename = cfg_obj_asstring(nameobj); + zonetype = ""; + + if (specialzonetype == special_zonetype_rpz) { + if (++num_zones > 64) { + cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR, + "more than 64 response policy " + "zones in view '%s'", + viewname); + return (ISC_R_FAILURE); + } + } + + tresult = dns_name_fromstring(name, zonename, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", zonename); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + continue; + } + dns_name_format(name, namebuf, sizeof(namebuf)); + tresult = isc_symtab_lookup(symtab, namebuf, 3, &value); + if (tresult == ISC_R_SUCCESS) { + obj = NULL; + zoneobj = value.as_cpointer; + if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) { + zoneobj = cfg_tuple_get(zoneobj, "options"); + } + if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) { + (void)cfg_map_get(zoneobj, "type", &obj); + } + if (obj != NULL) { + zonetype = cfg_obj_asstring(obj); + } + } + if (strcasecmp(zonetype, "primary") != 0 && + strcasecmp(zonetype, "master") != 0 && + strcasecmp(zonetype, "secondary") != 0 && + strcasecmp(zonetype, "slave") != 0) + { + cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR, + "%s '%s'%s%s is not a primary or secondary " + "zone", + rpz_catz, zonename, forview, viewname); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + return (result); +} + +static isc_result_t +check_catz(const cfg_obj_t *catz_obj, const char *viewname, isc_mem_t *mctx, + isc_log_t *logctx) { + const cfg_listelt_t *element; + const cfg_obj_t *obj, *nameobj, *primariesobj; + const char *zonename; + const char *forview = " for view "; + isc_result_t result, tresult; + isc_symtab_t *symtab = NULL; + dns_fixedname_t fixed; + dns_name_t *name = dns_fixedname_initname(&fixed); + + if (viewname == NULL) { + viewname = ""; + forview = ""; + } + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = cfg_tuple_get(catz_obj, "zone list"); + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + char namebuf[DNS_NAME_FORMATSIZE]; + + obj = cfg_listelt_value(element); + nameobj = cfg_tuple_get(obj, "zone name"); + zonename = cfg_obj_asstring(nameobj); + + tresult = dns_name_fromstring(name, zonename, 0, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", zonename); + if (result == ISC_R_SUCCESS) { + result = tresult; + continue; + } + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + tresult = + nameexist(nameobj, namebuf, 1, symtab, + "catalog zone '%s': already added here %s:%u", + logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + continue; + } + + primariesobj = cfg_tuple_get(obj, "default-primaries"); + if (primariesobj != NULL && cfg_obj_istuple(primariesobj)) { + primariesobj = cfg_tuple_get(obj, "default-masters"); + if (primariesobj != NULL && + cfg_obj_istuple(primariesobj)) + { + cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR, + "catalog zone '%s'%s%s: " + "'default-primaries' and " + "'default-masters' can not be both " + "defined", + zonename, forview, viewname); + result = ISC_R_FAILURE; + break; + } + } + } + + if (symtab != NULL) { + isc_symtab_destroy(&symtab); + } + + return (result); +} + +/*% + * Data structure used for the 'callback_data' argument to check_one_plugin(). + */ +struct check_one_plugin_data { + isc_mem_t *mctx; + isc_log_t *lctx; + cfg_aclconfctx_t *actx; + isc_result_t *check_result; +}; + +/*% + * A callback for the cfg_pluginlist_foreach() call in check_viewconf() below. + * Since the point is to check configuration of all plugins even when + * processing some of them fails, always return ISC_R_SUCCESS and indicate any + * check failures through the 'check_result' variable passed in via the + * 'callback_data' structure. + */ +static isc_result_t +check_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj, + const char *plugin_path, const char *parameters, + void *callback_data) { + struct check_one_plugin_data *data = 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) { + cfg_obj_log(obj, data->lctx, ISC_LOG_ERROR, + "%s: plugin check failed: " + "unable to get full plugin path: %s", + plugin_path, isc_result_totext(result)); + return (result); + } + + result = ns_plugin_check(full_path, parameters, config, + cfg_obj_file(obj), cfg_obj_line(obj), + data->mctx, data->lctx, data->actx); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, data->lctx, ISC_LOG_ERROR, + "%s: plugin check failed: %s", full_path, + isc_result_totext(result)); + *data->check_result = result; + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +check_dnstap(const cfg_obj_t *voptions, const cfg_obj_t *config, + isc_log_t *logctx) { +#ifdef HAVE_DNSTAP + const cfg_obj_t *options = NULL; + const cfg_obj_t *obj = NULL; + + if (config != NULL) { + (void)cfg_map_get(config, "options", &options); + } + if (options != NULL) { + (void)cfg_map_get(options, "dnstap-output", &obj); + } + if (obj == NULL) { + if (voptions != NULL) { + (void)cfg_map_get(voptions, "dnstap", &obj); + } + if (options != NULL && obj == NULL) { + (void)cfg_map_get(options, "dnstap", &obj); + } + if (obj != NULL) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'dnstap-output' must be set if 'dnstap' " + "is set"); + return (ISC_R_FAILURE); + } + } + return (ISC_R_SUCCESS); +#else /* ifdef HAVE_DNSTAP */ + UNUSED(voptions); + UNUSED(config); + UNUSED(logctx); + + return (ISC_R_SUCCESS); +#endif /* ifdef HAVE_DNSTAP */ +} + +static isc_result_t +check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, + const char *viewname, dns_rdataclass_t vclass, + isc_symtab_t *files, isc_symtab_t *keydirs, bool check_plugins, + bool nodeprecate, isc_symtab_t *inview, isc_log_t *logctx, + isc_mem_t *mctx) { + const cfg_obj_t *zones = NULL; + const cfg_obj_t *view_tkeys = NULL, *global_tkeys = NULL; + const cfg_obj_t *view_mkeys = NULL, *global_mkeys = NULL; + const cfg_obj_t *view_ta = NULL, *global_ta = NULL; + const cfg_obj_t *check_keys[2] = { NULL, NULL }; + const cfg_obj_t *keys = NULL; + const cfg_listelt_t *element, *element2; + isc_symtab_t *symtab = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult = ISC_R_SUCCESS; + cfg_aclconfctx_t *actx = NULL; + const cfg_obj_t *obj; + const cfg_obj_t *options = NULL; + const cfg_obj_t *opts = NULL; + const cfg_obj_t *plugin_list = NULL; + bool autovalidation = false; + unsigned int tflags = 0, dflags = 0; + int i; + + /* + * Get global options block + */ + (void)cfg_map_get(config, "options", &options); + + /* + * The most relevant options for this view + */ + if (voptions != NULL) { + opts = voptions; + } else { + opts = options; + } + + /* + * Check that all zone statements are syntactically correct and + * there are no duplicate zones. + */ + tresult = isc_symtab_create(mctx, 1000, freekey, mctx, false, &symtab); + if (tresult != ISC_R_SUCCESS) { + return (ISC_R_NOMEMORY); + } + + cfg_aclconfctx_create(mctx, &actx); + + if (voptions != NULL) { + (void)cfg_map_get(voptions, "zone", &zones); + } else { + (void)cfg_map_get(config, "zone", &zones); + } + + for (element = cfg_list_first(zones); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zone = cfg_listelt_value(element); + + tresult = check_zoneconf(zone, voptions, config, symtab, files, + keydirs, inview, viewname, vclass, + nodeprecate, actx, logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + /* + * Check that the response-policy and catalog-zones options + * refer to zones that exist. + */ + if (opts != NULL) { + obj = NULL; + if ((cfg_map_get(opts, "response-policy", &obj) == + ISC_R_SUCCESS) && + (check_rpz_catz("response-policy zone", obj, viewname, + symtab, logctx, + special_zonetype_rpz) != ISC_R_SUCCESS)) + { + result = ISC_R_FAILURE; + } + + obj = NULL; + if ((cfg_map_get(opts, "catalog-zones", &obj) == + ISC_R_SUCCESS) && + (check_rpz_catz("catalog zone", obj, viewname, symtab, + logctx, + special_zonetype_catz) != ISC_R_SUCCESS)) + { + result = ISC_R_FAILURE; + } + } + + /* + * Check catalog-zones configuration. + */ + if (opts != NULL) { + obj = NULL; + if ((cfg_map_get(opts, "catalog-zones", &obj) == + ISC_R_SUCCESS) && + (check_catz(obj, viewname, mctx, logctx) != ISC_R_SUCCESS)) + { + result = ISC_R_FAILURE; + } + } + + isc_symtab_destroy(&symtab); + + /* + * Check that forwarding is reasonable. + */ + if (opts != NULL && check_forward(opts, NULL, logctx) != ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + + /* + * Check non-zero options at the global and view levels. + */ + if (options != NULL && check_nonzero(options, logctx) != ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + if (voptions != NULL && + check_nonzero(voptions, logctx) != ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + + /* + * Check that dual-stack-servers is reasonable. + */ + if (opts != NULL && check_dual_stack(opts, logctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + /* + * Check that rrset-order is reasonable. + */ + if (opts != NULL && check_order(opts, logctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + /* + * Check that all key statements are syntactically correct and + * there are no duplicate keys. + */ + tresult = isc_symtab_create(mctx, 1000, freekey, mctx, false, &symtab); + if (tresult != ISC_R_SUCCESS) { + goto cleanup; + } + + (void)cfg_map_get(config, "key", &keys); + tresult = check_keylist(keys, symtab, mctx, logctx); + if (tresult == ISC_R_EXISTS) { + result = ISC_R_FAILURE; + } else if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + + if (voptions != NULL) { + keys = NULL; + (void)cfg_map_get(voptions, "key", &keys); + tresult = check_keylist(keys, symtab, mctx, logctx); + if (tresult == ISC_R_EXISTS) { + result = ISC_R_FAILURE; + } else if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + } + + /* + * Global servers can refer to keys in views. + */ + if (check_servers(config, voptions, symtab, mctx, logctx) != + ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + + isc_symtab_destroy(&symtab); + + /* + * Load all DNSSEC keys. + */ + if (voptions != NULL) { + (void)cfg_map_get(voptions, "trusted-keys", &view_tkeys); + (void)cfg_map_get(voptions, "trust-anchors", &view_ta); + (void)cfg_map_get(voptions, "managed-keys", &view_mkeys); + } + (void)cfg_map_get(config, "trusted-keys", &global_tkeys); + (void)cfg_map_get(config, "trust-anchors", &global_ta); + (void)cfg_map_get(config, "managed-keys", &global_mkeys); + + /* + * Check trusted-keys. + */ + check_keys[0] = view_tkeys; + check_keys[1] = global_tkeys; + for (i = 0; i < 2; i++) { + if (check_keys[i] != NULL) { + unsigned int flags = 0; + + for (element = cfg_list_first(check_keys[i]); + element != NULL; element = cfg_list_next(element)) + { + const cfg_obj_t *keylist = + cfg_listelt_value(element); + for (element2 = cfg_list_first(keylist); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + obj = cfg_listelt_value(element2); + tresult = check_trust_anchor( + obj, false, &flags, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + if ((flags & ROOT_KSK_STATIC) != 0) { + cfg_obj_log(check_keys[i], logctx, + ISC_LOG_WARNING, + "trusted-keys entry for the root " + "zone WILL FAIL after key " + "rollover - use trust-anchors " + "with initial-key " + "or initial-ds instead."); + } + + tflags |= flags; + } + } + + /* + * Check dnssec/managed-keys. (Only one or the other can be used.) + */ + if ((view_mkeys != NULL || global_mkeys != NULL) && + (view_ta != NULL || global_ta != NULL)) + { + keys = (view_mkeys != NULL) ? view_mkeys : global_mkeys; + + cfg_obj_log(keys, logctx, ISC_LOG_ERROR, + "use of managed-keys is not allowed when " + "trust-anchors is also in use"); + result = ISC_R_FAILURE; + } + + if (view_ta == NULL && global_ta == NULL) { + view_ta = view_mkeys; + global_ta = global_mkeys; + } + + check_keys[0] = view_ta; + check_keys[1] = global_ta; + for (i = 0; i < 2; i++) { + if (check_keys[i] != NULL) { + unsigned int flags = 0; + + for (element = cfg_list_first(check_keys[i]); + element != NULL; element = cfg_list_next(element)) + { + const cfg_obj_t *keylist = + cfg_listelt_value(element); + for (element2 = cfg_list_first(keylist); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + obj = cfg_listelt_value(element2); + tresult = check_trust_anchor( + obj, true, &flags, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + + if ((flags & ROOT_KSK_STATIC) != 0) { + cfg_obj_log(check_keys[i], logctx, + ISC_LOG_WARNING, + "static entry for the root " + "zone WILL FAIL after key " + "rollover - use trust-anchors " + "with initial-key " + "or initial-ds instead."); + } + + if ((flags & ROOT_KSK_2010) != 0 && + (flags & ROOT_KSK_2017) == 0) + { + cfg_obj_log(check_keys[i], logctx, + ISC_LOG_WARNING, + "initial-key entry for the root " + "zone uses the 2010 key without " + "the updated 2017 key"); + } + + dflags |= flags; + } + } + + if ((tflags & ROOT_KSK_ANY) != 0 && (dflags & ROOT_KSK_ANY) != 0) { + keys = (view_ta != NULL) ? view_ta : global_ta; + cfg_obj_log(keys, logctx, ISC_LOG_WARNING, + "both trusted-keys and trust-anchors " + "for the root zone are present"); + } + + if ((dflags & ROOT_KSK_ANY) == ROOT_KSK_ANY) { + keys = (view_ta != NULL) ? view_ta : global_ta; + cfg_obj_log(keys, logctx, ISC_LOG_WARNING, + "both initial and static entries for the " + "root zone are present"); + } + + obj = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "dnssec-validation", &obj); + } + if (obj == NULL && options != NULL) { + (void)cfg_map_get(options, "dnssec-validation", &obj); + } + if (obj != NULL && !cfg_obj_isboolean(obj)) { + autovalidation = true; + } + + tresult = check_ta_conflicts(global_ta, view_ta, global_tkeys, + view_tkeys, autovalidation, mctx, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + /* + * Check options. + */ + if (voptions != NULL) { + tresult = check_options(voptions, NULL, logctx, mctx, + optlevel_view); + } else { + tresult = check_options(config, config, logctx, mctx, + optlevel_config); + } + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_dnstap(voptions, config, logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_viewacls(actx, voptions, config, logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_recursionacls(actx, voptions, viewname, config, logctx, + mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_dns64(actx, voptions, config, logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_ratelimit(actx, voptions, config, logctx, mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + + /* + * Load plugins. + */ + if (check_plugins) { + if (voptions != NULL) { + (void)cfg_map_get(voptions, "plugin", &plugin_list); + } else { + (void)cfg_map_get(config, "plugin", &plugin_list); + } + } + + { + struct check_one_plugin_data check_one_plugin_data = { + .mctx = mctx, + .lctx = logctx, + .actx = actx, + .check_result = &tresult, + }; + + (void)cfg_pluginlist_foreach(config, plugin_list, logctx, + check_one_plugin, + &check_one_plugin_data); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + +cleanup: + if (symtab != NULL) { + isc_symtab_destroy(&symtab); + } + if (actx != NULL) { + cfg_aclconfctx_detach(&actx); + } + + return (result); +} + +static const char *default_channels[] = { "default_syslog", "default_stderr", + "default_debug", "null", NULL }; + +static isc_result_t +bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) { + const cfg_obj_t *categories = NULL; + const cfg_obj_t *category; + const cfg_obj_t *channels = NULL; + const cfg_obj_t *channel; + const cfg_listelt_t *element; + const cfg_listelt_t *delement; + const char *channelname; + const char *catname; + 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 *logobj = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_symtab_t *symtab = NULL; + isc_symvalue_t symvalue; + int i; + + (void)cfg_map_get(config, "logging", &logobj); + if (logobj == NULL) { + return (ISC_R_SUCCESS); + } + + result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + + symvalue.as_cpointer = NULL; + for (i = 0; default_channels[i] != NULL; i++) { + tresult = isc_symtab_define(symtab, default_channels[i], 1, + symvalue, isc_symexists_replace); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + cfg_map_get(logobj, "channel", &channels); + + for (element = cfg_list_first(channels); element != NULL; + element = cfg_list_next(element)) + { + channel = cfg_listelt_value(element); + channelname = cfg_obj_asstring(cfg_map_getname(channel)); + fileobj = syslogobj = nullobj = stderrobj = NULL; + (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, logctx, ISC_LOG_ERROR, + "channel '%s': exactly one of file, " + "syslog, " + "null, and stderr must be present", + channelname); + result = ISC_R_FAILURE; + } + tresult = isc_symtab_define(symtab, channelname, 1, symvalue, + isc_symexists_replace); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + cfg_map_get(logobj, "category", &categories); + + for (element = cfg_list_first(categories); element != NULL; + element = cfg_list_next(element)) + { + category = cfg_listelt_value(element); + catname = cfg_obj_asstring(cfg_tuple_get(category, "name")); + if (isc_log_categorybyname(logctx, catname) == NULL) { + cfg_obj_log(category, logctx, ISC_LOG_ERROR, + "undefined category: '%s'", catname); + result = ISC_R_FAILURE; + } + channels = cfg_tuple_get(category, "destinations"); + for (delement = cfg_list_first(channels); delement != NULL; + delement = cfg_list_next(delement)) + { + channel = cfg_listelt_value(delement); + channelname = cfg_obj_asstring(channel); + tresult = isc_symtab_lookup(symtab, channelname, 1, + &symvalue); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(channel, logctx, ISC_LOG_ERROR, + "undefined channel: '%s'", + channelname); + result = tresult; + } + } + } + isc_symtab_destroy(&symtab); + return (result); +} + +static isc_result_t +bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist, + isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *control_keylist; + const cfg_listelt_t *element; + const cfg_obj_t *key; + const char *keyval; + + control_keylist = cfg_tuple_get(control, "keys"); + if (cfg_obj_isvoid(control_keylist)) { + return (ISC_R_SUCCESS); + } + + for (element = cfg_list_first(control_keylist); element != NULL; + element = cfg_list_next(element)) + { + key = cfg_listelt_value(element); + keyval = cfg_obj_asstring(key); + + if (!rndckey_exists(keylist, keyval)) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "unknown key '%s'", keyval); + result = ISC_R_NOTFOUND; + } + } + return (result); +} + +static isc_result_t +bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS, tresult; + cfg_aclconfctx_t *actx = NULL; + const cfg_listelt_t *element, *element2; + const cfg_obj_t *allow; + const cfg_obj_t *control; + const cfg_obj_t *controls; + const cfg_obj_t *controlslist = NULL; + const cfg_obj_t *inetcontrols; + const cfg_obj_t *unixcontrols; + const cfg_obj_t *keylist = NULL; + const char *path; + uint32_t perm, mask; + dns_acl_t *acl = NULL; + isc_sockaddr_t addr; + int i; + + (void)cfg_map_get(config, "controls", &controlslist); + if (controlslist == NULL) { + return (ISC_R_SUCCESS); + } + + (void)cfg_map_get(config, "key", &keylist); + + cfg_aclconfctx_create(mctx, &actx); + + /* + * INET: Check allow clause. + * UNIX: Check "perm" for sanity, check path length. + */ + for (element = cfg_list_first(controlslist); element != NULL; + element = cfg_list_next(element)) + { + controls = cfg_listelt_value(element); + unixcontrols = NULL; + inetcontrols = NULL; + (void)cfg_map_get(controls, "unix", &unixcontrols); + (void)cfg_map_get(controls, "inet", &inetcontrols); + for (element2 = cfg_list_first(inetcontrols); element2 != NULL; + element2 = cfg_list_next(element2)) + { + control = cfg_listelt_value(element2); + allow = cfg_tuple_get(control, "allow"); + tresult = cfg_acl_fromconfig(allow, config, logctx, + actx, mctx, 0, &acl); + if (acl != NULL) { + dns_acl_detach(&acl); + } + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + tresult = bind9_check_controlskeys(control, keylist, + logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + for (element2 = cfg_list_first(unixcontrols); element2 != NULL; + element2 = cfg_list_next(element2)) + { + control = cfg_listelt_value(element2); + path = cfg_obj_asstring(cfg_tuple_get(control, "path")); + tresult = isc_sockaddr_frompath(&addr, path); + if (tresult == ISC_R_NOSPACE) { + cfg_obj_log(control, logctx, ISC_LOG_ERROR, + "unix control '%s': path too long", + path); + result = ISC_R_NOSPACE; + } + perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); + for (i = 0; i < 3; i++) { +#ifdef NEED_SECURE_DIRECTORY + mask = (0x1 << (i * 3)); /* SEARCH */ +#else /* ifdef NEED_SECURE_DIRECTORY */ + mask = (0x6 << (i * 3)); /* READ + WRITE */ +#endif /* ifdef NEED_SECURE_DIRECTORY */ + if ((perm & mask) == mask) { + break; + } + } + if (i == 0) { + cfg_obj_log(control, logctx, ISC_LOG_WARNING, + "unix control '%s' allows access " + "to everyone", + path); + } else if (i == 3) { + cfg_obj_log(control, logctx, ISC_LOG_WARNING, + "unix control '%s' allows access " + "to nobody", + path); + } + tresult = bind9_check_controlskeys(control, keylist, + logctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + } + cfg_aclconfctx_detach(&actx); + return (result); +} + +isc_result_t +bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins, + bool nodeprecate, isc_log_t *logctx, isc_mem_t *mctx) { + const cfg_obj_t *options = NULL; + const cfg_obj_t *views = NULL; + const cfg_obj_t *acls = NULL; + const cfg_listelt_t *velement; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_symtab_t *symtab = NULL; + isc_symtab_t *files = NULL; + isc_symtab_t *keydirs = NULL; + isc_symtab_t *inview = NULL; + + static const char *builtin[] = { "localhost", "localnets", "any", + "none" }; + + (void)cfg_map_get(config, "options", &options); + + if (options != NULL && check_options(options, config, logctx, mctx, + optlevel_options) != ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + + if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + if (bind9_check_primarylists(config, logctx, mctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + + if (bind9_check_parentalagentlists(config, logctx, mctx) != + ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + +#if HAVE_LIBNGHTTP2 + if (bind9_check_httpservers(config, logctx, mctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } +#endif /* HAVE_LIBNGHTTP2 */ + + if (bind9_check_tls_definitions(config, logctx, mctx) != ISC_R_SUCCESS) + { + result = ISC_R_FAILURE; + } + + (void)cfg_map_get(config, "view", &views); + + if (views != NULL && options != NULL) { + if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + + /* + * Use case insensitive comparison as not all file + * systems are case sensitive. This will prevent people + * using FOO.DB and foo.db on case sensitive file + * systems but that shouldn't be a major issue. + */ + } + } + + /* + * Use case insensitive comparison as not all file systems are + * case sensitive. This will prevent people using FOO.DB and foo.db + * on case sensitive file systems but that shouldn't be a major issue. + */ + tresult = isc_symtab_create(mctx, 100, NULL, NULL, false, &files); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + + tresult = isc_symtab_create(mctx, 100, freekey, mctx, false, &keydirs); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + + tresult = isc_symtab_create(mctx, 100, freekey, mctx, true, &inview); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + + if (views == NULL) { + tresult = check_viewconf(config, NULL, NULL, dns_rdataclass_in, + files, keydirs, check_plugins, + nodeprecate, inview, logctx, mctx); + if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else { + const cfg_obj_t *zones = NULL; + const cfg_obj_t *plugins = NULL; + + (void)cfg_map_get(config, "zone", &zones); + if (zones != NULL) { + cfg_obj_log(zones, logctx, ISC_LOG_ERROR, + "when using 'view' statements, " + "all zones must be in views"); + result = ISC_R_FAILURE; + } + + (void)cfg_map_get(config, "plugin", &plugins); + if (plugins != NULL) { + cfg_obj_log(plugins, logctx, ISC_LOG_ERROR, + "when using 'view' statements, " + "all plugins must be defined in views"); + result = ISC_R_FAILURE; + } + } + + tresult = isc_symtab_create(mctx, 100, NULL, NULL, true, &symtab); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + goto cleanup; + } + for (velement = cfg_list_first(views); velement != NULL; + velement = cfg_list_next(velement)) + { + const cfg_obj_t *view = cfg_listelt_value(velement); + const cfg_obj_t *vname = cfg_tuple_get(view, "name"); + const cfg_obj_t *voptions = cfg_tuple_get(view, "options"); + const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class"); + dns_rdataclass_t vclass = dns_rdataclass_in; + const char *key = cfg_obj_asstring(vname); + isc_symvalue_t symvalue; + unsigned int symtype; + + tresult = ISC_R_SUCCESS; + if (cfg_obj_isstring(vclassobj)) { + isc_textregion_t r; + + DE_CONST(cfg_obj_asstring(vclassobj), r.base); + r.length = strlen(r.base); + tresult = dns_rdataclass_fromtext(&vclass, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR, + "view '%s': invalid class %s", + cfg_obj_asstring(vname), r.base); + } + } + symtype = vclass + 1; + if (tresult == ISC_R_SUCCESS && symtab != NULL) { + symvalue.as_cpointer = view; + tresult = isc_symtab_define(symtab, key, symtype, + symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + const char *file; + unsigned int line; + RUNTIME_CHECK(isc_symtab_lookup(symtab, key, + symtype, + &symvalue) == + ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + cfg_obj_log(view, logctx, ISC_LOG_ERROR, + "view '%s': already exists " + "previous definition: %s:%u", + key, file, line); + result = tresult; + } else if (tresult != ISC_R_SUCCESS) { + result = tresult; + } else if ((strcasecmp(key, "_bind") == 0 && + vclass == dns_rdataclass_ch) || + (strcasecmp(key, "_default") == 0 && + vclass == dns_rdataclass_in)) + { + cfg_obj_log(view, logctx, ISC_LOG_ERROR, + "attempt to redefine builtin view " + "'%s'", + key); + result = ISC_R_EXISTS; + } + } + if (tresult == ISC_R_SUCCESS) { + tresult = check_viewconf(config, voptions, key, vclass, + files, keydirs, check_plugins, + nodeprecate, inview, logctx, + mctx); + } + if (tresult != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + cfg_map_get(config, "acl", &acls); + + if (acls != NULL) { + const cfg_listelt_t *elt; + const cfg_listelt_t *elt2; + const char *aclname; + + for (elt = cfg_list_first(acls); elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *acl = cfg_listelt_value(elt); + unsigned int line = cfg_obj_line(acl); + unsigned int i; + + aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name")); + for (i = 0; i < sizeof(builtin) / sizeof(builtin[0]); + i++) + { + if (strcasecmp(aclname, builtin[i]) == 0) { + { + cfg_obj_log(acl, logctx, + ISC_LOG_ERROR, + "attempt to " + "redefine " + "builtin acl '%s'", + aclname); + result = ISC_R_FAILURE; + break; + } + } + } + + for (elt2 = cfg_list_next(elt); elt2 != NULL; + elt2 = cfg_list_next(elt2)) + { + const cfg_obj_t *acl2 = cfg_listelt_value(elt2); + const char *name; + name = cfg_obj_asstring( + cfg_tuple_get(acl2, "name")); + if (strcasecmp(aclname, name) == 0) { + const char *file = cfg_obj_file(acl); + + if (file == NULL) { + file = "<unknown file>"; + } + + cfg_obj_log(acl2, logctx, ISC_LOG_ERROR, + "attempt to redefine " + "acl '%s' previous " + "definition: %s:%u", + name, file, line); + result = ISC_R_FAILURE; + } + } + } + } + +cleanup: + if (symtab != NULL) { + isc_symtab_destroy(&symtab); + } + if (inview != NULL) { + isc_symtab_destroy(&inview); + } + if (files != NULL) { + isc_symtab_destroy(&files); + } + if (keydirs != NULL) { + isc_symtab_destroy(&keydirs); + } + + return (result); +} diff --git a/lib/bind9/getaddresses.c b/lib/bind9/getaddresses.c new file mode 100644 index 0000000..5fa7770 --- /dev/null +++ b/lib/bind9/getaddresses.c @@ -0,0 +1,164 @@ +/* + * 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 <string.h> + +#include <isc/net.h> +#include <isc/netaddr.h> +#include <isc/netdb.h> +#include <isc/netscope.h> +#include <isc/result.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <bind9/getaddresses.h> + +isc_result_t +bind9_getaddresses(const char *hostname, in_port_t port, isc_sockaddr_t *addrs, + int addrsize, int *addrcount) { + struct in_addr in4; + struct in6_addr in6; + bool have_ipv4, have_ipv6; + int i; + + struct addrinfo *ai = NULL, *tmpai, hints; + int result; + + REQUIRE(hostname != NULL); + REQUIRE(addrs != NULL); + REQUIRE(addrcount != NULL); + REQUIRE(addrsize > 0); + + have_ipv4 = (isc_net_probeipv4() == ISC_R_SUCCESS); + have_ipv6 = (isc_net_probeipv6() == ISC_R_SUCCESS); + + /* + * Try IPv4, then IPv6. In order to handle the extended format + * for IPv6 scoped addresses (address%scope_ID), we'll use a local + * working buffer of 128 bytes. The length is an ad-hoc value, but + * should be enough for this purpose; the buffer can contain a string + * of at least 80 bytes for scope_ID in addition to any IPv6 numeric + * addresses (up to 46 bytes), the delimiter character and the + * terminating NULL character. + */ + if (inet_pton(AF_INET, hostname, &in4) == 1) { + if (have_ipv4) { + isc_sockaddr_fromin(&addrs[0], &in4, port); + } else { + isc_sockaddr_v6fromin(&addrs[0], &in4, port); + } + *addrcount = 1; + return (ISC_R_SUCCESS); + } else if (strlen(hostname) <= 127U) { + char tmpbuf[128], *d; + uint32_t zone = 0; + + strlcpy(tmpbuf, hostname, sizeof(tmpbuf)); + d = strchr(tmpbuf, '%'); + if (d != NULL) { + *d = '\0'; + } + + if (inet_pton(AF_INET6, tmpbuf, &in6) == 1) { + isc_netaddr_t na; + + if (!have_ipv6) { + return (ISC_R_FAMILYNOSUPPORT); + } + + if (d != NULL) { + isc_result_t iresult; + + iresult = isc_netscope_pton(AF_INET6, d + 1, + &in6, &zone); + + if (iresult != ISC_R_SUCCESS) { + return (iresult); + } + } + + isc_netaddr_fromin6(&na, &in6); + isc_netaddr_setzone(&na, zone); + isc_sockaddr_fromnetaddr( + &addrs[0], (const isc_netaddr_t *)&na, port); + + *addrcount = 1; + return (ISC_R_SUCCESS); + } + } + memset(&hints, 0, sizeof(hints)); + if (!have_ipv6) { + hints.ai_family = PF_INET; + } else if (!have_ipv4) { + hints.ai_family = PF_INET6; + } else { + hints.ai_family = PF_UNSPEC; +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +#endif /* ifdef AI_ADDRCONFIG */ + } + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG +again: +#endif /* ifdef AI_ADDRCONFIG */ + result = getaddrinfo(hostname, NULL, &hints, &ai); + switch (result) { + case 0: + break; + case EAI_NONAME: +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ + return (ISC_R_NOTFOUND); +#ifdef AI_ADDRCONFIG + case EAI_BADFLAGS: + if ((hints.ai_flags & AI_ADDRCONFIG) != 0) { + hints.ai_flags &= ~AI_ADDRCONFIG; + goto again; + } +#endif /* ifdef AI_ADDRCONFIG */ + FALLTHROUGH; + default: + return (ISC_R_FAILURE); + } + for (tmpai = ai, i = 0; tmpai != NULL && i < addrsize; + tmpai = tmpai->ai_next) + { + if (tmpai->ai_family != AF_INET && tmpai->ai_family != AF_INET6) + { + continue; + } + if (tmpai->ai_family == AF_INET) { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)tmpai->ai_addr; + isc_sockaddr_fromin(&addrs[i], &sin->sin_addr, port); + } else { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)tmpai->ai_addr; + isc_sockaddr_fromin6(&addrs[i], &sin6->sin6_addr, port); + } + i++; + } + freeaddrinfo(ai); + *addrcount = i; + if (*addrcount == 0) { + return (ISC_R_NOTFOUND); + } else { + return (ISC_R_SUCCESS); + } +} diff --git a/lib/bind9/include/bind9/check.h b/lib/bind9/include/bind9/check.h new file mode 100644 index 0000000..240170e --- /dev/null +++ b/lib/bind9/include/bind9/check.h @@ -0,0 +1,65 @@ +/* + * 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 bind9/check.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +#include <isccfg/cfg.h> + +#ifndef MAX_MIN_CACHE_TTL +#define MAX_MIN_CACHE_TTL 90 +#endif /* MAX_MIN_CACHE_TTL */ + +#ifndef MAX_MIN_NCACHE_TTL +#define MAX_MIN_NCACHE_TTL 90 +#endif /* MAX_MIN_NCACHE_TTL */ + +#ifndef MAX_MAX_NCACHE_TTL +#define MAX_MAX_NCACHE_TTL 7 * 24 * 3600 +#endif /* MAX_MAX_NCACHE_TTL */ + +ISC_LANG_BEGINDECLS + +isc_result_t +bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins, + bool nodeprecate, isc_log_t *logctx, isc_mem_t *mctx); +/*%< + * Check the syntactic validity of a configuration parse tree generated from + * a named.conf file. + * + * If 'check_plugins' is true, load plugins and check the validity of their + * parameters as well. + * + * If 'nodeprecate' is true, do not warn about deprecated configuration. + * + * Requires: + *\li config is a valid parse tree + * + *\li logctx is a valid logging context. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_FAILURE + */ + +isc_result_t +bind9_check_key(const cfg_obj_t *config, isc_log_t *logctx); +/*%< + * Same as bind9_check_namedconf(), but for a single 'key' statement. + */ + +ISC_LANG_ENDDECLS diff --git a/lib/bind9/include/bind9/getaddresses.h b/lib/bind9/include/bind9/getaddresses.h new file mode 100644 index 0000000..2904257 --- /dev/null +++ b/lib/bind9/include/bind9/getaddresses.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 bind9/getaddresses.h */ + +#include <isc/lang.h> +#include <isc/net.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +bind9_getaddresses(const char *hostname, in_port_t port, isc_sockaddr_t *addrs, + int addrsize, int *addrcount); +/*%< + * Use the system resolver to get the addresses associated with a hostname. + * If successful, the number of addresses found is returned in 'addrcount'. + * If a hostname lookup is performed and addresses of an unknown family is + * seen, it is ignored. If more than 'addrsize' addresses are seen, the + * first 'addrsize' are returned and the remainder silently truncated. + * + * This routine may block. If called by a program using the isc_app + * framework, it should be surrounded by isc_app_block()/isc_app_unblock(). + * + * Requires: + *\li 'hostname' is not NULL. + *\li 'addrs' is not NULL. + *\li 'addrsize' > 0 + *\li 'addrcount' is not NULL. + * + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOTFOUND + *\li #ISC_R_FAMILYNOSUPPORT - 'hostname' is an IPv6 address, and IPv6 is + * not supported. + */ + +ISC_LANG_ENDDECLS |