diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:59:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:59:48 +0000 |
commit | 3b9b6d0b8e7f798023c9d109c490449d528fde80 (patch) | |
tree | 2e1c188dd7b8d7475cd163de9ae02c428343669b /bin/plugins | |
parent | Initial commit. (diff) | |
download | bind9-3b9b6d0b8e7f798023c9d109c490449d528fde80.tar.xz bind9-3b9b6d0b8e7f798023c9d109c490449d528fde80.zip |
Adding upstream version 1:9.18.19.upstream/1%9.18.19upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | bin/plugins/Makefile.am | 15 | ||||
-rw-r--r-- | bin/plugins/Makefile.in | 847 | ||||
-rw-r--r-- | bin/plugins/filter-a.c | 879 | ||||
-rw-r--r-- | bin/plugins/filter-a.rst | 86 | ||||
-rw-r--r-- | bin/plugins/filter-aaaa.c | 881 | ||||
-rw-r--r-- | bin/plugins/filter-aaaa.rst | 90 |
6 files changed, 2798 insertions, 0 deletions
diff --git a/bin/plugins/Makefile.am b/bin/plugins/Makefile.am new file mode 100644 index 0000000..611dd1b --- /dev/null +++ b/bin/plugins/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) + +pkglib_LTLIBRARIES = filter-aaaa.la +pkglib_LTLIBRARIES += filter-a.la + +filter_aaaa_la_SOURCES = filter-aaaa.c +filter_a_la_SOURCES = filter-a.c +filter_aaaa_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +filter_a_la_LDFLAGS = -avoid-version -module -shared -export-dynamic diff --git a/bin/plugins/Makefile.in b/bin/plugins/Makefile.in new file mode 100644 index 0000000..b1d1bb8 --- /dev/null +++ b/bin/plugins/Makefile.in @@ -0,0 +1,847 @@ +# 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 + +subdir = bin/plugins +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__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)$(pkglibdir)" +LTLIBRARIES = $(pkglib_LTLIBRARIES) +filter_a_la_LIBADD = +am_filter_a_la_OBJECTS = filter-a.lo +filter_a_la_OBJECTS = $(am_filter_a_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 = +filter_a_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(filter_a_la_LDFLAGS) $(LDFLAGS) -o $@ +filter_aaaa_la_LIBADD = +am_filter_aaaa_la_OBJECTS = filter-aaaa.lo +filter_aaaa_la_OBJECTS = $(am_filter_aaaa_la_OBJECTS) +filter_aaaa_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(filter_aaaa_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)/filter-a.Plo \ + ./$(DEPDIR)/filter-aaaa.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 = $(filter_a_la_SOURCES) $(filter_aaaa_la_SOURCES) +DIST_SOURCES = $(filter_a_la_SOURCES) $(filter_aaaa_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) $(LIBISCCFG_CFLAGS) +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 + +pkglib_LTLIBRARIES = filter-aaaa.la filter-a.la +filter_aaaa_la_SOURCES = filter-aaaa.c +filter_a_la_SOURCES = filter-a.c +filter_aaaa_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +filter_a_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +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 bin/plugins/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/plugins/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-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || 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)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ + } + +uninstall-pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ + done + +clean-pkglibLTLIBRARIES: + -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) + @list='$(pkglib_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}; \ + } + +filter-a.la: $(filter_a_la_OBJECTS) $(filter_a_la_DEPENDENCIES) $(EXTRA_filter_a_la_DEPENDENCIES) + $(AM_V_CCLD)$(filter_a_la_LINK) -rpath $(pkglibdir) $(filter_a_la_OBJECTS) $(filter_a_la_LIBADD) $(LIBS) + +filter-aaaa.la: $(filter_aaaa_la_OBJECTS) $(filter_aaaa_la_DEPENDENCIES) $(EXTRA_filter_aaaa_la_DEPENDENCIES) + $(AM_V_CCLD)$(filter_aaaa_la_LINK) -rpath $(pkglibdir) $(filter_aaaa_la_OBJECTS) $(filter_aaaa_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter-a.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter-aaaa.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 $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(pkglibdir)"; 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-libtool clean-pkglibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/filter-a.Plo + -rm -f ./$(DEPDIR)/filter-aaaa.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-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-pkglibLTLIBRARIES + +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)/filter-a.Plo + -rm -f ./$(DEPDIR)/filter-aaaa.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-pkglibLTLIBRARIES + +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-libtool clean-pkglibLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir \ + doc-am doc-local dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkglibLTLIBRARIES \ + 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-pkglibLTLIBRARIES 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/bin/plugins/filter-a.c b/bin/plugins/filter-a.c new file mode 100644 index 0000000..8556cb7 --- /dev/null +++ b/bin/plugins/filter-a.c @@ -0,0 +1,879 @@ +/* + * 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 */ + +/* aliases for the exported symbols */ + +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include <isc/buffer.h> +#include <isc/hash.h> +#include <isc/ht.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/result.h> +#include <isc/types.h> +#include <isc/util.h> + +#include <dns/acl.h> +#include <dns/db.h> +#include <dns/enumtype.h> +#include <dns/log.h> +#include <dns/message.h> +#include <dns/rdataset.h> +#include <dns/types.h> +#include <dns/view.h> + +#include <isccfg/aclconf.h> +#include <isccfg/cfg.h> +#include <isccfg/grammar.h> + +#include <ns/client.h> +#include <ns/hooks.h> +#include <ns/log.h> +#include <ns/query.h> +#include <ns/types.h> + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +/* + * Possible values for the settings of filter-a-on-v6 and + * filter-a-on-v4: "no" is NONE, "yes" is FILTER, "break-dnssec" + * is BREAK_DNSSEC. + */ +typedef enum { NONE = 0, FILTER = 1, BREAK_DNSSEC = 2 } filter_a_t; + +/* + * Persistent data for use by this module. This will be associated + * with client object address in the hash table, and will remain + * accessible until the client object is detached. + */ +typedef struct filter_data { + filter_a_t mode; + uint32_t flags; +} filter_data_t; + +typedef struct filter_instance { + ns_plugin_t *module; + isc_mem_t *mctx; + + /* + * Hash table associating a client object with its persistent data. + */ + isc_ht_t *ht; + isc_mutex_t hlock; + + /* + * Values configured when the module is loaded. + */ + filter_a_t v4_a; + filter_a_t v6_a; + dns_acl_t *a_acl; +} filter_instance_t; + +/* + * Per-client flags set by this module + */ +#define FILTER_A_RECURSING 0x0001 /* Recursing for AAAA */ +#define FILTER_A_FILTERED 0x0002 /* A was removed from answer */ + +/* + * Client attribute tests. + */ +#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0) +#define RECURSIONOK(c) (((c)->query.attributes & NS_QUERYATTR_RECURSIONOK) != 0) + +/* + * Forward declarations of functions referenced in install_hooks(). + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); + +/*% + * Register the functions to be called at each hook point in 'hooktable', using + * memory context 'mctx' for allocating copies of stack-allocated structures + * passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata' + * argument to every callback. + */ +static void +install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx, + filter_instance_t *inst) { + const ns_hook_t filter_init = { + .action = filter_qctx_initialize, + .action_data = inst, + }; + + const ns_hook_t filter_respbegin = { + .action = filter_respond_begin, + .action_data = inst, + }; + + const ns_hook_t filter_respanyfound = { + .action = filter_respond_any_found, + .action_data = inst, + }; + + const ns_hook_t filter_prepresp = { + .action = filter_prep_response_begin, + .action_data = inst, + }; + + const ns_hook_t filter_donesend = { + .action = filter_query_done_send, + .action_data = inst, + }; + + const ns_hook_t filter_destroy = { + .action = filter_qctx_destroy, + .action_data = inst, + }; + + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &filter_init); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_BEGIN, &filter_respbegin); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_ANY_FOUND, + &filter_respanyfound); + ns_hook_add(hooktable, mctx, NS_QUERY_PREP_RESPONSE_BEGIN, + &filter_prepresp); + ns_hook_add(hooktable, mctx, NS_QUERY_DONE_SEND, &filter_donesend); + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &filter_destroy); +} + +/** +** Support for parsing of parameters and configuration of the module. +**/ + +/* + * Support for parsing of parameters. + */ +static const char *filter_a_enums[] = { "break-dnssec", NULL }; + +static isc_result_t +parse_filter_a(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} + +static void +doc_filter_a(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean); +} + +static cfg_type_t cfg_type_filter_a = { + "filter_a", parse_filter_a, cfg_print_ustring, + doc_filter_a, &cfg_rep_string, filter_a_enums, +}; + +static cfg_clausedef_t param_clauses[] = { + { "filter-a", &cfg_type_bracketed_aml, 0 }, + { "filter-a-on-v6", &cfg_type_filter_a, 0 }, + { "filter-a-on-v4", &cfg_type_filter_a, 0 }, +}; + +static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; + +static cfg_type_t cfg_type_parameters = { "filter-a-params", cfg_parse_mapbody, + cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, param_clausesets }; + +static isc_result_t +parse_filter_a_on(const cfg_obj_t *param_obj, const char *param_name, + filter_a_t *dstp) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = cfg_map_get(param_obj, param_name, &obj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + *dstp = FILTER; + } else { + *dstp = NONE; + } + } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) { + *dstp = BREAK_DNSSEC; + } else { + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +static isc_result_t +check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *aclobj = NULL; + dns_acl_t *acl = NULL; + filter_a_t f4 = NONE, f6 = NONE; + + cfg_map_get(fmap, "filter-a", &aclobj); + if (aclobj == NULL) { + return (result); + } + + CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, &acl)); + + CHECK(parse_filter_a_on(fmap, "filter-a-on-v6", &f6)); + CHECK(parse_filter_a_on(fmap, "filter-a-on-v4", &f4)); + + if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-a\" is 'none;' but " + "either filter-a-on-v6 or filter-a-on-v4 " + "is enabled"); + result = ISC_R_FAILURE; + } else if (f4 == NONE && f6 == NONE && !dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-a\" is set but " + "neither filter-a-on-v6 or filter-a-on-v4 " + "is enabled"); + result = ISC_R_FAILURE; + } + +cleanup: + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +parse_parameters(filter_instance_t *inst, const char *parameters, + const void *cfg, const char *cfg_file, unsigned long cfg_line, + isc_mem_t *mctx, isc_log_t *lctx, void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + const cfg_obj_t *obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + + CHECK(parse_filter_a_on(param_obj, "filter-a-on-v6", &inst->v6_a)); + CHECK(parse_filter_a_on(param_obj, "filter-a-on-v4", &inst->v4_a)); + + result = cfg_map_get(param_obj, "filter-a", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, + &inst->a_acl)); + } else { + CHECK(dns_acl_any(mctx, &inst->a_acl)); + } + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/** +** Mandatory plugin API functions: +** +** - plugin_destroy +** - plugin_register +** - plugin_version +** - plugin_check +**/ + +/* + * Called by ns_plugin_register() to initialize the plugin and + * register hook functions into the view hook table. + */ +isc_result_t +plugin_register(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx, ns_hooktable_t *hooktable, void **instp) { + filter_instance_t *inst = NULL; + isc_result_t result = ISC_R_SUCCESS; + + isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, + ISC_LOG_INFO, + "registering 'filter-a' " + "module from %s:%lu, %s parameters", + cfg_file, cfg_line, parameters != NULL ? "with" : "no"); + + inst = isc_mem_get(mctx, sizeof(*inst)); + memset(inst, 0, sizeof(*inst)); + isc_mem_attach(mctx, &inst->mctx); + + if (parameters != NULL) { + CHECK(parse_parameters(inst, parameters, cfg, cfg_file, + cfg_line, mctx, lctx, actx)); + } + + isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + isc_mutex_init(&inst->hlock); + + /* + * Set hook points in the view's hooktable. + */ + install_hooks(hooktable, mctx, inst); + + *instp = inst; + +cleanup: + if (result != ISC_R_SUCCESS) { + plugin_destroy((void **)&inst); + } + + return (result); +} + +isc_result_t +plugin_check(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/* + * Called by ns_plugins_free(); frees memory allocated by + * the module when it was registered. + */ +void +plugin_destroy(void **instp) { + filter_instance_t *inst = (filter_instance_t *)*instp; + + if (inst->ht != NULL) { + isc_ht_destroy(&inst->ht); + isc_mutex_destroy(&inst->hlock); + } + if (inst->a_acl != NULL) { + dns_acl_detach(&inst->a_acl); + } + + isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst)); + *instp = NULL; + + return; +} + +/* + * Returns plugin API version for compatibility checks. + */ +int +plugin_version(void) { + return (NS_PLUGIN_VERSION); +} + +/** +** "filter-a" feature implementation begins here. +**/ + +/*% + * Structure describing the filtering to be applied by process_section(). + */ +typedef struct section_filter { + query_ctx_t *qctx; + filter_a_t mode; + dns_section_t section; + const dns_name_t *name; + dns_rdatatype_t type; + bool only_if_aaaa_exists; +} section_filter_t; + +/* + * Check whether this is an IPv4 client. + */ +static bool +is_v4_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) { + return (true); + } + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +/* + * Check whether this is an IPv6 client. + */ +static bool +is_v6_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +static filter_data_t * +client_state_get(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = NULL; + isc_result_t result; + + LOCK(&inst->hlock); + result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), (void **)&client_state); + UNLOCK(&inst->hlock); + + return (result == ISC_R_SUCCESS ? client_state : NULL); +} + +static void +client_state_create(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state; + isc_result_t result; + + client_state = isc_mem_get(inst->mctx, sizeof(*client_state)); + + client_state->mode = NONE; + client_state->flags = 0; + + LOCK(&inst->hlock); + result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), client_state); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +static void +client_state_destroy(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + if (client_state == NULL) { + return; + } + + LOCK(&inst->hlock); + result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client)); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_mem_put(inst->mctx, client_state, sizeof(*client_state)); +} + +/*% + * Mark 'rdataset' and 'sigrdataset' as rendered, gracefully handling NULL + * pointers and non-associated rdatasets. + */ +static void +mark_as_rendered(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { + if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } +} + +/*% + * Check whether an RRset of given 'type' is present at given 'name'. If + * it is found and either it is not signed or the combination of query + * flags and configured processing 'mode' allows it, mark the RRset and its + * associated signatures as already rendered to prevent them from appearing + * in the response message stored in 'qctx'. If 'only_if_aaaa_exists' is + * true, an RRset of type AAAA must also exist at 'name' in order for the + * above processing to happen. + */ +static bool +process_name(query_ctx_t *qctx, filter_a_t mode, const dns_name_t *name, + dns_rdatatype_t type, bool only_if_aaaa_exists) { + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + isc_result_t result; + bool modified = false; + + if (only_if_aaaa_exists) { + CHECK(dns_message_findtype(name, dns_rdatatype_aaaa, 0, NULL)); + } + + (void)dns_message_findtype(name, type, 0, &rdataset); + (void)dns_message_findtype(name, dns_rdatatype_rrsig, type, + &sigrdataset); + + if (rdataset != NULL && + (sigrdataset == NULL || !WANTDNSSEC(qctx->client) || + mode == BREAK_DNSSEC)) + { + /* + * An RRset of given 'type' was found at 'name' and at least + * one of the following is true: + * + * - the RRset is not signed, + * - the client did not set the DO bit in its request, + * - configuration allows us to tamper with signed responses. + * + * This means it is okay to filter out this RRset and its + * signatures, if any, from the response. + */ + mark_as_rendered(rdataset, sigrdataset); + modified = true; + } + +cleanup: + return (modified); +} + +/*% + * Apply the requested section filter, i.e. prevent (when possible, as + * determined by process_name()) RRsets of given 'type' from being rendered + * in the given 'section' of the response message stored in 'qctx'. Clear + * the AD bit if the answer and/or authority section was modified. If + * 'name' is NULL, all names in the given 'section' are processed; + * otherwise, only 'name' is. 'only_if_aaaa_exists' is passed through to + * process_name(). + */ +static void +process_section(const section_filter_t *filter) { + query_ctx_t *qctx = filter->qctx; + filter_a_t mode = filter->mode; + dns_section_t section = filter->section; + const dns_name_t *name = filter->name; + dns_rdatatype_t type = filter->type; + bool only_if_aaaa_exists = filter->only_if_aaaa_exists; + + dns_message_t *message = qctx->client->message; + isc_result_t result; + + for (result = dns_message_firstname(message, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, section)) + { + dns_name_t *cur = NULL; + dns_message_currentname(message, section, &cur); + if (name != NULL && !dns_name_equal(name, cur)) { + /* + * We only want to process 'name' and this is not it. + */ + continue; + } + + if (!process_name(qctx, mode, cur, type, only_if_aaaa_exists)) { + /* + * Response was not modified, do not touch the AD bit. + */ + continue; + } + + if (section == DNS_SECTION_ANSWER || + section == DNS_SECTION_AUTHORITY) + { + message->flags &= ~DNS_MESSAGEFLAG_AD; + } + } +} + +/* + * Initialize filter state, fetching it from a memory pool and storing it + * in a hash table keyed according to the client object; this enables us to + * retrieve persistent data related to a client query for as long as the + * object persists. + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state; + + *resp = ISC_R_UNSET; + + client_state = client_state_get(qctx, inst); + if (client_state == NULL) { + client_state_create(qctx, inst); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Determine whether this client should have A filtered or not, based on + * the client address family and the settings of filter-a-on-v6 and + * filter-a-on-v4. + */ +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (inst->v4_a != NONE || inst->v6_a != NONE) { + result = ns_client_checkaclsilent(qctx->client, NULL, + inst->a_acl, true); + if (result == ISC_R_SUCCESS && inst->v4_a != NONE && + is_v4_client(qctx->client)) + { + client_state->mode = inst->v4_a; + } else if (result == ISC_R_SUCCESS && inst->v6_a != NONE && + is_v6_client(qctx->client)) + { + client_state->mode = inst->v6_a; + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide A rrsets if there is a matching AAAA. Trigger recursion if + * necessary to find out whether an AAAA exists. + * + * (This version is for processing answers to explicit A queries; ANY + * queries are handled in filter_respond_any_found().) + */ +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result = ISC_R_UNSET; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (client_state->mode != BREAK_DNSSEC && + (client_state->mode != FILTER || + (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL && + dns_rdataset_isassociated(qctx->sigrdataset)))) + { + return (NS_HOOK_CONTINUE); + } + + if (qctx->qtype == dns_rdatatype_a) { + dns_rdataset_t *trdataset; + trdataset = ns_client_newrdataset(qctx->client); + result = dns_db_findrdataset( + qctx->db, qctx->node, qctx->version, dns_rdatatype_aaaa, + 0, qctx->client->now, trdataset, NULL); + if (dns_rdataset_isassociated(trdataset)) { + dns_rdataset_disassociate(trdataset); + } + ns_client_putrdataset(qctx->client, &trdataset); + + /* + * We found an A. If we also found an AAAA, then the A + * must not be rendered. + * + * If the AAAA is not in our cache, then any result other than + * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no AAAAA, + * and so AAAAs are okay. + * + * We assume there is no AAAA if we can't recurse for this + * client. That might be the wrong answer, but what else + * can we do? Besides, the fact that we have the A and + * are using this mechanism in the first place suggests + * that we care more about AAAAs than As, and would have + * cached an AAAA if it existed. + */ + if (result == ISC_R_SUCCESS) { + mark_as_rendered(qctx->rdataset, qctx->sigrdataset); + qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD; + client_state->flags |= FILTER_A_FILTERED; + } else if (!qctx->authoritative && RECURSIONOK(qctx->client) && + (result == DNS_R_DELEGATION || + result == ISC_R_NOTFOUND)) + { + /* + * This is an ugly kludge to recurse + * for the AAAA and discard the result.??? + * + * Continue to add the A now. + * We'll make a note to not render it + * if the recursion for the AAAA succeeds. + */ + result = ns_query_recurse(qctx->client, + dns_rdatatype_aaaa, + qctx->client->query.qname, + NULL, NULL, qctx->resuming); + if (result == ISC_R_SUCCESS) { + client_state->flags |= FILTER_A_RECURSING; + qctx->client->query.attributes |= + NS_QUERYATTR_RECURSING; + } + } + } else if (qctx->qtype == dns_rdatatype_aaaa && + (client_state->flags & FILTER_A_RECURSING) != 0) + { + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->fname, + .type = dns_rdatatype_a, + }; + process_section(&filter_answer); + + client_state->flags &= ~FILTER_A_RECURSING; + + result = ns_query_done(qctx); + + *resp = result; + + return (NS_HOOK_RETURN); + } + + *resp = result; + return (NS_HOOK_CONTINUE); +} + +/* + * When answering an ANY query, remove A if AAAA is present. + */ +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + /* + * If we are authoritative, require an AAAA record to be + * present before filtering out A records; otherwise, + * just assume an AAAA record exists even if it was not in the + * cache (and therefore is not in the response message), + * thus proceeding with filtering out A records. + */ + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->tname, + .type = dns_rdatatype_a, + .only_if_aaaa_exists = qctx->authoritative, + }; + process_section(&filter_answer); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide A rrsets in the additional section if there is a matching AAAA, and + * hide NS in the authority section if A was filtered in the answer + * section. + */ +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + const section_filter_t filter_additional = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ADDITIONAL, + .type = dns_rdatatype_a, + .only_if_aaaa_exists = true, + }; + process_section(&filter_additional); + + if ((client_state->flags & FILTER_A_FILTERED) != 0) { + const section_filter_t filter_authority = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_AUTHORITY, + .type = dns_rdatatype_ns, + }; + process_section(&filter_authority); + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * If the client is being detached, then we can delete our persistent data + * from hash table and return it to the memory pool. + */ +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + + *resp = ISC_R_UNSET; + + if (!qctx->detach_client) { + return (NS_HOOK_CONTINUE); + } + + client_state_destroy(qctx, inst); + + return (NS_HOOK_CONTINUE); +} diff --git a/bin/plugins/filter-a.rst b/bin/plugins/filter-a.rst new file mode 100644 index 0000000..16b3dee --- /dev/null +++ b/bin/plugins/filter-a.rst @@ -0,0 +1,86 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: filter-a +.. _man_filter-a: + +filter-a.so - filter A in DNS responses when AAAA is present +--------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`plugin query` "filter-a.so" [{ parameters }]; + +Description +~~~~~~~~~~~ + +:program:`filter-a.so` is a query plugin module for :iscman:`named`, enabling +:iscman:`named` to omit some IPv4 addresses when responding to clients. + +For example: + +:: + + plugin query "filter-a.so" { + filter-a-on-v6 yes; + filter-a-on-v4 yes; + filter-a { 192.0.2.1; 2001:db8:2::1; }; + }; + +This module is intended to aid transition from IPv4 to IPv6 by +withholding IPv4 addresses from DNS clients which are not connected to +the IPv4 Internet, when the name being looked up has an IPv6 address +available. Use of this module is not recommended unless absolutely +necessary. + +Note: This mechanism can erroneously cause other servers not to give +A records to their clients. If a recursing server with both IPv6 and +IPv4 network connections queries an authoritative server using this +mechanism via IPv6, it is denied A records even if its client is +using IPv4. + +Options +~~~~~~~ + +``filter-a`` + This option specifies a list of client addresses for which A filtering is to + be applied. The default is ``any``. + +``filter-a-on-v6`` + If set to ``yes``, this option indicates that the DNS client is at an IPv6 address, in + ``filter-a``. If the response does not include DNSSEC + signatures, then all A records are deleted from the response. This + filtering applies to all responses, not only authoritative + ones. + + If set to ``break-dnssec``, then A records are deleted even when + DNSSEC is enabled. As suggested by the name, this causes the response + to fail to verify, because the DNSSEC protocol is designed to detect + deletions. + + This mechanism can erroneously cause other servers not to give A + records to their clients. If a recursing server with both IPv6 and IPv4 + network connections queries an authoritative server using this + mechanism via IPv6, it is denied A records even if its client is + using IPv4. + +``filter-a-on-v4`` + This option is identical to ``filter-a-on-v6``, except that it filters A responses + to queries from IPv4 clients instead of IPv6 clients. To filter all + responses, set both options to ``yes``. + +See Also +~~~~~~~~ + +BIND 9 Administrator Reference Manual. diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c new file mode 100644 index 0000000..1ec1fd8 --- /dev/null +++ b/bin/plugins/filter-aaaa.c @@ -0,0 +1,881 @@ +/* + * 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 */ + +/* aliases for the exported symbols */ + +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include <isc/buffer.h> +#include <isc/hash.h> +#include <isc/ht.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/result.h> +#include <isc/types.h> +#include <isc/util.h> + +#include <dns/acl.h> +#include <dns/db.h> +#include <dns/enumtype.h> +#include <dns/log.h> +#include <dns/message.h> +#include <dns/rdataset.h> +#include <dns/types.h> +#include <dns/view.h> + +#include <isccfg/aclconf.h> +#include <isccfg/cfg.h> +#include <isccfg/grammar.h> + +#include <ns/client.h> +#include <ns/hooks.h> +#include <ns/log.h> +#include <ns/query.h> +#include <ns/types.h> + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +/* + * Possible values for the settings of filter-aaaa-on-v4 and + * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec" + * is BREAK_DNSSEC. + */ +typedef enum { NONE = 0, FILTER = 1, BREAK_DNSSEC = 2 } filter_aaaa_t; + +/* + * Persistent data for use by this module. This will be associated + * with client object address in the hash table, and will remain + * accessible until the client object is detached. + */ +typedef struct filter_data { + filter_aaaa_t mode; + uint32_t flags; +} filter_data_t; + +typedef struct filter_instance { + ns_plugin_t *module; + isc_mem_t *mctx; + + /* + * Hash table associating a client object with its persistent data. + */ + isc_ht_t *ht; + isc_mutex_t hlock; + + /* + * Values configured when the module is loaded. + */ + filter_aaaa_t v4_aaaa; + filter_aaaa_t v6_aaaa; + dns_acl_t *aaaa_acl; +} filter_instance_t; + +/* + * Per-client flags set by this module + */ +#define FILTER_AAAA_RECURSING 0x0001 /* Recursing for A */ +#define FILTER_AAAA_FILTERED 0x0002 /* AAAA was removed from answer */ + +/* + * Client attribute tests. + */ +#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0) +#define RECURSIONOK(c) (((c)->query.attributes & NS_QUERYATTR_RECURSIONOK) != 0) + +/* + * Forward declarations of functions referenced in install_hooks(). + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); + +/*% + * Register the functions to be called at each hook point in 'hooktable', using + * memory context 'mctx' for allocating copies of stack-allocated structures + * passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata' + * argument to every callback. + */ +static void +install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx, + filter_instance_t *inst) { + const ns_hook_t filter_init = { + .action = filter_qctx_initialize, + .action_data = inst, + }; + + const ns_hook_t filter_respbegin = { + .action = filter_respond_begin, + .action_data = inst, + }; + + const ns_hook_t filter_respanyfound = { + .action = filter_respond_any_found, + .action_data = inst, + }; + + const ns_hook_t filter_prepresp = { + .action = filter_prep_response_begin, + .action_data = inst, + }; + + const ns_hook_t filter_donesend = { + .action = filter_query_done_send, + .action_data = inst, + }; + + const ns_hook_t filter_destroy = { + .action = filter_qctx_destroy, + .action_data = inst, + }; + + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &filter_init); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_BEGIN, &filter_respbegin); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_ANY_FOUND, + &filter_respanyfound); + ns_hook_add(hooktable, mctx, NS_QUERY_PREP_RESPONSE_BEGIN, + &filter_prepresp); + ns_hook_add(hooktable, mctx, NS_QUERY_DONE_SEND, &filter_donesend); + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &filter_destroy); +} + +/** +** Support for parsing of parameters and configuration of the module. +**/ + +/* + * Support for parsing of parameters. + */ +static const char *filter_aaaa_enums[] = { "break-dnssec", NULL }; + +static isc_result_t +parse_filter_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} + +static void +doc_filter_aaaa(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean); +} + +static cfg_type_t cfg_type_filter_aaaa = { + "filter_aaaa", parse_filter_aaaa, cfg_print_ustring, + doc_filter_aaaa, &cfg_rep_string, filter_aaaa_enums, +}; + +static cfg_clausedef_t param_clauses[] = { + { "filter-aaaa", &cfg_type_bracketed_aml, 0 }, + { "filter-aaaa-on-v4", &cfg_type_filter_aaaa, 0 }, + { "filter-aaaa-on-v6", &cfg_type_filter_aaaa, 0 }, +}; + +static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; + +static cfg_type_t cfg_type_parameters = { + "filter-aaaa-params", cfg_parse_mapbody, cfg_print_mapbody, + cfg_doc_mapbody, &cfg_rep_map, param_clausesets +}; + +static isc_result_t +parse_filter_aaaa_on(const cfg_obj_t *param_obj, const char *param_name, + filter_aaaa_t *dstp) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = cfg_map_get(param_obj, param_name, &obj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + *dstp = FILTER; + } else { + *dstp = NONE; + } + } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) { + *dstp = BREAK_DNSSEC; + } else { + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +static isc_result_t +check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *aclobj = NULL; + dns_acl_t *acl = NULL; + filter_aaaa_t f4 = NONE, f6 = NONE; + + cfg_map_get(fmap, "filter-aaaa", &aclobj); + if (aclobj == NULL) { + return (result); + } + + CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, &acl)); + + CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v4", &f4)); + CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v6", &f6)); + + if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-aaaa\" is 'none;' but " + "either filter-aaaa-on-v4 or filter-aaaa-on-v6 " + "is enabled"); + result = ISC_R_FAILURE; + } else if (f4 == NONE && f6 == NONE && !dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-aaaa\" is set but " + "neither filter-aaaa-on-v4 or filter-aaaa-on-v6 " + "is enabled"); + result = ISC_R_FAILURE; + } + +cleanup: + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +parse_parameters(filter_instance_t *inst, const char *parameters, + const void *cfg, const char *cfg_file, unsigned long cfg_line, + isc_mem_t *mctx, isc_log_t *lctx, void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + const cfg_obj_t *obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + + CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v4", + &inst->v4_aaaa)); + CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v6", + &inst->v6_aaaa)); + + result = cfg_map_get(param_obj, "filter-aaaa", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, + &inst->aaaa_acl)); + } else { + CHECK(dns_acl_any(mctx, &inst->aaaa_acl)); + } + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/** +** Mandatory plugin API functions: +** +** - plugin_destroy +** - plugin_register +** - plugin_version +** - plugin_check +**/ + +/* + * Called by ns_plugin_register() to initialize the plugin and + * register hook functions into the view hook table. + */ +isc_result_t +plugin_register(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx, ns_hooktable_t *hooktable, void **instp) { + filter_instance_t *inst = NULL; + isc_result_t result = ISC_R_SUCCESS; + + isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, + ISC_LOG_INFO, + "registering 'filter-aaaa' " + "module from %s:%lu, %s parameters", + cfg_file, cfg_line, parameters != NULL ? "with" : "no"); + + inst = isc_mem_get(mctx, sizeof(*inst)); + memset(inst, 0, sizeof(*inst)); + isc_mem_attach(mctx, &inst->mctx); + + if (parameters != NULL) { + CHECK(parse_parameters(inst, parameters, cfg, cfg_file, + cfg_line, mctx, lctx, actx)); + } + + isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + isc_mutex_init(&inst->hlock); + + /* + * Set hook points in the view's hooktable. + */ + install_hooks(hooktable, mctx, inst); + + *instp = inst; + +cleanup: + if (result != ISC_R_SUCCESS) { + plugin_destroy((void **)&inst); + } + + return (result); +} + +isc_result_t +plugin_check(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/* + * Called by ns_plugins_free(); frees memory allocated by + * the module when it was registered. + */ +void +plugin_destroy(void **instp) { + filter_instance_t *inst = (filter_instance_t *)*instp; + + if (inst->ht != NULL) { + isc_ht_destroy(&inst->ht); + isc_mutex_destroy(&inst->hlock); + } + if (inst->aaaa_acl != NULL) { + dns_acl_detach(&inst->aaaa_acl); + } + + isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst)); + *instp = NULL; + + return; +} + +/* + * Returns plugin API version for compatibility checks. + */ +int +plugin_version(void) { + return (NS_PLUGIN_VERSION); +} + +/** +** "filter-aaaa" feature implementation begins here. +**/ + +/*% + * Structure describing the filtering to be applied by process_section(). + */ +typedef struct section_filter { + query_ctx_t *qctx; + filter_aaaa_t mode; + dns_section_t section; + const dns_name_t *name; + dns_rdatatype_t type; + bool only_if_a_exists; +} section_filter_t; + +/* + * Check whether this is an IPv4 client. + */ +static bool +is_v4_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) { + return (true); + } + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +/* + * Check whether this is an IPv6 client. + */ +static bool +is_v6_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +static filter_data_t * +client_state_get(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = NULL; + isc_result_t result; + + LOCK(&inst->hlock); + result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), (void **)&client_state); + UNLOCK(&inst->hlock); + + return (result == ISC_R_SUCCESS ? client_state : NULL); +} + +static void +client_state_create(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state; + isc_result_t result; + + client_state = isc_mem_get(inst->mctx, sizeof(*client_state)); + + client_state->mode = NONE; + client_state->flags = 0; + + LOCK(&inst->hlock); + result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), client_state); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +static void +client_state_destroy(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + if (client_state == NULL) { + return; + } + + LOCK(&inst->hlock); + result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client)); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_mem_put(inst->mctx, client_state, sizeof(*client_state)); +} + +/*% + * Mark 'rdataset' and 'sigrdataset' as rendered, gracefully handling NULL + * pointers and non-associated rdatasets. + */ +static void +mark_as_rendered(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { + if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } +} + +/*% + * Check whether an RRset of given 'type' is present at given 'name'. If + * it is found and either it is not signed or the combination of query + * flags and configured processing 'mode' allows it, mark the RRset and its + * associated signatures as already rendered to prevent them from appearing + * in the response message stored in 'qctx'. If 'only_if_a_exists' is + * true, an RRset of type A must also exist at 'name' in order for the + * above processing to happen. + */ +static bool +process_name(query_ctx_t *qctx, filter_aaaa_t mode, const dns_name_t *name, + dns_rdatatype_t type, bool only_if_a_exists) { + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + isc_result_t result; + bool modified = false; + + if (only_if_a_exists) { + CHECK(dns_message_findtype(name, dns_rdatatype_a, 0, NULL)); + } + + (void)dns_message_findtype(name, type, 0, &rdataset); + (void)dns_message_findtype(name, dns_rdatatype_rrsig, type, + &sigrdataset); + + if (rdataset != NULL && + (sigrdataset == NULL || !WANTDNSSEC(qctx->client) || + mode == BREAK_DNSSEC)) + { + /* + * An RRset of given 'type' was found at 'name' and at least + * one of the following is true: + * + * - the RRset is not signed, + * - the client did not set the DO bit in its request, + * - configuration allows us to tamper with signed responses. + * + * This means it is okay to filter out this RRset and its + * signatures, if any, from the response. + */ + mark_as_rendered(rdataset, sigrdataset); + modified = true; + } + +cleanup: + return (modified); +} + +/*% + * Apply the requested section filter, i.e. prevent (when possible, as + * determined by process_name()) RRsets of given 'type' from being rendered + * in the given 'section' of the response message stored in 'qctx'. Clear + * the AD bit if the answer and/or authority section was modified. If + * 'name' is NULL, all names in the given 'section' are processed; + * otherwise, only 'name' is. 'only_if_a_exists' is passed through to + * process_name(). + */ +static void +process_section(const section_filter_t *filter) { + query_ctx_t *qctx = filter->qctx; + filter_aaaa_t mode = filter->mode; + dns_section_t section = filter->section; + const dns_name_t *name = filter->name; + dns_rdatatype_t type = filter->type; + bool only_if_a_exists = filter->only_if_a_exists; + + dns_message_t *message = qctx->client->message; + isc_result_t result; + + for (result = dns_message_firstname(message, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, section)) + { + dns_name_t *cur = NULL; + dns_message_currentname(message, section, &cur); + if (name != NULL && !dns_name_equal(name, cur)) { + /* + * We only want to process 'name' and this is not it. + */ + continue; + } + + if (!process_name(qctx, mode, cur, type, only_if_a_exists)) { + /* + * Response was not modified, do not touch the AD bit. + */ + continue; + } + + if (section == DNS_SECTION_ANSWER || + section == DNS_SECTION_AUTHORITY) + { + message->flags &= ~DNS_MESSAGEFLAG_AD; + } + } +} + +/* + * Initialize filter state, fetching it from a memory pool and storing it + * in a hash table keyed according to the client object; this enables us to + * retrieve persistent data related to a client query for as long as the + * object persists. + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state; + + *resp = ISC_R_UNSET; + + client_state = client_state_get(qctx, inst); + if (client_state == NULL) { + client_state_create(qctx, inst); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Determine whether this client should have AAAA filtered or not, based on + * the client address family and the settings of filter-aaaa-on-v4 and + * filter-aaaa-on-v6. + */ +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (inst->v4_aaaa != NONE || inst->v6_aaaa != NONE) { + result = ns_client_checkaclsilent(qctx->client, NULL, + inst->aaaa_acl, true); + if (result == ISC_R_SUCCESS && inst->v4_aaaa != NONE && + is_v4_client(qctx->client)) + { + client_state->mode = inst->v4_aaaa; + } else if (result == ISC_R_SUCCESS && inst->v6_aaaa != NONE && + is_v6_client(qctx->client)) + { + client_state->mode = inst->v6_aaaa; + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide AAAA rrsets if there is a matching A. Trigger recursion if + * necessary to find out whether an A exists. + * + * (This version is for processing answers to explicit AAAA queries; ANY + * queries are handled in filter_respond_any_found().) + */ +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result = ISC_R_UNSET; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (client_state->mode != BREAK_DNSSEC && + (client_state->mode != FILTER || + (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL && + dns_rdataset_isassociated(qctx->sigrdataset)))) + { + return (NS_HOOK_CONTINUE); + } + + if (qctx->qtype == dns_rdatatype_aaaa) { + dns_rdataset_t *trdataset; + trdataset = ns_client_newrdataset(qctx->client); + result = dns_db_findrdataset( + qctx->db, qctx->node, qctx->version, dns_rdatatype_a, 0, + qctx->client->now, trdataset, NULL); + if (dns_rdataset_isassociated(trdataset)) { + dns_rdataset_disassociate(trdataset); + } + ns_client_putrdataset(qctx->client, &trdataset); + + /* + * We found an AAAA. If we also found an A, then the AAAA + * must not be rendered. + * + * If the A is not in our cache, then any result other than + * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A, + * and so AAAAs are okay. + * + * We assume there is no A if we can't recurse for this + * client. That might be the wrong answer, but what else + * can we do? Besides, the fact that we have the AAAA and + * are using this mechanism in the first place suggests + * that we care more about As than AAAAs, and would have + * cached an A if it existed. + */ + if (result == ISC_R_SUCCESS) { + mark_as_rendered(qctx->rdataset, qctx->sigrdataset); + qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD; + client_state->flags |= FILTER_AAAA_FILTERED; + } else if (!qctx->authoritative && RECURSIONOK(qctx->client) && + (result == DNS_R_DELEGATION || + result == ISC_R_NOTFOUND)) + { + /* + * This is an ugly kludge to recurse + * for the A and discard the result. + * + * Continue to add the AAAA now. + * We'll make a note to not render it + * if the recursion for the A succeeds. + */ + result = ns_query_recurse(qctx->client, dns_rdatatype_a, + qctx->client->query.qname, + NULL, NULL, qctx->resuming); + if (result == ISC_R_SUCCESS) { + client_state->flags |= FILTER_AAAA_RECURSING; + qctx->client->query.attributes |= + NS_QUERYATTR_RECURSING; + } + } + } else if (qctx->qtype == dns_rdatatype_a && + (client_state->flags & FILTER_AAAA_RECURSING) != 0) + { + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->fname, + .type = dns_rdatatype_aaaa, + }; + process_section(&filter_answer); + + client_state->flags &= ~FILTER_AAAA_RECURSING; + + result = ns_query_done(qctx); + + *resp = result; + + return (NS_HOOK_RETURN); + } + + *resp = result; + return (NS_HOOK_CONTINUE); +} + +/* + * When answering an ANY query, remove AAAA if A is present. + */ +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + /* + * If we are authoritative, require an A record to be + * present before filtering out AAAA records; otherwise, + * just assume an A record exists even if it was not in the + * cache (and therefore is not in the response message), + * thus proceeding with filtering out AAAA records. + */ + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->tname, + .type = dns_rdatatype_aaaa, + .only_if_a_exists = qctx->authoritative, + }; + process_section(&filter_answer); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide AAAA rrsets in the additional section if there is a matching A, and + * hide NS in the authority section if AAAA was filtered in the answer + * section. + */ +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + const section_filter_t filter_additional = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ADDITIONAL, + .type = dns_rdatatype_aaaa, + .only_if_a_exists = true, + }; + process_section(&filter_additional); + + if ((client_state->flags & FILTER_AAAA_FILTERED) != 0) { + const section_filter_t filter_authority = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_AUTHORITY, + .type = dns_rdatatype_ns, + }; + process_section(&filter_authority); + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * If the client is being detached, then we can delete our persistent data + * from hash table and return it to the memory pool. + */ +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + + *resp = ISC_R_UNSET; + + if (!qctx->detach_client) { + return (NS_HOOK_CONTINUE); + } + + client_state_destroy(qctx, inst); + + return (NS_HOOK_CONTINUE); +} diff --git a/bin/plugins/filter-aaaa.rst b/bin/plugins/filter-aaaa.rst new file mode 100644 index 0000000..8cd7556 --- /dev/null +++ b/bin/plugins/filter-aaaa.rst @@ -0,0 +1,90 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: filter-aaaa +.. _man_filter-aaaa: + +filter-aaaa.so - filter AAAA in DNS responses when A is present +--------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`plugin query` "filter-aaaa.so" [{ parameters }]; + +Description +~~~~~~~~~~~ + +:program:`filter-aaaa.so` is a query plugin module for :iscman:`named`, enabling +:iscman:`named` to omit some IPv6 addresses when responding to clients. + +Until BIND 9.12, this feature was implemented natively in :iscman:`named` and +enabled with the ``filter-aaaa`` ACL and the ``filter-aaaa-on-v4`` and +``filter-aaaa-on-v6`` options. These options are now deprecated in +:iscman:`named.conf` but can be passed as parameters to the +``filter-aaaa.so`` plugin, for example: + +:: + + plugin query "filter-aaaa.so" { + filter-aaaa-on-v4 yes; + filter-aaaa-on-v6 yes; + filter-aaaa { 192.0.2.1; 2001:db8:2::1; }; + }; + +This module is intended to aid transition from IPv4 to IPv6 by +withholding IPv6 addresses from DNS clients which are not connected to +the IPv6 Internet, when the name being looked up has an IPv4 address +available. Use of this module is not recommended unless absolutely +necessary. + +Note: This mechanism can erroneously cause other servers not to give +AAAA records to their clients. If a recursing server with both IPv6 and +IPv4 network connections queries an authoritative server using this +mechanism via IPv4, it is denied AAAA records even if its client is +using IPv6. + +Options +~~~~~~~ + +``filter-aaaa`` + This option specifies a list of client addresses for which AAAA filtering is to + be applied. The default is ``any``. + +``filter-aaaa-on-v4`` + If set to ``yes``, this option indicates that the DNS client is at an IPv4 address, in + ``filter-aaaa``. If the response does not include DNSSEC + signatures, then all AAAA records are deleted from the response. This + filtering applies to all responses, not only authoritative + ones. + + If set to ``break-dnssec``, then AAAA records are deleted even when + DNSSEC is enabled. As suggested by the name, this causes the response + to fail to verify, because the DNSSEC protocol is designed to detect + deletions. + + This mechanism can erroneously cause other servers not to give AAAA + records to their clients. If a recursing server with both IPv6 and IPv4 + network connections queries an authoritative server using this + mechanism via IPv4, it is denied AAAA records even if its client is + using IPv6. + +``filter-aaaa-on-v6`` + This option is identical to ``filter-aaaa-on-v4``, except that it filters AAAA responses + to queries from IPv6 clients instead of IPv4 clients. To filter all + responses, set both options to ``yes``. + +See Also +~~~~~~~~ + +BIND 9 Administrator Reference Manual. |