diff --git a/.clang-format b/.clang-format index 124d82a269..54a39fdb4d 100644 --- a/.clang-format +++ b/.clang-format @@ -78,4 +78,4 @@ PenaltyBreakString: 80 PenaltyExcessCharacter: 100 Standard: Cpp11 ContinuationIndentWidth: 8 -ForEachMacros: [ 'cds_lfs_for_each', 'cds_lfs_for_each_safe', 'cds_list_for_each_entry_safe', 'ISC_LIST_FOREACH', 'ISC_LIST_FOREACH_SAFE' ] +ForEachMacros: [ 'cds_lfs_for_each', 'cds_lfs_for_each_safe', 'cds_list_for_each_entry_safe', 'ISC_LIST_FOREACH', 'ISC_LIST_FOREACH_SAFE', 'ISC_LIST_FOREACH_REV', 'ISC_LIST_FOREACH_REV_SAFE' ] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..6191af8065 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.sh{,.in}] +indent_style = space +indent_size = 2 +binary_next_line = true +switch_case_indent = true diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ad3c2b1e98..57b9988e37 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1534,3 +1534,9 @@ ffbe6b95371c99b7fb05e6de17a8d6b7bf4f629f 1436025e20ae13cfe55df14d62f5812c0cea2ee9 # subshell notation in system tests 05baf7206b7baaf91cb8e049ad13e413c52cdf3f +# reformat shell scripts with shfmt +4cb8b13987b930952238cc88e84272b8cf911933 +# Reformat sources with up-to-date clang-format-17 +79d93600116faabd89798522817ad95a69684fff +# Reformat sources with up-to-date clang-format-18 +b7de2c7cb959fa35099d72c3f9b13938348c74e6 diff --git a/.gitignore b/.gitignore index 3edbde71e7..242bd0efdb 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ doc/man/dnssec-importkey.8in doc/man/dnssec-keyfromlabel.8in doc/man/dnssec-keygen.8in doc/man/dnssec-keymgr.8in +doc/man/dnssec-ksr.8in doc/man/dnssec-revoke.8in doc/man/dnssec-settime.8in doc/man/dnssec-signzone.8in diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 594a2dfbf5..0396e175db 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,7 +15,7 @@ variables: TEST_PARALLEL_JOBS: 4 CONFIGURE: ./configure - CLANG_VERSION: 16 + CLANG_VERSION: 18 CLANG: "clang-${CLANG_VERSION}" SCAN_BUILD: "scan-build-${CLANG_VERSION}" LLVM_SYMBOLIZER: "/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer" @@ -34,8 +34,6 @@ variables: UBSAN_OPTIONS: "halt_on_error=1:abort_on_error=1:disable_coredump=0" - TARBALL_EXTENSION: xz - AM_COLOR_TESTS: always WITHOUT_READLINE: "--without-readline" @@ -57,6 +55,8 @@ variables: BIND_STRESS_TEST_OS: linux BIND_STRESS_TEST_ARCH: amd64 + HYPOTHESIS_PROFILE: "ci" + default: # Allow all running CI jobs to be automatically canceled when a new # version of a branch is pushed. @@ -73,6 +73,7 @@ stages: - performance - docs - postcheck + - postmerge - release ### Runner Tag Templates @@ -82,15 +83,7 @@ stages: - libvirt - amd64 -# Jobs with these tags do not run on AWS but on permanent OVH systems. - -.linux-respdiff-amd64: &linux_respdiff_amd64 - tags: - - linux - - ovh - - amd64 - -# Autoscaling GitLab Runner on AWS EC2 +# Autoscaling GitLab Runner on AWS EC2 (amd64) .linux-amd64: &linux_amd64 tags: @@ -99,35 +92,32 @@ stages: - runner-manager - amd64 -# Stress-testing runners +# Autoscaling GitLab Runner on AWS EC2 (arm64) -.linux-stress-amd64: &linux_stress_amd64 +.linux-arm64: &linux_arm64 tags: - - amd64 + - linux - aws - - linux-stress - - stress - -.linux-stress-arm64: &linux_stress_arm64 - tags: + - runner-manager - aarch64 - - aws - - linux-stress - - stress + +# Autoscaling GitLab Runner on AWS EC2 (FreeBSD) .freebsd-stress-amd64: &freebsd_stress_amd64 tags: - - amd64 + - bsd-stress-test - aws - - bsd-stress - - stress + - autoscaler + - shell + - stress-test + - amd64 ### Docker Image Templates # Alpine Linux -.alpine-3.18-amd64: &alpine_3_18_amd64_image - image: "$CI_REGISTRY_IMAGE:alpine-3.18-amd64" +.alpine-3.20-amd64: &alpine_3_20_amd64_image + image: "$CI_REGISTRY_IMAGE:alpine-3.20-amd64" <<: *linux_amd64 # Oracle Linux @@ -154,10 +144,6 @@ stages: image: "$CI_REGISTRY_IMAGE:debian-bullseye-amd64" <<: *linux_amd64 -.respdiff-debian-bookworm-amd64: &respdiff_debian_bookworm_amd64_image - image: "$CI_REGISTRY_IMAGE:debian-bookworm-amd64" - <<: *linux_respdiff_amd64 - .debian-bookworm-amd64: &debian_bookworm_amd64_image image: "$CI_REGISTRY_IMAGE:debian-bookworm-amd64" <<: *linux_amd64 @@ -182,17 +168,17 @@ stages: # Fedora -.tsan-fedora-38-amd64: &tsan_fedora_38_amd64_image - image: "$CI_REGISTRY_IMAGE:tsan-fedora-38-amd64" +.tsan-fedora-40-amd64: &tsan_fedora_40_amd64_image + image: "$CI_REGISTRY_IMAGE:tsan-fedora-40-amd64" <<: *linux_amd64 -.fedora-38-amd64: &fedora_38_amd64_image - image: "$CI_REGISTRY_IMAGE:fedora-38-amd64" +.fedora-40-amd64: &fedora_40_amd64_image + image: "$CI_REGISTRY_IMAGE:fedora-40-amd64" <<: *linux_amd64 -.fedora-38-arm64: &fedora_38_arm64_image - image: "$CI_REGISTRY_IMAGE:fedora-38-arm64" - <<: *linux_stress_arm64 +.fedora-40-arm64: &fedora_40_arm64_image + image: "$CI_REGISTRY_IMAGE:fedora-40-arm64" + <<: *linux_arm64 # Ubuntu @@ -204,6 +190,10 @@ stages: image: "$CI_REGISTRY_IMAGE:ubuntu-jammy-amd64" <<: *linux_amd64 +.ubuntu-noble-amd64: &ubuntu_noble_amd64_image + image: "$CI_REGISTRY_IMAGE:ubuntu-noble-amd64" + <<: *linux_amd64 + # Base image # This is a meta image that is used as a base for non-specific jobs @@ -212,31 +202,33 @@ stages: ### QCOW2 Image Templates -.freebsd-12-amd64: &freebsd_12_amd64_image - image: "freebsd-12.4-x86_64" +.freebsd-13-amd64: &freebsd_13_amd64_image + image: "freebsd-13.3-x86_64" <<: *libvirt_amd64 -.freebsd-13-amd64: &freebsd_13_amd64_image - image: "freebsd-13.2-x86_64" +.freebsd-14-amd64: &freebsd_14_amd64_image + image: "freebsd-14.0-x86_64" <<: *libvirt_amd64 .openbsd-amd64: &openbsd_amd64_image - image: "openbsd-7.3-x86_64" + image: "openbsd-7.5-x86_64" <<: *libvirt_amd64 ### Job Templates -.api-schedules-tags-triggers-web-triggering-rules: &api_schedules_tags_triggers_web_triggering_rules +.api-pipelines-schedules-tags-triggers-web-triggering-rules: &api_pipelines_schedules_tags_triggers_web_triggering_rules only: - api + - pipelines - schedules - tags - triggers - web -.api-schedules-triggers-web-triggering-rules: &api_schedules_triggers_web_triggering_rules +.api-pipelines-schedules-triggers-web-triggering-rules: &api_pipelines_schedules_triggers_web_triggering_rules only: - api + - pipelines - schedules - triggers - web @@ -245,6 +237,7 @@ stages: only: - api - merge_requests + - pipelines - schedules - tags - triggers @@ -280,11 +273,11 @@ stages: # change directory to the workspace before including this .find_python: &find_python - - PYTHON="$(source bin/tests/system/conf.sh; echo $PYTHON)" + - PYTHON="$(cat bin/tests/system/isctest/vars/.ac_vars/PYTHON)" - test -x "$PYTHON" .find_pytest: &find_pytest - - PYTEST="$(source bin/tests/system/conf.sh; echo $PYTEST)" + - PYTEST="$(cat bin/tests/system/isctest/vars/.ac_vars/PYTEST)" - test -x "$PYTEST" .parse_tsan: &parse_tsan @@ -307,8 +300,8 @@ stages: # Unpack release tarball and continue work in the extracted directory. .unpack_release_tarball: &unpack_release_tarball - - tar --extract --file bind-*.tar.${TARBALL_EXTENSION} - - rm -f bind-*.tar.${TARBALL_EXTENSION} + - tar --extract --file bind-*.tar.xz + - rm -f bind-*.tar.xz - cd bind-* .build: &build_job @@ -345,53 +338,29 @@ stages: sudo sh -x bin/tests/system/ifconfig.sh up; fi -cross-version-config-tests: - stage: system +.display_pytest_failures: &display_pytest_failures + - awk '/^=+ FAILURES =+/{flag=1;next}/^=+.*=+$/{flag=0}flag' bin/tests/system/pytest.out.txt || true + - awk '/^=+ ERRORS =+/{flag=1;next}/^=+.*=+$/{flag=0}flag' bin/tests/system/pytest.out.txt || true + +.shotgun: &shotgun_job <<: *base_image - <<: *default_triggering_rules - variables: - CC: gcc - CFLAGS: "${CFLAGS_COMMON}" - # Disable option checking to prevent problems with new default options in - # the &configure anchor. - EXTRA_CONFIGURE: "--disable-option-checking" + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules + stage: performance script: - - *configure - - *setup_interfaces - - make -j${BUILD_PARALLEL_JOBS:-1} - # Build system test binaries. - - make -C bin/tests/system -j${TEST_PARALLEL_JOBS:-1} check TESTS="" - - export BIND_BRANCH="$(sed -n -E "s|^m4_define\(\[bind_VERSION_MINOR\], ([0-9]+)\)dnl$|\1|p" configure.ac)" - # When testing a .0 release, compare it against the previous development - # release (e.g., 9.19.0 and 9.18.0 should both be compared against 9.17.22). - - if [ "$(sed -n -E "s|^m4_define\(\[bind_VERSION_PATCH\], ([0-9]+)\)dnl$|\1|p" configure.ac)" = "0" ]; then export BIND_BRANCH=$((BIND_BRANCH - 1 - (BIND_BRANCH % 2))); fi - - BASELINE="$(curl -s "https://gitlab.isc.org/api/v4/projects/1/repository/tags?search=^v9.${BIND_BRANCH}&order_by=version" | jq -r ".[0].name")" - - git clone --branch "${BASELINE}" --depth 1 https://gitlab.isc.org/isc-projects/bind9.git "bind-${BASELINE}" - - cd "bind-${BASELINE}" - - autoreconf -fi - - *configure - - make -j${BUILD_PARALLEL_JOBS:-1} - - *find_pytest - - cd bin/tests/system - # Run the setup phase of all system tests in the most recently tagged BIND 9 - # release using the binaries built for the current BIND 9 version. This - # intends to detect obvious backward compatibility issues with the latter. - - sed -i -E "s|(export TOP_BUILDDIR)=.*|\1=${CI_PROJECT_DIR}|" conf.sh - - > - "$PYTEST" --setup-only -n "${TEST_PARALLEL_JOBS:-1}" + - if [ -z "$CI_COMMIT_TAG" ]; then export SHOTGUN_ROUNDS=1; else export SHOTGUN_ROUNDS=3; fi + - PIPELINE_ID=$(curl -s -X POST --fail + -F "token=$CI_JOB_TOKEN" + -F ref=main + -F "variables[SHOTGUN_TEST_VERSION]=['$CI_COMMIT_REF_NAME', '$BIND_BASELINE_VERSION']" + -F "variables[SHOTGUN_DURATION]=300" + -F "variables[SHOTGUN_ROUNDS]=$SHOTGUN_ROUNDS" + -F "variables[SHOTGUN_TRAFFIC_MULTIPLIER]=$SHOTGUN_TRAFFIC_MULTIPLIER" + -F "variables[SHOTGUN_SCENARIO]=$SHOTGUN_SCENARIO" + https://gitlab.isc.org/api/v4/projects/188/trigger/pipeline | jq .id) + - util/ci-wait-shotgun.py $PIPELINE_ID needs: - - job: autoreconf + - job: ci-variables artifacts: true - artifacts: - paths: - - bind-* - untracked: true - expire_in: "1 day" - when: on_failure - -.display_pytest_failures: &display_pytest_failures - - awk '/^=+ FAILURES =+/{flag=1;next}/^=+.*=+$/{flag=0}flag' bin/tests/system/pytest.out.txt || true - - awk '/^=+ ERRORS =+/{flag=1;next}/^=+.*=+$/{flag=0}flag' bin/tests/system/pytest.out.txt || true .system_test_common: &system_test_common <<: *default_triggering_rules @@ -401,6 +370,8 @@ cross-version-config-tests: - *setup_interfaces script: - *find_pytest + - *find_python + - ( if [ "${CI_DISPOSABLE_ENVIRONMENT}" = "true" ]; then sleep 3000; "$PYTHON" "${CI_PROJECT_DIR}/util/get-running-system-tests.py"; fi ) & - cd bin/tests/system - > "$PYTEST" --junit-xml="$CI_PROJECT_DIR"/junit.xml -n "$TEST_PARALLEL_JOBS" | tee pytest.out.txt @@ -419,6 +390,14 @@ cross-version-config-tests: reports: junit: junit.xml +.system_test_make_check: &system_test_make_check_job + <<: *system_test_common + script: + - cd bin/tests/system + - make -j${TEST_PARALLEL_JOBS:-1} check + after_script: + - cat bin/tests/system/test-suite.log || true + .system_test_gcov: &system_test_gcov_job <<: *system_test_common artifacts: @@ -491,7 +470,6 @@ cross-version-config-tests: script: - *configure - make -j${BUILD_PARALLEL_JOBS:-1} -k doc V=1 - - qpdf --check doc/arm/_build/latex/Bv9ARM.pdf - find doc/man/ -maxdepth 1 -name "*.[0-9]" -exec mandoc -T lint "{}" \; | ( ! grep -v -e "skipping paragraph macro. sp after" -e "unknown font, skipping request. ft C" -e "input text line longer than 80 bytes" ) .respdiff: &respdiff_job @@ -501,14 +479,14 @@ cross-version-config-tests: - *configure - make -j${BUILD_PARALLEL_JOBS:-1} V=1 - *setup_interfaces - - git clone --depth 1 https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.isc.org/isc-private/bind-qa.git - - cd bind-qa/bind9/respdiff + - git clone --depth 1 https://gitlab.isc.org/isc-projects/bind9-qa.git + - cd bind9-qa/respdiff needs: [] artifacts: paths: - - bind-qa/bind9/respdiff + - bind9-qa/respdiff exclude: - - bind-qa/bind9/respdiff/rspworkdir/data.mdb # Exclude a 10 GB file. + - bind9-qa/respdiff/rspworkdir/data.mdb # Exclude a 10 GB file. untracked: true when: always @@ -523,15 +501,6 @@ misc: <<: *precheck_job script: - sh util/checklibs.sh > checklibs.out - - sh util/tabify-changes < CHANGES > CHANGES.tmp - - diff -urNap CHANGES CHANGES.tmp - - perl util/check-changes CHANGES - - sh util/check-line-length.sh CHANGES - - test ! -f CHANGES.SE || sh util/tabify-changes < CHANGES.SE > CHANGES.tmp - - test ! -f CHANGES.SE || diff -urNap CHANGES.SE CHANGES.tmp - - test ! -f CHANGES.SE || perl util/check-changes master=0 CHANGES.SE - - test ! -f CHANGES.SE || sh util/check-line-length.sh CHANGES.SE - - rm CHANGES.tmp - sh util/check-categories.sh - sh util/check-gitignore.sh - sh util/check-trailing-whitespace.sh @@ -544,6 +513,18 @@ misc: - checklibs.out when: on_failure +changes: + <<: *precheck_job + except: + - pipelines + script: + - sh util/tabify-changes < CHANGES > CHANGES.tmp + - diff -urNap CHANGES CHANGES.tmp + - perl util/check-changes CHANGES + - sh util/check-line-length.sh CHANGES + - rm CHANGES.tmp + needs: [] + black: <<: *precheck_job needs: [] @@ -557,6 +538,23 @@ black: expire_in: "1 week" when: on_failure +ci-variables: + stage: precheck + <<: *precheck_job + script: + - export BIND_BASELINE_BRANCH="$(sed -n -E "s|^m4_define\(\[bind_VERSION_MINOR\], ([0-9]+)\)dnl$|\1|p" configure.ac)" + # When testing a .0 release, compare it against the previous development + # release (e.g., 9.19.0 and 9.18.0 should both be compared against 9.17.22). + - if [ "$(sed -n -E "s|^m4_define\(\[bind_VERSION_PATCH\], ([0-9]+)\)dnl$|\1|p" configure.ac)" = "0" ]; then export BIND_BASELINE_BRANCH=$((BIND_BASELINE_BRANCH - 1 - (BIND_BASELINE_BRANCH % 2))); fi + - BIND_BASELINE_VERSION="$(curl -s "https://gitlab.isc.org/api/v4/projects/1/repository/tags?search=^v9.${BIND_BASELINE_BRANCH}&order_by=version" | jq -r ".[0].name")" + - echo "BIND_BASELINE_VERSION=$BIND_BASELINE_VERSION" >> ci_vars.env + needs: + - job: autoreconf + artifacts: true + artifacts: + reports: + dotenv: ci_vars.env + clang-format: <<: *precheck_job needs: [] @@ -580,6 +578,8 @@ coccinelle: pylint: <<: *precheck_job needs: [] + variables: + PYTHONPATH: "${CI_PROJECT_DIR}/bin/tests/system" script: - pylint --rcfile $CI_PROJECT_DIR/.pylintrc $(git ls-files '*.py' | grep -vE '(ans\.py|dangerfile\.py|^bin/tests/system/)') # Ignore Pylint wrong-import-position error in system test to enable use of pytest.importorskip @@ -594,16 +594,33 @@ reuse: script: - reuse lint +shfmt: + <<: *precheck_job + needs: [] + script: + - shfmt -w -i 2 -ci -bn . $(find . -name "*.sh.in") + - git diff > shfmt.patch + - if test "$(git status --porcelain | grep -Ev '\?\?' | wc -l)" -gt "0"; then git status --short; exit 1; fi + artifacts: + paths: + - shfmt.patch + expire_in: "1 week" + when: on_failure + danger: <<: *precheck_job + # Keep the GIT_DEPTH environment variable set to a "high number" before + # https://github.com/libgit2/libgit2/pull/6662 is addressed and integrated + # into pygit2. + variables: + GIT_DEPTH: 1000 needs: [] script: - - danger-python ci -f + - pip install git+https://gitlab.isc.org/isc-projects/hazard.git + - hazard only: refs: - merge_requests - variables: - - $DANGER_GITLAB_API_TOKEN checkbashisms: <<: *precheck_job @@ -611,6 +628,11 @@ checkbashisms: script: - checkbashisms $(find . -path './.git' -prune -o -type f -exec sh -c 'head -n 1 "{}" | grep -qsF "#!/bin/sh"' \; -print) +mypy: + <<: *precheck_job + script: + - mypy "bin/tests/system/isctest/" + tarball-create: stage: precheck <<: *base_image @@ -626,7 +648,7 @@ tarball-create: artifacts: paths: - diff.patch - - bind-*.tar.${TARBALL_EXTENSION} + - bind-*.tar.xz when: always needs: - job: autoreconf @@ -654,28 +676,77 @@ docs:tarball: - job: tarball-create artifacts: true -# Jobs for regular GCC builds on Alpine Linux 3.18 (amd64) +# Job detecting named.conf breakage introduced since the previous point release -gcc:alpine3.18:amd64: +cross-version-config-tests: + stage: system + <<: *base_image + <<: *default_triggering_rules + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON}" + # Disable option checking to prevent problems with new default options in + # the &configure anchor. + EXTRA_CONFIGURE: "--disable-option-checking" + PYTEST: "/usr/bin/pytest-3" + script: + - *configure + - *setup_interfaces + - make -j${BUILD_PARALLEL_JOBS:-1} + - git clone --branch "${BIND_BASELINE_VERSION}" --depth 1 https://gitlab.isc.org/isc-projects/bind9.git "bind-${BIND_BASELINE_VERSION}" + - cd "bind-${BIND_BASELINE_VERSION}" + - autoreconf -fi + - *configure + - make -j${BUILD_PARALLEL_JOBS:-1} + - cd bin/tests/system + # Run the setup phase of all system tests in the most recently tagged BIND 9 + # release using the binaries built for the current BIND 9 version. This + # intends to detect obvious backward compatibility issues with the latter. + - > + if [ -f isctest/vars/autoconf.py ]; then + echo "${CI_PROJECT_DIR}" > isctest/vars/.ac_vars/TOP_BUILDDIR + else + sed -i -E "s|(export TOP_BUILDDIR)=.*|\1=${CI_PROJECT_DIR}|" conf.sh; + fi + - > + "$PYTEST" --setup-only --junit-xml="$CI_PROJECT_DIR"/junit.xml -n "${TEST_PARALLEL_JOBS:-1}" + needs: + - job: autoreconf + artifacts: true + - job: ci-variables + artifacts: true + artifacts: + reports: + junit: junit.xml + paths: + - bind-* + - junit.xml + untracked: true + expire_in: "1 day" + when: always + +# Jobs for regular GCC builds on Alpine Linux 3.20 (amd64) + +gcc:alpine3.20:amd64: variables: CC: gcc CFLAGS: "${CFLAGS_COMMON}" EXTRA_CONFIGURE: "${WITHOUT_READLINE}" - <<: *alpine_3_18_amd64_image + <<: *alpine_3_20_amd64_image <<: *build_job -system:gcc:alpine3.18:amd64: - <<: *alpine_3_18_amd64_image +system:gcc:alpine3.20:amd64: + <<: *alpine_3_20_amd64_image <<: *system_test_job needs: - - job: gcc:alpine3.18:amd64 + - job: gcc:alpine3.20:amd64 artifacts: true -unit:gcc:alpine3.18:amd64: - <<: *alpine_3_18_amd64_image +unit:gcc:alpine3.20:amd64: + <<: *alpine_3_20_amd64_image <<: *unit_test_job needs: - - job: gcc:alpine3.18:amd64 + - job: gcc:alpine3.20:amd64 artifacts: true # Jobs for regular GCC builds on Oracle Linux 8 (amd64) @@ -735,12 +806,12 @@ gcc:8fips:amd64: EXTRA_CONFIGURE: "--with-libidn2 --enable-fips-mode --disable-tracing" <<: *oraclelinux_8fips_amd64_image <<: *build_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules system:gcc:8fips:amd64: <<: *oraclelinux_8fips_amd64_image <<: *system_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules needs: - job: gcc:8fips:amd64 artifacts: true @@ -748,7 +819,7 @@ system:gcc:8fips:amd64: unit:gcc:8fips:amd64: <<: *oraclelinux_8fips_amd64_image <<: *unit_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules needs: - job: gcc:8fips:amd64 artifacts: true @@ -760,12 +831,12 @@ gcc:9fips:amd64: EXTRA_CONFIGURE: "--with-libidn2 --enable-fips-mode --disable-leak-detection --disable-tracing" <<: *oraclelinux_9fips_amd64_image <<: *build_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules system:gcc:9fips:amd64: <<: *oraclelinux_9fips_amd64_image <<: *system_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules needs: - job: gcc:9fips:amd64 artifacts: true @@ -773,7 +844,7 @@ system:gcc:9fips:amd64: unit:gcc:9fips:amd64: <<: *oraclelinux_9fips_amd64_image <<: *unit_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules needs: - job: gcc:9fips:amd64 artifacts: true @@ -823,6 +894,30 @@ unit:gcc:bookworm:amd64: - job: gcc:bookworm:amd64 artifacts: true +# Jobs for RBT zone- & cache-enabled GCC builds on Debian 12 "bookworm" (amd64) + +gcc:bookworm:rbt:amd64: + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON}" + EXTRA_CONFIGURE: "--with-libidn2 --with-zonedb=rbt --with-cachedb=rbt" + <<: *debian_bookworm_amd64_image + <<: *build_job + +system:gcc:bookworm:rbt:amd64: + <<: *debian_bookworm_amd64_image + <<: *system_test_job + needs: + - job: unit:gcc:bookworm:rbt:amd64 + artifacts: true + +unit:gcc:bookworm:rbt:amd64: + <<: *debian_bookworm_amd64_image + <<: *unit_test_job + needs: + - job: gcc:bookworm:rbt:amd64 + artifacts: true + # Build job for cross-compiled GCC builds on 64-bit Debian 12 "bookworm" # (amd64) with 32-bit BIND 9. @@ -877,7 +972,7 @@ gcc:ossl3:sid:amd64: <<: *build_job system:gcc:ossl3:sid:amd64: - # Set up environment variables to run pkcs11-provider system tests + # Set up environment variables to run pkcs11-provider based system tests variables: OPENSSL_CONF: "/var/tmp/etc/openssl-provider.cnf" SOFTHSM2_CONF: "/var/tmp/softhsm2/softhsm2.conf" @@ -909,6 +1004,7 @@ gcc:sid:amd64: system:gcc:sid:amd64: <<: *debian_sid_amd64_image <<: *system_test_job + <<: *system_test_make_check_job needs: - job: gcc:sid:amd64 artifacts: true @@ -942,7 +1038,7 @@ system:gcc:out-of-tree: artifacts: true <<: *base_image <<: *system_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules unit:gcc:out-of-tree: variables: @@ -952,7 +1048,7 @@ unit:gcc:out-of-tree: artifacts: true <<: *base_image <<: *unit_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules # Jobs for tarball GCC builds on Debian 12 "bookworm" (amd64) @@ -972,7 +1068,7 @@ gcc:tarball: system:gcc:tarball: <<: *base_image <<: *system_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules before_script: - cd bind-* - *setup_interfaces @@ -986,7 +1082,7 @@ system:gcc:tarball: unit:gcc:tarball: <<: *base_image <<: *unit_test_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *api_pipelines_schedules_tags_triggers_web_triggering_rules before_script: - cd bind-* needs: @@ -1073,7 +1169,31 @@ unit:gcc:jammy:amd64: - job: gcc:jammy:amd64 artifacts: true -# Jobs for ASAN builds on Fedora 38 (amd64) +# Jobs for regular GCC builds on Ubuntu 24.04 Noble Numbat (amd64) + +gcc:noble:amd64: + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON}" + EXTRA_CONFIGURE: "--with-libidn2" + <<: *ubuntu_noble_amd64_image + <<: *build_job + +system:gcc:noble:amd64: + <<: *ubuntu_noble_amd64_image + <<: *system_test_job + needs: + - job: gcc:noble:amd64 + artifacts: true + +unit:gcc:noble:amd64: + <<: *ubuntu_noble_amd64_image + <<: *unit_test_job + needs: + - job: gcc:noble:amd64 + artifacts: true + +# Jobs for ASAN builds on Fedora 40 (amd64) gcc:asan: variables: @@ -1081,20 +1201,20 @@ gcc:asan: CFLAGS: "${CFLAGS_COMMON} -fsanitize=address,undefined" LDFLAGS: "-fsanitize=address,undefined" EXTRA_CONFIGURE: "--with-libidn2 --without-jemalloc" - <<: *fedora_38_amd64_image + <<: *fedora_40_amd64_image <<: *build_job system:gcc:asan: variables: LSAN_OPTIONS: "suppressions=$CI_PROJECT_DIR/suppr-lsan.txt" - <<: *fedora_38_amd64_image + <<: *fedora_40_amd64_image <<: *system_test_job needs: - job: gcc:asan artifacts: true unit:gcc:asan: - <<: *fedora_38_amd64_image + <<: *fedora_40_amd64_image <<: *unit_test_job needs: - job: gcc:asan @@ -1123,7 +1243,7 @@ unit:clang:asan: - job: clang:asan artifacts: true -# Jobs for TSAN builds on Fedora 38 (amd64) +# Jobs for TSAN builds on Fedora 40 (amd64) gcc:tsan: variables: @@ -1131,13 +1251,13 @@ gcc:tsan: CFLAGS: "${CFLAGS_COMMON} -fsanitize=thread" LDFLAGS: "-fsanitize=thread" EXTRA_CONFIGURE: "--with-libidn2 --enable-pthread-rwlock --without-jemalloc" - <<: *tsan_fedora_38_amd64_image + <<: *tsan_fedora_40_amd64_image <<: *build_job system:gcc:tsan: variables: TSAN_OPTIONS: "${TSAN_OPTIONS_FEDORA}" - <<: *tsan_fedora_38_amd64_image + <<: *tsan_fedora_40_amd64_image <<: *system_test_tsan_job needs: - job: gcc:tsan @@ -1146,7 +1266,7 @@ system:gcc:tsan: unit:gcc:tsan: variables: TSAN_OPTIONS: "${TSAN_OPTIONS_FEDORA}" - <<: *tsan_fedora_38_amd64_image + <<: *tsan_fedora_40_amd64_image <<: *unit_test_tsan_job needs: - job: gcc:tsan @@ -1260,56 +1380,60 @@ unit:clang:bookworm:amd64: - job: clang:bookworm:amd64 artifacts: true -# Jobs for Clang builds on FreeBSD 12 (amd64) +# Jobs for Clang builds on FreeBSD 13 (amd64) -clang:freebsd12:amd64: +clang:freebsd13:amd64: variables: CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "${WITH_READLINE_EDITLINE}" + # Use MIT Kerberos5 for BIND 9 GSS-API support because of FreeBSD Heimdal + # incompatibility; see https://bugs.freebsd.org/275241. + EXTRA_CONFIGURE: "${WITH_READLINE_LIBEDIT} --with-gssapi=/usr/local/bin/krb5-config" USER: gitlab-runner - <<: *freebsd_12_amd64_image + <<: *freebsd_13_amd64_image <<: *build_job -system:clang:freebsd12:amd64: - <<: *freebsd_12_amd64_image +system:clang:freebsd13:amd64: + <<: *freebsd_13_amd64_image <<: *system_test_job variables: USER: gitlab-runner needs: - - job: clang:freebsd12:amd64 + - job: clang:freebsd13:amd64 artifacts: true -unit:clang:freebsd12:amd64: - <<: *freebsd_12_amd64_image +unit:clang:freebsd13:amd64: + <<: *freebsd_13_amd64_image <<: *unit_test_job needs: - - job: clang:freebsd12:amd64 + - job: clang:freebsd13:amd64 artifacts: true -# Jobs for Clang builds on FreeBSD 13 (amd64) +# Jobs for Clang builds on FreeBSD 14 (amd64) -clang:freebsd13:amd64: +clang:freebsd14:amd64: variables: CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "${WITH_READLINE_LIBEDIT}" + # Use MIT Kerberos5 for BIND 9 GSS-API support because of FreeBSD Heimdal + # incompatibility; see https://bugs.freebsd.org/275241. + EXTRA_CONFIGURE: "${WITH_READLINE_EDITLINE} --with-gssapi=/usr/local/bin/krb5-config" USER: gitlab-runner - <<: *freebsd_13_amd64_image + <<: *freebsd_14_amd64_image <<: *build_job -system:clang:freebsd13:amd64: - <<: *freebsd_13_amd64_image +system:clang:freebsd14:amd64: + <<: *freebsd_14_amd64_image <<: *system_test_job variables: USER: gitlab-runner needs: - - job: clang:freebsd13:amd64 + - job: clang:freebsd14:amd64 artifacts: true -unit:clang:freebsd13:amd64: - <<: *freebsd_13_amd64_image +unit:clang:freebsd14:amd64: + <<: *freebsd_14_amd64_image <<: *unit_test_job needs: - - job: clang:freebsd13:amd64 + - job: clang:freebsd14:amd64 artifacts: true # Jobs for Clang builds on OpenBSD (amd64) @@ -1322,17 +1446,6 @@ clang:openbsd:amd64: <<: *openbsd_amd64_image <<: *build_job -system:clang:openbsd:amd64: - <<: *openbsd_amd64_image - <<: *system_test_job - <<: *api_schedules_triggers_web_triggering_rules - variables: - USER: gitlab-runner - needs: - - job: clang:openbsd:amd64 - artifacts: true - allow_failure: true - unit:clang:openbsd:amd64: <<: *openbsd_amd64_image <<: *unit_test_job @@ -1342,27 +1455,24 @@ unit:clang:openbsd:amd64: - job: clang:openbsd:amd64 artifacts: true -# Job producing a release tarball +# Job producing a release directory release: <<: *base_image stage: release script: - - export BIND_DIRECTORY="$(basename "$(find . -name "bind-*.tar.*" -printf "%f")" ".tar.${TARBALL_EXTENSION}")" + - export BIND_DIRECTORY="$(basename bind-*.tar.xz ".tar.xz")" # Prepare release tarball contents (tarballs + documentation) - - mkdir -p release/doc/arm - - pushd release - - mv "../${BIND_DIRECTORY}.tar.${TARBALL_EXTENSION}" . - - tar --extract --file="${BIND_DIRECTORY}.tar.${TARBALL_EXTENSION}" + - mkdir -p "${BIND_DIRECTORY}-release/doc/arm" + - pushd "${BIND_DIRECTORY}-release" + - mv "../${BIND_DIRECTORY}.tar.xz" . + - tar --extract --file="${BIND_DIRECTORY}.tar.xz" - mv "${BIND_DIRECTORY}"/{CHANGES*,COPYRIGHT,LICENSE,README.md,srcid} . - rm -rf "${BIND_DIRECTORY}" - mv "../doc/arm/_build/html" doc/arm/ - - mv "../doc/arm/_build/latex/Bv9ARM.pdf" doc/arm/ - mv "../doc/arm/_build/epub/Bv9ARM.epub" doc/arm/ - echo 'Redirect' > "RELEASE-NOTES-${BIND_DIRECTORY}.html" - popd - # Create release tarball - - tar --create --file="${CI_COMMIT_TAG}.tar.gz" --gzip release/ needs: - job: tarball-create artifacts: true @@ -1370,10 +1480,52 @@ release: artifacts: true only: - tags + artifacts: + paths: + - "*-release" + expire_in: "1 month" + +# Job signing the source tarballs in the release directory + +sign: + stage: release + tags: + - signer + script: + - export RELEASE_DIRECTORY="$(echo *-release)" + - pushd "${RELEASE_DIRECTORY}" + - | + echo + cat > /tmp/sign-bind9.sh <>> Signing \${FILE}..." + gpg2 --local-user "\${SIGNING_KEY_FINGERPRINT}" --armor --digest-algo SHA512 --detach-sign --output "\${FILE}.asc" "\${FILE}" + done + } 2>&1 | tee "${CI_PROJECT_DIR}/signing.log" + EOF + chmod +x /tmp/sign-bind9.sh + echo -e "\e[31m*** Please sign the releases by following the instructions at:\e[0m" + echo -e "\e[31m*** \e[0m" + echo -e "\e[31m*** ${SIGNING_HELP_URL}\e[0m" + echo -e "\e[31m*** \e[0m" + echo -e "\e[31m*** Sleeping until files in ${PWD} are signed... ⌛\e[0m" + while [ "$(find . -name "*.asc" -size +0 | sed "s|\.asc$||" | sort)" != "$(find . -name "*.tar.xz" | sort)" ]; do sleep 10; done + - popd + - tar --create --file="${RELEASE_DIRECTORY}.tar.gz" --gzip "${RELEASE_DIRECTORY}" artifacts: paths: - "*.tar.gz" + - signing.log expire_in: never + needs: + - job: release + artifacts: true + only: + - tags + when: manual + allow_failure: false # Coverity Scan analysis upload @@ -1430,7 +1582,7 @@ coverity: # Respdiff tests -respdiff-short: +respdiff: <<: *respdiff_job <<: *default_triggering_rules <<: *debian_bookworm_amd64_image @@ -1439,9 +1591,9 @@ respdiff-short: CFLAGS: "${CFLAGS_COMMON} -Og -DISC_TRACK_PTHREADS_OBJECTS" MAX_DISAGREEMENTS_PERCENTAGE: "0.5" script: - - bash respdiff.sh -m /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 -s named -q "${PWD}/10k_a.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" + - bash respdiff.sh -m /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" -respdiff-short:asan: +respdiff:asan: <<: *respdiff_job <<: *default_triggering_rules <<: *debian_bookworm_amd64_image @@ -1452,9 +1604,9 @@ respdiff-short:asan: EXTRA_CONFIGURE: "--disable-dnsrps --without-jemalloc" MAX_DISAGREEMENTS_PERCENTAGE: "0.5" script: - - bash respdiff.sh -s named -q "${PWD}/10k_a.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" + - bash respdiff.sh -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" -respdiff-short:tsan: +respdiff:tsan: <<: *respdiff_job <<: *default_triggering_rules <<: *tsan_debian_bookworm_amd64_image @@ -1466,219 +1618,312 @@ respdiff-short:tsan: MAX_DISAGREEMENTS_PERCENTAGE: "0.5" TSAN_OPTIONS: "${TSAN_OPTIONS_DEBIAN}" script: - - bash respdiff.sh -s named -q "${PWD}/10k_a.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" + - bash respdiff.sh -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" after_script: - *find_python - *parse_tsan -respdiff-long: +respdiff-third-party: <<: *respdiff_job - <<: *api_schedules_tags_triggers_web_triggering_rules - <<: *respdiff_debian_bookworm_amd64_image - variables: - CC: gcc - CFLAGS: "${CFLAGS_COMMON} -Og -DISC_TRACK_PTHREADS_OBJECTS" - MAX_DISAGREEMENTS_PERCENTAGE: "0.5" - script: - - bash respdiff.sh -m /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" - -respdiff-long:asan: - <<: *respdiff_job - <<: *api_schedules_tags_triggers_web_triggering_rules + <<: *default_triggering_rules <<: *debian_bookworm_amd64_image variables: CC: gcc - CFLAGS: "${CFLAGS_COMMON} -Og -fsanitize=address,undefined" - LDFLAGS: "-fsanitize=address,undefined" - EXTRA_CONFIGURE: "--disable-dnsrps --without-jemalloc" + CFLAGS: "${CFLAGS_COMMON} -Og" MAX_DISAGREEMENTS_PERCENTAGE: "0.5" script: - - bash respdiff.sh -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" + - bash respdiff.sh -s third_party -q "${PWD}/100k_mixed.txt" -c 1 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" -respdiff-long:tsan: - <<: *respdiff_job - <<: *api_schedules_tags_triggers_web_triggering_rules - <<: *tsan_debian_bookworm_amd64_image +# Performance tests + +# Run shotgun:udp right away, but delay other shotgun jobs sligthly in order to +# allow re-use of the built container image. Otherwise, the jobs would do the +# same builds in parallel rather than re-use the already built image. +shotgun:udp: + <<: *shotgun_job variables: - CC: gcc - CFLAGS: "${CFLAGS_COMMON} -Og -fsanitize=thread" - LDFLAGS: "-fsanitize=thread" - EXTRA_CONFIGURE: "--disable-dnsrps --enable-pthread-rwlock --without-jemalloc" - MAX_DISAGREEMENTS_PERCENTAGE: "0.5" - TSAN_OPTIONS: "${TSAN_OPTIONS_DEBIAN}" - script: - - bash respdiff.sh -s named -q "${PWD}/100k_mixed.txt" -c 3 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" "/usr/local/respdiff-reference-bind/sbin/named" - after_script: - - *find_python - - *parse_tsan + SHOTGUN_SCENARIO: udp + SHOTGUN_TRAFFIC_MULTIPLIER: 15 -respdiff-long-third-party: - <<: *respdiff_job - <<: *api_schedules_tags_triggers_web_triggering_rules - <<: *debian_bookworm_amd64_image +shotgun:tcp: + <<: *shotgun_job variables: - CC: gcc - CFLAGS: "${CFLAGS_COMMON} -Og" - MAX_DISAGREEMENTS_PERCENTAGE: "0.5" - script: - - bash respdiff.sh -s third_party -q "${PWD}/100k_mixed.txt" -c 1 -w "${PWD}/rspworkdir" "${CI_PROJECT_DIR}" + SHOTGUN_SCENARIO: tcp + SHOTGUN_TRAFFIC_MULTIPLIER: 6 + when: delayed + start_in: 5 minutes -# "Stress" tests +shotgun:dot: + <<: *shotgun_job + variables: + SHOTGUN_SCENARIO: dot + SHOTGUN_TRAFFIC_MULTIPLIER: 3 + when: delayed + start_in: 5 minutes -# Parallel build in the "make" step is avoided since multiple jobs can be -# executed concurrently on the same runner. This may present problems when one -# job runs a performance-sensitive task of replying to queries while another -# takes all cores to build BIND. -.stress: &stress_job +.stress-test: &stress_test stage: performance script: - *configure - *setup_interfaces - - make -k all V=1 + - make -j${BUILD_PARALLEL_JOBS:-1} -k all V=1 - make DESTDIR="${INSTALL_PATH}" install - - git clone --depth 1 https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.isc.org/isc-private/bind-qa.git - - cd bind-qa/bind9/stress + - git clone --depth 1 https://gitlab.isc.org/isc-projects/bind9-qa.git + - cd bind9-qa/stress - LD_LIBRARY_PATH="${INSTALL_PATH}/usr/local/lib" BIND_INSTALL_PATH="${INSTALL_PATH}/usr/local" WORKSPACE="${CI_PROJECT_DIR}" bash stress.sh needs: - job: autoreconf artifacts: true + +.stress-test-short: &stress_test_short_job + <<: *stress_test + artifacts: + untracked: true + when: always + only: + - merge_requests + +stress:short:authoritative:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: authoritative + RATE: 10000 + RUN_TIME: 15 + +stress:short:recursive:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: recursive + RATE: 10000 + RUN_TIME: 15 + +stress:short:rpz:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: rpz + RATE: 1500 + RUN_TIME: 15 + +stress:short:authoritative:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: authoritative + RATE: 10000 + RUN_TIME: 15 + +stress:short:recursive:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: recursive + RATE: 10000 + RUN_TIME: 15 + +stress:short:rpz:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_short_job + variables: + CC: gcc + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/bin/flame + MODE: rpz + RATE: 1500 + RUN_TIME: 15 + +stress:short:authoritative:freebsd13:amd64: + <<: *freebsd_stress_amd64 + <<: *stress_test_short_job + variables: + CC: clang + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/local/bin/flame + MODE: authoritative + RATE: 10000 + RUN_TIME: 15 + +stress:short:recursive:freebsd13:amd64: + <<: *freebsd_stress_amd64 + <<: *stress_test_short_job + variables: + CC: clang + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/local/bin/flame + MODE: recursive + RATE: 10000 + RUN_TIME: 15 + +stress:short:rpz:freebsd13:amd64: + <<: *freebsd_stress_amd64 + <<: *stress_test_short_job + variables: + CC: clang + CFLAGS: "${CFLAGS_COMMON} -Og" + FLAME: /usr/local/bin/flame + MODE: rpz + RATE: 1500 + RUN_TIME: 15 + +.stress-test-long: &stress_test_long_job + <<: *stress_test artifacts: untracked: true expire_in: "1 week" when: always timeout: 2h -stress:authoritative:fedora:38:amd64: - <<: *fedora_38_amd64_image - <<: *linux_stress_amd64 - <<: *stress_job +stress:authoritative:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: authoritative RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /authoritative/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) -stress:recursive:fedora:38:amd64: - <<: *fedora_38_amd64_image - <<: *linux_stress_amd64 - <<: *stress_job +stress:recursive:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: recursive RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /recursive/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) -stress:rpz:fedora:38:amd64: - <<: *fedora_38_amd64_image - <<: *linux_stress_amd64 - <<: *stress_job +stress:rpz:fedora:40:amd64: + <<: *fedora_40_amd64_image + <<: *linux_amd64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: rpz RATE: 1500 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /rpz/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) -stress:authoritative:fedora:38:arm64: - <<: *fedora_38_arm64_image - <<: *linux_stress_arm64 - <<: *stress_job +stress:authoritative:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: authoritative RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /authoritative/i && $BIND_STRESS_TEST_ARCH =~ /arm64/i) -stress:recursive:fedora:38:arm64: - <<: *fedora_38_arm64_image - <<: *linux_stress_arm64 - <<: *stress_job +stress:recursive:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: recursive RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /recursive/i && $BIND_STRESS_TEST_ARCH =~ /arm64/i) -stress:rpz:fedora:38:arm64: - <<: *fedora_38_arm64_image - <<: *linux_stress_arm64 - <<: *stress_job +stress:rpz:fedora:40:arm64: + <<: *fedora_40_arm64_image + <<: *linux_arm64 + <<: *stress_test_long_job variables: CC: gcc CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/bin/flame MODE: rpz RATE: 1500 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /rpz/i && $BIND_STRESS_TEST_ARCH =~ /arm64/i) -stress:authoritative:freebsd12:amd64: - <<: *freebsd_12_amd64_image +stress:authoritative:freebsd13:amd64: <<: *freebsd_stress_amd64 - <<: *stress_job + <<: *stress_test_long_job variables: CC: clang CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/local/bin/flame MODE: authoritative RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /freebsd/i && $BIND_STRESS_TEST_MODE =~ /authoritative/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) -stress:recursive:freebsd12:amd64: - <<: *freebsd_12_amd64_image +stress:recursive:freebsd13:amd64: <<: *freebsd_stress_amd64 - <<: *stress_job + <<: *stress_test_long_job variables: CC: clang CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/local/bin/flame MODE: recursive RATE: 10000 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /freebsd/i && $BIND_STRESS_TEST_MODE =~ /recursive/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) -stress:rpz:freebsd12:amd64: - <<: *freebsd_12_amd64_image +stress:rpz:freebsd13:amd64: <<: *freebsd_stress_amd64 - <<: *stress_job + <<: *stress_test_long_job variables: CC: clang CFLAGS: "${CFLAGS_COMMON} -Og" FLAME: /usr/local/bin/flame MODE: rpz RATE: 1500 - RUN_TIME: 1 + RUN_TIME: 60 only: variables: - $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /freebsd/i && $BIND_STRESS_TEST_MODE =~ /rpz/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i) @@ -1751,3 +1996,24 @@ pairwise: only: variables: - $PAIRWISE_TESTING + +backports: + <<: *base_image + stage: postmerge + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && ($CI_COMMIT_REF_NAME =~ /^bind-9.[0-9]+$/ || $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH)' + variables: + # automated commits will inherit identification from the user who pressed Merge button + GIT_COMMITTER_NAME: $GITLAB_USER_NAME + GIT_COMMITTER_EMAIL: $GITLAB_USER_EMAIL + # avoid leftover branches from previous jobs + GIT_STRATEGY: clone + # assumed max depth of a MR for backport + GIT_DEPTH: 200 + script: + # CI job token is not sufficient for push operations + - git remote get-url origin | sed -e "s/gitlab-ci-token:$CI_JOB_TOKEN/oauth2:$BIND_TEAM_WRITE_TOKEN/" | xargs git remote set-url --push origin + # force-pushing is disabled so we have to have merge request on top + - MERGE_REQUEST_ID="$(git log -1 --format='%b' | sed --silent -e 's/^See merge request [^!]\+!//p')" + - git clone --depth 1 https://gitlab.isc.org/isc-projects/bind9-qa.git + - bind9-qa/releng/backport_mr.py $CI_PROJECT_ID "$MERGE_REQUEST_ID" diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index f8b82b694b..60bab3f6ce 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -2,45 +2,62 @@ If the bug you are reporting is potentially security-related - for example, if it involves an assertion failure or other crash in `named` that can be triggered repeatedly - then please make sure that you make the new issue -confidential! +confidential by clicking the checkbox at the bottom! --> ### Summary -(Summarize the bug encountered concisely.) + -### BIND version used +### BIND version affected + ### Steps to reproduce -(How one can reproduce the issue - this is very important.) + + +1. +2. +3. ### What is the current *bug* behavior? -(What actually happens.) + ### What is the expected *correct* behavior? -(What you should see instead.) + ### Relevant configuration files -(Paste any relevant configuration files - please use code blocks (```) + -### Possible fixes +### Relevant logs -(If you can, link to the line of code that might be responsible for the -problem.) + -/label ~bug +/label ~Bug diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 0000000000..c0079fa2a8 --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -0,0 +1,8 @@ +Hi and thanks for filing an issue! It will be read with care by human beings. + +It would be a tremendous help if you could follow these steps first: +- [ ] Search the existing issues in GitLab (both open and closed) to see if your report might be a duplicate. We have a large database here and many issues have already been fixed in the latest versions! +- [ ] Make sure this is **not** a support question. If you have specific trouble configuring or debugging your setup, please use the bind-users mailing list: https://lists.isc.org/mailman/listinfo/bind-users +- [ ] You have read and understood the "out in the open" support policy: https://blog.powerdns.com/2016/01/18/open-source-support-out-in-the-open/ . Even though it was written by the PowerDNS folks, we follow it as well! + +Before continuing, **please select the appropriate issue template in the drop-down menu above, under the heading _Description_**. diff --git a/.gitlab/issue_templates/Feature_Request.md b/.gitlab/issue_templates/Feature_Request.md index 9ad28f49ed..72d2cb4931 100644 --- a/.gitlab/issue_templates/Feature_Request.md +++ b/.gitlab/issue_templates/Feature_Request.md @@ -8,4 +8,4 @@ ### Links / references -/label ~"feature request" +/label ~Feature diff --git a/.gitlab/issue_templates/CVE.md b/.gitlab/issue_templates/Internal_use_only-CVE.md similarity index 84% rename from .gitlab/issue_templates/CVE.md rename to .gitlab/issue_templates/Internal_use_only-CVE.md index 782c2a4d04..7dcf7fe3a9 100644 --- a/.gitlab/issue_templates/CVE.md +++ b/.gitlab/issue_templates/Internal_use_only-CVE.md @@ -16,11 +16,9 @@ confidential! | Mattermost Channel: | [CVE-YYYY-NNNN][mattermost_url] | | Support Ticket: | [URL] | | Release Checklist: | #NNNN | -| Post-mortem Etherpad: | [postmortem-YYYY-MM][postmortem_url] | [cvss_score]: https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:X/AC:X/PR:X/UI:X/S:X/C:X/I:X/A:X&version=3.1 [mattermost_url]: -[postmortem_url]: :bulb: **Click [here][checklist_explanations] (internal resource) for general information about the security incident handling process.** @@ -30,14 +28,14 @@ confidential! - [ ] [:link:][step_deputy] **(IM)** Pick a Deputy Incident Manager - [ ] [:link:][step_respond] **(IM)** Respond to the bug reporter - - [ ] [:link:][step_etherpad] **(IM)** Create an Etherpad for post-mortem - [ ] [:link:][step_public_mrs] **(SwEng)** Ensure there are no public merge requests which inadvertently disclose the issue - [ ] [:link:][step_assign_cve_id] **(IM)** Assign a CVE identifier - [ ] [:link:][step_note_cve_info] **(SwEng)** Update this issue with the assigned CVE identifier and the CVSS score - [ ] [:link:][step_versions_affected] **(SwEng)** Determine the range of product versions affected (including the Subscription Edition) - [ ] [:link:][step_workarounds] **(SwEng)** Determine whether workarounds for the problem exist - [ ] [:link:][step_coordinate] **(SwEng)** If necessary, coordinate with other parties - - [ ] [:link:][step_earliest] **(Support)** Prepare and send out "earliest" notifications + - [ ] [:link:][step_earliest_prepare] **(Support)** Prepare "earliest" notification text and hand it off to Marketing + - [ ] [:link:][step_earliest_send] **(Marketing)** Update "earliest" notification document in SF portal and send bulk email to earliest customers - [ ] [:link:][step_advisory_mr] **(Support)** Create a merge request for the Security Advisory and include all readily available information in it - [ ] [:link:][step_reproducer_mr] **(SwEng)** Prepare a private merge request containing a system test reproducing the problem - [ ] [:link:][step_notify_support] **(SwEng)** Notify Support when a reproducer is ready @@ -55,46 +53,42 @@ confidential! ### At T-5 - - [ ] [:link:][step_send_asn] **(Support)** Send ASN to eligible customers - - [ ] [:link:][step_preannouncement] **(Support)** (BIND 9 only) Send a pre-announcement email to the *bind-announce* mailing list to alert users that the upcoming release will include security fixes - -### At T-4 - - - [ ] [:link:][step_verify_asn] **(Support)** Verify that all ASN-eligible customers have received the notification email + - [ ] [:link:][step_asn_documents] **(Marketing)** Update the text on the T-5 (from the Printing Press project) and "earliest" ASN documents in the SF portal + - [ ] [:link:][step_asn_links] **(Marketing)** (BIND 9 only) Update the BIND -S information document in SF with download links to the new versions + - [ ] [:link:][step_asn_send] **(Marketing)** Bulk email eligible customers to check the SF portal + - [ ] [:link:][step_preannouncement] **(Marketing)** (BIND 9 only) Send a pre-announcement email to the *bind-announce* mailing list to alert users that the upcoming release will include security fixes ### At T-1 - - [ ] [:link:][step_check_customers] **(Support)** Verify that any new or reinstated customers have received the notification email - [ ] [:link:][step_packager_emails] **(First IM)** Send notifications to OS packagers ### On the Day of Public Disclosure - - [ ] [:link:][step_clearance] **(IM)** Grant Support clearance to proceed with public release - - [ ] [:link:][step_publish] **(Support)** Publish the releases (as outlined in the release checklist) + - [ ] [:link:][step_clearance] **(IM)** Grant QA & Marketing clearance to proceed with public release + - [ ] [:link:][step_publish] **(QA/Marketing)** Publish the releases (as outlined in the release checklist) - [ ] [:link:][step_matrix] **(Support)** (BIND 9 only) Add the new CVEs to the vulnerability matrix in the Knowledge Base - [ ] [:link:][step_publish_advisory] **(Support)** Bump Document Version for the Security Advisory and publish it in the Knowledge Base - [ ] [:link:][step_notifications] **(First IM)** Send notification emails to third parties - [ ] [:link:][step_mitre] **(First IM)** Advise MITRE about the disclosed CVEs - [ ] [:link:][step_merge_advisory] **(First IM)** Merge the Security Advisory merge request - [ ] [:link:][step_embargo_end] **(IM)** Inform original reporter (if external) that the security disclosure process is complete - - [ ] [:link:][step_customers] **(Support)** Inform customers a fix has been released + - [ ] [:link:][step_asn_clear] **(Marketing)** Update the SF portal to clear the ASN + - [ ] [:link:][step_customers] **(Marketing)** Email ASN recipients that the embargo is lifted ### After Public Disclosure - - [ ] [:link:][step_postmortem] **(First IM)** Organize post-mortem meeting and make sure it happens - - [ ] [:link:][step_tickets] **(Support)** Close support tickets - [ ] [:link:][step_regression] **(QA)** Merge a regression test reproducing the bug into all affected (and still maintained) branches [step_deputy]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#pick-a-deputy-incident-manager [step_respond]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#respond-to-the-bug-reporter -[step_etherpad]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#create-an-etherpad-for-post-mortem [step_public_mrs]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#ensure-there-are-no-public-merge-requests-which-inadvertently-disclose-the-issue [step_assign_cve_id]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#assign-a-cve-identifier [step_note_cve_info]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#update-this-issue-with-the-assigned-cve-identifier-and-the-cvss-score [step_versions_affected]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#determine-the-range-of-product-versions-affected-including-the-subscription-edition [step_workarounds]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#determine-whether-workarounds-for-the-problem-exist [step_coordinate]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#if-necessary-coordinate-with-other-parties -[step_earliest]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#prepare-and-send-out-earliest-notifications +[step_earliest_prepare]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#prepare-earliest-notification-text-and-hand-it-off-to-marketing +[step_earliest_send]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#update-earliest-notification-document-in-sf-portal-and-send-bulk-email-to-earliest-customers [step_advisory_mr]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#create-a-merge-request-for-the-security-advisory-and-include-all-readily-available-information-in-it [step_reproducer_mr]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#prepare-a-private-merge-request-containing-a-system-test-reproducing-the-problem [step_notify_support]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#notify-support-when-a-reproducer-is-ready @@ -109,12 +103,12 @@ confidential! [step_merge_fixes]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#merge-the-cve-fixes-in-cve-identifier-order [step_patches]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#prepare-a-standalone-patch-for-the-last-stable-release-of-each-affected-and-still-maintained-product-branch [step_asn_releases]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#prepare-asn-releases-as-outlined-in-the-release-checklist -[step_send_asn]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#send-asn-to-eligible-customers +[step_asn_documents]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#update-the-text-on-the-t-5-from-the-printing-press-project-and-earliest-asn-documents-in-the-sf-portal +[step_asn_links]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#bind-9-only-update-the-bind-s-information-document-in-sf-with-download-links-to-the-new-versions +[step_asn_send]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#bulk-email-eligible-customers-to-check-the-sf-portal [step_preannouncement]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#bind-9-only-send-a-pre-announcement-email-to-the-bind-announce-mailing-list-to-alert-users-that-the-upcoming-release-will-include-security-fixes -[step_verify_asn]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#verify-that-all-asn-eligible-customers-have-received-the-notification-email -[step_check_customers]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#verify-that-any-new-or-reinstated-customers-have-received-the-notification-email [step_packager_emails]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#send-notifications-to-os-packagers -[step_clearance]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#grant-support-clearance-to-proceed-with-public-release +[step_clearance]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#grant-qa-marketing-clearance-to-proceed-with-public-release [step_publish]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#publish-the-releases-as-outlined-in-the-release-checklist [step_matrix]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#bind-9-only-add-the-new-cves-to-the-vulnerability-matrix-in-the-knowledge-base [step_publish_advisory]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#bump-document-version-for-the-security-advisory-and-publish-it-in-the-knowledge-base @@ -122,7 +116,8 @@ confidential! [step_mitre]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#advise-mitre-about-the-disclosed-cves [step_merge_advisory]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#merge-the-security-advisory-merge-request [step_embargo_end]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#inform-original-reporter-if-external-that-the-security-disclosure-process-is-complete -[step_customers]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#inform-customers-a-fix-has-been-released -[step_postmortem]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#organize-post-mortem-meeting-and-make-sure-it-happens -[step_tickets]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#close-support-tickets +[step_asn_clear]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#update-the-sf-portal-to-clear-the-asn +[step_customers]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#email-asn-recipients-that-the-embargo-is-lifted [step_regression]: https://gitlab.isc.org/isc-private/isc-wiki/-/wikis/Security-Incident-Handling-Checklist-Explanations#merge-a-regression-test-reproducing-the-bug-into-all-affected-and-still-maintained-branches + +/confidential diff --git a/.gitlab/issue_templates/Release.md b/.gitlab/issue_templates/Release.md deleted file mode 100644 index 82d635e70d..0000000000 --- a/.gitlab/issue_templates/Release.md +++ /dev/null @@ -1,100 +0,0 @@ -## Release Schedule - -**Code Freeze:** - -**Tagging Deadline:** - -**Public Release:** - -## Documentation Review Links - -**Closed issues assigned to the milestone without a release note:** - - - []() - - []() - - []() - -**Merge requests merged into the milestone without a release note:** - - - []() - - []() - - []() - -**Merge requests merged into the milestone without a `CHANGES` entry:** - - - []() - - []() - - []() - -## Release Checklist - -### Before the Code Freeze - - - [ ] ***(QA)*** Rebase -S editions on top of current open-source versions: `git checkout bind-9.18-sub && git rebase origin/bind-9.18` - - [ ] ***(QA)*** [Inform](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/inform_supp_marketing.py) Support and Marketing of impending release (and give estimated release dates). - - [ ] ***(QA)*** Ensure there are no permanent test failures on any platform. Check [public](https://gitlab.isc.org/isc-projects/bind9/-/pipelines?scope=all&source=schedule) and [private](https://gitlab.isc.org/isc-private/bind9/-/pipelines?scope=all&source=schedule) scheduled pipelines. - - [ ] ***(QA)*** Check [Perflab](https://perflab.isc.org/) to ensure there has been no unexplained drop in performance for the versions being released. - - [ ] ***(QA)*** Check whether all issues assigned to the release milestone are resolved[^1]. - - [ ] ***(QA)*** Ensure that there are no outstanding [merge requests in the private repository](https://gitlab.isc.org/isc-private/bind9/-/merge_requests/)[^1] (Subscription Edition only). - - [ ] ***(QA)*** [Ensure](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/check_backports.py) all merge requests marked for backporting have been indeed backported. - - [ ] ***(QA)*** [Announce](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/inform_code_freeze.py) (on Mattermost) that the code freeze is in effect. - -### Before the Tagging Deadline - - - [ ] ***(QA)*** Inspect the current output of the `cross-version-config-tests` job to verify that no unexpected backward-incompatible change was introduced in the current release cycle. - - [ ] ***(QA)*** Ensure release notes are correct, ask Support and Marketing to check them as well. [Example](https://gitlab.isc.org/isc-private/bind9/-/merge_requests/510) - - [ ] ***(QA)*** Add a release marker to `CHANGES`. Examples: [9.18](https://gitlab.isc.org/isc-projects/bind9/-/commit/f14d8ad78c0506fd4247187f2177f8eceeb6b3b9), [9.16](https://gitlab.isc.org/isc-projects/bind9/-/commit/1bcdf21874f99a00da389d723e0ad07dfd70f9f1) - - [ ] ***(QA)*** Add a release marker to `CHANGES.SE` (Subscription Edition only). [Example](https://gitlab.isc.org/isc-private/bind9/-/commit/0f03d5737bcbdaa1bf713c6db1887b14938c3421) - - [ ] ***(QA)*** Update BIND 9 version in `configure.ac` ([9.18+](https://gitlab.isc.org/isc-projects/bind9/-/commit/3c85ab7f4c35e6d8acef1393606002a0a8730100)) or `version` ([9.16](https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/7692/diffs?commit_id=1bcdf21874f99a00da389d723e0ad07dfd70f9f1)). - - [ ] ***(QA)*** Rebuild `configure` using Autoconf on `docs.isc.org` (9.16). - - [ ] ***(QA)*** Update GitLab settings for all maintained branches to disallow merging to them: [public](https://gitlab.isc.org/isc-projects/bind9/-/settings/repository), [private](https://gitlab.isc.org/isc-private/bind9/-/settings/repository) - - [ ] ***(QA)*** Tag the releases in the private repository (`git tag -s -m "BIND 9.x.y" v9.x.y`). - -### Before the ASN Deadline (for ASN Releases) or the Public Release Date (for Regular Releases) - - - [ ] ***(QA)*** Check that the formatting is correct for HTML and PDF versions of release notes. - - [ ] ***(QA)*** Check that the formatting of the generated man pages is correct. - - [ ] ***(QA)*** Verify GitLab CI results [for the tags](https://gitlab.isc.org/isc-private/bind9/-/pipelines?scope=tags) created and sign off on the releases to be published. - - [ ] ***(QA)*** Update GitLab settings for all maintained branches to allow merging to them again: [public](https://gitlab.isc.org/isc-projects/bind9/-/settings/repository), [private](https://gitlab.isc.org/isc-private/bind9/-/settings/repository) - - [ ] ***(QA)*** Prepare (using [`version_bump.py`](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/version_bump.py)) and merge MRs resetting the release notes and updating the version string for each maintained branch. - - [ ] ***(QA)*** Announce (on Mattermost) that the code freeze is over. - - [ ] ***(QA)*** Request signatures for the tarballs, providing their location and checksums. Ask [signers on Mattermost](https://mattermost.isc.org/isc/channels/bind-9-qa). - - [ ] ***(Signers)*** Ensure that the contents of tarballs and tags are identical. - - [ ] ***(Signers)*** Validate tarball checksums, sign tarballs, and upload signatures. - - [ ] ***(QA)*** Verify tarball signatures and check tarball checksums again: Run `publish_bind.sh` on repo.isc.org to pre-publish. - - [ ] ***(Support)*** Pre-publish ASN and/or Subscription Edition tarballs so that packages can be built. - - [ ] ***(QA)*** Build and test ASN and/or Subscription Edition packages (in [cloudsmith branch in private repo](https://gitlab.isc.org/isc-private/rpms/bind/-/tree/cloudsmith)). [Example](https://gitlab.isc.org/isc-private/rpms/bind/-/commit/e2512f4cfaf991827a635e374e7e93b27a5f38ba) - - [ ] ***(QA)*** Prepare the `patches/` subdirectory for each security release (if applicable). - - [ ] ***(QA)*** Notify Support that the releases have been prepared. - - [ ] ***(Support)*** Send out ASNs (if applicable). - -### On the Day of Public Release - - - [ ] ***(Support)*** Wait for clearance from Security Officer to proceed with the public release (if applicable). - - [ ] ***(Support)*** Place tarballs in public location on FTP site. - - [ ] ***(Support)*** Publish links to downloads on ISC website. [Example](https://gitlab.isc.org/website/theme-staging-site/-/commit/1ac7b30b73cb03228df4cd5651fa4e774ac35625) - - [ ] ***(Support)*** Add the new releases to the [vulnerability matrix in the Knowledge Base](https://kb.isc.org/docs/aa-00913). - - [ ] ***(Support)*** Use the [Printing Press project](https://gitlab.isc.org/isc-private/printing-press/-/wikis/home#adding-new-documents) to prepare a release announcement email and send it to the *bind-announce* mailing list. - - [ ] ***(Support)*** Write email to *bind-users* (if a major release). [Example](https://lists.isc.org/pipermail/bind-users/2022-January/105624.html) - - [ ] ***(Support)*** Send eligible customers updated links to the Subscription Edition (update the -S edition delivery tickets, even if those links were provided earlier via an ASN ticket). - - [ ] ***(Support)*** Update tickets in case of waiting support customers. - - [ ] ***(QA)*** Build and test any outstanding private packages in [private repo](https://gitlab.isc.org/isc-private/rpms/bind/-/tree/cloudsmith). [Example](https://gitlab.isc.org/isc-private/rpms/bind/-/commit/2007d566db81dd9dfd79e571e2f600a3bc284da4) - - [ ] ***(QA)*** Build [public RPMs](https://gitlab.isc.org/isc-packages/rpms/bind). [Example commit](https://gitlab.isc.org/isc-packages/rpms/bind/-/commit/3b5e851ea7c4e3570371a4878b5461f02a44f8cc) which triggers [Copr builds](https://copr.fedorainfracloud.org/coprs/isc/) automatically - - [ ] ***(SwEng)*** Build Debian/Ubuntu packages. - - [ ] ***(SwEng)*** Update Docker files [here](https://gitlab.isc.org/isc-projects/bind9-docker/-/branches) and make sure push is synchronized to [GitHub](https://github.com/isc-projects/bind9-docker). [Docker Hub](https://hub.docker.com/r/internetsystemsconsortium/bind9) should pick it up automatically. [Example](https://gitlab.isc.org/isc-projects/bind9-docker/-/commit/cada7e10e9af951595c98bfffc4bd42512faac05) - - [ ] ***(QA)*** Inform Marketing of the release. - - [ ] ***(Marketing)*** Post a short note to Mastodon. - - [ ] ***(Marketing)*** Update [Wikipedia entry for BIND](https://en.wikipedia.org/wiki/BIND). - - [ ] ***(Marketing)*** Write blog article (if a major release). - - [ ] ***(QA)*** Ensure all new tags are annotated and signed. `git show --show-signature v9.19.12` - - [ ] ***(QA)*** Push tags for the published releases to the public repository. - - [ ] ***(QA)*** Using [`merge_tag.py`](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/merge_tag.py), merge published release tags back into the their relevant development/maintenance branches. - - [ ] ***(QA)*** Ensure `allow_failure: true` is removed from the `cross-version-config-tests` job if it was set during the current release cycle. - - [ ] ***(QA)*** Sanitize confidential issues which are assigned to the current release milestone and do not describe a security vulnerability, then make them public. - - [ ] ***(QA)*** Sanitize [confidential issues](https://gitlab.isc.org/isc-projects/bind9/-/issues/?sort=milestone_due_desc&state=opened&confidential=yes) which are assigned to older release milestones and describe security vulnerabilities, then make them public if appropriate[^2]. - - [ ] ***(QA)*** Update QA tools used in GitLab CI (e.g. Black, PyLint, Sphinx) by modifying the relevant [`Dockerfile`](https://gitlab.isc.org/isc-projects/images/-/merge_requests/228/diffs). - - [ ] ***(QA)*** Run a pipeline to rebuild all [images](https://gitlab.isc.org/isc-projects/images) used in GitLab CI. - - [ ] ***(QA)*** Update [`metadata.json`](https://gitlab.isc.org/isc-private/bind-qa/-/blob/master/bind9/releng/metadata.json) with the upcoming release information. - -[^1]: If not, use the time remaining until the tagging deadline to ensure all outstanding issues are either resolved or moved to a different milestone. -[^2]: As a rule of thumb, security vulnerabilities which have reproducers merged to the public repository are considered okay for full disclosure. diff --git a/.gitlab/issue_templates/Security_issue.md b/.gitlab/issue_templates/Security_issue.md new file mode 100644 index 0000000000..2789c45e24 --- /dev/null +++ b/.gitlab/issue_templates/Security_issue.md @@ -0,0 +1,139 @@ +### Summary + + +### BIND versions affected + + +### Preconditions and assumptions + + +### Attacker's abilities + + + +### Impact + + + +### Steps to reproduce + + +1. +2. +3. + +### What is the current *bug* behavior? + + + +### What is the expected *correct* behavior? + + + +### Relevant logs + + +### Coordination +- Does this issue affect multiple implementations? + + +- Have you shared the information with anyone else? + + +- What is your plan to publicize this issue? + + +### Acknowledgements + + + + +/label ~Bug ~Security +/confidential diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..3204f5b481 --- /dev/null +++ b/.mailmap @@ -0,0 +1,26 @@ +Alan Clegg +Aram Sargsyan +Artem Boldariev +Curtis Blackburn +Curtis Blackburn +Diego Fronza +Evan Hunt Evan Hunt +Håvard Eidnes +Jeremy C. Reed +Jeremy C. Reed +Joey Salazar +John H. DuBois III +Mark Andrews +Mark Andrews +Mark Andrews +Matthijs Mekking +Nicki Křížek +Ondřej Surý +Ondřej Surý +Ondřej Surý +Petr Menšík +Petr Menšík +Robert Edmonds +Tatuya JINMEI 神明達哉 +Witold Kręcicki +Witold Kręcicki diff --git a/.pylintrc b/.pylintrc index 07d503514d..b5ea55a5e5 100644 --- a/.pylintrc +++ b/.pylintrc @@ -5,6 +5,7 @@ disable= C0115, # missing-class-docstring C0116, # missing-function-docstring C0209, # consider-using-f-string + C0301, # line-too-long, handled better by black C0415, # import-outside-toplevel R0801, # duplicate-code R0903, # too-few-public-methods diff --git a/.reuse/dep5 b/.reuse/dep5 index ded1db308a..a58145d214 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -24,6 +24,7 @@ Files: **/*.after* **/testdata/* .github/* .gitlab/* + .mailmap AUTHORS CHANGES COPYRIGHT @@ -38,15 +39,31 @@ Files: **/*.after* bin/tests/system/doth/CA/index.txt bin/tests/system/doth/CA/index.txt.attr bin/tests/system/doth/CA/serial - bin/tests/system/notify/ns4/named.port.in + bin/tests/system/formerr/badnsec3owner + bin/tests/system/formerr/badrecordname + bin/tests/system/formerr/dupans + bin/tests/system/formerr/dupquestion + bin/tests/system/formerr/keyclass + bin/tests/system/formerr/malformeddeltype + bin/tests/system/formerr/malformedrrsig bin/tests/system/formerr/nametoolong bin/tests/system/formerr/noquestions - bin/tests/system/formerr/twoquestions + bin/tests/system/formerr/optwrongname + bin/tests/system/formerr/qtypeasanswer + bin/tests/system/formerr/questionclass + bin/tests/system/formerr/shortquestion + bin/tests/system/formerr/shortrecord + bin/tests/system/formerr/tsignotlast + bin/tests/system/formerr/tsigwrongclass + bin/tests/system/formerr/twoquestionnames + bin/tests/system/formerr/twoquestiontypes + bin/tests/system/formerr/wrongclass bin/tests/system/forward/CA/CA.cfg bin/tests/system/forward/CA/README bin/tests/system/forward/CA/index.txt bin/tests/system/forward/CA/index.txt.attr bin/tests/system/forward/CA/serial + bin/tests/system/isctest/vars/.ac_vars/* bin/tests/system/journal/ns1/managed-keys.bind.in bin/tests/system/journal/ns1/managed-keys.bind.jnl.in bin/tests/system/journal/ns2/managed-keys.bind.in @@ -54,6 +71,10 @@ Files: **/*.after* bin/tests/system/keepalive/expected bin/tests/system/legacy/ns6/edns512.db.signed bin/tests/system/legacy/ns7/edns512-notcp.db.signed + bin/tests/system/masterfile/knowngood.include + bin/tests/system/masterfile/knowngood.ttl1 + bin/tests/system/masterfile/knowngood.ttl2 + bin/tests/system/notify/ns4/named.port.in bin/tests/system/nsupdate/CA/CA.cfg bin/tests/system/nsupdate/CA/README bin/tests/system/nsupdate/CA/index.txt @@ -81,7 +102,10 @@ Files: **/*.after* bin/tests/system/unknown/large.out bin/tests/system/xfer/ans5/badkeydata bin/tests/system/xfer/ans5/badmessageid + bin/tests/system/xfer/ans5/ednsformerr + bin/tests/system/xfer/ans5/ednsnotimp bin/tests/system/xfer/ans5/goodaxfr + bin/tests/system/xfer/ans5/ixfrnotimp bin/tests/system/xfer/ans5/partial bin/tests/system/xfer/ans5/soamismatch bin/tests/system/xfer/ans5/unknownkey @@ -157,6 +181,7 @@ Files: **/.clang-format .clang-format .clang-format.headers .dir-locals.el + .editorconfig .git-blame-ignore-revs .gitattributes .gitignore @@ -170,6 +195,7 @@ Files: **/.clang-format doc/misc/options doc/misc/rndc.grammar sonar-project.properties + tests/bench/names.csv Copyright: Internet Systems Consortium, Inc. ("ISC") License: CC0-1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c108dbeec6..a013c4ea73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,7 +173,7 @@ of documentation in the BIND source tree: they document, in files ending in `.rst`: for example, the `named` man page is `bin/named/named.rst`. * The *BIND 9 Administrator Reference Manual* is in the .rst files in - `doc/arm/`; the PDF and HTML versions are automatically generated from + `doc/arm/`; the HTML version is automatically generated from the `.rst` files. * API documentation is in the header file describing the API, in Doxygen-formatted comments. diff --git a/LICENSES/LicenseRef-Automake-exception-2.0.txt b/LICENSES/Autoconf-exception-generic.txt similarity index 100% rename from LICENSES/LicenseRef-Automake-exception-2.0.txt rename to LICENSES/Autoconf-exception-generic.txt diff --git a/Makefile.docs b/Makefile.docs index 674f51d2fc..a205c5efc7 100644 --- a/Makefile.docs +++ b/Makefile.docs @@ -25,11 +25,8 @@ common_SPHINXOPTS = \ -a \ $(SPHINX_V) -# The "today" variable set below is not directly used in the ARM, but its value -# is implicitly inserted on the title page of the PDF file produced by Sphinx. ALLSPHINXOPTS = \ $(common_SPHINXOPTS) \ - -D today="$(RELEASE_DATE)" \ -D rst_epilog="$$(printf "$${RST_EPILOG}")" \ $(SPHINXOPTS) \ $(srcdir) diff --git a/Makefile.tests b/Makefile.tests index e1b7e0e046..a0ea914d40 100644 --- a/Makefile.tests +++ b/Makefile.tests @@ -21,3 +21,8 @@ AM_CPPFLAGS += \ LDADD += \ $(top_builddir)/tests/libtest/libtest.la \ $(CMOCKA_LIBS) + +if HAVE_JEMALLOC +AM_CFLAGS += $(JEMALLOC_CFLAGS) +LDADD += $(JEMALLOC_LIBS) +endif diff --git a/Makefile.top b/Makefile.top index d01317dadb..5a2fae22e4 100644 --- a/Makefile.top +++ b/Makefile.top @@ -23,12 +23,20 @@ AM_LDFLAGS += \ -Wl,-flat_namespace endif HOST_MACOS -LIBISC_CFLAGS = \ +if HAVE_JEMALLOC +LIBISC_CFLAGS = $(JEMALLOC_CFLAGS) +LIBISC_LIBS = $(JEMALLOC_LIBS) +else +LIBISC_CFLAGS = +LIBISC_LIBS = +endif + +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 +LIBISC_LIBS += $(top_builddir)/lib/isc/libisc.la if HAVE_DTRACE LIBISC_DTRACE = $(top_builddir)/lib/isc/probes.lo endif @@ -63,3 +71,9 @@ LIBISCCC_CFLAGS = \ LIBISCCC_LIBS = \ $(top_builddir)/lib/isccc/libisccc.la + +LIBSAQ_CFLAGS = \ + -I$(top_srcdir)/lib/saq/include + +LIBSAQ_LIBS = \ + $(top_builddir)/lib/saq/libsaq.la diff --git a/bin/check/check-tool.c b/bin/check/check-tool.c index 93cde40550..352d83acbf 100644 --- a/bin/check/check-tool.c +++ b/bin/check/check-tool.c @@ -73,7 +73,7 @@ #define ERR_IS_MXCNAME 6 #define ERR_IS_SRVCNAME 7 -static const char *dbtype[] = { "rbt" }; +static const char *dbtype[] = { ZONEDB_DEFAULT }; int debug = 0; const char *journal = NULL; @@ -88,6 +88,7 @@ bool dochecksrv = false; bool docheckns = false; #endif /* if CHECK_LOCAL */ dns_zoneopt_t zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF | DNS_ZONEOPT_MANYERRORS | DNS_ZONEOPT_CHECKNAMES | DNS_ZONEOPT_CHECKINTEGRITY | #if CHECK_SIBLING diff --git a/bin/check/named-checkconf.c b/bin/check/named-checkconf.c index c4c4155b1b..7c442b917d 100644 --- a/bin/check/named-checkconf.c +++ b/bin/check/named-checkconf.c @@ -63,7 +63,7 @@ usage(void) { "usage: %s [-achijlvz] [-p [-x]] [-t directory] " "[named.conf]\n", program); - exit(1); + exit(EXIT_SUCCESS); } /*% directory callback */ @@ -242,7 +242,9 @@ configure_zone(const char *vclass, const char *view, const cfg_obj_t *zconfig, * Skip checks when using an alternate data source. */ cfg_map_get(zoptions, "database", &dbobj); - if (dbobj != NULL && strcmp("rbt", cfg_obj_asstring(dbobj)) != 0) { + if (dbobj != NULL && + strcmp(ZONEDB_DEFAULT, cfg_obj_asstring(dbobj)) != 0) + { return (ISC_R_SUCCESS); } diff --git a/bin/check/named-checkzone.c b/bin/check/named-checkzone.c index 795624651a..db2e345561 100644 --- a/bin/check/named-checkzone.c +++ b/bin/check/named-checkzone.c @@ -79,7 +79,7 @@ usage(void) { "%s zonename [ (filename|-) ]\n", prog_name, progmode == progmode_check ? "[-o filename]" : "-o filename"); - exit(1); + exit(EXIT_FAILURE); } static void @@ -147,15 +147,12 @@ main(int argc, char **argv) { UNREACHABLE(); } - /* Compilation specific defaults */ + /* When compiling, disable checks by default */ if (progmode == progmode_compile) { - zone_options |= (DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_FATALNS | - DNS_ZONEOPT_CHECKSPF | DNS_ZONEOPT_CHECKDUPRR | - DNS_ZONEOPT_CHECKNAMES | - DNS_ZONEOPT_CHECKNAMESFAIL | - DNS_ZONEOPT_CHECKWILDCARD); - } else { - zone_options |= (DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF); + zone_options = 0; + docheckmx = false; + docheckns = false; + dochecksrv = false; } #define ARGCMP(X) (strcmp(isc_commandline_argument, X) == 0) @@ -209,7 +206,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -i: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -243,7 +240,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -k: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -254,7 +251,7 @@ main(int argc, char **argv) { if (*endp != '\0') { fprintf(stderr, "source serial number " "must be numeric"); - exit(1); + exit(EXIT_FAILURE); } break; @@ -265,7 +262,7 @@ main(int argc, char **argv) { if (*endp != '\0') { fprintf(stderr, "maximum TTL " "must be numeric"); - exit(1); + exit(EXIT_FAILURE); } break; @@ -282,7 +279,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -n: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -299,7 +296,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -m: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -324,7 +321,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -r: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -337,7 +334,7 @@ main(int argc, char **argv) { fprintf(stderr, "unknown or unsupported style: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -347,13 +344,13 @@ main(int argc, char **argv) { fprintf(stderr, "isc_dir_chroot: %s: %s\n", isc_commandline_argument, isc_result_totext(result)); - exit(1); + exit(EXIT_FAILURE); } break; case 'v': printf("%s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); case 'w': workdir = isc_commandline_argument; @@ -367,7 +364,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -C: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -388,7 +385,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -M: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -405,7 +402,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -S: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -417,7 +414,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "invalid argument to -T: %s\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -441,7 +438,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", prog_name, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -450,7 +447,7 @@ main(int argc, char **argv) { if (result != ISC_R_SUCCESS) { fprintf(stderr, "isc_dir_chdir: %s: %s\n", workdir, isc_result_totext(result)); - exit(1); + exit(EXIT_FAILURE); } } @@ -466,7 +463,7 @@ main(int argc, char **argv) { } else { fprintf(stderr, "unknown file format: %s\n", inputformatstr); - exit(1); + exit(EXIT_FAILURE); } } @@ -484,12 +481,12 @@ main(int argc, char **argv) { rawversion > 1U) { fprintf(stderr, "unknown raw format version\n"); - exit(1); + exit(EXIT_FAILURE); } } else { fprintf(stderr, "unknown file format: %s\n", outputformatstr); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/bin/check/named-compilezone.rst b/bin/check/named-compilezone.rst index 0b4c98d449..8c68d0d6d2 100644 --- a/bin/check/named-compilezone.rst +++ b/bin/check/named-compilezone.rst @@ -30,10 +30,16 @@ Description :program:`named-compilezone` checks the syntax and integrity of a zone file, and dumps the zone contents to a specified file in a specified format. -It applies strict check levels by default, since the -dump output is used as an actual zone file loaded by :iscman:`named`. -When manually specified otherwise, the check levels must at least be as -strict as those specified in the :iscman:`named` configuration file. + +Unlike :program:`named-checkzone`, zone contents are not strictly checked +by default. If the output is to be used as an actual zone file to be loaded +by :iscman:`named`, then the check levels should be manually configured to +be at least as strict as those specified in the :iscman:`named` configuration +file. + +Running :program:`named-checkzone` on the input prior to compiling will +ensure that the zone compiles with the default requirements of +:iscman:`named`. Options ~~~~~~~ @@ -77,13 +83,13 @@ Options ``check-svcb:fail`` turns on additional checks on ``_dns`` SVCB records and ``check-svcb:ignore`` disables these checks. The - default is ``check-svcb:fail``. + default is ``check-svcb:ignore``. .. option:: -i mode This option performs post-load zone integrity checks. Possible modes are - ``full`` (the default), ``full-sibling``, ``local``, - ``local-sibling``, and ``none``. + ``full``, ``full-sibling``, ``local``, + ``local-sibling``, and ``none`` (the default). Mode ``full`` checks that MX records refer to A or AAAA records (both in-zone and out-of-zone hostnames). Mode ``local`` only @@ -127,7 +133,7 @@ Options .. option:: -k mode This option performs ``check-names`` checks with the specified failure mode. - Possible modes are ``fail`` (the default), ``warn``, and ``ignore``. + Possible modes are ``fail``, ``warn``, and ``ignore`` (the default). .. option:: -l ttl @@ -144,19 +150,19 @@ Options .. option:: -m mode This option specifies whether MX records should be checked to see if they are - addresses. Possible modes are ``fail``, ``warn`` (the default), and - ``ignore``. + addresses. Possible modes are ``fail``, ``warn``, and + ``ignore`` (the default). .. option:: -M mode This option checks whether a MX record refers to a CNAME. Possible modes are - ``fail``, ``warn`` (the default), and ``ignore``. + ``fail``, ``warn``, and ``ignore`` (the default). .. option:: -n mode This option specifies whether NS records should be checked to see if they are - addresses. Possible modes are ``fail`` (the default), ``warn``, and - ``ignore``. + addresses. Possible modes are ``fail``, ``warn``, and + ``ignore`` (the default). .. option:: -o filename @@ -167,7 +173,7 @@ Options This option checks for records that are treated as different by DNSSEC but are semantically equal in plain DNS. Possible modes are ``fail``, - ``warn`` (the default), and ``ignore``. + ``warn``, and ``ignore`` (the default). .. option:: -s style @@ -180,7 +186,7 @@ Options .. option:: -S mode This option checks whether an SRV record refers to a CNAME. Possible modes are - ``fail``, ``warn`` (the default), and ``ignore``. + ``fail``, ``warn``, and ``ignore`` (the default). .. option:: -t directory @@ -192,7 +198,7 @@ Options This option checks whether Sender Policy Framework (SPF) records exist and issues a warning if an SPF-formatted TXT record is not also present. Possible - modes are ``warn`` (the default) and ``ignore``. + modes are ``warn`` and ``ignore`` (the default). .. option:: -w directory @@ -210,7 +216,7 @@ Options This option specifies whether to check for non-terminal wildcards. Non-terminal wildcards are almost always the result of a failure to understand the wildcard matching algorithm (:rfc:`4592`). Possible modes are ``warn`` - (the default) and ``ignore``. + and ``ignore`` (the default). .. option:: zonename diff --git a/bin/confgen/keygen.c b/bin/confgen/keygen.c index 4698d1a078..348c05d874 100644 --- a/bin/confgen/keygen.c +++ b/bin/confgen/keygen.c @@ -124,7 +124,7 @@ generate_key(isc_mem_t *mctx, dns_secalg_t alg, int keysize, DO("generate key", dst_key_generate(dns_rootname, alg, keysize, 0, 0, DNS_KEYPROTO_ANY, - dns_rdataclass_in, mctx, &key, NULL)); + dns_rdataclass_in, NULL, mctx, &key, NULL)); isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); diff --git a/bin/confgen/rndc-confgen.c b/bin/confgen/rndc-confgen.c index 4c2c3cafed..c840e2c284 100644 --- a/bin/confgen/rndc-confgen.c +++ b/bin/confgen/rndc-confgen.c @@ -146,7 +146,8 @@ main(int argc, char **argv) { keyfile = isc_commandline_argument; break; case 'h': - usage(0); + usage(EXIT_SUCCESS); + break; case 'k': case 'y': /* Compatible with rndc -y. */ keyname = isc_commandline_argument; @@ -192,15 +193,15 @@ main(int argc, char **argv) { if (isc_commandline_option != '?') { fprintf(stderr, "%s: invalid argument -%c\n", program, isc_commandline_option); - usage(1); + usage(EXIT_FAILURE); } else { - usage(0); + usage(EXIT_SUCCESS); } break; default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -209,7 +210,7 @@ main(int argc, char **argv) { POST(argv); if (argc > 0) { - usage(1); + usage(EXIT_FAILURE); } if (alg == DST_ALG_HMACMD5) { diff --git a/bin/confgen/tsig-keygen.c b/bin/confgen/tsig-keygen.c index 620717c626..c421702c8b 100644 --- a/bin/confgen/tsig-keygen.c +++ b/bin/confgen/tsig-keygen.c @@ -138,13 +138,13 @@ main(int argc, char **argv) { keysize = alg_bits(alg); break; case 'h': - usage(0); + usage(EXIT_SUCCESS); case 'k': case 'y': if (progmode == progmode_confgen) { keyname = isc_commandline_argument; } else { - usage(1); + usage(EXIT_FAILURE); } break; case 'M': @@ -157,7 +157,7 @@ main(int argc, char **argv) { if (progmode == progmode_confgen) { quiet = true; } else { - usage(1); + usage(EXIT_FAILURE); } break; case 'r': @@ -167,29 +167,29 @@ main(int argc, char **argv) { if (progmode == progmode_confgen) { self_domain = isc_commandline_argument; } else { - usage(1); + usage(EXIT_FAILURE); } break; case 'z': if (progmode == progmode_confgen) { zone = isc_commandline_argument; } else { - usage(1); + usage(EXIT_FAILURE); } break; case '?': if (isc_commandline_option != '?') { fprintf(stderr, "%s: invalid argument -%c\n", program, isc_commandline_option); - usage(1); + usage(EXIT_FAILURE); } else { - usage(0); + usage(EXIT_SUCCESS); } break; default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -200,11 +200,11 @@ main(int argc, char **argv) { POST(argv); if (self_domain != NULL && zone != NULL) { - usage(1); /* -s and -z cannot coexist */ + usage(EXIT_FAILURE); /* -s and -z cannot coexist */ } if (argc > isc_commandline_index) { - usage(1); + usage(EXIT_FAILURE); } /* Use canonical algorithm name */ diff --git a/bin/confgen/tsig-keygen.rst b/bin/confgen/tsig-keygen.rst index 7420bede43..5ecce5d0e1 100644 --- a/bin/confgen/tsig-keygen.rst +++ b/bin/confgen/tsig-keygen.rst @@ -27,9 +27,10 @@ Synopsis Description ~~~~~~~~~~~ -:program:`tsig-keygen` is an utility that generates keys for use in TSIG signing. -The resulting keys can be used, for example, to secure dynamic DNS updates -to a zone, or for the :iscman:`rndc` command channel. +:program:`tsig-keygen` is an utility that generates keys for use with TSIG +(Transaction Signatures) as defined in :rfc:`2845`. The resulting keys can be used, +for example, to secure dynamic DNS updates to a zone, or for the :iscman:`rndc` +command channel. A domain name can be specified on the command line to be used as the name of the generated key. If no name is specified, the default is ``tsig-key``. diff --git a/bin/confgen/util.c b/bin/confgen/util.c index 23b7f3fd57..5d7ce509e4 100644 --- a/bin/confgen/util.c +++ b/bin/confgen/util.c @@ -13,14 +13,16 @@ /*! \file */ -#include "util.h" #include #include #include #include +#include #include +#include "util.h" + extern bool verbose; extern const char *progname; @@ -45,6 +47,5 @@ fatal(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } diff --git a/bin/delv/delv.c b/bin/delv/delv.c index 0ee545a962..a8e1c65bae 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -247,7 +247,7 @@ usage(void) { "process)\n" " +[no]yaml (Present the results as " "YAML)\n"); - exit(1); + exit(EXIT_FAILURE); } noreturn static void @@ -263,8 +263,7 @@ fatal(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } static void @@ -1165,7 +1164,7 @@ plus_option(char *option) { if (state) { fprintf(stderr, "Invalid option: " "+dlv is obsolete\n"); - exit(1); + exit(EXIT_FAILURE); } break; case 'n': /* dnssec */ @@ -1380,10 +1379,6 @@ plus_option(char *option) { fprintf(stderr, "Invalid option: +%s\n", option); usage(); } - - if (qmin && !fulltrace) { - fatal("'+qmin' cannot be used without '+ns'"); - } return; } @@ -1436,7 +1431,7 @@ dash_option(char *option, char *next, bool *open_type_class) { break; case 'h': usage(); - exit(0); + exit(EXIT_SUCCESS); case 'i': no_sigs = true; root_validation = false; @@ -1446,7 +1441,7 @@ dash_option(char *option, char *next, bool *open_type_class) { break; case 'v': printf("delv %s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); default: UNREACHABLE(); } @@ -1583,7 +1578,7 @@ dash_option(char *option, char *next, bool *open_type_class) { typeset = true; } else { fprintf(stderr, "Invalid IP address %s\n", value); - exit(1); + exit(EXIT_FAILURE); } return (value_from_next); invalid_option: @@ -1732,6 +1727,11 @@ parse_args(int argc, char **argv) { } } + /* check consistency */ + if (qmin && !fulltrace) { + fatal("'+qmin' cannot be used without '+ns'"); + } + /* * If no qname or qtype specified, search for root/NS * If no qtype specified, use A @@ -1961,7 +1961,8 @@ recvresponse(void *arg) { fatal("request event result: %s", isc_result_totext(result)); } - dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &response); + dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, + &response); result = dns_request_getresponse(request, response, DNS_MESSAGEPARSE_PRESERVEORDER); @@ -2076,7 +2077,8 @@ sendquery(void *arg) { /* Construct query message */ CHECK(convert_name(&qfn, &query_name, qname)); - dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message); + dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, + &message); message->opcode = dns_opcode_query; message->flags = DNS_MESSAGEFLAG_RD | DNS_MESSAGEFLAG_AD; if (cdflag) { @@ -2103,9 +2105,8 @@ sendquery(void *arg) { dns_view_attach(view, &(dns_view_t *){ NULL }); CHECK(dns_request_create(requestmgr, message, NULL, &peer, NULL, NULL, - DNS_REQUESTOPT_TCP, NULL, 1, 0, 0, - isc_loop_current(loopmgr), recvresponse, - message, &request)); + DNS_REQUESTOPT_TCP, NULL, 1, 0, 0, isc_loop(), + recvresponse, message, &request)); return; cleanup: @@ -2144,7 +2145,7 @@ run_server(void *arg) { ns_server_create(mctx, matchview, &sctx); - CHECK(dns_dispatchmgr_create(mctx, netmgr, &dispatchmgr)); + CHECK(dns_dispatchmgr_create(mctx, loopmgr, netmgr, &dispatchmgr)); isc_sockaddr_any(&any); CHECK(dns_dispatch_createudp(dispatchmgr, &any, &dispatch)); CHECK(ns_interfacemgr_create(mctx, sctx, loopmgr, netmgr, dispatchmgr, @@ -2152,7 +2153,7 @@ run_server(void *arg) { CHECK(dns_view_create(mctx, dispatchmgr, dns_rdataclass_in, "_default", &view)); - CHECK(dns_cache_create(loopmgr, dns_rdataclass_in, "", &cache)); + CHECK(dns_cache_create(loopmgr, dns_rdataclass_in, "", mctx, &cache)); dns_view_setcache(view, cache, false); dns_cache_detach(&cache); dns_view_setdstport(view, destport); @@ -2167,8 +2168,8 @@ run_server(void *arg) { dns_view_initsecroots(view); CHECK(setup_dnsseckeys(NULL, view)); - CHECK(dns_view_createresolver(view, loopmgr, 1, netmgr, 0, - tlsctx_client_cache, dispatch, NULL)); + CHECK(dns_view_createresolver(view, netmgr, 0, tlsctx_client_cache, + dispatch, NULL)); isc_stats_create(mctx, &resstats, dns_resstatscounter_max); dns_resolver_setstats(view->resolver, resstats); @@ -2184,9 +2185,10 @@ run_server(void *arg) { CHECK(isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE, &addr, ns_client_request, ifp, accept_cb, ifp, 10, - NULL, NULL, &ifp->tcplistensocket)); + NULL, NULL, ISC_NM_PROXY_NONE, + &ifp->tcplistensocket)); ifp->flags |= NS_INTERFACEFLAG_LISTENING; - isc_async_current(loopmgr, sendquery, ifp->tcplistensocket); + isc_async_current(sendquery, ifp->tcplistensocket); return; diff --git a/bin/dig/dig.c b/bin/dig/dig.c index 54f7f81e33..c98fa88277 100644 --- a/bin/dig/dig.c +++ b/bin/dig/dig.c @@ -119,7 +119,7 @@ usage(void) { print_usage(stderr); fprintf(stderr, "\nUse \"dig -h\" (or \"dig -h | more\") " "for complete list of options\n"); - exit(1); + exit(EXIT_FAILURE); } #endif /* if TARGET_OS_IPHONE */ @@ -252,6 +252,14 @@ help(void) { "request)\n" " +padding=### (Set padding block size " "[0])\n" + " " + "+[no]proxy[=src_addr[#src_port]-dst_addr[#dst_port]] " + "(Add PROXYv2 headers to the queries. If addresses are omitted, " + "LOCAL PROXYv2 headers are added)\n" + " " + "+[no]proxy-plain[=src_addr[#src_port]-dst_addr[#dst_port]] " + "(The same as '+[no]proxy', but send PROXYv2 headers ahead of " + "any encryption if an encrypted transport is used)\n" " +qid=### (Specify the query ID to " "use when sending queries)\n" " +[no]qr (Print question before " @@ -298,8 +306,7 @@ help(void) { " +[no]tls-keyfile=file (Load client TLS " "private key from file)\n" " +[no]trace (Trace delegation down " - "from root " - "[+dnssec])\n" + "from root [implies +dnssec])\n" " +tries=### (Set number of UDP " "attempts) [3]\n" " +[no]ttlid (Control display of ttls " @@ -365,6 +372,39 @@ received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { } printf(";; SERVER: %s(%s) (%s)\n", fromtext, query->userarg, proto); + + if (query->lookup->proxy_mode) { + printf(";; CLIENT PROXY HEADER"); + + if ((dig_lookup_is_tls(query->lookup) || + (query->lookup->https_mode && + !query->lookup->http_plain)) && + query->lookup->proxy_plain) + { + printf(" (plain)"); + } + + printf(": "); + + if (!query->lookup->proxy_local) { + char src_buf[ISC_SOCKADDR_FORMATSIZE] = { 0 }; + char dst_buf[ISC_SOCKADDR_FORMATSIZE] = { 0 }; + + isc_sockaddr_format( + &query->lookup->proxy_src_addr, src_buf, + sizeof(src_buf)); + + isc_sockaddr_format( + &query->lookup->proxy_dst_addr, dst_buf, + sizeof(dst_buf)); + printf("source: %s, destination: %s", src_buf, + dst_buf); + } else { + printf("LOCAL"); + } + + printf("\n"); + } time(&tnow); (void)localtime_r(&tnow, &tmnow); @@ -1052,6 +1092,323 @@ printgreeting(int argc, char **argv, dig_lookup_t *lookup) { goto invalid_option; \ } while (0) +/* + * Parse source and destination addresses in the same format as used by "kdig": + * + * SRC_ADDR[#SRC_PORT]-DST_ADDR[#DST_PORT] + * + * This can be described (pretty closely for our purpose) using the + * following EBNF grammar: + * + * S = proxy-addrs. (* start rule *) + * proxy-addrs = addr "-" addr EOF. + * addr = addr-char { addr-char } ["#" port ]. + * port = digit { digit }. + * addr-char = . + * EOF = '\0'. + */ +#define MATCH(ch) (st->str[0] == (ch)) +#define MATCH_DIGIT() isdigit((unsigned char)(st->str[0])) +#define ADVANCE() st->str++ +#define GETP() (st->str) + +typedef struct isc_proxy_addrs_parser_state { + const char *str; + + const char *last_addr_start; + size_t last_addr_len; + + const char *last_port_start; + size_t last_port_len; + + const char *src_addr_start; + size_t src_addr_len; + + const char *src_port_start; + size_t src_port_len; + + const char *dst_addr_start; + size_t dst_addr_len; + + const char *dst_port_start; + size_t dst_port_len; +} isc_proxy_addrs_parser_state_t; + +static bool +rule_proxy_addrs(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_addr(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_port(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_addr_char(isc_proxy_addrs_parser_state_t *st); + +static void +proxy_handle_port_string(const char *port_start, const size_t port_len, + in_port_t *pport) { + char buf[512] = { 0 }; /* max */ + size_t string_size = 0, max_string_bytes = 0; + unsigned int tmp; + isc_result_t result; + + string_size = port_len + 1; + max_string_bytes = string_size > sizeof(buf) ? sizeof(buf) + : string_size; + + (void)strlcpy(buf, port_start, max_string_bytes); + result = parse_uint(&tmp, buf, MAXPORT, "port number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + *pport = tmp; +} + +static isc_result_t +proxy_handle_addr_string(const char *addr_start, const size_t addr_len, + const in_port_t addr_port, isc_sockaddr_t *addr) { + isc_result_t result = ISC_R_FAILURE; + char buf[512] = { 0 }; /* max */ + size_t string_size = 0, max_string_bytes = 0; + struct in_addr ipv4 = { 0 }; + struct in6_addr ipv6 = { 0 }; + int ret = 0; + + string_size = addr_len + 1; + max_string_bytes = string_size > sizeof(buf) ? sizeof(buf) + : string_size; + + (void)strlcpy(buf, addr_start, max_string_bytes); + + ret = inet_pton(AF_INET, buf, &ipv4); + if (ret == 1) { + isc_sockaddr_fromin(addr, &ipv4, addr_port); + result = ISC_R_SUCCESS; + } else { + ret = inet_pton(AF_INET6, buf, &ipv6); + if (ret == 1) { + isc_sockaddr_fromin6(addr, &ipv6, addr_port); + result = ISC_R_SUCCESS; + } + } + + return (result); +} + +static bool +parse_proxy_addresses(const char *addrs, isc_sockaddr_t *psrc, + isc_sockaddr_t *pdst) { + isc_result_t result = ISC_R_FAILURE; + isc_sockaddr_t src = { 0 }, dst = { 0 }; + isc_proxy_addrs_parser_state_t st = { 0 }; + in_port_t src_port = 0, dst_port = 53; /* Follow kdig footsteps */ + + REQUIRE(addrs != NULL && *addrs != '\0'); + REQUIRE(psrc != NULL); + REQUIRE(pdst != NULL); + + st.str = addrs; + + /* start syntax analysis and verification */ + if (!rule_proxy_addrs(&st)) { + warn("PROXY source and destination addresses cannot be parsed"); + return (false); + } + + /* get port numeric values */ + if (st.src_port_len > 0) { + INSIST(st.src_port_start != NULL); + proxy_handle_port_string(st.src_port_start, st.src_port_len, + &src_port); + } + + if (st.dst_port_len > 0) { + INSIST(st.dst_port_start != NULL); + proxy_handle_port_string(st.dst_port_start, st.dst_port_len, + &dst_port); + } + + /* get addresses */ + INSIST(st.src_addr_len > 0); + INSIST(st.src_addr_start != NULL); + INSIST(st.dst_addr_len > 0); + INSIST(st.dst_addr_start != NULL); + + result = proxy_handle_addr_string(st.src_addr_start, st.src_addr_len, + src_port, &src); + if (result != ISC_R_SUCCESS) { + warn("Cannot get PROXY source address: %s", + isc_result_totext(result)); + return (false); + } + + result = proxy_handle_addr_string(st.dst_addr_start, st.dst_addr_len, + dst_port, &dst); + if (result != ISC_R_SUCCESS) { + warn("Cannot get PROXY destination address: %s", + isc_result_totext(result)); + return (false); + } + + /* addresses should be of the same type */ + if (isc_sockaddr_pf(&src) != isc_sockaddr_pf(&dst)) { + warn("PROXY source and destination addresses must be of the " + "same type"); + return (false); + } + + *psrc = src; + *pdst = dst; + + return (true); +} + +static bool +rule_proxy_addrs(isc_proxy_addrs_parser_state_t *st) { + if (!rule_addr(st)) { + return (false); + } + + st->src_addr_start = st->last_addr_start; + st->src_addr_len = st->last_addr_len; + st->src_port_start = st->last_port_start; + st->src_port_len = st->last_port_len; + + if (!MATCH('-')) { + return (false); + } + + ADVANCE(); + + if (!rule_addr(st)) { + return (false); + } + + st->dst_addr_start = st->last_addr_start; + st->dst_addr_len = st->last_addr_len; + st->dst_port_start = st->last_port_start; + st->dst_port_len = st->last_port_len; + + if (!MATCH('\0')) { + return (false); + } + + return (true); +} + +static bool +rule_addr(isc_proxy_addrs_parser_state_t *st) { + const char *start = GETP(); + if (!rule_addr_char(st)) { + return (false); + } + + while (rule_addr_char(st)) { + /* skip */ + } + + st->last_addr_start = start; + st->last_addr_len = GETP() - start; + + if (MATCH('#')) { + ADVANCE(); + + if (!rule_port(st)) { + return (false); + } + } + + return (true); +} + +static bool +rule_port(isc_proxy_addrs_parser_state_t *st) { + const char *start = GETP(); + if (!MATCH_DIGIT()) { + return (false); + } + + ADVANCE(); + + while (MATCH_DIGIT()) { + ADVANCE(); + } + + st->last_port_start = start; + st->last_port_len = GETP() - start; + + return (true); +} + +static bool +rule_addr_char(isc_proxy_addrs_parser_state_t *st) { + if (MATCH('#') || MATCH('-') || MATCH('\0')) { + return (false); + } + + ADVANCE(); + + return (true); +} + +#undef GETP +#undef ADVANCE +#undef MATCH_DIGIT +#undef MATCH + +static bool +plus_proxy_handle_addresses(const char *value, const bool state, + dig_lookup_t *lookup) { + lookup->proxy_mode = state; + if (!state) { + /* + * We are not interested in the option value in that + * case + */ + return (true); + } + + if (value == NULL || *value == '\0') { + lookup->proxy_local = true; + return (true); + } + + if (!parse_proxy_addresses(value, &lookup->proxy_src_addr, + &lookup->proxy_dst_addr)) + { + return (false); + } + return (true); +} + +static bool +plus_proxy_options(const char *cmd, const char *value, const bool state, + dig_lookup_t *lookup) { + switch (cmd[5]) { + case '-': + FULLCHECK("proxy-plain"); + lookup->proxy_plain = state; + if (!plus_proxy_handle_addresses(value, state, lookup)) { + goto invalid_option; + } + break; + case '\0': + FULLCHECK("proxy"); + if (!plus_proxy_handle_addresses(value, state, lookup)) { + goto invalid_option; + } + break; + default: + goto invalid_option; + } + return (true); + +invalid_option: + return (false); +} + static bool plus_tls_options(const char *cmd, const char *value, const bool state, dig_lookup_t *lookup) { @@ -1318,6 +1675,8 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, switch (cmd[1]) { case 'e': /* defname */ FULLCHECK("defname"); + fprintf(stderr, ";; +[no]defname option is " + "deprecated; use +[no]search\n"); if (!lookup->trace) { usesearch = state; } @@ -1438,6 +1797,10 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, "ednsflags"); goto exit_or_usage; } + if (lookup->edns == -1) { + lookup->edns = + DEFAULT_EDNS_VERSION; + } lookup->ednsflags = num; break; case 'n': @@ -1692,12 +2055,11 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, goto invalid_option; } break; - case 'm': /* multiline */ + case 'm': switch (cmd[1]) { case 'a': FULLCHECK("mapped"); - fprintf(stderr, ";; +mapped option is deprecated"); - break; + fatal("+mapped option no longer supported"); case 'u': FULLCHECK("multiline"); lookup->multiline = state; @@ -1797,19 +2159,30 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, } break; case 'p': - FULLCHECK("padding"); - if (state && lookup->edns == -1) { - lookup->edns = DEFAULT_EDNS_VERSION; - } - if (value == NULL) { - goto need_value; - } - result = parse_uint(&num, value, 512, "padding"); - if (result != ISC_R_SUCCESS) { - warn("Couldn't parse padding"); - goto exit_or_usage; + switch (cmd[1]) { + case 'a': + FULLCHECK("padding"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, 512, "padding"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse padding"); + goto exit_or_usage; + } + lookup->padding = (uint16_t)num; + break; + case 'r': + if (!plus_proxy_options(cmd, value, state, lookup)) { + goto invalid_option; + } + break; + default: + goto invalid_option; } - lookup->padding = (uint16_t)num; break; case 'q': switch (cmd[1]) { @@ -1940,8 +2313,7 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, break; case 'i': /* sigchase */ FULLCHECK("sigchase"); - fprintf(stderr, ";; +sigchase option is deprecated"); - break; + fatal("+sigchase option no longer supported"); case 'p': /* split */ FULLCHECK("split"); if (value != NULL && !state) { @@ -2065,8 +2437,7 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, break; case 'o': FULLCHECK("topdown"); - fprintf(stderr, ";; +topdown option is deprecated"); - break; + fatal("+topdown option no longer supported"); case 'r': switch (cmd[2]) { case 'a': /* trace */ @@ -2107,9 +2478,8 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, break; case 'u': /* trusted-key */ FULLCHECK("trusted-key"); - fprintf(stderr, ";; +trusted-key option is " - "deprecated"); - break; + fatal("+trusted-key option " + "no longer supported"); default: goto invalid_option; } @@ -2146,9 +2516,8 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, switch (cmd[2]) { case 'e': FULLCHECK("unexpected"); - fprintf(stderr, ";; +unexpected option " - "is deprecated"); - break; + fatal("+unexpected option " + "no longer supported"); case 'k': FULLCHECK("unknownformat"); lookup->print_unknown_format = state; @@ -2156,11 +2525,7 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, default: goto invalid_option; } - break; - default: - goto invalid_option; } - break; case 'v': FULLCHECK("vc"); @@ -2263,17 +2628,15 @@ dash_option(char *option, char *next, dig_lookup_t **lookup, break; case 'h': help(); - exit(0); + exit(EXIT_SUCCESS); break; case 'i': - /* deprecated */ - break; + fatal("-%c removed", option[0]); case 'm': /* memdebug */ /* memdebug is handled in preparse_args() */ break; case 'n': - /* deprecated */ - break; + fatal("-%c removed", option[0]); case 'r': debug("digrc (late)"); digrc = false; @@ -2283,7 +2646,7 @@ dash_option(char *option, char *next, dig_lookup_t **lookup, break; case 'v': printf("DiG %s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); break; } if (strlen(option) > 1U) { @@ -2458,7 +2821,7 @@ dash_option(char *option, char *next, dig_lookup_t **lookup, ptr = ptr2; ptr2 = ptr3; } else { - hmac = DST_ALG_HMACMD5; + hmac_alg = DST_ALG_HMACMD5; digestbits = 0; } /* XXXONDREJ: FIXME */ @@ -2492,7 +2855,7 @@ dash_option(char *option, char *next, dig_lookup_t **lookup, ISC_LIST_APPEND(lookup_list, *lookup, link); } else { fprintf(stderr, "Invalid IP address %s\n", value); - exit(1); + exit(EXIT_FAILURE); } return (value_from_next); invalid_option: diff --git a/bin/dig/dig.rst b/bin/dig/dig.rst index 32029417f5..83c0ab7acf 100644 --- a/bin/dig/dig.rst +++ b/bin/dig/dig.rst @@ -507,6 +507,44 @@ abbreviation is unambiguous; for example, :option:`+cd` is equivalent to mandatory. Responses to padded queries may also be padded, but only if the query uses TCP or DNS COOKIE. +.. option:: +proxy[=src_addr[#src_port]-dst_addr[#dst_port]], +noproxy + + When this option is set, :program:`dig` adds PROXYv2 headers to the + queries. When source and destination addresses are specified, the + headers contain them and use the ``PROXY`` command. It means for + the remote peer that the queries were sent on behalf of another + node and that the PROXYv2 header reflects the original connection + endpoints. The default source port is ``0`` and destination port is + `53`. + + For encrypted DNS transports, to prevent accidental information + leakage, encryption is applied to the PROXYv2 headers: the headers + are sent right after the handshake process has been completed. + + For plain DNS transports, no encryption is applied to the PROXYv2 + headers. + + If the addressees are omitted, PROXYv2 headers, that use the + ``LOCAL`` command set, are added instead. For the remote peer, that + means that the queries were sent on purpose without being relayed, + so the real connection endpoint addresses must be used. + +.. option:: +proxy-plain[=src_addr[#src_port]-dst_addr[#dst_port], +noproxy-plain + + The same as ``+[no]proxy``, but instructs ``dig`` to send PROXYv2 + headers ahead of any encryption, before any handshake messages are + sent. That makes :program:`dig` behave exactly how it is described + in the PROXY protocol specification, but not all software expects + such behaviour. + + Please consult the software documentation to find out if you need + this option. (for example, ``dnsdist`` expects encrypted PROXYv2 + headers sent over TLS when encryption is used, while ``HAProxy`` + and many other software packages expect plain ones). + + For plain DNS transports the option is effectively an alias for the + ``+[no]proxy`` described above. + .. option:: +qid=value This option specifies the query ID to use when sending queries. @@ -576,11 +614,6 @@ abbreviation is unambiguous; for example, :option:`+cd` is equivalent to This option performs [or does not perform] a search showing intermediate results. -.. option:: +sigchase, +nosigchase - - This feature is now obsolete and has been removed; use :iscman:`delv` - instead. - .. option:: +split=W This option splits long hex- or base64-formatted fields in resource records into @@ -650,25 +683,23 @@ abbreviation is unambiguous; for example, :option:`+cd` is equivalent to server TLS certificate verification. Otherwise, the DNS server name is used. This option has no effect if :option:`+tls-ca` is not specified. -.. option:: +topdown, +notopdown - - This feature is related to :option:`dig +sigchase`, which is obsolete and - has been removed. Use :iscman:`delv` instead. - .. option:: +trace, +notrace - This option toggles tracing of the delegation path from the root name servers for - the name being looked up. Tracing is disabled by default. When - tracing is enabled, :program:`dig` makes iterative queries to resolve the - name being looked up. It follows referrals from the root servers, - showing the answer from each server that was used to resolve the - lookup. + This option toggles tracing of the delegation path from the root name + servers for the name being looked up. Tracing is disabled by default. + When tracing is enabled, :program:`dig` makes iterative queries to + resolve the name being looked up. It follows referrals from the root + servers, showing the answer from each server that was used to resolve + the lookup. If ``@server`` is also specified, it affects only the initial query for the root zone name servers. - :option:`+dnssec` is also set when :option:`+trace` is set, to better emulate the - default queries from a name server. + :option:`+dnssec` is set when :option:`+trace` is set, to better + emulate the default queries from a name server. + + Note that the ``delv +ns`` option can also be used for tracing the + resolution of a name from the root (see :iscman:`delv`). .. option:: +tries=T @@ -676,11 +707,6 @@ abbreviation is unambiguous; for example, :option:`+cd` is equivalent to instead of the default, 3. If ``T`` is less than or equal to zero, the number of tries is silently rounded up to 1. -.. option:: +trusted-key=#### - - This option formerly specified trusted keys for use with :option:`dig +sigchase`. This - feature is now obsolete and has been removed; use :iscman:`delv` instead. - .. option:: +ttlid, +nottlid This option displays [or does not display] the TTL when printing the record. diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 5e8e8422a5..1e2303a1af 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -138,7 +138,7 @@ char keyfile[MXNAME] = ""; char keysecret[MXNAME] = ""; unsigned char cookie_secret[33]; unsigned char cookie[8]; -dst_algorithm_t hmac = DST_ALG_UNKNOWN; +dst_algorithm_t hmac_alg = DST_ALG_UNKNOWN; unsigned int digestbits = 0; isc_buffer_t *namebuf = NULL; dns_tsigkey_t *tsigkey = NULL; @@ -359,8 +359,6 @@ get_reverse(char *reverse, size_t len, char *value, bool strict) { } } -void (*dighost_pre_exit_hook)(void) = NULL; - #if TARGET_OS_IPHONE void warn(const char *format, ...) { @@ -393,10 +391,7 @@ digexit(void) { exitcode = 10; } if (fatalexit != 0) { - exitcode = fatalexit; - } - if (dighost_pre_exit_hook != NULL) { - dighost_pre_exit_hook(); + _exit(fatalexit); } exit(exitcode); } @@ -411,7 +406,11 @@ fatal(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - isc__tls_setfatalmode(); + if (fatalexit == 0 && exitcode != 0) { + fatalexit = exitcode; + } else if (fatalexit == 0) { + fatalexit = EXIT_FAILURE; + } digexit(); } @@ -794,6 +793,11 @@ clone_lookup(dig_lookup_t *lookold, bool servers) { looknew->rrcomments = lookold->rrcomments; looknew->fuzzing = lookold->fuzzing; looknew->fuzztime = lookold->fuzztime; + looknew->proxy_mode = lookold->proxy_mode; + looknew->proxy_plain = lookold->proxy_plain; + looknew->proxy_local = lookold->proxy_local; + looknew->proxy_src_addr = lookold->proxy_src_addr; + looknew->proxy_dst_addr = lookold->proxy_dst_addr; if (lookold->ecs_addr != NULL) { looknew->ecs_addr = isc_mem_get(mctx, @@ -874,7 +878,7 @@ setup_text_key(void) { secretsize = isc_buffer_usedlength(&secretbuf); - if (hmac == DST_ALG_UNKNOWN) { + if (hmac_alg == DST_ALG_UNKNOWN) { result = DST_R_UNSUPPORTEDALG; goto failure; } @@ -884,7 +888,7 @@ setup_text_key(void) { goto failure; } - result = dns_tsigkey_create(&keyname, hmac, secretstore, + result = dns_tsigkey_create(&keyname, hmac_alg, secretstore, (int)secretsize, mctx, &tsigkey); failure: if (result != ISC_R_SUCCESS) { @@ -1036,35 +1040,35 @@ parse_hmac(const char *algname) { digestbits = 0; if (strcasecmp(buf, "hmac-md5") == 0) { - hmac = DST_ALG_HMACMD5; + hmac_alg = DST_ALG_HMACMD5; } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { - hmac = DST_ALG_HMACMD5; + hmac_alg = DST_ALG_HMACMD5; digestbits = parse_bits(&buf[9], "digest-bits [0..128]", 128); } else if (strcasecmp(buf, "hmac-sha1") == 0) { - hmac = DST_ALG_HMACSHA1; + hmac_alg = DST_ALG_HMACSHA1; digestbits = 0; } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { - hmac = DST_ALG_HMACSHA1; + hmac_alg = DST_ALG_HMACSHA1; digestbits = parse_bits(&buf[10], "digest-bits [0..160]", 160); } else if (strcasecmp(buf, "hmac-sha224") == 0) { - hmac = DST_ALG_HMACSHA224; + hmac_alg = DST_ALG_HMACSHA224; } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { - hmac = DST_ALG_HMACSHA224; + hmac_alg = DST_ALG_HMACSHA224; digestbits = parse_bits(&buf[12], "digest-bits [0..224]", 224); } else if (strcasecmp(buf, "hmac-sha256") == 0) { - hmac = DST_ALG_HMACSHA256; + hmac_alg = DST_ALG_HMACSHA256; } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { - hmac = DST_ALG_HMACSHA256; + hmac_alg = DST_ALG_HMACSHA256; digestbits = parse_bits(&buf[12], "digest-bits [0..256]", 256); } else if (strcasecmp(buf, "hmac-sha384") == 0) { - hmac = DST_ALG_HMACSHA384; + hmac_alg = DST_ALG_HMACSHA384; } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { - hmac = DST_ALG_HMACSHA384; + hmac_alg = DST_ALG_HMACSHA384; digestbits = parse_bits(&buf[12], "digest-bits [0..384]", 384); } else if (strcasecmp(buf, "hmac-sha512") == 0) { - hmac = DST_ALG_HMACSHA512; + hmac_alg = DST_ALG_HMACSHA512; } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { - hmac = DST_ALG_HMACSHA512; + hmac_alg = DST_ALG_HMACSHA512; digestbits = parse_bits(&buf[12], "digest-bits [0..512]", 512); } else { fprintf(stderr, @@ -1170,7 +1174,7 @@ setup_file_key(void) { case DST_ALG_HMACSHA256: case DST_ALG_HMACSHA384: case DST_ALG_HMACSHA512: - hmac = dst_key_alg(dstkey); + hmac_alg = dst_key_alg(dstkey); break; default: dst_key_attach(dstkey, &sig0key); @@ -1179,9 +1183,9 @@ setup_file_key(void) { } if (dstkey != NULL) { - result = dns_tsigkey_createfromkey(dst_key_name(dstkey), hmac, - dstkey, false, false, NULL, - 0, 0, mctx, &tsigkey); + result = dns_tsigkey_createfromkey( + dst_key_name(dstkey), hmac_alg, dstkey, false, false, + NULL, 0, 0, mctx, &tsigkey); if (result != ISC_R_SUCCESS) { printf(";; Couldn't create key %s: %s\n", keynametext, isc_result_totext(result)); @@ -1383,6 +1387,7 @@ typedef struct dig_ednsoptname { dig_ednsoptname_t optnames[] = { { 1, "LLQ" }, /* draft-sekar-dns-llq */ + { 2, "UL" }, /* draft-ietf-dnssd-update-lease */ { 3, "NSID" }, /* RFC 5001 */ { 5, "DAU" }, /* RFC 6975 */ { 6, "DHU" }, /* RFC 6975 */ @@ -2192,7 +2197,8 @@ setup_lookup(dig_lookup_t *lookup) { debug("setup_lookup(%p)", lookup); - dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &lookup->sendmsg); + dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, + &lookup->sendmsg); if (lookup->new_search) { debug("resetting lookup counter."); @@ -2920,6 +2926,9 @@ start_tcp(dig_query_t *query) { bool tls_mode = false; isc_tlsctx_client_session_cache_t *sess_cache = NULL; int local_timeout; + isc_nm_proxy_type_t proxy_type = ISC_NM_PROXY_NONE; + isc_nm_proxyheader_info_t proxy_info = { 0 }; + isc_nm_proxyheader_info_t *ppi = NULL; REQUIRE(DIG_VALID_QUERY(query)); @@ -3011,6 +3020,22 @@ start_tcp(dig_query_t *query) { } } + if (query->lookup->proxy_mode) { + proxy_type = ISC_NM_PROXY_PLAIN; + if ((tls_mode || (query->lookup->https_mode && + !query->lookup->http_plain)) && + !query->lookup->proxy_plain) + { + proxy_type = ISC_NM_PROXY_ENCRYPTED; + } + if (!query->lookup->proxy_local) { + isc_nm_proxyheader_info_init( + &proxy_info, &query->lookup->proxy_src_addr, + &query->lookup->proxy_dst_addr, NULL); + ppi = &proxy_info; + } + } + REQUIRE(query != NULL); query_attach(query, &connectquery); @@ -3023,7 +3048,8 @@ start_tcp(dig_query_t *query) { } isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, tcp_connected, connectquery, - local_timeout, tlsctx, sess_cache); + local_timeout, tlsctx, sess_cache, + proxy_type, ppi); #if HAVE_LIBNGHTTP2 } else if (query->lookup->https_mode) { char uri[4096] = { 0 }; @@ -3043,12 +3069,13 @@ start_tcp(dig_query_t *query) { isc_nm_httpconnect(netmgr, &localaddr, &query->sockaddr, uri, !query->lookup->https_get, tcp_connected, connectquery, tlsctx, sess_cache, - local_timeout); + local_timeout, proxy_type, ppi); #endif } else { isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, tcp_connected, connectquery, - local_timeout, NULL, NULL); + local_timeout, NULL, NULL, proxy_type, + ppi); } return; @@ -3204,6 +3231,7 @@ udp_ready(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { start_udp(next); check_if_done(); } else { + dighost_error("no servers could be reached\n"); clear_current_lookup(); } @@ -3297,9 +3325,24 @@ start_udp(dig_query_t *query) { } query_attach(query, &connectquery); - isc_nm_udpconnect(netmgr, &localaddr, &query->sockaddr, udp_ready, - connectquery, - (timeout ? timeout : UDP_TIMEOUT) * 1000); + if (query->lookup->proxy_mode) { + isc_nm_proxyheader_info_t proxy_info = { 0 }; + isc_nm_proxyheader_info_t *ppi = NULL; + if (!query->lookup->proxy_local) { + isc_nm_proxyheader_info_init( + &proxy_info, &query->lookup->proxy_src_addr, + &query->lookup->proxy_dst_addr, NULL); + ppi = &proxy_info; + } + isc_nm_proxyudpconnect(netmgr, &localaddr, &query->sockaddr, + udp_ready, connectquery, + (timeout ? timeout : UDP_TIMEOUT) * 1000, + ppi); + } else { + isc_nm_udpconnect(netmgr, &localaddr, &query->sockaddr, + udp_ready, connectquery, + (timeout ? timeout : UDP_TIMEOUT) * 1000); + } } /*% @@ -3523,6 +3566,12 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { debug("tcp_connected(%p, %s, %p)", handle, isc_result_totext(eresult), query); + if (eresult == ISC_R_SHUTTINGDOWN) { + query_detach(&query); + cancel_all(); + return; + } + lookup_attach(query->lookup, &l); if (eresult == ISC_R_CANCELED || eresult == ISC_R_TLSBADPEERCERT || @@ -3601,6 +3650,7 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { start_tcp(next); check_if_done(); } else { + dighost_error("no servers could be reached\n"); clear_current_lookup(); } @@ -3907,7 +3957,9 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, { debug("recv_done: cancel"); isc_nmhandle_detach(&query->readhandle); - if (!query->canceled) { + if (eresult == ISC_R_SHUTTINGDOWN) { + cancel_all(); + } else if (!query->canceled) { cancel_lookup(l); } query_detach(&query); @@ -4124,7 +4176,7 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, goto keep_query; } - dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); + dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &msg); if (tsigkey != NULL) { if (l->querysig == NULL) { @@ -4302,29 +4354,32 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, if ((msg->rcode == dns_rcode_servfail && !l->servfail_stops) || (check_ra && (msg->flags & DNS_MESSAGEFLAG_RA) == 0 && l->recurse)) { + const char *err = (msg->rcode == dns_rcode_servfail && + !l->servfail_stops) + ? "SERVFAIL reply" + : "recursion not available"; dig_query_t *next = ISC_LIST_NEXT(query, link); if (l->current_query == query) { query_detach(&l->current_query); } - if (next != NULL) { + if (next != NULL && (!l->ns_search_only || l->trace_root)) { + dighost_comments(l, + "Got %s from %s, trying next server", + err, query->servname); debug("sending query %p", next); if (l->tcp_mode) { start_tcp(next); } else { start_udp(next); } - dighost_comments(l, - "Got %s from %s, trying next " - "server", - msg->rcode == dns_rcode_servfail - ? "SERVFAIL reply" - : "recursion not available", - query->servname); if (check_if_queries_done(l, query)) { goto cancel_lookup; } goto detach_query; + } else { + dighost_comments(l, "Got %s from %s", err, + query->servname); } } diff --git a/bin/dig/dighost.h b/bin/dig/dighost.h index af4379ce4c..99c0c3c864 100644 --- a/bin/dig/dighost.h +++ b/bin/dig/dighost.h @@ -187,6 +187,13 @@ struct dig_lookup { char *tls_key_file; isc_tlsctx_cache_t *tls_ctx_cache; }; + struct { + bool proxy_mode; + bool proxy_plain; + bool proxy_local; + isc_sockaddr_t proxy_src_addr; + isc_sockaddr_t proxy_dst_addr; + }; isc_stdtime_t fuzztime; }; @@ -259,7 +266,7 @@ extern isc_sockaddr_t localaddr; extern char keynametext[MXNAME]; extern char keyfile[MXNAME]; extern char keysecret[MXNAME]; -extern dst_algorithm_t hmac; +extern dst_algorithm_t hmac_alg; extern unsigned int digestbits; extern dns_tsigkey_t *tsigkey; extern bool validated; diff --git a/bin/dig/host.c b/bin/dig/host.c index 68906389e4..baacb8d54c 100644 --- a/bin/dig/host.c +++ b/bin/dig/host.c @@ -132,7 +132,7 @@ show_usage(void) { " -W specifies how long to wait for a reply\n" " -4 use IPv4 query transport only\n" " -6 use IPv6 query transport only\n"); - exit(1); + exit(EXIT_FAILURE); } static void @@ -656,7 +656,7 @@ pre_parse_args(int argc, char **argv) { break; case 'V': printf("host %s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); break; case 'w': break; diff --git a/bin/dig/nslookup.c b/bin/dig/nslookup.c index dc43f7cdcf..12ba49d311 100644 --- a/bin/dig/nslookup.c +++ b/bin/dig/nslookup.c @@ -849,7 +849,7 @@ usage(void) { "'host' using default server\n"); fprintf(stderr, " nslookup [-opt ...] host server # just look up " "'host' using 'server'\n"); - exit(1); + exit(EXIT_FAILURE); } static void @@ -862,7 +862,7 @@ parse_args(int argc, char **argv) { if (argv[0][0] == '-') { if (strncasecmp(argv[0], "-ver", 4) == 0) { printf("nslookup %s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); } else if (argv[0][1] != 0) { setoption(&argv[0][1]); } else { diff --git a/bin/dnssec/.gitignore b/bin/dnssec/.gitignore index 9d50f6cc8c..c7917cdbbe 100644 --- a/bin/dnssec/.gitignore +++ b/bin/dnssec/.gitignore @@ -2,6 +2,7 @@ dnssec-cds dnssec-dsfromkey dnssec-keyfromlabel dnssec-keygen +dnssec-ksr dnssec-makekeyset dnssec-revoke dnssec-settime diff --git a/bin/dnssec/Makefile.am b/bin/dnssec/Makefile.am index 3f7aed488e..26726da8d3 100644 --- a/bin/dnssec/Makefile.am +++ b/bin/dnssec/Makefile.am @@ -2,6 +2,7 @@ include $(top_srcdir)/Makefile.top AM_CPPFLAGS += \ $(LIBISC_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ $(LIBDNS_CFLAGS) AM_CPPFLAGS += \ @@ -12,6 +13,7 @@ noinst_LTLIBRARIES = libdnssectool.la LDADD += \ libdnssectool.la \ $(LIBISC_LIBS) \ + $(LIBISCCFG_LIBS) \ $(LIBDNS_LIBS) \ $(OPENSSL_LIBS) @@ -21,6 +23,7 @@ bin_PROGRAMS = \ dnssec-importkey \ dnssec-keyfromlabel \ dnssec-keygen \ + dnssec-ksr \ dnssec-revoke \ dnssec-settime \ dnssec-signzone \ @@ -32,22 +35,18 @@ libdnssectool_la_SOURCES = \ dnssec_keygen_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(LIBISCCFG_CFLAGS) \ $(OPENSSL_CFLAGS) dnssec_keygen_LDADD = \ $(LDADD) \ - $(LIBISCCFG_LIBS) \ $(OPENSSL_LIBS) dnssec_signzone_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(LIBISCCFG_CFLAGS) \ $(OPENSSL_CFLAGS) dnssec_signzone_LDADD = \ $(LDADD) \ - $(LIBISCCFG_LIBS) \ $(OPENSSL_LIBS) dnssec_dsfromkey_CPPFLAGS = \ @@ -57,5 +56,4 @@ dnssec_dsfromkey_CPPFLAGS = \ dnssec_dsfromkey_LDADD = \ $(LDADD) \ - $(LIBISCCFG_LIBS) \ $(OPENSSL_LIBS) diff --git a/bin/dnssec/dnssec-cds.c b/bin/dnssec/dnssec-cds.c index 2e2d8999f1..ebd619aaa3 100644 --- a/bin/dnssec/dnssec-cds.c +++ b/bin/dnssec/dnssec-cds.c @@ -247,8 +247,8 @@ static void load_db(const char *filename, dns_db_t **dbp, dns_dbnode_t **nodep) { isc_result_t result; - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, dbp); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, dbp); check_result(result, "dns_db_create()"); result = dns_db_load(*dbp, filename, dns_masterformat_text, @@ -979,8 +979,8 @@ update_diff(const char *cmd, uint32_t ttl, dns_rdataset_t *addset, dns_rdataset_t diffset; uint32_t save; - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, &update_db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, &update_db); check_result(result, "dns_db_create()"); result = dns_db_newversion(update_db, &update_version); @@ -1056,7 +1056,7 @@ usage(void) { " -T TTL of DS records\n" " -V print version\n" " -v \n"); - exit(1); + exit(EXIT_FAILURE); } static void @@ -1354,5 +1354,6 @@ main(int argc, char *argv[]) { cleanup: print_mem_stats = true; cleanup(); - exit(0); + + return (0); } diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c index e60eff783c..cdb8147eed 100644 --- a/bin/dnssec/dnssec-dsfromkey.c +++ b/bin/dnssec/dnssec-dsfromkey.c @@ -60,6 +60,7 @@ static dns_name_t *name = NULL; static isc_mem_t *mctx = NULL; static uint32_t ttl; static bool emitttl = false; +static unsigned int split_width = 0; static isc_result_t initname(char *setname) { @@ -106,8 +107,8 @@ loadset(const char *filename, dns_rdataset_t *rdataset) { dns_name_format(name, setname, sizeof(setname)); - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, &db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, &db); if (result != ISC_R_SUCCESS) { fatal("can't create database"); } @@ -285,8 +286,8 @@ emit(dns_dsdigest_t dt, bool showall, bool cds, dns_rdata_t *rdata) { fatal("can't print name"); } - result = dns_rdata_tofmttext(&ds, (dns_name_t *)NULL, 0, 0, 0, "", - &textb); + result = dns_rdata_tofmttext(&ds, (dns_name_t *)NULL, 0, 0, split_width, + "", &textb); if (result != ISC_R_SUCCESS) { fatal("can't print rdata"); @@ -353,13 +354,14 @@ usage(void) { " -f zonefile: read keys from a zone file\n" " -h: print help information\n" " -K directory: where to find key or keyset files\n" + " -w split base64 rdata text into chunks\n" " -s: read keys from keyset- file\n" " -T: TTL of output records (omitted by default)\n" " -v level: verbosity\n" " -V: print version information\n"); fprintf(stderr, "Output: DS or CDS RRs\n"); - exit(-1); + exit(EXIT_FAILURE); } int @@ -389,7 +391,7 @@ main(int argc, char **argv) { isc_commandline_errprint = false; -#define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV" +#define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:whV" while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { switch (ch) { case '1': @@ -441,6 +443,9 @@ main(int argc, char **argv) { fatal("-v must be followed by a number"); } break; + case 'w': + split_width = UINT_MAX; + break; case 'F': /* Reserved for FIPS mode */ FALLTHROUGH; @@ -461,7 +466,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } #if OPENSSL_VERSION_NUMBER >= 0x30200000L && OPENSSL_API_LEVEL >= 30200 diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c index df01289e19..c93a0158eb 100644 --- a/bin/dnssec/dnssec-importkey.c +++ b/bin/dnssec/dnssec-importkey.c @@ -103,8 +103,8 @@ loadset(const char *filename, dns_rdataset_t *rdataset) { dns_name_format(name, setname, sizeof(setname)); - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, &db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, &db); if (result != ISC_R_SUCCESS) { fatal("can't create database"); } @@ -289,7 +289,7 @@ usage(void) { fprintf(stderr, " -D sync date/[+-]offset/none: set/unset " "CDS and CDNSKEY deletion date\n"); - exit(-1); + exit(EXIT_FAILURE); } int @@ -395,7 +395,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/bin/dnssec/dnssec-keyfromlabel.c b/bin/dnssec/dnssec-keyfromlabel.c index 80022095b1..e574ccc70a 100644 --- a/bin/dnssec/dnssec-keyfromlabel.c +++ b/bin/dnssec/dnssec-keyfromlabel.c @@ -102,7 +102,7 @@ usage(void) { fprintf(stderr, " K++.key, " "K++.private\n"); - exit(-1); + exit(EXIT_FAILURE); } int @@ -331,7 +331,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -403,6 +403,7 @@ main(int argc, char **argv) { case DST_ALG_SPHINCSSHA256128S: case DST_ALG_XMSS: case DST_ALG_XMSSMT: + case DST_ALG_MERKLE_TREE: break; default: fatal("%s is incompatible with NSEC3; " @@ -611,7 +612,7 @@ main(int argc, char **argv) { fatal("failed to get key %s/%s: %s", namestr, algstr, isc_result_totext(ret)); UNREACHABLE(); - exit(-1); + exit(EXIT_FAILURE); } /* diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index d352739984..328d2e6f19 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -57,10 +57,6 @@ #include #include -#include -#include -#include -#include #if OPENSSL_VERSION_NUMBER >= 0x30200000L && OPENSSL_API_LEVEL >= 30200 #include #include @@ -69,9 +65,6 @@ #include "dnssectool.h" -#define MAX_RSA 4096 /* should be long enough... */ -#define MAX_DH 4096 /* should be long enough... */ - const char *program = "dnssec-keygen"; /* @@ -94,6 +87,7 @@ struct keygen_ctx { const char *policy; const char *configfile; const char *directory; + dns_keystore_t *keystore; char *algname; char *nametype; char *type; @@ -104,8 +98,9 @@ struct keygen_ctx { int options; int dbits; dns_ttl_t ttl; - uint16_t kskflag; - uint16_t revflag; + bool wantzsk; + bool wantksk; + bool wantrev; dns_secalg_t alg; /* timing data */ int prepub; @@ -191,7 +186,7 @@ usage(void) { fprintf(stderr, " -d (0 => max, default)\n"); fprintf(stderr, " -E :\n"); fprintf(stderr, " name of an OpenSSL engine to use\n"); - fprintf(stderr, " -f : KSK | REVOKE\n"); + fprintf(stderr, " -f : ZSK | KSK | REVOKE\n"); fprintf(stderr, " -F: FIPS mode\n"); fprintf(stderr, " -L : default key TTL\n"); fprintf(stderr, " -p : (default: 3 [dnssec])\n"); @@ -234,7 +229,7 @@ usage(void) { fprintf(stderr, " K++.key, " "K++.private\n"); - exit(-1); + exit(EXIT_FAILURE); } static void @@ -261,52 +256,6 @@ progress(int p) { (void)fflush(stderr); } -static void -kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, - dns_kasp_t **kaspp) { - const cfg_listelt_t *element; - const cfg_obj_t *kasps = NULL; - dns_kasp_t *kasp = NULL, *kasp_next; - isc_result_t result = ISC_R_NOTFOUND; - dns_kasplist_t kasplist; - - ISC_LIST_INIT(kasplist); - - (void)cfg_map_get(config, "dnssec-policy", &kasps); - for (element = cfg_list_first(kasps); element != NULL; - element = cfg_list_next(element)) - { - cfg_obj_t *kconfig = cfg_listelt_value(element); - kasp = NULL; - if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), - name) != 0) - { - continue; - } - - result = cfg_kasp_fromconfig(kconfig, NULL, true, mctx, lctx, - &kasplist, &kasp); - if (result != ISC_R_SUCCESS) { - fatal("failed to configure dnssec-policy '%s': %s", - cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), - isc_result_totext(result)); - } - INSIST(kasp != NULL); - dns_kasp_freeze(kasp); - break; - } - - *kaspp = kasp; - - /* - * Cleanup kasp list. - */ - for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { - kasp_next = ISC_LIST_NEXT(kasp, link); - ISC_LIST_UNLINK(kasplist, kasp, link); - dns_kasp_detach(&kasp); - } -} static void keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { char filename[255]; @@ -324,7 +273,9 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { dst_key_t *prevkey = NULL; UNUSED(argc); + dns_secalg_format(ctx->alg, algstr, sizeof(algstr)); + if (ctx->predecessor == NULL) { if (ctx->prepub == -1) { ctx->prepub = 0; @@ -355,6 +306,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { break; } } + if (ctx->use_nsec3) { switch (ctx->alg) { case DST_ALG_RSASHA1: @@ -372,6 +324,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { case DST_ALG_SPHINCSSHA256128S: case DST_ALG_XMSS: case DST_ALG_XMSSMT: + case DST_ALG_MERKLE_TREE: break; default: fatal("algorithm %s is incompatible with NSEC3" @@ -427,6 +380,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { case DST_ALG_SPHINCSSHA256128S: case DST_ALG_XMSS: case DST_ALG_XMSSMT: + case DST_ALG_MERKLE_TREE: break; default: fatal("key size not specified (-b option)"); @@ -626,6 +580,9 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { fatal("XMSSMT failed to get bits based on param"); } break; + case DST_ALG_MERKLE_TREE: + ctx->size = 256; + break; } if (ctx->nametype == NULL) { @@ -656,8 +613,12 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ flags |= ctx->signatory; } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */ - flags |= ctx->kskflag; - flags |= ctx->revflag; + if (ctx->ksk || ctx->wantksk) { + flags |= DNS_KEYFLAG_KSK; + } + if (ctx->wantrev) { + flags |= DNS_KEYFLAG_REVOKE; + } } if (ctx->protocol == -1) { @@ -701,26 +662,41 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { do { conflict = false; + if (!ctx->quiet && show_progress) { fprintf(stderr, "Generating key pair."); + } + + if (ctx->keystore != NULL && ctx->policy != NULL) { + ret = dns_keystore_keygen( + ctx->keystore, name, ctx->policy, ctx->rdclass, + mctx, ctx->alg, ctx->size, flags, &key); + } else if (!ctx->quiet && show_progress) { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, - ctx->rdclass, mctx, &key, + ctx->rdclass, NULL, mctx, &key, &progress); - putc('\n', stderr); - fflush(stderr); } else { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, - ctx->rdclass, mctx, &key, NULL); + ctx->rdclass, NULL, mctx, &key, + NULL); + } + + if (!ctx->quiet && show_progress) { + putc('\n', stderr); + fflush(stderr); } + if (ret != ISC_R_SUCCESS) { char namestr[DNS_NAME_FORMATSIZE]; dns_name_format(name, namestr, sizeof(namestr)); fatal("failed to generate key %s/%s: %s\n", namestr, algstr, isc_result_totext(ret)); } + dst_key_setbits(key, ctx->dbits); + /* * Set key timing metadata (unless using -C) * @@ -765,7 +741,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { } if (ctx->setrev) { - if (ctx->kskflag == 0) { + if (!ctx->wantksk) { fprintf(stderr, "%s: warning: Key is " "not flagged as a KSK, but -R " @@ -822,6 +798,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { */ dst_key_setprivateformat(key, 1, 2); } + /* Set the default key TTL */ if (ctx->setttl) { dst_key_setttl(key, ctx->ttl); @@ -903,6 +880,18 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { } } +static void +check_keystore_options(keygen_ctx_t *ctx) { + ctx->directory = dns_keystore_directory(ctx->keystore, NULL); + if (ctx->directory != NULL) { + isc_result_t ret = try_dir(ctx->directory); + if (ret != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", ctx->directory, + isc_result_totext(ret)); + } + } +} + int main(int argc, char **argv) { char *algname = NULL, *freeit = NULL; @@ -995,9 +984,11 @@ main(int argc, char **argv) { case 'f': c = (unsigned char)(isc_commandline_argument[0]); if (toupper(c) == 'K') { - ctx.kskflag = DNS_KEYFLAG_KSK; + ctx.wantksk = true; + } else if (toupper(c) == 'Z') { + ctx.wantzsk = true; } else if (toupper(c) == 'R') { - ctx.revflag = DNS_KEYFLAG_REVOKE; + ctx.wantrev = true; } else { fatal("unknown flag '%s'", isc_commandline_argument); @@ -1178,7 +1169,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -1301,8 +1292,8 @@ main(int argc, char **argv) { if (ctx.size != -1) { fatal("-k and -b cannot be used together"); } - if (ctx.kskflag || ctx.revflag) { - fatal("-k and -f cannot be used together"); + if (ctx.wantrev) { + fatal("-k and -fR cannot be used together"); } if (ctx.options & DST_TYPE_KEY) { fatal("-k and -T KEY cannot be used together"); @@ -1317,7 +1308,6 @@ main(int argc, char **argv) { ctx.use_nsec3 = false; ctx.alg = DST_ALG_ECDSA256; ctx.size = 0; - ctx.kskflag = DNS_KEYFLAG_KSK; ctx.ttl = 3600; ctx.setttl = true; ctx.ksk = true; @@ -1342,7 +1332,8 @@ main(int argc, char **argv) { ctx.policy, ctx.configfile); } - kasp_from_conf(config, mctx, ctx.policy, &kasp); + kasp_from_conf(config, mctx, lctx, ctx.policy, + ctx.directory, engine, &kasp); if (kasp == NULL) { fatal("failed to load dnssec-policy '%s'", ctx.policy); @@ -1356,22 +1347,26 @@ main(int argc, char **argv) { ctx.ttl = dns_kasp_dnskeyttl(kasp); ctx.setttl = true; - kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); - - while (kaspkey != NULL) { + for (kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kaspkey != NULL; + kaspkey = ISC_LIST_NEXT(kaspkey, link)) + { ctx.use_nsec3 = false; ctx.alg = dns_kasp_key_algorithm(kaspkey); ctx.size = dns_kasp_key_size(kaspkey); - ctx.kskflag = dns_kasp_key_ksk(kaspkey) - ? DNS_KEYFLAG_KSK - : 0; ctx.ksk = dns_kasp_key_ksk(kaspkey); ctx.zsk = dns_kasp_key_zsk(kaspkey); ctx.lifetime = dns_kasp_key_lifetime(kaspkey); - + ctx.keystore = dns_kasp_key_keystore(kaspkey); + if (ctx.keystore != NULL) { + check_keystore_options(&ctx); + } + if ((ctx.ksk && !ctx.wantksk && ctx.wantzsk) || + (ctx.zsk && !ctx.wantzsk && ctx.wantksk)) + { + continue; + } keygen(&ctx, mctx, argc, argv); - - kaspkey = ISC_LIST_NEXT(kaspkey, link); } dns_kasp_detach(&kasp); diff --git a/bin/dnssec/dnssec-keygen.rst b/bin/dnssec/dnssec-keygen.rst index 121ced4e02..2e12fe60cd 100644 --- a/bin/dnssec/dnssec-keygen.rst +++ b/bin/dnssec/dnssec-keygen.rst @@ -27,9 +27,7 @@ Description ~~~~~~~~~~~ :program:`dnssec-keygen` generates keys for DNSSEC (Secure DNS), as defined in -:rfc:`2535` and :rfc:`4034`. It can also generate keys for use with TSIG -(Transaction Signatures) as defined in :rfc:`2845`, or TKEY (Transaction -Key) as defined in :rfc:`2930`. +:rfc:`2535` and :rfc:`4034`. The ``name`` of the key is specified on the command line. For DNSSEC keys, this must match the name of the zone for which the key is being @@ -107,7 +105,13 @@ Options .. option:: -f flag This option sets the specified flag in the flag field of the KEY/DNSKEY record. - The only recognized flags are KSK (Key-Signing Key) and REVOKE. + The only recognized flags are ZSK (Zone-Signing Key), KSK (Key-Signing Key) + and REVOKE. + + Note that ZSK is not a physical flag in the DNSKEY record, it is merely used + to explicitly tell that you want to create a ZSK. Setting :option:`-f` in + conjunction with :option:`-k` will result in generating keys that only + match the given role set with this option. .. option:: -F diff --git a/bin/dnssec/dnssec-ksr.c b/bin/dnssec/dnssec-ksr.c new file mode 100644 index 0000000000..493c483d6e --- /dev/null +++ b/bin/dnssec/dnssec-ksr.c @@ -0,0 +1,1327 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dnssectool.h" + +const char *program = "dnssec-ksr"; + +/* + * Infrastructure + */ +static isc_log_t *lctx = NULL; +static isc_mem_t *mctx = NULL; +const char *engine = NULL; +/* + * The domain we are working on + */ +static const char *namestr = NULL; +static dns_fixedname_t fname; +static dns_name_t *name = NULL; +/* + * KSR context + */ +struct ksr_ctx { + const char *policy; + const char *configfile; + const char *file; + const char *keydir; + dns_keystore_t *keystore; + isc_stdtime_t now; + isc_stdtime_t start; + isc_stdtime_t end; + bool setstart; + bool setend; + /* keygen */ + dns_ttl_t ttl; + dns_secalg_t alg; + int size; + time_t lifetime; + time_t propagation; + time_t publishsafety; + time_t retiresafety; + time_t sigrefresh; + time_t sigvalidity; + time_t signdelay; + time_t ttlsig; +}; +typedef struct ksr_ctx ksr_ctx_t; + +/* + * These are set here for backwards compatibility. + * They are raised to 2048 in FIPS mode. + */ +static int min_rsa = 1024; +static int min_dh = 128; + +#define KSR_LINESIZE 1500 /* should be long enough for any DNSKEY record */ +#define DATETIME_INDEX 25 + +#define MAXWIRE (64 * 1024) + +#define STR(t) ((t).value.as_textregion.base) + +#define READLINE(lex, opt, token) + +#define NEXTTOKEN(lex, opt, token) \ + { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret != ISC_R_SUCCESS) \ + goto cleanup; \ + } + +#define BADTOKEN() \ + { \ + ret = ISC_R_UNEXPECTEDTOKEN; \ + goto cleanup; \ + } + +#define CHECK(r) \ + ret = (r); \ + if (ret != ISC_R_SUCCESS) { \ + goto fail; \ + } + +isc_bufferlist_t cleanup_list = ISC_LIST_INITIALIZER; + +static void +usage(int ret) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s options [options] \n", program); + fprintf(stderr, "\n"); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -E : name of an OpenSSL engine to use\n"); + fprintf(stderr, " -e : end date\n"); + fprintf(stderr, " -F: FIPS mode\n"); + fprintf(stderr, " -f: KSR file to sign\n"); + fprintf(stderr, " -i : start date\n"); + fprintf(stderr, " -K : key directory\n"); + fprintf(stderr, " -k : name of a DNSSEC policy\n"); + fprintf(stderr, " -l : file with dnssec-policy config\n"); + fprintf(stderr, " -h: print usage and exit\n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, " -v : set verbosity level\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Commands:\n"); + fprintf(stderr, " keygen: pregenerate ZSKs\n"); + fprintf(stderr, " request: create a Key Signing Request (KSR)\n"); + fprintf(stderr, " sign: sign a KSR, creating a Signed Key " + "Response (SKR)\n"); + exit(ret); +} + +static void +checkparams(ksr_ctx_t *ksr, const char *command) { + if (ksr->configfile == NULL) { + fatal("%s requires a configuration file", command); + } + if (ksr->policy == NULL) { + fatal("%s requires a dnssec-policy", command); + } + if (!ksr->setend) { + fatal("%s requires an end date", command); + } + if (!ksr->setstart) { + ksr->start = ksr->now; + } + if (ksr->keydir == NULL) { + ksr->keydir = "."; + } +} + +static void +getkasp(ksr_ctx_t *ksr, dns_kasp_t **kasp) { + cfg_parser_t *parser = NULL; + cfg_obj_t *config = NULL; + + RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) == ISC_R_SUCCESS); + if (cfg_parse_file(parser, ksr->configfile, &cfg_type_namedconf, + &config) != ISC_R_SUCCESS) + { + fatal("unable to load dnssec-policy '%s' from '%s'", + ksr->policy, ksr->configfile); + } + kasp_from_conf(config, mctx, lctx, ksr->policy, ksr->keydir, engine, + kasp); + if (*kasp == NULL) { + fatal("failed to load dnssec-policy '%s'", ksr->policy); + } + if (ISC_LIST_EMPTY(dns_kasp_keys(*kasp))) { + fatal("dnssec-policy '%s' has no keys configured", ksr->policy); + } + cfg_obj_destroy(parser, &config); + cfg_parser_destroy(&parser); +} + +static int +keytag_cmp(const void *k1, const void *k2) { + dns_dnsseckey_t **key1 = (dns_dnsseckey_t **)k1; + dns_dnsseckey_t **key2 = (dns_dnsseckey_t **)k2; + if (dst_key_id((*key1)->key) < dst_key_id((*key2)->key)) { + return (-1); + } else if (dst_key_id((*key1)->key) > dst_key_id((*key2)->key)) { + return (1); + } + return (0); +} + +static void +get_dnskeys(ksr_ctx_t *ksr, dns_dnsseckeylist_t *keys) { + dns_dnsseckeylist_t keys_read; + dns_dnsseckey_t **keys_sorted; + int i = 0, n = 0; + isc_result_t ret; + + ISC_LIST_INIT(*keys); + ISC_LIST_INIT(keys_read); + ret = dns_dnssec_findmatchingkeys(name, NULL, ksr->keydir, NULL, + ksr->now, mctx, &keys_read); + if (ret != ISC_R_SUCCESS && ret != ISC_R_NOTFOUND) { + fatal("failed to load existing keys from %s: %s", ksr->keydir, + isc_result_totext(ret)); + } + /* Sort on keytag. */ + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(keys_read); dk != NULL; + dk = ISC_LIST_NEXT(dk, link)) + { + n++; + } + keys_sorted = isc_mem_cget(mctx, n, sizeof(dns_dnsseckey_t *)); + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(keys_read); dk != NULL; + dk = ISC_LIST_NEXT(dk, link), i++) + { + keys_sorted[i] = dk; + } + qsort(keys_sorted, n, sizeof(dns_dnsseckey_t *), keytag_cmp); + while (!ISC_LIST_EMPTY(keys_read)) { + dns_dnsseckey_t *key = ISC_LIST_HEAD(keys_read); + ISC_LIST_UNLINK(keys_read, key, link); + } + /* Save sorted list in 'keys' */ + for (i = 0; i < n; i++) { + ISC_LIST_APPEND(*keys, keys_sorted[i], link); + } + INSIST(ISC_LIST_EMPTY(keys_read)); + isc_mem_cput(mctx, keys_sorted, n, sizeof(dns_dnsseckey_t *)); +} + +static void +setcontext(ksr_ctx_t *ksr, dns_kasp_t *kasp) { + ksr->propagation = dns_kasp_zonepropagationdelay(kasp); + ksr->publishsafety = dns_kasp_publishsafety(kasp); + ksr->retiresafety = dns_kasp_retiresafety(kasp); + ksr->sigvalidity = dns_kasp_sigvalidity_dnskey(kasp); + ksr->sigrefresh = dns_kasp_sigrefresh(kasp); + ksr->signdelay = dns_kasp_signdelay(kasp); + ksr->ttl = dns_kasp_dnskeyttl(kasp); + ksr->ttlsig = dns_kasp_zonemaxttl(kasp, true); +} + +static void +cleanup(dns_dnsseckeylist_t *keys, dns_kasp_t *kasp) { + while (!ISC_LIST_EMPTY(*keys)) { + dns_dnsseckey_t *key = ISC_LIST_HEAD(*keys); + ISC_LIST_UNLINK(*keys, key, link); + dst_key_free(&key->key); + dns_dnsseckey_destroy(mctx, &key); + } + dns_kasp_detach(&kasp); + + isc_buffer_t *cbuf = ISC_LIST_HEAD(cleanup_list); + while (cbuf != NULL) { + isc_buffer_t *nbuf = ISC_LIST_NEXT(cbuf, link); + ISC_LIST_UNLINK(cleanup_list, cbuf, link); + isc_buffer_free(&cbuf); + cbuf = nbuf; + } +} + +static void +progress(int p) { + char c = '*'; + switch (p) { + case 0: + c = '.'; + break; + case 1: + c = '+'; + break; + case 2: + c = '*'; + break; + case 3: + c = ' '; + break; + default: + break; + } + (void)putc(c, stderr); + (void)fflush(stderr); +} + +static void +freerrset(dns_rdataset_t *rdataset) { + dns_rdatalist_t *rdlist; + dns_rdata_t *rdata; + + if (!dns_rdataset_isassociated(rdataset)) { + return; + } + + dns_rdatalist_fromrdataset(rdataset, &rdlist); + + for (rdata = ISC_LIST_HEAD(rdlist->rdata); rdata != NULL; + rdata = ISC_LIST_HEAD(rdlist->rdata)) + { + ISC_LIST_UNLINK(rdlist->rdata, rdata, link); + isc_mem_put(mctx, rdata, sizeof(*rdata)); + } + isc_mem_put(mctx, rdlist, sizeof(*rdlist)); + dns_rdataset_disassociate(rdataset); +} + +static void +create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, + isc_stdtime_t inception, isc_stdtime_t active, + isc_stdtime_t *expiration) { + bool conflict = false; + bool freekey = false; + bool show_progress = true; + char algstr[DNS_SECALG_FORMATSIZE]; + char filename[PATH_MAX + 1]; + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + dst_key_t *key = NULL; + int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); + isc_buffer_t buf; + isc_result_t ret; + isc_stdtime_t prepub; + + isc_stdtime_tostring(inception, timestr, sizeof(timestr)); + + /* Check algorithm and size. */ + dns_secalg_format(ksr->alg, algstr, sizeof(algstr)); + if (!dst_algorithm_supported(ksr->alg)) { + fatal("unsupported algorithm: %s", algstr); + } + INSIST(ksr->size >= 0); + switch (ksr->alg) { + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + if (isc_fips_mode()) { + /* verify-only in FIPS mode */ + fatal("unsupported algorithm: %s", algstr); + } + FALLTHROUGH; + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + if (ksr->size != 0 && + (ksr->size < min_rsa || ksr->size > MAX_RSA)) + { + fatal("RSA key size %d out of range", ksr->size); + } + break; + case DST_ALG_ECDSA256: + ksr->size = 256; + break; + case DST_ALG_ECDSA384: + ksr->size = 384; + break; + case DST_ALG_ED25519: + ksr->size = 256; + break; + case DST_ALG_ED448: + ksr->size = 456; + break; + default: + show_progress = false; + break; + } + + isc_buffer_init(&buf, filename, sizeof(filename) - 1); + + /* Check existing keys. */ + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(*keys); dk != NULL; + dk = ISC_LIST_NEXT(dk, link)) + { + isc_stdtime_t act = 0, inact = 0; + + if (!dns_kasp_key_match(kaspkey, dk)) { + continue; + } + (void)dst_key_gettime(dk->key, DST_TIME_ACTIVATE, &act); + (void)dst_key_gettime(dk->key, DST_TIME_INACTIVE, &inact); + /* + * If this key's activation time is set after the inception + * time, it is not eligble for the current bundle. + */ + if (act > inception) { + continue; + } + /* + * If this key's inactive time is set before the inception + * time, it is not eligble for the current bundle. + */ + if (inact > 0 && inception >= inact) { + continue; + } + + /* Found matching existing key. */ + if (verbose > 0 && show_progress) { + fprintf(stderr, + "Selecting key pair for bundle %s: ", timestr); + fflush(stderr); + } + key = dk->key; + *expiration = inact; + goto output; + } + + /* No existing keys match. */ + do { + conflict = false; + + if (verbose > 0 && show_progress) { + fprintf(stderr, + "Generating key pair for bundle %s: ", timestr); + } + if (ksr->keystore != NULL && ksr->policy != NULL) { + ret = dns_keystore_keygen( + ksr->keystore, name, ksr->policy, + dns_rdataclass_in, mctx, ksr->alg, ksr->size, + DNS_KEYOWNER_ZONE, &key); + } else if (show_progress) { + ret = dst_key_generate( + name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE, + DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL, + mctx, &key, &progress); + fflush(stderr); + } else { + ret = dst_key_generate( + name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE, + DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL, + mctx, &key, NULL); + } + + if (ret != ISC_R_SUCCESS) { + fatal("failed to generate key %s/%s: %s\n", namestr, + algstr, isc_result_totext(ret)); + } + + /* Do not overwrite an existing key. */ + if (key_collision(key, name, ksr->keydir, mctx, NULL)) { + conflict = true; + if (verbose > 0) { + isc_buffer_clear(&buf); + ret = dst_key_buildfilename(key, 0, ksr->keydir, + &buf); + if (ret == ISC_R_SUCCESS) { + fprintf(stderr, + "%s: %s already exists, or " + "might collide with another " + "key upon revokation. " + "Generating a new key\n", + program, filename); + } + } + dst_key_free(&key); + } + } while (conflict); + + freekey = true; + + /* Set key timing metadata. */ + prepub = ksr->ttl + ksr->publishsafety + ksr->propagation; + dst_key_setttl(key, ksr->ttl); + dst_key_setnum(key, DST_NUM_LIFETIME, ksr->lifetime); + dst_key_setbool(key, DST_BOOL_KSK, false); + dst_key_setbool(key, DST_BOOL_ZSK, true); + dst_key_settime(key, DST_TIME_CREATED, ksr->now); + dst_key_settime(key, DST_TIME_PUBLISH, (active - prepub)); + dst_key_settime(key, DST_TIME_ACTIVATE, active); + if (ksr->lifetime > 0) { + isc_stdtime_t inactive = (active + ksr->lifetime); + isc_stdtime_t remove = ksr->ttlsig + ksr->propagation + + ksr->retiresafety + ksr->signdelay; + dst_key_settime(key, DST_TIME_INACTIVE, inactive); + dst_key_settime(key, DST_TIME_DELETE, (inactive + remove)); + *expiration = inactive; + } else { + *expiration = 0; + } + + ret = dst_key_tofile(key, options, ksr->keydir); + if (ret != ISC_R_SUCCESS) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + fatal("failed to write key %s: %s\n", keystr, + isc_result_totext(ret)); + } + +output: + isc_buffer_clear(&buf); + ret = dst_key_buildfilename(key, 0, NULL, &buf); + if (ret != ISC_R_SUCCESS) { + fatal("dst_key_buildfilename returned: %s\n", + isc_result_totext(ret)); + } + printf("%s\n", filename); + fflush(stdout); + if (freekey) { + dst_key_free(&key); + } +} + +static void +print_rdata(dns_rdataset_t *rrset) { + isc_buffer_t target; + isc_region_t r; + isc_result_t ret; + char buf[4096]; + + isc_buffer_init(&target, buf, sizeof(buf)); + ret = dns_rdataset_totext(rrset, name, false, false, &target); + if (ret != ISC_R_SUCCESS) { + fatal("failed to print rdata"); + } + isc_buffer_usedregion(&target, &r); + fprintf(stdout, "%.*s", (int)r.length, (char *)r.base); +} + +static isc_stdtime_t +print_dnskeys(dns_kasp_key_t *kaspkey, dns_ttl_t ttl, dns_dnsseckeylist_t *keys, + isc_stdtime_t inception, isc_stdtime_t next_inception) { + char algstr[DNS_SECALG_FORMATSIZE]; + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + dns_rdatalist_t *rdatalist = NULL; + dns_rdataset_t rdataset = DNS_RDATASET_INIT; + isc_result_t ret = ISC_R_SUCCESS; + isc_stdtime_t next_bundle = next_inception; + + isc_stdtime_tostring(inception, timestr, sizeof(timestr)); + dns_secalg_format(dns_kasp_key_algorithm(kaspkey), algstr, + sizeof(algstr)); + + /* Fetch matching key pair. */ + rdatalist = isc_mem_get(mctx, sizeof(*rdatalist)); + dns_rdatalist_init(rdatalist); + rdatalist->rdclass = dns_rdataclass_in; + rdatalist->type = dns_rdatatype_dnskey; + rdatalist->ttl = ttl; + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(*keys); dk != NULL; + dk = ISC_LIST_NEXT(dk, link)) + { + isc_stdtime_t pub = 0, del = 0; + + (void)dst_key_gettime(dk->key, DST_TIME_PUBLISH, &pub); + (void)dst_key_gettime(dk->key, DST_TIME_DELETE, &del); + + /* Determine next bundle. */ + if (pub > 0 && pub > inception && pub < next_bundle) { + next_bundle = pub; + } + if (del > 0 && del > inception && del < next_bundle) { + next_bundle = del; + } + /* Find matching key. */ + if (!dns_kasp_key_match(kaspkey, dk)) { + continue; + } + if (pub > inception) { + continue; + } + if (del != 0 && inception >= del) { + continue; + } + /* Found matching key pair, add DNSKEY record to RRset. */ + isc_buffer_t buf; + isc_buffer_t *newbuf = NULL; + dns_rdata_t *rdata = NULL; + isc_region_t r; + unsigned char rdatabuf[DST_KEY_MAXSIZE]; + + rdata = isc_mem_get(mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf)); + CHECK(dst_key_todns(dk->key, &buf)); + isc_buffer_usedregion(&buf, &r); + isc_buffer_allocate(mctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, dns_rdataclass_in, + dns_rdatatype_dnskey, &r); + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + } + /* Error if no key pair found. */ + if (ISC_LIST_EMPTY(rdatalist->rdata)) { + fatal("no %s/%s zsk key pair found for bundle %s", namestr, + algstr, timestr); + } + + /* All good, print DNSKEY RRset. */ + dns_rdatalist_tordataset(rdatalist, &rdataset); + print_rdata(&rdataset); + +fail: + /* Cleanup */ + freerrset(&rdataset); + + if (ret != ISC_R_SUCCESS) { + fatal("failed to print %s/%s zsk key pair found for bundle %s", + namestr, algstr, timestr); + } + + return (next_bundle); +} + +static void +sign_rrset(ksr_ctx_t *ksr, isc_stdtime_t inception, isc_stdtime_t expiration, + dns_rdataset_t *rrset, dns_dnsseckeylist_t *keys) { + dns_rdatalist_t *rrsiglist = NULL; + dns_rdataset_t rrsigset = DNS_RDATASET_INIT; + isc_result_t ret; + + UNUSED(ksr); + + /* Bundle header */ + if (rrset->type == dns_rdatatype_dnskey) { + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + char utc[sizeof("YYYYMMDDHHSSMM")]; + isc_buffer_t timebuf; + isc_buffer_t b; + isc_region_t r; + isc_buffer_init(&timebuf, timestr, sizeof(timestr)); + isc_stdtime_tostring(inception, timestr, sizeof(timestr)); + isc_buffer_init(&b, utc, sizeof(utc)); + ret = dns_time32_totext(inception, &b); + if (ret != ISC_R_SUCCESS) { + fatal("failed to convert bundle time32 to text: %s", + isc_result_totext(ret)); + } + isc_buffer_usedregion(&b, &r); + fprintf(stdout, ";; SignedKeyResponse 1.0 %.*s (%s)\n", + (int)r.length, r.base, timestr); + } + + /* RRset */ + print_rdata(rrset); + + /* Signatures */ + rrsiglist = isc_mem_get(mctx, sizeof(*rrsiglist)); + dns_rdatalist_init(rrsiglist); + rrsiglist->rdclass = dns_rdataclass_in; + rrsiglist->type = dns_rdatatype_rrsig; + rrsiglist->ttl = rrset->ttl; + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(*keys); dk != NULL; + dk = ISC_LIST_NEXT(dk, link)) + { + isc_buffer_t buf; + isc_buffer_t *newbuf = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t *rrsig = NULL; + isc_region_t rs; + unsigned char rdatabuf[SIG_FORMATSIZE]; + isc_stdtime_t clockskew = inception - 3600; + + rrsig = isc_mem_get(mctx, sizeof(*rrsig)); + dns_rdata_init(rrsig); + isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf)); + ret = dns_dnssec_sign(name, rrset, dk->key, &clockskew, + &expiration, mctx, &buf, &rdata); + if (ret != ISC_R_SUCCESS) { + fatal("failed to sign KSR"); + } + isc_buffer_usedregion(&buf, &rs); + isc_buffer_allocate(mctx, &newbuf, rs.length); + isc_buffer_putmem(newbuf, rs.base, rs.length); + isc_buffer_usedregion(newbuf, &rs); + dns_rdata_fromregion(rrsig, dns_rdataclass_in, + dns_rdatatype_rrsig, &rs); + ISC_LIST_APPEND(rrsiglist->rdata, rrsig, link); + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + } + dns_rdatalist_tordataset(rrsiglist, &rrsigset); + print_rdata(&rrsigset); + freerrset(&rrsigset); +} + +/* + * Create the DNSKEY, CDS, and CDNSKEY records beloing to the KSKs + * listed in 'keys'. + */ +static void +create_ksk(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys, + dns_rdataset_t *dnskeyset, dns_rdataset_t *cdnskeyset, + dns_rdataset_t *cdsset) { + dns_rdatalist_t *dnskeylist = isc_mem_get(mctx, sizeof(*dnskeylist)); + dns_rdatalist_t *cdnskeylist = isc_mem_get(mctx, sizeof(*cdnskeylist)); + dns_rdatalist_t *cdslist = isc_mem_get(mctx, sizeof(*cdslist)); + isc_result_t ret = ISC_R_SUCCESS; + dns_kasp_digestlist_t digests = dns_kasp_digests(kasp); + + dns_rdatalist_init(dnskeylist); + dnskeylist->rdclass = dns_rdataclass_in; + dnskeylist->type = dns_rdatatype_dnskey; + dnskeylist->ttl = ksr->ttl; + + dns_rdatalist_init(cdnskeylist); + cdnskeylist->rdclass = dns_rdataclass_in; + cdnskeylist->type = dns_rdatatype_cdnskey; + cdnskeylist->ttl = ksr->ttl; + + dns_rdatalist_init(cdslist); + cdslist->rdclass = dns_rdataclass_in; + cdslist->type = dns_rdatatype_cds; + cdslist->ttl = ksr->ttl; + + for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(*keys); dk != NULL; + dk = ISC_LIST_NEXT(dk, link)) + { + isc_buffer_t buf; + isc_buffer_t *newbuf; + dns_rdata_t *rdata; + isc_region_t r; + isc_region_t rcds; + unsigned char kskbuf[DST_KEY_MAXSIZE]; + unsigned char cdnskeybuf[DST_KEY_MAXSIZE]; + unsigned char cdsbuf[DNS_DS_BUFFERSIZE]; + + /* KSK */ + newbuf = NULL; + rdata = isc_mem_get(mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + + isc_buffer_init(&buf, kskbuf, sizeof(kskbuf)); + CHECK(dst_key_todns(dk->key, &buf)); + isc_buffer_usedregion(&buf, &r); + isc_buffer_allocate(mctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, dns_rdataclass_in, + dns_rdatatype_dnskey, &r); + ISC_LIST_APPEND(dnskeylist->rdata, rdata, link); + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + + /* CDNSKEY */ + newbuf = NULL; + rdata = isc_mem_get(mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + + isc_buffer_init(&buf, cdnskeybuf, sizeof(cdnskeybuf)); + CHECK(dst_key_todns(dk->key, &buf)); + isc_buffer_usedregion(&buf, &r); + isc_buffer_allocate(mctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, dns_rdataclass_in, + dns_rdatatype_cdnskey, &r); + if (dns_kasp_cdnskey(kasp)) { + ISC_LIST_APPEND(cdnskeylist->rdata, rdata, link); + } + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + + /* CDS */ + for (dns_kasp_digest_t *alg = ISC_LIST_HEAD(digests); + alg != NULL; alg = ISC_LIST_NEXT(alg, link)) + { + isc_buffer_t *newbuf2 = NULL; + dns_rdata_t *rdata2 = NULL; + dns_rdata_t cds = DNS_RDATA_INIT; + + rdata2 = isc_mem_get(mctx, sizeof(*rdata2)); + dns_rdata_init(rdata2); + + CHECK(dns_ds_buildrdata(name, rdata, alg->digest, + cdsbuf, &cds)); + cds.type = dns_rdatatype_cds; + dns_rdata_toregion(&cds, &rcds); + isc_buffer_allocate(mctx, &newbuf2, rcds.length); + isc_buffer_putmem(newbuf2, rcds.base, rcds.length); + isc_buffer_usedregion(newbuf2, &rcds); + dns_rdata_fromregion(rdata2, dns_rdataclass_in, + dns_rdatatype_cds, &rcds); + ISC_LIST_APPEND(cdslist->rdata, rdata2, link); + ISC_LIST_APPEND(cleanup_list, newbuf2, link); + isc_buffer_clear(newbuf2); + } + + if (!dns_kasp_cdnskey(kasp)) { + isc_mem_put(mctx, rdata, sizeof(*rdata)); + } + } + /* All good */ + dns_rdatalist_tordataset(dnskeylist, dnskeyset); + dns_rdatalist_tordataset(cdnskeylist, cdnskeyset); + dns_rdatalist_tordataset(cdslist, cdsset); + return; + +fail: + fatal("failed to create KSK/CDS/CDNSKEY"); +} + +static void +sign_bundle(ksr_ctx_t *ksr, isc_stdtime_t inception, + isc_stdtime_t next_inception, dns_rdatalist_t *rdatalist, + dns_rdataset_t *cds, dns_rdataset_t *cdnskey, + dns_dnsseckeylist_t *keys) { + dns_rdataset_t rrset = DNS_RDATASET_INIT; + isc_stdtime_t expiration; + + dns_rdataset_init(&rrset); + dns_rdatalist_tordataset(rdatalist, &rrset); + expiration = inception + ksr->sigvalidity; + while (inception <= next_inception) { + sign_rrset(ksr, inception, expiration, &rrset, keys); + if (dns_rdataset_count(cdnskey) > 0) { + sign_rrset(ksr, inception, expiration, cdnskey, keys); + } + if (dns_rdataset_count(cds) > 0) { + sign_rrset(ksr, inception, expiration, cds, keys); + } + inception = expiration - ksr->sigrefresh; + expiration = inception + ksr->sigvalidity; + } + freerrset(&rrset); +} + +static isc_result_t +parse_dnskey(isc_lex_t *lex, char *owner, isc_buffer_t *buf, dns_ttl_t *ttl) { + dns_fixedname_t dfname; + dns_name_t *dname = NULL; + dns_rdataclass_t rdclass = dns_rdataclass_in; + isc_buffer_t b; + isc_result_t ret; + isc_token_t token; + unsigned int opt = ISC_LEXOPT_EOL; + + isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); + + /* Read the domain name */ + if (!strcmp(owner, "@")) { + BADTOKEN(); + } + + dname = dns_fixedname_initname(&dfname); + isc_buffer_init(&b, owner, strlen(owner)); + isc_buffer_add(&b, strlen(owner)); + ret = dns_name_fromtext(dname, &b, dns_rootname, 0, NULL); + if (ret != ISC_R_SUCCESS) { + return (ret); + } + if (dns_name_compare(dname, name) != 0) { + return (DNS_R_BADOWNERNAME); + } + isc_buffer_clear(&b); + + /* Read the next word: either TTL, class, or type */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + + /* If it's a TTL, read the next one */ + ret = dns_ttl_fromtext(&token.value.as_textregion, ttl); + if (ret == ISC_R_SUCCESS) { + NEXTTOKEN(lex, opt, &token); + } + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + + /* If it's a class, read the next one */ + ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion); + if (ret == ISC_R_SUCCESS) { + NEXTTOKEN(lex, opt, &token); + } + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + + /* Must be the type */ + if (strcasecmp(STR(token), "DNSKEY") != 0) { + BADTOKEN(); + } + + ret = dns_rdata_fromtext(NULL, rdclass, dns_rdatatype_dnskey, lex, name, + 0, mctx, buf, NULL); + +cleanup: + isc_lex_setcomments(lex, 0); + return (ret); +} + +static void +keygen(ksr_ctx_t *ksr) { + dns_kasp_t *kasp = NULL; + dns_dnsseckeylist_t keys; + bool noop = true; + + /* Check parameters */ + checkparams(ksr, "keygen"); + /* Get the policy */ + getkasp(ksr, &kasp); + /* Get existing keys */ + get_dnskeys(ksr, &keys); + /* Set context */ + setcontext(ksr, kasp); + /* Key generation */ + for (dns_kasp_key_t *kk = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kk != NULL; kk = ISC_LIST_NEXT(kk, link)) + { + if (dns_kasp_key_ksk(kk)) { + /* only ZSKs allowed */ + continue; + } + ksr->alg = dns_kasp_key_algorithm(kk); + ksr->lifetime = dns_kasp_key_lifetime(kk); + ksr->keystore = dns_kasp_key_keystore(kk); + ksr->size = dns_kasp_key_size(kk); + noop = false; + + for (isc_stdtime_t inception = ksr->start, act = ksr->start; + inception < ksr->end; inception += ksr->lifetime) + { + create_zsk(ksr, kk, &keys, inception, act, &act); + if (ksr->lifetime == 0) { + /* unlimited lifetime, but not infinite loop */ + break; + } + } + } + if (noop) { + fatal("policy '%s' has no zsks", ksr->policy); + } + /* Cleanup */ + cleanup(&keys, kasp); +} + +static void +request(ksr_ctx_t *ksr) { + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + dns_dnsseckeylist_t keys; + dns_kasp_t *kasp = NULL; + isc_stdtime_t next = 0; + isc_stdtime_t inception = 0; + + /* Check parameters */ + checkparams(ksr, "request"); + /* Get the policy */ + getkasp(ksr, &kasp); + /* Get keys */ + get_dnskeys(ksr, &keys); + /* Set context */ + setcontext(ksr, kasp); + /* Create request */ + inception = ksr->start; + while (inception <= ksr->end) { + char utc[sizeof("YYYYMMDDHHSSMM")]; + isc_buffer_t b; + isc_region_t r; + isc_result_t ret; + + isc_stdtime_tostring(inception, timestr, sizeof(timestr)); + isc_buffer_init(&b, utc, sizeof(utc)); + ret = dns_time32_totext(inception, &b); + if (ret != ISC_R_SUCCESS) { + fatal("failed to convert bundle time32 to text: %s", + isc_result_totext(ret)); + } + isc_buffer_usedregion(&b, &r); + fprintf(stdout, ";; KeySigningRequest 1.0 %.*s (%s)\n", + (int)r.length, r.base, timestr); + + next = ksr->end + 1; + for (dns_kasp_key_t *kk = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kk != NULL; kk = ISC_LIST_NEXT(kk, link)) + { + /* + * Output the DNSKEY records for the current bundle + * that starts at 'inception. The 'next' variable is + * updated to the start time of the + * next bundle, determined by the earliest publication + * or withdrawal of a key that is after the current + * inception. + */ + if (dns_kasp_key_ksk(kk)) { + /* We only want ZSKs in the request. */ + continue; + } + + next = print_dnskeys(kk, ksr->ttl, &keys, inception, + next); + } + inception = next; + } + + isc_stdtime_tostring(ksr->now, timestr, sizeof(timestr)); + fprintf(stdout, ";; KeySigningRequest 1.0 generated at %s by %s\n", + timestr, PACKAGE_VERSION); + + /* Cleanup */ + cleanup(&keys, kasp); +} + +static void +sign(ksr_ctx_t *ksr) { + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + bool have_bundle = false; + dns_dnsseckeylist_t keys; + dns_kasp_t *kasp = NULL; + dns_rdatalist_t *rdatalist = NULL; + dns_rdataset_t ksk = DNS_RDATASET_INIT; + dns_rdataset_t cdnskey = DNS_RDATASET_INIT; + dns_rdataset_t cds = DNS_RDATASET_INIT; + isc_result_t ret; + isc_stdtime_t inception; + isc_lex_t *lex = NULL; + isc_lexspecials_t specials; + isc_token_t token; + unsigned int opt = ISC_LEXOPT_EOL; + + /* Check parameters */ + checkparams(ksr, "sign"); + if (ksr->file == NULL) { + fatal("'sign' requires a KSR file"); + } + /* Get the policy */ + getkasp(ksr, &kasp); + /* Get keys */ + get_dnskeys(ksr, &keys); + /* Set context */ + setcontext(ksr, kasp); + /* Sign request */ + inception = ksr->start; + isc_lex_create(mctx, KSR_LINESIZE, &lex); + memset(specials, 0, sizeof(specials)); + specials['('] = 1; + specials[')'] = 1; + specials['"'] = 1; + isc_lex_setspecials(lex, specials); + ret = isc_lex_openfile(lex, ksr->file); + if (ret != ISC_R_SUCCESS) { + fatal("unable to open KSR file %s: %s", ksr->file, + isc_result_totext(ret)); + } + + /* KSK, CDS and CDNSKEY */ + create_ksk(ksr, kasp, &keys, &ksk, &cdnskey, &cds); + + for (ret = isc_lex_gettoken(lex, opt, &token); ret == ISC_R_SUCCESS; + ret = isc_lex_gettoken(lex, opt, &token)) + { + if (token.type != isc_tokentype_string) { + fatal("bad KSR file %s(%lu): syntax error", ksr->file, + isc_lex_getsourceline(lex)); + } + + if (strcmp(STR(token), ";;") == 0) { + char bundle[KSR_LINESIZE]; + isc_stdtime_t next_inception; + + CHECK(isc_lex_gettoken(lex, opt, &token)); + if (token.type != isc_tokentype_string || + strcmp(STR(token), "KeySigningRequest") != 0) + { + fatal("bad KSR file %s(%lu): expected " + "'KeySigningRequest'", + ksr->file, isc_lex_getsourceline(lex)); + } + + CHECK(isc_lex_gettoken(lex, opt, &token)); + if (token.type != isc_tokentype_string) { + fatal("bad KSR file %s(%lu): expected string", + ksr->file, isc_lex_getsourceline(lex)); + } + + if (strcmp(STR(token), "1.0") != 0) { + fatal("bad KSR file %s(%lu): expected version", + ksr->file, isc_lex_getsourceline(lex)); + } + + CHECK(isc_lex_gettoken(lex, opt, &token)); + if (token.type != isc_tokentype_string) { + fatal("bad KSR file %s(%lu): expected datetime", + ksr->file, isc_lex_getsourceline(lex)); + } + if (strcmp(STR(token), "generated") == 0) { + /* Final bundle */ + goto readline; + } + + /* Date and time of bundle */ + sscanf(STR(token), "%s", bundle); + next_inception = strtotime(bundle, ksr->now, ksr->now, + NULL); + + if (have_bundle) { + /* Sign previous bundle */ + sign_bundle(ksr, inception, next_inception, + rdatalist, &cds, &cdnskey, &keys); + fprintf(stdout, "\n"); + } + + /* Start next bundle */ + rdatalist = isc_mem_get(mctx, sizeof(*rdatalist)); + dns_rdatalist_init(rdatalist); + rdatalist->rdclass = dns_rdataclass_in; + rdatalist->type = dns_rdatatype_dnskey; + rdatalist->ttl = ksr->ttl; + for (isc_result_t r = dns_rdatalist_first(&ksk); + r == ISC_R_SUCCESS; r = dns_rdatalist_next(&ksk)) + { + dns_rdata_t *clone = + isc_mem_get(mctx, sizeof(*clone)); + dns_rdata_init(clone); + dns_rdatalist_current(&ksk, clone); + ISC_LIST_APPEND(rdatalist->rdata, clone, link); + } + inception = next_inception; + have_bundle = true; + + readline: + /* Read remainder of header line */ + do { + ret = isc_lex_gettoken(lex, opt, &token); + if (ret != ISC_R_SUCCESS) { + fatal("bad KSR file %s(%lu): bad " + "header (%s)", + ksr->file, + isc_lex_getsourceline(lex), + isc_result_totext(ret)); + } + } while (token.type != isc_tokentype_eol); + } else { + /* Parse DNSKEY */ + dns_ttl_t ttl = ksr->ttl; + isc_buffer_t buf; + isc_buffer_t *newbuf = NULL; + dns_rdata_t *rdata = NULL; + isc_region_t r; + u_char rdatabuf[DST_KEY_MAXSIZE]; + + INSIST(rdatalist != NULL); + + rdata = isc_mem_get(mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf)); + ret = parse_dnskey(lex, STR(token), &buf, &ttl); + if (ret != ISC_R_SUCCESS) { + fatal("bad KSR file %s(%lu): bad DNSKEY (%s)", + ksr->file, isc_lex_getsourceline(lex), + isc_result_totext(ret)); + } + isc_buffer_usedregion(&buf, &r); + isc_buffer_allocate(mctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, dns_rdataclass_in, + dns_rdatatype_dnskey, &r); + if (rdatalist != NULL && ttl < rdatalist->ttl) { + rdatalist->ttl = ttl; + } + + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + } + } + + if (ret != ISC_R_EOF) { + fatal("bad KSR file %s(%lu): trailing garbage data", ksr->file, + isc_lex_getsourceline(lex)); + } + + /* Final bundle */ + if (have_bundle && rdatalist != NULL) { + sign_bundle(ksr, inception, ksr->end, rdatalist, &cds, &cdnskey, + &keys); + } else { + fatal("bad KSR file %s(%lu): no bundles", ksr->file, + isc_lex_getsourceline(lex)); + } + + /* Bundle footer */ + isc_stdtime_tostring(ksr->now, timestr, sizeof(timestr)); + fprintf(stdout, ";; SignedKeyResponse 1.0 generated at %s by %s\n", + timestr, PACKAGE_VERSION); + +fail: + /* Clean up */ + freerrset(&ksk); + freerrset(&cdnskey); + freerrset(&cds); + + isc_lex_destroy(&lex); + cleanup(&keys, kasp); +} + +int +main(int argc, char *argv[]) { + isc_result_t ret; + isc_buffer_t buf; + int ch; + char *endp; + bool set_fips_mode = false; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 + OSSL_PROVIDER *fips = NULL, *base = NULL; +#endif + ksr_ctx_t ksr = { + .now = isc_stdtime_now(), + }; + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + +#define OPTIONS "E:e:Ff:hi:K:k:l:v:V" + while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { + switch (ch) { + case 'E': + engine = isc_commandline_argument; + break; + case 'e': + ksr.end = strtotime(isc_commandline_argument, ksr.now, + ksr.now, &ksr.setend); + break; + case 'F': + set_fips_mode = true; + break; + case 'f': + ksr.file = isc_commandline_argument; + break; + case 'h': + usage(0); + break; + case 'i': + ksr.start = strtotime(isc_commandline_argument, ksr.now, + ksr.now, &ksr.setstart); + break; + case 'K': + ksr.keydir = isc_commandline_argument; + ret = try_dir(ksr.keydir); + if (ret != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", + ksr.keydir, isc_result_totext(ret)); + } + break; + case 'k': + ksr.policy = isc_commandline_argument; + break; + case 'l': + ksr.configfile = isc_commandline_argument; + break; + case 'V': + version(program); + break; + case 'v': + verbose = strtoul(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + default: + usage(1); + break; + } + } + argv += isc_commandline_index; + argc -= isc_commandline_index; + + if (argc != 2) { + fatal("must provide a command and zone name"); + } + + ret = dst_lib_init(mctx, engine); + if (ret != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", isc_result_totext(ret)); + } + + /* + * After dst_lib_init which will set FIPS mode if requested + * at build time. The minumums are both raised to 2048. + */ + if (isc_fips_mode()) { + min_rsa = min_dh = 2048; + } + + setup_logging(mctx, &lctx); + + if (set_fips_mode) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 + fips = OSSL_PROVIDER_load(NULL, "fips"); + if (fips == NULL) { + fatal("Failed to load FIPS provider"); + } + base = OSSL_PROVIDER_load(NULL, "base"); + if (base == NULL) { + OSSL_PROVIDER_unload(fips); + fatal("Failed to load base provider"); + } +#endif + if (!isc_fips_mode()) { + if (isc_fips_set_mode(1) != ISC_R_SUCCESS) { + fatal("setting FIPS mode failed"); + } + } + } + + /* zone */ + namestr = argv[1]; + name = dns_fixedname_initname(&fname); + isc_buffer_init(&buf, argv[1], strlen(argv[1])); + isc_buffer_add(&buf, strlen(argv[1])); + ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + if (ret != ISC_R_SUCCESS) { + fatal("invalid zone name %s: %s", argv[1], + isc_result_totext(ret)); + } + + /* command */ + if (strcmp(argv[0], "keygen") == 0) { + keygen(&ksr); + } else if (strcmp(argv[0], "request") == 0) { + request(&ksr); + } else if (strcmp(argv[0], "sign") == 0) { + sign(&ksr); + } else { + fatal("unknown command '%s'", argv[0]); + } + + exit(0); +} diff --git a/bin/dnssec/dnssec-ksr.rst b/bin/dnssec/dnssec-ksr.rst new file mode 100644 index 0000000000..1ed7275aca --- /dev/null +++ b/bin/dnssec/dnssec-ksr.rst @@ -0,0 +1,162 @@ +.. 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:: dnssec-ksr +.. program:: dnssec-ksr +.. _man_dnssec-ksr: + +dnssec-ksr - Create signed key response (SKR) files for offline KSK setups +-------------------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-ksr` [**-E** engine] [**-e** date/offset] [**-F**] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-V**] [**-v** level] {command} {zone} + +Description +~~~~~~~~~~~ + +The :program:`dnssec-ksr` can be used to issue several commands that are needed +to generate presigned RRsets for a zone where the private key file of the Key +Signing Key (KSK) is typically offline. This requires Zone Signing Keys +(ZSKs) to be pregenerated, and the DNSKEY, CDNSKEY, and CDS RRsets to be +already signed in advance. + +The latter is done by creating Key Signing Requests (KSRs) that can be imported +to the environment where the KSK is available. Once there, this program can +create Signed Key Responses (SKRs) that can be loaded by an authoritative DNS +server. + +Options +~~~~~~~ + +.. option:: -E engine + + This option specifies the cryptographic hardware to use, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -e date/offset + + This option sets the end date for which keys or SKRs need to be generated + (depending on the command). + +.. option:: -F + + This options turns on FIPS (US Federal Information Processing Standards) + mode if the underlying crytographic library supports running in FIPS + mode. + +.. option:: -h + + This option prints a short summary of the options and arguments to + :program:`dnssec-ksr`. + +.. option:: -i date/offset + + This option sets the start date for which keys or SKRs need to be generated + (depending on the command). + +.. option:: -K directory + + This option sets the directory in which the key files are to be read or + written (depending on the command). + +.. option:: -k policy + + This option sets the specific ``dnssec-policy`` for which keys need to + be generated, or signed. + +.. option:: -l file + + This option provides a configuration file that contains a ``dnssec-policy`` + statement (matching the policy set with :option:`-k`). + +.. option:: -V + + This option prints version information. + +.. option:: -v level + + This option sets the debugging level. Level 1 is intended to be usefully + verbose for general users; higher levels are intended for developers. + +``command`` + + The KSR command to be executed. See below for the available commands. + +``zone`` + + The name of the zone for which the KSR command is being executed. + +Commands +~~~~~~~~ + +.. option:: keygen + + Pregenerate a number of zone signing keys (ZSKs), given a DNSSEC policy and + an interval. The number of generated keys depends on the interval and the + ZSK lifetime. + +.. option:: request + + Create a Key Signing Request (KSR), given a DNSSEC policy and an interval. + This will generate a file with a number of key bundles, where each bundle + contains the currently published ZSKs (according to the timing metadata). + +.. option:: sign + + Sign a Key Signing Request (KSR), given a DNSSEC policy and an interval, + creating a Signed Key Response (SKR). This will add the corresponding DNSKEY, + CDS, and CDNSKEY records for the KSK that is being used for signing. + +Exit Status +~~~~~~~~~~~ + +The :program:`dnssec-ksr` command exits 0 on success, or non-zero if an error +occurred. + +Examples +~~~~~~~~ + +When you need to generate keys for the zone "example.com" for the next year, +given a ``dnssec-policy`` named "mypolicy": + +:: + + dnssec-ksr -i now -e +1y -k mypolicy -l named.conf keygen example.com + +Creating a KSR for the same zone and period can be done with: + +:: + + dnssec-ksr -i now -e +1y -k mypolicy -l named.conf request example.com > ksr.txt + +Typically you would now transfer the KSR to the system that has access to the KSK. + +Signing the KSR created above can be done with: + +:: + + dnssec-ksr -i now -e +1y -k kskpolicy -l named.conf -f ksr.txt sign example.com + +Make sure that the DNSSEC parameters in ``kskpolicy`` match those in ``mypolicy``. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, +:iscman:`dnssec-signzone(8) `, +BIND 9 Administrator Reference Manual. diff --git a/bin/dnssec/dnssec-revoke.c b/bin/dnssec/dnssec-revoke.c index ada83fd96a..cf7f2b74de 100644 --- a/bin/dnssec/dnssec-revoke.c +++ b/bin/dnssec/dnssec-revoke.c @@ -58,7 +58,7 @@ usage(void) { fprintf(stderr, " K++.key, " "K++.private\n"); - exit(-1); + exit(EXIT_FAILURE); } int @@ -130,7 +130,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c index fcd8c0cb53..f19d0781b5 100644 --- a/bin/dnssec/dnssec-settime.c +++ b/bin/dnssec/dnssec-settime.c @@ -101,7 +101,7 @@ usage(void) { fprintf(stderr, " K++.key, " "K++.private\n"); - exit(-1); + exit(EXIT_FAILURE); } static void @@ -536,7 +536,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index e158e8ce77..094d905ba3 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -183,6 +183,8 @@ static bool set_maxttl = false; static dns_ttl_t maxttl = 0; static bool no_max_check = false; static const char *sync_records = "cdnskey,cds:sha-256"; +static bool defer_signing = false; + #define INCSTAT(counter) \ if (printstats) { \ @@ -275,13 +277,13 @@ lock_and_dumpnode(dns_name_t *name, dns_dbnode_t *node) { UNLOCK(&namelock); } -/*% +/* * Sign the given RRset with given key, and add the signature record to the * given tuple. */ static void signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, - dns_ttl_t ttl, dns_diff_t *add, const char *logmsg) { + dns_ttl_t ttl, dns_diff_t *add, const char *logmsg, bool check_now) { isc_result_t result; isc_stdtime_t jendtime, expiry; char keystr[DST_KEY_FORMATSIZE]; @@ -298,7 +300,6 @@ signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, } else { expiry = endtime; } - jendtime = (jitter != 0) ? expiry - isc_random_uniform(jitter) : expiry; isc_buffer_init(&b, array, sizeof(array)); result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime, @@ -309,7 +310,9 @@ signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, } INCSTAT(nsigned); - if (tryverify) { + // With deferred signing we need to wait until the signature is finalized + // before we can test it if verifies. + if (tryverify && check_now) { result = dns_dnssec_verify(name, rdataset, key, true, 0, mctx, &trdata, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { @@ -490,7 +493,7 @@ setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, */ static void signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, - dns_rdataset_t *set) { + dns_rdataset_t *set, bool check_now) { dns_rdataset_t sigset; dns_rdata_t sigrdata = DNS_RDATA_INIT; dns_rdata_rrsig_t rrsig; @@ -543,7 +546,6 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, } else { result = dns_rdataset_first(&sigset); } - while (result == ISC_R_SUCCESS) { bool expired, future; bool keep = false, resign = false; @@ -660,9 +662,8 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, if (resign) { INSIST(!keep); - signwithkey(name, set, key->key, ttl, add, - "resigning with dnskey"); + "resigning with dnskey", check_now); nowsignedby[key->index] = true; } @@ -717,8 +718,10 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, if (isksk(key) || !have_ksk || (iszsk(key) && !keyset_kskonly)) { - signwithkey(name, set, key->key, ttl, add, - "signing with dnskey"); + if (!defer_signing || !iszsk(key) || (set->type == dns_rdatatype_dnskey && !dst_key_is_deferred_signing(key->key))) { + signwithkey(name, set, key->key, ttl, add, + "signing with dnskey", check_now); + } } } else if (iszsk(key)) { /* @@ -778,8 +781,10 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, * skip signing with this key. */ if (!have_pre_sig) { - signwithkey(name, set, key->key, ttl, add, - "signing with dnskey"); + if (set->type != dns_rdatatype_rrsig) { + signwithkey(name, set, key->key, ttl, add, + "signing with dnskey", check_now); + } } } } @@ -1000,8 +1005,8 @@ opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass, } isc_buffer_putuint8(&b, 0); - result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone, - rdclass, 0, NULL, dbp); + result = dns_db_create(mctx, ZONEDB_DEFAULT, dns_rootname, + dns_dbtype_zone, rdclass, 0, NULL, dbp); check_result(result, "dns_db_create()"); result = dns_db_load(*dbp, filename, inputformat, DNS_MASTER_HINT); @@ -1169,7 +1174,7 @@ has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { * Signs all records at a name. */ static void -signname(dns_dbnode_t *node, dns_name_t *name) { +signname(dns_dbnode_t *node, bool apex, dns_name_t *name) { isc_result_t result; dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter; @@ -1200,7 +1205,8 @@ signname(dns_dbnode_t *node, dns_name_t *name) { dns_rdatasetiter_current(rdsiter, &rdataset); /* If this is a RRSIG set, skip it. */ - if (rdataset.type == dns_rdatatype_rrsig) { + if (rdataset.type == dns_rdatatype_rrsig || + (defer_signing && rdataset.type == dns_rdatatype_dnskey)) { goto skip; } @@ -1220,9 +1226,13 @@ signname(dns_dbnode_t *node, dns_name_t *name) { dns_name_format(name, namebuf, sizeof(namebuf)); fatal("'%s': found DS RRset without NS RRset\n", namebuf); + } else if (rdataset.type == dns_rdatatype_dnskey && !apex) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + fatal("'%s': Non-apex DNSKEY RRset\n", namebuf); } - signset(&del, &add, node, name, &rdataset); + signset(&del, &add, node, name, &rdataset, !defer_signing); skip: dns_rdataset_disassociate(&rdataset); @@ -1539,8 +1549,10 @@ signapex(void) { check_result(result, "dns_dbiterator_seek()"); result = dns_dbiterator_current(gdbiter, &node, name); check_dns_dbiterator_current(result); - signname(node, name); - dumpnode(name, node); + signname(node, true, name); + if (!defer_signing) { + dumpnode(name, node); + } dns_db_detachnode(gdb, &node); result = dns_dbiterator_first(gdbiter); if (result == ISC_R_NOMORE) { @@ -1643,7 +1655,9 @@ assignwork(void *arg) { } if (!found) { - dumpnode(name, node); + if (!defer_signing) { + dumpnode(name, node); + } dns_db_detachnode(gdb, &node); } @@ -1668,15 +1682,17 @@ assignwork(void *arg) { UNLOCK(&namelock); - signname(node, dns_fixedname_name(&fname)); + signname(node, false, dns_fixedname_name(&fname)); /*% * Write a node to the output file, and restart the worker task. */ - lock_and_dumpnode(dns_fixedname_name(&fname), node); + if (!defer_signing) { + lock_and_dumpnode(dns_fixedname_name(&fname), node); + } dns_db_detachnode(gdb, &node); - isc_async_current(loopmgr, assignwork, NULL); + isc_async_current(assignwork, NULL); } /*% @@ -2574,8 +2590,8 @@ loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) { isc_result_totext(result)); } - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, db); check_result(result, "dns_db_create()"); result = dns_db_load(*db, file, inputformat, 0); @@ -2641,7 +2657,7 @@ loadzonekeys(bool preserve_keys, bool load_public) { /* Load keys corresponding to the existing DNSKEY RRset. */ result = dns_dnssec_keylistfromrdataset( - gorigin, directory, mctx, &rdataset, &keysigs, &soasigs, + gorigin, NULL, directory, mctx, &rdataset, &keysigs, &soasigs, preserve_keys, load_public, &keylist); if (result != ISC_R_SUCCESS) { fatal("failed to load the zone keys: %s", @@ -2832,8 +2848,8 @@ build_final_keylist(void) { /* * Find keys that match this zone in the key repository. */ - result = dns_dnssec_findmatchingkeys(gorigin, directory, now, mctx, - &matchkeys); + result = dns_dnssec_findmatchingkeys(gorigin, NULL, directory, NULL, + now, mctx, &matchkeys); if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; } @@ -3164,8 +3180,8 @@ writeset(const char *prefix, dns_rdatatype_t type) { dns_diff_append(&diff, &tuple); } - result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone, - gclass, 0, NULL, &db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, dns_rootname, + dns_dbtype_zone, gclass, 0, NULL, &db); check_result(result, "dns_db_create"); result = dns_db_newversion(db, &dbversion); @@ -3302,7 +3318,7 @@ usage(void) { fprintf(stderr, "(default: all zone keys that have private keys)\n"); fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n"); - exit(0); + exit(EXIT_FAILURE); } static void @@ -3346,6 +3362,378 @@ print_stats(isc_time_t *timer_start, isc_time_t *timer_finish, (unsigned int)(time_ms / 1000), (unsigned int)(time_ms % 1000)); } +static void +finalize_node_rrsigs(dns_dbnode_t *node, dns_name_t *name, dns_diff_t *add, dns_diff_t *del, dns_dnsseckey_t *key, uint16_t old_keytag) { + isc_result_t result; + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdataset_t rdataset; + dns_rdataset_init(&rdataset); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + + /* Iterate over only rrsigs and update them to include the authpath. */ + if (rdataset.type != dns_rdatatype_rrsig) { + goto skip; + } + isc_result_t rrsigresult; + for (rrsigresult = dns_rdataset_first(&rdataset); + rrsigresult == ISC_R_SUCCESS; + rrsigresult = dns_rdataset_next(&rdataset)) { + dns_rdata_t rrsig_rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t rrsig; + dns_rdataset_current(&rdataset, &rrsig_rdata); + if (rrsig_rdata.type == dns_rdatatype_rrsig) { + rrsigresult = dns_rdata_tostruct(&rrsig_rdata, &rrsig, NULL); + if (rrsigresult == ISC_R_SUCCESS) { + if (rrsig.keyid == old_keytag) { + char data[4096]; + isc_buffer_t databuf; + dns_difftuple_t *addtuple = NULL; + dns_difftuple_t *deltuple = NULL; + dns_rdata_t new_rrsig_rdata = DNS_RDATA_INIT; + isc_buffer_init(&databuf, data, sizeof(data)); + check_result(dst_key_signature_finalize(key->key, &databuf, &rrsig_rdata, &new_rrsig_rdata), "signature finalize"); + if (tryverify) { + dns_rdataset_t target_rdataset; + dns_rdataset_init(&target_rdataset); + rrsigresult = dns_db_findrdataset(gdb, node, gversion, rrsig.covered, + 0, 0, &target_rdataset, NULL); + check_result(rrsigresult, "find RRSIG's covered rdataset"); + + rrsigresult = dns_dnssec_verify(name, &target_rdataset, key->key, true, 0, mctx, + &new_rrsig_rdata, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { + vbprintf(3, "\tsignature verified\n"); + INCSTAT(nverified); + } else { + vbprintf(3, "\tsignature failed to verify\n"); + INCSTAT(nverifyfailed); + } + dns_rdataset_disassociate(&target_rdataset); + } + result = dns_difftuple_create(mctx, + DNS_DIFFOP_ADDRESIGN, name, + rdataset.ttl, &new_rrsig_rdata, &addtuple); + check_result(result, "dns_difftuple_create"); + result = dns_difftuple_create(mctx, + DNS_DIFFOP_DELRESIGN, name, + rdataset.ttl, &rrsig_rdata, &deltuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(add, &addtuple); + dns_diff_append(del, &deltuple); + } + dns_rdata_freestruct(&rrsig); + } + } + } + skip: + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + dns_rdatasetiter_destroy(&rdsiter); +} + +static void +finalize_rrsigs(dns_diff_t *add, dns_diff_t *del, dns_dnsseckey_t *key, uint16_t old_keytag) { + dns_dbnode_t *node = NULL; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + dns_dbiterator_t *dbiter = NULL; + result = dns_db_createiterator(gdb, 0, &dbiter); + check_result(result, "dns_db_createiterator()"); + name = dns_fixedname_initname(&fixed); + result = dns_dbiterator_seek(dbiter, gorigin); + check_result(result, "dns_dbiterator_seek()"); + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + finalize_node_rrsigs(node, name, add, del, key, old_keytag); + dns_db_detachnode(gdb, &node); + result = dns_dbiterator_first(dbiter); + dns_fixedname_t fname; + + result = ISC_R_SUCCESS; + while (result == ISC_R_SUCCESS) { + name = dns_fixedname_initname(&fname); + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + if (dns_name_equal(name, gorigin)) { + goto next; + } + finalize_node_rrsigs(node, name, add, del, key, old_keytag); + next: + result = dns_dbiterator_next(dbiter); + dns_db_detachnode(gdb, &node); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + fatal("failure iterating database: %s", + isc_result_totext(result)); + } + } + dns_dbiterator_destroy(&dbiter); +} + +static void +defered_finalize_signing(dns_dbnode_t *node, dns_name_t *name) { + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdatasetiter_t *rdsiter; + dns_diff_t del, add; + char namestr[DNS_NAME_FORMATSIZE]; + + dns_rdataset_init(&rdataset); + dns_name_format(name, namestr, sizeof(namestr)); + + /* + * Now iterate through the rdatasets. + */ + dns_diff_init(mctx, &del); + dns_diff_init(mctx, &add); + rdsiter = NULL; + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + + /* Now that the all other signatures are signed, finalize defer signing + * key and sign DNSKEY set. */ + if (rdataset.type != dns_rdatatype_dnskey) { + goto finalize_skip; + } + + // Finalize all keys located in this set + isc_result_t keyresult; + for (keyresult = dns_rdataset_first(&rdataset); + keyresult == ISC_R_SUCCESS; + keyresult = dns_rdataset_next(&rdataset)) { + dns_rdata_t key_rdata = DNS_RDATA_INIT; + dns_rdata_dnskey_t dnskey; + dns_rdataset_current(&rdataset, &key_rdata); + if (key_rdata.type == dns_rdatatype_dnskey) { + isc_region_t key_r; + key_r.base = key_rdata.data; + key_r.length = key_rdata.length; + uint16_t keytag = dst_region_computeid(&key_r); + keyresult = dns_rdata_tostruct(&key_rdata, &dnskey, NULL); + if (keyresult == ISC_R_SUCCESS) { + if (dst_algorithm_is_deferred_signing(dnskey.algorithm)) { + dns_dnsseckey_t *key = NULL; + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (dst_key_id(key->key) == keytag && + dst_key_alg(key->key) == dnskey.algorithm) + { + dns_difftuple_t *addtuple = NULL; + dns_difftuple_t *deltuple = NULL; + unsigned char data[4096]; + isc_buffer_t buffer; + isc_region_t r; + + isc_buffer_init(&buffer, data, sizeof(data)); + check_result(dst_key_finalize(key->key), "dst_key_finalize"); + dns_rdata_t new_dnskey_rdata = DNS_RDATA_INIT; + dns_rdata_t old_dnskey_rdata = DNS_RDATA_INIT; + + dns_rdata_clone(&key_rdata, &old_dnskey_rdata); + result = dst_key_todns(key->key, &buffer); + check_result(result, "dst_key_todns"); + isc_buffer_usedregion(&buffer, &r); + dns_rdata_fromregion(&new_dnskey_rdata, key_rdata.rdclass, dns_rdatatype_dnskey, &r); + + result = dns_difftuple_create(mctx, + DNS_DIFFOP_ADDRESIGN, name, + rdataset.ttl, &new_dnskey_rdata, &addtuple); + check_result(result, "dns_difftuple_create"); + result = dns_difftuple_create(mctx, + DNS_DIFFOP_DELRESIGN, name, + rdataset.ttl, &old_dnskey_rdata, &deltuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(&add, &addtuple); + dns_diff_append(&del, &deltuple); + break; + } + } + if (key != NULL) { + finalize_rrsigs(&add, &del, key, keytag); + } + } + dns_rdata_freestruct(&dnskey); + } + } + } + + finalize_skip: + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration for name '%s' failed: %s", namestr, + isc_result_totext(result)); + } + + dns_rdatasetiter_destroy(&rdsiter); + result = dns_diff_applysilently(&del, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to delete SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + + result = dns_diff_applysilently(&add, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to add SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + + dns_diff_clear(&del); + dns_diff_clear(&add); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + + /* Now that the all other signatures are signed, finalize defer signing + * key and sign DNSKEY set. */ + if (rdataset.type != dns_rdatatype_dnskey) { + goto sign_skip; + } + + + signset(&del, &add, node, name, &rdataset, true); + + sign_skip: + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + + dns_rdatasetiter_destroy(&rdsiter); + result = dns_diff_applysilently(&del, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to delete SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + + result = dns_diff_applysilently(&add, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to add SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + dns_diff_clear(&del); + dns_diff_clear(&add); +} + +static void +defered_finalize(void) { + dns_dbnode_t *node = NULL; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + name = dns_fixedname_initname(&fixed); + result = dns_dbiterator_seek(gdbiter, gorigin); + check_result(result, "dns_dbiterator_seek()"); + result = dns_dbiterator_current(gdbiter, &node, name); + check_dns_dbiterator_current(result); + defered_finalize_signing(node, name); + dumpnode(name, node); + dns_db_detachnode(gdb, &node); + result = dns_dbiterator_first(gdbiter); + dns_fixedname_t fname; + dns_rdataset_t nsec; + bool found; + static dns_name_t *zonecut = NULL; /* Protected by namelock. */ + static dns_fixedname_t fzonecut; /* Protected by namelock. */ + + result = ISC_R_SUCCESS; + while (result == ISC_R_SUCCESS) { + name = dns_fixedname_initname(&fname); + found = false; + while (!found) { + result = dns_dbiterator_current(gdbiter, &node, name); + check_dns_dbiterator_current(result); + if (dns_name_equal(name, gorigin)) { + dns_db_detachnode(gdb, &node); + goto next; + } + /* + * Sort the zone data from the glue and out-of-zone data. + * For NSEC zones nodes with zone data have NSEC records. + * For NSEC3 zones the NSEC3 nodes are zone data but + * outside of the zone name space. For the rest we need + * to track the bottom of zone cuts. + * Nodes which don't need to be signed are dumped here. + */ + dns_rdataset_init(&nsec); + result = dns_db_findrdataset(gdb, node, gversion, nsec_datatype, + 0, 0, &nsec, NULL); + if (dns_rdataset_isassociated(&nsec)) { + dns_rdataset_disassociate(&nsec); + } + if (result == ISC_R_SUCCESS) { + found = true; + } else if (nsec_datatype == dns_rdatatype_nsec3) { + if (dns_name_issubdomain(name, gorigin) && + (zonecut == NULL || + !dns_name_issubdomain(name, zonecut))) + { + if (is_delegation(gdb, gversion, gorigin, name, + node, NULL)) + { + zonecut = savezonecut(&fzonecut, name); + if (!OPTOUT(nsec3flags) || + secure(name, node)) + { + found = true; + } + } else if (has_dname(gdb, gversion, node)) { + zonecut = savezonecut(&fzonecut, name); + found = true; + } else { + found = true; + } + } + } + + if (!found) { + dumpnode(name, node); + dns_db_detachnode(gdb, &node); + } + + next: + result = dns_dbiterator_next(gdbiter); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + fatal("failure iterating database: %s", + isc_result_totext(result)); + } + } + if (!dns_name_equal(name, gorigin)) { + defered_finalize_signing(node, dns_fixedname_name(&fname)); + /*% + * Write a node to the output file, and restart the worker task. + */ + dumpnode(dns_fixedname_name(&fname), node); + } + dns_db_detachnode(gdb, &node); + + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + fatal("failure iterating database: %s", + isc_result_totext(result)); + } + } + +} + int main(int argc, char *argv[]) { int ch; @@ -3497,7 +3885,7 @@ main(int argc, char *argv[]) { set_iter = true; /* too-many is NOT DOCUMENTED */ if (strcmp(isc_commandline_argument, "too-many") == 0) { - nsec3iter = 151; + nsec3iter = 51; no_max_check = true; break; } @@ -3553,7 +3941,7 @@ main(int argc, char *argv[]) { if (*endp != '\0') { fprintf(stderr, "source serial number " "must be numeric"); - exit(1); + exit(EXIT_FAILURE); } break; @@ -3568,7 +3956,7 @@ main(int argc, char *argv[]) { if (*endp != '\0') { fprintf(stderr, "maximum TTL " "must be numeric"); - exit(1); + exit(EXIT_FAILURE); } break; @@ -3692,7 +4080,7 @@ main(int argc, char *argv[]) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -3840,7 +4228,7 @@ main(int argc, char *argv[]) { rawversion > 1U) { fprintf(stderr, "unknown raw format version\n"); - exit(1); + exit(EXIT_FAILURE); } } else { fatal("unknown file format: %s", outputformatstr); @@ -3950,6 +4338,9 @@ main(int argc, char *argv[]) { key = ISC_LIST_NEXT(key, link)) { key->index = keycount++; + if (dst_key_is_deferred_signing(key->key)) { + defer_signing = true; + } } if (keycount == 0) { @@ -4085,6 +4476,10 @@ main(int argc, char *argv[]) { fatal("process aborted by user"); } } + if (defer_signing) { + // Finalize and dump everything + defered_finalize(); + } postsign(); sign_finish = isc_time_now(); diff --git a/bin/dnssec/dnssec-signzone.rst b/bin/dnssec/dnssec-signzone.rst index e6fb455c3d..5c2f1d6b45 100644 --- a/bin/dnssec/dnssec-signzone.rst +++ b/bin/dnssec/dnssec-signzone.rst @@ -273,7 +273,7 @@ Options with cached copies of the old DNSKEY RRset. The :option:`-Q` option forces :program:`dnssec-signzone` to remove signatures from keys that are no longer active. This enables ZSK rollover using the procedure described in - :rfc:`4641#4.2.1.1` ("Pre-Publish Key Rollover"). + :rfc:`6781#4.1.1.1` ("Pre-Publish Key Rollover"). .. option:: -q @@ -290,7 +290,7 @@ Options This option is similar to :option:`-Q`, except it forces :program:`dnssec-signzone` to remove signatures from keys that are no longer published. This enables ZSK rollover using the procedure described in - :rfc:`4641#4.2.1.2` ("Double Signature Zone Signing Key + :rfc:`6781#4.1.1.2` ("Double Signature Zone Signing Key Rollover"). .. option:: -S @@ -374,6 +374,7 @@ Options .. note:: ``-3 -`` is the recommended configuration. Adding salt provides no practical benefits. + See :rfc:`9276`. .. option:: -H iterations @@ -382,6 +383,7 @@ Options .. warning:: Values greater than 0 cause interoperability issues and also increase the risk of CPU-exhausting DoS attacks. + See :rfc:`9276`. .. option:: -A @@ -390,6 +392,7 @@ Options .. warning:: Do not use this option unless all its implications are fully understood. This option is intended only for extremely large zones (comparable to ``com.``) with sparse secure delegations. + See :rfc:`9276`. .. option:: -AA @@ -443,4 +446,4 @@ See Also ~~~~~~~~ :iscman:`dnssec-keygen(8) `, BIND 9 Administrator Reference Manual, :rfc:`4033`, -:rfc:`4641`. +:rfc:`6781`. diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c index e6b8972b02..c4ce4eadf7 100644 --- a/bin/dnssec/dnssec-verify.c +++ b/bin/dnssec/dnssec-verify.c @@ -109,8 +109,8 @@ loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) { isc_result_totext(result)); } - result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, - NULL, db); + result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, + rdclass, 0, NULL, db); check_result(result, "dns_db_create()"); result = dns_db_load(*db, file, inputformat, 0); @@ -162,7 +162,7 @@ usage(void) { fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, " "not ZSKs\n"); fprintf(stderr, "\t-z:\tAll records signed with KSKs\n"); - exit(0); + exit(EXIT_SUCCESS); } int @@ -271,7 +271,7 @@ main(int argc, char *argv[]) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index 85e2955185..e34c972c67 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -83,8 +84,7 @@ fatal(const char *format, ...) { if (fatalcallback != NULL) { (*fatalcallback)(); } - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } void @@ -114,7 +114,7 @@ vbprintf(int level, const char *fmt, ...) { void version(const char *name) { printf("%s %s\n", name, PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); } void @@ -498,7 +498,8 @@ key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, alg = dst_key_alg(dstkey); ISC_LIST_INIT(matchkeys); - result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys); + result = dns_dnssec_findmatchingkeys(name, NULL, dir, NULL, now, mctx, + &matchkeys); if (result == ISC_R_NOTFOUND) { return (false); } @@ -600,3 +601,88 @@ loadjournal(isc_mem_t *mctx, dns_db_t *db, const char *file) { cleanup: dns_journal_destroy(&jnl); } + +void +kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *lctx, + const char *name, const char *keydir, const char *engine, + dns_kasp_t **kaspp) { + isc_result_t result = ISC_R_NOTFOUND; + const cfg_listelt_t *element; + const cfg_obj_t *kasps = NULL; + dns_kasp_t *kasp = NULL, *kasp_next; + dns_kasplist_t kasplist; + const cfg_obj_t *keystores = NULL; + dns_keystore_t *ks = NULL, *ks_next; + dns_keystorelist_t kslist; + + ISC_LIST_INIT(kasplist); + ISC_LIST_INIT(kslist); + + (void)cfg_map_get(config, "key-store", &keystores); + for (element = cfg_list_first(keystores); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + ks = NULL; + result = cfg_keystore_fromconfig(kconfig, mctx, lctx, engine, + &kslist, NULL); + if (result != ISC_R_SUCCESS) { + fatal("failed to configure key-store '%s': %s", + cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + isc_result_totext(result)); + } + } + /* Default key-directory key store. */ + ks = NULL; + (void)cfg_keystore_fromconfig(NULL, mctx, lctx, engine, &kslist, &ks); + INSIST(ks != NULL); + if (keydir != NULL) { + /* '-K keydir' takes priority */ + dns_keystore_setdirectory(ks, keydir); + } + dns_keystore_detach(&ks); + + (void)cfg_map_get(config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + kasp = NULL; + if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + name) != 0) + { + continue; + } + + result = cfg_kasp_fromconfig(kconfig, NULL, true, mctx, lctx, + &kslist, &kasplist, &kasp); + if (result != ISC_R_SUCCESS) { + fatal("failed to configure dnssec-policy '%s': %s", + cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + isc_result_totext(result)); + } + INSIST(kasp != NULL); + dns_kasp_freeze(kasp); + break; + } + + *kaspp = kasp; + + /* + * Cleanup kasp list. + */ + for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(kasplist, kasp, link); + dns_kasp_detach(&kasp); + } + + /* + * Cleanup keystore list. + */ + for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { + ks_next = ISC_LIST_NEXT(ks, link); + ISC_LIST_UNLINK(kslist, ks, link); + dns_keystore_detach(&ks); + } +} diff --git a/bin/dnssec/dnssectool.h b/bin/dnssec/dnssectool.h index 5bc69cd14d..c9a1a5f757 100644 --- a/bin/dnssec/dnssectool.h +++ b/bin/dnssec/dnssectool.h @@ -20,10 +20,18 @@ #include #include +#include #include #include +#include +#include +#include + +#define MAX_RSA 4096 /* should be long enough... */ +#define MAX_DH 4096 /* should be long enough... */ + /*! verbosity: set by -v and -q option in each program, defined in dnssectool.c */ extern int verbose; @@ -108,3 +116,8 @@ isoptarg(const char *arg, char **argv, void (*usage)(void)); void loadjournal(isc_mem_t *mctx, dns_db_t *db, const char *journal); + +void +kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *lctx, + const char *name, const char *keydir, const char *engine, + dns_kasp_t **kaspp); diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl index 91f59440d2..ac15b7c934 100644 --- a/bin/named/bind9.xsl +++ b/bin/named/bind9.xsl @@ -303,6 +303,7 @@ Status, Server, Zones, + Incoming Zone Transfers, Network, Memory and Traffic Size

@@ -907,6 +908,63 @@ + + +

Incoming Zone Transfers for View

+ + + + + + + + + + + + + + + + + + + + + + + + + + + even + odd + + + + + + + + + + + + + + + + + + + + + + + +
Zone NameZone TypeLocal SerialRemote SerialIXFRFirst RefreshStateAdditional Refresh QueuedLocal AddressRemote AddressSOA TransportTransportTSIG Key NameDuration (s)Messages ReceivedRecords ReceivedBytes Received
+
+

Memory Usage Summary

diff --git a/bin/named/builtin.c b/bin/named/builtin.c index 1f46f0ad4f..9c446e7c2f 100644 --- a/bin/named/builtin.c +++ b/bin/named/builtin.c @@ -529,13 +529,17 @@ authors_lookup(bdbnode_t *node) { isc_result_t result; const char **p = NULL; static const char *authors[] = { - "Mark Andrews", "Curtis Blackburn", "James Brister", - "Ben Cottrell", "John H. DuBois III", "Francis Dupont", - "Michael Graff", "Andreas Gustafsson", "Bob Halley", - "Evan Hunt", "JINMEI Tatuya", "Witold Krecicki", - "David Lawrence", "Scott Mann", "Danny Mayer", - "Damien Neil", "Matt Nelson", "Jeremy C. Reed", - "Michael Sawyer", "Brian Wellington", NULL + "Mark Andrews", "Curtis Blackburn", + "James Brister", "Ben Cottrell", + "John H. DuBois III", "Francis Dupont", + "Michael Graff", "Andreas Gustafsson", + "Bob Halley", "Evan Hunt", + "JINMEI Tatuya", "Witold Krecicki", + "David Lawrence", "Scott Mann", + "Danny Mayer", "Aydin Mercan", + "Damien Neil", "Matt Nelson", + "Jeremy C. Reed", "Michael Sawyer", + "Brian Wellington", NULL }; /* diff --git a/bin/named/config.c b/bin/named/config.c index ed43f33f92..94d7610a55 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -67,7 +67,6 @@ options {\n\ interface-interval 60;\n\ listen-on {any;};\n\ listen-on-v6 {any;};\n\ -# lock-file \"" NAMED_LOCALSTATEDIR "/run/named/named.lock\";\n\ match-mapped-addresses no;\n\ max-ixfr-ratio 100%;\n\ max-rsa-exponent-size 0; /* no limit */\n\ @@ -133,6 +132,8 @@ options {\n\ /* view */\n\ allow-new-zones no;\n\ allow-notify {none;};\n\ + allow-proxy {none;};\n\ + allow-proxy-on {any;};\n\ allow-query-cache { localnets; localhost; };\n\ allow-query-cache-on { any; };\n\ allow-recursion { localnets; localhost; };\n\ @@ -188,8 +189,6 @@ options {\n\ request-expire true;\n\ request-ixfr true;\n\ require-server-cookie no;\n\ - resolver-nonbackoff-tries 3;\n\ - resolver-retry-interval 800; /* in milliseconds */\n\ root-key-sentinel yes;\n\ servfail-ttl 1;\n\ # sortlist \n\ @@ -201,13 +200,14 @@ options {\n\ synth-from-dnssec yes;\n\ # topology \n\ transfer-format many-answers;\n\ + resolver-use-dns64 false;\n\ v6-bias 50;\n\ zero-no-soa-ttl-cache no;\n\ \n\ /* zone */\n\ allow-query {any;};\n\ allow-query-on {any;};\n\ - allow-transfer {any;};\n\ + allow-transfer {none;};\n\ # also-notify \n\ check-integrity yes;\n\ check-mx-cname warn;\n\ @@ -298,6 +298,7 @@ dnssec-policy \"default\" {\n\ publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\ retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\ purge-keys " DNS_KASP_PURGE_KEYS "; \n\ + signatures-jitter " DNS_KASP_SIG_JITTER "; \n\ signatures-refresh " DNS_KASP_SIG_REFRESH "; \n\ signatures-validity " DNS_KASP_SIG_VALIDITY "; \n\ signatures-validity-dnskey " DNS_KASP_SIG_VALIDITY_DNSKEY "; \n\ @@ -327,14 +328,14 @@ dnssec-policy \"insecure\" {\n\ "# END TRUST ANCHORS\n\ \n\ primaries " DEFAULT_IANA_ROOT_ZONE_PRIMARIES " {\n\ - 2001:500:200::b; # b.root-servers.net\n\ + 2801:1b8:10::b; # b.root-servers.net\n\ 2001:500:2::c; # c.root-servers.net\n\ 2001:500:2f::f; # f.root-servers.net\n\ 2001:500:12::d0d; # g.root-servers.net\n\ 2001:7fd::1; # k.root-servers.net\n\ 2620:0:2830:202::132; # xfr.cjr.dns.icann.org\n\ 2620:0:2d0:202::132; # xfr.lax.dns.icann.org\n\ - 199.9.14.201; # b.root-servers.net\n\ + 170.247.170.2; # b.root-servers.net\n\ 192.33.4.12; # c.root-servers.net\n\ 192.5.5.241; # f.root-servers.net\n\ 192.112.36.4; # g.root-servers.net\n\ diff --git a/bin/named/control.c b/bin/named/control.c index 7f07db1240..a3e009799e 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -177,6 +177,7 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly, /* Do not flush master files */ named_server_flushonshutdown(named_g_server, false); named_os_shutdownmsg(cmdline, *text); + isc_loopmgr_shutdown(named_g_loopmgr); result = ISC_R_SHUTTINGDOWN; } else if (command_compare(command, NAMED_COMMAND_STOP)) { /* @@ -194,6 +195,7 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly, #endif /* ifdef HAVE_LIBSCF */ named_server_flushonshutdown(named_g_server, true); named_os_shutdownmsg(cmdline, *text); + isc_loopmgr_shutdown(named_g_loopmgr); result = ISC_R_SHUTTINGDOWN; } else if (command_compare(command, NAMED_COMMAND_ADDZONE) || command_compare(command, NAMED_COMMAND_MODZONE)) diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 57783d19af..e276497f9e 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -49,7 +49,7 @@ #include #include -#undef NAMED_CONTROLCONF_TRACE +/* Add -DNAMED_CONTROLCONF_TRACE=1 to CFLAGS for detailed reference tracing */ typedef struct controlkey controlkey_t; typedef ISC_LIST(controlkey_t) controlkeylist_t; @@ -91,10 +91,9 @@ struct controllistener { isc_sockaddr_t address; isc_nmsocket_t *sock; dns_acl_t *acl; - bool exiting; + bool shuttingdown; isc_refcount_t references; controlkeylist_t keys; - isc_mutex_t connections_lock; controlconnectionlist_t connections; isc_socktype_t type; uint32_t perm; @@ -120,6 +119,8 @@ static void conn_cleanup(controlconnection_t *conn); static void conn_free(controlconnection_t *conn); +static void +conn_shutdown(controlconnection_t *conn); #if NAMED_CONTROLCONF_TRACE #define controllistener_ref(ptr) \ @@ -148,6 +149,14 @@ ISC_REFCOUNT_DECL(controlconnection); #define CLOCKSKEW 300 +#define CHECK(x) \ + { \ + result = (x); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } + static void free_controlkey(controlkey_t *key, isc_mem_t *mctx) { if (key->keyname != NULL) { @@ -170,11 +179,8 @@ free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { static void free_listener(controllistener_t *listener) { - INSIST(listener->exiting); - INSIST(ISC_LIST_EMPTY(listener->connections)); - - isc_refcount_destroy(&listener->references); - + REQUIRE(listener->shuttingdown); + REQUIRE(ISC_LIST_EMPTY(listener->connections)); REQUIRE(listener->sock == NULL); free_controlkeylist(&listener->keys, listener->mctx); @@ -182,7 +188,6 @@ free_listener(controllistener_t *listener) { if (listener->acl != NULL) { dns_acl_detach(&listener->acl); } - isc_mutex_destroy(&listener->connections_lock); isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); } @@ -197,33 +202,33 @@ ISC_REFCOUNT_IMPL(controlconnection, conn_free); static void shutdown_listener(controllistener_t *listener) { - if (!listener->exiting) { - char socktext[ISC_SOCKADDR_FORMATSIZE]; + controlconnection_t *conn = NULL; + controlconnection_t *next = NULL; - for (controlconnection_t *conn = - ISC_LIST_HEAD(listener->connections); - conn != NULL; conn = ISC_LIST_HEAD(listener->connections)) - { - control_recvmessage(conn->ccmsg.handle, - ISC_R_SHUTTINGDOWN, conn); - } - - ISC_LIST_UNLINK(listener->controls->listeners, listener, link); + /* Don't shutdown the same listener twice */ + if (listener->shuttingdown) { + return; + } + listener->shuttingdown = true; - isc_sockaddr_format(&listener->address, socktext, - sizeof(socktext)); - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, - "stopping command channel on %s", socktext); -#if 0 - /* XXX: no unix domain socket support */ - if (listener->type == isc_socktype_unix) { - isc_socket_cleanunix(&listener->address, true); - } -#endif - listener->exiting = true; + for (conn = ISC_LIST_HEAD(listener->connections); conn != NULL; + conn = next) + { + /* + * 'conn' is likely to be freed by the conn_shutdown() call. + */ + next = ISC_LIST_NEXT(conn, link); + conn_shutdown(conn); } + ISC_LIST_UNLINK(listener->controls->listeners, listener, link); + + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&listener->address, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, + "stopping command channel on %s", socktext); + isc_nm_stoplistening(listener->sock); isc_nmsocket_close(&listener->sock); controllistener_detach(&listener); @@ -237,11 +242,6 @@ address_ok(isc_sockaddr_t *sockaddr, controllistener_t *listener) { isc_result_t result; int match; - /* ACL doesn't apply to unix domain sockets */ - if (listener->type != isc_socktype_tcp) { - return (true); - } - isc_netaddr_fromsockaddr(&netaddr, sockaddr); result = dns_acl_match(&netaddr, NULL, listener->acl, env, &match, @@ -252,33 +252,36 @@ address_ok(isc_sockaddr_t *sockaddr, controllistener_t *listener) { static void control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { controlconnection_t *conn = (controlconnection_t *)arg; - controllistener_t *listener = conn->listener; - isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle); - if (conn->result == ISC_R_SHUTTINGDOWN) { - isc_loopmgr_shutdown(named_g_loopmgr); - goto cleanup_sendhandle; + if (conn->shuttingdown) { + /* The connection is shuttingdown */ + result = ISC_R_SHUTTINGDOWN; + } + + if (result == ISC_R_SUCCESS) { + /* Everything is peachy, continue reading from the socket */ + isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, + conn); + /* Detach the sending reference */ + controlconnection_detach(&conn); + return; } - if (listener->controls->shuttingdown || result == ISC_R_SHUTTINGDOWN) { - goto cleanup_sendhandle; - } else if (result != ISC_R_SUCCESS) { + if (result != ISC_R_SHUTTINGDOWN) { char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle); isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, "error sending command response to %s: %s", socktext, isc_result_totext(result)); - goto cleanup_sendhandle; } - isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn); + /* Shutdown the reading */ + conn_shutdown(conn); -cleanup_sendhandle: - if (result != ISC_R_SUCCESS) { - control_recvmessage(handle, result, conn); - } + /* Detach the sending reference */ controlconnection_detach(&conn); } @@ -385,6 +388,7 @@ control_respond(controlconnection_t *conn) { r.base = conn->buffer->base; r.length = conn->buffer->used; + /* Attach the sending reference */ controlconnection_ref(conn); isccc_ccmsg_sendmessage(&conn->ccmsg, &r, control_senddone, conn); @@ -395,13 +399,33 @@ control_respond(controlconnection_t *conn) { static void control_command(void *arg) { controlconnection_t *conn = (controlconnection_t *)arg; - controllistener_t *listener = conn->listener; - if (!listener->controls->shuttingdown) { + /* Don't run the command if we already started the shutdown */ + if (!conn->shuttingdown) { conn->result = named_control_docommand( - conn->request, listener->readonly, &conn->text); + conn->request, conn->listener->readonly, &conn->text); control_respond(conn); } + + /* Detach the control command reference */ + controlconnection_detach(&conn); +} + +static void +conn_shutdown(controlconnection_t *conn) { + /* Don't shutdown the same controlconnection twice */ + if (conn->shuttingdown) { + return; + } + conn->shuttingdown = true; + + /* + * Close the TCP connection to make sure that no read callback will be + * called for it ever again. + */ + isccc_ccmsg_disconnect(&conn->ccmsg); + + /* Detach the reading reference */ controlconnection_detach(&conn); } @@ -415,21 +439,7 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, isccc_time_t exp; uint32_t nonce; - if (conn->shuttingdown) { - return; - } - - /* Is the server shutting down? */ - if (listener->controls->shuttingdown) { - result = ISC_R_SHUTTINGDOWN; - } - if (result != ISC_R_SUCCESS) { - if (result == ISC_R_SHUTTINGDOWN) { - listener->controls->shuttingdown = true; - } else if (result != ISC_R_EOF) { - log_invalid(&conn->ccmsg, result); - } goto cleanup; } @@ -455,13 +465,13 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, } if (key == NULL) { - log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); + result = ISCCC_R_BADAUTH; goto cleanup; } /* We shouldn't be getting a reply. */ if (isccc_cc_isreply(conn->request)) { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); + result = ISC_R_FAILURE; goto cleanup; } @@ -472,7 +482,7 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, */ conn->ctrl = isccc_alist_lookup(conn->request, "_ctrl"); if (!isccc_alist_alistp(conn->ctrl)) { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); + result = ISC_R_FAILURE; goto cleanup; } @@ -480,11 +490,11 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, if ((sent + CLOCKSKEW) < conn->now || (sent - CLOCKSKEW) > conn->now) { - log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); + result = ISCCC_R_CLOCKSKEW; goto cleanup; } } else { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); + result = ISC_R_FAILURE; goto cleanup; } @@ -494,7 +504,7 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, if (isccc_cc_lookupuint32(conn->ctrl, "_exp", &exp) == ISC_R_SUCCESS && conn->now > exp) { - log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); + result = ISCCC_R_EXPIRED; goto cleanup; } @@ -510,7 +520,6 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, if (result == ISC_R_EXISTS) { result = ISCCC_R_DUPLICATE; } - log_invalid(&conn->ccmsg, result); goto cleanup; } @@ -519,7 +528,7 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, ISC_R_SUCCESS || conn->nonce != nonce)) { - log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); + result = ISCCC_R_BADAUTH; goto cleanup; } @@ -537,40 +546,48 @@ control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, return; } - /* - * Trigger the command. - */ + /* Attach the command reference */ controlconnection_ref(conn); + + /* Trigger the command asynchronously. */ isc_async_run(named_g_mainloop, control_command, conn); return; cleanup: - conn->shuttingdown = true; - controlconnection_detach(&conn); + switch (result) { + case ISC_R_SHUTTINGDOWN: + case ISC_R_EOF: + break; + default: + log_invalid(&conn->ccmsg, result); + } + + conn_shutdown(conn); } static void conn_free(controlconnection_t *conn) { + /* Make sure that the connection was shutdown first */ + REQUIRE(conn->shuttingdown); + controllistener_t *listener = conn->listener; + isccc_ccmsg_invalidate(&conn->ccmsg); + conn_cleanup(conn); if (conn->buffer != NULL) { isc_buffer_free(&conn->buffer); } - LOCK(&listener->connections_lock); ISC_LIST_UNLINK(listener->connections, conn, link); - UNLOCK(&listener->connections_lock); #ifdef ENABLE_AFL if (named_g_fuzz_type == isc_fuzz_rndc) { named_fuzz_notify(); } #endif /* ifdef ENABLE_AFL */ - isccc_ccmsg_invalidate(&conn->ccmsg); - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), "freeing control connection"); @@ -582,15 +599,26 @@ conn_free(controlconnection_t *conn) { static void newconnection(controllistener_t *listener, isc_nmhandle_t *handle) { + /* Don't create new connection if we are shutting down */ + if (listener->shuttingdown) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), + "rejected new control connection: %s", + isc_result_totext(ISC_R_SHUTTINGDOWN)); + return; + } + controlconnection_t *conn = isc_mem_get(listener->mctx, sizeof(*conn)); isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), "allocate new control connection"); - *conn = (controlconnection_t){ .alg = DST_ALG_UNKNOWN }; - - isc_refcount_init(&conn->references, 1); - controllistener_attach(listener, &conn->listener); + *conn = (controlconnection_t){ + .alg = DST_ALG_UNKNOWN, + .references = ISC_REFCOUNT_INITIALIZER(1), + .listener = controllistener_ref(listener), + .link = ISC_LINK_INITIALIZER, + }; /* isccc_ccmsg_init() attaches to the handle */ isccc_ccmsg_init(listener->mctx, handle, &conn->ccmsg); @@ -598,10 +626,9 @@ newconnection(controllistener_t *listener, isc_nmhandle_t *handle) { /* Set a 32 KiB upper limit on incoming message. */ isccc_ccmsg_setmaxsize(&conn->ccmsg, 32768); - LOCK(&listener->connections_lock); - ISC_LIST_INITANDAPPEND(listener->connections, conn, link); - UNLOCK(&listener->connections_lock); + ISC_LIST_APPEND(listener->connections, conn, link); + /* The reading reference has been initialized in the initializer */ isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn); } @@ -641,8 +668,7 @@ controls_shutdown(named_controls_t *controls) { listener = next) { /* - * This is asynchronous. As listeners shut down, they will - * call their callbacks. + * As listeners shut down, they will call their callbacks. */ next = ISC_LIST_NEXT(listener, link); shutdown_listener(listener); @@ -651,8 +677,20 @@ controls_shutdown(named_controls_t *controls) { void named_controls_shutdown(named_controls_t *controls) { - controls_shutdown(controls); + /* + * Don't ever shutdown the controls twice. + * + * NOTE: This functions is called when the server is shutting down, but + * controls_shutdown() can and will be called multiple times - on each + * reconfiguration, the listeners will be torn down and recreated again, + * see named_controls_configure() for details. + */ + if (controls->shuttingdown) { + return; + } controls->shuttingdown = true; + + controls_shutdown(controls); } static isc_result_t @@ -781,14 +819,6 @@ register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, } } -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - static isc_result_t get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { isc_result_t result; @@ -1031,33 +1061,6 @@ update_listener(named_controls_t *cp, controllistener_t **listenerp, socktext, isc_result_totext(result)); } -#if 0 - /* XXX: no unix socket support yet */ - if (result == ISC_R_SUCCESS && type == isc_socktype_unix) { - uint32_t perm, owner, group; - perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); - owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner")); - group = cfg_obj_asuint32(cfg_tuple_get(control, "group")); - result = ISC_R_SUCCESS; - if (listener->perm != perm || listener->owner != owner || - listener->group != group) - { - result = isc_socket_permunix(&listener->address, perm, - owner, group); - } - if (result == ISC_R_SUCCESS) { - listener->perm = perm; - listener->owner = owner; - listener->group = group; - } else if (control != NULL) { - cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, - "couldn't update ownership/permission for " - "command channel %s", - socktext); - } - } -#endif - *listenerp = listener; } @@ -1075,12 +1078,17 @@ add_listener(named_controls_t *cp, controllistener_t **listenerp, isc_result_t result = ISC_R_SUCCESS; int pf; + /* Don't create new listener if we are shutting down */ + if (cp->shuttingdown) { + result = ISC_R_SHUTTINGDOWN; + goto shuttingdown; + } + listener = isc_mem_get(mctx, sizeof(*listener)); *listener = (controllistener_t){ .controls = cp, .address = *addr, .type = type }; isc_mem_attach(mctx, &listener->mctx); - isc_mutex_init(&listener->connections_lock); ISC_LINK_INIT(listener, link); ISC_LIST_INIT(listener->keys); ISC_LIST_INIT(listener->connections); @@ -1129,35 +1137,14 @@ add_listener(named_controls_t *cp, controllistener_t **listenerp, pf = isc_sockaddr_pf(&listener->address); if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || - (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) || (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) { CHECK(ISC_R_FAMILYNOSUPPORT); } -#if 0 - /* XXX: no unix socket support yet */ - if (type == isc_socktype_unix) { - isc_socket_cleanunix(&listener->address, false); - } -#endif - CHECK(isc_nm_listentcp(named_g_netmgr, ISC_NM_LISTEN_ONE, &listener->address, control_newconn, listener, 5, NULL, &listener->sock)); -#if 0 - /* XXX: no unix socket support yet */ - if (type == isc_socktype_unix) { - listener->perm = - cfg_obj_asuint32(cfg_tuple_get(control, "perm")); - listener->owner = - cfg_obj_asuint32(cfg_tuple_get(control, "owner")); - listener->group = - cfg_obj_asuint32(cfg_tuple_get(control, "group")); - result = isc_socket_permunix(&listener->address, listener->perm, - listener->owner, listener->group); - } -#endif isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, @@ -1167,9 +1154,10 @@ add_listener(named_controls_t *cp, controllistener_t **listenerp, cleanup: isc_refcount_decrement(&listener->references); - listener->exiting = true; + listener->shuttingdown = true; free_listener(listener); +shuttingdown: if (control != NULL) { cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, "couldn't add command channel %s: %s", socktext, @@ -1214,8 +1202,19 @@ named_controls_configure(named_controls_t *cp, const cfg_obj_t *config, { const cfg_obj_t *controls = NULL; const cfg_obj_t *inetcontrols = NULL; + const cfg_obj_t *unixcontrols = NULL; controls = cfg_listelt_value(element); + + (void)cfg_map_get(controls, "unix", &unixcontrols); + if (unixcontrols != NULL) { + cfg_obj_log(controls, named_g_lctx, + ISC_LOG_ERROR, + "UNIX domain sockets are not " + "supported"); + return (ISC_R_FAILURE); + } + (void)cfg_map_get(controls, "inet", &inetcontrols); if (inetcontrols == NULL) { continue; @@ -1231,8 +1230,8 @@ named_controls_configure(named_controls_t *cp, const cfg_obj_t *config, /* * The parser handles BIND 8 configuration file - * syntax, so it allows unix phrases as well - * inet phrases with no keys{} clause. + * syntax, so it allows inet phrases with no + * keys{} clause. */ control = cfg_listelt_value(element2); @@ -1280,91 +1279,6 @@ named_controls_configure(named_controls_t *cp, const cfg_obj_t *config, } } } - for (element = cfg_list_first(controlslist); element != NULL; - element = cfg_list_next(element)) - { - const cfg_obj_t *controls = NULL; - const cfg_obj_t *unixcontrols = NULL; - - controls = cfg_listelt_value(element); - (void)cfg_map_get(controls, "unix", &unixcontrols); - if (unixcontrols == NULL) { - continue; - } - - cfg_obj_log(controls, named_g_lctx, ISC_LOG_ERROR, - "UNIX domain sockets not yet supported"); - return (ISC_R_FAILURE); - -#if 0 - /* XXX: no unix domain socket support in netmgr */ - for (element2 = cfg_list_first(unixcontrols); - element2 != NULL; - element2 = cfg_list_next(element2)) - { - const cfg_obj_t *control = NULL; - const cfg_obj_t *path = NULL; - isc_sockaddr_t addr; - isc_result_t result; - - /* - * The parser handles BIND 8 configuration file - * syntax, so it allows unix phrases as well - * inet phrases with no keys{} clause. - */ - control = cfg_listelt_value(element2); - - path = cfg_tuple_get(control, "path"); - result = isc_sockaddr_frompath( - &addr, cfg_obj_asstring(path)); - if (result != ISC_R_SUCCESS) { - isc_log_write( - named_g_lctx, - NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_CONTROL, - ISC_LOG_DEBUG(9), - "control channel '%s': %s", - cfg_obj_asstring(path), - isc_result_totext(result)); - continue; - } - - isc_log_write(named_g_lctx, - NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_CONTROL, - ISC_LOG_DEBUG(9), - "processing control channel '%s'", - cfg_obj_asstring(path)); - - update_listener(cp, &listener, control, config, - &addr, aclconfctx, - cfg_obj_asstring(path), - isc_socktype_unix); - - if (listener != NULL) { - /* - * Remove the listener from the old - * list, so it won't be shut down. - */ - ISC_LIST_UNLINK(cp->listeners, listener, - link); - } else { - /* - * This is a new listener. - */ - add_listener(cp, &listener, control, - config, &addr, aclconfctx, - cfg_obj_asstring(path), - isc_socktype_unix); - } - - if (listener != NULL) { - ISC_LIST_APPEND(new_listeners, listener, - link); - } - } -#endif - } } else { int i; @@ -1461,6 +1375,7 @@ named_controls_destroy(named_controls_t **ctrlsp) { named_controls_t *controls = *ctrlsp; *ctrlsp = NULL; + REQUIRE(controls->shuttingdown); REQUIRE(ISC_LIST_EMPTY(controls->listeners)); LOCK(&controls->symtab_lock); diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index 03e35f2cd4..0fc26f212d 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -50,7 +50,6 @@ EXTERN isc_mem_t *named_g_mctx INIT(NULL); EXTERN unsigned int named_g_cpus INIT(0); -EXTERN unsigned int named_g_udpdisp INIT(0); EXTERN isc_loop_t *named_g_mainloop INIT(NULL); EXTERN isc_loopmgr_t *named_g_loopmgr INIT(NULL); EXTERN bool named_g_loopmgr_running INIT(false); @@ -119,11 +118,6 @@ EXTERN const char *named_g_logfile INIT(NULL); EXTERN const char *named_g_defaultsessionkeyfile INIT(NAMED_LOCALSTATEDIR "/run/named/" "session.key"); -EXTERN const char *named_g_defaultlockfile INIT(NAMED_LOCALSTATEDIR "/run/" - "named/" - "named." - "lock"); -EXTERN bool named_g_forcelock INIT(false); #if NAMED_RUN_PID_DIR EXTERN const char *named_g_defaultpidfile INIT(NAMED_LOCALSTATEDIR "/run/named/" diff --git a/bin/named/include/named/os.h b/bin/named/include/named/os.h index 0f7c1c5385..6066fc391d 100644 --- a/bin/named/include/named/os.h +++ b/bin/named/include/named/os.h @@ -56,9 +56,6 @@ named_os_openfile(const char *filename, mode_t mode, bool switch_user); void named_os_writepidfile(const char *filename, bool first_time); -bool -named_os_issingleton(const char *filename); - void named_os_shutdown(void); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 509101f277..52a13d5658 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -65,6 +65,7 @@ struct named_server { dns_zonemgr_t *zonemgr; dns_viewlist_t viewlist; dns_kasplist_t kasplist; + dns_keystorelist_t keystorelist; ns_interfacemgr_t *interfacemgr; dns_db_t *in_roothints; @@ -103,8 +104,6 @@ struct named_server { dns_dtenv_t *dtenv; /*%< Dnstap environment */ - char *lockfile; - isc_tlsctx_cache_t *tlsctx_server_cache; isc_tlsctx_cache_t *tlsctx_client_cache; diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h index dbecd4a79e..1eb059b25a 100644 --- a/bin/named/include/named/zoneconf.h +++ b/bin/named/include/named/zoneconf.h @@ -28,8 +28,8 @@ ISC_LANG_BEGINDECLS isc_result_t named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_kasplist_t *kasplist, dns_zone_t *zone, - dns_zone_t *raw); + dns_kasplist_t *kasplist, dns_keystorelist_t *keystores, + dns_zone_t *zone, dns_zone_t *raw); /*%< * Configure or reconfigure a zone according to the named.conf * data. diff --git a/bin/named/main.c b/bin/named/main.c index 7877da2414..7e028e7e83 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -196,7 +196,7 @@ named_main_earlyfatal(const char *format, ...) { } va_end(args); - exit(1); + _exit(EXIT_FAILURE); } noreturn static void @@ -235,7 +235,7 @@ assertion_failed(const char *file, int line, isc_assertiontype_t type, if (named_g_coreok) { abort(); } - exit(1); + _exit(EXIT_FAILURE); } noreturn static void @@ -275,7 +275,7 @@ library_fatal_error(const char *file, int line, const char *func, if (named_g_coreok) { abort(); } - exit(1); + _exit(EXIT_FAILURE); } static void @@ -314,7 +314,7 @@ usage(void) { "[-p port] [-s]\n" " [-S sockets] [-t chrootdir] [-u " "username] [-U listeners]\n" - " [-X lockfile] [-m " + " [-m " "{usage|trace|record|size|mctx}]\n" " [-M fill|nofill]\n" "usage: named [-v|-V|-C]\n"); @@ -668,7 +668,6 @@ printversion(bool verbose) { printf(" rndc configuration: %s\n", rndcconf); printf(" nsupdate session key: %s\n", named_g_defaultsessionkeyfile); printf(" named PID file: %s\n", named_g_defaultpidfile); - printf(" named lock file: %s\n", named_g_defaultlockfile); #if defined(HAVE_GEOIP2) #define RTC(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS) RTC(cfg_parser_create(mctx, named_g_lctx, &parser)); @@ -890,7 +889,7 @@ parse_command_line(int argc, char *argv[]) { printf("# Built-in default values. " "This is NOT the run-time configuration!\n"); printf("%s", named_config_getdefault()); - exit(0); + exit(EXIT_SUCCESS); case 'd': named_g_debuglevel = parse_int(isc_commandline_argument, "debug " @@ -946,30 +945,24 @@ parse_command_line(int argc, char *argv[]) { parse_T_opt(isc_commandline_argument); break; case 'U': - named_g_udpdisp = parse_int(isc_commandline_argument, - "number of UDP listeners " - "per interface"); + /* Obsolete. No longer in use. Ignore. */ + named_main_earlywarning("option '-U' has been removed"); break; case 'u': named_g_username = isc_commandline_argument; break; case 'v': printversion(false); - exit(0); + exit(EXIT_SUCCESS); case 'V': printversion(true); - exit(0); + exit(EXIT_SUCCESS); case 'x': /* Obsolete. No longer in use. Ignore. */ break; case 'X': - named_g_forcelock = true; - if (strcasecmp(isc_commandline_argument, "none") != 0) { - named_g_defaultlockfile = - isc_commandline_argument; - } else { - named_g_defaultlockfile = NULL; - } + /* Obsolete. No longer in use. Abort. */ + named_main_earlyfatal("option '-X' has been removed"); break; case 'F': #if OPENSSL_VERSION_NUMBER >= 0x30200000L && OPENSSL_API_LEVEL >= 30200 @@ -998,7 +991,7 @@ parse_command_line(int argc, char *argv[]) { case '?': usage(); if (isc_commandline_option == '?') { - exit(0); + exit(EXIT_SUCCESS); } p = strchr(NAMED_MAIN_ARGS, isc_commandline_option); if (p == NULL || *++p != ':') { @@ -1041,16 +1034,6 @@ create_managers(void) { ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s", named_g_cpus_detected, named_g_cpus_detected == 1 ? "" : "s", named_g_cpus, named_g_cpus == 1 ? "" : "s"); - if (named_g_udpdisp == 0) { - named_g_udpdisp = named_g_cpus_detected; - } - if (named_g_udpdisp > named_g_cpus) { - named_g_udpdisp = named_g_cpus; - } - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, - "using %u UDP listener%s per interface", named_g_udpdisp, - named_g_udpdisp == 1 ? "" : "s"); isc_managers_create(&named_g_mctx, named_g_cpus, &named_g_loopmgr, &named_g_netmgr); diff --git a/bin/named/named.rst b/bin/named/named.rst index 698ffcdfd1..ee893faba7 100644 --- a/bin/named/named.rst +++ b/bin/named/named.rst @@ -21,7 +21,7 @@ named - Internet domain name server Synopsis ~~~~~~~~ -:program:`named` [ [**-4**] | [**-6**] ] [**-c** config-file] [**-C**] [**-d** debug-level] [**-D** string] [**-E** engine-name] [**-f**] [**-g**] [**-L** logfile] [**-M** option] [**-m** flag] [**-n** #cpus] [**-p** port] [**-s**] [**-t** directory] [**-U** #listeners] [**-u** user] [**-v**] [**-V**] [**-X** lock-file] +:program:`named` [ [**-4**] | [**-6**] ] [**-c** config-file] [**-C**] [**-d** debug-level] [**-D** string] [**-E** engine-name] [**-f**] [**-g**] [**-L** logfile] [**-M** option] [**-m** flag] [**-n** #cpus] [**-p** port] [**-s**] [**-t** directory] [**-u** user] [**-v**] [**-V**] ] Description ~~~~~~~~~~~ @@ -163,14 +163,7 @@ Options .. option:: -U #listeners - This option tells :program:`named` the number of ``#listeners`` worker threads to listen on, for incoming UDP packets on - each address. If not specified, :program:`named` calculates a default - value based on the number of detected CPUs: 1 for 1 CPU, and the - number of detected CPUs minus one for machines with more than 1 CPU. - This cannot be increased to a value higher than the number of CPUs. - If :option:`-n` has been set to a higher value than the number of detected - CPUs, then :option:`-U` may be increased as high as that value, but no - higher. + This option has been removed. Attempts to use it now result in a warning. .. option:: -u user @@ -198,10 +191,7 @@ Options .. option:: -X lock-file - This option acquires a lock on the specified file at runtime; this helps to - prevent duplicate :program:`named` instances from running simultaneously. - Use of this option overrides the ``lock-file`` option in - :iscman:`named.conf`. If set to ``none``, the lock file check is disabled. + This option has been removed and using it will cause a fatal error. Signals ~~~~~~~ diff --git a/bin/named/os.c b/bin/named/os.c index 858ccbff3f..9efc114479 100644 --- a/bin/named/os.c +++ b/bin/named/os.c @@ -51,9 +51,7 @@ #endif /* ifdef HAVE_LIBSCF */ static char *pidfile = NULL; -static char *lockfile = NULL; static int devnullfd = -1; -static int singletonfd = -1; #ifndef ISC_FACILITY #define ISC_FACILITY LOG_DAEMON @@ -430,10 +428,10 @@ named_os_daemonize(void) { char buf; n = read(dfd[0], &buf, 1); if (n == 1) { - _exit(0); + _exit(EXIT_SUCCESS); } } while (n == -1 && errno == EINTR); - _exit(1); + _exit(EXIT_FAILURE); } (void)close(dfd[0]); @@ -705,24 +703,6 @@ cleanup_pidfile(void) { pidfile = NULL; } -static void -cleanup_lockfile(void) { - if (singletonfd != -1) { - close(singletonfd); - singletonfd = -1; - } - - if (lockfile != NULL) { - int n = unlink(lockfile); - if (n == -1 && errno != ENOENT) { - named_main_earlywarning("unlink '%s': failed", - lockfile); - } - free(lockfile); - lockfile = NULL; - } -} - /* * Ensure that a directory exists. * NOTE: This function overwrites the '/' characters in 'filename' with @@ -906,69 +886,10 @@ named_os_writepidfile(const char *filename, bool first_time) { (void)fclose(fh); } -bool -named_os_issingleton(const char *filename) { - char strbuf[ISC_STRERRORSIZE]; - struct flock lock; - - if (singletonfd != -1) { - return (true); - } - - if (strcasecmp(filename, "none") == 0) { - return (true); - } - - /* - * Make the containing directory if it doesn't exist. - */ - lockfile = strdup(filename); - if (lockfile == NULL) { - strerror_r(errno, strbuf, sizeof(strbuf)); - named_main_earlyfatal("couldn't allocate memory for '%s': %s", - filename, strbuf); - } else { - int ret = mkdirpath(lockfile, named_main_earlywarning); - if (ret == -1) { - named_main_earlywarning("couldn't create '%s'", - filename); - cleanup_lockfile(); - return (false); - } - } - - /* - * named_os_openfile() uses safeopen() which removes any existing - * files. We can't use that here. - */ - singletonfd = open(filename, O_WRONLY | O_CREAT, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (singletonfd == -1) { - cleanup_lockfile(); - return (false); - } - - memset(&lock, 0, sizeof(lock)); - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 1; - - /* Non-blocking (does not wait for lock) */ - if (fcntl(singletonfd, F_SETLK, &lock) == -1) { - close(singletonfd); - singletonfd = -1; - return (false); - } - - return (true); -} - void named_os_shutdown(void) { closelog(); cleanup_pidfile(); - cleanup_lockfile(); } void diff --git a/bin/named/server.c b/bin/named/server.c index 38cdfc8353..c7e2138ba8 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -32,7 +32,6 @@ #include #endif -#include #include #include #include @@ -59,6 +58,7 @@ #include #include #include +#include #include #include @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -399,6 +400,9 @@ const char *empty_zones[] = { /* RFC 8375 */ "HOME.ARPA", + /* RFC 9462 */ + "RESOLVER.ARPA", + NULL }; @@ -413,7 +417,8 @@ static isc_result_t listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, - isc_mem_t *mctx, ns_listenelt_t **target); + isc_mem_t *mctx, isc_nm_proxy_type_t proxy, + ns_listenelt_t **target); #endif static isc_result_t @@ -440,8 +445,8 @@ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, dns_view_t *view, dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, - cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, - bool modify); + dns_keystorelist_t *keystores, cfg_aclconfctx_t *aclconf, + bool added, bool old_rpz_ok, bool modify); static void configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig, @@ -1314,8 +1319,8 @@ get_view_querysource_dispatch(const cfg_obj_t **maps, int af, isc_sockaddr_format(&sa, buf, sizeof(buf)); isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "could not get query source dispatcher (%s)", - buf); + "could not get query source dispatcher (%s): %s", + buf, isc_result_totext(result)); return (result); } @@ -2484,7 +2489,7 @@ configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t **maps, } #endif /* ifndef USE_DNSRPS */ - result = dns_rpz_new_zones(view->mctx, named_g_loopmgr, rps_cstr, + result = dns_rpz_new_zones(view, named_g_loopmgr, rps_cstr, rps_cstr_size, &view->rpzs); if (result != ISC_R_SUCCESS) { return (result); @@ -2621,10 +2626,10 @@ configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t **maps, } if (*old_rpz_okp) { - dns_rpz_shutdown_rpzs(view->rpzs); - dns_rpz_detach_rpzs(&view->rpzs); - dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs); - dns_rpz_detach_rpzs(&pview->rpzs); + dns_rpz_zones_shutdown(view->rpzs); + dns_rpz_zones_detach(&view->rpzs); + dns_rpz_zones_attach(pview->rpzs, &view->rpzs); + dns_rpz_zones_detach(&pview->rpzs); } else if (old != NULL && pview != NULL) { ++pview->rpzs->rpz_ver; view->rpzs->rpz_ver = pview->rpzs->rpz_ver; @@ -2655,6 +2660,10 @@ catz_addmodzone_cb(void *arg) { ns_cfgctx_t *cfg = NULL; dns_zone_t *zone = NULL; + if (isc_loop_shuttingdown(isc_loop_get(named_g_loopmgr, isc_tid()))) { + goto cleanup; + } + cfg = (ns_cfgctx_t *)cz->view->new_zone_config; if (cfg == NULL) { isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, @@ -2790,12 +2799,12 @@ catz_addmodzone_cb(void *arg) { zoneobj = cfg_listelt_value(cfg_list_first(zlist)); /* Mark view unfrozen so that zone can be added */ - isc_loopmgr_pause(named_g_loopmgr); dns_view_thaw(cz->view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, cz->view, &cz->cbd->server->viewlist, - &cz->cbd->server->kasplist, cfg->actx, true, + &cz->cbd->server->kasplist, + &cz->cbd->server->keystorelist, cfg->actx, true, false, cz->mod); dns_view_freeze(cz->view); isc_loopmgr_resume(named_g_loopmgr); @@ -2851,7 +2860,7 @@ catz_addmodzone_cb(void *arg) { } dns_catz_entry_detach(cz->origin, &cz->entry); dns_catz_zone_detach(&cz->origin); - dns_view_detach(&cz->view); + dns_view_weakdetach(&cz->view); isc_mem_putanddetach(&cz->mctx, cz, sizeof(*cz)); } @@ -2864,6 +2873,10 @@ catz_delzone_cb(void *arg) { char cname[DNS_NAME_FORMATSIZE]; const char *file = NULL; + if (isc_loop_shuttingdown(isc_loop_get(named_g_loopmgr, isc_tid()))) { + goto cleanup; + } + isc_loopmgr_pause(named_g_loopmgr); dns_name_format(dns_catz_entry_getname(cz->entry), cname, @@ -2876,7 +2889,7 @@ catz_delzone_cb(void *arg) { "catz: catz_delzone_cb: " "zone '%s' not found", cname); - goto cleanup; + goto resume; } if (!dns_zone_getadded(zone)) { @@ -2885,7 +2898,7 @@ catz_delzone_cb(void *arg) { "catz: catz_delzone_cb: " "zone '%s' is not a dynamically added zone", cname); - goto cleanup; + goto resume; } if (dns_zone_get_parentcatz(zone) != cz->origin) { @@ -2894,7 +2907,7 @@ catz_delzone_cb(void *arg) { "catz: catz_delzone_cb: zone " "'%s' exists in multiple catalog zones", cname); - goto cleanup; + goto resume; } /* Stop answering for this zone */ @@ -2903,7 +2916,9 @@ catz_delzone_cb(void *arg) { dns_zone_unload(zone); } - CHECK(dns_view_delzone(cz->view, zone)); + if (dns_view_delzone(cz->view, zone) != ISC_R_SUCCESS) { + goto resume; + } file = dns_zone_getfile(zone); if (file != NULL) { isc_file_remove(file); @@ -2918,14 +2933,15 @@ catz_delzone_cb(void *arg) { "catz: catz_delzone_cb: " "zone '%s' deleted", cname); -cleanup: +resume: isc_loopmgr_resume(named_g_loopmgr); +cleanup: if (zone != NULL) { dns_zone_detach(&zone); } dns_catz_entry_detach(cz->origin, &cz->entry); dns_catz_zone_detach(&cz->origin); - dns_view_detach(&cz->view); + dns_view_weakdetach(&cz->view); isc_mem_putanddetach(&cz->mctx, cz, sizeof(*cz)); } @@ -2957,7 +2973,7 @@ catz_run(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view, dns_catz_entry_attach(entry, &cz->entry); dns_catz_zone_attach(origin, &cz->origin); - dns_view_attach(view, &cz->view); + dns_view_weakattach(view, &cz->view); isc_async_run(named_g_mainloop, action, cz); @@ -3379,7 +3395,7 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, const cfg_obj_t *obj; const cfg_obj_t *zconfig; const cfg_obj_t *zoptions; - const char *rbt_dbtype[4] = { "rbt" }; + const char *default_dbtype[4] = { ZONEDB_DEFAULT }; const char *sep = ": view "; const char *str; const char *viewname = view->name; @@ -3392,7 +3408,7 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, dns_name_t *ns; dns_name_t *zname; dns_zone_t *zone = NULL; - int rbt_dbtypec = 1; + int default_dbtypec = 1; isc_result_t result; dns_namereln_t namereln; int order; @@ -3434,7 +3450,7 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, } } if (db == NULL) { - CHECK(dns_db_create(view->mctx, "rbt", name, + CHECK(dns_db_create(view->mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone, view->rdclass, 0, NULL, &db)); CHECK(dns_db_newversion(db, &version)); @@ -3454,15 +3470,15 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, } /* - * Is the existing zone the ok to use? + * Is the existing zone ok to use? */ if (pzone != NULL) { unsigned int typec; - const char **dbargv; + const char **dbargv = NULL; if (db != NULL) { - typec = rbt_dbtypec; - dbargv = rbt_dbtype; + typec = default_dbtypec; + dbargv = default_dbtype; } else { typec = empty_dbtypec; dbargv = empty_dbtype; @@ -3882,10 +3898,7 @@ create_mapped_acl(void) { isc_netaddr_fromin6(&addr, &in6); - result = dns_acl_create(named_g_mctx, 1, &acl); - if (result != ISC_R_SUCCESS) { - return (result); - } + dns_acl_create(named_g_mctx, 1, &acl); result = dns_iptable_addprefix(acl->iptable, &addr, 96, true); if (result == ISC_R_SUCCESS) { @@ -3981,8 +3994,9 @@ static const char *const response_synonyms[] = { "response", NULL }; static isc_result_t configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, cfg_obj_t *vconfig, named_cachelist_t *cachelist, - dns_kasplist_t *kasplist, const cfg_obj_t *bindkeys, - isc_mem_t *mctx, cfg_aclconfctx_t *actx, bool need_hints) { + dns_kasplist_t *kasplist, dns_keystorelist_t *keystores, + const cfg_obj_t *bindkeys, isc_mem_t *mctx, + cfg_aclconfctx_t *actx, bool need_hints) { const cfg_obj_t *maps[4]; const cfg_obj_t *cfgmaps[3]; const cfg_obj_t *optionmaps[3]; @@ -4037,10 +4051,9 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, named_cache_t *nsc; bool zero_no_soattl; dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL; - unsigned int query_timeout, ndisp; + unsigned int query_timeout; bool old_rpz_ok = false; dns_dyndbctx_t *dctx = NULL; - unsigned int resolver_param; dns_ntatable_t *ntatable = NULL; const char *qminmode = NULL; dns_adb_t *adb = NULL; @@ -4128,7 +4141,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, view, viewlist, - kasplist, actx, false, old_rpz_ok, false)); + kasplist, keystores, actx, false, + old_rpz_ok, false)); zone_element_latest = element; } @@ -4322,6 +4336,11 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, INSIST(result == ISC_R_SUCCESS); zero_no_soattl = cfg_obj_asboolean(obj); + obj = NULL; + result = named_config_get(maps, "resolver-use-dns64", &obj); + INSIST(result == ISC_R_SUCCESS); + view->usedns64 = cfg_obj_asboolean(obj); + obj = NULL; result = named_config_get(maps, "dns64", &obj); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") && @@ -4541,6 +4560,19 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, view->staleanswerclienttimeout = (uint32_t)-1; } else { view->staleanswerclienttimeout = cfg_obj_asuint32(obj); + + /* + * BIND 9 no longer supports non-zero values of + * stale-answer-client-timeout. + */ + if (view->staleanswerclienttimeout != 0) { + view->staleanswerclienttimeout = 0; + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "BIND 9 no longer supports non-zero values of " + "stale-answer-client-timeout, adjusted to 0"); + } } obj = NULL; @@ -4643,7 +4675,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, * is simply a named cache that is not shared. */ CHECK(dns_cache_create(named_g_loopmgr, view->rdclass, - cachename, &cache)); + cachename, mctx, &cache)); } nsc = isc_mem_get(mctx, sizeof(*nsc)); nsc->cache = NULL; @@ -4684,10 +4716,9 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, goto cleanup; } - ndisp = 4 * ISC_MIN(named_g_udpdisp, MAX_UDP_DISPATCH); - CHECK(dns_view_createresolver( - view, named_g_loopmgr, ndisp, named_g_netmgr, resopts, - named_g_server->tlsctx_client_cache, dispatch4, dispatch6)); + CHECK(dns_view_createresolver(view, named_g_netmgr, resopts, + named_g_server->tlsctx_client_cache, + dispatch4, dispatch6)); if (resstats == NULL) { isc_stats_create(mctx, &resstats, dns_resstatscounter_max); @@ -4806,27 +4837,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, query_timeout = cfg_obj_asuint32(obj); dns_resolver_settimeout(view->resolver, query_timeout); - /* - * Adjust stale-answer-client-timeout upper bound - * to be resolver-query-timeout - 1s. - * This assignment is safe as dns_resolver_settimeout() - * ensures that resolver->querytimeout value will be in the - * [MINIMUM_QUERY_TIMEOUT, MAXIMUM_QUERY_TIMEOUT] range and - * MINIMUM_QUERY_TIMEOUT is > 1000 (in ms). - */ - if (view->staleanswerclienttimeout != (uint32_t)-1 && - view->staleanswerclienttimeout > - (dns_resolver_gettimeout(view->resolver) - 1000)) - { - view->staleanswerclienttimeout = - dns_resolver_gettimeout(view->resolver) - 1000; - isc_log_write( - named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, - "stale-answer-client-timeout adjusted to %" PRIu32, - view->staleanswerclienttimeout); - } - /* Specify whether to use 0-TTL for negative response for SOA query */ dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl); @@ -4892,23 +4902,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, } view->maxbits = maxbits; - /* - * Set resolver retry parameters. - */ - obj = NULL; - CHECK(named_config_get(maps, "resolver-retry-interval", &obj)); - resolver_param = cfg_obj_asuint32(obj); - if (resolver_param > 0) { - dns_resolver_setretryinterval(view->resolver, resolver_param); - } - - obj = NULL; - CHECK(named_config_get(maps, "resolver-nonbackoff-tries", &obj)); - resolver_param = cfg_obj_asuint32(obj); - if (resolver_param > 0) { - dns_resolver_setnonbackofftries(view->resolver, resolver_param); - } - /* * Set supported DNSSEC algorithms. */ @@ -5209,6 +5202,13 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache-on", NULL, actx, named_g_mctx, &view->cacheonacl)); + CHECK(configure_view_acl(vconfig, config, named_g_config, "allow-proxy", + NULL, actx, named_g_mctx, &view->proxyacl)); + + CHECK(configure_view_acl(vconfig, config, named_g_config, + "allow-proxy-on", NULL, actx, named_g_mctx, + &view->proxyonacl)); + if (strcmp(view->name, "_bind") != 0 && view->rdclass != dns_rdataclass_chaos) { @@ -5470,6 +5470,21 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, INSIST(result == ISC_R_SUCCESS); dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj)); + obj = NULL; + result = named_config_get(maps, "max-validations-per-fetch", &obj); + if (result == ISC_R_SUCCESS) { + dns_resolver_setmaxvalidations(view->resolver, + cfg_obj_asuint32(obj)); + } + + obj = NULL; + result = named_config_get(maps, "max-validation-failures-per-fetch", + &obj); + if (result == ISC_R_SUCCESS) { + dns_resolver_setmaxvalidationfails(view->resolver, + cfg_obj_asuint32(obj)); + } + obj = NULL; result = named_config_get(maps, "fetches-per-zone", &obj); INSIST(result == ISC_R_SUCCESS); @@ -6444,8 +6459,8 @@ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, dns_view_t *view, dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, - cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, - bool modify) { + dns_keystorelist_t *keystores, cfg_aclconfctx_t *aclconf, + bool added, bool old_rpz_ok, bool modify) { dns_view_t *pview = NULL; /* Production view */ dns_zone_t *zone = NULL; /* New or reused zone */ dns_zone_t *raw = NULL; /* New or reused raw zone */ @@ -6639,7 +6654,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_setstats(zone, named_g_server->zonestats); } CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, - kasplist, zone, NULL)); + kasplist, keystores, zone, NULL)); dns_zone_attach(zone, &view->redirect); goto cleanup; } @@ -6815,7 +6830,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, * Configure the zone. */ CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, kasplist, - zone, raw)); + keystores, zone, raw)); /* * Add the zone to its view in the new view list. @@ -7056,7 +7071,7 @@ tat_done(void *arg) { dns_fetchresponse_t *resp = (dns_fetchresponse_t *)arg; ns_tat_t *tat = NULL; - INSIST(resp != NULL && resp->type == FETCHDONE); + INSIST(resp != NULL); tat = resp->arg; @@ -7449,7 +7464,7 @@ generate_session_key(const char *filename, const char *keynamestr, /* generate key */ result = dst_key_generate(keyname, alg, bits, 1, 0, DNS_KEYPROTO_ANY, - dns_rdataclass_in, mctx, &key, NULL); + dns_rdataclass_in, NULL, mctx, &key, NULL); if (result != ISC_R_SUCCESS) { return (result); } @@ -7815,7 +7830,8 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, view, &named_g_server->viewlist, - &named_g_server->kasplist, actx, true, + &named_g_server->kasplist, + &named_g_server->keystorelist, actx, true, false, false)); } @@ -8000,7 +8016,8 @@ configure_newzone(const cfg_obj_t *zconfig, cfg_obj_t *config, cfg_aclconfctx_t *actx) { return (configure_zone( config, zconfig, vconfig, view, &named_g_server->viewlist, - &named_g_server->kasplist, actx, true, false, false)); + &named_g_server->kasplist, &named_g_server->keystorelist, actx, + true, false, false)); } /*% @@ -8132,89 +8149,6 @@ get_newzone_config(dns_view_t *view, const char *zonename, #endif /* HAVE_LMDB */ -static isc_result_t -check_lockfile(named_server_t *server, const cfg_obj_t *config, - bool first_time) { - isc_result_t result; - const char *filename = NULL; - const cfg_obj_t *maps[3]; - const cfg_obj_t *options; - const cfg_obj_t *obj; - int i; - - i = 0; - options = NULL; - result = cfg_map_get(config, "options", &options); - if (result == ISC_R_SUCCESS) { - maps[i++] = options; - } - maps[i++] = named_g_defaults; - maps[i] = NULL; - - obj = NULL; - (void)named_config_get(maps, "lock-file", &obj); - - if (!first_time) { - if (obj != NULL && !cfg_obj_isstring(obj) && - server->lockfile != NULL && - strcmp(cfg_obj_asstring(obj), server->lockfile) != 0) - { - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, - "changing 'lock-file' " - "has no effect until the " - "server is restarted"); - } - - return (ISC_R_SUCCESS); - } - - if (obj != NULL) { - if (cfg_obj_isvoid(obj)) { - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), - "skipping lock-file check "); - return (ISC_R_SUCCESS); - } else if (named_g_forcelock) { - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, - "'lock-file' has no effect " - "because the server was run with -X"); - server->lockfile = isc_mem_strdup( - server->mctx, named_g_defaultlockfile); - } else { - filename = cfg_obj_asstring(obj); - server->lockfile = isc_mem_strdup(server->mctx, - filename); - } - - if (server->lockfile == NULL) { - return (ISC_R_NOMEMORY); - } - } - - if (named_g_forcelock && named_g_defaultlockfile != NULL) { - INSIST(server->lockfile == NULL); - server->lockfile = isc_mem_strdup(server->mctx, - named_g_defaultlockfile); - } - - if (server->lockfile == NULL) { - return (ISC_R_SUCCESS); - } - - if (named_os_issingleton(server->lockfile)) { - return (ISC_R_SUCCESS); - } - - isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, - "could not lock %s; another named " - "process may be running", - server->lockfile); - return (ISC_R_FAILURE); -} - static isc_result_t load_configuration(const char *filename, named_server_t *server, bool first_time) { @@ -8227,10 +8161,14 @@ load_configuration(const char *filename, named_server_t *server, const cfg_obj_t *options; const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports; const cfg_obj_t *kasps; + const cfg_obj_t *keystores; dns_kasp_t *kasp = NULL; dns_kasp_t *kasp_next = NULL; dns_kasp_t *default_kasp = NULL; dns_kasplist_t tmpkasplist, kasplist; + dns_keystore_t *keystore = NULL; + dns_keystore_t *keystore_next = NULL; + dns_keystorelist_t tmpkeystorelist, keystorelist; const cfg_obj_t *views; dns_view_t *view_next = NULL; @@ -8266,9 +8204,10 @@ load_configuration(const char *filename, named_server_t *server, /* * Require the reconfiguration to happen always on the main loop */ - REQUIRE(isc_loop_current(named_g_loopmgr) == named_g_mainloop); + REQUIRE(isc_loop() == named_g_mainloop); ISC_LIST_INIT(kasplist); + ISC_LIST_INIT(keystorelist); ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); ISC_LIST_INIT(cachelist); @@ -8430,14 +8369,6 @@ load_configuration(const char *filename, named_server_t *server, setstring(server, &server->bindkeysfile, NULL); } - /* - * Check the process lockfile. - */ - result = check_lockfile(server, config, first_time); - if (result != ISC_R_SUCCESS) { - goto cleanup_bindkeys_parser; - } - #if defined(HAVE_GEOIP2) /* * Release any previously opened GeoIP2 databases. @@ -8993,6 +8924,33 @@ load_configuration(const char *filename, named_server_t *server, */ (void)configure_session_key(maps, server, named_g_mctx, first_time); + /* + * Create the built-in key store ("key-directory"). + */ + result = cfg_keystore_fromconfig(NULL, named_g_mctx, named_g_lctx, + named_g_engine, &keystorelist, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup_keystorelist; + } + + /* + * Create the DNSSEC key stores. + */ + keystores = NULL; + (void)cfg_map_get(config, "key-store", &keystores); + for (element = cfg_list_first(keystores); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + keystore = NULL; + result = cfg_keystore_fromconfig(kconfig, named_g_mctx, + named_g_lctx, named_g_engine, + &keystorelist, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup_keystorelist; + } + } + /* * Create the built-in kasp policies ("default", "insecure"). */ @@ -9006,7 +8964,7 @@ load_configuration(const char *filename, named_server_t *server, kasp = NULL; result = cfg_kasp_fromconfig(kconfig, default_kasp, true, named_g_mctx, named_g_lctx, - &kasplist, &kasp); + &keystorelist, &kasplist, &kasp); if (result != ISC_R_SUCCESS) { goto cleanup_kasplist; } @@ -9035,7 +8993,7 @@ load_configuration(const char *filename, named_server_t *server, kasp = NULL; result = cfg_kasp_fromconfig(kconfig, default_kasp, true, named_g_mctx, named_g_lctx, - &kasplist, &kasp); + &keystorelist, &kasplist, &kasp); if (result != ISC_R_SUCCESS) { goto cleanup_kasplist; } @@ -9043,8 +9001,15 @@ load_configuration(const char *filename, named_server_t *server, dns_kasp_freeze(kasp); dns_kasp_detach(&kasp); } - dns_kasp_detach(&default_kasp); + + /* + * Save keystore list and kasp list. + */ + tmpkeystorelist = server->keystorelist; + server->keystorelist = keystorelist; + keystorelist = tmpkeystorelist; + tmpkasplist = server->kasplist; server->kasplist = kasplist; kasplist = tmpkasplist; @@ -9148,7 +9113,8 @@ load_configuration(const char *filename, named_server_t *server, } result = configure_view(view, &viewlist, config, vconfig, - &cachelist, &server->kasplist, bindkeys, + &cachelist, &server->kasplist, + &server->keystorelist, bindkeys, named_g_mctx, named_g_aclconfctx, true); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); @@ -9169,7 +9135,8 @@ load_configuration(const char *filename, named_server_t *server, goto cleanup_cachelist; } result = configure_view(view, &viewlist, config, NULL, - &cachelist, &server->kasplist, bindkeys, + &cachelist, &server->kasplist, + &server->keystorelist, bindkeys, named_g_mctx, named_g_aclconfctx, true); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); @@ -9196,10 +9163,10 @@ load_configuration(const char *filename, named_server_t *server, goto cleanup_cachelist; } - result = configure_view(view, &viewlist, config, vconfig, - &cachelist, &server->kasplist, bindkeys, - named_g_mctx, named_g_aclconfctx, - false); + result = configure_view( + view, &viewlist, config, vconfig, &cachelist, + &server->kasplist, &server->keystorelist, bindkeys, + named_g_mctx, named_g_aclconfctx, false); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); goto cleanup_cachelist; @@ -9518,8 +9485,6 @@ load_configuration(const char *filename, named_server_t *server, INSIST(result == ISC_R_SUCCESS); if (strcasecmp(cfg_obj_asstring(obj), "siphash24") == 0) { server->sctx->cookiealg = ns_cookiealg_siphash24; - } else if (strcasecmp(cfg_obj_asstring(obj), "aes") == 0) { - server->sctx->cookiealg = ns_cookiealg_aes; } else { UNREACHABLE(); } @@ -9586,21 +9551,6 @@ load_configuration(const char *filename, named_server_t *server, goto cleanup_altsecrets; } break; - case ns_cookiealg_aes: - expectedlength = ISC_AES128_KEYLENGTH; - if (usedlength != expectedlength) { - result = ISC_R_RANGE; - isc_log_write( - named_g_lctx, - NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, - ISC_LOG_ERROR, - "AES cookie-secret must be 128 " - "bits: %s", - isc_result_totext(result)); - goto cleanup_altsecrets; - } - break; } } } else { @@ -9713,6 +9663,15 @@ load_configuration(const char *filename, named_server_t *server, dns_kasp_detach(&kasp); } +cleanup_keystorelist: + for (keystore = ISC_LIST_HEAD(keystorelist); keystore != NULL; + keystore = keystore_next) + { + keystore_next = ISC_LIST_NEXT(keystore, link); + ISC_LIST_UNLINK(keystorelist, keystore, link); + dns_keystore_detach(&keystore); + } + cleanup_v6portset: isc_portset_destroy(named_g_mctx, &v6portset); @@ -9901,11 +9860,10 @@ run_server(void *arg) { named_server_t *server = (named_server_t *)arg; dns_geoip_databases_t *geoip = NULL; - dns_zonemgr_create(named_g_mctx, named_g_loopmgr, named_g_netmgr, - &server->zonemgr); + dns_zonemgr_create(named_g_mctx, named_g_netmgr, &server->zonemgr); - CHECKFATAL(dns_dispatchmgr_create(named_g_mctx, named_g_netmgr, - &named_g_dispatchmgr), + CHECKFATAL(dns_dispatchmgr_create(named_g_mctx, named_g_loopmgr, + named_g_netmgr, &named_g_dispatchmgr), "creating dispatch manager"); dns_dispatchmgr_setstats(named_g_dispatchmgr, server->resolverstats); @@ -9977,6 +9935,7 @@ shutdown_server(void *arg) { named_server_t *server = (named_server_t *)arg; dns_view_t *view = NULL, *view_next = NULL; dns_kasp_t *kasp = NULL, *kasp_next = NULL; + dns_keystore_t *keystore = NULL, *keystore_next = NULL; bool flush = server->flushonshutdown; named_cache_t *nsc = NULL; @@ -10023,6 +9982,14 @@ shutdown_server(void *arg) { dns_kasp_detach(&kasp); } + for (keystore = ISC_LIST_HEAD(server->keystorelist); keystore != NULL; + keystore = keystore_next) + { + keystore_next = ISC_LIST_NEXT(keystore, link); + ISC_LIST_UNLINK(server->keystorelist, keystore, link); + dns_keystore_detach(&keystore); + } + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = view_next) { @@ -10129,6 +10096,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { /* Initialize server data structures. */ ISC_LIST_INIT(server->kasplist); + ISC_LIST_INIT(server->keystorelist); ISC_LIST_INIT(server->viewlist); /* Must be first. */ @@ -10229,9 +10197,6 @@ named_server_destroy(named_server_t **serverp) { if (server->hostname != NULL) { isc_mem_free(server->mctx, server->hostname); } - if (server->lockfile != NULL) { - isc_mem_free(server->mctx, server->lockfile); - } if (server->zonemgr != NULL) { dns_zonemgr_detach(&server->zonemgr); @@ -10240,6 +10205,7 @@ named_server_destroy(named_server_t **serverp) { dst_lib_destroy(); INSIST(ISC_LIST_EMPTY(server->kasplist)); + INSIST(ISC_LIST_EMPTY(server->keystorelist)); INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); @@ -10268,8 +10234,7 @@ fatal(const char *msg, isc_result_t result) { NAMED_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "exiting (due to fatal error)"); named_os_shutdown(); - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } static isc_result_t @@ -10297,8 +10262,15 @@ reload(named_server_t *server) { atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS); #if HAVE_LIBSYSTEMD - sd_notify(0, "RELOADING=1\n" - "STATUS=reload command received\n"); + char buf[512]; + int n = snprintf(buf, sizeof(buf), + "RELOADING=1\n" + "MONOTONIC_USEC=%" PRIu64 "\n" + "STATUS=reload command received\n", + (uint64_t)isc_time_monotonic() / NS_PER_US); + if (n > 0 && (size_t)n < sizeof(buf)) { + sd_notify(0, buf); + } #endif /* HAVE_LIBSYSTEMD */ CHECK(loadconfig(server)); @@ -10678,8 +10650,15 @@ named_server_reconfigcommand(named_server_t *server) { isc_result_t result; atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS); #if HAVE_LIBSYSTEMD - sd_notify(0, "RELOADING=1\n" - "STATUS=reconfig command received\n"); + char buf[512]; + int n = snprintf(buf, sizeof(buf), + "RELOADING=1\n" + "MONOTONIC_USEC=%" PRIu64 "\n" + "STATUS=reconfig command received\n", + (uint64_t)isc_time_monotonic() / NS_PER_US); + if (n > 0 && (size_t)n < sizeof(buf)) { + sd_notify(0, buf); + } #endif /* HAVE_LIBSYSTEMD */ CHECK(loadconfig(server)); @@ -10898,9 +10877,10 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, const cfg_obj_t *tlsobj = NULL, *httpobj = NULL; const cfg_obj_t *portobj = NULL; const cfg_obj_t *http_server = NULL; + const cfg_obj_t *proxyobj = NULL; in_port_t port = 0; const char *key = NULL, *cert = NULL, *ca_file = NULL, - *dhparam_file = NULL, *ciphers = NULL; + *dhparam_file = NULL, *ciphers = NULL, *cipher_suites = NULL; bool tls_prefer_server_ciphers = false, tls_prefer_server_ciphers_set = false; bool tls_session_tickets = false, tls_session_tickets_set = false; @@ -10909,6 +10889,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, uint32_t tls_protos = 0; ns_listen_tls_params_t tls_params = { 0 }; const char *tlsname = NULL; + isc_nm_proxy_type_t proxy = ISC_NM_PROXY_NONE; REQUIRE(target != NULL && *target == NULL); @@ -10929,6 +10910,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, const cfg_obj_t *tlsmap = NULL; const cfg_obj_t *tls_proto_list = NULL; const cfg_obj_t *ciphers_obj = NULL; + const cfg_obj_t *cipher_suites_obj = NULL; const cfg_obj_t *prefer_server_ciphers_obj = NULL; const cfg_obj_t *session_tickets_obj = NULL; @@ -10989,6 +10971,13 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, ciphers = cfg_obj_asstring(ciphers_obj); } + if (cfg_map_get(tlsmap, "cipher-suites", + &cipher_suites_obj) == ISC_R_SUCCESS) + { + cipher_suites = + cfg_obj_asstring(cipher_suites_obj); + } + if (cfg_map_get(tlsmap, "prefer-server-ciphers", &prefer_server_ciphers_obj) == ISC_R_SUCCESS) @@ -11016,6 +11005,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, .protocols = tls_protos, .dhparam_file = dhparam_file, .ciphers = ciphers, + .cipher_suites = cipher_suites, .prefer_server_ciphers = tls_prefer_server_ciphers, .prefer_server_ciphers_set = tls_prefer_server_ciphers_set, .session_tickets = tls_session_tickets, @@ -11092,16 +11082,31 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, port = (in_port_t)cfg_obj_asuint32(portobj); } + proxyobj = cfg_tuple_get(ltup, "proxy"); + if (proxyobj != NULL && cfg_obj_isstring(proxyobj)) { + const char *proxyval = cfg_obj_asstring(proxyobj); + + if (strcasecmp(proxyval, "encrypted") == 0) { + INSIST(do_tls == true); + proxy = ISC_NM_PROXY_ENCRYPTED; + } else if (strcasecmp(proxyval, "plain") == 0) { + proxy = ISC_NM_PROXY_PLAIN; + } else { + UNREACHABLE(); + } + } + #ifdef HAVE_LIBNGHTTP2 if (http) { CHECK(listenelt_http(http_server, family, do_tls, &tls_params, - tlsctx_cache, port, mctx, &delt)); + tlsctx_cache, port, mctx, proxy, &delt)); } #endif /* HAVE_LIBNGHTTP2 */ if (!http) { CHECK(ns_listenelt_create(mctx, port, NULL, family, do_tls, - &tls_params, tlsctx_cache, &delt)); + &tls_params, tlsctx_cache, proxy, + &delt)); } result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config, @@ -11122,7 +11127,8 @@ static isc_result_t listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, - isc_mem_t *mctx, ns_listenelt_t **target) { + isc_mem_t *mctx, isc_nm_proxy_type_t proxy, + ns_listenelt_t **target) { isc_result_t result = ISC_R_SUCCESS; ns_listenelt_t *delt = NULL; char **endpoints = NULL; @@ -11187,9 +11193,9 @@ listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, INSIST(i == len); - result = ns_listenelt_create_http(mctx, port, NULL, family, tls, - tls_params, tlsctx_cache, endpoints, - len, max_clients, max_streams, &delt); + result = ns_listenelt_create_http( + mctx, port, NULL, family, tls, tls_params, tlsctx_cache, proxy, + endpoints, len, max_clients, max_streams, &delt); if (result != ISC_R_SUCCESS) { goto error; } @@ -12087,8 +12093,8 @@ named_server_flushnode(named_server_t *server, isc_lex_t *lex, bool tree) { isc_result_t named_server_status(named_server_t *server, isc_buffer_t **text) { isc_result_t result; - unsigned int zonecount, xferrunning, xferdeferred, soaqueries; - unsigned int automatic; + unsigned int zonecount, xferrunning, xferdeferred, xferfirstrefresh; + unsigned int soaqueries, automatic; const char *ob = "", *cb = "", *alt = ""; char boottime[ISC_FORMATHTTPTIMESTAMP_SIZE]; char configtime[ISC_FORMATHTTPTIMESTAMP_SIZE]; @@ -12111,6 +12117,8 @@ named_server_status(named_server_t *server, isc_buffer_t **text) { DNS_ZONESTATE_XFERRUNNING); xferdeferred = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_XFERDEFERRED); + xferfirstrefresh = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_XFERFIRSTREFRESH); soaqueries = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_SOAQUERY); automatic = dns_zonemgr_getcount(server->zonemgr, @@ -12154,10 +12162,6 @@ named_server_status(named_server_t *server, isc_buffer_t **text) { snprintf(line, sizeof(line), "worker threads: %u\n", named_g_cpus); CHECK(putstr(text, line)); - snprintf(line, sizeof(line), "UDP listeners per interface: %u\n", - named_g_udpdisp); - CHECK(putstr(text, line)); - snprintf(line, sizeof(line), "number of zones: %u (%u automatic)\n", zonecount, automatic); CHECK(putstr(text, line)); @@ -12171,6 +12175,10 @@ named_server_status(named_server_t *server, isc_buffer_t **text) { snprintf(line, sizeof(line), "xfers deferred: %u\n", xferdeferred); CHECK(putstr(text, line)); + snprintf(line, sizeof(line), "xfers first refresh: %u\n", + xferfirstrefresh); + CHECK(putstr(text, line)); + snprintf(line, sizeof(line), "soa queries in progress: %u\n", soaqueries); CHECK(putstr(text, line)); @@ -12187,6 +12195,12 @@ named_server_status(named_server_t *server, isc_buffer_t **text) { isc_quota_getmax(&server->sctx->recursionquota)); CHECK(putstr(text, line)); + snprintf(line, sizeof(line), "recursive high-water: %u\n", + (unsigned int)ns_stats_get_counter( + server->sctx->nsstats, + ns_statscounter_recurshighwater)); + CHECK(putstr(text, line)); + snprintf(line, sizeof(line), "tcp clients: %u/%u\n", isc_quota_getused(&server->sctx->tcpquota), isc_quota_getmax(&server->sctx->tcpquota)); @@ -13445,8 +13459,9 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Mark view unfrozen and configure zone */ dns_view_thaw(view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, cfg->actx, - true, false, false); + &server->viewlist, &server->kasplist, + &server->keystorelist, cfg->actx, true, false, + false); dns_view_freeze(view); isc_loopmgr_resume(named_g_loopmgr); @@ -13630,8 +13645,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Reconfigure the zone */ dns_view_thaw(view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, cfg->actx, - true, false, true); + &server->viewlist, &server->kasplist, + &server->keystorelist, cfg->actx, true, false, + true); dns_view_freeze(view); isc_loopmgr_resume(named_g_loopmgr); @@ -14656,7 +14672,6 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, char output[65355]; isc_stdtime_t now, when; isc_time_t timenow, timewhen; - const char *dir; dns_db_t *db = NULL; dns_dbversion_t *version = NULL; @@ -14791,7 +14806,6 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, } /* Get DNSSEC keys. */ - dir = dns_zone_getkeydirectory(zone); CHECK(dns_zone_getdb(zone, &db)); dns_db_currentversion(db, &version); LOCK(&kasp->lock); @@ -14823,11 +14837,11 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, LOCK(&kasp->lock); if (use_keyid) { - result = dns_keymgr_checkds_id(kasp, &keys, dir, now, - when, dspublish, keyid, + result = dns_keymgr_checkds_id(kasp, &keys, now, when, + dspublish, keyid, (unsigned int)algorithm); } else { - result = dns_keymgr_checkds(kasp, &keys, dir, now, when, + result = dns_keymgr_checkds(kasp, &keys, now, when, dspublish); } UNLOCK(&kasp->lock); @@ -14878,7 +14892,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, isc_result_t ret; LOCK(&kasp->lock); - result = dns_keymgr_rollover(kasp, &keys, dir, now, when, keyid, + result = dns_keymgr_rollover(kasp, &keys, now, when, keyid, (unsigned int)algorithm); UNLOCK(&kasp->lock); @@ -15096,29 +15110,26 @@ named_server_zonestatus(named_server_t *server, isc_lex_t *lex, { dns_name_t *name; dns_fixedname_t fixed; - dns_rdataset_t next; + isc_stdtime_t resign; + dns_typepair_t typepair; - dns_rdataset_init(&next); name = dns_fixedname_initname(&fixed); - result = dns_db_getsigningtime(db, &next, name); + result = dns_db_getsigningtime(db, &resign, name, &typepair); if (result == ISC_R_SUCCESS) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; + resign -= dns_zone_getsigresigninginterval(zone); + dns_name_format(name, namebuf, sizeof(namebuf)); - dns_rdatatype_format(next.covers, typebuf, - sizeof(typebuf)); + dns_rdatatype_format(DNS_TYPEPAIR_COVERS(typepair), + typebuf, sizeof(typebuf)); snprintf(resignbuf, sizeof(resignbuf), "%s/%s", namebuf, typebuf); - isc_time_set( - &resigntime, - next.resign - - dns_zone_getsigresigninginterval(zone), - 0); + isc_time_set(&resigntime, resign, 0); isc_time_formathttptimestamp(&resigntime, rtbuf, sizeof(rtbuf)); - dns_rdataset_disassociate(&next); } } @@ -15347,6 +15358,8 @@ named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly, * If -dump was specified, list NTA's and return */ if (dump) { + size_t last = 0; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { @@ -15358,6 +15371,12 @@ named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly, continue; } + if (last != isc_buffer_usedlength(*text)) { + CHECK(putstr(text, "\n")); + } + + last = isc_buffer_usedlength(*text); + CHECK(dns_ntatable_totext(ntatable, view->name, text)); } CHECK(putnull(text)); diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index 349bec0eef..b9b85e0594 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -33,7 +33,9 @@ #include #include #include +#include #include +#include #include #include @@ -325,6 +327,8 @@ init_desc(void) { SET_NSSTATDESC(updatebadprereq, "updates rejected due to prerequisite failure", "UpdateBadPrereq"); + SET_NSSTATDESC(recurshighwater, "Recursive clients high-water", + "RecursHighwater"); SET_NSSTATDESC(recursclients, "recursing clients", "RecursClients"); SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64"); SET_NSSTATDESC(ratedropped, "responses dropped for rate limits", @@ -553,8 +557,6 @@ init_desc(void) { SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open"); SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open"); SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open"); - SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen"); - SET_SOCKSTATDESC(rawopen, "Raw sockets opened", "RawOpen"); SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures", "UDP4OpenFail"); SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures", @@ -563,18 +565,10 @@ init_desc(void) { "TCP4OpenFail"); SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures", "TCP6OpenFail"); - SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures", - "UnixOpenFail"); - SET_SOCKSTATDESC(rawopenfail, "Raw socket open failures", - "RawOpenFail"); SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close"); SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close"); SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close"); SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close"); - SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose"); - SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed", - "FDWatchClose"); - SET_SOCKSTATDESC(rawclose, "Raw sockets closed", "RawClose"); SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures", "UDP4BindFail"); SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures", @@ -583,10 +577,6 @@ init_desc(void) { "TCP4BindFail"); SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures", "TCP6BindFail"); - SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures", - "UnixBindFail"); - SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures", - "FdwatchBindFail"); SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures", "UDP4ConnFail"); SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures", @@ -595,10 +585,6 @@ init_desc(void) { "TCP4ConnFail"); SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures", "TCP6ConnFail"); - SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures", - "UnixConnFail"); - SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures", - "FDwatchConnFail"); SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established", "UDP4Conn"); SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established", @@ -607,48 +593,30 @@ init_desc(void) { "TCP4Conn"); SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established", "TCP6Conn"); - SET_SOCKSTATDESC(unixconnect, "Unix domain connections established", - "UnixConn"); - SET_SOCKSTATDESC(fdwatchconnect, - "FDwatch domain connections established", - "FDwatchConn"); SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures", "TCP4AcceptFail"); SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures", "TCP6AcceptFail"); - SET_SOCKSTATDESC(unixacceptfail, - "Unix domain connection accept failures", - "UnixAcceptFail"); SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted", "TCP4Accept"); SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted", "TCP6Accept"); - SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted", - "UnixAccept"); SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr"); SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr"); SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr"); SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr"); - SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors", - "UnixSendErr"); - SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors", - "FDwatchSendErr"); SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr"); SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr"); SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr"); SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr"); - SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors", - "UnixRecvErr"); - SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors", - "FDwatchRecvErr"); - SET_SOCKSTATDESC(rawrecvfail, "Raw recv errors", "RawRecvErr"); SET_SOCKSTATDESC(udp4active, "UDP/IPv4 sockets active", "UDP4Active"); SET_SOCKSTATDESC(udp6active, "UDP/IPv6 sockets active", "UDP6Active"); SET_SOCKSTATDESC(tcp4active, "TCP/IPv4 sockets active", "TCP4Active"); SET_SOCKSTATDESC(tcp6active, "TCP/IPv6 sockets active", "TCP6Active"); - SET_SOCKSTATDESC(unixactive, "Unix domain sockets active", - "UnixActive"); - SET_SOCKSTATDESC(rawactive, "Raw sockets active", "RawActive"); + SET_SOCKSTATDESC(tcp4clients, "TCP/IPv4 clients currently connected", + "TCP4Clients"); + SET_SOCKSTATDESC(tcp6clients, "TCP/IPv6 clients currently connected", + "TCP6Clients"); INSIST(i == isc_sockstatscounter_max); /* Initialize DNSSEC statistics */ @@ -1304,6 +1272,7 @@ dnssecsignstat_dump(uint32_t kval, uint64_t val, void *arg) { #define STATS_XML_STATUS 0x00 /* display only common statistics */ #define STATS_XML_SERVER 0x01 #define STATS_XML_ZONES 0x02 +#define STATS_XML_XFRINS 0x04 #define STATS_XML_NET 0x08 #define STATS_XML_MEM 0x10 #define STATS_XML_TRAFFIC 0x20 @@ -1489,6 +1458,280 @@ zone_xmlrender(dns_zone_t *zone, void *arg) { return (ISC_R_FAILURE); } +static isc_result_t +xfrin_xmlrender(dns_zone_t *zone, void *arg) { + char buf[1024 + 32]; /* sufficiently large for zone name and class */ + dns_rdataclass_t rdclass; + const char *ztype; + uint32_t serial; + isc_sockaddr_t addr; + const isc_sockaddr_t *addrp = NULL; + char addr_buf[ISC_SOCKADDR_FORMATSIZE]; + dns_transport_type_t transport_type; + xmlTextWriterPtr writer = arg; + dns_zonestat_level_t statlevel; + int xmlrc; + dns_xfrin_t *xfr = NULL; + bool is_firstrefresh, is_running, is_deferred, is_presoa, is_pending; + bool needs_refresh; + bool is_first_data_received, is_ixfr; + unsigned int nmsg = 0; + unsigned int nrecs = 0; + uint64_t nbytes = 0; + + statlevel = dns_zone_getstatlevel(zone); + if (statlevel == dns_zonestat_none) { + return (ISC_R_SUCCESS); + } + + if (dns_zone_getxfr(zone, &xfr, &is_firstrefresh, &is_running, + &is_deferred, &is_presoa, &is_pending, + &needs_refresh) != ISC_R_SUCCESS) + { + /* + * Failed to get information about the zone's incoming transfer + * (if any), but we still want to continue generating the + * remaining parts of the output. + */ + return (ISC_R_SUCCESS); + } + + if (!is_running && !is_deferred && !is_presoa && !is_pending && + !needs_refresh) + { + if (xfr != NULL) { + dns_xfrin_detach(&xfr); + } + /* No ongoing/queued transfer. */ + return (ISC_R_SUCCESS); + } + + if (is_running && xfr == NULL) { + /* The transfer is finished, and it's shutting down. */ + return (ISC_R_SUCCESS); + } + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "xfrin")); + + dns_zone_nameonly(zone, buf, sizeof(buf)); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR buf)); + + rdclass = dns_zone_getclass(zone); + dns_rdataclass_format(rdclass, buf, sizeof(buf)); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "class", + ISC_XMLCHAR buf)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + ztype = user_zonetype(zone); + if (ztype != NULL) { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial")); + if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) { + TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteserial")); + if (is_running) { + serial = dns_xfrin_getendserial(xfr); + if (serial != 0) { + TRY0(xmlTextWriterWriteFormatString(writer, "%u", + serial)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "firstrefresh")); + TRY0(xmlTextWriterWriteString( + writer, ISC_XMLCHAR(is_firstrefresh ? "Yes" : "No"))); + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state")); + if (is_running) { + const char *xfr_state = NULL; + + dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received, + &is_ixfr); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR xfr_state)); + } else if (is_deferred) { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Deferred")); + } else if (is_presoa) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "Refresh SOA")); + } else if (is_pending) { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "Pending")); + } else if (needs_refresh) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "Needs Refresh")); + } else { + UNREACHABLE(); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refreshqueued")); + TRY0(xmlTextWriterWriteString( + writer, + ISC_XMLCHAR(is_running && needs_refresh ? "Yes" : "No"))); + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "localaddr")); + if (is_running) { + addrp = dns_xfrin_getsourceaddr(xfr); + isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf)); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf)); + } else if (is_presoa) { + addr = dns_zone_getsourceaddr(zone); + isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf)); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "remoteaddr")); + if (is_running) { + addrp = dns_xfrin_getprimaryaddr(xfr); + isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf)); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf)); + } else if (is_presoa) { + addr = dns_zone_getprimaryaddr(zone); + isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf)); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR addr_buf)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "soatransport")); + if (is_running || is_presoa) { + if (is_running) { + transport_type = dns_xfrin_getsoatransporttype(xfr); + } else { + transport_type = dns_zone_getrequesttransporttype(zone); + } + if (transport_type == DNS_TRANSPORT_UDP) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "UDP")); + } else if (transport_type == DNS_TRANSPORT_TCP) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "TCP")); + } else if (transport_type == DNS_TRANSPORT_TLS) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "TLS")); + } else if (transport_type == DNS_TRANSPORT_NONE) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "None")); + } else { + /* We don't expect any other SOA transport type. */ + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "transport")); + if (is_running) { + transport_type = dns_xfrin_gettransporttype(xfr); + if (transport_type == DNS_TRANSPORT_TCP) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "TCP")); + } else if (transport_type == DNS_TRANSPORT_TLS) { + TRY0(xmlTextWriterWriteString(writer, + ISC_XMLCHAR "TLS")); + } else { + /* We don't expect any other transport type. */ + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tsigkeyname")); + if (is_running) { + const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr); + char tsigkeyname_buf[DNS_NAME_FORMATSIZE]; + + if (tsigkeyname != NULL) { + dns_name_format(tsigkeyname, tsigkeyname_buf, + sizeof(tsigkeyname_buf)); + TRY0(xmlTextWriterWriteString( + writer, ISC_XMLCHAR tsigkeyname_buf)); + } + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "duration")); + if (is_running || is_deferred || is_presoa || is_pending) { + isc_time_t start = is_running ? dns_xfrin_getstarttime(xfr) + : dns_zone_getxfrintime(zone); + isc_time_t now = isc_time_now(); + isc_time_t diff; + uint32_t sec; + + isc_time_subtract(&now, &start, &diff); + sec = isc_time_seconds(&diff); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu32, sec)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "0")); + } + TRY0(xmlTextWriterEndElement(writer)); + + if (is_running) { + dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes); + } + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nmsg")); + TRY0(xmlTextWriterWriteFormatString(writer, "%u", nmsg)); + TRY0(xmlTextWriterEndElement(writer)); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nrecs")); + TRY0(xmlTextWriterWriteFormatString(writer, "%u", nrecs)); + TRY0(xmlTextWriterEndElement(writer)); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nbytes")); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, nbytes)); + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ixfr")); + if (is_running && is_first_data_received) { + TRY0(xmlTextWriterWriteString( + writer, ISC_XMLCHAR(is_ixfr ? "Yes" : "No"))); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "")); + } + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterEndElement(writer)); /* xfrin */ + + if (xfr != NULL) { + dns_xfrin_detach(&xfr); + } + + return (ISC_R_SUCCESS); + +cleanup: + if (xfr != NULL) { + dns_xfrin_detach(&xfr); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Failed at xfrin_xmlrender()"); + + return (ISC_R_FAILURE); +} + static isc_result_t generatexml(named_server_t *server, uint32_t flags, int *buflen, xmlChar **buf) { @@ -1767,8 +2010,8 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen, */ view = ISC_LIST_HEAD(server->viewlist); TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views")); - while (view != NULL && - ((flags & (STATS_XML_SERVER | STATS_XML_ZONES)) != 0)) + while (view != NULL && ((flags & (STATS_XML_SERVER | STATS_XML_ZONES | + STATS_XML_XFRINS)) != 0)) { isc_stats_t *istats = NULL; dns_stats_t *dstats = NULL; @@ -1786,6 +2029,14 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen, TRY0(xmlTextWriterEndElement(writer)); /* /zones */ } + if ((flags & STATS_XML_XFRINS) != 0) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "xfrins")); + CHECK(dns_zt_apply(view->zonetable, true, NULL, + xfrin_xmlrender, writer)); + TRY0(xmlTextWriterEndElement(writer)); /* /xfrins */ + } + if ((flags & STATS_XML_SERVER) == 0) { TRY0(xmlTextWriterEndElement(writer)); /* /view */ view = ISC_LIST_NEXT(view, link); @@ -1974,6 +2225,17 @@ render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, freecb, freecb_args)); } +static isc_result_t +render_xml_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_XFRINS, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + static isc_result_t render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg, unsigned int *retcode, const char **retmsg, @@ -2016,6 +2278,7 @@ render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, #define STATS_JSON_STATUS 0x00 /* display only common statistics */ #define STATS_JSON_SERVER 0x01 #define STATS_JSON_ZONES 0x02 +#define STATS_JSON_XFRINS 0x04 #define STATS_JSON_NET 0x08 #define STATS_JSON_MEM 0x10 #define STATS_JSON_TRAFFIC 0x20 @@ -2266,6 +2529,260 @@ zone_jsonrender(dns_zone_t *zone, void *arg) { return (result); } +static isc_result_t +xfrin_jsonrender(dns_zone_t *zone, void *arg) { + isc_result_t result; + char buf[1024 + 32]; /* sufficiently large for zone name and class */ + char classbuf[64]; /* sufficiently large for class */ + char *zone_name_only = NULL; + char *class_only = NULL; + dns_rdataclass_t rdclass; + uint32_t serial; + json_object *xfrinarray = (json_object *)arg; + json_object *xfrinobj = NULL; + isc_sockaddr_t addr; + const isc_sockaddr_t *addrp = NULL; + char addr_buf[ISC_SOCKADDR_FORMATSIZE]; + dns_transport_type_t transport_type; + dns_zonestat_level_t statlevel; + dns_xfrin_t *xfr = NULL; + bool is_firstrefresh, is_running, is_deferred, is_presoa, is_pending; + bool needs_refresh; + bool is_first_data_received, is_ixfr; + unsigned int nmsg = 0; + unsigned int nrecs = 0; + uint64_t nbytes = 0; + + statlevel = dns_zone_getstatlevel(zone); + if (statlevel == dns_zonestat_none) { + return (ISC_R_SUCCESS); + } + + dns_zone_nameonly(zone, buf, sizeof(buf)); + zone_name_only = buf; + + rdclass = dns_zone_getclass(zone); + dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf)); + class_only = classbuf; + + if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) { + xfrinobj = addzone(zone_name_only, class_only, + user_zonetype(zone), 0, false); + } else { + xfrinobj = addzone(zone_name_only, class_only, + user_zonetype(zone), serial, true); + } + + if (xfrinobj == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = dns_zone_getxfr(zone, &xfr, &is_firstrefresh, &is_running, + &is_deferred, &is_presoa, &is_pending, + &needs_refresh); + if (result != ISC_R_SUCCESS) { + result = ISC_R_SUCCESS; + goto cleanup; + } + + if (!is_running && !is_deferred && !is_presoa && !is_pending && + !needs_refresh) + { + /* No ongoing/queued transfer. */ + goto cleanup; + } + + if (is_running && xfr == NULL) { + /* The transfer is finished, and it's shutting down. */ + goto cleanup; + } + + if (is_running) { + serial = dns_xfrin_getendserial(xfr); + if (serial != 0) { + json_object_object_add(xfrinobj, "remoteserial", + json_object_new_int64(serial)); + } + } + + json_object_object_add( + xfrinobj, "firstrefresh", + json_object_new_string(is_firstrefresh ? "Yes" : "No")); + + if (is_running) { + const char *xfr_state = NULL; + + dns_xfrin_getstate(xfr, &xfr_state, &is_first_data_received, + &is_ixfr); + json_object_object_add(xfrinobj, "state", + json_object_new_string(xfr_state)); + } else if (is_deferred) { + json_object_object_add(xfrinobj, "state", + json_object_new_string("Deferred")); + } else if (is_presoa) { + json_object_object_add(xfrinobj, "state", + json_object_new_string("Refresh SOA")); + } else if (is_pending) { + json_object_object_add(xfrinobj, "state", + json_object_new_string("Pending")); + } else if (needs_refresh) { + json_object_object_add(xfrinobj, "state", + json_object_new_string("Needs Refresh")); + } else { + UNREACHABLE(); + } + + json_object_object_add( + xfrinobj, "refreshqueued", + json_object_new_string(is_running && needs_refresh ? "Yes" + : "No")); + + if (is_running) { + addrp = dns_xfrin_getsourceaddr(xfr); + isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf)); + json_object_object_add(xfrinobj, "localaddr", + json_object_new_string(addr_buf)); + } else if (is_presoa) { + addr = dns_zone_getsourceaddr(zone); + isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf)); + json_object_object_add(xfrinobj, "localaddr", + json_object_new_string(addr_buf)); + } else { + json_object_object_add(xfrinobj, "localaddr", + json_object_new_string("-")); + } + + if (is_running) { + addrp = dns_xfrin_getprimaryaddr(xfr); + isc_sockaddr_format(addrp, addr_buf, sizeof(addr_buf)); + json_object_object_add(xfrinobj, "remoteaddr", + json_object_new_string(addr_buf)); + } else if (is_presoa) { + addr = dns_zone_getprimaryaddr(zone); + isc_sockaddr_format(&addr, addr_buf, sizeof(addr_buf)); + json_object_object_add(xfrinobj, "remoteaddr", + json_object_new_string(addr_buf)); + } else { + json_object_object_add(xfrinobj, "remoteaddr", + json_object_new_string("-")); + } + + if (is_running || is_presoa) { + if (is_running) { + transport_type = dns_xfrin_getsoatransporttype(xfr); + } else { + transport_type = dns_zone_getrequesttransporttype(zone); + } + + if (transport_type == DNS_TRANSPORT_UDP) { + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("UDP")); + } else if (transport_type == DNS_TRANSPORT_TCP) { + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("TCP")); + } else if (transport_type == DNS_TRANSPORT_TLS) { + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("TLS")); + } else if (transport_type == DNS_TRANSPORT_NONE) { + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("None")); + } else { + /* We don't expect any other SOA transport type. */ + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("-")); + } + } else { + json_object_object_add(xfrinobj, "soatransport", + json_object_new_string("-")); + } + + if (is_running) { + transport_type = dns_xfrin_gettransporttype(xfr); + if (transport_type == DNS_TRANSPORT_TCP) { + json_object_object_add(xfrinobj, "transport", + json_object_new_string("TCP")); + } else if (transport_type == DNS_TRANSPORT_TLS) { + json_object_object_add(xfrinobj, "transport", + json_object_new_string("TLS")); + } else { + /* We don't expect any other transport type. */ + json_object_object_add(xfrinobj, "transport", + json_object_new_string("-")); + } + } else { + json_object_object_add(xfrinobj, "transport", + json_object_new_string("-")); + } + + if (is_running) { + const dns_name_t *tsigkeyname = dns_xfrin_gettsigkeyname(xfr); + char tsigkeyname_buf[DNS_NAME_FORMATSIZE]; + + if (tsigkeyname != NULL) { + dns_name_format(tsigkeyname, tsigkeyname_buf, + sizeof(tsigkeyname_buf)); + json_object_object_add( + xfrinobj, "tsigkeyname", + json_object_new_string(tsigkeyname_buf)); + } else { + json_object_object_add(xfrinobj, "tsigkeyname", NULL); + } + } else { + json_object_object_add(xfrinobj, "tsigkeyname", NULL); + } + + if (is_running || is_deferred || is_presoa || is_pending) { + isc_time_t start = is_running ? dns_xfrin_getstarttime(xfr) + : dns_zone_getxfrintime(zone); + isc_time_t now = isc_time_now(); + isc_time_t diff; + uint32_t sec; + + isc_time_subtract(&now, &start, &diff); + sec = isc_time_seconds(&diff); + json_object_object_add(xfrinobj, "duration", + json_object_new_int64((int64_t)sec)); + } else { + json_object_object_add(xfrinobj, "duration", + json_object_new_int64(0)); + } + + if (is_running) { + dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes); + } + json_object_object_add(xfrinobj, "nmsg", + json_object_new_int64((int64_t)nmsg)); + json_object_object_add(xfrinobj, "nrecs", + json_object_new_int64((int64_t)nrecs)); + json_object_object_add( + xfrinobj, "nbytes", + json_object_new_int64(nbytes > INT64_MAX ? INT64_MAX + : (int64_t)nbytes)); + + if (is_running && is_first_data_received) { + json_object_object_add( + xfrinobj, "ixfr", + json_object_new_string(is_ixfr ? "Yes" : "No")); + } else { + json_object_object_add(xfrinobj, "ixfr", + json_object_new_string("")); + } + + json_object_array_add(xfrinarray, xfrinobj); + xfrinobj = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (xfr != NULL) { + dns_xfrin_detach(&xfr); + } + if (xfrinobj != NULL) { + json_object_put(xfrinobj); + } + return (result); +} + static isc_result_t generatejson(named_server_t *server, size_t *msglen, const char **msg, json_object **rootp, uint32_t flags) { @@ -2484,7 +3001,9 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, #endif /* ifdef HAVE_DNSTAP */ } - if ((flags & (STATS_JSON_ZONES | STATS_JSON_SERVER)) != 0) { + if ((flags & + (STATS_JSON_SERVER | STATS_JSON_ZONES | STATS_JSON_XFRINS)) != 0) + { viewlist = json_object_new_object(); CHECKMEM(viewlist); @@ -2492,7 +3011,7 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, view = ISC_LIST_HEAD(server->viewlist); while (view != NULL) { - json_object *za, *v = json_object_new_object(); + json_object *za, *xa, *v = json_object_new_object(); dns_adb_t *adb = NULL; CHECKMEM(v); @@ -2512,6 +3031,20 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, json_object_put(za); } + xa = json_object_new_array(); + CHECKMEM(xa); + + if ((flags & STATS_JSON_XFRINS) != 0) { + CHECK(dns_zt_apply(view->zonetable, true, NULL, + xfrin_jsonrender, xa)); + } + + if (json_object_array_length(xa) != 0) { + json_object_object_add(v, "xfrins", xa); + } else { + json_object_put(xa); + } + if ((flags & STATS_JSON_SERVER) != 0) { json_object *res = NULL; dns_stats_t *dstats = NULL; @@ -2895,6 +3428,17 @@ render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, freecb, freecb_args)); } +static isc_result_t +render_json_xfrins(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_XFRINS, arg, retcode, retmsg, mimetype, + b, freecb, freecb_args)); +} + static isc_result_t render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg, unsigned int *retcode, const char **retmsg, @@ -3100,6 +3644,9 @@ add_listener(named_server_t *server, named_statschannel_t **listenerp, isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v" STATS_XML_VERSION_MAJOR "/zones", false, render_xml_zones, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/xfrins", false, + render_xml_xfrins, server); isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v" STATS_XML_VERSION_MAJOR "/net", false, render_xml_net, server); @@ -3125,6 +3672,9 @@ add_listener(named_server_t *server, named_statschannel_t **listenerp, isc_httpdmgr_addurl(listener->httpdmgr, "/json/v" STATS_JSON_VERSION_MAJOR "/zones", false, render_json_zones, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/xfrins", false, + render_json_xfrins, server); isc_httpdmgr_addurl(listener->httpdmgr, "/json/v" STATS_JSON_VERSION_MAJOR "/net", false, render_json_net, server); diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c index f24aab11dd..d1736d0726 100644 --- a/bin/named/transportconf.c +++ b/bin/named/transportconf.c @@ -120,11 +120,13 @@ add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { dns_transport_set_tls_versions); parse_transport_option(doh, transport, "ciphers", dns_transport_set_ciphers); + parse_transport_option(doh, transport, "cipher-suites", + dns_transport_set_cipher_suites); parse_transport_bool_option( doh, transport, "prefer-server-ciphers", - dns_transport_set_prefer_server_ciphers) - parse_transport_option(doh, transport, "ca-file", - dns_transport_set_cafile); + dns_transport_set_prefer_server_ciphers); + parse_transport_option(doh, transport, "ca-file", + dns_transport_set_cafile); parse_transport_option(doh, transport, "remote-hostname", dns_transport_set_remote_hostname); } @@ -172,11 +174,13 @@ add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { dns_transport_set_tls_versions); parse_transport_option(tls, transport, "ciphers", dns_transport_set_ciphers); + parse_transport_option(tls, transport, "cipher-suites", + dns_transport_set_cipher_suites); parse_transport_bool_option( tls, transport, "prefer-server-ciphers", - dns_transport_set_prefer_server_ciphers) - parse_transport_option(tls, transport, "ca-file", - dns_transport_set_cafile); + dns_transport_set_prefer_server_ciphers); + parse_transport_option(tls, transport, "ca-file", + dns_transport_set_cafile); parse_transport_option(tls, transport, "remote-hostname", dns_transport_set_remote_hostname); } diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index fa34b4c7d4..c45051b42a 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -866,8 +866,8 @@ process_notifytype(dns_notifytype_t ntype, dns_zonetype_t ztype, isc_result_t named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_kasplist_t *kasplist, dns_zone_t *zone, - dns_zone_t *raw) { + dns_kasplist_t *kasplist, dns_keystorelist_t *keystorelist, + dns_zone_t *zone, dns_zone_t *raw) { isc_result_t result; const char *zname; dns_rdataclass_t zclass; @@ -885,7 +885,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, uint32_t count; unsigned int dbargc; char **dbargv; - static char default_dbtype[] = "rbt"; + static char default_dbtype[] = ZONEDB_DEFAULT; static char dlz_dbtype[] = "dlz"; char *cpval = default_dbtype; isc_mem_t *mctx = dns_zone_getmctx(zone); @@ -1576,6 +1576,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, filename = cfg_obj_asstring(obj); CHECK(dns_zone_setkeydirectory(zone, filename)); } + /* Also save a reference to the keystore list. */ + dns_zone_setkeystores(zone, keystorelist); obj = NULL; result = named_config_get(maps, "sig-signing-signatures", &obj); @@ -2014,13 +2016,7 @@ named_zone_inlinesigning(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, } maps[i] = NULL; - /* "inline-signing" is a zone-only clause, so look in maps[0] only. */ - res = cfg_map_get(maps[0], "inline-signing", &signing); - if (res == ISC_R_SUCCESS && cfg_obj_isboolean(signing)) { - return (cfg_obj_asboolean(signing)); - } - - /* If inline-signing is not set, check the value in dnssec-policy. */ + /* Check the value in dnssec-policy. */ policy = NULL; res = named_config_get(maps, "dnssec-policy", &policy); /* If no dnssec-policy found, then zone is not using inline-signing. */ @@ -2039,5 +2035,15 @@ named_zone_inlinesigning(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, inline_signing = dns_kasp_inlinesigning(kasp); dns_kasp_detach(&kasp); + /* + * The zone option 'inline-signing' may override the value in + * dnssec-policy. This is a zone-only option, so look in maps[0] + * only. + */ + res = cfg_map_get(maps[0], "inline-signing", &signing); + if (res == ISC_R_SUCCESS && cfg_obj_isboolean(signing)) { + return (cfg_obj_asboolean(signing)); + } + return (inline_signing); } diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index 3e673f493d..3048df8345 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -102,6 +102,8 @@ #define DNSDEFAULTPORT 53 +#define DEFAULT_EDNS_BUFSIZE 1232 + /* Number of addresses to request from isc_getaddresses() */ #define MAX_SERVERADDRS 4 @@ -175,6 +177,8 @@ static isc_mutex_t answer_lock; static dns_message_t *answer = NULL; static uint32_t default_ttl = 0; static bool default_ttl_set = false; +static uint32_t lease = 0, keylease = 0; +static bool lease_set = false, keylease_set = false; static bool checknames = true; static bool checksvcb = true; static const char *resolvconf = RESOLV_CONF; @@ -275,8 +279,7 @@ fatal(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } static void @@ -364,7 +367,8 @@ reset_system(void) { if (updatemsg != NULL) { dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER); } else { - dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &updatemsg); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, + &updatemsg); } updatemsg->opcode = dns_opcode_update; if (usegsstsig) { @@ -378,13 +382,13 @@ reset_system(void) { } static bool -parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, +parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac_alg, uint16_t *digestbitsp) { uint16_t digestbits = 0; isc_result_t result; char buf[20]; - REQUIRE(hmac != NULL); + REQUIRE(hmac_alg != NULL); REQUIRE(hmacstr != NULL); if (len >= sizeof(buf)) { @@ -396,9 +400,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, strlcpy(buf, hmacstr, ISC_MIN(len + 1, sizeof(buf))); if (strcasecmp(buf, "hmac-md5") == 0) { - *hmac = DST_ALG_HMACMD5; + *hmac_alg = DST_ALG_HMACMD5; } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { - *hmac = DST_ALG_HMACMD5; + *hmac_alg = DST_ALG_HMACMD5; result = isc_parse_uint16(&digestbits, &buf[9], 10); if (result != ISC_R_SUCCESS || digestbits > 128) { error("digest-bits out of range [0..128]"); @@ -406,9 +410,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, } *digestbitsp = (digestbits + 7) & ~0x7U; } else if (strcasecmp(buf, "hmac-sha1") == 0) { - *hmac = DST_ALG_HMACSHA1; + *hmac_alg = DST_ALG_HMACSHA1; } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { - *hmac = DST_ALG_HMACSHA1; + *hmac_alg = DST_ALG_HMACSHA1; result = isc_parse_uint16(&digestbits, &buf[10], 10); if (result != ISC_R_SUCCESS || digestbits > 160) { error("digest-bits out of range [0..160]"); @@ -416,9 +420,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, } *digestbitsp = (digestbits + 7) & ~0x7U; } else if (strcasecmp(buf, "hmac-sha224") == 0) { - *hmac = DST_ALG_HMACSHA224; + *hmac_alg = DST_ALG_HMACSHA224; } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { - *hmac = DST_ALG_HMACSHA224; + *hmac_alg = DST_ALG_HMACSHA224; result = isc_parse_uint16(&digestbits, &buf[12], 10); if (result != ISC_R_SUCCESS || digestbits > 224) { error("digest-bits out of range [0..224]"); @@ -426,9 +430,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, } *digestbitsp = (digestbits + 7) & ~0x7U; } else if (strcasecmp(buf, "hmac-sha256") == 0) { - *hmac = DST_ALG_HMACSHA256; + *hmac_alg = DST_ALG_HMACSHA256; } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { - *hmac = DST_ALG_HMACSHA256; + *hmac_alg = DST_ALG_HMACSHA256; result = isc_parse_uint16(&digestbits, &buf[12], 10); if (result != ISC_R_SUCCESS || digestbits > 256) { error("digest-bits out of range [0..256]"); @@ -436,9 +440,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, } *digestbitsp = (digestbits + 7) & ~0x7U; } else if (strcasecmp(buf, "hmac-sha384") == 0) { - *hmac = DST_ALG_HMACSHA384; + *hmac_alg = DST_ALG_HMACSHA384; } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { - *hmac = DST_ALG_HMACSHA384; + *hmac_alg = DST_ALG_HMACSHA384; result = isc_parse_uint16(&digestbits, &buf[12], 10); if (result != ISC_R_SUCCESS || digestbits > 384) { error("digest-bits out of range [0..384]"); @@ -446,9 +450,9 @@ parse_hmac(const char *hmacstr, size_t len, dst_algorithm_t *hmac, } *digestbitsp = (digestbits + 7) & ~0x7U; } else if (strcasecmp(buf, "hmac-sha512") == 0) { - *hmac = DST_ALG_HMACSHA512; + *hmac_alg = DST_ALG_HMACSHA512; } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { - *hmac = DST_ALG_HMACSHA512; + *hmac_alg = DST_ALG_HMACSHA512; result = isc_parse_uint16(&digestbits, &buf[12], 10); if (result != ISC_R_SUCCESS || digestbits > 512) { error("digest-bits out of range [0..512]"); @@ -488,7 +492,7 @@ setup_keystr(void) { dns_fixedname_t fkeyname; dns_name_t *mykeyname = NULL; char *name = NULL; - dst_algorithm_t hmac; + dst_algorithm_t hmac_alg; uint16_t digestbits = 0; mykeyname = dns_fixedname_initname(&fkeyname); @@ -507,11 +511,11 @@ setup_keystr(void) { } name = secretstr; secretstr = n + 1; - if (!parse_hmac(keystr, s - keystr, &hmac, &digestbits)) { - exit(1); + if (!parse_hmac(keystr, s - keystr, &hmac_alg, &digestbits)) { + exit(EXIT_FAILURE); } } else { - hmac = DST_ALG_HMACMD5; + hmac_alg = DST_ALG_HMACMD5; name = keystr; n = s; } @@ -538,8 +542,8 @@ setup_keystr(void) { secretlen = isc_buffer_usedlength(&secretbuf); debug("keycreate"); - result = dns_tsigkey_create(mykeyname, hmac, secret, secretlen, gmctx, - &tsigkey); + result = dns_tsigkey_create(mykeyname, hmac_alg, secret, secretlen, + gmctx, &tsigkey); if (result != ISC_R_SUCCESS) { fprintf(stderr, "could not create key from %s: %s\n", keystr, isc_result_totext(result)); @@ -622,7 +626,7 @@ static void setup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) { dst_key_t *dstkey = NULL; isc_result_t result; - dst_algorithm_t hmac = DST_ALG_UNKNOWN; + dst_algorithm_t hmac_alg = DST_ALG_UNKNOWN; debug("Creating key..."); @@ -658,7 +662,7 @@ setup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) { case DST_ALG_HMACSHA256: case DST_ALG_HMACSHA384: case DST_ALG_HMACSHA512: - hmac = dst_key_alg(dstkey); + hmac_alg = dst_key_alg(dstkey); break; default: dst_key_attach(dstkey, &sig0key); @@ -666,9 +670,9 @@ setup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) { return; } - result = dns_tsigkey_createfromkey(dst_key_name(dstkey), hmac, dstkey, - false, false, NULL, 0, 0, mctx, - &tsigkey); + result = dns_tsigkey_createfromkey(dst_key_name(dstkey), hmac_alg, + dstkey, false, false, NULL, 0, 0, + mctx, &tsigkey); dst_key_free(&dstkey); if (result != ISC_R_SUCCESS) { fprintf(stderr, "could not create key from %s: %s\n", keyfile, @@ -798,7 +802,7 @@ create_name(const char *str, char *namedata, size_t len, dns_name_t *name) { } static void -setup_system(void) { +setup_system(void *arg ISC_ATTR_UNUSED) { isc_result_t result; isc_sockaddr_t bind_any, bind_any6; isc_sockaddrlist_t *nslist; @@ -919,7 +923,7 @@ setup_system(void) { irs_resconf_destroy(&resconf); - result = dns_dispatchmgr_create(gmctx, netmgr, &dispatchmgr); + result = dns_dispatchmgr_create(gmctx, loopmgr, netmgr, &dispatchmgr); check_result(result, "dns_dispatchmgr_create"); result = dst_lib_init(gmctx, NULL); @@ -1049,7 +1053,7 @@ pre_parse_args(int argc, char **argv) { "[-A tlscafile] [-H tlshostname] " "[-O] ] [-v] [-V] [-P] [-T] [-4 | -6] " "[filename]\n"); - exit(1); + exit(EXIT_FAILURE); case 'P': for (t = 0xff00; t <= 0xfffe; t++) { @@ -1087,7 +1091,7 @@ pre_parse_args(int argc, char **argv) { } } if (doexit) { - exit(0); + exit(EXIT_SUCCESS); } isc_commandline_reset = true; isc_commandline_index = 1; @@ -1162,7 +1166,7 @@ parse_args(int argc, char **argv) { "bad library debug value " "'%s'\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } logdebuglevel = i; break; @@ -1193,7 +1197,7 @@ parse_args(int argc, char **argv) { "bad port number " "'%s'\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; case 'S': @@ -1205,7 +1209,7 @@ parse_args(int argc, char **argv) { if (result != ISC_R_SUCCESS) { fprintf(stderr, "bad timeout '%s'\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } if (timeout == 0) { timeout = UINT_MAX; @@ -1217,7 +1221,7 @@ parse_args(int argc, char **argv) { if (result != ISC_R_SUCCESS) { fprintf(stderr, "bad udp timeout '%s'\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; case 'r': @@ -1226,7 +1230,7 @@ parse_args(int argc, char **argv) { if (result != ISC_R_SUCCESS) { fprintf(stderr, "bad udp retries '%s'\n", isc_commandline_argument); - exit(1); + exit(EXIT_FAILURE); } break; @@ -1237,19 +1241,19 @@ parse_args(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option: %c\n", argv[0], isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } if (keyfile != NULL && keystr != NULL) { fprintf(stderr, "%s: cannot specify both -k and -y\n", argv[0]); - exit(1); + exit(EXIT_FAILURE); } #if HAVE_GSSAPI if (usegsstsig && (keyfile != NULL || keystr != NULL)) { fprintf(stderr, "%s: cannot specify -g with -k or -y\n", argv[0]); - exit(1); + exit(EXIT_FAILURE); } #else /* HAVE_GSSAPI */ if (usegsstsig) { @@ -1257,7 +1261,7 @@ parse_args(int argc, char **argv) { "%s: cannot specify -g or -o, " "program not linked with GSS API Library\n", argv[0]); - exit(1); + exit(EXIT_FAILURE); } #endif /* HAVE_GSSAPI */ @@ -1270,14 +1274,14 @@ parse_args(int argc, char **argv) { "%s: cannot specify the -K option without" "the -E option, and vice versa.\n", argv[0]); - exit(1); + exit(EXIT_FAILURE); } if (tls_ca_file != NULL && tls_always_verify_remote == false) { fprintf(stderr, "%s: cannot specify the -A option in " "conjuction with the -O option.\n", argv[0]); - exit(1); + exit(EXIT_FAILURE); } } @@ -1291,7 +1295,7 @@ parse_args(int argc, char **argv) { fprintf(stderr, "could not open '%s': %s\n", argv[isc_commandline_index], isc_result_totext(result)); - exit(1); + exit(EXIT_FAILURE); } } if (!force_interactive) { @@ -1517,6 +1521,90 @@ evaluate_prereq(char *cmdline) { return (make_prereq(cmdline, ispositive, isrrset)); } +static void +updateopt(void) { + isc_result_t result; + dns_ednsopt_t ednsopts[1]; + unsigned char ul[8]; + unsigned int count = 0; + + if (lease_set) { + isc_buffer_t b; + INSIST(count < ARRAY_SIZE(ednsopts)); + ednsopts[count++] = (dns_ednsopt_t){ .code = DNS_OPT_UL, + .length = keylease_set ? 8 + : 4, + .value = ul }; + + isc_buffer_init(&b, ul, sizeof(ul)); + isc_buffer_putuint32(&b, lease); + isc_buffer_putuint32(&b, keylease); + } + + if (count != 0) { + dns_rdataset_t *opt = NULL; + result = dns_message_buildopt(updatemsg, &opt, 0, + DEFAULT_EDNS_BUFSIZE, 0, ednsopts, + count); + check_result(result, "dns_message_buildopt"); + result = dns_message_setopt(updatemsg, opt); + check_result(result, "dns_message_setopt"); + } else { + result = dns_message_setopt(updatemsg, NULL); + check_result(result, "dns_message_setopt"); + } +} + +static uint16_t +evaluate_lease(char *cmdline) { + char *word; + isc_result_t result; + uint32_t value1, value2; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read ttl\n"); + return (STATUS_SYNTAX); + } + + if (!strcasecmp(word, "none")) { + lease = 0; + lease_set = false; + keylease = 0; + keylease_set = false; + updateopt(); + return (STATUS_MORE); + } + + result = isc_parse_uint32(&value1, word, 10); + if (result != ISC_R_SUCCESS) { + return (STATUS_SYNTAX); + } + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + lease = value1; + lease_set = true; + keylease = 0; + keylease_set = false; + updateopt(); + return (STATUS_MORE); + } + + result = isc_parse_uint32(&value2, word, 10); + if (result != ISC_R_SUCCESS) { + return (STATUS_SYNTAX); + } + + lease = value1; + lease_set = true; + keylease = value2; + keylease_set = true; + updateopt(); + + return (STATUS_MORE); +} + static uint16_t evaluate_server(char *cmdline) { char *word, *server; @@ -1633,7 +1721,7 @@ evaluate_key(char *cmdline) { int secretlen; unsigned char *secret = NULL; isc_buffer_t secretbuf; - dst_algorithm_t hmac = DST_ALG_UNKNOWN; + dst_algorithm_t hmac_alg = DST_ALG_UNKNOWN; uint16_t digestbits = 0; char *n; @@ -1647,12 +1735,12 @@ evaluate_key(char *cmdline) { n = strchr(namestr, ':'); if (n != NULL) { - if (!parse_hmac(namestr, n - namestr, &hmac, &digestbits)) { + if (!parse_hmac(namestr, n - namestr, &hmac_alg, &digestbits)) { return (STATUS_SYNTAX); } namestr = n + 1; } else { - hmac = DST_ALG_HMACMD5; + hmac_alg = DST_ALG_HMACMD5; } isc_buffer_init(&b, namestr, strlen(namestr)); @@ -1684,8 +1772,8 @@ evaluate_key(char *cmdline) { if (tsigkey != NULL) { dns_tsigkey_detach(&tsigkey); } - result = dns_tsigkey_create(mykeyname, hmac, secret, secretlen, gmctx, - &tsigkey); + result = dns_tsigkey_create(mykeyname, hmac_alg, secret, secretlen, + gmctx, &tsigkey); isc_mem_free(gmctx, secret); if (result != ISC_R_SUCCESS) { fprintf(stderr, "could not create key from %s %s: %s\n", @@ -2158,7 +2246,7 @@ show_message(FILE *stream, dns_message_t *msg, const char *description) { if (bufsz > MAXTEXT) { fprintf(stderr, "could not allocate large enough " "buffer to display message\n"); - exit(1); + exit(EXIT_FAILURE); } if (buf != NULL) { isc_buffer_free(&buf); @@ -2221,6 +2309,9 @@ do_next_command(char *cmdline) { if (strcasecmp(word, "add") == 0) { return (update_addordelete(cmdline, false)); } + if (strcasecmp(word, "lease") == 0) { + return (evaluate_lease(cmdline)); + } if (strcasecmp(word, "server") == 0) { return (evaluate_server(cmdline)); } @@ -2393,7 +2484,7 @@ static void done_update(void) { ddebug("done_update()"); - isc_async_current(loopmgr, getinput, NULL); + isc_async_current(getinput, NULL); } static void @@ -2470,7 +2561,7 @@ update_completed(void *arg) { } LOCK(&answer_lock); - dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &answer); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &answer); result = dns_request_getresponse(request, answer, DNS_MESSAGEPARSE_PRESERVEORDER); switch (result) { @@ -2658,7 +2749,7 @@ recvsoa(void *arg) { reqinfo = NULL; ddebug("About to create rcvmsg"); - dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &rcvmsg); result = dns_request_getresponse(request, rcvmsg, DNS_MESSAGEPARSE_PRESERVEORDER); if (result == DNS_R_TSIGERRORSET && servers != NULL) { @@ -3069,7 +3160,7 @@ start_gssrequest(dns_name_t *primary) { keyname->attributes.nocompress = true; rmsg = NULL; - dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &rmsg); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, &rmsg); /* Build first request. */ context = GSS_C_NO_CONTEXT; @@ -3184,7 +3275,7 @@ recvgss(void *arg) { isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); ddebug("recvgss creating rcvmsg"); - dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &rcvmsg); result = dns_request_getresponse(request, rcvmsg, DNS_MESSAGEPARSE_PRESERVEORDER); @@ -3295,7 +3386,8 @@ start_update(void) { return; } - dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &soaquery); + dns_message_create(gmctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, + &soaquery); if (default_servers) { soaquery->flags |= DNS_MESSAGEFLAG_RD; @@ -3475,8 +3567,7 @@ main(int argc, char **argv) { timeoutms = timeout * 1000; isc_nm_settimeouts(netmgr, timeoutms, timeoutms, timeoutms, timeoutms); - setup_system(); - + isc_loopmgr_setup(loopmgr, setup_system, NULL); isc_loopmgr_setup(loopmgr, getinput, NULL); isc_loopmgr_teardown(loopmgr, shutdown_program, NULL); isc_loopmgr_run(loopmgr); @@ -3485,7 +3576,7 @@ main(int argc, char **argv) { if (seenerror) { return (2); - } else { - return (0); } + + return (0); } diff --git a/bin/nsupdate/nsupdate.rst b/bin/nsupdate/nsupdate.rst index 88263904ed..b98d70bbff 100644 --- a/bin/nsupdate/nsupdate.rst +++ b/bin/nsupdate/nsupdate.rst @@ -323,6 +323,11 @@ The command formats and their meanings are as follows: By default check-svcb processing is on. If check-svcb processing fails, the record is not added to the UPDATE message. +``lease time [keytime]`` + Set the EDNS Update Lease (UL) option to value to ``time`` and + optionally also set the key lease time to ``keytime`` in seconds. + If ``time`` is ``none`` the lease times are cleared. + ``prereq nxdomain domain-name`` This command requires that no resource record of any type exist with the name ``domain-name``. diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 6e8261c784..1d35ded62b 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -260,18 +260,11 @@ get_addresses(const char *host, in_port_t port) { REQUIRE(host != NULL); - if (*host == '/') { - result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs], - host); - if (result == ISC_R_SUCCESS) { - nserveraddrs++; - } - } else { - count = SERVERADDRS - nserveraddrs; - result = isc_getaddresses( - host, port, &serveraddrs[nserveraddrs], count, &found); - nserveraddrs += found; - } + count = SERVERADDRS - nserveraddrs; + result = isc_getaddresses(host, port, &serveraddrs[nserveraddrs], count, + &found); + nserveraddrs += found; + if (result != ISC_R_SUCCESS) { fatal("couldn't get address for '%s': %s", host, isc_result_totext(result)); @@ -312,8 +305,7 @@ rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { fatal("recv failed: %s", isc_result_totext(result)); } - source.rstart = isc_buffer_base(ccmsg->buffer); - source.rend = isc_buffer_used(ccmsg->buffer); + isccc_ccmsg_toregion(ccmsg, &source); DO("parse message", isccc_cc_fromwire(&source, &response, algorithm, &secret)); @@ -355,7 +347,7 @@ rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { isccc_sexpr_free(&response); - isccc_ccmsg_invalidate(ccmsg); + isccc_ccmsg_disconnect(ccmsg); isc_loopmgr_shutdown(loopmgr); } @@ -388,8 +380,7 @@ rndc_recvnonce(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, fatal("recv failed: %s", isc_result_totext(result)); } - source.rstart = isc_buffer_base(ccmsg->buffer); - source.rend = isc_buffer_used(ccmsg->buffer); + isccc_ccmsg_toregion(ccmsg, &source); DO("parse message", isccc_cc_fromwire(&source, &response, algorithm, &secret)); @@ -518,11 +509,6 @@ rndc_startconnect(isc_sockaddr_t *addr) { case AF_INET6: local = &local6; break; - case AF_UNIX: - /* - * TODO: support UNIX domain sockets in netgmr. - */ - fatal("UNIX domain sockets not currently supported"); default: UNREACHABLE(); } @@ -944,7 +930,7 @@ main(int argc, char **argv) { default: fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); - exit(1); + exit(EXIT_FAILURE); } } @@ -1015,6 +1001,8 @@ main(int argc, char **argv) { isc_loopmgr_run(loopmgr); + isccc_ccmsg_invalidate(&rndc_ccmsg); + isc_log_destroy(&log); isc_log_setcontext(NULL); diff --git a/bin/rndc/rndc.conf b/bin/rndc/rndc.conf deleted file mode 100644 index 78ee858515..0000000000 --- a/bin/rndc/rndc.conf +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ - -/* - * Sample rndc configuration file. - */ - -options { - default-server localhost; - default-key "key"; -}; - -server localhost { - key "key"; -}; - -key "cc64b3d1db63fc88d7cb5d2f9f57d258" { - algorithm hmac-sha256; - secret "34f88008d07deabbe65bd01f1d233d47"; -}; - -server "test1" { - key "cc64b3d1db63fc88d7cb5d2f9f57d258"; - port 5353; - addresses { 10.53.0.1; }; -}; - -key "key" { - algorithm hmac-sha256; - secret "c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K"; -}; diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index db43974a4b..da9f0710e4 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -440,6 +440,7 @@ Currently supported commands are: .. option:: zone [class [view]] If a zone is specified, this command reloads only the given zone. + If no zone is specified, the reloading happens asynchronously. .. program:: rndc @@ -604,7 +605,8 @@ Currently supported commands are: refused. If the zone has changed and the ``ixfr-from-differences`` option is in use, the journal file is updated to reflect changes in the zone. Otherwise, if the zone has changed, any existing - journal file is removed. + journal file is removed. If no zone is specified, the reloading happens + asynchronously. See also :option:`rndc freeze`. diff --git a/bin/rndc/util.c b/bin/rndc/util.c index 23b7f3fd57..e0529aa772 100644 --- a/bin/rndc/util.c +++ b/bin/rndc/util.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -45,6 +46,5 @@ fatal(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - isc__tls_setfatalmode(); - exit(1); + _exit(EXIT_FAILURE); } diff --git a/bin/tests/.gitignore b/bin/tests/.gitignore index 26185a7889..9a60c2ca80 100644 --- a/bin/tests/.gitignore +++ b/bin/tests/.gitignore @@ -3,7 +3,6 @@ nxtify sdig *_test gsstest -conf.sh dlopen keycreate keydelete diff --git a/bin/tests/startperf/setup.sh b/bin/tests/startperf/setup.sh index 66821386d7..775667c4f1 100644 --- a/bin/tests/startperf/setup.sh +++ b/bin/tests/startperf/setup.sh @@ -11,20 +11,20 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -usage () { - echo "Usage: $0 [-s] []" - echo " -s: use the same zone file all zones" - exit 1 +usage() { + echo "Usage: $0 [-s] []" + echo " -s: use the same zone file all zones" + exit 1 } if [ "$#" -lt 1 -o "$#" -gt 3 ]; then - usage + usage fi single_file="" if [ $1 = "-s" ]; then - single_file=yes - shift + single_file=yes + shift fi nzones=$1 @@ -35,9 +35,9 @@ nrecords=5 . ../system/conf.sh -cat << EOF +cat < zones/$zonename.db - echo "zone $zonename { type primary; file \"zones/$zonename.db\"; };" - fi + if [ $single_file ]; then + echo "zone $zonename { type primary; file \"smallzone.db\"; };" + else + [ -d zones ] || mkdir zones + $PERL mkzonefile.pl $zonename $nrecords >zones/$zonename.db + echo "zone $zonename { type primary; file \"zones/$zonename.db\"; };" + fi done diff --git a/bin/tests/system/.gitignore b/bin/tests/system/.gitignore index e2daf1cef5..3c2db219b0 100644 --- a/bin/tests/system/.gitignore +++ b/bin/tests/system/.gitignore @@ -13,12 +13,14 @@ named.run /random.data /*.log /*.trs +/*.xml /resolve -/legacy.run.sh /run.log /start.sh /stop.sh /ifconfig.sh +/isctest/vars/.ac_vars/* +!/isctest/vars/.ac_vars/*.in # Ignore file names with underscore in their name except python or shell files. # This is done to ignore the temporary directories and symlinks created by the @@ -26,3 +28,4 @@ named.run /*_* !/*_*.py !/*_*.sh +!/_common diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am index 44282372de..842bb5db8b 100644 --- a/bin/tests/system/Makefile.am +++ b/bin/tests/system/Makefile.am @@ -24,6 +24,8 @@ LDADD += \ $(LIBDNS_LIBS) if HAVE_PERL +if HAVE_PYTHON +if HAVE_PYTEST noinst_PROGRAMS = \ feature-test \ @@ -68,23 +70,14 @@ rpz_dnsrps_LDADD = \ $(LIBDNS_LIBS) \ $(DLOPEN_LIBS) -TESTS = - -if HAVE_PERLMOD_TIME_HIRES -TESTS += serve-stale -endif HAVE_PERLMOD_TIME_HIRES - -if HAVE_PERLMOD_NET_DNS -TESTS += \ - rpzrecurse -endif HAVE_PERLMOD_NET_DNS - -if HAVE_LIBNGHTTP2 -TESTS += \ - doth -endif - -TESTS += \ +# Longer running tests are listed (and executed) first to take the most +# advantage of parallel execution. +TESTS = \ + rpz \ + rpzrecurse \ + serve-stale \ + timeouts \ + upforwd \ acl \ additional \ addzone \ @@ -96,13 +89,21 @@ TESTS += \ case \ catz \ cds \ + chain \ checkconf \ + checkds \ checknames \ checkzone \ + cookie \ database \ dialup \ + digdelv \ + dispatch \ dlzexternal \ dns64 \ + dnssec \ + dnstap \ + doth \ dsdigest \ dyndb \ ecdsa \ @@ -111,18 +112,23 @@ TESTS += \ emptyzones \ enginepkcs11 \ filter-aaaa \ + fetchlimit \ formerr \ + forward \ geoip2 \ glue \ idna \ include-multiplecfg \ inline \ integrity \ + ixfr \ hooks \ host \ journal \ + kasp \ keepalive \ keyfromlabel \ + ksr \ legacy \ limits \ logfileconfig \ @@ -131,28 +137,41 @@ TESTS += \ metadata \ mirror \ mkeys \ + multisigner \ names \ notify \ nsec3 \ nslookup \ + nsupdate \ + nzd2nzf \ padding \ pending \ + proxy \ + pipelined \ + qmin \ + reclimit \ redirect \ + resolver \ rndc \ rootkeysentinel \ - rpz \ + rpzextra \ rrchecker \ rrl \ rrsetorder \ rsabigexponent \ runtime \ sfcache \ + shutdown \ smartsign \ sortlist \ spf \ staticstub \ + statistics \ + statschannel \ + stress \ stub \ synthfromdnssec \ + tcp \ tools \ transport-acl \ tsig \ @@ -162,62 +181,21 @@ TESTS += \ verify \ views \ wildcard \ + xfer \ xferquota \ + zero \ zonechecks -if HAVE_LMDB -TESTS += nzd2nzf -endif # HAVE_LMDB - -if HAVE_PERLMOD_NET_DNS - -TESTS += \ - fetchlimit \ - ixfr \ - nsupdate \ - resolver \ - statistics \ - stress \ - upforwd \ - zero - -if HAVE_DNSTAP -TESTS += dnstap -endif - -if HAVE_PERLMOD_FILE_FETCH -TESTS += statschannel -endif HAVE_PERLMOD_FILE_FETCH - -if HAVE_PERLMOD_DIGEST_HMAC -TESTS += xfer -endif HAVE_PERLMOD_DIGEST_HMAC - -if HAVE_PERLMOD_NET_DNS_NAMESERVER -TESTS += reclimit -endif HAVE_PERLMOD_NET_DNS_NAMESERVER - -endif HAVE_PERLMOD_NET_DNS - -if HAVE_PYTHON -TESTS += kasp multisigner tcp pipelined - -if HAVE_PYTEST -TESTS += checkds dispatch rpzextra shutdown timeouts -endif - -if HAVE_PYMOD_DNS -TESTS += qmin cookie -if HAVE_PERLMOD_NET_DNS -TESTS += digdelv dnssec forward -if HAVE_PERLMOD_NET_DNS_NAMESERVER -TESTS += chain -endif HAVE_PERLMOD_NET_DNS_NAMESERVER -endif HAVE_PERLMOD_NET_DNS -endif HAVE_PYMOD_DNS - -endif HAVE_PYTHON - +else !HAVE_PYTEST +check: + echo pytest is not available, no tests were ran + exit 1 +endif !HAVE_PYTEST +else !HAVE_PYTHON +check: + echo Python is not available, no tests were ran + exit 1 +endif !HAVE_PYTHON else !HAVE_PERL check: echo Perl is not available, no tests were ran @@ -232,12 +210,9 @@ LOG_DRIVER_V_1 = --verbose yes LOG_DRIVER = $(srcdir)/custom-test-driver AM_LOG_DRIVER_FLAGS = $(LOG_DRIVER_V) -LOG_COMPILER = $(builddir)/legacy.run.sh -AM_LOG_FLAGS = -r - -$(TESTS): legacy.run.sh +LOG_COMPILER = $(srcdir)/run.sh test-local: check clean-local:: - -find $(builddir) -maxdepth 1 -type d -name "*_*" | xargs rm -rf + -find -L . -mindepth 1 -maxdepth 1 -type d -name "*_*" -and -not -name "_common" -exec rm -rf {} \; diff --git a/bin/tests/system/README b/bin/tests/system/README deleted file mode 100644 index 6c5bae15c1..0000000000 --- a/bin/tests/system/README +++ /dev/null @@ -1,778 +0,0 @@ -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. - -Introduction -=== -This directory holds a simple test environment for running bind9 system tests -involving multiple name servers. - -With the exception of "common" (which holds configuration information common to -multiple tests), each directory holds a set of scripts and configuration -files to test different parts of BIND. The directories are named for the -aspect of BIND they test, for example: - - dnssec/ DNSSEC tests - forward/ Forwarding tests - glue/ Glue handling tests - -etc. - -Typically each set of tests sets up 2-5 name servers and then performs one or -more tests against them. Within the test subdirectory, each name server has a -separate subdirectory containing its configuration data. These subdirectories -are named "nsN" or "ansN" (where N is a number between 1 and 8, e.g. ns1, ans2 -etc.) - -The tests are completely self-contained and do not require access to the real -DNS. Generally, one of the test servers (usually ns1) is set up as a root -nameserver and is listed in the hints file of the others. - - -Preparing to Run the Tests -=== -To enable all servers to run on the same machine, they bind to separate virtual -IP addresses on the loopback interface. ns1 runs on 10.53.0.1, ns2 on -10.53.0.2, etc. Before running any tests, you must set up these addresses by -running the command - - sh ifconfig.sh up - -as root. The interfaces can be removed by executing the command: - - sh ifconfig.sh down - -... also as root. - -The servers use unprivileged ports (above 1024) instead of the usual port 53, -so they can be run without root privileges once the interfaces have been set -up. - - -Note for MacOS Users ---- -If you wish to make the interfaces survive across reboots, copy -org.isc.bind.system and org.isc.bind.system.plist to /Library/LaunchDaemons -then run - - launchctl load /Library/LaunchDaemons/org.isc.bind.system.plist - -... as root. - - -Running the System Tests with pytest -=== - -The pytest system test runner is currently in development, but it is the -recommended way to run tests. Please report issues to QA. - -Running an Individual Test ---- - -pytest -k - -Note that in comparison to the legacy test runner, some additional tests might -be picked up when specifying just the system test directory name. To check -which tests will be executed, you can use the `--collect-only` option. You -might also be able to find a more specific test name to provide to ensure only -your desired test is executed. See help for `-k` option in `pytest --help` for -more info. - -It is also possible to run a single individual pytest test case. For example, -you can use the name test_sslyze_dot to execute just the test_sslyze_dot() -function from doth/tests_sslyze.py. The entire needed setup and teardown will -be handled by the framework. - -Running All the System Tests ---- - -Issuing plain `pytest` command without any argument will execute all tests -sequenatially. To execute them in parallel, ensure you have pytest-xdist -installed and run: - -pytest -n - - -Running the System Tests Using the Legacy Runner -=== - -!!! WARNING !!! ---- -The legacy way to run system tests is currently being reworked into a pytest -system test runner described in the previous section. The contents of this -section might be out of date and no longer applicable. Please try and use the -pytest runner if possible and report issues and missing features. - -Running an Individual Test ---- -The tests can be run individually using the following command: - - sh legacy.run.sh [flags] [] - -e.g. - - sh legacy.run.sh [flags] notify - -Optional flags are: - - -k Keep servers running after the test completes. Each test - usually starts a number of nameservers, either instances - of the "named" being tested, or custom servers (written in - Python or Perl) that feature test-specific behavior. The - servers are automatically started before the test is run - and stopped after it ends. This flag leaves them running - at the end of the test, so that additional queries can be - sent by hand. To stop the servers afterwards, use the - command "sh stop.sh ". - - -n Noclean - do not remove the output files if the test - completes successfully. By default, files created by the - test are deleted if it passes; they are not deleted if the - test fails. - - -p Sets the range of ports used by the test. A block of 100 - ports is available for each test, the number given to the - "-p" switch being the number of the start of that block - (e.g. "-p 7900" will mean that the test is able to use - ports 7900 through 7999). If not specified, the test will - have ports 5000 to 5099 available to it. - -Arguments are: - - test-name Mandatory. The name of the test, which is the name of the - subdirectory in bin/tests/system holding the test files. - - test-arguments Optional arguments that are passed to each of the test's - scripts. - - -Running All The System Tests ---- -To run all the system tests, enter the command: - - make [-j numproc] test - -The optional "numproc" argument specifies the maximum number of tests that can -run in parallel. The default is 1, which means that all of the tests run -sequentially. If greater than 1, up to "numproc" tests will run simultaneously, -new tests being started as tests finish. Each test will get a unique set of -ports, so there is no danger of tests interfering with one another. Parallel -running will reduce the total time taken to run the BIND system tests, but will -mean that the output from all the tests sent to the screen will be mixed up -with one another. - -In this case, retention of the output files after a test completes successfully -is specified by setting the environment variable SYSTEMTEST_NO_CLEAN to 1 prior -to running make, e.g. - - SYSTEMTEST_NO_CLEAN=1 make [-j numproc] test - -while setting environment variable SYSTEMTEST_FORCE_COLOR to 1 forces system -test output to be printed in color. - - -Format of Test Output ---- -All output from the system tests is in the form of lines with the following -structure: - - :: [()] - -e.g. - - I:catz:checking that dom1.example is not served by primary (1) - -The meanings of the fields are as follows: - - -This indicates the type of message. This is one of: - - S Start of the test - A Start of test (retained for backwards compatibility) - T Start of test (retained for backwards compatibility) - E End of the test - I Information. A test will typically output many of these messages - during its run, indicating test progress. Note that such a message may - be of the form "I:testname:failed", indicating that a sub-test has - failed. - R Result. Each test will result in one such message, which is of the - form: - - R:: - - where is one of: - - PASS The test passed - FAIL The test failed - SKIPPED The test was not run, usually because some - prerequisites required to run the test are missing. - - -This is the name of the test from which the message emanated, which is also the -name of the subdirectory holding the test files. - - -This is text output by the test during its execution. - -() -If present, this will correlate with a file created by the test. The tests -execute commands and route the output of each command to a file. The name of -this file depends on the command and the test, but will usually be of the form: - - .out. - -e.g. nsupdate.out.test28, dig.out.q3. This aids diagnosis of problems by -allowing the output that caused the problem message to be identified. - - -Re-Running the Tests ---- -If there is a requirement to re-run a test (or the entire test suite), the -files produced by the tests should be deleted first. Normally, these files are -deleted if the test succeeds but are retained on error. The legacy.run.sh -script automatically calls a given test's clean.sh script before invoking its -setup.sh script. - -Deletion of the files produced by the set of tests (e.g. after the execution of -make) can be carried out using the command: - - sh cleanall.sh - -or - - make testclean - -(Note that the Makefile has two other targets for cleaning up files: "clean" -will delete all the files produced by the tests, as well as the object and -executable files used by the tests. "distclean" does all the work of "clean" -as well as deleting configuration files produced by "configure".) - - -Developer Notes -=== -This section is intended for developers writing new tests. - - -Overview ---- -As noted above, each test is in a separate directory. To interact with the -test framework, the directories contain the following standard files: - -prereq.sh Run at the beginning to determine whether the test can be run at - all; if not, we see a R:SKIPPED result. This file is optional: - if not present, the test is assumed to have all its prerequisites - met. - -setup.sh Run after prereq.sh, this sets up the preconditions for the tests. - Although optional, virtually all tests will require such a file to - set up the ports they should use for the test. - -tests.sh Runs the actual tests. This file is mandatory. - -clean.sh Run at the end to clean up temporary files, but only if the test - was completed successfully and its running was not inhibited by the - "-n" switch being passed to "legacy.run.sh". Otherwise the - temporary files are left in place for inspection. - -ns These subdirectories contain test name servers that can be queried - or can interact with each other. The value of N indicates the - address the server listens on: for example, ns2 listens on - 10.53.0.2, and ns4 on 10.53.0.4. All test servers use an - unprivileged port, so they don't need to run as root. These - servers log at the highest debug level and the log is captured in - the file "named.run". - -ans Like ns[X], but these are simple mock name servers implemented in - Perl or Python. They are generally programmed to misbehave in ways - named would not so as to exercise named's ability to interoperate - with badly behaved name servers. - - -Port Usage ---- -In order for the tests to run in parallel, each test requires a unique set of -ports. These are specified by the "-p" option passed to "legacy.run.sh", which -sets environment variables that the scripts listed above can reference. - -The convention used in the system tests is that the number passed is the start -of a range of 100 ports. The test is free to use the ports as required, -although the first ten ports in the block are named and generally tests use the -named ports for their intended purpose. The names of the environment variables -are: - - PORT Number to be used for the query port. - CONTROLPORT Number to be used as the RNDC control port. - EXTRAPORT1 - EXTRAPORT8 Eight port numbers that can be used as needed. - -Two other environment variables are defined: - - LOWPORT The lowest port number in the range. - HIGHPORT The highest port number in the range. - -Since port ranges usually start on a boundary of 10, the variables are set such -that the last digit of the port number corresponds to the number of the -EXTRAPORTn variable. For example, if the port range were to start at 5200, the -port assignments would be: - - PORT = 5200 - EXTRAPORT1 = 5201 - : - EXTRAPORT8 = 5208 - CONTROLPORT = 5209 - LOWPORT = 5200 - HIGHPORT = 5299 - -When running tests in parallel (i.e. giving a value of "numproc" greater than 1 -in the "make" command listed above), it is guaranteed that each -test will get a set of unique port numbers. - - -Writing a Test ---- -The test framework requires up to four shell scripts (listed above) as well as -a number of nameserver instances to run. Certain expectations are put on each -script: - - -General ---- -1. Each of the four scripts will be invoked with the command - - (cd ; sh