Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6078fad8ae | ||
|
|
c89fe7adb9 | ||
|
|
7c2eb9531a | ||
|
|
8a57e01c4c | ||
|
|
c604cf69d9 | ||
|
|
296ec40818 | ||
|
|
605fb0f1f1 | ||
|
|
be0837df1f | ||
|
|
87d29f48d4 | ||
|
|
47e89b637b | ||
|
|
274121e771 | ||
|
|
b6d4441e46 | ||
|
|
0e39e81039 | ||
|
|
4635b065d4 | ||
|
|
e30487c2b7 | ||
|
|
cbcefd172e | ||
|
|
ecfa5dd737 | ||
|
|
0f879219c5 | ||
|
|
4f3d003828 |
+32
-42
@@ -206,7 +206,7 @@ stages:
|
||||
### QCOW2 Image Templates
|
||||
|
||||
.freebsd-13-amd64: &freebsd_13_amd64_image
|
||||
image: "freebsd-13.3-x86_64"
|
||||
image: "freebsd-13.2-x86_64"
|
||||
<<: *libvirt_amd64
|
||||
|
||||
.freebsd-14-amd64: &freebsd_14_amd64_image
|
||||
@@ -219,19 +219,17 @@ stages:
|
||||
|
||||
### Job Templates
|
||||
|
||||
.api-pipelines-schedules-tags-triggers-web-triggering-rules: &api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
.api-schedules-tags-triggers-web-triggering-rules: &api_schedules_tags_triggers_web_triggering_rules
|
||||
only:
|
||||
- api
|
||||
- pipelines
|
||||
- schedules
|
||||
- tags
|
||||
- triggers
|
||||
- web
|
||||
|
||||
.api-pipelines-schedules-triggers-web-triggering-rules: &api_pipelines_schedules_triggers_web_triggering_rules
|
||||
.api-schedules-triggers-web-triggering-rules: &api_schedules_triggers_web_triggering_rules
|
||||
only:
|
||||
- api
|
||||
- pipelines
|
||||
- schedules
|
||||
- triggers
|
||||
- web
|
||||
@@ -240,7 +238,6 @@ stages:
|
||||
only:
|
||||
- api
|
||||
- merge_requests
|
||||
- pipelines
|
||||
- schedules
|
||||
- tags
|
||||
- triggers
|
||||
@@ -347,7 +344,7 @@ stages:
|
||||
|
||||
.shotgun: &shotgun_job
|
||||
<<: *base_image
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
stage: performance
|
||||
script:
|
||||
- if [ -z "$CI_COMMIT_TAG" ]; then export SHOTGUN_ROUNDS=1; else export SHOTGUN_ROUNDS=3; fi
|
||||
@@ -504,6 +501,15 @@ 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
|
||||
@@ -516,18 +522,6 @@ 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: []
|
||||
@@ -806,12 +800,12 @@ gcc:8fips:amd64:
|
||||
EXTRA_CONFIGURE: "--with-libidn2 --enable-fips-mode --disable-tracing"
|
||||
<<: *oraclelinux_8fips_amd64_image
|
||||
<<: *build_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
|
||||
system:gcc:8fips:amd64:
|
||||
<<: *oraclelinux_8fips_amd64_image
|
||||
<<: *system_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
needs:
|
||||
- job: gcc:8fips:amd64
|
||||
artifacts: true
|
||||
@@ -819,7 +813,7 @@ system:gcc:8fips:amd64:
|
||||
unit:gcc:8fips:amd64:
|
||||
<<: *oraclelinux_8fips_amd64_image
|
||||
<<: *unit_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
needs:
|
||||
- job: gcc:8fips:amd64
|
||||
artifacts: true
|
||||
@@ -831,12 +825,12 @@ gcc:9fips:amd64:
|
||||
EXTRA_CONFIGURE: "--with-libidn2 --enable-fips-mode --disable-leak-detection --disable-tracing"
|
||||
<<: *oraclelinux_9fips_amd64_image
|
||||
<<: *build_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
|
||||
system:gcc:9fips:amd64:
|
||||
<<: *oraclelinux_9fips_amd64_image
|
||||
<<: *system_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
needs:
|
||||
- job: gcc:9fips:amd64
|
||||
artifacts: true
|
||||
@@ -844,7 +838,7 @@ system:gcc:9fips:amd64:
|
||||
unit:gcc:9fips:amd64:
|
||||
<<: *oraclelinux_9fips_amd64_image
|
||||
<<: *unit_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
needs:
|
||||
- job: gcc:9fips:amd64
|
||||
artifacts: true
|
||||
@@ -1014,7 +1008,7 @@ system:gcc:out-of-tree:
|
||||
artifacts: true
|
||||
<<: *base_image
|
||||
<<: *system_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
|
||||
unit:gcc:out-of-tree:
|
||||
variables:
|
||||
@@ -1024,7 +1018,7 @@ unit:gcc:out-of-tree:
|
||||
artifacts: true
|
||||
<<: *base_image
|
||||
<<: *unit_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
|
||||
# Jobs for tarball GCC builds on Debian 12 "bookworm" (amd64)
|
||||
|
||||
@@ -1044,7 +1038,7 @@ gcc:tarball:
|
||||
system:gcc:tarball:
|
||||
<<: *base_image
|
||||
<<: *system_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
before_script:
|
||||
- cd bind-*
|
||||
- *setup_interfaces
|
||||
@@ -1058,7 +1052,7 @@ system:gcc:tarball:
|
||||
unit:gcc:tarball:
|
||||
<<: *base_image
|
||||
<<: *unit_test_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
before_script:
|
||||
- cd bind-*
|
||||
needs:
|
||||
@@ -1337,9 +1331,7 @@ unit:clang:bookworm:amd64:
|
||||
clang:freebsd13:amd64:
|
||||
variables:
|
||||
CFLAGS: "${CFLAGS_COMMON}"
|
||||
# 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"
|
||||
EXTRA_CONFIGURE: "${WITH_READLINE_LIBEDIT}"
|
||||
USER: gitlab-runner
|
||||
<<: *freebsd_13_amd64_image
|
||||
<<: *build_job
|
||||
@@ -1365,9 +1357,7 @@ unit:clang:freebsd13:amd64:
|
||||
clang:freebsd14:amd64:
|
||||
variables:
|
||||
CFLAGS: "${CFLAGS_COMMON}"
|
||||
# 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"
|
||||
EXTRA_CONFIGURE: "${WITH_READLINE_EDITLINE}"
|
||||
USER: gitlab-runner
|
||||
<<: *freebsd_14_amd64_image
|
||||
<<: *build_job
|
||||
@@ -1577,7 +1567,7 @@ respdiff-short:tsan:
|
||||
|
||||
respdiff-long:
|
||||
<<: *respdiff_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *respdiff_debian_bookworm_amd64_image
|
||||
variables:
|
||||
CC: gcc
|
||||
@@ -1588,7 +1578,7 @@ respdiff-long:
|
||||
|
||||
respdiff-long:asan:
|
||||
<<: *respdiff_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *debian_bookworm_amd64_image
|
||||
variables:
|
||||
CC: gcc
|
||||
@@ -1601,7 +1591,7 @@ respdiff-long:asan:
|
||||
|
||||
respdiff-long:tsan:
|
||||
<<: *respdiff_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *tsan_debian_bookworm_amd64_image
|
||||
variables:
|
||||
CC: gcc
|
||||
@@ -1619,7 +1609,7 @@ respdiff-long:tsan:
|
||||
|
||||
respdiff-long-third-party:
|
||||
<<: *respdiff_job
|
||||
<<: *api_pipelines_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *api_schedules_tags_triggers_web_triggering_rules
|
||||
<<: *debian_bookworm_amd64_image
|
||||
variables:
|
||||
CC: gcc
|
||||
@@ -1768,7 +1758,7 @@ stress:rpz:fedora:39:arm64:
|
||||
variables:
|
||||
- $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /linux/i && $BIND_STRESS_TEST_MODE =~ /rpz/i && $BIND_STRESS_TEST_ARCH =~ /arm64/i)
|
||||
|
||||
stress:authoritative:freebsd13:amd64:
|
||||
stress:authoritative:freebsd12:amd64:
|
||||
<<: *freebsd_stress_amd64
|
||||
<<: *stress_job
|
||||
variables:
|
||||
@@ -1782,7 +1772,7 @@ stress:authoritative:freebsd13:amd64:
|
||||
variables:
|
||||
- $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /freebsd/i && $BIND_STRESS_TEST_MODE =~ /authoritative/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i)
|
||||
|
||||
stress:recursive:freebsd13:amd64:
|
||||
stress:recursive:freebsd12:amd64:
|
||||
<<: *freebsd_stress_amd64
|
||||
<<: *stress_job
|
||||
variables:
|
||||
@@ -1796,7 +1786,7 @@ stress:recursive:freebsd13:amd64:
|
||||
variables:
|
||||
- $CI_COMMIT_TAG || ($BIND_STRESS_TEST_OS =~ /freebsd/i && $BIND_STRESS_TEST_MODE =~ /recursive/i && $BIND_STRESS_TEST_ARCH =~ /amd64/i)
|
||||
|
||||
stress:rpz:freebsd13:amd64:
|
||||
stress:rpz:freebsd12:amd64:
|
||||
<<: *freebsd_stress_amd64
|
||||
<<: *stress_job
|
||||
variables:
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
## 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 charts from `shotgun:*` jobs in the scheduled pipelines to verify there is no unexplained performance drop for any protocol.
|
||||
- [ ] ***(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 the HTML version 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)*** Rebase the Subscription Edition branches (including recent release prep commits) on top of the open source branches with updated version strings.
|
||||
- [ ] ***(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.
|
||||
- [ ] ***(QA)*** Prepare the `patches/` subdirectory for each security release (if applicable).
|
||||
- [ ] ***(QA)*** 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)
|
||||
- [ ] ***(Marketing)*** Prepare and send out ASN emails (as outlined in the CVE checklist; if applicable).
|
||||
|
||||
### On the Day of Public Release
|
||||
|
||||
- [ ] ***(QA)*** Wait for clearance from Security Officer to proceed with the public release (if applicable).
|
||||
- [ ] ***(QA)*** Place tarballs in public location on FTP site.
|
||||
- [ ] ***(QA)*** Inform Marketing of the release, providing FTP links for the published tarballs.
|
||||
- [ ] ***(QA)*** Use the [Printing Press project](https://gitlab.isc.org/isc-private/printing-press/-/wikis/home#adding-new-documents) to prepare a release announcement email.
|
||||
- [ ] ***(Marketing)*** Publish links to downloads on ISC website. [Example](https://gitlab.isc.org/website/theme-staging-site/-/commit/1ac7b30b73cb03228df4cd5651fa4e774ac35625)
|
||||
- [ ] ***(Marketing)*** Update the BIND -S information document in SF with download links to the new versions. (If this is a security release, this will have already been done as part of the ASN process.)
|
||||
- [ ] ***(Marketing)*** Update the Current Software Versions document in the SF portal if any stable versions were released.
|
||||
- [ ] ***(Marketing)*** Send the release announcement email to the *bind-announce* mailing list (and to *bind-users* if a major release - [example](https://lists.isc.org/pipermail/bind-users/2022-January/105624.html)).
|
||||
- [ ] ***(Marketing)*** Announce release on social media sites.
|
||||
- [ ] ***(Marketing)*** Update [Wikipedia entry for BIND](https://en.wikipedia.org/wiki/BIND).
|
||||
- [ ] ***(Support)*** Add the new releases to the [vulnerability matrix in the Knowledge Base](https://kb.isc.org/docs/aa-00913).
|
||||
- [ ] ***(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)*** 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.
|
||||
@@ -1,39 +1,6 @@
|
||||
6369. [func] The 'fixed' value for the 'rrset-order' option has
|
||||
been marked and documented as deprecated. [GL #4446]
|
||||
|
||||
6368. [func] The 'sortlist' option has been marked and documented
|
||||
as deprecated. [GL #4593]
|
||||
|
||||
6367. [bug] Since the dns_validator_destroy() function doesn't
|
||||
guarantee that it destroys the validator, rename it to
|
||||
dns_validator_shutdown() and require explicit
|
||||
dns_validator_detach() to follow. Implement an expected
|
||||
behavior of the function to release a name associated
|
||||
with the validator. [GL #4654]
|
||||
|
||||
6366. [bug] An assertion could be triggered in the QPDB cache when
|
||||
encountering a delegation below a DNAME. [GL #4652]
|
||||
|
||||
6365. [placeholder]
|
||||
|
||||
6364. [protocol] Add RESOLVER.ARPA to the built in empty zones.
|
||||
[GL #4580]
|
||||
|
||||
6363. [bug] dig/mdig +ednsflags=<non-zero-value> did not re-enable
|
||||
EDNS if it had been disabled. [GL #4641]
|
||||
|
||||
6362. [bug] Reduce memory consumption of QP-trie based databases
|
||||
by dynamically allocating the nodenames. [GL #4614]
|
||||
|
||||
6361. [bug] Some invalid ISO 8601 durations were accepted
|
||||
erroneously. [GL #4624]
|
||||
|
||||
6360. [bug] Don't return static-stub synthesised NS RRset.
|
||||
[GL #4608]
|
||||
|
||||
6359. [bug] Fix bug in Depends (keymgr_dep) function. [GL #4552]
|
||||
|
||||
--- 9.19.22 released ---
|
||||
6359. [bug] Reduce memory consumption of QP zone and cache
|
||||
databases by dynamically allocating nodenames.
|
||||
[GL #4614]
|
||||
|
||||
6358. [bug] Fix validate_dnskey_dsset when KSK is not signing,
|
||||
do not skip remainder of DS RRset. [GL #4625]
|
||||
|
||||
+6
-5
@@ -2103,8 +2103,9 @@ 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(),
|
||||
recvresponse, message, &request));
|
||||
DNS_REQUESTOPT_TCP, NULL, 1, 0, 0,
|
||||
isc_loop_current(loopmgr), recvresponse,
|
||||
message, &request));
|
||||
return;
|
||||
|
||||
cleanup:
|
||||
@@ -2166,8 +2167,8 @@ run_server(void *arg) {
|
||||
dns_view_initsecroots(view);
|
||||
CHECK(setup_dnsseckeys(NULL, view));
|
||||
|
||||
CHECK(dns_view_createresolver(view, netmgr, 0, tlsctx_client_cache,
|
||||
dispatch, NULL));
|
||||
CHECK(dns_view_createresolver(view, loopmgr, netmgr, 0,
|
||||
tlsctx_client_cache, dispatch, NULL));
|
||||
|
||||
isc_stats_create(mctx, &resstats, dns_resstatscounter_max);
|
||||
dns_resolver_setstats(view->resolver, resstats);
|
||||
@@ -2186,7 +2187,7 @@ run_server(void *arg) {
|
||||
NULL, NULL, ISC_NM_PROXY_NONE,
|
||||
&ifp->tcplistensocket));
|
||||
ifp->flags |= NS_INTERFACEFLAG_LISTENING;
|
||||
isc_async_current(sendquery, ifp->tcplistensocket);
|
||||
isc_async_current(loopmgr, sendquery, ifp->tcplistensocket);
|
||||
|
||||
return;
|
||||
|
||||
|
||||
@@ -1797,10 +1797,6 @@ 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':
|
||||
|
||||
@@ -1674,7 +1674,7 @@ assignwork(void *arg) {
|
||||
lock_and_dumpnode(dns_fixedname_name(&fname), node);
|
||||
dns_db_detachnode(gdb, &node);
|
||||
|
||||
isc_async_current(assignwork, NULL);
|
||||
isc_async_current(loopmgr, assignwork, NULL);
|
||||
}
|
||||
|
||||
/*%
|
||||
|
||||
+8
-11
@@ -90,7 +90,6 @@
|
||||
#include <dns/order.h>
|
||||
#include <dns/peer.h>
|
||||
#include <dns/private.h>
|
||||
#include <dns/rbt.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rdatalist.h>
|
||||
#include <dns/rdataset.h>
|
||||
@@ -152,11 +151,11 @@
|
||||
#endif /* HAVE_LMDB */
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#define SIZE_MAX ((size_t) - 1)
|
||||
#endif /* ifndef SIZE_MAX */
|
||||
|
||||
#ifndef SIZE_AS_PERCENT
|
||||
#define SIZE_AS_PERCENT ((size_t)-2)
|
||||
#define SIZE_AS_PERCENT ((size_t) - 2)
|
||||
#endif /* ifndef SIZE_AS_PERCENT */
|
||||
|
||||
/* RFC7828 defines timeout as 16-bit value specified in units of 100
|
||||
@@ -400,9 +399,6 @@ const char *empty_zones[] = {
|
||||
/* RFC 8375 */
|
||||
"HOME.ARPA",
|
||||
|
||||
/* RFC 9462 */
|
||||
"RESOLVER.ARPA",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -4701,9 +4697,9 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
CHECK(dns_view_createresolver(view, named_g_netmgr, resopts,
|
||||
named_g_server->tlsctx_client_cache,
|
||||
dispatch4, dispatch6));
|
||||
CHECK(dns_view_createresolver(
|
||||
view, named_g_loopmgr, named_g_netmgr, resopts,
|
||||
named_g_server->tlsctx_client_cache, dispatch4, dispatch6));
|
||||
|
||||
if (resstats == NULL) {
|
||||
isc_stats_create(mctx, &resstats, dns_resstatscounter_max);
|
||||
@@ -8187,7 +8183,7 @@ load_configuration(const char *filename, named_server_t *server,
|
||||
/*
|
||||
* Require the reconfiguration to happen always on the main loop
|
||||
*/
|
||||
REQUIRE(isc_loop() == named_g_mainloop);
|
||||
REQUIRE(isc_loop_current(named_g_loopmgr) == named_g_mainloop);
|
||||
|
||||
ISC_LIST_INIT(kasplist);
|
||||
ISC_LIST_INIT(keystorelist);
|
||||
@@ -9838,7 +9834,8 @@ 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_netmgr, &server->zonemgr);
|
||||
dns_zonemgr_create(named_g_mctx, named_g_loopmgr, named_g_netmgr,
|
||||
&server->zonemgr);
|
||||
|
||||
CHECKFATAL(dns_dispatchmgr_create(named_g_mctx, named_g_loopmgr,
|
||||
named_g_netmgr, &named_g_dispatchmgr),
|
||||
|
||||
@@ -2484,7 +2484,7 @@ static void
|
||||
done_update(void) {
|
||||
ddebug("done_update()");
|
||||
|
||||
isc_async_current(getinput, NULL);
|
||||
isc_async_current(loopmgr, getinput, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -10,9 +10,5 @@
|
||||
# information regarding copyright ownership.
|
||||
|
||||
|
||||
import isctest.mark
|
||||
|
||||
|
||||
@isctest.mark.flaky(max_runs=2)
|
||||
def test_autosign(run_tests_sh):
|
||||
run_tests_sh()
|
||||
|
||||
@@ -120,8 +120,7 @@ A.E.F.IP6.ARPA
|
||||
B.E.F.IP6.ARPA
|
||||
8.B.D.0.1.0.0.2.IP6.ARPA
|
||||
EMPTY.AS112.ARPA
|
||||
HOME.ARPA
|
||||
RESOLVER.ARPA"
|
||||
HOME.ARPA"
|
||||
|
||||
n=$((n + 1))
|
||||
ret=0
|
||||
@@ -135,7 +134,7 @@ for zone in ${emptyzones}; do
|
||||
count=$((count + 1))
|
||||
done
|
||||
lines=$(grep "automatic empty zone: " ns1/named.run | wc -l)
|
||||
test $count -eq $lines -a $count -eq 100 || {
|
||||
test $count -eq $lines -a $count -eq 99 || {
|
||||
ret=1
|
||||
echo_i "failed (count mismatch)"
|
||||
}
|
||||
|
||||
@@ -51,22 +51,22 @@ sub reply_handler {
|
||||
STDOUT->flush();
|
||||
|
||||
if ($qname eq "example.broken") {
|
||||
if ($qtype eq "SOA") {
|
||||
if ($qtype eq "SOA") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
|
||||
push @ans, $rr;
|
||||
} elsif ($qtype eq "NS") {
|
||||
} elsif ($qtype eq "NS") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname");
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
|
||||
push @add, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "cname-to-$synth2") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
|
||||
$rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
|
||||
$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
|
||||
push @ans, $rr;
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "$synth" || $qname eq "$synth2") {
|
||||
@@ -115,30 +115,6 @@ sub reply_handler {
|
||||
push @ans, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
# The next few branches produce a zone with an illegal NS below a DNAME.
|
||||
} elsif ($qname eq "jeff.dname") {
|
||||
if ($qtype eq "SOA") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
|
||||
push @ans, $rr;
|
||||
} elsif ($qtype eq "NS") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass NS ns.jeff.dname.");
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
|
||||
push @add, $rr;
|
||||
} elsif ($qtype eq "DNAME") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME mutt.example.");
|
||||
push @ans, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "ns.jeff.dname") {
|
||||
if ($qtype eq "A") {
|
||||
my $rr = new Net::DNS::RR("$qname $ttl $qclass A 10.53.0.3");
|
||||
push @ans, $rr;
|
||||
} elsif ($qtype eq "AAAA") {
|
||||
my $rr = new Net::DNS::RR("jeff.dname. $ttl $qclass SOA . . 0 0 0 0 $ttl");
|
||||
push @auth, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} else {
|
||||
$rcode = "REFUSED";
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ ns3.example.broken. A 10.53.0.3
|
||||
example.dname. NS ns3.example.dname.
|
||||
ns3.example.dname. A 10.53.0.3
|
||||
|
||||
; regression test for illegal NS below DNAME
|
||||
jeff.dname. NS ns.jeff.dname.
|
||||
ns.jeff.dname. A 10.53.0.3
|
||||
|
||||
domain0.nil. NS ns2.domain0.nil
|
||||
domain1.nil. NS ns2.domain0.nil
|
||||
domain2.nil. NS ns2.domain0.nil
|
||||
|
||||
@@ -48,9 +48,6 @@ signed-sub2 NS ns2.sub2
|
||||
signed-sub2 DS 44137 8 2 1CB4F54E0B4F4F85109143113A3C679716A2377D86EB0907846A03FB 0C0A3927
|
||||
d CNAME d.signed-sub2
|
||||
|
||||
mutt NS ns5.mutt
|
||||
ns5.mutt A 10.53.0.5
|
||||
|
||||
; long CNAME loop
|
||||
loop CNAME goop
|
||||
goop CNAME boop
|
||||
|
||||
@@ -40,8 +40,3 @@ zone "signed-sub5.example" {
|
||||
type primary;
|
||||
file "sub.db";
|
||||
};
|
||||
|
||||
zone "mutt.example" {
|
||||
type primary;
|
||||
file "mutt.db";
|
||||
};
|
||||
|
||||
@@ -626,16 +626,5 @@ grep 'status: NOERROR' dig.out.7.$n >/dev/null 2>&1 || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
# Regression test for GL #4652
|
||||
n=$((n + 1))
|
||||
echo_i "checking handling of illegal NS below DNAME ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS @10.53.0.7 DNAME jeff.dname. >dig.out.ns7.1.$n 2>&1
|
||||
grep 'status: NOERROR' dig.out.ns7.1.$n >/dev/null 2>&1 || ret=1
|
||||
$DIG $DIGOPTS @10.53.0.7 NS jeff.dname. >dig.out.ns7.2.$n 2>&1
|
||||
grep 'status: SERVFAIL' dig.out.ns7.2.$n >/dev/null 2>&1 || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
||||
@@ -1,25 +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.
|
||||
*/
|
||||
|
||||
dnssec-policy "invalid-sigrefresh" {
|
||||
keys {
|
||||
csk lifetime unlimited algorithm 13;
|
||||
};
|
||||
signatures-refresh P7.5D;
|
||||
};
|
||||
|
||||
zone "example.net" {
|
||||
type primary;
|
||||
file "example.db";
|
||||
dnssec-policy "invalid-sigrefresh";
|
||||
};
|
||||
@@ -28,12 +28,6 @@ options {
|
||||
avoid-v6-udp-ports { range 1 1023; };
|
||||
|
||||
dnssec-must-be-secure mustbesecure.example yes;
|
||||
|
||||
sortlist { };
|
||||
|
||||
rrset-order {
|
||||
name "fixed.example" order fixed;
|
||||
};
|
||||
};
|
||||
|
||||
trusted-keys {
|
||||
|
||||
@@ -165,12 +165,6 @@ warnings=$(grep "'notify' is disabled" <checkconf.out$n | wc -l)
|
||||
if [ $ret -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
if grep "^#define DNS_RDATASET_FIXED" "$TOP_BUILDDIR/config.h" >/dev/null 2>&1; then
|
||||
test_fixed=true
|
||||
else
|
||||
test_fixed=false
|
||||
fi
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "checking named-checkconf deprecate warnings ($n)"
|
||||
ret=0
|
||||
@@ -185,18 +179,12 @@ grep "option 'avoid-v6-udp-ports' is deprecated" <checkconf.out$n.1 >/dev/null |
|
||||
grep "option 'dialup' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
grep "option 'heartbeat-interval' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
grep "option 'dnssec-must-be-secure' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
grep "option 'sortlist' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
grep "token 'port' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
if $test_fixed; then
|
||||
grep "rrset-order: order 'fixed' is deprecated" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
else
|
||||
grep "rrset-order: order 'fixed' was disabled at compilation time" <checkconf.out$n.1 >/dev/null || ret=1
|
||||
fi
|
||||
if [ $ret -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# set -i to ignore deprecate warnings
|
||||
$CHECKCONF -i deprecated.conf 2>&1 | grep_v "rrset-order: order 'fixed' was disabled at compilation time" >checkconf.out$n.2
|
||||
grep '^.+$' <checkconf.out$n.2 >/dev/null && ret=1
|
||||
$CHECKCONF -i deprecated.conf >checkconf.out$n.2 2>&1
|
||||
grep '.*' <checkconf.out$n.2 >/dev/null && ret=1
|
||||
if [ $ret -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ status=$((status + ret))
|
||||
|
||||
echo_i "checking with max ttl (text) ($n)"
|
||||
ret=0
|
||||
$CHECKZONE -i local -l 300 example zones/good1.db >test.out1.$n 2>&1 && ret=1
|
||||
$CHECKZONE -i local -l 600 example zones/good1.db >test.out2.$n 2>&1 || ret=1
|
||||
$CHECKZONE -l 300 example zones/good1.db >test.out1.$n 2>&1 && ret=1
|
||||
$CHECKZONE -l 600 example zones/good1.db >test.out2.$n 2>&1 || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
@@ -650,5 +650,3 @@ parse_openssl_config() {
|
||||
esac
|
||||
done < "$OPENSSL_CONF"
|
||||
}
|
||||
|
||||
grep_v() { grep -v "$@" || test $? = 1; }
|
||||
|
||||
@@ -68,8 +68,6 @@ export KRB5_CONFIG=/dev/null
|
||||
# use local keytab instead of default /etc/krb5.keytab
|
||||
export KRB5_KTNAME=dns.keytab
|
||||
|
||||
export ANS_LOG_LEVEL=debug
|
||||
|
||||
#
|
||||
# Programs detected by configure
|
||||
# Variables will be empty if no program was found by configure
|
||||
|
||||
@@ -30,9 +30,6 @@ import isctest
|
||||
# pylint: disable=redefined-outer-name
|
||||
|
||||
|
||||
isctest.log.init_conftest_logger()
|
||||
isctest.log.avoid_duplicated_logs()
|
||||
|
||||
# ----------------- Older pytest / xdist compatibility -------------------
|
||||
# As of 2023-01-11, the minimal supported pytest / xdist versions are
|
||||
# determined by what is available in EL8/EPEL8:
|
||||
|
||||
@@ -20,7 +20,7 @@ import dns.message
|
||||
def test_dialup_zone_transfer(named_port, servers, ns):
|
||||
msg = dns.message.make_query("example.", "SOA")
|
||||
# Drop the RD flag from the query
|
||||
msg.flags &= ~dns.flags.RD
|
||||
msg.flags -= dns.flags.RD
|
||||
ns1response = isctest.query.tcp(msg, "10.53.0.1")
|
||||
with servers[f"ns{ns}"].watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line(
|
||||
|
||||
@@ -1117,14 +1117,6 @@ if [ -x "$DIG" ]; then
|
||||
grep -F "IN A 10.0.0.1" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check that dig +noedns +ednsflags=<nonzero> re-enables EDNS ($n)"
|
||||
dig_with_opts @10.53.0.3 +qr +noedns +ednsflags=0x70 a.example >dig.out.test$n 2>&1 || ret=1
|
||||
grep "; EDNS: version: 0, flags:; MBZ: 0x0070, udp: 1232" dig.out.test$n >/dev/null || ret=1
|
||||
grep "; EDNS: version: 0, flags:; udp: 1232" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
else
|
||||
echo_i "$DIG is needed, so skipping these dig tests"
|
||||
fi
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include <dns/db.h>
|
||||
#include <dns/diff.h>
|
||||
#include <dns/enumclass.h>
|
||||
#include <dns/rbt.h>
|
||||
#include <dns/rdatalist.h>
|
||||
#include <dns/rdatastruct.h>
|
||||
#include <dns/soa.h>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
[ "prereq/var/tmp/etc/openssl-provider.cnf" = "prereq${OPENSSL_CONF}" ] || {
|
||||
[ "prereq/var/tmp/etc/openssl-provider.cnf" -eq "prereq${OPENSSL_CONF}" ] || {
|
||||
echo_i "skip: pkcs11-provider not enabled"
|
||||
exit 255
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import isctest.mark
|
||||
|
||||
|
||||
@isctest.mark.flaky(max_runs=3) # GL#4605
|
||||
def test_enginepkcs11(run_tests_sh):
|
||||
run_tests_sh()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
rm -f */named.conf
|
||||
rm -f */named.memstats
|
||||
rm -f */named.run
|
||||
rm -f dig.out
|
||||
rm -f ns*/K*
|
||||
rm -f ns*/dsset-*
|
||||
rm -f ns*/managed-keys.bind*
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
; <<>> DiG 9.0 <<>> +norec @10.53.0.1 -p 5300 foo.bar.fi. A
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58772
|
||||
;; flags: qr ad; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 7
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;foo.bar.fi. IN A
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
fi. 172800 IN NS NS.EU.NET.
|
||||
fi. 172800 IN NS NS.TELE.fi.
|
||||
fi. 172800 IN NS PRIFI.EUNET.fi.
|
||||
fi. 172800 IN NS NS.UU.NET.
|
||||
fi. 172800 IN NS T.NS.VERIO.NET.
|
||||
fi. 172800 IN NS HYDRA.HELSINKI.fi.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
NS.TELE.fi. 172800 IN A 193.210.19.19
|
||||
NS.TELE.fi. 172800 IN A 193.210.18.18
|
||||
PRIFI.EUNET.fi. 172800 IN A 193.66.1.146
|
||||
NS.UU.NET. 172800 IN A 137.39.1.3
|
||||
T.NS.VERIO.NET. 172800 IN A 192.67.14.16
|
||||
HYDRA.HELSINKI.fi. 172800 IN A 128.214.4.29
|
||||
NS.EU.NET. 172800 IN A 192.16.202.11
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
; <<>> DiG 9.0 <<>> @10.53.0.1 -p 5300 example.net a
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29409
|
||||
;; flags: qr rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;example.net. IN A
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
example.net. 300 IN NS ns2.example.
|
||||
example.net. 300 IN NS ns1.example.
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
dig_with_opts() {
|
||||
"$DIG" +norec -p "${PORT}" "$@"
|
||||
}
|
||||
|
||||
status=0
|
||||
n=0
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing that a ccTLD referral gets a full glue set from the root zone ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 foo.bar.fi. A >dig.out.$n || ret=1
|
||||
digcomp --lc fi.good dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing that we don't find out-of-zone glue ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 example.net. A >dig.out.$n || ret=1
|
||||
digcomp noglue.good dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for unsigned referrals close to UDP packet size limit (A glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +noedns foo.subdomain-a.tc-test-unsigned. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for unsigned referrals close to UDP packet size limit (AAAA glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +noedns foo.subdomain-aaaa.tc-test-unsigned. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for unsigned referrals close to UDP packet size limit (A+AAAA glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +noedns foo.subdomain-both.tc-test-unsigned. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for signed referrals close to UDP packet size limit (A glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +dnssec +bufsize=512 foo.subdomain-a.tc-test-signed. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for signed referrals close to UDP packet size limit (AAAA glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +dnssec +bufsize=512 foo.subdomain-aaaa.tc-test-signed. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "testing truncation for signed referrals close to UDP packet size limit (A+AAAA glue) ($n)"
|
||||
ret=0
|
||||
dig_with_opts @10.53.0.1 +ignore +dnssec +bufsize=512 foo.subdomain-both.tc-test-signed. >dig.out.$n || ret=1
|
||||
grep -q "flags:[^;]* tc" dig.out.$n || ret=1
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
@@ -1,104 +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.
|
||||
|
||||
import dns.message
|
||||
|
||||
import isctest
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("dns", minversion="2.0.0")
|
||||
|
||||
|
||||
def test_glue_full_glue_set():
|
||||
"""test that a ccTLD referral gets a full glue set from the root zone"""
|
||||
msg = dns.message.make_query("foo.bar.fi", "A")
|
||||
msg.flags &= ~dns.flags.RD
|
||||
res = isctest.query.udp(msg, "10.53.0.1")
|
||||
|
||||
answer = """;ANSWER
|
||||
;AUTHORITY
|
||||
fi. 172800 IN NS HYDRA.HELSINKI.fi.
|
||||
fi. 172800 IN NS NS.EU.NET.
|
||||
fi. 172800 IN NS NS.UU.NET.
|
||||
fi. 172800 IN NS NS.TELE.fi.
|
||||
fi. 172800 IN NS T.NS.VERIO.NET.
|
||||
fi. 172800 IN NS PRIFI.EUNET.fi.
|
||||
;ADDITIONAL
|
||||
NS.TELE.fi. 172800 IN A 193.210.18.18
|
||||
NS.TELE.fi. 172800 IN A 193.210.19.19
|
||||
PRIFI.EUNET.fi. 172800 IN A 193.66.1.146
|
||||
HYDRA.HELSINKI.fi. 172800 IN A 128.214.4.29
|
||||
NS.EU.NET. 172800 IN A 192.16.202.11
|
||||
T.NS.VERIO.NET. 172800 IN A 192.67.14.16
|
||||
NS.UU.NET. 172800 IN A 137.39.1.3
|
||||
"""
|
||||
expected_answer = dns.message.from_text(answer)
|
||||
|
||||
isctest.check.noerror(res)
|
||||
isctest.check.rrsets_equal(res.answer, expected_answer.answer)
|
||||
isctest.check.rrsets_equal(res.authority, expected_answer.authority)
|
||||
isctest.check.rrsets_equal(res.additional, expected_answer.additional)
|
||||
|
||||
|
||||
def test_glue_no_glue_set():
|
||||
"""test that out-of-zone glue is not found"""
|
||||
msg = dns.message.make_query("example.net.", "A")
|
||||
msg.flags &= ~dns.flags.RD
|
||||
res = isctest.query.udp(msg, "10.53.0.1")
|
||||
|
||||
answer = """;ANSWER
|
||||
;AUTHORITY
|
||||
example.net. 300 IN NS ns2.example.
|
||||
example.net. 300 IN NS ns1.example.
|
||||
;ADDITIONAL
|
||||
"""
|
||||
expected_answer = dns.message.from_text(answer)
|
||||
|
||||
isctest.check.noerror(res)
|
||||
isctest.check.rrsets_equal(res.answer, expected_answer.answer)
|
||||
isctest.check.rrsets_equal(res.authority, expected_answer.authority)
|
||||
isctest.check.rrsets_equal(res.additional, expected_answer.additional)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"qname,dnssec",
|
||||
[
|
||||
# test truncation for unsigned referrals close to UDP packet size limit (A glue)
|
||||
("foo.subdomain-a.tc-test-unsigned.", False),
|
||||
# test truncation for unsigned referrals close to UDP packet size limit (AAAA glue)
|
||||
("foo.subdomain-aaaa.tc-test-unsigned.", False),
|
||||
# test truncation for unsigned referrals close to UDP packet size limit (A+AAAA glue)
|
||||
("foo.subdomain-both.tc-test-unsigned.", False),
|
||||
# test truncation for signed referrals close to UDP packet size limit (A glue)
|
||||
("foo.subdomain-a.tc-test-signed.", True),
|
||||
# test truncation for signed referrals close to UDP packet size limit (AAAA glue)
|
||||
("foo.subdomain-aaaa.tc-test-signed.", True),
|
||||
# test truncation for signed referrals close to UDP packet size limit (A+AAAA glue)
|
||||
("foo.subdomain-both.tc-test-signed.", True),
|
||||
],
|
||||
)
|
||||
def test_glue_truncation(qname, dnssec):
|
||||
msg = dns.message.make_query(qname, "A")
|
||||
msg.flags &= ~dns.flags.RD
|
||||
if dnssec:
|
||||
msg.use_edns(
|
||||
payload=512,
|
||||
# Zones used in this test were created with dig in mind that, unlike dnspython,
|
||||
# by default, sets a cookie. Given that the message size must be close to the
|
||||
# truncation limit, we also need to set a cookie here.
|
||||
options=[dns.edns.GenericOption(dns.edns.OptionType.COOKIE, b"0xda13cc")],
|
||||
)
|
||||
msg.want_dnssec(wanted=True)
|
||||
res = isctest.query.udp(msg, "10.53.0.1")
|
||||
|
||||
isctest.check.noerror(res)
|
||||
assert res.flags & dns.flags.TC
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
|
||||
|
||||
def test_glue(run_tests_sh):
|
||||
run_tests_sh()
|
||||
@@ -1,799 +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.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
Callable,
|
||||
Coroutine,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
import abc
|
||||
import asyncio
|
||||
import enum
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import signal
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.node
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
import dns.zone
|
||||
|
||||
try:
|
||||
RdataType = dns.rdatatype.RdataType
|
||||
RdataClass = dns.rdataclass.RdataClass
|
||||
except AttributeError: # dnspython < 2.0.0 compat
|
||||
RdataType = int # type: ignore
|
||||
RdataClass = int # type: ignore
|
||||
|
||||
|
||||
_UdpHandler = Callable[
|
||||
[bytes, Tuple[str, int], asyncio.DatagramTransport], Coroutine[Any, Any, None]
|
||||
]
|
||||
|
||||
|
||||
_TcpHandler = Callable[
|
||||
[asyncio.StreamReader, asyncio.StreamWriter], Coroutine[Any, Any, None]
|
||||
]
|
||||
|
||||
|
||||
class _AsyncUdpHandler(asyncio.DatagramProtocol):
|
||||
"""
|
||||
Protocol implementation for handling UDP traffic using asyncio.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
handler: _UdpHandler,
|
||||
) -> None:
|
||||
self._transport: Optional[asyncio.DatagramTransport] = None
|
||||
self._handler: _UdpHandler = handler
|
||||
|
||||
def connection_made(self, transport: asyncio.BaseTransport) -> None:
|
||||
"""
|
||||
Called by asyncio when a connection is made.
|
||||
"""
|
||||
self._transport = cast(asyncio.DatagramTransport, transport)
|
||||
|
||||
def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None:
|
||||
"""
|
||||
Called by asyncio when a datagram is received.
|
||||
"""
|
||||
assert self._transport
|
||||
handler_coroutine = self._handler(data, addr, self._transport)
|
||||
try:
|
||||
# Python >= 3.7
|
||||
asyncio.create_task(handler_coroutine)
|
||||
except AttributeError:
|
||||
# Python < 3.7
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(handler_coroutine)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class AsyncServer:
|
||||
"""
|
||||
A generic asynchronous server which may handle UDP and/or TCP traffic.
|
||||
|
||||
Once the server is executed as asyncio coroutine, it will keep running
|
||||
until a SIGINT/SIGTERM signal is received.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
udp_handler: Optional[_UdpHandler],
|
||||
tcp_handler: Optional[_TcpHandler],
|
||||
pidfile: Optional[str] = None,
|
||||
) -> None:
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s %(levelname)8s %(message)s",
|
||||
level=os.environ.get("ANS_LOG_LEVEL", "INFO").upper(),
|
||||
)
|
||||
try:
|
||||
ipv4_address = sys.argv[1]
|
||||
except IndexError:
|
||||
ipv4_address = self._get_ipv4_address_from_directory_name()
|
||||
|
||||
last_ipv4_address_octet = ipv4_address.split(".")[-1]
|
||||
ipv6_address = f"fd92:7065:b8e:ffff::{last_ipv4_address_octet}"
|
||||
|
||||
try:
|
||||
port = int(sys.argv[2])
|
||||
except IndexError:
|
||||
port = int(os.environ.get("PORT", 5300))
|
||||
|
||||
logging.info("Setting up IPv4 listener at %s:%d", ipv4_address, port)
|
||||
logging.info("Setting up IPv6 listener at [%s]:%d", ipv6_address, port)
|
||||
|
||||
self._ip_addresses: Tuple[str, str] = (ipv4_address, ipv6_address)
|
||||
self._port: int = port
|
||||
self._udp_handler: Optional[_UdpHandler] = udp_handler
|
||||
self._tcp_handler: Optional[_TcpHandler] = tcp_handler
|
||||
self._pidfile: Optional[str] = pidfile
|
||||
self._work_done: Optional[asyncio.Future] = None
|
||||
|
||||
def _get_ipv4_address_from_directory_name(self) -> str:
|
||||
containing_directory = pathlib.Path().absolute().stem
|
||||
match_result = re.match(r"ans(?P<index>\d+)", containing_directory)
|
||||
if not match_result:
|
||||
raise RuntimeError("Unable to auto-determine the IPv4 address to use")
|
||||
|
||||
return f"10.53.0.{match_result.group('index')}"
|
||||
|
||||
def run(self) -> None:
|
||||
"""
|
||||
Start the server in an asynchronous coroutine.
|
||||
"""
|
||||
coroutine = self._run
|
||||
try:
|
||||
# Python >= 3.7
|
||||
asyncio.run(coroutine())
|
||||
except AttributeError:
|
||||
# Python < 3.7
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(coroutine())
|
||||
|
||||
async def _run(self) -> None:
|
||||
self._setup_signals()
|
||||
assert self._work_done
|
||||
await self._listen_udp()
|
||||
await self._listen_tcp()
|
||||
self._write_pidfile()
|
||||
await self._work_done
|
||||
self._cleanup_pidfile()
|
||||
|
||||
def _get_asyncio_loop(self) -> asyncio.AbstractEventLoop:
|
||||
try:
|
||||
# Python >= 3.7
|
||||
loop = asyncio.get_running_loop()
|
||||
except AttributeError:
|
||||
# Python < 3.7
|
||||
loop = asyncio.get_event_loop()
|
||||
return loop
|
||||
|
||||
def _setup_signals(self) -> None:
|
||||
loop = self._get_asyncio_loop()
|
||||
self._work_done = loop.create_future()
|
||||
loop.add_signal_handler(signal.SIGINT, functools.partial(self._signal_done))
|
||||
loop.add_signal_handler(signal.SIGTERM, functools.partial(self._signal_done))
|
||||
|
||||
def _signal_done(self) -> None:
|
||||
assert self._work_done
|
||||
self._work_done.set_result(True)
|
||||
|
||||
async def _listen_udp(self) -> None:
|
||||
if not self._udp_handler:
|
||||
return
|
||||
loop = self._get_asyncio_loop()
|
||||
for ip_address in self._ip_addresses:
|
||||
await loop.create_datagram_endpoint(
|
||||
lambda: _AsyncUdpHandler(cast(_UdpHandler, self._udp_handler)),
|
||||
(ip_address, self._port),
|
||||
)
|
||||
|
||||
async def _listen_tcp(self) -> None:
|
||||
if not self._tcp_handler:
|
||||
return
|
||||
for ip_address in self._ip_addresses:
|
||||
await asyncio.start_server(
|
||||
self._tcp_handler, host=ip_address, port=self._port
|
||||
)
|
||||
|
||||
def _write_pidfile(self) -> None:
|
||||
if not self._pidfile:
|
||||
return
|
||||
logging.info("Writing PID to %s", self._pidfile)
|
||||
with open(self._pidfile, "w", encoding="ascii") as pidfile:
|
||||
print(f"{os.getpid()}", file=pidfile)
|
||||
|
||||
def _cleanup_pidfile(self) -> None:
|
||||
if not self._pidfile:
|
||||
return
|
||||
logging.info("Removing %s", self._pidfile)
|
||||
os.unlink(self._pidfile)
|
||||
|
||||
|
||||
class DnsProtocol(enum.Enum):
|
||||
UDP = enum.auto()
|
||||
TCP = enum.auto()
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@dataclass
|
||||
class QueryContext:
|
||||
"""
|
||||
Context for the incoming query which may be used for preparing the response.
|
||||
"""
|
||||
|
||||
query: dns.message.Message
|
||||
response: dns.message.Message
|
||||
peer: Tuple[str, int]
|
||||
protocol: DnsProtocol
|
||||
zone: Optional[dns.zone.Zone] = None
|
||||
soa: Optional[dns.rrset.RRset] = None
|
||||
node: Optional[dns.node.Node] = None
|
||||
answer: Optional[dns.rdataset.Rdataset] = None
|
||||
|
||||
@property
|
||||
def qname(self) -> dns.name.Name:
|
||||
return self.query.question[0].name
|
||||
|
||||
@property
|
||||
def qclass(self) -> RdataClass:
|
||||
return self.query.question[0].rdclass
|
||||
|
||||
@property
|
||||
def qtype(self) -> RdataType:
|
||||
return self.query.question[0].rdtype
|
||||
|
||||
|
||||
@dataclass
|
||||
class ResponseAction(abc.ABC):
|
||||
"""
|
||||
Base class for actions that can be taken in response to a query.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
async def perform(self) -> Optional[Union[dns.message.Message, bytes]]:
|
||||
"""
|
||||
This method is expected to carry out arbitrary actions (e.g. wait for a
|
||||
specific amount of time, modify the answer, etc.) and then return the
|
||||
DNS response to send (a dns.message.Message, a raw bytes object, or
|
||||
None, which prevents any response from being sent).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@dataclass
|
||||
class DnsResponseSend(ResponseAction):
|
||||
"""
|
||||
Action which yields a dns.message.Message response.
|
||||
|
||||
The response may be sent with a delay if requested.
|
||||
|
||||
Depending on the value of the `authoritative` property, this class may set
|
||||
the AA bit in the response (True), clear it (False), or not touch it at all
|
||||
(None).
|
||||
"""
|
||||
|
||||
response: dns.message.Message
|
||||
authoritative: Optional[bool] = None
|
||||
delay: float = 0.0
|
||||
|
||||
async def perform(self) -> Optional[Union[dns.message.Message, bytes]]:
|
||||
"""
|
||||
Yield a potentially delayed response that is a dns.message.Message.
|
||||
"""
|
||||
assert isinstance(self.response, dns.message.Message)
|
||||
if self.authoritative is not None:
|
||||
if self.authoritative:
|
||||
self.response.flags |= dns.flags.AA
|
||||
else:
|
||||
self.response.flags &= ~dns.flags.AA
|
||||
if self.delay > 0:
|
||||
logging.info(
|
||||
"Delaying response (ID=%d) by %d ms",
|
||||
self.response.id,
|
||||
self.delay * 1000,
|
||||
)
|
||||
await asyncio.sleep(self.delay)
|
||||
return self.response
|
||||
|
||||
|
||||
@dataclass
|
||||
class BytesResponseSend(ResponseAction):
|
||||
"""
|
||||
Action which yields a raw response that is a sequence of bytes.
|
||||
|
||||
The response may be sent with a delay if requested.
|
||||
"""
|
||||
|
||||
response: bytes
|
||||
delay: float = 0.0
|
||||
|
||||
async def perform(self) -> Optional[Union[dns.message.Message, bytes]]:
|
||||
"""
|
||||
Yield a potentially delayed response that is a sequence of bytes.
|
||||
"""
|
||||
assert isinstance(self.response, bytes)
|
||||
if self.delay > 0:
|
||||
logging.info("Delaying raw response by %d ms", self.delay * 1000)
|
||||
await asyncio.sleep(self.delay)
|
||||
return self.response
|
||||
|
||||
|
||||
@dataclass
|
||||
class ResponseDrop(ResponseAction):
|
||||
"""
|
||||
Action which does nothing - as if a packet was dropped.
|
||||
"""
|
||||
|
||||
async def perform(self) -> Optional[Union[dns.message.Message, bytes]]:
|
||||
return None
|
||||
|
||||
|
||||
class ResponseHandler(abc.ABC):
|
||||
"""
|
||||
Base class for generic response handlers.
|
||||
|
||||
If a query passes the `match()` function logic, then it is handled by this
|
||||
response handler and response(s) may be generated by the `get_responses()`
|
||||
method.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def match(self, qctx: QueryContext) -> bool:
|
||||
"""
|
||||
Matching logic - query is handled when it returns True.
|
||||
"""
|
||||
return True
|
||||
|
||||
@abc.abstractmethod
|
||||
async def get_responses(
|
||||
self, qctx: QueryContext
|
||||
) -> AsyncGenerator[ResponseAction, None]:
|
||||
"""
|
||||
Custom handler which may produce response(s) to matching queries.
|
||||
|
||||
The response prepared from zone data is passed to this method in
|
||||
qctx.response.
|
||||
"""
|
||||
yield DnsResponseSend(qctx.response)
|
||||
|
||||
|
||||
class DomainHandler(ResponseHandler):
|
||||
"""
|
||||
Base class used for deriving custom domain handlers.
|
||||
|
||||
The derived class must specify a list of `domains` that it wants to handle.
|
||||
Queries for any of these domains (and their subdomains) will then be passed
|
||||
to the `get_response()` method in the derived class.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def domains(self) -> List[str]:
|
||||
"""
|
||||
A list of domain names handled by this class.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._domains: List[dns.name.Name] = [
|
||||
dns.name.from_text(d) for d in self.domains
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.__class__.__name__}(domains: {', '.join(self.domains)})"
|
||||
|
||||
def match(self, qctx: QueryContext) -> bool:
|
||||
"""
|
||||
Handle queries whose QNAME matches any of the domains handled by this
|
||||
class.
|
||||
"""
|
||||
for domain in self._domains:
|
||||
if qctx.qname.is_subdomain(domain):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@dataclass
|
||||
class _ZoneTreeNode:
|
||||
"""
|
||||
A node representing a zone with one origin.
|
||||
"""
|
||||
|
||||
zone: Optional[dns.zone.Zone]
|
||||
children: List["_ZoneTreeNode"] = field(default_factory=list)
|
||||
|
||||
|
||||
class _ZoneTree:
|
||||
"""
|
||||
Tree with independent zones.
|
||||
|
||||
This zone tree is used as a backing structure for the DNS server. The
|
||||
individual zones are independent to allow the (single) server to serve both
|
||||
the parent zone and a child zone if needed.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._root: _ZoneTreeNode = _ZoneTreeNode(None)
|
||||
|
||||
def add(self, zone: dns.zone.Zone) -> None:
|
||||
"""
|
||||
Add a zone to the tree and rearrange sub-zones if necessary.
|
||||
"""
|
||||
assert zone.origin
|
||||
best_match = self._find_best_match(zone.origin, self._root)
|
||||
added_node = _ZoneTreeNode(zone)
|
||||
self._move_children(best_match, added_node)
|
||||
best_match.children.append(added_node)
|
||||
|
||||
def _find_best_match(
|
||||
self, name: dns.name.Name, start_node: _ZoneTreeNode
|
||||
) -> _ZoneTreeNode:
|
||||
for child in start_node.children:
|
||||
assert child.zone
|
||||
assert child.zone.origin
|
||||
if name.is_subdomain(child.zone.origin):
|
||||
return self._find_best_match(name, child)
|
||||
return start_node
|
||||
|
||||
def _move_children(self, node_from: _ZoneTreeNode, node_to: _ZoneTreeNode) -> None:
|
||||
assert node_to.zone
|
||||
assert node_to.zone.origin
|
||||
|
||||
children_to_move = []
|
||||
for child in node_from.children:
|
||||
assert child.zone
|
||||
assert child.zone.origin
|
||||
if child.zone.origin.is_subdomain(node_to.zone.origin):
|
||||
children_to_move.append(child)
|
||||
|
||||
for child in children_to_move:
|
||||
node_from.children.remove(child)
|
||||
node_to.children.append(child)
|
||||
|
||||
def find_best_zone(self, name: dns.name.Name) -> Optional[dns.zone.Zone]:
|
||||
"""
|
||||
Return the closest matching zone (if any) for the domain name.
|
||||
"""
|
||||
node = self._find_best_match(name, self._root)
|
||||
return node.zone if node != self._root else None
|
||||
|
||||
|
||||
class AsyncDnsServer(AsyncServer):
|
||||
"""
|
||||
DNS server which responds to queries based on zone data and/or custom
|
||||
handlers.
|
||||
|
||||
The server may use custom handlers which allow arbitrary query processing.
|
||||
These don't need to be standards-compliant and can be used for testing all
|
||||
sorts of scenarios, including delaying responses, synthesizing them based
|
||||
on query contents etc.
|
||||
|
||||
The server also loads any zone files (*.db) found in its directory and
|
||||
serves them. Responses prepared using zone data can then be modified,
|
||||
replaced, or suppressed by query handlers. Query handlers can also generate
|
||||
response from scratch, without using zone data at all.
|
||||
"""
|
||||
|
||||
def __init__(self, load_zones: bool = True):
|
||||
super().__init__(self._handle_udp, self._handle_tcp, "ans.pid")
|
||||
|
||||
self._zone_tree: _ZoneTree = _ZoneTree()
|
||||
self._response_handlers: List[ResponseHandler] = []
|
||||
|
||||
if load_zones:
|
||||
self._load_zones()
|
||||
|
||||
def install_response_handler(self, handler: ResponseHandler) -> None:
|
||||
"""
|
||||
Add a response handler which will be used to handle matching queries.
|
||||
|
||||
Response handlers can modify, replace, or suppress the answers prepared
|
||||
from zone file contents.
|
||||
"""
|
||||
logging.info("Installing response handler: %s", handler)
|
||||
self._response_handlers.append(handler)
|
||||
|
||||
def _load_zones(self) -> None:
|
||||
for entry in os.scandir():
|
||||
entry_path = pathlib.Path(entry.path)
|
||||
if entry_path.suffix != ".db":
|
||||
continue
|
||||
origin = dns.name.from_text(entry_path.stem)
|
||||
logging.info("Loading zone file %s", entry_path)
|
||||
zone = dns.zone.from_file(entry.path, origin, relativize=False)
|
||||
self._zone_tree.add(zone)
|
||||
|
||||
async def _handle_udp(
|
||||
self, wire: bytes, peer: Tuple[str, int], transport: asyncio.DatagramTransport
|
||||
) -> None:
|
||||
logging.debug("Received UDP message: %s", wire.hex())
|
||||
responses = self._handle_query(wire, peer, DnsProtocol.UDP)
|
||||
async for response in responses:
|
||||
transport.sendto(response, peer)
|
||||
|
||||
async def _handle_tcp(
|
||||
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
||||
) -> None:
|
||||
wire_length_bytes = await reader.read(2)
|
||||
(wire_length,) = struct.unpack("!H", wire_length_bytes)
|
||||
logging.debug("Receiving TCP message (%d octets)...", wire_length)
|
||||
|
||||
wire = await reader.read(wire_length)
|
||||
full_message = wire_length_bytes + wire
|
||||
logging.debug("Received complete TCP message: %s", full_message.hex())
|
||||
|
||||
peer = writer.get_extra_info("peername")
|
||||
responses = self._handle_query(wire, peer, DnsProtocol.TCP)
|
||||
async for response in responses:
|
||||
writer.write(response)
|
||||
try:
|
||||
await writer.drain()
|
||||
except ConnectionResetError:
|
||||
logging.error(
|
||||
"TCP connection from %s reset by peer", self._format_peer(peer)
|
||||
)
|
||||
return
|
||||
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
|
||||
def _format_peer(self, peer: Tuple[str, int]) -> str:
|
||||
host = peer[0]
|
||||
port = peer[1]
|
||||
if "::" in host:
|
||||
host = f"[{host}]"
|
||||
return f"{host}:{port}"
|
||||
|
||||
def _log_query(
|
||||
self, qctx: QueryContext, peer: Tuple[str, int], protocol: DnsProtocol
|
||||
) -> None:
|
||||
logging.info(
|
||||
"Received %s/%s/%s (ID=%d) query from %s (%s)",
|
||||
qctx.qname.to_text(omit_final_dot=True),
|
||||
dns.rdataclass.to_text(qctx.qclass),
|
||||
dns.rdatatype.to_text(qctx.qtype),
|
||||
qctx.query.id,
|
||||
self._format_peer(peer),
|
||||
protocol.name,
|
||||
)
|
||||
logging.debug(
|
||||
"\n".join([f"[IN] {l}" for l in [""] + str(qctx.query).splitlines()])
|
||||
)
|
||||
|
||||
def _log_response(
|
||||
self,
|
||||
qctx: QueryContext,
|
||||
response: Optional[Union[dns.message.Message, bytes]],
|
||||
peer: Tuple[str, int],
|
||||
protocol: DnsProtocol,
|
||||
) -> None:
|
||||
if not response:
|
||||
logging.info(
|
||||
"Not sending a response to query (ID=%d) from %s (%s)",
|
||||
qctx.query.id,
|
||||
self._format_peer(peer),
|
||||
protocol.name,
|
||||
)
|
||||
return
|
||||
|
||||
if isinstance(response, dns.message.Message):
|
||||
try:
|
||||
qname = response.question[0].name.to_text(omit_final_dot=True)
|
||||
qclass = dns.rdataclass.to_text(response.question[0].rdclass)
|
||||
qtype = dns.rdatatype.to_text(response.question[0].rdtype)
|
||||
except IndexError:
|
||||
qname = "<empty>"
|
||||
qclass = "-"
|
||||
qtype = "-"
|
||||
|
||||
logging.info(
|
||||
"Sending %s/%s/%s (ID=%d) response (%d/%d/%d/%d) to a query (ID=%d) from %s (%s)",
|
||||
qname,
|
||||
qclass,
|
||||
qtype,
|
||||
response.id,
|
||||
len(response.question),
|
||||
len(response.answer),
|
||||
len(response.authority),
|
||||
len(response.additional),
|
||||
qctx.query.id,
|
||||
self._format_peer(peer),
|
||||
protocol.name,
|
||||
)
|
||||
logging.debug(
|
||||
"\n".join([f"[OUT] {l}" for l in [""] + str(response).splitlines()])
|
||||
)
|
||||
return
|
||||
|
||||
logging.info(
|
||||
"Sending response (%d bytes) to a query (ID=%d) from %s (%s)",
|
||||
len(response),
|
||||
qctx.query.id,
|
||||
self._format_peer(peer),
|
||||
protocol.name,
|
||||
)
|
||||
logging.debug("[OUT] %s", response.hex())
|
||||
|
||||
async def _handle_query(
|
||||
self, wire: bytes, peer: Tuple[str, int], protocol: DnsProtocol
|
||||
) -> AsyncGenerator[bytes, None]:
|
||||
"""
|
||||
Yield wire data to send as a response over the established transport.
|
||||
"""
|
||||
query = dns.message.from_wire(wire)
|
||||
response_stub = dns.message.make_response(query)
|
||||
qctx = QueryContext(query, response_stub, peer, protocol)
|
||||
self._log_query(qctx, peer, protocol)
|
||||
responses = self._prepare_responses(qctx)
|
||||
async for response in responses:
|
||||
self._log_response(qctx, response, peer, protocol)
|
||||
if response:
|
||||
if isinstance(response, dns.message.Message):
|
||||
response = response.to_wire(max_size=65535)
|
||||
if protocol == DnsProtocol.UDP:
|
||||
yield response
|
||||
else:
|
||||
response_length = struct.pack("!H", len(response))
|
||||
yield response_length + response
|
||||
|
||||
async def _prepare_responses(
|
||||
self, qctx: QueryContext
|
||||
) -> AsyncGenerator[Optional[Union[dns.message.Message, bytes]], None]:
|
||||
"""
|
||||
Yield response(s) either from response handlers or zone data.
|
||||
"""
|
||||
self._prepare_response_from_zone_data(qctx)
|
||||
|
||||
response_handled = False
|
||||
async for action in self._run_response_handlers(qctx):
|
||||
yield await action.perform()
|
||||
response_handled = True
|
||||
|
||||
if not response_handled:
|
||||
yield qctx.response
|
||||
|
||||
def _prepare_response_from_zone_data(self, qctx: QueryContext) -> None:
|
||||
"""
|
||||
Prepare a response to the query based on the available zone data.
|
||||
|
||||
The functionality is split across smaller functions that modify the
|
||||
query context until a proper response is formed.
|
||||
"""
|
||||
if self._refused_response(qctx):
|
||||
return
|
||||
|
||||
if self._delegation_response(qctx):
|
||||
return
|
||||
|
||||
qctx.response.flags |= dns.flags.AA
|
||||
|
||||
if self._ent_response(qctx):
|
||||
return
|
||||
|
||||
if self._nxdomain_response(qctx):
|
||||
return
|
||||
|
||||
if self._nodata_response(qctx):
|
||||
return
|
||||
|
||||
self._noerror_response(qctx)
|
||||
|
||||
def _refused_response(self, qctx: QueryContext) -> bool:
|
||||
qctx.zone = self._zone_tree.find_best_zone(qctx.qname)
|
||||
if qctx.zone:
|
||||
return False
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.REFUSED)
|
||||
return True
|
||||
|
||||
def _delegation_response(self, qctx: QueryContext) -> bool:
|
||||
assert qctx.zone
|
||||
|
||||
name = qctx.qname
|
||||
delegation = None
|
||||
|
||||
while name != qctx.zone.origin:
|
||||
node = qctx.zone.get_node(name)
|
||||
if node:
|
||||
delegation = node.get_rdataset(qctx.qclass, dns.rdatatype.NS)
|
||||
if delegation:
|
||||
break
|
||||
name = name.parent()
|
||||
|
||||
if not delegation:
|
||||
return False
|
||||
|
||||
delegation_rrset = dns.rrset.RRset(name, qctx.qclass, dns.rdatatype.NS)
|
||||
delegation_rrset.update(delegation)
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.NOERROR)
|
||||
qctx.response.authority.append(delegation_rrset)
|
||||
|
||||
self._delegation_response_additional(qctx)
|
||||
|
||||
return True
|
||||
|
||||
def _delegation_response_additional(self, qctx: QueryContext) -> None:
|
||||
assert qctx.zone
|
||||
assert qctx.response.authority[0]
|
||||
|
||||
for nameserver in qctx.response.authority[0]:
|
||||
if not nameserver.target.is_subdomain(qctx.response.authority[0].name):
|
||||
continue
|
||||
glue_a = qctx.zone.get_rrset(nameserver.target, dns.rdatatype.A)
|
||||
if glue_a:
|
||||
qctx.response.additional.append(glue_a)
|
||||
glue_aaaa = qctx.zone.get_rrset(nameserver.target, dns.rdatatype.AAAA)
|
||||
if glue_aaaa:
|
||||
qctx.response.additional.append(glue_aaaa)
|
||||
|
||||
def _ent_response(self, qctx: QueryContext) -> bool:
|
||||
assert qctx.zone
|
||||
assert qctx.zone.origin
|
||||
|
||||
qctx.soa = qctx.zone.find_rrset(qctx.zone.origin, dns.rdatatype.SOA)
|
||||
assert qctx.soa
|
||||
|
||||
qctx.node = qctx.zone.get_node(qctx.qname)
|
||||
if qctx.node or not any(
|
||||
n for n in qctx.zone.nodes if n.is_subdomain(qctx.qname)
|
||||
):
|
||||
return False
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.NOERROR)
|
||||
qctx.response.authority.append(qctx.soa)
|
||||
return True
|
||||
|
||||
def _nxdomain_response(self, qctx: QueryContext) -> bool:
|
||||
assert qctx.soa
|
||||
|
||||
if qctx.node:
|
||||
return False
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.NXDOMAIN)
|
||||
qctx.response.authority.append(qctx.soa)
|
||||
return True
|
||||
|
||||
def _nodata_response(self, qctx: QueryContext) -> bool:
|
||||
assert qctx.node
|
||||
assert qctx.soa
|
||||
|
||||
qctx.answer = qctx.node.get_rdataset(qctx.qclass, qctx.qtype)
|
||||
if qctx.answer:
|
||||
return False
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.NOERROR)
|
||||
qctx.response.authority.append(qctx.soa)
|
||||
return True
|
||||
|
||||
def _noerror_response(self, qctx: QueryContext) -> None:
|
||||
assert qctx.answer
|
||||
|
||||
answer_rrset = dns.rrset.RRset(qctx.qname, qctx.qclass, qctx.qtype)
|
||||
answer_rrset.update(qctx.answer)
|
||||
|
||||
qctx.response.set_rcode(dns.rcode.NOERROR)
|
||||
qctx.response.answer.append(answer_rrset)
|
||||
|
||||
async def _run_response_handlers(
|
||||
self, qctx: QueryContext
|
||||
) -> AsyncGenerator[ResponseAction, None]:
|
||||
"""
|
||||
Yield response(s) to the query from a matching query handler.
|
||||
"""
|
||||
for handler in self._response_handlers:
|
||||
if handler.match(qctx):
|
||||
async for response in handler.get_responses(qctx):
|
||||
yield response
|
||||
return
|
||||
@@ -9,13 +9,11 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
import dns.rcode
|
||||
import dns.message
|
||||
import dns.zone
|
||||
|
||||
import isctest.log
|
||||
|
||||
# compatiblity with dnspython<2.0.0
|
||||
try:
|
||||
@@ -40,58 +38,8 @@ def servfail(message: dns.message.Message) -> None:
|
||||
rcode(message, dns_rcode.SERVFAIL)
|
||||
|
||||
|
||||
def rrsets_equal(
|
||||
first_rrset: dns.rrset.RRset,
|
||||
second_rrset: dns.rrset.RRset,
|
||||
compare_ttl: Optional[bool] = False,
|
||||
) -> None:
|
||||
"""Compare two RRset (optionally including TTL)"""
|
||||
|
||||
def compare_rrs(rr1, rrset):
|
||||
rr2 = next((other_rr for other_rr in rrset if rr1 == other_rr), None)
|
||||
assert rr2 is not None, f"No corresponding RR found for: {rr1}"
|
||||
if compare_ttl:
|
||||
assert rr1.ttl == rr2.ttl
|
||||
|
||||
isctest.log.debug(
|
||||
"%s() first RRset:\n%s",
|
||||
rrsets_equal.__name__,
|
||||
"\n".join([str(rr) for rr in first_rrset]),
|
||||
)
|
||||
isctest.log.debug(
|
||||
"%s() second RRset:\n%s",
|
||||
rrsets_equal.__name__,
|
||||
"\n".join([str(rr) for rr in second_rrset]),
|
||||
)
|
||||
def rrsets_equal(first_rrset: dns.rrset.RRset, second_rrset: dns.rrset.RRset) -> None:
|
||||
for rr in first_rrset:
|
||||
compare_rrs(rr, second_rrset)
|
||||
assert rr in second_rrset
|
||||
for rr in second_rrset:
|
||||
compare_rrs(rr, first_rrset)
|
||||
|
||||
|
||||
def zones_equal(
|
||||
first_zone: dns.zone.Zone,
|
||||
second_zone: dns.zone.Zone,
|
||||
compare_ttl: Optional[bool] = False,
|
||||
) -> None:
|
||||
"""Compare two zones (optionally including TTL)"""
|
||||
|
||||
isctest.log.debug(
|
||||
"%s() first zone:\n%s",
|
||||
zones_equal.__name__,
|
||||
first_zone.to_text(relativize=False),
|
||||
)
|
||||
isctest.log.debug(
|
||||
"%s() second zone:\n%s",
|
||||
zones_equal.__name__,
|
||||
second_zone.to_text(relativize=False),
|
||||
)
|
||||
assert first_zone == second_zone
|
||||
if compare_ttl:
|
||||
for name, node in first_zone.nodes.items():
|
||||
for rdataset in node:
|
||||
found_rdataset = second_zone.find_rdataset(
|
||||
name=name, rdtype=rdataset.rdtype
|
||||
)
|
||||
assert found_rdataset
|
||||
assert found_rdataset.ttl == rdataset.ttl
|
||||
assert rr in first_rrset
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
# information regarding copyright ownership.
|
||||
|
||||
from .basic import (
|
||||
avoid_duplicated_logs,
|
||||
deinit_module_logger,
|
||||
deinit_test_logger,
|
||||
init_conftest_logger,
|
||||
init_module_logger,
|
||||
init_test_logger,
|
||||
debug,
|
||||
|
||||
@@ -53,6 +53,10 @@ def avoid_duplicated_logs():
|
||||
logging.root.handlers.remove(handler)
|
||||
|
||||
|
||||
init_conftest_logger()
|
||||
avoid_duplicated_logs()
|
||||
|
||||
|
||||
def init_module_logger(system_test_name: str, testdir: Path):
|
||||
logger = logging.getLogger(system_test_name)
|
||||
logger.handlers.clear()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#
|
||||
# Clean up after limits tests.
|
||||
#
|
||||
rm -f dig.out.*
|
||||
rm -f */named.memstats
|
||||
rm -f */named.conf
|
||||
rm -f */named.run
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19019,94 +19019,94 @@ a-maximum-rrset A 10.0.0.0
|
||||
A 10.0.15.157
|
||||
A 10.0.15.158
|
||||
A 10.0.15.159
|
||||
A 10.0.15.160
|
||||
A 10.0.15.161
|
||||
A 10.0.15.162
|
||||
A 10.0.15.163
|
||||
A 10.0.15.164
|
||||
A 10.0.15.165
|
||||
A 10.0.15.166
|
||||
A 10.0.15.167
|
||||
A 10.0.15.168
|
||||
A 10.0.15.169
|
||||
A 10.0.15.170
|
||||
A 10.0.15.171
|
||||
A 10.0.15.172
|
||||
A 10.0.15.173
|
||||
A 10.0.15.174
|
||||
A 10.0.15.175
|
||||
A 10.0.15.176
|
||||
A 10.0.15.177
|
||||
A 10.0.15.178
|
||||
A 10.0.15.179
|
||||
A 10.0.15.180
|
||||
A 10.0.15.181
|
||||
A 10.0.15.182
|
||||
A 10.0.15.183
|
||||
A 10.0.15.184
|
||||
A 10.0.15.185
|
||||
A 10.0.15.186
|
||||
A 10.0.15.187
|
||||
A 10.0.15.188
|
||||
A 10.0.15.189
|
||||
A 10.0.15.190
|
||||
A 10.0.15.191
|
||||
A 10.0.15.192
|
||||
A 10.0.15.193
|
||||
A 10.0.15.194
|
||||
A 10.0.15.195
|
||||
A 10.0.15.196
|
||||
A 10.0.15.197
|
||||
A 10.0.15.198
|
||||
A 10.0.15.199
|
||||
A 10.0.15.200
|
||||
A 10.0.15.201
|
||||
A 10.0.15.202
|
||||
A 10.0.15.203
|
||||
A 10.0.15.204
|
||||
A 10.0.15.205
|
||||
A 10.0.15.206
|
||||
A 10.0.15.207
|
||||
A 10.0.15.208
|
||||
A 10.0.15.209
|
||||
A 10.0.15.210
|
||||
A 10.0.15.211
|
||||
A 10.0.15.212
|
||||
A 10.0.15.213
|
||||
A 10.0.15.214
|
||||
A 10.0.15.215
|
||||
A 10.0.15.216
|
||||
A 10.0.15.217
|
||||
A 10.0.15.218
|
||||
A 10.0.15.219
|
||||
A 10.0.15.220
|
||||
A 10.0.15.221
|
||||
A 10.0.15.222
|
||||
A 10.0.15.223
|
||||
A 10.0.15.224
|
||||
A 10.0.15.225
|
||||
A 10.0.15.226
|
||||
A 10.0.15.227
|
||||
A 10.0.15.228
|
||||
A 10.0.15.229
|
||||
A 10.0.15.230
|
||||
A 10.0.15.231
|
||||
A 10.0.15.232
|
||||
A 10.0.15.233
|
||||
A 10.0.15.234
|
||||
A 10.0.15.235
|
||||
A 10.0.15.236
|
||||
A 10.0.15.237
|
||||
A 10.0.15.238
|
||||
A 10.0.15.239
|
||||
A 10.0.15.240
|
||||
A 10.0.15.241
|
||||
A 10.0.15.242
|
||||
A 10.0.15.243
|
||||
A 10.0.15.244
|
||||
A 10.0.15.245
|
||||
A 10.0.15.246
|
||||
A 10.0.15.247
|
||||
A 10.0.15.248
|
||||
A 10.0.15.249
|
||||
A 10.0.15.250
|
||||
A 10.1.0.0
|
||||
A 10.1.0.1
|
||||
A 10.1.0.2
|
||||
A 10.1.0.3
|
||||
A 10.1.0.4
|
||||
A 10.1.0.5
|
||||
A 10.1.0.6
|
||||
A 10.1.0.7
|
||||
A 10.1.0.8
|
||||
A 10.1.0.9
|
||||
A 10.1.0.10
|
||||
A 10.1.0.11
|
||||
A 10.1.0.12
|
||||
A 10.1.0.13
|
||||
A 10.1.0.14
|
||||
A 10.1.0.15
|
||||
A 10.1.0.16
|
||||
A 10.1.0.17
|
||||
A 10.1.0.18
|
||||
A 10.1.0.19
|
||||
A 10.1.0.20
|
||||
A 10.1.0.21
|
||||
A 10.1.0.22
|
||||
A 10.1.0.23
|
||||
A 10.1.0.24
|
||||
A 10.1.0.25
|
||||
A 10.1.0.26
|
||||
A 10.1.0.27
|
||||
A 10.1.0.28
|
||||
A 10.1.0.29
|
||||
A 10.1.0.30
|
||||
A 10.1.0.31
|
||||
A 10.1.0.32
|
||||
A 10.1.0.33
|
||||
A 10.1.0.34
|
||||
A 10.1.0.35
|
||||
A 10.1.0.36
|
||||
A 10.1.0.37
|
||||
A 10.1.0.38
|
||||
A 10.1.0.39
|
||||
A 10.1.0.40
|
||||
A 10.1.0.41
|
||||
A 10.1.0.42
|
||||
A 10.1.0.43
|
||||
A 10.1.0.44
|
||||
A 10.1.0.45
|
||||
A 10.1.0.46
|
||||
A 10.1.0.47
|
||||
A 10.1.0.48
|
||||
A 10.1.0.49
|
||||
A 10.1.0.50
|
||||
A 10.1.0.51
|
||||
A 10.1.0.52
|
||||
A 10.1.0.53
|
||||
A 10.1.0.54
|
||||
A 10.1.0.55
|
||||
A 10.1.0.56
|
||||
A 10.1.0.57
|
||||
A 10.1.0.58
|
||||
A 10.1.0.59
|
||||
A 10.1.0.60
|
||||
A 10.1.0.61
|
||||
A 10.1.0.62
|
||||
A 10.1.0.63
|
||||
A 10.1.0.64
|
||||
A 10.1.0.65
|
||||
A 10.1.0.66
|
||||
A 10.1.0.67
|
||||
A 10.1.0.68
|
||||
A 10.1.0.69
|
||||
A 10.1.0.70
|
||||
A 10.1.0.71
|
||||
A 10.1.0.72
|
||||
A 10.1.0.73
|
||||
A 10.1.0.74
|
||||
A 10.1.0.75
|
||||
A 10.1.0.76
|
||||
A 10.1.0.77
|
||||
A 10.1.0.78
|
||||
A 10.1.0.79
|
||||
A 10.1.0.80
|
||||
A 10.1.0.81
|
||||
A 10.1.0.82
|
||||
A 10.1.0.83
|
||||
A 10.1.0.84
|
||||
A 10.1.0.85
|
||||
A 10.1.0.86
|
||||
A 10.1.0.87
|
||||
A 10.1.0.88
|
||||
A 10.1.0.89
|
||||
A 10.1.0.90
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
DIGOPTS="-p ${PORT}"
|
||||
|
||||
status=0
|
||||
|
||||
echo_i "1000 A records"
|
||||
$DIG $DIGOPTS +tcp +norec 1000.example. @10.53.0.1 a >dig.out.1000 || status=1
|
||||
# $DIG $DIGOPTS 1000.example. @10.53.0.1 a > knowngood.dig.out.1000
|
||||
digcomp knowngood.dig.out.1000 dig.out.1000 || status=1
|
||||
|
||||
echo_i "2000 A records"
|
||||
$DIG $DIGOPTS +tcp +norec 2000.example. @10.53.0.1 a >dig.out.2000 || status=1
|
||||
# $DIG $DIGOPTS 2000.example. @10.53.0.1 a > knowngood.dig.out.2000
|
||||
digcomp knowngood.dig.out.2000 dig.out.2000 || status=1
|
||||
|
||||
echo_i "3000 A records"
|
||||
$DIG $DIGOPTS +tcp +norec 3000.example. @10.53.0.1 a >dig.out.3000 || status=1
|
||||
# $DIG $DIGOPTS 3000.example. @10.53.0.1 a > knowngood.dig.out.3000
|
||||
digcomp knowngood.dig.out.3000 dig.out.3000 || status=1
|
||||
|
||||
echo_i "4000 A records"
|
||||
$DIG $DIGOPTS +tcp +norec 4000.example. @10.53.0.1 a >dig.out.4000 || status=1
|
||||
# $DIG $DIGOPTS 4000.example. @10.53.0.1 a > knowngood.dig.out.4000
|
||||
digcomp knowngood.dig.out.4000 dig.out.4000 || status=1
|
||||
|
||||
echo_i "exactly maximum rrset"
|
||||
$DIG $DIGOPTS +tcp +norec +noedns a-maximum-rrset.example. @10.53.0.1 a >dig.out.a-maximum-rrset \
|
||||
|| status=1
|
||||
# $DIG $DIGOPTS a-maximum-rrset.example. @10.53.0.1 a > knowngood.dig.out.a-maximum-rrset
|
||||
digcomp knowngood.dig.out.a-maximum-rrset dig.out.a-maximum-rrset || status=1
|
||||
|
||||
echo_i "exceed maximum rrset (5000 A records)"
|
||||
$DIG $DIGOPTS +tcp +norec +noadd 5000.example. @10.53.0.1 a >dig.out.exceed || status=1
|
||||
# Look for truncation bit (tc).
|
||||
grep 'flags: .*tc.*;' dig.out.exceed >/dev/null || {
|
||||
echo_i "TC bit was not set"
|
||||
status=1
|
||||
}
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
@@ -1,52 +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.
|
||||
|
||||
import itertools
|
||||
|
||||
import isctest
|
||||
import pytest
|
||||
|
||||
import dns.message
|
||||
|
||||
# Everything from getting a big answer to creating an RR set with thousands
|
||||
# of records takes minutes of CPU and real time with dnspython < 2.0.0.
|
||||
pytest.importorskip("dns", minversion="2.0.0")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,limit",
|
||||
[
|
||||
("1000", 1000),
|
||||
("2000", 2000),
|
||||
("3000", 3000),
|
||||
("4000", 4000),
|
||||
("a-maximum-rrset", 4091),
|
||||
],
|
||||
)
|
||||
def test_limits(name, limit):
|
||||
msg_query = dns.message.make_query(f"{name}.example.", "A")
|
||||
res = isctest.query.tcp(msg_query, "10.53.0.1")
|
||||
|
||||
iplist = [
|
||||
f"10.0.{x}.{y}"
|
||||
for x, y in itertools.islice(itertools.product(range(256), repeat=2), limit)
|
||||
]
|
||||
|
||||
msg_rrset = [dns.rrset.from_text_list(f"{name}.example.", "5M", "IN", "A", iplist)]
|
||||
|
||||
assert res.answer == msg_rrset
|
||||
|
||||
|
||||
def test_limit_exceeded():
|
||||
msg_query = dns.message.make_query("5000.example.", "A")
|
||||
res = isctest.query.tcp(msg_query, "10.53.0.1")
|
||||
|
||||
assert res.flags & dns.flags.TC, "TC flag was not set"
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
|
||||
|
||||
def test_limits(run_tests_sh):
|
||||
run_tests_sh()
|
||||
@@ -11,7 +11,9 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
rm -f dig.out.*
|
||||
rm -f */named.memstats
|
||||
rm -f */named.conf
|
||||
rm -f */named.run
|
||||
rm -f checkzone.out*
|
||||
rm -f ns*/managed-keys.bind*
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
include. 300 IN SOA ns.include. hostmaster.include. 1 3600 1800 1814400 3600
|
||||
include. 300 IN NS ns.include.
|
||||
a.include. 300 IN A 10.0.0.1
|
||||
a.include. 300 IN A 10.0.0.99
|
||||
a.a.include. 300 IN A 10.0.1.1
|
||||
b.foo.a.include. 300 IN A 10.0.2.2
|
||||
b.include. 300 IN A 10.0.0.2
|
||||
a.b.include. 300 IN A 10.0.1.1
|
||||
c.b.include. 300 IN A 10.0.0.3
|
||||
b.foo.b.include. 300 IN A 10.0.2.2
|
||||
ns.include. 300 IN A 127.0.0.1
|
||||
include. 300 IN SOA ns.include. hostmaster.include. 1 3600 1800 1814400 3600
|
||||
@@ -0,0 +1,10 @@
|
||||
ttl1. 3 IN SOA ns.ttl1. hostmaster.ttl1. 1 3600 1800 1814400 3
|
||||
ttl1. 3 IN NS ns.ttl1.
|
||||
a.ttl1. 3 IN TXT "soa minttl 3"
|
||||
b.ttl1. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl1. 3 IN TXT "soa minttl 3"
|
||||
d.ttl1. 1 IN TXT "default ttl 1"
|
||||
e.ttl1. 4 IN TXT "explicit ttl 4"
|
||||
f.ttl1. 1 IN TXT "default ttl 1"
|
||||
ns.ttl1. 3 IN A 10.53.0.1
|
||||
ttl1. 3 IN SOA ns.ttl1. hostmaster.ttl1. 1 3600 1800 1814400 3
|
||||
@@ -0,0 +1,10 @@
|
||||
ttl2. 1 IN SOA ns.ttl2. hostmaster.ttl2. 1 3600 1800 1814400 3
|
||||
ttl2. 1 IN NS ns.ttl2.
|
||||
a.ttl2. 1 IN TXT "inherited ttl 1"
|
||||
b.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl2. 2 IN TXT "inherited ttl 2"
|
||||
d.ttl2. 3 IN TXT "default ttl 3"
|
||||
e.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
f.ttl2. 3 IN TXT "default ttl 3"
|
||||
ns.ttl2. 1 IN A 10.53.0.1
|
||||
ttl2. 1 IN SOA ns.ttl2. hostmaster.ttl2. 1 3600 1800 1814400 3
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
DIGOPTS="-p ${PORT}"
|
||||
|
||||
status=0
|
||||
n=0
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test master file \$INCLUDE semantics ($n)"
|
||||
$DIG $DIGOPTS +nostats +nocmd include. axfr @10.53.0.1 >dig.out.$n || ret=1
|
||||
|
||||
diff dig.out.$n knowngood.include || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test master file BIND 8 compatibility TTL and \$TTL semantics ($n)"
|
||||
$DIG $DIGOPTS +nostats +nocmd ttl1. axfr @10.53.0.1 >dig.out.$n || ret=1
|
||||
|
||||
diff dig.out.$n knowngood.ttl1 || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test of master file RFC1035 TTL and \$TTL semantics ($n)"
|
||||
$DIG $DIGOPTS +nostats +nocmd ttl2. axfr @10.53.0.1 >dig.out.$n || ret=1
|
||||
|
||||
diff dig.out.$n knowngood.ttl2 || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test that the nameserver is running with a missing master file ($n)"
|
||||
$DIG $DIGOPTS +tcp +noall +answer example soa @10.53.0.2 >dig.out.$n || ret=1
|
||||
grep SOA dig.out.$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test that the nameserver returns SERVFAIL for a missing master file ($n)"
|
||||
$DIG $DIGOPTS +tcp +all missing soa @10.53.0.2 >dig.out.$n || ret=1
|
||||
grep "status: SERVFAIL" dig.out.$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
ret=0
|
||||
n=$((n + 1))
|
||||
echo_i "test owner inheritance after "'$INCLUDE'" ($n)"
|
||||
$CHECKZONE -Dq example zone/inheritownerafterinclude.db >checkzone.out$n
|
||||
diff checkzone.out$n zone/inheritownerafterinclude.good || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
@@ -1,119 +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.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import dns.message
|
||||
import dns.zone
|
||||
|
||||
import isctest
|
||||
|
||||
|
||||
def test_masterfile_include_semantics():
|
||||
"""Test master file $INCLUDE semantics"""
|
||||
msg_axfr = dns.message.make_query("include.", "AXFR")
|
||||
res_axfr = isctest.query.tcp(msg_axfr, "10.53.0.1")
|
||||
axfr_include_semantics = """;ANSWER
|
||||
include. 300 IN SOA ns.include. hostmaster.include. 1 3600 1800 1814400 3600
|
||||
include. 300 IN NS ns.include.
|
||||
a.include. 300 IN A 10.0.0.1
|
||||
a.include. 300 IN A 10.0.0.99
|
||||
a.a.include. 300 IN A 10.0.1.1
|
||||
b.foo.a.include. 300 IN A 10.0.2.2
|
||||
b.include. 300 IN A 10.0.0.2
|
||||
a.b.include. 300 IN A 10.0.1.1
|
||||
c.b.include. 300 IN A 10.0.0.3
|
||||
b.foo.b.include. 300 IN A 10.0.2.2
|
||||
ns.include. 300 IN A 127.0.0.1
|
||||
"""
|
||||
expected = dns.message.from_text(axfr_include_semantics)
|
||||
isctest.check.rrsets_equal(res_axfr.answer, expected.answer, compare_ttl=True)
|
||||
|
||||
|
||||
def test_masterfile_bind_8_compat_semantics():
|
||||
"""Test master file BIND 8 TTL and $TTL semantics compatibility"""
|
||||
msg_axfr = dns.message.make_query("ttl1.", "AXFR")
|
||||
res_axfr = isctest.query.tcp(msg_axfr, "10.53.0.1")
|
||||
axfr_ttl_semantics = """;ANSWER
|
||||
ttl1. 3 IN SOA ns.ttl1. hostmaster.ttl1. 1 3600 1800 1814400 3
|
||||
ttl1. 3 IN NS ns.ttl1.
|
||||
a.ttl1. 3 IN TXT "soa minttl 3"
|
||||
b.ttl1. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl1. 3 IN TXT "soa minttl 3"
|
||||
d.ttl1. 1 IN TXT "default ttl 1"
|
||||
e.ttl1. 4 IN TXT "explicit ttl 4"
|
||||
f.ttl1. 1 IN TXT "default ttl 1"
|
||||
ns.ttl1. 3 IN A 10.53.0.1
|
||||
"""
|
||||
expected = dns.message.from_text(axfr_ttl_semantics)
|
||||
isctest.check.rrsets_equal(res_axfr.answer, expected.answer, compare_ttl=True)
|
||||
|
||||
|
||||
def test_masterfile_rfc_1035_semantics():
|
||||
"""Test master file RFC1035 TTL and $TTL semantics"""
|
||||
msg_axfr = dns.message.make_query("ttl2.", "AXFR")
|
||||
res_axfr = isctest.query.tcp(msg_axfr, "10.53.0.1")
|
||||
axfr_ttl_semantics = """;ANSWER
|
||||
ttl2. 1 IN SOA ns.ttl2. hostmaster.ttl2. 1 3600 1800 1814400 3
|
||||
ttl2. 1 IN NS ns.ttl2.
|
||||
a.ttl2. 1 IN TXT "inherited ttl 1"
|
||||
b.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl2. 2 IN TXT "inherited ttl 2"
|
||||
d.ttl2. 3 IN TXT "default ttl 3"
|
||||
e.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
f.ttl2. 3 IN TXT "default ttl 3"
|
||||
ns.ttl2. 1 IN A 10.53.0.1
|
||||
"""
|
||||
expected = dns.message.from_text(axfr_ttl_semantics)
|
||||
isctest.check.rrsets_equal(res_axfr.answer, expected.answer, compare_ttl=True)
|
||||
|
||||
|
||||
def test_masterfile_missing_master_file():
|
||||
"""Test nameserver running with a missing master file"""
|
||||
msg_soa = dns.message.make_query("example.", "SOA")
|
||||
res_soa = isctest.query.tcp(msg_soa, "10.53.0.2")
|
||||
expected_soa_rr = """;ANSWER
|
||||
example. 300 IN SOA mname1. . 2010042407 20 20 1814400 3600
|
||||
"""
|
||||
expected = dns.message.from_text(expected_soa_rr)
|
||||
isctest.check.rrsets_equal(res_soa.answer, expected.answer, compare_ttl=True)
|
||||
|
||||
|
||||
def test_masterfile_missing_master_file_servfail():
|
||||
"""Test nameserver returning SERVFAIL for a missing master file"""
|
||||
msg_soa = dns.message.make_query("missing.", "SOA")
|
||||
res_soa = isctest.query.tcp(msg_soa, "10.53.0.2")
|
||||
isctest.check.servfail(res_soa)
|
||||
|
||||
|
||||
def test_masterfile_owner_inheritance():
|
||||
"""Test owner inheritance after $INCLUDE"""
|
||||
checker_output = subprocess.run(
|
||||
[
|
||||
os.environ["CHECKZONE"],
|
||||
"-D",
|
||||
"-q",
|
||||
"example",
|
||||
"zone/inheritownerafterinclude.db",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
).stdout.decode("utf-8")
|
||||
owner_inheritance_zone = """
|
||||
example. 0 IN SOA . . 0 0 0 0 0
|
||||
example. 0 IN TXT "this should be at the zone apex"
|
||||
example. 0 IN NS .
|
||||
"""
|
||||
checker_zone = dns.zone.from_text(checker_output, origin="example.")
|
||||
expected = dns.zone.from_text(owner_inheritance_zone, origin="example.")
|
||||
isctest.check.zones_equal(checker_zone, expected, compare_ttl=True)
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
|
||||
|
||||
def test_masterfile(run_tests_sh):
|
||||
run_tests_sh()
|
||||
@@ -0,0 +1,3 @@
|
||||
example. 0 IN SOA . . 0 0 0 0 0
|
||||
example. 0 IN NS .
|
||||
example. 0 IN TXT "this should be at the zone apex"
|
||||
@@ -12,6 +12,5 @@
|
||||
$TTL 300
|
||||
@ IN SOA a.root-servers.nil. hostmaster.example.net. 0 0 0 0 0
|
||||
@ IN NS a.root-servers.nil.
|
||||
10.in-addr.arpa TXT turn off redirect
|
||||
* IN A 100.100.100.1
|
||||
* IN AAAA 2001:ffff:ffff::100.100.100.1
|
||||
|
||||
@@ -518,14 +518,6 @@ n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking nxdomain-redirect against built-in RFC-1918 zone ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS -x 10.0.0.1 @10.53.0.4 -b 10.53.0.2 >dig.out.ns4.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking tld nxdomain-redirect against signed root zone ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS @10.53.0.5 asdfasdfasdf >dig.out.ns5.test$n || ret=1
|
||||
|
||||
@@ -505,21 +505,9 @@ n=$((n + 1))
|
||||
echo_i "check that '-t aaaa' in .digrc does not have unexpected side effects ($n)"
|
||||
ret=0
|
||||
echo "-t aaaa" >.digrc
|
||||
(
|
||||
HOME="$(pwd)"
|
||||
export HOME
|
||||
dig_with_opts @10.53.0.4 . >dig.out.1.${n}
|
||||
) || ret=1
|
||||
(
|
||||
HOME="$(pwd)"
|
||||
export HOME
|
||||
dig_with_opts @10.53.0.4 . A >dig.out.2.${n}
|
||||
) || ret=1
|
||||
(
|
||||
HOME="$(pwd)"
|
||||
export HOME
|
||||
dig_with_opts @10.53.0.4 -x 127.0.0.1 >dig.out.3.${n}
|
||||
) || ret=1
|
||||
(HOME="$(pwd)" dig_with_opts @10.53.0.4 . >dig.out.1.${n}) || ret=1
|
||||
(HOME="$(pwd)" dig_with_opts @10.53.0.4 . A >dig.out.2.${n}) || ret=1
|
||||
(HOME="$(pwd)" dig_with_opts @10.53.0.4 -x 127.0.0.1 >dig.out.3.${n}) || ret=1
|
||||
grep ';\..*IN.*AAAA$' dig.out.1.${n} >/dev/null || ret=1
|
||||
grep ';\..*IN.*A$' dig.out.2.${n} >/dev/null || ret=1
|
||||
grep 'extra type option' dig.out.2.${n} >/dev/null && ret=1
|
||||
|
||||
@@ -436,7 +436,7 @@ n=$((n + 1))
|
||||
echo_i "testing automatic zones are reported ($n)"
|
||||
ret=0
|
||||
$RNDC -s 10.53.0.4 -p ${EXTRAPORT6} -c ns4/key6.conf status >rndc.out.1.test$n || ret=1
|
||||
grep "number of zones: 201 (200 automatic)" rndc.out.1.test$n >/dev/null || ret=1
|
||||
grep "number of zones: 199 (198 automatic)" rndc.out.1.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
IN
|
||||
CH
|
||||
HS
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
rm -f classlist.out privatelist.out typelist.out tempzone checkzone.out* checker.out
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
status=0
|
||||
n=0
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "class list ($n)"
|
||||
$RRCHECKER -C >classlist.out
|
||||
diff classlist.out classlist.good || {
|
||||
echo_i "failed"
|
||||
status=$((status + 1))
|
||||
}
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "type list ($n)"
|
||||
$RRCHECKER -T >typelist.out
|
||||
diff typelist.out typelist.good || {
|
||||
echo_i "failed"
|
||||
status=$((status + 1))
|
||||
}
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "private type list ($n)"
|
||||
$RRCHECKER -P >privatelist.out
|
||||
diff privatelist.out privatelist.good || {
|
||||
echo_i "failed"
|
||||
status=$((status + 1))
|
||||
}
|
||||
|
||||
myecho() {
|
||||
cat <<EOF
|
||||
$*
|
||||
EOF
|
||||
}
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check conversions to canonical format ($n)"
|
||||
ret=0
|
||||
$SHELL ${TOP_SRCDIR}/bin/tests/system/genzone.sh 0 >tempzone
|
||||
$CHECKZONE -Dq . tempzone | sed '/^;/d' >checkzone.out$n
|
||||
while read -r name tt cl ty rest; do
|
||||
myecho "$cl $ty $rest" | $RRCHECKER -p >checker.out || {
|
||||
ret=1
|
||||
echo_i "'$cl $ty $rest' not handled."
|
||||
}
|
||||
read -r cl0 ty0 rest0 <checker.out
|
||||
test "$cl $ty $rest" = "$cl0 $ty0 $rest0" || {
|
||||
ret=1
|
||||
echo_i "'$cl $ty $rest' != '$cl0 $ty0 $rest0'"
|
||||
}
|
||||
done <checkzone.out$n
|
||||
test $ret -eq 0 || {
|
||||
echo_i "failed"
|
||||
status=$((status + 1))
|
||||
}
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check conversions to and from unknown record format ($n)"
|
||||
ret=0
|
||||
$CHECKZONE -Dq . tempzone | sed '/^;/d' >checkzone.out$n
|
||||
while read -r name tt cl ty rest; do
|
||||
myecho "$cl $ty $rest" | $RRCHECKER -u >checker.out || {
|
||||
ret=1
|
||||
echo_i "'$cl $ty $rest' not converted to unknown record format"
|
||||
}
|
||||
read -r clu tyu restu <checker.out
|
||||
myecho "$clu $tyu $restu" | $RRCHECKER -p >checker.out || {
|
||||
ret=1
|
||||
echo_i "'$cl $ty $rest' not converted back to canonical format"
|
||||
}
|
||||
read -r cl0 ty0 rest0 <checker.out
|
||||
test "$cl $ty $rest" = "$cl0 $ty0 $rest0" || {
|
||||
ret=1
|
||||
echo_i "'$cl $ty $rest' != '$cl0 $ty0 $rest0'"
|
||||
}
|
||||
done <checkzone.out$n
|
||||
test $ret -eq 0 || {
|
||||
echo_i "failed"
|
||||
status=$((status + 1))
|
||||
}
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
@@ -1,188 +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.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"option,expected_result",
|
||||
[
|
||||
("-C", ["HS", "CH", "IN"]),
|
||||
(
|
||||
"-T",
|
||||
[
|
||||
"A",
|
||||
"A6",
|
||||
"AAAA",
|
||||
"AFSDB",
|
||||
"AMTRELAY",
|
||||
"APL",
|
||||
"ATMA",
|
||||
"AVC",
|
||||
"CAA",
|
||||
"CDNSKEY",
|
||||
"CDS",
|
||||
"CERT",
|
||||
"CNAME",
|
||||
"CSYNC",
|
||||
"DHCID",
|
||||
"DLV",
|
||||
"DNAME",
|
||||
"DNSKEY",
|
||||
"DOA",
|
||||
"DS",
|
||||
"EID",
|
||||
"EUI48",
|
||||
"EUI64",
|
||||
"GID",
|
||||
"GPOS",
|
||||
"HINFO",
|
||||
"HIP",
|
||||
"HTTPS",
|
||||
"IPSECKEY",
|
||||
"ISDN",
|
||||
"KEY",
|
||||
"KX",
|
||||
"L32",
|
||||
"L64",
|
||||
"LOC",
|
||||
"LP",
|
||||
"MB",
|
||||
"MD",
|
||||
"MF",
|
||||
"MG",
|
||||
"MINFO",
|
||||
"MR",
|
||||
"MX",
|
||||
"NAPTR",
|
||||
"NID",
|
||||
"NIMLOC",
|
||||
"NINFO",
|
||||
"NS",
|
||||
"NSAP",
|
||||
"NSAP-PTR",
|
||||
"NSEC",
|
||||
"NSEC3",
|
||||
"NSEC3PARAM",
|
||||
"NULL",
|
||||
"NXT",
|
||||
"OPENPGPKEY",
|
||||
"PTR",
|
||||
"PX",
|
||||
"RESINFO",
|
||||
"RKEY",
|
||||
"RP",
|
||||
"RRSIG",
|
||||
"RT",
|
||||
"SIG",
|
||||
"SINK",
|
||||
"SMIMEA",
|
||||
"SOA",
|
||||
"SPF",
|
||||
"SRV",
|
||||
"SSHFP",
|
||||
"SVCB",
|
||||
"TA",
|
||||
"TALINK",
|
||||
"TLSA",
|
||||
"TXT",
|
||||
"UID",
|
||||
"UINFO",
|
||||
"UNSPEC",
|
||||
"URI",
|
||||
"WKS",
|
||||
"X25",
|
||||
"ZONEMD",
|
||||
],
|
||||
),
|
||||
("-P", []),
|
||||
],
|
||||
)
|
||||
def test_rrchecker_list_standard_names(option, expected_result):
|
||||
stdout = subprocess.run(
|
||||
[
|
||||
os.environ["RRCHECKER"],
|
||||
option,
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
).stdout.decode("utf-8")
|
||||
values = [line for line in stdout.split("\n") if line.strip()]
|
||||
|
||||
assert sorted(values) == sorted(expected_result)
|
||||
|
||||
|
||||
def run_rrchecker(option, rr_class, rr_type, rr_rest):
|
||||
with subprocess.Popen(
|
||||
[os.environ["RRCHECKER"], option],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
) as process:
|
||||
rrchecker_output, _ = process.communicate(
|
||||
f"{rr_class} {rr_type} {rr_rest}".encode("utf-8")
|
||||
)
|
||||
return rrchecker_output.decode("utf-8").split()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option", ["-p", "-u"])
|
||||
def test_rrchecker_conversions(option):
|
||||
tempzone_file = "tempzone"
|
||||
with open(tempzone_file, "w", encoding="utf-8") as file:
|
||||
subprocess.run(
|
||||
[
|
||||
os.environ["SHELL"],
|
||||
os.environ["TOP_SRCDIR"] + "/bin/tests/system/genzone.sh",
|
||||
"0",
|
||||
],
|
||||
stdout=file,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
checkzone_output = subprocess.run(
|
||||
[
|
||||
os.environ["CHECKZONE"],
|
||||
"-D",
|
||||
"-q",
|
||||
".",
|
||||
tempzone_file,
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
).stdout.decode("utf-8")
|
||||
checkzone_output = [
|
||||
line for line in checkzone_output.splitlines() if not line.startswith(";")
|
||||
]
|
||||
|
||||
for rr in checkzone_output:
|
||||
rr_parts_orig = rr.split()
|
||||
assert len(rr_parts_orig) >= 4, f"invalid rr: {rr}"
|
||||
rr_class_orig, rr_type_orig, rr_rest_orig = (
|
||||
rr_parts_orig[2],
|
||||
rr_parts_orig[3],
|
||||
" ".join(rr_parts_orig[4:]),
|
||||
)
|
||||
rr_class, rr_type, rr_rest = rr_class_orig, rr_type_orig, rr_rest_orig
|
||||
if option == "-u":
|
||||
rr_class, rr_type, *rr_rest = run_rrchecker(
|
||||
"-u", rr_class_orig, rr_type_orig, rr_rest_orig
|
||||
)
|
||||
rr_rest = " ".join(rr_rest)
|
||||
|
||||
rr_class, rr_type, *rr_rest = run_rrchecker("-p", rr_class, rr_type, rr_rest)
|
||||
|
||||
assert rr_class_orig == rr_class
|
||||
assert rr_type_orig == rr_type
|
||||
assert rr_rest_orig == " ".join(rr_rest)
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
|
||||
|
||||
def test_rrchecker(run_tests_sh):
|
||||
run_tests_sh()
|
||||
@@ -0,0 +1,82 @@
|
||||
A
|
||||
NS
|
||||
MD
|
||||
MF
|
||||
CNAME
|
||||
SOA
|
||||
MB
|
||||
MG
|
||||
MR
|
||||
NULL
|
||||
WKS
|
||||
PTR
|
||||
HINFO
|
||||
MINFO
|
||||
MX
|
||||
TXT
|
||||
RP
|
||||
AFSDB
|
||||
X25
|
||||
ISDN
|
||||
RT
|
||||
NSAP
|
||||
NSAP-PTR
|
||||
SIG
|
||||
KEY
|
||||
PX
|
||||
GPOS
|
||||
AAAA
|
||||
LOC
|
||||
NXT
|
||||
EID
|
||||
NIMLOC
|
||||
SRV
|
||||
ATMA
|
||||
NAPTR
|
||||
KX
|
||||
CERT
|
||||
A6
|
||||
DNAME
|
||||
SINK
|
||||
APL
|
||||
DS
|
||||
SSHFP
|
||||
IPSECKEY
|
||||
RRSIG
|
||||
NSEC
|
||||
DNSKEY
|
||||
DHCID
|
||||
NSEC3
|
||||
NSEC3PARAM
|
||||
TLSA
|
||||
SMIMEA
|
||||
HIP
|
||||
NINFO
|
||||
RKEY
|
||||
TALINK
|
||||
CDS
|
||||
CDNSKEY
|
||||
OPENPGPKEY
|
||||
CSYNC
|
||||
ZONEMD
|
||||
SVCB
|
||||
HTTPS
|
||||
SPF
|
||||
UINFO
|
||||
UID
|
||||
GID
|
||||
UNSPEC
|
||||
NID
|
||||
L32
|
||||
L64
|
||||
LP
|
||||
EUI48
|
||||
EUI64
|
||||
URI
|
||||
CAA
|
||||
AVC
|
||||
DOA
|
||||
AMTRELAY
|
||||
RESINFO
|
||||
TA
|
||||
DLV
|
||||
@@ -323,7 +323,6 @@ sub construct_ans_command {
|
||||
}
|
||||
|
||||
if (-e "$testdir/$server/ans.py") {
|
||||
$ENV{'PYTHONPATH'} = $testdir . ":" . $ENV{'srcdir'};
|
||||
$command = "$PYTHON -u ans.py 10.53.0.$n $queryport";
|
||||
} elsif (-e "$testdir/$server/ans.pl") {
|
||||
$command = "$PERL ans.pl";
|
||||
|
||||
@@ -17,6 +17,3 @@ a.root-servers.nil. A 10.53.0.1
|
||||
example.com. NS example.
|
||||
|
||||
ns.example.net. A 10.53.0.3
|
||||
|
||||
unsigned. NS ns.unsigned.
|
||||
ns.unsigned. A 10.53.0.3
|
||||
|
||||
@@ -33,7 +33,6 @@ options {
|
||||
recursion yes;
|
||||
dnssec-validation yes;
|
||||
notify no;
|
||||
minimal-responses no;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
@@ -61,8 +60,3 @@ zone "undelegated" {
|
||||
type static-stub;
|
||||
server-addresses { 10.53.0.3; };
|
||||
};
|
||||
|
||||
zone "unsigned" {
|
||||
type static-stub;
|
||||
server-addresses { 10.53.0.3; };
|
||||
};
|
||||
|
||||
@@ -44,8 +44,3 @@ zone "undelegated" {
|
||||
type primary;
|
||||
file "undelegated.db.signed";
|
||||
};
|
||||
|
||||
zone "unsigned" {
|
||||
type primary;
|
||||
file "unsigned.db";
|
||||
};
|
||||
|
||||
@@ -1,24 +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.
|
||||
|
||||
$ORIGIN .
|
||||
$TTL 300 ; 5 minutes
|
||||
unsigned IN SOA ns.unsigned. hostmaster.unsigned. (
|
||||
2010080906 ; serial
|
||||
20 ; refresh (20 seconds)
|
||||
20 ; retry (20 seconds)
|
||||
1814400 ; expire (3 weeks)
|
||||
3600 ; minimum (1 hour)
|
||||
)
|
||||
unsigned. 5 NS ns.unsigned.
|
||||
ns.unsigned. A 10.53.0.3
|
||||
|
||||
data.unsigned. 20 TXT "example org data"
|
||||
@@ -211,25 +211,5 @@ grep "status: NOERROR" dig.out.ns2.soa.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "checking static-stub synthesised NS is not returned ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS unsigned. @10.53.0.2 ns >dig.out.ns2.ns.test$n || ret=1
|
||||
sleep 2
|
||||
$DIG $DIGOPTS data.unsigned @10.53.0.2 txt >dig.out.ns2.txt1.test$n || ret=1
|
||||
sleep 4
|
||||
$DIG $DIGOPTS data.unsigned @10.53.0.2 txt >dig.out.ns2.txt2.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.ns2.ns.test$n >/dev/null || ret=1
|
||||
grep "status: NOERROR" dig.out.ns2.txt1.test$n >/dev/null || ret=1
|
||||
# NS RRset from zone is returned
|
||||
grep '^unsigned\..*NS.ns\.unsigned\.$' dig.out.ns2.txt1.test$n >/dev/null || ret=1
|
||||
grep '^unsigned\..*NS.unsigned\.$' dig.out.ns2.txt1.test$n >/dev/null && ret=1
|
||||
# NS expired and synthesised response is not returned
|
||||
grep "status: NOERROR" dig.out.ns2.txt2.test$n >/dev/null || ret=1
|
||||
grep '^unsigned\..*NS.ns\.unsigned\.$' dig.out.ns2.txt2.test$n >/dev/null && ret=1
|
||||
grep '^unsigned\..*NS.unsigned\.$' dig.out.ns2.txt2.test$n >/dev/null && ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
||||
@@ -1354,9 +1354,6 @@ plus_option(char *option, struct query *query, bool global) {
|
||||
"ednsflags");
|
||||
CHECK("parse_xint(ednsflags)",
|
||||
result);
|
||||
if (query->edns == -1) {
|
||||
query->edns = 1;
|
||||
}
|
||||
query->ednsflags = num;
|
||||
break;
|
||||
case 'o':
|
||||
|
||||
+10
-34
@@ -16,7 +16,7 @@
|
||||
#
|
||||
m4_define([bind_VERSION_MAJOR], 9)dnl
|
||||
m4_define([bind_VERSION_MINOR], 19)dnl
|
||||
m4_define([bind_VERSION_PATCH], 24)dnl
|
||||
m4_define([bind_VERSION_PATCH], 22)dnl
|
||||
m4_define([bind_VERSION_EXTRA], -dev)dnl
|
||||
m4_define([bind_DESCRIPTION], [(Development Release)])dnl
|
||||
m4_define([bind_SRCID], [m4_esyscmd_s([git rev-parse --short HEAD | cut -b1-7])])dnl
|
||||
@@ -171,7 +171,7 @@ AC_ARG_ENABLE([developer],
|
||||
|
||||
AS_IF([test "$enable_developer" = "yes"],
|
||||
[DEVELOPER_MODE=yes
|
||||
STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1 -DDNS_RBTDB_STRONG_RWLOCK_CHECK=1 -DISC_MUTEX_ERROR_CHECK=1"
|
||||
STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1 -DISC_MUTEX_ERROR_CHECK=1"
|
||||
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
||||
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
||||
test "${with_cmocka+set}" = set || with_cmocka=yes
|
||||
@@ -1203,8 +1203,8 @@ AC_ARG_ENABLE([fixed-rrset],
|
||||
[enable fixed rrset ordering [default=no]])],
|
||||
[], [enable_fixed_rrset="no"])
|
||||
AS_IF([test "$enable_fixed_rrset" = "yes"],
|
||||
[AC_MSG_WARN([fixed rrset ordering is deprecated and will be removed in the future release])
|
||||
AC_DEFINE([DNS_RDATASET_FIXED], [1], [Define to enable "rrset-order fixed" syntax.])])
|
||||
[AC_DEFINE([DNS_RDATASET_FIXED], [1],
|
||||
[Define to enable "rrset-order fixed" syntax.])])
|
||||
|
||||
#
|
||||
# Activate dnstap?
|
||||
@@ -1570,34 +1570,14 @@ AS_IF([test -z "$DTRACE"],
|
||||
AC_SUBST([DTRACE])
|
||||
|
||||
#
|
||||
# Which should be the default zone database, RBTDB or QPZONE?
|
||||
# [pairwise: --with-zonedb=qp, --with-zonedb=rbt]
|
||||
# The default zone database is always QPZONE now.
|
||||
#
|
||||
AC_ARG_WITH([zonedb],
|
||||
[AS_HELP_STRING([--with-zonedb=detect],[specify default zone database type (default is "qpzone")])],
|
||||
[],[with_zonedb=qp])
|
||||
zonedb="qpzone"
|
||||
AS_CASE([$with_zonedb],
|
||||
[RBT*|rbt*],[zonedb="rbt"],
|
||||
[QP|qp],[],
|
||||
[AC_MSG_ERROR([Unknown zone database type])]
|
||||
)
|
||||
AC_DEFINE_UNQUOTED([ZONEDB_DEFAULT], ["$zonedb"], [Default zone database type])
|
||||
AC_DEFINE_UNQUOTED([ZONEDB_DEFAULT], ["qpzone"], [Default zone database type])
|
||||
|
||||
#
|
||||
# Which should be the default zone database, RBTDB or QPCACHE?
|
||||
# [pairwise: --with-cachedb=qp, --with-cachedb=rbt]
|
||||
# The default cache database is always QPCACHE now.
|
||||
#
|
||||
AC_ARG_WITH([cachedb],
|
||||
[AS_HELP_STRING([--with-cachedb=detect],[specify default cache database type (default is "qpcache")])],
|
||||
[],[with_cachedb=qp])
|
||||
cachedb="qpcache"
|
||||
AS_CASE([$with_cachedb],
|
||||
[RBT*|rbt*],[cachedb="rbt"],
|
||||
[QP*|qp*],[],
|
||||
[AC_MSG_ERROR([Unknown cache database type])]
|
||||
)
|
||||
AC_DEFINE_UNQUOTED([CACHEDB_DEFAULT], ["$cachedb"], [Default cache database type])
|
||||
AC_DEFINE_UNQUOTED([CACHEDB_DEFAULT], ["qpcache"], [Default cache database type])
|
||||
|
||||
#
|
||||
# Files to configure. These are listed here because we used to
|
||||
@@ -1697,12 +1677,8 @@ report() {
|
||||
if test "yes" = "$with_jemalloc"; then
|
||||
echo " Memory allocator: jemalloc"
|
||||
fi
|
||||
if test "yes" = "$enable_full_report" -o "rbt" = "$zonedb"; then
|
||||
echo " Default zone database type: $zonedb"
|
||||
fi
|
||||
if test "yes" = "$enable_full_report" -o "rbt" = "$zonedb"; then
|
||||
echo " Default cache database type: $cachedb"
|
||||
fi
|
||||
echo " Default zone database type: $zonedb"
|
||||
echo " Default cache database type: $cachedb"
|
||||
if test "yes" = "$enable_full_report" -o "standard" = "$with_locktype"; then
|
||||
echo " Mutex lock type: $with_locktype"
|
||||
fi
|
||||
|
||||
@@ -45,7 +45,6 @@ def lines_containing(lines, string):
|
||||
changes_issue_or_mr_id_regex = re.compile(rb"\[(GL [#!]|RT #)[0-9]+\]")
|
||||
relnotes_issue_or_mr_id_regex = re.compile(rb":gl:`[#!][0-9]+`")
|
||||
release_notes_regex = re.compile(r"doc/(arm|notes)/notes-.*\.(rst|xml)")
|
||||
rdata_regex = re.compile(r"lib/dns/rdata/")
|
||||
|
||||
modified_files = danger.git.modified_files
|
||||
affected_files = (
|
||||
@@ -337,16 +336,6 @@ if changes_added_lines:
|
||||
# Notes" label set. (This ensures that merge requests updating release
|
||||
# notes can be easily found using the "Release Notes" label.)
|
||||
#
|
||||
# * A file was added to or deleted from the lib/dns/rdata/ subdirectory but
|
||||
# release notes were not modified. This is probably a mistake because new
|
||||
# RR types are a user-visible change (and so is removing support for
|
||||
# existing ones).
|
||||
#
|
||||
# * "Release notes" and "No CHANGES" labels are both set at the same time.
|
||||
# (If something is worth a release note, it should surely show up in
|
||||
# CHANGES.) MRs with certain labels set ("Documentation", "Release") are
|
||||
# exempt because these are typically used during release process.
|
||||
#
|
||||
# - WARN if any of the following is true:
|
||||
#
|
||||
# * This merge request does not update release notes and has the "Customer"
|
||||
@@ -371,30 +360,11 @@ if not release_notes_changed:
|
||||
"This merge request has the *Customer* label set. "
|
||||
"Update release notes unless the changes introduced are trivial."
|
||||
)
|
||||
rdata_types_add_rm = list(
|
||||
filter(rdata_regex.match, danger.git.created_files + danger.git.deleted_files)
|
||||
)
|
||||
if rdata_types_add_rm:
|
||||
fail(
|
||||
"This merge request adds new files to `lib/dns/rdata/` and/or "
|
||||
"deletes existing files from that directory, which almost certainly "
|
||||
"means that it adds support for a new RR type or removes support "
|
||||
"for an existing one. Please add a relevant release note."
|
||||
)
|
||||
if release_notes_changed and not release_notes_label_set:
|
||||
fail(
|
||||
"This merge request modifies release notes. "
|
||||
"Revert release note modifications or set the *Release Notes* label."
|
||||
)
|
||||
if (
|
||||
release_notes_label_set
|
||||
and no_changes_label_set
|
||||
and not ("Documentation" in mr_labels or "Release" in mr_labels)
|
||||
):
|
||||
fail(
|
||||
"This merge request is labeled with both *Release notes* and *No CHANGES*. "
|
||||
"A user-visible change should also be mentioned in the `CHANGES` file."
|
||||
)
|
||||
|
||||
if release_notes_changed:
|
||||
modified_or_new_files = danger.git.modified_files + danger.git.created_files
|
||||
@@ -452,25 +422,6 @@ if switches_added:
|
||||
"job to exercise the new `./configure` switches."
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# PRE-RELEASE TESTING
|
||||
###############################################################################
|
||||
#
|
||||
# WARN if the merge request is marked with the "Security" label, but not with
|
||||
# the label used for marking merge requests for pre-release testing (if the
|
||||
# latter is defined by the relevant environment variable).
|
||||
|
||||
pre_release_testing_label = os.getenv("PRE_RELEASE_TESTING_LABEL")
|
||||
if (
|
||||
pre_release_testing_label
|
||||
and "Security" in mr_labels
|
||||
and pre_release_testing_label not in mr_labels
|
||||
):
|
||||
warn(
|
||||
"This merge request is marked with the *Security* label, but it is not "
|
||||
f"marked for pre-release testing (*{pre_release_testing_label}*)."
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# USER-VISIBLE LOG LEVELS
|
||||
###############################################################################
|
||||
|
||||
@@ -303,10 +303,6 @@ November 2020.
|
||||
:rfc:`9103` - W. Toorop, S. Dickinson, S. Sahib, P. Aras, and A. Mankin.
|
||||
*DNS Zone Transfer over TLS.* August 2021. [#rfc9103]_
|
||||
|
||||
:rfc:`9460` - B. Schwartz, M. Bishop and E. Nygren, *Service Binding and
|
||||
Parameter Specification via the DNS (SVCB and HTTPS Resource Records).*
|
||||
November 2023.
|
||||
|
||||
Best Current Practice RFCs
|
||||
--------------------------
|
||||
|
||||
@@ -446,9 +442,6 @@ Notes
|
||||
.. [#rfc9103] Strict TLS and Mutual TLS authentication mechanisms are
|
||||
not supported yet.
|
||||
|
||||
.. [#rfc9460] Additional section processing is not supported for HTTPS and
|
||||
SVCB records.
|
||||
|
||||
.. _internet_drafts:
|
||||
|
||||
Internet Drafts
|
||||
|
||||
@@ -39,7 +39,6 @@ information about each release, and source code.
|
||||
.. include:: ../notes/notes-known-issues.rst
|
||||
|
||||
.. include:: ../notes/notes-current.rst
|
||||
.. include:: ../notes/notes-9.19.22.rst
|
||||
.. include:: ../notes/notes-9.19.21.rst
|
||||
.. include:: ../notes/notes-9.19.20.rst
|
||||
.. include:: ../notes/notes-9.19.19.rst
|
||||
|
||||
@@ -47,7 +47,7 @@ following systems:
|
||||
- Ubuntu LTS 20.04, 22.04
|
||||
- Fedora 39
|
||||
- Red Hat Enterprise Linux / CentOS / Oracle Linux 8, 9
|
||||
- FreeBSD 13.3, 14.0
|
||||
- FreeBSD 13.2, 14.0
|
||||
- Alpine Linux 3.19
|
||||
|
||||
The amd64 CPU architecture is fully supported and regularly tested.
|
||||
|
||||
@@ -4014,11 +4014,9 @@ client's address. This only requires configuring the name servers, not
|
||||
all the clients.
|
||||
|
||||
.. namedconf:statement:: sortlist
|
||||
:tags: query, deprecated
|
||||
:tags: query
|
||||
:short: Controls the ordering of RRs returned to the client, based on the client's IP address.
|
||||
|
||||
This option is deprecated and will be removed in a future release.
|
||||
|
||||
The :any:`sortlist` statement (see below) takes an :term:`address_match_list` and
|
||||
interprets it in a special way. Each top-level statement in the :any:`sortlist`
|
||||
must itself be an explicit :term:`address_match_list` with one or two elements. The
|
||||
@@ -4132,8 +4130,6 @@ RRset Ordering
|
||||
``fixed``
|
||||
Records are returned in the order they are defined in the zone file.
|
||||
|
||||
This value is deprecated and will be removed in a future release.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``fixed`` option is only available if BIND is configured with
|
||||
@@ -4785,7 +4781,6 @@ The current list of empty zones is:
|
||||
- B.E.F.IP6.ARPA
|
||||
- EMPTY.AS112.ARPA
|
||||
- HOME.ARPA
|
||||
- RESOLVER.ARPA
|
||||
|
||||
Empty zones can be set at the view level and only apply to views of
|
||||
class IN. Disabled empty zones are only inherited from options if there
|
||||
|
||||
@@ -1,260 +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.
|
||||
-->
|
||||
|
||||
Red-Black Tree Implementation Notes
|
||||
|
||||
OVERVIEW
|
||||
|
||||
BIND9's basic name storage mechanism is to use a modified form of
|
||||
balanced binary tree known as a red-black tree. Red-black trees
|
||||
provide for relatively efficient storage, retrieval and removal of
|
||||
data while maintaining the lexical order of all stored keys, a
|
||||
necessary function for DNS security.
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
A red-black tree is a balanced binary tree named for the coloring that
|
||||
is done in the tree, identifying each node as either red or black.
|
||||
There are two simple rules for maintaining the color of nodes:
|
||||
(1) A red node has only black children.
|
||||
(2) The path from the root to any leaf node always includes the
|
||||
same number of black nodes.
|
||||
|
||||
Whenever a key is added or removed, adjustments are made to adhere to
|
||||
those two rules. These adjustments are relatively cheap to make but
|
||||
maintain the balance of the tree, thus making for efficient addition,
|
||||
lookup and deletion operations, all of which are O(log N). The color
|
||||
of a node is not relevant to external users of the tree; it is needed
|
||||
only to maintain the balance of the tree.
|
||||
|
||||
For more information on basic red-black trees, see _Introduction to
|
||||
Algorithms_, Cormen, Leiserson, and Rivest, MIT Press / McGraw Hill,
|
||||
1990, ISBN 0-262-03141-8, chapter 14.
|
||||
|
||||
In BIND9, the red-black tree implementation uses DNS names as keys,
|
||||
and can store arbitrary data with each key value. "name" and "key"
|
||||
are used interchangeably in this document.
|
||||
|
||||
The basic red-black tree algorithm is further adapted for use in BIND9
|
||||
to incorporate the notion of hierarchy, creating a tree of red-black
|
||||
trees. Where there is more than one name with a common suffix, all
|
||||
names with that suffix are stored in their own red-black tree, with a
|
||||
down pointer from the suffix locating the subtree.
|
||||
|
||||
For example, consider storing the following names:
|
||||
a x.d.e.f o.w.y.d.e.f
|
||||
b z.d.e.f p.w.y.d.e.f
|
||||
c g.h q.w.y.d.e.f
|
||||
|
||||
No matter which order the keys were added, this would result in a tree
|
||||
that can be visualized as:
|
||||
|
||||
b
|
||||
/ \
|
||||
a d.e.f
|
||||
/|\
|
||||
c | g.h
|
||||
|
|
||||
w.y
|
||||
/|\
|
||||
x | z
|
||||
|
|
||||
p
|
||||
/ \
|
||||
o q
|
||||
|
||||
This tree shows that when there is no key for a particular label, and
|
||||
when there is only one known label for its immediate subordinate, then
|
||||
multiple labels can appear in a single node, such as at d.e.f and g.h.
|
||||
It also demonstrates that there can be more nodes in the tree of trees
|
||||
than there are actual keys (which degrades the O(log N) performance
|
||||
marginally); the nodes at d.e.f and w.y do not represent keys.
|
||||
|
||||
As an aside, remember that when ordering DNS names, labels are
|
||||
examined from the right, therefore w.y sorts after x and before z.
|
||||
|
||||
A split can occur not only on a regular label boundary, but also
|
||||
between any two bits in an EDNS bitstring label. The common-suffix
|
||||
rules will be applied to keep as many bits together as possible.
|
||||
|
||||
In the current implementation of the tree of trees, a node is
|
||||
considered to "formally" exist only if it has data associated with
|
||||
it. So if the above tree then had the key d.e.f added to it, the
|
||||
operation would succeed rather than getting an "already exists"
|
||||
error.
|
||||
|
||||
Along the same lines, if a key is added with a name which is a proper
|
||||
superdomain of the name stored in an existing node, the operation will
|
||||
succeed by splitting the existing node into one node that is the key
|
||||
and another node that is the remaining parts of the name. Adding e.f
|
||||
to the above tree results in the top level red-black tree having a
|
||||
node named e.f where the current d.e.f is, and a down pointer from
|
||||
d.e.f to a "tree" of a single node named d. The down pointer from d
|
||||
would be kept to the level which has x, w.y, and z.
|
||||
|
||||
A similar split of d.e.f would occur if the name k.e.f were added.
|
||||
The top level tree would have the node e.f with a down pointer to a
|
||||
level that had both d and k, and d would continue to have its down
|
||||
pointer to the x, w.y and z level.
|
||||
|
||||
It is guaranteed when splitting that external references to the node
|
||||
that is split will remain valid --- in the previous examples, anything
|
||||
that was pointing to the node that was d.e.f will still point to the
|
||||
node that is now just d.
|
||||
|
||||
When deleting keys, nodes can be rejoined. If both of p.w.y.d.e.f and
|
||||
q.w.y.d.e.f were removed from the example tree, the node named w.y
|
||||
would become o.w.y. Unlike splitting, it is _not_ guaranteed that
|
||||
external references remain consistent; sometimes they will, sometimes
|
||||
they won't. Also, note that deletion is not perfectly symmetric with
|
||||
addition. If you "undo" the last addition with a deletion of the same
|
||||
key then the tree of trees is not guaranteed to have exactly the same
|
||||
structure as it had prior to the addition. Sometimes, but not always.
|
||||
|
||||
Rejoining does not happen if it would violate any of the rules that
|
||||
cause a split. o would not be rejoined with w.y if w.y had data
|
||||
associated with the key; o would remain as a single node on its own
|
||||
level. This emphasizes the rule that a node is considered to formally
|
||||
exist only if data is associated with it, because even if w.y.d.e.f
|
||||
had been explicitly added as a key but with no data, then o would
|
||||
still be merged with the w.y node when p and q were deleted.
|
||||
|
||||
Searching for a node generally returns one of three possible results:
|
||||
either the key is found, a superdomain (partial match) of the key is
|
||||
found, or no part of the key is found. The first and last are rather
|
||||
obvious, and the second result basically means that a hierarchically
|
||||
enclosing name is found; e.g, searching for bb.rc.vix.com turned up
|
||||
rc.vix.com, but not the full name.
|
||||
|
||||
No locking is done within the RBT library. @@@
|
||||
|
||||
CHAINS
|
||||
|
||||
@@@
|
||||
|
||||
When a partial match is made, level_matches is set while the chain
|
||||
points to the partial match node that was found. Then the chain is
|
||||
adjusted to point to the DNSSEC predecessor node, which might not even
|
||||
be under the same top level domain as the name that was searched for.
|
||||
For example, consider a database that had only the names vix.com and
|
||||
isc.org. A search for uu.net would leave the chain pointed to
|
||||
vix.com, the DNSSEC predecessor. Though this might first appear to
|
||||
cause level_matches to be bogus because the chain has been unwound and
|
||||
sent down another path, note that the partial match node will always
|
||||
be in the chain of the predecessor, too --- and often the partial
|
||||
match node will be the predecessor itself. In the vix.com/isc.org
|
||||
example, the search for uu.net finds a partial match at ".", which is
|
||||
of course also in the path to the vix.com predecessor. A search for
|
||||
www.isc.org would find that isc.org is both the partial match and the
|
||||
predecessor.
|
||||
|
||||
EXTERNAL PROGRAMMATIC DETAILS
|
||||
|
||||
This section details the functions used to interact with the BIND9
|
||||
red-black tree library, or RBT for short.
|
||||
|
||||
A source file that will be using RBT will usually need to include
|
||||
<dns/rbt.h>. This header file automatically includes <isc/result.h),
|
||||
<isc/mem.h>, <dns/types.h>, and <dns/name.h>.
|
||||
|
||||
The rbt.h file has more complete descriptions of each of the functions
|
||||
named here, including what is required for each argument, what each
|
||||
function ensures (and might not ensure) will occur, and the full range
|
||||
of possible results for each call. Note well: if a function returns a
|
||||
dns_result_t rather than void, it definitely means there is something
|
||||
that can go possibly wrong in the function and it should be checked by
|
||||
the caller.
|
||||
|
||||
A new tree of trees must be initialized using:
|
||||
|
||||
dns_result_t dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
|
||||
void *deleter_arg, dns_rbt_t **rbtp);
|
||||
|
||||
The memory context, mctx, must be a non-null pointer that was
|
||||
initialized with isc_mem_create(). The deleter argument, if non-null,
|
||||
should point to a function that is responsible for cleaning up any
|
||||
memory associated with the data pointer of a node when the node is
|
||||
deleted. It is passed the deleted node's data pointer as its first
|
||||
argument and deleter_arg as its second argument.
|
||||
|
||||
After initializing an RBT manager, to add keys to the tree, use:
|
||||
|
||||
dns_result_t dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data);
|
||||
|
||||
The name _must_ be an absolute name. It is not required that the data
|
||||
pointer be non-null, but it is recommended that it point to something,
|
||||
even just invalid memory, because of the various searching and
|
||||
deletion issues described in the previous section. The RBT code will
|
||||
not attempt to dereference the pointer.
|
||||
|
||||
To find a key in the tree, use:
|
||||
|
||||
dns_result_t dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, void **data);
|
||||
|
||||
The data parameter must not be NULL, but *data must be NULL. The
|
||||
result will be either DNS_R_SUCCESS, DNS_R_PARTIALMATCH or
|
||||
DNS_R_NOTFOUND. In the first case, an exact match was found for the
|
||||
name and there was an associate data pointer, which is returned via
|
||||
the data parameter. A partial match results when the name has not
|
||||
been found but a superdomain name, with data, does exist; then the
|
||||
data for that name is returned in the data parameter. If no data is
|
||||
found for the name or a superdomain, *data will remain NULL.
|
||||
|
||||
|
||||
INTERNAL PROGRAMMATIC DETAILS
|
||||
|
||||
This section is mainly relevant to the RBT DB implementation. It is
|
||||
highly recommended that programmers using the RBT library stick to the
|
||||
functions named in the previous section.
|
||||
|
||||
The dns_rbt_addname and dns_rbt_findname functions named in the
|
||||
previous section are wrappers around dns_rbt_addnode and
|
||||
dns_rbt_findnode. The *node functions for the most part do not
|
||||
particularly care whether a node has an associated data pointer or
|
||||
not, whereas the *name functions do. The one exception to this is
|
||||
that when a PARTIALMATCH is returned for a search, the indicated node
|
||||
is the deepest match that has data, rather than just the deepest
|
||||
match. Even that behavior is selectable, however, using the boolean
|
||||
empty_data_ok argument to dns_rbt_findnode.
|
||||
|
||||
Each node in the tree of trees is represented by the following structure:
|
||||
|
||||
typedef struct dns_rbtnode {
|
||||
struct dns_rbtnode *left;
|
||||
struct dns_rbtnode *right;
|
||||
struct dns_rbtnode *down;
|
||||
/*
|
||||
* The following bitfields add up to a total bitwidth of 32.
|
||||
* The range of values necessary for each item is indicated,
|
||||
* but in the case of "attributes" the field is wider to accommodate
|
||||
* possible future expansion. "offsetlen" could be one bit
|
||||
* narrower by always adjusting its value by 1 to find the real
|
||||
* offsetlen, but doing so does not gain anything (except perhaps
|
||||
* another bit for "attributes", which doesn't yet need any more).
|
||||
*/
|
||||
unsigned int color:1; /* range is 0..1 */
|
||||
unsigned int attributes:6; /* range is 0..2 */
|
||||
unsigned int namelen:8; /* range is 1..255 */
|
||||
unsigned int offsetlen:8; /* range is 1..128 */
|
||||
unsigned int padbytes:9; /* range is 0..380 */
|
||||
/*
|
||||
* These values are used in the RBT DB implementation. The
|
||||
* appropriate node lock must be held before accessing them.
|
||||
*/
|
||||
void *data;
|
||||
unsigned int dirty:1;
|
||||
unsigned int locknum:DNS_RBT_LOCKLENGTH;
|
||||
unsigned int references:DNS_RBT_REFLENGTH;
|
||||
} dns_rbtnode_t;
|
||||
|
||||
@@@
|
||||
+2
-2
@@ -274,7 +274,7 @@ options {
|
||||
sig-signing-signatures <integer>;
|
||||
sig-signing-type <integer>;
|
||||
sig-validity-interval <integer> [ <integer> ]; // obsolete
|
||||
sortlist { <address_match_element>; ... }; // deprecated
|
||||
sortlist { <address_match_element>; ... };
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
@@ -569,7 +569,7 @@ view <string> [ <class> ] {
|
||||
sig-signing-signatures <integer>;
|
||||
sig-signing-type <integer>;
|
||||
sig-validity-interval <integer> [ <integer> ]; // obsolete
|
||||
sortlist { <address_match_element>; ... }; // deprecated
|
||||
sortlist { <address_match_element>; ... };
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
|
||||
@@ -1,107 +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.
|
||||
|
||||
Notes for BIND 9.19.22
|
||||
----------------------
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- Information on incoming zone transfers in the statistics channel now also shows
|
||||
the zones' "first refresh" flag, which indicates that a zone is not fully
|
||||
ready and that its first ever refresh is pending or is in progress. The number
|
||||
of such zones is now also exposed by the ``rndc status`` command. :gl:`#4241`
|
||||
|
||||
- The statistics channel now includes counters that indicate the number
|
||||
of currently connected TCP IPv4/IPv6 clients. :gl:`#4425`
|
||||
|
||||
- HSM support was added to :any:`dnssec-policy`. Keys can now be configured with a
|
||||
``key-store`` that allows users to set the directory where key files are stored and to
|
||||
set a PKCS#11 URI string. The latter requires OpenSSL 3 and a valid PKCS#11
|
||||
provider to be configured for OpenSSL. :gl:`#1129`
|
||||
|
||||
- The ``tls`` block was extended with a new ``cipher-suites`` option
|
||||
that allows permitted cipher suites for TLSv1.3 to be set. Please
|
||||
consult the documentation for additional details.
|
||||
:gl:`#3504`
|
||||
|
||||
- Support for the RESINFO record type was added. :gl:`#4413`
|
||||
|
||||
Removed Features
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- BIND 9 no longer supports non-zero :any:`stale-answer-client-timeout` values,
|
||||
when the feature is turned on. When using a non-zero value, :iscman:`named` now
|
||||
generates a warning log message, and treats the value as ``0``. :gl:`#4447`
|
||||
|
||||
Feature Changes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- The ``dnssec-validation yes`` option now requires an explicitly configured
|
||||
:any:`trust-anchors` statement. If using manual trust anchors is not
|
||||
operationally required, then please consider using ``dnssec-validation auto``
|
||||
instead. :gl:`#4373`
|
||||
|
||||
- The red-black tree data structure used in the RBTDB (the default
|
||||
database implementation for cache and zone databases),
|
||||
has been replaced with QP-tries. This is expected to improve
|
||||
performance and scalability, though in the current implementation
|
||||
it is known to have larger memory consumption.
|
||||
|
||||
A side effect of this change is that zone files that are created with
|
||||
:any:`masterfile-style` ``relative`` - for example, the output of
|
||||
:any:`dnssec-signzone` - will no longer have multiple different
|
||||
`$ORIGIN` statements. There should be no other changes to server
|
||||
behavior.
|
||||
|
||||
The old RBT-based database still exists for now, and can be used by
|
||||
specifying ``database rbt`` in a ``zone`` statement in ``named.conf``,
|
||||
or by compiling with ``configure --with-zonedb=rbt --with-cachedb=rbt``.
|
||||
:gl:`#4411`
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- A regression in cache-cleaning code enabled memory use to grow
|
||||
significantly more quickly than before, until the configured
|
||||
:any:`max-cache-size` limit was reached. This has been fixed.
|
||||
:gl:`#4596`
|
||||
|
||||
- Using :option:`rndc flush` inadvertently caused cache cleaning to
|
||||
become less effective. This could ultimately lead to the configured
|
||||
:any:`max-cache-size` limit being exceeded and has now been fixed.
|
||||
:gl:`#4621`
|
||||
|
||||
- The logic for cleaning up expired cached DNS records was
|
||||
tweaked to be more aggressive. This change helps with enforcing
|
||||
:any:`max-cache-ttl` and :any:`max-ncache-ttl` in a timely manner.
|
||||
:gl:`#4591`
|
||||
|
||||
- Changes to ``listen-on`` statements were ignored on reconfiguration
|
||||
unless the port or interface address was changed, making it
|
||||
impossible to change a related listener transport type. That issue
|
||||
has been fixed.
|
||||
|
||||
ISC would like to thank Thomas Amgarten for bringing this issue to
|
||||
our attention. :gl:`#4518` :gl:`#4528`
|
||||
|
||||
- It was possible to trigger a use-after-free assertion when the overmem cache
|
||||
cleaning was initiated. This has been fixed. :gl:`#4595`
|
||||
|
||||
ISC would like to thank Jinmei Tatuya of Infoblox for bringing
|
||||
this issue to our attention.
|
||||
|
||||
Known Issues
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- There are no new known issues with this release. See :ref:`above
|
||||
<relnotes_known_issues>` for a list of all known issues affecting this
|
||||
BIND 9 branch.
|
||||
@@ -9,7 +9,7 @@
|
||||
.. See the COPYRIGHT file distributed with this work for additional
|
||||
.. information regarding copyright ownership.
|
||||
|
||||
Notes for BIND 9.19.24
|
||||
Notes for BIND 9.19.22
|
||||
----------------------
|
||||
|
||||
Security Fixes
|
||||
@@ -20,22 +20,82 @@ Security Fixes
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- None.
|
||||
- The ``tls`` block was extended with a new ``cipher-suites`` option
|
||||
that allows setting allowed cipher suites for TLSv1.3. Please
|
||||
consult the documentation for additional details.
|
||||
:gl:`#3504`
|
||||
|
||||
- The statistics channel now includes counters that indicate the number
|
||||
of currently connected TCP IPv4/IPv6 clients. :gl:`#4425`
|
||||
|
||||
- The statistics channel's incoming zone transfers information now also shows
|
||||
the zones' "first refresh" flag, which indicates that a zone is not fully
|
||||
ready yet, and its first ever refresh is pending or is in-progress. The number
|
||||
of such zones is now also exposed by the ``rndc status`` command. :gl:`#4241`
|
||||
|
||||
- Add HSM support to :any:`dnssec-policy`. You can now configure keys with a
|
||||
``key-store`` that allows you to set the directory to store the key files and
|
||||
set a PKCS#11 URI string. The latter requires OpenSSL 3 and a valid PKCS#11
|
||||
provider to be configured for OpenSSL. :gl`#1129`.
|
||||
|
||||
Removed Features
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- None.
|
||||
- BIND 9 no longer supports non-zero :any:`stale-answer-client-timeout` values,
|
||||
when the feature is turned on. When using a non-zero value, ``named`` now
|
||||
generates a warning log message, and treats the value as ``0``. :gl:`#4447`
|
||||
|
||||
Feature Changes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- None.
|
||||
- The ``dnssec-validation yes`` option now requires an explicitly configured
|
||||
:any:`trust-anchors` statement. If using manual trust anchors is not
|
||||
operationally required, then please consider using ``dnssec-validation auto``
|
||||
instead. :gl:`#4373`
|
||||
|
||||
- The red-black tree data structure used in the RBTDB (the default
|
||||
database implementation for cache and zone databases),
|
||||
has been replaced with QP-tries. This is expected to improve
|
||||
performance and scalability, though in the current implementation
|
||||
it is known to have larger memory consumption.
|
||||
|
||||
A side effect of this change is that zone files that are created with
|
||||
:any:`masterfile-style` ``relative`` - for example, the output of
|
||||
:any:`dnssec-signzone` - will no longer have multiple different
|
||||
`$ORIGIN` statements. There should be no other changes to server
|
||||
behavior.
|
||||
|
||||
The old RBT-based database still exists for now, and can be used by
|
||||
specifying ``database rbt`` in a ``zone`` statement in ``named.conf``,
|
||||
or by compiling with ``configure --with-zonedb=rbt --with-cachedb=rbt``.
|
||||
:gl:`#4411`.
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- None.
|
||||
- Changes to ``listen-on`` statements were ignored on reconfiguration
|
||||
unless the port or interface address was changed, making it
|
||||
impossible to change a related listener transport type. That issue
|
||||
has been fixed.
|
||||
|
||||
ISC would like to thank Thomas Amgarten for bringing this issue to
|
||||
our attention. :gl:`#4518`, :gl:`#4528`
|
||||
|
||||
- A use-after-free assertion might get triggered when the overmem cache
|
||||
cleaning triggers. :gl:`#4595`
|
||||
|
||||
ISC would like to thank to Jinmei Tatuya from Infoblox for bringing
|
||||
this issue to our attention.
|
||||
|
||||
- A regression in cache-cleaning code enabled memory use to grow
|
||||
significantly more quickly than before, until the configured
|
||||
:any:`max-cache-size` limit was reached. This has been fixed.
|
||||
:gl:`#4596`
|
||||
|
||||
- Using :option:`rndc flush` inadvertently caused cache cleaning to
|
||||
become less effective. This could ultimately lead to the configured
|
||||
:any:`max-cache-size` limit being exceeded and has now been fixed.
|
||||
:gl:`#4621`
|
||||
|
||||
Known Issues
|
||||
~~~~~~~~~~~~
|
||||
|
||||
+1
-7
@@ -102,7 +102,6 @@ libdns_la_HEADERS = \
|
||||
include/dns/peer.h \
|
||||
include/dns/private.h \
|
||||
include/dns/qp.h \
|
||||
include/dns/rbt.h \
|
||||
include/dns/rcode.h \
|
||||
include/dns/rdata.h \
|
||||
include/dns/rdataclass.h \
|
||||
@@ -218,12 +217,7 @@ libdns_la_SOURCES = \
|
||||
qp_p.h \
|
||||
qpzone_p.h \
|
||||
qpzone.c \
|
||||
rbt.c \
|
||||
rbt-cachedb.c \
|
||||
rbt-zonedb.c \
|
||||
rbtdb.c \
|
||||
rbtdb_p.h \
|
||||
qpcache.c \
|
||||
qpcache.c \
|
||||
qpcache_p.h \
|
||||
rcode.c \
|
||||
rdata.c \
|
||||
|
||||
+7
-3
@@ -99,6 +99,8 @@ struct dns_adb {
|
||||
dns_view_t *view;
|
||||
dns_resolver_t *res;
|
||||
|
||||
isc_loopmgr_t *loopmgr;
|
||||
|
||||
isc_refcount_t references;
|
||||
|
||||
dns_adbnamelist_t names_lru;
|
||||
@@ -1841,7 +1843,8 @@ ISC_REFCOUNT_IMPL(dns_adb, destroy);
|
||||
*/
|
||||
|
||||
void
|
||||
dns_adb_create(isc_mem_t *mem, dns_view_t *view, dns_adb_t **newadb) {
|
||||
dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_loopmgr_t *loopmgr,
|
||||
dns_adb_t **newadb) {
|
||||
dns_adb_t *adb = NULL;
|
||||
|
||||
REQUIRE(mem != NULL);
|
||||
@@ -1850,6 +1853,7 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, dns_adb_t **newadb) {
|
||||
|
||||
adb = isc_mem_get(mem, sizeof(dns_adb_t));
|
||||
*adb = (dns_adb_t){
|
||||
.loopmgr = loopmgr,
|
||||
.names_lru = ISC_LIST_INITIALIZER,
|
||||
.entries_lru = ISC_LIST_INITIALIZER,
|
||||
};
|
||||
@@ -2990,8 +2994,8 @@ fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth,
|
||||
*/
|
||||
result = dns_resolver_createfetch(
|
||||
adb->res, adbname->name, type, name, nameservers, NULL, NULL, 0,
|
||||
options, depth, qc, isc_loop(), fetch_callback, adbname,
|
||||
&fetch->rdataset, NULL, &fetch->fetch);
|
||||
options, depth, qc, isc_loop_current(adb->loopmgr),
|
||||
fetch_callback, adbname, &fetch->rdataset, NULL, &fetch->fetch);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
DP(ENTER_LEVEL, "fetch_name: createfetch failed with %s",
|
||||
isc_result_totext(result));
|
||||
|
||||
+1
-1
@@ -861,7 +861,7 @@ dns__catz_timer_start(dns_catz_zone_t *catz) {
|
||||
isc_interval_set(&interval, 0, 0);
|
||||
}
|
||||
|
||||
catz->loop = isc_loop();
|
||||
catz->loop = isc_loop_current(catz->catzs->loopmgr);
|
||||
|
||||
isc_timer_create(catz->loop, dns__catz_timer_cb, catz,
|
||||
&catz->updatetimer);
|
||||
|
||||
+6
-5
@@ -198,8 +198,8 @@ getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr,
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_nm_t *nm,
|
||||
isc_tlsctx_cache_t *tlsctx_client_cache,
|
||||
createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_loopmgr_t *loopmgr,
|
||||
isc_nm_t *nm, isc_tlsctx_cache_t *tlsctx_client_cache,
|
||||
dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4,
|
||||
dns_dispatch_t *dispatchv6, dns_view_t **viewp) {
|
||||
isc_result_t result;
|
||||
@@ -214,7 +214,7 @@ createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_nm_t *nm,
|
||||
/* Initialize view security roots */
|
||||
dns_view_initsecroots(view);
|
||||
|
||||
CHECK(dns_view_createresolver(view, nm, 0, tlsctx_client_cache,
|
||||
CHECK(dns_view_createresolver(view, loopmgr, nm, 0, tlsctx_client_cache,
|
||||
dispatchv4, dispatchv6));
|
||||
CHECK(dns_db_create(mctx, CACHEDB_DEFAULT, dns_rootname,
|
||||
dns_dbtype_cache, rdclass, 0, NULL,
|
||||
@@ -290,8 +290,9 @@ dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
|
||||
isc_refcount_init(&client->references, 1);
|
||||
|
||||
/* Create the default view for class IN */
|
||||
result = createview(mctx, dns_rdataclass_in, nm, tlsctx_client_cache,
|
||||
client->dispatchmgr, dispatchv4, dispatchv6, &view);
|
||||
result = createview(mctx, dns_rdataclass_in, loopmgr, nm,
|
||||
tlsctx_client_cache, client->dispatchmgr,
|
||||
dispatchv4, dispatchv6, &view);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup_references;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ struct dns_dbimplementation {
|
||||
#include "db_p.h"
|
||||
#include "qpcache_p.h"
|
||||
#include "qpzone_p.h"
|
||||
#include "rbtdb_p.h"
|
||||
|
||||
unsigned int dns_pps = 0U;
|
||||
|
||||
@@ -72,7 +71,6 @@ static ISC_LIST(dns_dbimplementation_t) implementations;
|
||||
static isc_rwlock_t implock;
|
||||
static isc_once_t once = ISC_ONCE_INIT;
|
||||
|
||||
static dns_dbimplementation_t rbtimp;
|
||||
static dns_dbimplementation_t qpimp;
|
||||
static dns_dbimplementation_t qpzoneimp;
|
||||
|
||||
@@ -82,12 +80,6 @@ initialize(void) {
|
||||
|
||||
ISC_LIST_INIT(implementations);
|
||||
|
||||
rbtimp = (dns_dbimplementation_t){
|
||||
.name = "rbt",
|
||||
.create = dns__rbtdb_create,
|
||||
.link = ISC_LINK_INITIALIZER,
|
||||
};
|
||||
|
||||
qpimp = (dns_dbimplementation_t){
|
||||
.name = "qpcache",
|
||||
.create = dns__qpcache_create,
|
||||
@@ -100,7 +92,6 @@ initialize(void) {
|
||||
.link = ISC_LINK_INITIALIZER,
|
||||
};
|
||||
|
||||
ISC_LIST_APPEND(implementations, &rbtimp, link);
|
||||
ISC_LIST_APPEND(implementations, &qpimp, link);
|
||||
ISC_LIST_APPEND(implementations, &qpzoneimp, link);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <isc/urcu.h>
|
||||
|
||||
#include <dns/nsec3.h>
|
||||
#include <dns/rbt.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
#define RDATATYPE_NCACHEANY DNS_TYPEPAIR_VALUE(0, dns_rdatatype_any)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <dns/ecs.h>
|
||||
#include <dns/nsec.h>
|
||||
#include <dns/rbt.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdatatype.h>
|
||||
#include <dns/result.h>
|
||||
|
||||
+3
-4
@@ -75,13 +75,13 @@ new_forwarders(isc_mem_t *mctx, const dns_name_t *name,
|
||||
forwarders = isc_mem_get(mctx, sizeof(*forwarders));
|
||||
*forwarders = (dns_forwarders_t){
|
||||
.fwdpolicy = fwdpolicy,
|
||||
.name = DNS_NAME_INITEMPTY,
|
||||
.fwdrs = ISC_LIST_INITIALIZER,
|
||||
};
|
||||
isc_mem_attach(mctx, &forwarders->mctx);
|
||||
isc_refcount_init(&forwarders->references, 1);
|
||||
|
||||
dns_name_dupwithoffsets(name, mctx, &forwarders->name);
|
||||
forwarders->name = dns_fixedname_initname(&forwarders->fn);
|
||||
dns_name_copy(name, forwarders->name);
|
||||
|
||||
return (forwarders);
|
||||
}
|
||||
@@ -212,7 +212,6 @@ destroy_forwarders(dns_forwarders_t *forwarders) {
|
||||
}
|
||||
isc_mem_put(forwarders->mctx, fwd, sizeof(*fwd));
|
||||
}
|
||||
dns_name_free(&forwarders->name, forwarders->mctx);
|
||||
isc_mem_putanddetach(&forwarders->mctx, forwarders,
|
||||
sizeof(*forwarders));
|
||||
}
|
||||
@@ -241,7 +240,7 @@ static size_t
|
||||
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
|
||||
uint32_t ival ISC_ATTR_UNUSED) {
|
||||
dns_forwarders_t *fwd = pval;
|
||||
return (dns_qpkey_fromname(key, &fwd->name));
|
||||
return (dns_qpkey_fromname(key, fwd->name));
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -225,11 +225,12 @@ struct dns_adbaddrinfo {
|
||||
*/
|
||||
|
||||
/****
|
||||
**** FUNCTIONS
|
||||
****/
|
||||
**** FUNCTIONS
|
||||
****/
|
||||
|
||||
void
|
||||
dns_adb_create(isc_mem_t *mem, dns_view_t *view, dns_adb_t **newadb);
|
||||
dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_loopmgr_t *loopmgr,
|
||||
dns_adb_t **newadb);
|
||||
/*%<
|
||||
* Create a new ADB.
|
||||
*
|
||||
@@ -244,6 +245,8 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, dns_adb_t **newadb);
|
||||
*
|
||||
*\li 'view' be a pointer to a valid view.
|
||||
*
|
||||
*\li 'loopmgr' be a valid loop manager.
|
||||
*
|
||||
*\li 'newadb' != NULL && '*newadb' == NULL.
|
||||
*/
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ struct dns_forwarders {
|
||||
dns_fwdpolicy_t fwdpolicy;
|
||||
isc_mem_t *mctx;
|
||||
isc_refcount_t references;
|
||||
dns_name_t name;
|
||||
dns_fixedname_t fn;
|
||||
dns_name_t *name;
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
@@ -1339,14 +1339,4 @@ dns_name_isdnssvcb(const dns_name_t *name);
|
||||
* i.e. it starts with and optional _port label followed by a _dns label.
|
||||
*/
|
||||
|
||||
size_t
|
||||
dns_name_size(const dns_name_t *name);
|
||||
/*%<
|
||||
* Return the amount of dynamically allocated memory associated with
|
||||
* 'name' (which is 0 if 'name' is not dynamic).
|
||||
*
|
||||
* Requires:
|
||||
* \li 'name' to be valid.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
@@ -536,12 +536,9 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
|
||||
*
|
||||
* If 'foundname' is not NULL, it will be updated to contain the name
|
||||
* that was found (if any). The return code, ISC_R_SUCCESS or
|
||||
* DNS_R_PARTIALMATCH, indicates whether the name found is the name
|
||||
* that was requested, or an ancestor. If the result is ISC_R_NOTFOUND,
|
||||
* 'foundname' will not be updated. (NOTE: the name will be constructed
|
||||
* from the QP key of the found node, and this can be time-consuming.
|
||||
* In performance-critical code, it is faster to store a copy of the
|
||||
* name in the node data and use that instead of passing 'foundname'.)
|
||||
* DNS_R_PARTIALMATCH, indicates whether the name found is name that
|
||||
* was requested, or an ancestor. If the result is ISC_R_NOTFOUND,
|
||||
* 'foundname' will not be updated.
|
||||
*
|
||||
* If 'chain' is not NULL, it is updated to contain a QP chain with
|
||||
* references to the populated nodes in the tree between the root and
|
||||
|
||||
@@ -1,854 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! \file dns/rbt.h */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/assertions.h>
|
||||
#include <isc/crc64.h>
|
||||
#include <isc/lang.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/refcount.h>
|
||||
|
||||
#include <dns/types.h>
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
/*@{*/
|
||||
/*%
|
||||
* Option values for dns_rbt_findnode().
|
||||
* These are used to form a bitmask.
|
||||
*/
|
||||
#define DNS_RBTFIND_NOOPTIONS 0x00
|
||||
#define DNS_RBTFIND_EMPTYDATA 0x01
|
||||
#define DNS_RBTFIND_NOEXACT 0x02
|
||||
#define DNS_RBTFIND_NOPREDECESSOR 0x04
|
||||
/*@}*/
|
||||
|
||||
#define DNS_RBT_USEMAGIC 1
|
||||
|
||||
#define DNS_RBT_LOCKLENGTH (sizeof(((dns_rbtnode_t *)0)->locknum) * 8)
|
||||
|
||||
#define DNS_RBTNODE_MAGIC ISC_MAGIC('R', 'B', 'N', 'O')
|
||||
#if DNS_RBT_USEMAGIC
|
||||
#define DNS_RBTNODE_VALID(n) ISC_MAGIC_VALID(n, DNS_RBTNODE_MAGIC)
|
||||
#else /* if DNS_RBT_USEMAGIC */
|
||||
#define DNS_RBTNODE_VALID(n) true
|
||||
#endif /* if DNS_RBT_USEMAGIC */
|
||||
|
||||
/*%
|
||||
* This is the structure that is used for each node in the red/black
|
||||
* tree of trees. NOTE WELL: the implementation manages this as a variable
|
||||
* length structure, with the actual wire-format name and other data
|
||||
* appended to this structure. Allocating a contiguous block of memory for
|
||||
* multiple dns_rbtnode structures will not work.
|
||||
*/
|
||||
struct dns_rbtnode {
|
||||
#if DNS_RBT_USEMAGIC
|
||||
unsigned int magic;
|
||||
#endif /* if DNS_RBT_USEMAGIC */
|
||||
/*@{*/
|
||||
/*!
|
||||
* The following bitfields add up to a total bitwidth of 32.
|
||||
* The range of values necessary for each item is indicated.
|
||||
*
|
||||
* In each case below the "range" indicated is what's _necessary_ for
|
||||
* the bitfield to hold, not what it actually _can_ hold.
|
||||
*
|
||||
* Note: Tree lock must be held before modifying these
|
||||
* bit-fields.
|
||||
*
|
||||
* Note: The two "unsigned int :0;" unnamed bitfields on either
|
||||
* side of the bitfields below are scaffolding that border the
|
||||
* set of bitfields which are accessed after acquiring the tree
|
||||
* lock. Please don't insert any other bitfield members between
|
||||
* the unnamed bitfields unless they should also be accessed
|
||||
* after acquiring the tree lock.
|
||||
*/
|
||||
unsigned int : 0; /* start of bitfields c/o tree lock */
|
||||
unsigned int is_root : 1; /*%< range is 0..1 */
|
||||
unsigned int color : 1; /*%< range is 0..1 */
|
||||
unsigned int find_callback : 1; /*%< range is 0..1 */
|
||||
bool absolute : 1; /*%< node with absolute DNS name */
|
||||
unsigned int nsec : 2; /*%< range is 0..3 */
|
||||
unsigned int namelen : 8; /*%< range is 1..255 */
|
||||
unsigned int offsetlen : 8; /*%< range is 1..128 */
|
||||
unsigned int oldnamelen : 8; /*%< range is 1..255 */
|
||||
unsigned int : 0; /* end of bitfields c/o tree lock */
|
||||
/*@}*/
|
||||
|
||||
/*%
|
||||
* These are needed for hashing. The 'uppernode' points to the
|
||||
* node's superdomain node in the parent subtree, so that it can
|
||||
* be reached from a child that was found by a hash lookup.
|
||||
*/
|
||||
unsigned int hashval;
|
||||
dns_rbtnode_t *uppernode;
|
||||
dns_rbtnode_t *hashnext;
|
||||
|
||||
dns_rbtnode_t *parent;
|
||||
dns_rbtnode_t *left;
|
||||
dns_rbtnode_t *right;
|
||||
dns_rbtnode_t *down;
|
||||
|
||||
/*%
|
||||
* Used for LRU cache. This linked list is used to mark nodes which
|
||||
* have no data any longer, but we cannot unlink at that exact moment
|
||||
* because we did not or could not obtain a write lock on the tree.
|
||||
*/
|
||||
ISC_LINK(dns_rbtnode_t) deadlink;
|
||||
|
||||
/*@{*/
|
||||
/*!
|
||||
* These values are used in the RBT DB implementation. The appropriate
|
||||
* node lock must be held before accessing them.
|
||||
*
|
||||
* Note: The two "unsigned int :0;" unnamed bitfields on either
|
||||
* side of the bitfields below are scaffolding that border the
|
||||
* set of bitfields which are accessed after acquiring the node
|
||||
* lock. Please don't insert any other bitfield members between
|
||||
* the unnamed bitfields unless they should also be accessed
|
||||
* after acquiring the node lock.
|
||||
*
|
||||
* NOTE: Do not merge these fields into bitfields above, as
|
||||
* they'll all be put in the same qword that could be accessed
|
||||
* without the node lock as it shares the qword with other
|
||||
* members. Leave these members here so that they occupy a
|
||||
* separate region of memory.
|
||||
*/
|
||||
void *data;
|
||||
uint8_t : 0; /* start of bitfields c/o node lock */
|
||||
uint8_t dirty : 1;
|
||||
uint8_t wild : 1;
|
||||
uint8_t : 0; /* end of bitfields c/o node lock */
|
||||
uint16_t locknum; /* note that this is not in the bitfield */
|
||||
isc_refcount_t references;
|
||||
/*@}*/
|
||||
};
|
||||
|
||||
typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node,
|
||||
dns_name_t *name,
|
||||
void *callback_arg DNS__DB_FLARG);
|
||||
|
||||
typedef void (*dns_rbtdeleter_t)(void *, void *);
|
||||
|
||||
/*****
|
||||
***** Chain Info
|
||||
*****/
|
||||
|
||||
/*!
|
||||
* A chain is used to keep track of the sequence of nodes to reach any given
|
||||
* node from the root of the tree. Originally nodes did not have parent
|
||||
* pointers in them (for memory usage reasons) so there was no way to find
|
||||
* the path back to the root from any given node. Now that nodes have parent
|
||||
* pointers, chains might be going away in a future release, though the
|
||||
* movement functionality would remain.
|
||||
*
|
||||
* Chains may be used to iterate over a tree of trees. After setting up the
|
||||
* chain's structure using dns_rbtnodechain_init(), it needs to be initialized
|
||||
* to point to the lexically first or lexically last node in the tree of trees
|
||||
* using dns_rbtnodechain_first() or dns_rbtnodechain_last(), respectively.
|
||||
* Calling dns_rbtnodechain_next() or dns_rbtnodechain_prev() then moves the
|
||||
* chain over to the next or previous node, respectively.
|
||||
*
|
||||
* In any event, parent information, whether via parent pointers or chains, is
|
||||
* necessary information for iterating through the tree or for basic internal
|
||||
* tree maintenance issues (ie, the rotations that are done to rebalance the
|
||||
* tree when a node is added). The obvious implication of this is that for a
|
||||
* chain to remain valid, the tree has to be locked down against writes for the
|
||||
* duration of the useful life of the chain, because additions or removals can
|
||||
* change the path from the root to the node the chain has targeted.
|
||||
*
|
||||
* The dns_rbtnodechain_ functions _first, _last, _prev and _next all take
|
||||
* dns_name_t parameters for the name and the origin, which can be NULL. If
|
||||
* non-NULL, 'name' will end up pointing to the name data and offsets that are
|
||||
* stored at the node (and thus it will be read-only), so it should be a
|
||||
* regular dns_name_t that has been initialized with dns_name_init. When
|
||||
* 'origin' is non-NULL, it will get the name of the origin stored in it, so it
|
||||
* needs to have its own buffer space and offsets, which is most easily
|
||||
* accomplished with a dns_fixedname_t. It is _not_ necessary to reinitialize
|
||||
* either 'name' or 'origin' between calls to the chain functions.
|
||||
*
|
||||
* NOTE WELL: even though the name data at the root of the tree of trees will
|
||||
* be absolute (typically just "."), it will will be made into a relative name
|
||||
* with an origin of "." -- an empty name when the node is ".". This is
|
||||
* because a common on operation on 'name' and 'origin' is to use
|
||||
* dns_name_concatenate() on them to generate the complete name. An empty name
|
||||
* can be detected when dns_name_countlabels == 0, and is printed by
|
||||
* dns_name_totext()/dns_name_format() as "@", consistent with RFC1035's
|
||||
* definition of "@" as the current origin.
|
||||
*
|
||||
* dns_rbtnodechain_current is similar to the _first, _last, _prev and _next
|
||||
* functions but additionally can provide the node to which the chain points.
|
||||
*/
|
||||
|
||||
/*%
|
||||
* The number of level blocks to allocate at a time, same as the maximum
|
||||
* number of labels. Allocating space for 128 levels when the tree is
|
||||
* almost never that deep is wasteful, but it's not clear that it matters,
|
||||
* since the waste is only 1MB for 1000 concurrently active chains on a
|
||||
* system with 64-bit pointers.
|
||||
*/
|
||||
#define DNS_RBT_LEVELBLOCK 127
|
||||
|
||||
typedef struct dns_rbtnodechain {
|
||||
unsigned int magic;
|
||||
/*%
|
||||
* The terminal node of the chain. It is not in levels[].
|
||||
* This is ostensibly private ... but in a pinch it could be
|
||||
* used tell that the chain points nowhere without needing to
|
||||
* call dns_rbtnodechain_current().
|
||||
*/
|
||||
dns_rbtnode_t *end;
|
||||
/*%
|
||||
* Currently the maximum number of levels is allocated directly in
|
||||
* the structure, but future revisions of this code might have a
|
||||
* static initial block with dynamic growth.
|
||||
*/
|
||||
dns_rbtnode_t *levels[DNS_RBT_LEVELBLOCK];
|
||||
/*%
|
||||
* level_count indicates how deep the chain points into the
|
||||
* tree of trees, and is the index into the levels[] array.
|
||||
* Thus, levels[level_count - 1] is the last level node stored.
|
||||
* A chain that points to the top level of the tree of trees has
|
||||
* a level_count of 0, the first level has a level_count of 1, and
|
||||
* so on.
|
||||
*/
|
||||
unsigned int level_count;
|
||||
/*%
|
||||
* level_matches tells how many levels matched above the node
|
||||
* returned by dns_rbt_findnode(). A match (partial or exact) found
|
||||
* in the first level thus results in level_matches being set to 1.
|
||||
* This is used by the rbtdb to set the start point for a recursive
|
||||
* search of superdomains until the RR it is looking for is found.
|
||||
*/
|
||||
unsigned int level_matches;
|
||||
} dns_rbtnodechain_t;
|
||||
|
||||
/*****
|
||||
***** Public interfaces.
|
||||
*****/
|
||||
isc_result_t
|
||||
dns_rbt_create(isc_mem_t *mctx, dns_rbtdeleter_t deleter, void *deleter_arg,
|
||||
dns_rbt_t **rbtp);
|
||||
/*%<
|
||||
* Initialize a red-black tree of trees.
|
||||
*
|
||||
* Notes:
|
||||
*\li The deleter argument, if non-null, points to a function that is
|
||||
* responsible for cleaning up any memory associated with the data
|
||||
* pointer of a node when the node is deleted. It is passed the
|
||||
* deleted node's data pointer as its first argument and deleter_arg
|
||||
* as its second argument.
|
||||
*
|
||||
* Requires:
|
||||
* \li mctx is a pointer to a valid memory context.
|
||||
*\li rbtp != NULL && *rbtp == NULL
|
||||
*\li arg == NULL iff deleter == NULL
|
||||
*
|
||||
* Ensures:
|
||||
*\li If result is ISC_R_SUCCESS:
|
||||
* *rbtp points to a valid red-black tree manager
|
||||
*
|
||||
*\li If result is failure:
|
||||
* *rbtp does not point to a valid red-black tree manager.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS Success
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbt_addnode(dns_rbt_t *rbt, const dns_name_t *name, dns_rbtnode_t **nodep);
|
||||
|
||||
/*%<
|
||||
* Add 'name' to the tree of trees. On success, return the address of
|
||||
* the newly added node. If 'name' already existed, return ISC_R_EXISTS
|
||||
* and the address of the pre-existing node.
|
||||
*
|
||||
* Requires:
|
||||
*\li rbt is a valid rbt structure.
|
||||
*\li dns_name_isabsolute(name) == TRUE
|
||||
*\li nodep != NULL && *nodep == NULL
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'name' is not altered in any way.
|
||||
*
|
||||
*\li Any external references to nodes in the tree are unaffected by
|
||||
* node splits that are necessary to insert the new name.
|
||||
*
|
||||
*\li If result is ISC_R_SUCCESS:
|
||||
* 'name' is findable in the red/black tree of trees in O(log N).
|
||||
* *nodep is the node that was added for 'name'.
|
||||
*
|
||||
*\li If result is ISC_R_EXISTS:
|
||||
* The tree of trees is unaltered.
|
||||
* *nodep is the existing node for 'name'.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS Success
|
||||
*\li #ISC_R_EXISTS The name already exists, possibly without data.
|
||||
*\li #ISC_R_NOSPACE The name had more logical labels than are allowed.
|
||||
*/
|
||||
|
||||
#define dns_rbt_findnode(rbt, name, foundname, node, chain, options, callback, \
|
||||
callback_arg) \
|
||||
dns__rbt_findnode(rbt, name, foundname, node, chain, options, \
|
||||
callback, callback_arg DNS__DB_FILELINE)
|
||||
isc_result_t
|
||||
dns__rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname,
|
||||
dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
|
||||
unsigned int options, dns_rbtfindcallback_t callback,
|
||||
void *callback_arg DNS__DB_FLARG);
|
||||
/*%<
|
||||
* Find the node for 'name'.
|
||||
*
|
||||
* Notes:
|
||||
*\li A node that has no data is considered not to exist for this function,
|
||||
* unless the DNS_RBTFIND_EMPTYDATA option is set. This applies to both
|
||||
* exact matches and partial matches.
|
||||
*
|
||||
*\li If the chain parameter is non-NULL, then the path through the tree
|
||||
* to the DNSSEC predecessor of the searched for name is maintained,
|
||||
* unless the DNS_RBTFIND_NOPREDECESSOR or DNS_RBTFIND_NOEXACT option
|
||||
* is used. (For more details on those options, see below.)
|
||||
*
|
||||
*\li If there is no predecessor, then the chain will point to nowhere, as
|
||||
* indicated by chain->end being NULL or dns_rbtnodechain_current
|
||||
* returning ISC_R_NOTFOUND. Note that in a normal Internet DNS RBT
|
||||
* there will always be a predecessor for all names except the root
|
||||
* name, because '.' will exist and '.' is the predecessor of
|
||||
* everything. But you can certainly construct a trivial tree and a
|
||||
* search for it that has no predecessor.
|
||||
*
|
||||
*\li Within the chain structure, the 'levels' member of the structure holds
|
||||
* the root node of each level except the first.
|
||||
*
|
||||
*\li The 'level_count' of the chain indicates how deep the chain to the
|
||||
* predecessor name is, as an index into the 'levels[]' array. It does
|
||||
* not count name elements, per se, but only levels of the tree of trees,
|
||||
* the distinction arising because multiple labels from a name can be
|
||||
* stored on only one level. It is also does not include the level
|
||||
* that has the node, since that level is not stored in levels[].
|
||||
*
|
||||
*\li The chain's 'level_matches' is not directly related to the predecessor.
|
||||
* It is the number of levels above the level of the found 'node',
|
||||
* regardless of whether it was a partial match or exact match. When
|
||||
* the node is found in the top level tree, or no node is found at all,
|
||||
* level_matches is 0.
|
||||
*
|
||||
*\li When DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is
|
||||
* returned (also subject to DNS_RBTFIND_EMPTYDATA), even when
|
||||
* there is an exact match in the tree. In this case, the chain
|
||||
* will not point to the DNSSEC predecessor, but will instead point
|
||||
* to the exact match, if there was any. Thus the preceding paragraphs
|
||||
* should have "exact match" substituted for "predecessor" to describe
|
||||
* how the various elements of the chain are set. This was done to
|
||||
* ensure that the chain's state was sane, and to prevent problems that
|
||||
* occurred when running the predecessor location code under conditions
|
||||
* it was not designed for. It is not clear *where* the chain should
|
||||
* point when DNS_RBTFIND_NOEXACT is set, so if you end up using a chain
|
||||
* with this option because you want a particular node, let us know
|
||||
* where you want the chain pointed, so this can be made more firm.
|
||||
*
|
||||
* Requires:
|
||||
*\li rbt is a valid rbt manager.
|
||||
*\li dns_name_isabsolute(name) == TRUE.
|
||||
*\li node != NULL && *node == NULL.
|
||||
*\li #DNS_RBTFIND_NOEXACT and DNS_RBTFIND_NOPREDECESSOR are mutually
|
||||
* exclusive.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'name' and the tree are not altered in any way.
|
||||
*
|
||||
*\li If result is ISC_R_SUCCESS:
|
||||
*\verbatim
|
||||
* *node is the terminal node for 'name'.
|
||||
*
|
||||
* 'foundname' and 'name' represent the same name (though not
|
||||
* the same memory).
|
||||
*
|
||||
* 'chain' points to the DNSSEC predecessor, if any, of 'name'.
|
||||
*
|
||||
* chain->level_matches and chain->level_count are equal.
|
||||
*\endverbatim
|
||||
*
|
||||
* If result is DNS_R_PARTIALMATCH:
|
||||
*\verbatim
|
||||
* *node is the data associated with the deepest superdomain
|
||||
* of 'name' which has data.
|
||||
*
|
||||
* 'foundname' is the name of deepest superdomain (which has
|
||||
* data, unless the DNS_RBTFIND_EMPTYDATA option is set).
|
||||
*
|
||||
* 'chain' points to the DNSSEC predecessor, if any, of 'name'.
|
||||
*\endverbatim
|
||||
*
|
||||
*\li If result is ISC_R_NOTFOUND:
|
||||
*\verbatim
|
||||
* Neither the name nor a superdomain was found. *node is NULL.
|
||||
*
|
||||
* 'chain' points to the DNSSEC predecessor, if any, of 'name'.
|
||||
*
|
||||
* chain->level_matches is 0.
|
||||
*\endverbatim
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS Success
|
||||
*\li #DNS_R_PARTIALMATCH Superdomain found with data
|
||||
*\li #ISC_R_NOTFOUND No match, or superdomain with no data
|
||||
*\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, bool recurse);
|
||||
/*%<
|
||||
* Delete 'node' from the tree of trees.
|
||||
*
|
||||
* Notes:
|
||||
*\li When 'node' is removed, if recurse is true then all nodes
|
||||
* in levels down from it are removed too.
|
||||
*
|
||||
* Requires:
|
||||
*\li rbt is a valid rbt manager.
|
||||
*\li node != NULL.
|
||||
*
|
||||
* Ensures:
|
||||
*\li Does NOT ensure that any external references to nodes in the tree
|
||||
* are unaffected by node joins.
|
||||
*
|
||||
*\li If result is ISC_R_SUCCESS:
|
||||
* 'node' does not appear in the tree with data; however,
|
||||
* the node might still exist if it serves as a pointer to
|
||||
* a lower tree level as long as 'recurse' was false, hence
|
||||
* the node could can be found with dns_rbt_findnode when
|
||||
* that function's empty_data_ok parameter is true.
|
||||
*
|
||||
*\li If result is ISC_R_NOSPACE:
|
||||
* The node was deleted, but the tree structure was not
|
||||
* optimized.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS Success
|
||||
*\li #ISC_R_NOSPACE dns_name_concatenate failed when joining nodes.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name);
|
||||
/*%<
|
||||
* Convert the sequence of labels stored at 'node' into a 'name'.
|
||||
*
|
||||
* Notes:
|
||||
*\li This function does not return the full name, from the root, but
|
||||
* just the labels at the indicated node.
|
||||
*
|
||||
*\li The name data pointed to by 'name' is the information stored
|
||||
* in the node, not a copy. Altering the data at this pointer
|
||||
* will likely cause grief.
|
||||
*
|
||||
* Requires:
|
||||
* \li name->offsets == NULL
|
||||
*
|
||||
* Ensures:
|
||||
* \li 'name' is readonly.
|
||||
*
|
||||
* \li 'name' will point directly to the labels stored after the
|
||||
* dns_rbtnode_t struct.
|
||||
*
|
||||
* \li 'name' will have offsets that also point to the information stored
|
||||
* as part of the node.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name);
|
||||
/*%<
|
||||
* Like dns_rbt_namefromnode, but returns the full name from the root.
|
||||
*
|
||||
* Notes:
|
||||
* \li Unlike dns_rbt_namefromnode, the name will not point directly
|
||||
* to node data. Rather, dns_name_concatenate will be used to copy
|
||||
* the name data from each node into the 'name' argument.
|
||||
*
|
||||
* Requires:
|
||||
* \li name != NULL
|
||||
* \li name has a dedicated buffer.
|
||||
*
|
||||
* Returns:
|
||||
* \li ISC_R_SUCCESS
|
||||
* \li ISC_R_NOSPACE (possible via dns_name_concatenate)
|
||||
* \li DNS_R_NAMETOOLONG (possible via dns_name_concatenate)
|
||||
*/
|
||||
|
||||
char *
|
||||
dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size);
|
||||
/*%<
|
||||
* Format the full name of a node for printing, using dns_name_format().
|
||||
*
|
||||
* Notes:
|
||||
* \li 'size' is the length of the printname buffer. This should be
|
||||
* DNS_NAME_FORMATSIZE or larger.
|
||||
*
|
||||
* Requires:
|
||||
* \li node and printname are not NULL.
|
||||
*
|
||||
* Returns:
|
||||
* \li The 'printname' pointer.
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
dns_rbt_nodecount(dns_rbt_t *rbt);
|
||||
/*%<
|
||||
* Obtain the number of nodes in the tree of trees.
|
||||
*
|
||||
* Requires:
|
||||
* \li rbt is a valid rbt manager.
|
||||
*/
|
||||
|
||||
size_t
|
||||
dns_rbt_hashsize(dns_rbt_t *rbt);
|
||||
/*%<
|
||||
* Obtain the current number of buckets in the 'rbt' hash table.
|
||||
*
|
||||
* Requires:
|
||||
* \li rbt is a valid rbt manager.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbt_destroy(dns_rbt_t **rbtp, unsigned int quantum);
|
||||
/*%<
|
||||
* Stop working with a red-black tree of trees.
|
||||
* If 'quantum' is zero then the entire tree will be destroyed.
|
||||
* If 'quantum' is non zero then up to 'quantum' nodes will be destroyed
|
||||
* allowing the rbt to be incrementally destroyed by repeated calls to
|
||||
* dns_rbt_destroy2(). Once dns_rbt_destroy2() has been called no other
|
||||
* operations than dns_rbt_destroy()/dns_rbt_destroy2() should be
|
||||
* performed on the tree of trees.
|
||||
*
|
||||
* Requires:
|
||||
* \li *rbt is a valid rbt manager.
|
||||
*
|
||||
* Ensures on ISC_R_SUCCESS:
|
||||
* \li All space allocated by the RBT library has been returned.
|
||||
*
|
||||
* \li *rbt is invalidated as an rbt manager.
|
||||
*
|
||||
* Returns:
|
||||
* \li ISC_R_SUCCESS
|
||||
* \li ISC_R_QUOTA if 'quantum' nodes have been destroyed.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbt_printtext(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *),
|
||||
FILE *f);
|
||||
/*%<
|
||||
* Print an ASCII representation of the internal structure of the red-black
|
||||
* tree of trees to the passed stream.
|
||||
*
|
||||
* data_printer is a callback function that is called to print the data
|
||||
* in a node. It should print it to the passed FILE stream.
|
||||
*
|
||||
* Notes:
|
||||
* \li The name stored at each node, along with the node's color, is printed.
|
||||
* Then the down pointer, left and right pointers are displayed
|
||||
* recursively in turn. NULL down pointers are silently omitted;
|
||||
* NULL left and right pointers are printed.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbt_printdot(dns_rbt_t *rbt, bool show_pointers, FILE *f);
|
||||
/*%<
|
||||
* Print a GraphViz dot representation of the internal structure of the
|
||||
* red-black tree of trees to the passed stream.
|
||||
*
|
||||
* If show_pointers is TRUE, pointers are also included in the generated
|
||||
* graph.
|
||||
*
|
||||
* Notes:
|
||||
* \li The name stored at each node, along with the node's color is displayed.
|
||||
* Then the down pointer, left and right pointers are displayed
|
||||
* recursively in turn. NULL left, right and down pointers are
|
||||
* silently omitted.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f);
|
||||
/*%<
|
||||
* Print out various information about a node
|
||||
*
|
||||
* Requires:
|
||||
*\li 'n' is a valid pointer.
|
||||
*
|
||||
*\li 'f' points to a valid open FILE structure that allows writing.
|
||||
*/
|
||||
|
||||
size_t
|
||||
dns__rbt_getheight(dns_rbt_t *rbt);
|
||||
/*%<
|
||||
* Return the maximum height of sub-root nodes found in the red-black
|
||||
* forest.
|
||||
*
|
||||
* The height of a node is defined as the number of nodes in the longest
|
||||
* path from the node to a leaf. For each subtree in the forest, this
|
||||
* function determines the height of its root node. Then it returns the
|
||||
* maximum such height in the forest.
|
||||
*
|
||||
* Note: This function exists for testing purposes. Non-test code must
|
||||
* not use it.
|
||||
*
|
||||
* Requires:
|
||||
* \li rbt is a valid rbt manager.
|
||||
*/
|
||||
|
||||
bool
|
||||
dns__rbt_checkproperties(dns_rbt_t *rbt);
|
||||
/*%<
|
||||
* Check red-black properties of the forest.
|
||||
*
|
||||
* Note: This function exists for testing purposes. Non-test code must
|
||||
* not use it.
|
||||
*
|
||||
* Requires:
|
||||
* \li rbt is a valid rbt manager.
|
||||
*/
|
||||
|
||||
size_t
|
||||
dns__rbtnode_getdistance(dns_rbtnode_t *node);
|
||||
/*%<
|
||||
* Return the distance (in nodes) from the node to its upper node of its
|
||||
* subtree. The root node has a distance of 1. A child of the root node
|
||||
* has a distance of 2.
|
||||
*/
|
||||
|
||||
/*****
|
||||
***** Chain Functions
|
||||
*****/
|
||||
|
||||
void
|
||||
dns_rbtnodechain_init(dns_rbtnodechain_t *chain);
|
||||
/*%<
|
||||
* Initialize 'chain'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid pointer.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'chain' is suitable for use.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbtnodechain_reset(dns_rbtnodechain_t *chain);
|
||||
/*%<
|
||||
* Free any dynamic storage associated with 'chain', and then reinitialize
|
||||
* 'chain'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid pointer.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'chain' is suitable for use, and uses no dynamic storage.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain);
|
||||
/*%<
|
||||
* Free any dynamic storage associated with 'chain', and then invalidates it.
|
||||
*
|
||||
* Notes:
|
||||
*\li Future calls to any dns_rbtnodechain_ function will need to call
|
||||
* dns_rbtnodechain_init on the chain first (except, of course,
|
||||
* dns_rbtnodechain_init itself).
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'chain' is no longer suitable for use, and uses no dynamic storage.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
|
||||
dns_name_t *origin, dns_rbtnode_t **node);
|
||||
/*%<
|
||||
* Provide the name, origin and node to which the chain is currently pointed.
|
||||
*
|
||||
* Notes:
|
||||
*\li The tree need not have be locked against additions for the chain
|
||||
* to remain valid, however there are no guarantees if any deletion
|
||||
* has been made since the chain was established.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*
|
||||
* Ensures:
|
||||
*\li 'node', if non-NULL, is the node to which the chain was pointed
|
||||
* by dns_rbt_findnode, dns_rbtnodechain_first or dns_rbtnodechain_last.
|
||||
* If none were called for the chain since it was initialized or reset,
|
||||
* or if the was no predecessor to the name searched for with
|
||||
* dns_rbt_findnode, then '*node' is NULL and ISC_R_NOTFOUND is returned.
|
||||
*
|
||||
*\li 'name', if non-NULL, is the name stored at the terminal level of
|
||||
* the chain. This is typically a single label, like the "www" of
|
||||
* "www.isc.org", but need not be so. At the root of the tree of trees,
|
||||
* if the node is "." then 'name' is ".", otherwise it is relative to ".".
|
||||
* (Minimalist and atypical case: if the tree has just the name
|
||||
* "isc.org." then the root node's stored name is "isc.org." but 'name'
|
||||
* will be "isc.org".)
|
||||
*
|
||||
*\li 'origin', if non-NULL, is the sequence of labels in the levels
|
||||
* above the terminal level, such as "isc.org." in the above example.
|
||||
* 'origin' is always "." for the root node.
|
||||
*
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS name, origin & node were successfully set.
|
||||
*\li #ISC_R_NOTFOUND The chain does not point to any node.
|
||||
*\li <something_else> Any error return from dns_name_concatenate.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
||||
dns_name_t *name, dns_name_t *origin);
|
||||
/*%<
|
||||
* Set the chain to the lexically first node in the tree of trees.
|
||||
*
|
||||
* Notes:
|
||||
*\li By the definition of ordering for DNS names, the root of the tree of
|
||||
* trees is the very first node, since everything else in the megatree
|
||||
* uses it as a common suffix.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*\li 'rbt' is a valid rbt manager.
|
||||
*
|
||||
* Ensures:
|
||||
*\li The chain points to the very first node of the tree.
|
||||
*
|
||||
*\li 'name' and 'origin', if non-NULL, are set as described for
|
||||
* dns_rbtnodechain_current. Thus 'origin' will always be ".".
|
||||
*
|
||||
* Returns:
|
||||
*\li #DNS_R_NEWORIGIN The name & origin were successfully set.
|
||||
*\li <something_else> Any error result from dns_rbtnodechain_current.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
||||
dns_name_t *name, dns_name_t *origin);
|
||||
/*%<
|
||||
* Set the chain to the lexically last node in the tree of trees.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*\li 'rbt' is a valid rbt manager.
|
||||
*
|
||||
* Ensures:
|
||||
*\li The chain points to the very last node of the tree.
|
||||
*
|
||||
*\li 'name' and 'origin', if non-NULL, are set as described for
|
||||
* dns_rbtnodechain_current.
|
||||
*
|
||||
* Returns:
|
||||
*\li #DNS_R_NEWORIGIN The name & origin were successfully set.
|
||||
*\li <something_else> Any error result from dns_name_concatenate.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
|
||||
dns_name_t *origin);
|
||||
/*%<
|
||||
* Adjusts chain to point the DNSSEC predecessor of the name to which it
|
||||
* is currently pointed.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode,
|
||||
* dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that
|
||||
* dns_rbt_findnode is not guaranteed to point the chain somewhere,
|
||||
* since there may have been no predecessor to the searched for name.
|
||||
*
|
||||
* Ensures:
|
||||
*\li The chain is pointed to the predecessor of its current target.
|
||||
*
|
||||
*\li 'name' and 'origin', if non-NULL, are set as described for
|
||||
* dns_rbtnodechain_current.
|
||||
*
|
||||
*\li 'origin' is only if a new origin was found.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS The predecessor was found and 'name' was set.
|
||||
*\li #DNS_R_NEWORIGIN The predecessor was found with a
|
||||
* different origin and 'name' and 'origin' were set. \li #ISC_R_NOMORE There
|
||||
* was no predecessor. \li <something_else> Any error result from
|
||||
* dns_rbtnodechain_current.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
|
||||
dns_name_t *origin);
|
||||
/*%<
|
||||
* Adjusts chain to point the DNSSEC successor of the name to which it
|
||||
* is currently pointed.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'chain' is a valid chain.
|
||||
*\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode,
|
||||
* dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that
|
||||
* dns_rbt_findnode is not guaranteed to point the chain somewhere,
|
||||
* since there may have been no predecessor to the searched for name.
|
||||
*
|
||||
* Ensures:
|
||||
*\li The chain is pointed to the successor of its current target.
|
||||
*
|
||||
*\li 'name' and 'origin', if non-NULL, are set as described for
|
||||
* dns_rbtnodechain_current.
|
||||
*
|
||||
*\li 'origin' is only if a new origin was found.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS The successor was found and 'name' was set.
|
||||
*\li #DNS_R_NEWORIGIN The successor was found with a different
|
||||
* origin and 'name' and 'origin' were set.
|
||||
*\li #ISC_R_NOMORE There was no successor.
|
||||
*\li <something_else> Any error result from dns_name_concatenate.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
|
||||
dns_name_t *origin);
|
||||
/*%<
|
||||
* Descend down if possible.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name);
|
||||
/*%<
|
||||
* Find the next node at the current depth in DNSSEC order.
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
dns__rbtnode_namelen(dns_rbtnode_t *node);
|
||||
/*%<
|
||||
* Returns the length of the full name of the node. Used only internally
|
||||
* and in unit tests.
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
dns__rbtnode_getsize(dns_rbtnode_t *node);
|
||||
/*
|
||||
* Return allocated size for a node.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
@@ -117,12 +117,7 @@ struct dns_slabheader {
|
||||
|
||||
dns_db_t *db;
|
||||
dns_dbnode_t *node;
|
||||
/*%<
|
||||
* The database and database node objects containing
|
||||
* this rdataset, if any.
|
||||
*/
|
||||
|
||||
isc_stdtime_t last_used;
|
||||
ISC_LINK(struct dns_slabheader) link;
|
||||
|
||||
/*%
|
||||
|
||||
@@ -130,6 +130,8 @@ typedef struct dns_slabheader_proof dns_slabheader_proof_t;
|
||||
typedef struct dns_rbt dns_rbt_t;
|
||||
typedef struct dns_rbtdb dns_rbtdb_t;
|
||||
typedef struct dns_rbtdb_version dns_rbtdb_version_t;
|
||||
typedef struct dns_qpdb dns_qpdb_t;
|
||||
typedef struct dns_qpdb_version dns_qpdb_version_t;
|
||||
typedef struct dns_rbtnode dns_rbtnode_t;
|
||||
typedef ISC_LIST(dns_rbtnode_t) dns_rbtnodelist_t;
|
||||
typedef uint16_t dns_rcode_t;
|
||||
|
||||
@@ -235,17 +235,17 @@ dns_validator_cancel(dns_validator_t *validator);
|
||||
*/
|
||||
|
||||
void
|
||||
dns_validator_shutdown(dns_validator_t *val);
|
||||
dns_validator_destroy(dns_validator_t **validatorp);
|
||||
/*%<
|
||||
* Release the name associated with the DNSSEC validator.
|
||||
* Destroy a DNSSEC validator.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'val' points to a valid DNSSEC validator.
|
||||
*\li '*validatorp' points to a valid DNSSEC validator.
|
||||
* \li The validator must have completed and sent its completion
|
||||
* event.
|
||||
*
|
||||
* Ensures:
|
||||
*\li The name associated with the DNSSEC validator is released.
|
||||
*\li All resources used by the validator are freed.
|
||||
*/
|
||||
|
||||
#if DNS_VALIDATOR_TRACE
|
||||
|
||||
@@ -366,8 +366,9 @@ dns_view_weakdetach(dns_view_t **targetp);
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_view_createresolver(dns_view_t *view, isc_nm_t *netmgr,
|
||||
unsigned int options, isc_tlsctx_cache_t *tlsctx_cache,
|
||||
dns_view_createresolver(dns_view_t *view, isc_loopmgr_t *loopmgr,
|
||||
isc_nm_t *netmgr, unsigned int options,
|
||||
isc_tlsctx_cache_t *tlsctx_cache,
|
||||
dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6);
|
||||
/*%<
|
||||
* Create a resolver and address database for the view.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user