Compare commits

..
Author SHA1 Message Date
Ondřej Surý 6078fad8ae fixup! Temporarily disable TTL-based cleaning 2024-03-13 16:57:40 +01:00
Ondřej Surý c89fe7adb9 Temporarily disable TTL-based cleaning 2024-03-13 16:18:08 +01:00
Ondřej Surý 7c2eb9531a WIP: convert deadnodes to wait-free queue 2024-03-13 13:35:58 +01:00
Ondřej Surý 8a57e01c4c fixup! fixup! WIP: remove nodelock; use node->lock 2024-03-13 12:12:37 +01:00
Ondřej Surý c604cf69d9 fixup! WIP: remove nodelock; use node->lock 2024-03-13 11:43:32 +01:00
Ondřej Surý 296ec40818 WIP: remove nodelock; use node->lock 2024-03-13 11:29:09 +01:00
Ondřej Surý 605fb0f1f1 WIP: Remove RBTDB 2024-03-13 10:19:46 +01:00
Evan Hunt be0837df1f use dns_qpmulti instead of dns_qp in the cache
replace the single-threaded dns_qp objects in the qpcache
database with dns_qpmulti and eliminate the tree lock.
2024-03-13 00:59:29 -07:00
Evan Hunt 87d29f48d4 simplify qpcache iterators
in a cache database, unlike zones, NSEC3 records are stored in
the main tree. it is not necessary to maintain a separate 'nsec3'
tree, nor to have code in the dbiterator implementation to traverse
from one tree to another.

(if we ever implement synth-from-dnssec using NSEC3 records, we'll
need to revert this change. in the meantime, simpler code is better.)
2024-03-13 00:58:47 -07:00
Evan Hunt 47e89b637b various cleanups in qpcache.c
- change dns_qpdata_t to just qpdata_t as it's only accessed locally.
- shorten QPDB_HEADERNODE to just HEADERNODE
- remove unneeded struct members and misleading comments.
- remove unused static function parameters.
- rename 'find_callback' to 'delegating', since the find callback
  mechanism is no longer used for those.
- remove IS_CACHE requirements as they're now redundant: qpcache
  always has cache semantics.
2024-03-13 00:58:38 -07:00
Evan Hunt 274121e771 add static macros for ISC_REFCOUNT_DECL/IMPL
this commit adds a mechanism to statically declare attach/detach
and ref/unref methods, for objects that are only accessed within
a single C file.
2024-03-12 17:01:51 -07:00
Evan Hunt b6d4441e46 dns_name_dupwithoffsets() cannot fail
this function now always returns success; change it to void and
clean up its callers.
2024-03-12 17:01:51 -07:00
Evan Hunt 0e39e81039 remove dead code in rbtdb.c
dns_db_addrdataset() enforces a requirement that version can only
be NULL for a cache database. code that checks for zone semantics
and version == NULL can never be reached.
2024-03-12 16:44:59 -07:00
Evan Hunt 4635b065d4 support nodefullname in rbt-zonedb.c
this enables the 'dyndb' system test to pass when we
build using --with-zonedb=rbt.
2024-03-12 16:44:59 -07:00
Evan Hunt e30487c2b7 remove dead code in qpzone.c
qpzone does not support cache semantics, so dns_db_addrdataset(),
_deleterdataset() and _subtractrdataset() can't be run with
version == NULL; there's no need to check for it.

we can also clean up free_qpdb() a bit since current_version
is always non-NULL.
2024-03-12 16:44:59 -07:00
Evan Hunt cbcefd172e CHANGES for [GL #4614] 2024-03-12 16:44:59 -07:00
Evan Hunt ecfa5dd737 reduce memory consumption of rpz summary database
use dynamically allocated names instead of fixednames in rpz.c
2024-03-12 16:44:58 -07:00
Evan Hunt 0f879219c5 reduce memory consumption of qpcache database
as with qpzone, use a dynamically-allocated dns_name instead
of a dns_fixedname object to store node names in the QP database.
2024-03-12 16:44:58 -07:00
Evan Hunt 4f3d003828 reduce memory consumption of qpzone database
every node of a QP database contains a copy of the nodename,
which is used as the key for the QP-trie. previously, the name
was stored as a dns_fixedname object, which has room for up to
255 characters. we can reduce the space consumed by dynamically
allocating a dns_name object that's just long enough for the name
to be stored.
2024-03-12 16:44:58 -07:00
151 changed files with 16003 additions and 18351 deletions
+32 -42
View File
@@ -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:
+101
View File
@@ -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.
+3 -36
View File
@@ -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
View File
@@ -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;
-4
View File
@@ -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':
+1 -1
View File
@@ -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
View File
@@ -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),
+1 -1
View File
@@ -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()
+2 -3
View File
@@ -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)"
}
+7 -31
View File
@@ -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";
}
-4
View File
@@ -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
-3
View File
@@ -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
-5
View File
@@ -40,8 +40,3 @@ zone "signed-sub5.example" {
type primary;
file "sub.db";
};
zone "mutt.example" {
type primary;
file "mutt.db";
};
-11
View File
@@ -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 {
+2 -14
View File
@@ -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))
+2 -2
View File
@@ -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))
-2
View File
@@ -650,5 +650,3 @@ parse_openssl_config() {
esac
done < "$OPENSSL_CONF"
}
grep_v() { grep -v "$@" || test $? = 1; }
-2
View File
@@ -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
-3
View File
@@ -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(
-8
View File
@@ -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
-1
View File
@@ -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>
+1 -1
View File
@@ -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()
+1
View File
@@ -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*
+27
View File
@@ -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
+14
View File
@@ -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.
+90
View File
@@ -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
-104
View File
@@ -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
+14
View File
@@ -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()
-799
View File
@@ -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
+4 -56
View File
@@ -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
-2
View File
@@ -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,
+4
View File
@@ -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()
+1
View File
@@ -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
+91 -91
View File
@@ -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
+57
View File
@@ -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
-52
View File
@@ -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()
+2
View File
@@ -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
+75
View File
@@ -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
-8
View File
@@ -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
+3 -15
View File
@@ -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
+1 -1
View File
@@ -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
+14
View File
@@ -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
+98
View File
@@ -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()
+82
View File
@@ -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
-1
View File
@@ -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";
-3
View File
@@ -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"
-20
View File
@@ -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
-3
View File
@@ -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
View File
@@ -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
-49
View File
@@ -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
###############################################################################
-7
View File
@@ -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
-1
View File
@@ -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
+1 -1
View File
@@ -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.
+1 -6
View File
@@ -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
-260
View File
@@ -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
View File
@@ -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>;
-107
View File
@@ -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.
+65 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
-9
View File
@@ -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);
}
-1
View File
@@ -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)
-1
View File
@@ -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
View File
@@ -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
+6 -3
View File
@@ -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.
*/
+2 -1
View File
@@ -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
-10
View File
@@ -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
+3 -6
View File
@@ -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
-854
View File
@@ -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 &lt;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 &lt;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 &lt;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 &lt;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 &lt;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
-5
View File
@@ -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;
/*%
+2
View File
@@ -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;
+4 -4
View File
@@ -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
+3 -2
View File
@@ -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