diff --git a/.editorconfig b/.editorconfig index 6e1b7d5ab..0c95aa667 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,7 @@ indent_size = 2 insert_final_newline = true charset = utf-8 end_of_line = lf +trim_trailing_whitespace = true [{pom.xml,*.md}] indent_style = space diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..3fef52615 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text eol=lf +*.bin binary diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml new file mode 100644 index 000000000..1063946ba --- /dev/null +++ b/.github/actions/download-artifact/action.yml @@ -0,0 +1,62 @@ +name: Download artifact +description: Wrapper around GitHub's official action, with additional extraction before download + +# https://github.com/actions/download-artifact/blob/main/action.yml +inputs: + name: + description: Artifact name + required: true + path: + description: Destination path + required: false + default: . + +runs: + using: composite + steps: + - name: Download artifacts + if: github.event_name != 'workflow_run' + uses: actions/download-artifact@v7 + with: + pattern: ${{ inputs.name }} + path: ${{ inputs.path }} + merge-multiple: true + + - name: Download artifacts + if: github.event_name == 'workflow_run' + #v12 + uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 + with: + workflow: ${{ github.event.workflow_run.name }} + run_id: ${{ github.event.workflow_run.id }} + name: ${{ inputs.name }} + path: ${{ inputs.path }} + name_is_regexp: true + + - name: Extract artifacts + if: github.event_name != 'workflow_run' + run: | + for t in ${{ inputs.name }}*.tar + do + tar -xvf "${t}" + done + shell: bash + working-directory: ${{ inputs.path }} + + - name: Extract artifacts + if: github.event_name == 'workflow_run' + run: | + for t in ${{ inputs.name }}/${{ inputs.name }}*.tar + do + mv "${t}" . + tar -xvf "${t}" + done + shell: bash + working-directory: ${{ inputs.path }} + + - name: Remove archive + run: | + rm -f ${{ inputs.name }}.tar + rm -f ${{ inputs.name }}.zip + shell: bash + working-directory: ${{ inputs.path }} diff --git a/.github/actions/prepare-analysis/action.yml b/.github/actions/prepare-analysis/action.yml new file mode 100644 index 000000000..7a7b9fad7 --- /dev/null +++ b/.github/actions/prepare-analysis/action.yml @@ -0,0 +1,64 @@ +name: Prepare code analysis +description: Prepare the working directory for SonarQube code analysis + +inputs: + cache: + description: Cache type + +runs: + using: composite + steps: + - name: Get reports + uses: ./.github/actions/download-artifact + with: + name: reports-* + + - name: Get coverage + uses: ./.github/actions/download-artifact + with: + name: merged-coverage + + - name: Get classes + uses: ./.github/actions/download-artifact + with: + name: classes + + - name: Create paths for JUnit reporting + id: junit_paths + shell: bash + run: | + report_paths="" + check_name="" + for file in target/surefire-reports-* + do + report_paths="${file}/TEST-*.xml"$'\n'"${report_paths}" + check_name="JUnit Report ${file##target/surefire-reports-}"$'\n'"${check_name}" + done + echo "report_paths<> $GITHUB_OUTPUT + echo "check_name<> $GITHUB_OUTPUT + + - name: Publish Test Report + #v6.1.0 + uses: mikepenz/action-junit-report@a294a61c909bd8a4b563024a2faa28897fd53ebc + with: + commit: ${{ github.event.workflow_run.head_sha }} + report_paths: ${{ steps.junit_paths.outputs.report_paths }} + check_name: ${{ steps.junit_paths.outputs.check_name }} + require_tests: true + check_retries: true + detailed_summary: true + + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: ${{ env.BUILD_JAVA_VERSION }} + distribution: temurin + cache: ${{ inputs.cache }} + + - name: Cache SonarCloud packages + if: inputs.cache + uses: actions/cache@v5 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml new file mode 100644 index 000000000..ab93f1fbc --- /dev/null +++ b/.github/actions/upload-artifact/action.yml @@ -0,0 +1,50 @@ +name: Upload artifact +description: Wrapper around GitHub's official action, with additional archiving before upload + +# https://github.com/actions/upload-artifact/blob/main/action.yml +inputs: + name: + description: Artifact name + required: true + filename: + description: Tar filename in artifact + required: false + default: '' + path: + description: One or more files, directories or wildcard pattern that describes what to upload + required: true + if-no-files-found: + description: > + The desired behavior if no files are found using the provided path. + Available Options: + warn: Output a warning but do not fail the action + error: Fail the action with an error message + ignore: Do not output any warnings or errors, the action does not fail + required: false + default: warn + retention-days: + description: > + Duration after which artifact will expire in days. 0 means using default retention. + Minimum 1 day. + Maximum 90 days unless changed from the repository settings page. + required: false + default: '1' + +runs: + using: composite + steps: + - name: Archive artifacts + run: tar -cvf "${{inputs.name}}${{ inputs.filename }}.tar" $(echo "${{ inputs.path }}" | tr '\n' ' ') + shell: bash + + - name: Upload artifacts + uses: actions/upload-artifact@v6 + with: + if-no-files-found: ${{ inputs.if-no-files-found }} + name: ${{ inputs.name }} + path: ${{ inputs.name }}${{ inputs.filename }}.tar + retention-days: ${{ inputs.retention-days }} + + - name: Remove archive + run: rm -f ${{ inputs.name }}.tar + shell: bash diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml new file mode 100644 index 000000000..31b3c7775 --- /dev/null +++ b/.github/workflows/analyze.yml @@ -0,0 +1,91 @@ +name: Analyze PR + +on: + workflow_run: + workflows: + - 'Build' + types: + - completed + +permissions: + pull-requests: read + contents: read + checks: write + +env: + BUILD_JAVA_VERSION: '25' + +jobs: + analyze: + name: Analyze Code + # Only run on forks, in-repo PRs are analyzed directly + if: github.event.workflow_run.head_repository.owner.login != 'dnsjava' + runs-on: ubuntu-latest + steps: + - name: Download PR number artifact + id: get_pr_number + #v12 + uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 + with: + workflow: ${{ github.event.workflow_run.name }} + run_id: ${{ github.event.workflow_run.id }} + name: pr_number + + - name: Read Pull Request Number + id: pr_number + run: | + PR=$(cat pr_number.txt) + echo "pr_number=${PR}" >> "$GITHUB_OUTPUT" + + - name: Request PR data from GitHub API + id: get_pr_data + if: steps.get_pr_number.outputs.found_artifact + #v2.4.0 + uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + route: GET /repos/{full_name}/pulls/{number} + full_name: ${{ github.event.repository.full_name }} + number: ${{ steps.pr_number.outputs.pr_number }} + + - name: Checkout PR + uses: actions/checkout@v6 + with: + repository: ${{ github.event.workflow_run.head_repository.full_name }} + ref: ${{ github.event.workflow_run.head_sha }} + persist-credentials: false + path: pr + # for Sonar + fetch-depth: 0 + + - name: Checkout base + uses: actions/checkout@v6 + with: + repository: ${{ github.event.repository.full_name }} + ref: ${{ fromJson(steps.get_pr_data.outputs.data).base.ref }} + persist-credentials: false + path: base + + - name: Get analysis data + uses: ./base/.github/actions/prepare-analysis + + - name: Run SonarQube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + WF_REVISION: ${{ github.event.workflow_run.head_sha }} + WF_PRKEY: ${{ fromJson(steps.get_pr_data.outputs.data).number }} + WF_BRANCH: ${{ fromJson(steps.get_pr_data.outputs.data).head.ref }} + WF_BASE: ${{ fromJson(steps.get_pr_data.outputs.data).base.ref }} + run: | + cp -f base/pom.xml pr/ + cd pr + mvn -B \ + -f pom.xml \ + -Dsonar.scm.revision='${WF_REVISION}' \ + -Dsonar.pullrequest.key='${WF_PRKEY}' \ + -Dsonar.pullrequest.branch='${WF_BRANCH}' \ + -Dsonar.pullrequest.base='${WF_BASE}' \ + properties:read-project-properties \ + org.sonarsource.scanner.maven:sonar-maven-plugin:sonar diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..b1f3c236a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,228 @@ +name: Build + +on: + push: + branches: + - master + - 'release/**' + pull_request: + branches: + - master + - 'release/**' + +env: + BUILD_JAVA_VERSION: '25' + +jobs: + test: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, windows-latest ] + java: [ '8', '11', '17', '21', '25' ] + arch: [ 'x64' ] + include: + - os: windows-latest + java: '17' + arch: x86 + + name: Java ${{ matrix.java }}/${{ matrix.arch }}/${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.java }} + architecture: ${{ matrix.arch }} + distribution: temurin + cache: maven + + - name: Build with Maven + shell: bash + run: | + TEST_EXCLUSIONS=$([ '${{ matrix.java }}' == '25' ] && echo "" || echo "concurrency" ) + mvn verify \ + -B \ + -Dsurefire.rerunFailingTestsCount=2 \ + -"Dgpg.skip" \ + -DexcludedGroups="${TEST_EXCLUSIONS}" \ + jacoco:report + + - name: Copy build reports + shell: bash + if: always() # always run even if the previous step fails + run: | + cd target + mv jacoco.exec jacoco-${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }}.exec + mv surefire-reports surefire-reports-${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }} + + - name: Verify that the main classes are really compiled for Java 8 + if: matrix.os == 'ubuntu-latest' + run: | + class_file_version=$(javap -v target/classes/org/xbill/DNS/SimpleResolver.class | grep -oP "major version: \K\d+") + echo "::notice file=SimpleResolver.class::Class file version ${class_file_version}" + if [ "${class_file_version}" != "52" ]; then + echo "::error file=SimpleResolver.class::Class file version is not Java 8" + exit 1 + fi + + - name: Upload classes + uses: ./.github/actions/upload-artifact + if: always() && matrix.java == env.BUILD_JAVA_VERSION && matrix.arch == 'x64' && matrix.os == 'ubuntu-latest' + with: + name: classes + path: target/*classes + + - name: Upload JUnit Reports + uses: ./.github/actions/upload-artifact + if: always() # always run even if the previous step fails + with: + name: reports-${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }} + filename: ${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }} + path: target/surefire-reports-*/TEST-*.xml + + - name: Upload Coverage Reports + uses: ./.github/actions/upload-artifact + if: always() # always run even if the previous step fails + with: + name: coverage-${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }} + filename: ${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }} + path: target/jacoco-${{ matrix.java }}-${{ matrix.arch }}-${{ matrix.os }}.exec + + report: + name: JUnit Reports/JaCoCo Merge + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: ${{ env.BUILD_JAVA_VERSION }} + distribution: temurin + cache: maven + + - name: Get coverage artifact + uses: ./.github/actions/download-artifact + with: + name: coverage-* + + - name: Get classes + uses: ./.github/actions/download-artifact + with: + name: classes + + - name: Merge JaCoCo and output + run: mvn -B jacoco:merge jacoco:report + + - name: Upload + uses: ./.github/actions/upload-artifact + with: + name: merged-coverage + path: | + target/site/jacoco + target/jacoco.exec + + - name: Save PR number to file + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != 'dnsjava' + run: echo ${{ github.event.number }} > pr_number.txt + + - name: Archive PR number + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != 'dnsjava' + uses: actions/upload-artifact@v6 + with: + name: pr_number + path: pr_number.txt + + analyze: + name: Analyze Code + runs-on: ubuntu-latest + needs: report + if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == 'dnsjava' + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + # for Sonar + fetch-depth: 0 + + - name: Get analysis data + uses: ./.github/actions/prepare-analysis + with: + cache: maven + + - name: Run codecov + #v5.5.2 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de + + # doesn't work with PRs from forks, see + # https://portal.productboard.com/sonarsource/1-sonarcloud/c/50-sonarcloud-analyzes-external-pull-request + # or https://jira.sonarsource.com/browse/MMF-1371 (not public anymore) + - name: Run SonarQube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B properties:read-project-properties org.sonarsource.scanner.maven:sonar-maven-plugin:sonar + + release: + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/') + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up JDK ${{ env.BUILD_JAVA_VERSION }} + uses: actions/setup-java@v5 + with: + java-version: ${{ env.BUILD_JAVA_VERSION }} + architecture: 'x64' + distribution: temurin + cache: maven + server-id: central + server-username: SONATYPE_USER + server-password: SONATYPE_PW + + - name: Release to Maven Central + env: + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_PW: ${{ secrets.SONATYPE_PW }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PW }} + run: | + cat <(echo -e "${{ secrets.GPG_KEY }}") | gpg --batch --import + gpg --list-secret-keys --keyid-format LONG + mvn \ + --no-transfer-progress \ + --batch-mode \ + compile + # Verify that the main classes are really compiled for Java 8 + class_file_version=$(javap -v target/classes/org/xbill/DNS/SimpleResolver.class | grep -oP "major version: \K\d+") + echo "::notice file=SimpleResolver.class::Class file version ${class_file_version}" + if [ "${class_file_version}" == "52" ]; then + mvn \ + --no-transfer-progress \ + --batch-mode \ + -DperformRelease=true \ + -DskipTests \ + -Dcheckstyle.skip \ + -Dspotless.check.skip=true \ + -Danimal.sniffer.skip=true \ + deploy + else + echo "::error file=SimpleResolver.class::Class file version is not Java 8" + exit 1 + fi diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..f3280389c --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,61 @@ +name: "CodeQL" + +on: + push: + branches: + - master + - 'release/**' + pull_request: + # The branches below must be a subset of the branches above + branches: + - master + - 'release/**' + schedule: + #daily at 01:19 UTC + - cron: '19 1 * * *' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: java + + - name: Set up JDK 25 + uses: actions/setup-java@v5 + with: + java-version: 25 + distribution: temurin + check-latest: true + cache: maven + + - name: Build with Maven + # Skip tests, code style, etc. This is handled in the regular CI workflows. + run: | + mvn clean package -B -V \ + -DskipTests \ + -Dgpg.skip \ + -Dcheckstyle.skip \ + -Denforcer.skip \ + -Dmaven.javadoc.skip \ + -Dspotless.check.skip=true \ + -Danimal.sniffer.skip=true \ + compile \ + test-compile + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 diff --git a/.gitignore b/.gitignore index 4c4dfaab2..7ea6feabc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ *.iml target/ .DS_Store - +.vscode/ +jcstress-results-* diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f7d6f2e3a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -branches: - only: - - master - -language: java -jdk: - - openjdk8 - -install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -Dgpg.skip -B -V -script: mvn test jacoco:report coveralls:report -Dgpg.skip -B diff --git a/Changelog b/Changelog index 72f0610ff..9356884fd 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,231 @@ +01/18/2026 + - 3.6.4 released + - Fix Zone-class serialization (#391) + - Avoid Double DNS Lookup for Names with Labels >= ndots (#388) + - Prevent NPE when calling Message#getTSIG() on DNS request with + bad header (#384) + - Unwrap an exception in the legacy callback-based async interface (#383) + - Prevent ConcurrentModificationException in NIO clients + (#379, @bhaveshthakker) + - Handle `null` in all setSearchPath overloads equally (#157) + - Reduce warning level of invalid `hosts` file entries (#371) + - Remove a lock on the hot-path in the `hosts` file parser (#371) + - DoH Resolver makes use of the Multi-Release jar and tests are executed + for Java 8 and 11+ implementations (#385) + - Fix DoH Resolver initial request delay (#385) + +01/26/2025 + - 3.6.3 released + - Support custom hosts file size (@flaming-archer, #349) + - Fix origin handling in zone loaded from file or stream (#346) + - Prevent TCP port leak when closing IO (#351) + - Fix confusing parameter name in CNAMERecord (@chkal, #354) + - Optionally disable ShutdownHook in NioClient (@SvenssonWeb, #359) + - TSIG algorithm names from RFC 8945 + - Message.toWire can exceed MAXLENGTH (#355) + - TCP query might fail if the shared buffer is full (#357) + - Dynamic updates silently truncates records (#356) + - Fix DoH initial request using recommended nanoTime calculation + (@LinZong, #345) + +09/21/2024 + - 3.6.2 released + - Add new IANA Trust Anchor (@technolord, #337) + - Fix Zone handling with signed SOA (@frankarinnet, #335) + +07/28/2024 + - 3.6.1 released + - Properly fix LookupSession doesn't cache CNAMEs (#316) + - Move JEP-418 SPI to Java 18 to support EOL workflows (#329) + +07/21/2024 + - 3.6.0 released + - Fix CVE-2024-25638 (GHSA-cfxw-4h78-h7fw) + Lookup and LookupSession do not sanitize input properly, + allowing to smuggle additional responses, even with DNSSEC. + I would like to thank Thomas Bellebaum from Fraunhofer AISEC + (@bellebaum) and Martin Schanzenbach (@schanzen) for reporting + and assisting me with this issue. + - Fix CVE-2023-50387 (GHSA-crjg-w57m-rqqf) + Denial-of-Service Algorithmic Complexity Attacks (KeyTrap) + - Fix CVE-2023-50868 (GHSA-mmwx-rj87-vfgr) + NSEC3 closest encloser proof can exhaust CPU resources (KeyTrap) + - Fix running all DNSSEC on the specified executor + - Add new DNSSEC algorithm constants for SM2SM3 and ECC-GOST12 + - Add A/AAAA record constructor with IP address byte array + - Validate DS record digest lengths (#250) + - Fix NPE in SimpleResolver on invalid responses (#277) + - Add support for JEP 418: Internet-Address Resolution SPI (#290) + - Full JPMS support (#246) + - Pluggable I/O for SimpleResolver + (@chrisruffalo, #253) + - UDP port leak in SimpleResolver (#318) + - Fix clean shutdown in app containers when never used (#319) + - Fix concurrency issue in I/O clients (#315, #323) + - LookupSession doesn't cache CNAMEs (#316) + - SimpleResolver can fail with UPDATE response (#322) + - Replace synchronization in Zone with locks + (#305, based on work from @srijeet0406 in #306) + +11/11/2023 + - 3.5.3 released + - Fix CNAME in LookupSession (#279) + - Fix Name constructor failing with max length, relative name + and root origin (#289, @MMauro94) + - Add config option for Resolver I/O timeout (#273, @vmarian2) + - Extend I/O logging + - Prevent exception during TCP I/O with missing or truncated + length prefix + - Use internal base64 codec for Android compatibility (#271) + - Fix multi-message TSIG stream verification for pre-RFC8945 + servers (#295, @frankarinnet and @nguichon) + - Add StreamGenerator for generating RFC8945 compliant + multi-message streams (related to #295) + +11/16/2022 + - 3.5.2 released + - Correctly render empty TXT records (#254) + - More validation on TLSA data input (#257) + +05/15/2022 + - 3.5.1 released + - Fix validation of TSIG signed responses (#249) + - DS rdata digest validation hexadecimal digits (#252) + +02/05/2022 + - 3.5.0 released + - Add full built-in support for DNSSEC based on dnssecjava (#209) + - Make Record classes serializable again (#242) + - Allow SVCB ServiceMode records without params + (#244, @adam-stoler) + - Fix TCPClient receive timeouts + (#218 @nguydavi, #219) + +12/05/2021 + - 3.4.3 released + - Fix handling of buffers in DNSInput + (#224, #225 @nresare) + - Clear existing nameservers on config refresh (#226) + - Fix exception when calling ResolverConfig.refresh (#234) + +09/19/2021 + - 3.4.2 released + - Document behavior of ExtendedResolver.setTimeout (#206) + - Add overloads to use an Executor when sending queries in + resolvers (#211) + - Remove synchronous locks in DoH Resolver (related to #211) + - Fix broken CNAME handling in LookupSession (#212) + - "WireParseException: bad label type" when parsing Message + from ByteBuffer (#213) + - Remove unnecessary synchronization in org.xbill.DNS.Header::getID + (#215, @maltalex) + - Add examples for the LookupSession and direct Resolver usage + +07/30/2021 + - 3.4.1 released + - Allow signing with ED25519 and ED448 algorithms + (#200, Klaus Malorny) + - Rename echconfig to ech in SVCB/HTTPS records + (#202, @adam-stoler) + - Fix bug in Name.compareTo with byte-values >= 128 + (#205, @adam-stoler) + +06/09/2021 + - 3.4.0 released + - UnknownHostException provides details in message (#154) + - Limit length of relative Name to 254 (#165) + - Fix wildcard lookups in Zone (#169) + - Properly close UDP channel upon error + (#177, Walter Scott Johnson) + - Fix load balancing in ExtendedResolver + (#179, Paulo Costa) + - Add method to shutdown NIO threads (#180) + - Fix restoring active position on byte buffers + (#184, @ryru) + - Add support for extended DNS errors (RFC8914, #187) + - Fix TTL for SOA record to minimum of TTL and minimum field + (#191, @amitknx) + - Add support for hosts file in lookups (#195) + +10/28/2020 + - 3.3.1 released + - Fix value of getAlias in C/DNameRecord (#136) + - Fix bug with SVCB/HTTPS parsing of master file format + (PR#135, @adam-stoler) + +09/27/2020 + - 3.3.0 released + - Add support for SVCB and HTTPS records + (PR#116, @adam-stoler) + - Fix an issue with ndots in Lookup (#118) + - Support IPv4 mapped IPv6 address in AAAA record + (PR#120, @spwei) + - Validate range in Type + - Improve DOH Resolver (#123, #127) + Note that this resolver is more a proof of concept and not + production ready. See Javadoc and issue #123. + +07/11/2020 + - 3.2.2 released + - Fix JNA access violation in WindowsResolverConfigProvider + on 32bit JVMs + +06/22/2020 + - 3.2.1 released + - Include sources and Javadoc + +06/22/2020 + - 3.2.0 released + - Add Javadoc @since tags for APIs introduced since 3.0 + - Fix requiring JNA in certain classloaders (#112) + - Add property to skip initializing builtin resolver config (#112) + - Make ResolverConfig and Resolver API public (#111) + - Add properties for a fallback resolver config provider (#111) + - Close UDP socket on failures (#110) + - Refactor TSIG code and add trace logging (#109) + +05/15/2020 + - 3.1.0 released + - Fix order of OPT and TSIG records in messages (#108) + - Fix RRset.cycle() short overflows (#102) + - Fix race condition in resolver I/O (#104) + - Add support for custom record types + (#94, Klaus Malorny ) + +03/19/2020 + - 3.0.2 released + - Only select for tcp write when there is something to write + (PR#96, Rouzbeh Delavari) + +02/23/2020 + - 3.0.1 released + - Fix toString of RRset reporting empty when it contains signatures + - Fix a potential sorting bug when creating a DNSSEC digest + - Fix getting the resolvers of an ExtendedResolver (#92) + +02/15/2020 + - 3.0.0 released + - Parse RRsig records with epoch time format + - Add support for EdDSA DNSSEC signatures if BouncyCastle is available + (Ed25519 and Ed448, RFC 8080) + - Add missing RCode, OpCode and RR type mnemonics + +01/19/2020: + - 3.0.0-next.1 released + - Requires Java 8 and slf4j-api + - Adds support for Java 9+ and Android O+ via a new server config + lookup system (#6, #9) + - Resolving is now fully asynchronous, no new thread per query anymore + - Message provides information about the resolver that produced it (#41) + - Add support for Host Identity Protocol (HIP) records (RFC 8005, #47) + - Adds a DNS over HTTP (DoH) resolver (#66) + - Fixes some issues with the OSGi manifest (#70) + - Add support for the RES_OPTIONS environment variable (#57) + - Add support for relative $INCLUDE paths in master files (#75) + - Add support for custom DNS server port in config properties (#80) + - Adds new EDNS(0) options + - See the README for hints on migrating from v2.1.x to v3 + 05/25/2019: - 2.1.9 released - Fix getRRsetType for empty RRSIG records. @@ -301,7 +529,7 @@ - The TSIG verification routines (TSIG.verify, TSIG.StreamVerifier.verify() now update the Message object with the status of the verification in addition to returning the status. - + 6/03/2009 - The lists of servers and searchlist entries in ResolverConfig should not be static. @@ -536,7 +764,7 @@ 5/7/2005 - Fix several problems with empty names. - (Matt Rutherford ) + (Matt Rutherford ) 4/23/2005 - As per RFC 2181, the maximum allowed TTL value is 0x7FFFFFFF. @@ -960,7 +1188,7 @@ - Converting some types of records (TXT, for example) to wire format could throw an IndexOutOfBoundsException. - TSIG signed UDP queries weren't properly verified by jnamed. - - Add a method to render a Message with a specified maximum size - + - Add a method to render a Message with a specified maximum size - this method will properly truncate large responses and apply TSIG signatures. @@ -1026,7 +1254,7 @@ 10/6/2002 - Fix minor bugs in Name code (Bob Halley ) - + 10/1/2002 - Memory usage and speed improvements to the TypeMap class. @@ -1259,7 +1487,7 @@ (Christopher Fitch ) - Added a routine to build a SIG record based on the results of a DSA signature (Pasi Eronen ) - + 8/13/2000 - Added 'clear' command to update client - Removed some deprecated code @@ -1529,21 +1757,21 @@ 5/13/1999 - split WorkerThread into WorkerThread and ResolveThread -4/25/1999 +4/25/1999 - moved files to org.xbill.DNS - Cache round-robins RRsets before handing them out - changed the way ExtendedResolver decides when to send queries - various reflection changes -4/21/1999 +4/21/1999 - minor WorkerThread fixes -4/19/1999 +4/19/1999 - 0.9.1 released - WorkerThreads should die after 15 minutes of idle time - Address.getByName/getAllByName handle dotted quad IP addresses -4/18/1999 +4/18/1999 - 0.9 released - Finished javadoc-ing classes in DNS.* - Server should work now diff --git a/EXAMPLES.md b/EXAMPLES.md index 629411ce8..fd4cc7a38 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,6 +1,6 @@ -# dnsjava examples +# dnsjava Examples -All of these examples are code fragments. Code using these fragments should +All of these examples are code fragments. Code using these fragments should check exceptions when appropriate, and should: ```java @@ -13,33 +13,78 @@ import org.xbill.DNS.*; InetAddress addr = Address.getByName("www.dnsjava.org"); ``` -## Get the MX target and preference of a name +## Get the MX target and preference of a name (modern) +```java +LookupSession s = LookupSession.defaultBuilder().build(); +Name mxLookup = Name.fromString("gmail.com."); +s.lookupAsync(mxLookup, Type.MX) + .whenComplete( + (answers, ex) -> { + if (ex == null) { + if (answers.getRecords().isEmpty()) { + System.out.println(mxLookup + " has no MX"); + } else { + for (Record rec : answers.getRecords()) { + MXRecord mx = ((MXRecord) rec); + System.out.println( + "Host " + mx.getTarget() + " has preference " + mx.getPriority()); + } + } + } else { + ex.printStackTrace(); + } + }) + .toCompletableFuture() + .get(); +``` + +## Get the MX target and preference of a name (legacy) ```java -Record [] records = new Lookup("gmail.com", Type.MX).run(); -for (int i = 0; i < records.length; i++) { +Record[] records = new Lookup("gmail.com", Type.MX).run(); +for (int i = 0; i < records.length; i++) { MXRecord mx = (MXRecord) records[i]; System.out.println("Host " + mx.getTarget() + " has preference " + mx.getPriority()); } ``` +## Simple lookup with a Resolver +```java +Record queryRecord = Record.newRecord(Name.fromString("dnsjava.org."), Type.A, DClass.IN); +Message queryMessage = Message.newQuery(queryRecord); +Resolver r = new SimpleResolver("8.8.8.8"); +r.sendAsync(queryMessage) + .whenComplete( + (answer, ex) -> { + if (ex == null) { + System.out.println(answer); + } else { + ex.printStackTrace(); + } + }) + .toCompletableFuture() + .get(); +``` + ## Query a remote name server for its version ```java Lookup l = new Lookup("version.bind.", Type.TXT, DClass.CH); l.setResolver(new SimpleResolver(args[0])); l.run(); -if (l.getResult() == Lookup.SUCCESSFUL) +if (l.getResult() == Lookup.SUCCESSFUL) { System.out.println(l.getAnswers()[0].rdataToString()); +} ``` ## Transfer a zone from a server and print it ```java -ZoneTransferIn xfr = ZoneTransferIn.newAXFR(new Name("."), "192.5.5.241", null); -List records = xfr.run(); -for (Iterator it = records.iterator(); it.hasNext(); ) - System.out.println(it.next()); +ZoneTransferIn xfr = ZoneTransferIn.newAXFR(Name.root, "192.5.5.241", null); +xfr.run(); +for (Record r : xfr.getAXFR()) { + System.out.println(r); +} ``` ## Use DNS dynamic update to set the address of a host to a value specified on the command line @@ -73,6 +118,75 @@ System.out.println(n2.equals(n)); // True // www // dnsjava // org -for (int i = 0; i < n.labels(); i++) +for (int i = 0; i < n.labels(); i++) { System.out.println(n.getLabelString(i)); +} +``` + +## DNSSEC Resolver + +```java +import java.io.*; + +import java.nio.charset.StandardCharsets; +import org.xbill.DNS.*; + +public class ResolveExample { + // Root anchors, see https://data.iana.org/root-anchors/root-anchors.xml + static String ROOT = + ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n" + + ". IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16"; + + public static void main(String[] args) throws Exception { + // Send two sample queries using a standard resolver + SimpleResolver sr = new SimpleResolver("4.2.2.1"); + System.out.println("Standard resolver:"); + sendAndPrint(sr, "www.dnssec-failed.org."); + sendAndPrint(sr, "nic.ch."); + + // Send the same queries using the validating resolver with the + // trust anchor of the root zone + // https://data.iana.org/root-anchors/root-anchors.xml + ValidatingResolver vr = new ValidatingResolver(sr); + vr.loadTrustAnchors(new ByteArrayInputStream(ROOT.getBytes(StandardCharsets.US_ASCII))); + System.out.println("\n\nValidating resolver:"); + sendAndPrint(vr, "www.dnssec-failed.org."); + sendAndPrint(vr, "nic.ch."); + } + + private static void sendAndPrint(Resolver vr, String name) throws IOException { + System.out.println("\n---" + name); + Record qr = Record.newRecord(Name.fromConstantString(name), Type.A, DClass.IN); + Message response = vr.send(Message.newQuery(qr)); + System.out.println("AD-Flag: " + response.getHeader().getFlag(Flags.AD)); + System.out.println("RCode: " + Rcode.string(response.getRcode())); + for (RRset set : response.getSectionRRsets(Section.ADDITIONAL)) { + if (set.getName().equals(Name.root) && set.getType() == Type.TXT + && set.getDClass() == ValidatingResolver.VALIDATION_REASON_QCLASS) { + System.out.println("Reason: " + ((TXTRecord) set.first()).getStrings().get(0)); + } + } + } +} + +``` + +This should result in an output like +``` +Standard resolver: +---www.dnssec-failed.org. +AD-Flag: false +RCode: NOERROR +---nic.ch. +AD-Flag: false +RCode: NOERROR + +Validating resolver: +---www.dnssec-failed.org. +AD-Flag: false +RCode: SERVFAIL +Reason: Could not establish a chain of trust to keys for [dnssec-failed.org.]. Reason: No keys for dnssec-failed.org. have a DS for alg RSASHA1 +---nic.ch. +AD-Flag: true +RCode: NOERROR ``` diff --git a/LICENSE b/LICENSE index 70bae6b99..60bca5d90 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,30 @@ -Copyright (c) 1998-2011, Brian Wellington. +Copyright (c) 1998-2019, Brian Wellington +Copyright (c) 2005 VeriSign. All rights reserved. +Copyright (c) 2019-2023, dnsjava authors + All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.adoc b/README.adoc new file mode 100644 index 000000000..8f70f19be --- /dev/null +++ b/README.adoc @@ -0,0 +1,408 @@ += dnsjava + +image:https://github.com/dnsjava/dnsjava/actions/workflows/build.yml/badge.svg["GitHub CI Build Status",link="https://github.com/dnsjava/dnsjava/actions/workflows/build.yml"] +image:https://codecov.io/gh/dnsjava/dnsjava/branch/master/graph/badge.svg?token=FKmcwl1Oys["codecov",link="https://codecov.io/gh/dnsjava/dnsjava"] +image:https://img.shields.io/maven-central/v/dnsjava/dnsjava["Maven Central",link="https://central.sonatype.com/artifact/dnsjava/dnsjava"] +image:https://javadoc.io/badge/dnsjava/dnsjava.svg["Javadocs",link="https://javadoc.io/doc/dnsjava/dnsjava"] + +== Overview + +dnsjava is an implementation of DNS in Java. +It + +* supports almost all defined record types (including the DNSSEC types), and unknown types. +* can be used for queries, zone transfers, and dynamic updates. +* includes a cache which can be used by clients, and an authoritative only server. +* supports TSIG authenticated messages, DNSSEC verification, and EDNS0. +* is fully thread safe. + +== Getting started + +Have a look at the basic link:EXAMPLES.md[examples]. + +=== Config options + +Some settings of dnsjava can be configured via Java +https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html[system properties]: + +[cols=4*] +|=== +.2+h|Property +3+h|Explanation +h|Type +h|Default +h|Example + +.2+|dns[.fallback].server +3+|DNS server(s) to use for resolving. +Comma separated list. +Can be IPv4/IPv6 addresses or hostnames (which are resolved using Java's built in DNS support). +|String +|- +|8.8.8.8,[2001:4860:4860::8888]:853,dns.google + +.2+|dns[.fallback].search +3+|Comma separated list of DNS search paths. +|String +|- +|ds.example.com,example.com + +.2+|dns[.fallback].ndots +3+|Sets a threshold for the number of dots which must appear in a name given to resolve before an initial absolute query will be made. +|Integer +|1 +|2 + +.2+|dnsjava.options +3+|Comma separated key-value pairs, see <<_optionpairs>>. +|option list +|- +|BINDTTL,tsigfudge=1 + +.2+|dnsjava.configprovider.skipinit +3+|Set to true to disable static ResolverConfig initialization. +|Boolean +|false +|true + +.2+|dnsjava.configprovider.sunjvm.enabled +3+|Set to true to enable the reflection based DNS server lookup, see <<_limitations>>. +|Boolean +|false +|true + +.2+|dnsjava.udp.ephemeral.start +3+|First ephemeral port for UDP-based DNS queries. +|Integer +|49152 (Linux: 32768) +|50000 + +.2+|dnsjava.udp.ephemeral.end +3+|Last ephemeral port for UDP-based DNS queries. +|Integer +|65535 (Linux: 60999) +|60000 + +.2+|dnsjava.udp.ephemeral.use_ephemeral_port +3+|Use an OS-assigned ephemeral port for UDP queries. +Enabling this option is *insecure*! +Do NOT use it. +|Boolean +|false +|true + +.2+|dnsjava.lookup.max_iterations +3+|Maximum number of CNAMEs to follow in a chain. +|Integer +|16 +|20 + +.2+|dnsjava.lookup.use_hosts_file +3+|Use the system's hosts file for lookups before resorting to a resolver. +|Boolean +|true +|false + +.2+|dnsjava.hostsfile.max_size_bytes +3+|Set the size of the hosts file to be loaded at a time, in bytes. +|Integer +|16384 +|1000000 + +.2+|dnsjava.nio.selector_timeout +3+|Set selector timeout in milliseconds. Default/Max 1000, Min 1. +|Integer +|1000 +|700 + +.2+|dnsjava.nio.register_shutdown_hook +3+|Register Shutdown Hook for automatic termination of NIO. +If disabled, the nio selector thread will not automatically clean up on JVM termination. +|Boolean +|True +|False + +.2+|dnsjava.harden_unknown_additional +3+|Harden against unknown records in the authority section and additional section. +If disabled, such records are copied from the upstream and presented to the client together with the answer. +|Boolean +|True +|False + +4+h|DNSSEC Options +.2+|dnsjava.dnssec.keycache.max_ttl +3+|Maximum time-to-live (TTL) of entries in the key cache in seconds. +|Integer +|900 +|1800 + +.2+|dnsjava.dnssec.keycache.max_size +3+|Maximum number of entries in the key cache. +|Integer +|1000 +|5000 + +.2+|dnsjava.dnssec.nsec3.iterations.N +3+a|Maximum iteration count for the NSEC3 hashing function depending on the key size N. The defaults are from https://datatracker.ietf.org/doc/html/rfc5155#section-10.3[RFC5155]. +|Integer +2+a|- 1024 bit keys: 150 iterations +- 2048 bit keys: 500 iterations +- 4096 bit keys: 2500 iterations + +e.g. dnsjava.dnssec.nsec3.iterations.1024=200 + +.2+|dnsjava.dnssec.trust_anchor_file +3+|The file from which the trust anchor should be loaded. +The file must be formatted like a DNS zone master file. +It can only contain DS or DNSKEY records. +|String +|- +|/etc/dnssec-root-anchors + +.2+|dnsjava.dnssec.digest_preference +3+|Defines the preferred DS record digest algorithm if a zone has registered multiple DS records. +The list is comma-separated, the highest preference first. + +If this property is not specified, the DS record with the highest +https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml[digest ID] is chosen. +To stay compliant with the RFCs, the mandatory digest IDs must be listed in this property. + +The GOST digest requires https://www.bouncycastle.org/java.html[BouncyCastle] on the classpath. +|String +|- +|2,1,4 + +.2+|dnsjava.dnssec.harden_algo_downgrade +3+|Prevent algorithm downgrade when multiple algorithms are advertised in a zone's DS records. +If `false`, allows any algorithm to validate the zone. +|Boolean +|true +|false + +.2+|dnsjava.dnssec.max_validate_rrsigs +3+|Maximum number of RRSig records to validate until the response is considered bogus. +This is limited to avoid the 'KeyTrap' vulnerability (CVE-2023-50387). +|Integer +|8 +|4 + +.2+|dnsjava.dnssec.max_ds_match_failures +3+|Maximum number of DS records to validate until the response is considered bogus. +This is limited to avoid the 'KeyTrap' vulnerability (CVE-2023-50387). +|Integer +|4 +|2 + +.2+|dnsjava.dnssec.algorithm.ID +3+|Enable or disable a DS/DNSKEY algorithm. +See +https://datatracker.ietf.org/doc/html/rfc8624#section-3.1[RFC8624] for recommended values. +Note that algorithm number 1, `RSAMD5`, is disabled and cannot be enabled with this property. +|Boolean +2+|Disable ED448: +`dnsjava.dnssec.algorithm.16=false` + +.2+|dnsjava.dnssec.algorithm_rsa_min_key_size +3+|Set the minimum size, in bits, for RSA keys. +|Integer +|1024 +|512 + +.2+|dnsjava.dnssec.digest.ID +3+|Enable or disable a DS record digest algorithm. +See +https://datatracker.ietf.org/doc/html/rfc8624#section-3.3[RFC8624] for recommended values. +|Boolean +2+|Disable SHA.1: +`dnsjava.dnssec.digest.1=false` + +|=== + +[#_optionpairs] +==== dnsjava.options pairs + +The `dnsjava.options` configuration options can also be set programmatically through the `Options` class. +Please refer to the Javadoc for details. + +[cols="1,1,1,4",options=header] +|=== +| Key | Type | Default | Explanation +| `BINDTTL` | Boolean | false | Print TTLs in BIND format +| `multiline` | Boolean | false | Print records in multiline format +| `noPrintIN` | Boolean | false | Do not print the class of a record if it is `IN` +| `tsigfudge` | Integer | 300 | Sets the default TSIG fudge value (in seconds) +| `sig0validity` | Integer | 300 | Sets the default SIG(0) validity period (in seconds) +|=== + +=== Resolvers + +==== SimpleResolver + +Basic resolver that uses UDP by default and falls back to TCP if required. + +==== ExtendedResolver + +A `Resolver` that uses multiple ``Resolver``s to send the queries, defaulting to ``SimpleResolver``s. +Can be configured to query the servers in a round-robin order. +Blacklists a server if it times out. + +==== DohResolver + +Proof-of-concept DNS over HTTP resolver, e.g. to use https://dns.google/query. + +==== ValidatingResolver + +DNSSEC validating stub resolver. +Originally based on the work of the Unbound Java prototype from 2005/2006. +The Unbound prototype was stripped from all unnecessary parts, heavily modified, complemented with more than 300 unit test and found bugs were fixed. +Before the import into dnsjava, the resolver was developed as an independent library at https://github.com/ibauersachs/dnssecjava. +To migrate from dnssecjava, replace `org.jitsi` with `org.xbill.DNS` in Java packages and `org.jitsi` with `dnsjava` in property prefixes. + +Validated, secure responses contain the DNS `AD`-flag, while responses that failed validation return the `SERVFAIL`-RCode. +Insecure responses return the actual return code without the `AD`-flag set. +The reason why the validation failed or is insecure is provided as a localized string in the additional section under the record ./65280/TXT (a TXT record for the owner name of the root zone in the private query class `ValidatingResolver.VALIDATION_REASON_QCLASS`). +The Extended DNS Errors (EDE, https://datatracker.ietf.org/doc/html/rfc8914[RFC8914]) also provides the failure reason, although in less detail. + +The link:EXAMPLES.md[examples] contain a small demo. + +[IMPORTANT] +.Do not use the `ValidatingResolver` standalone. +A response will need CNAME/DNAME post-processing, and DNS messages can still be manipulated with DNSSEC alone. +Subsequent processing and validation of messages is intricate and best done using the built-in `LookupSession` (or the legacy `Lookup`) class. + +=== Migrating from version 2.1.x to v3 + +dnsjava v3 has significant API changes compared to version 2.1.x and is neither source nor binary compatible. +The most important changes are: + +* Requires at least Java 8 + +* Uses https://www.slf4j.org/[slf4j] for logging and thus needs `slf4j-api` +on the classpath + +* The link:USAGE.md[command line tools] were moved to the `org.xbill.DNS.tools` +package + +* On Windows, https://github.com/java-native-access/jna[JNA] should be on the classpath for the search path and proper DNS server finding. +As of Java 24, note that additional JVM config is required, see https://javadoc.io/doc/net.java.dev.jna/jna/latest/index.html#special-considerations-for-jdk24--heading[Special considerations for JDK24+]. + +* The `Resolver` API for custom resolvers has changed to use +`CompletionStage` for asynchronous resolving. +The built-in resolvers are now fully non-blocking and do not start a thread per query anymore. + +* Many methods return a `List` instead of an array. +Ideally, use a for-each loop. +If this is not possible, call `size()` instead of using `length`: +** Cache#findAnyRecords +** Cache#findRecords +** Lookup#getDefaultSearchPath +** Message#getSectionRRsets +** SetResponse#answers +** ResolverConfig + +* RRset returns a List instead of an `Iterator`. +Ideally, modify your code to use a for-each loop. +If this is not possible, create an iterator on the returned list: +** RRset#rrs +** RRset#sigs + +* Methods using `java.util.Date` are deprecated. +Use the new versions with +`java.time.Instant` or `java.time.Duration` instead + +* The type hierarchy of `SMIMEARecord` changed, it now inherits from +`TLSARecord` and constants are shared + +* ``Record``s are no longer marked as `Serializable` after 3.0. +While 3.5 reintroduced `Serializable`, it is preferred to use the RFC defined serialization formats directly: +** `toString()`, `rrToString()` ↔ `fromString()` +** `toWire()` ↔ `fromWire()`, `newRecord()` + +* `Message` and `Header` properly support `clone()` + +=== Replacing the standard Java DNS functionality + +==== Java 1.4 to 8 + +Java versions from 1.4 to 8 can load DNS service providers at runtime. +To load the dnsjava service provider, build dnsjava on JDK 8 and set the system property: + + sun.net.spi.nameservice.provider.1=dns,dnsjava + +This instructs the JVM to use the dnsjava service provide for DNS at the highest priority. + +==== Java 9 to 17 + +The functionality to load a DNS SPI was https://bugs.openjdk.java.net/browse/JDK-8134577[removed in JDK 9] and a replacement API was https://bugs.openjdk.java.net/browse/JDK-8192780[requested]. + +==== Java 18+ + +https://bugs.openjdk.java.net/browse/JDK-8263693[JEP 418: Internet-Address Resolution SPI] reintroduces a DNS SPI. +See https://github.com/dnsjava/dnsjava/issues/245[#245] for the support status in dnsjava. + +=== Build + +dnsjava uses https://maven.apache.org/[Maven] as the build system. +Run `mvn package` from the toplevel directory to build dnsjava. +JDK 8 or higher is required. + +=== Testing dnsjava + +mailto:rutherfo@cs.colorado.edu[Matt Rutherford] contributed a number of unit tests, which are in the tests subdirectory. + +The hierarchy under tests mirrors the `org.xbill.DNS` classes. +To run the unit tests, execute `mvn test`. + +[#_limitations] +== Limitations + +There is no standard way to determine what the local nameserver or DNS search path is at runtime from within the JVM. +dnsjava attempts several methods until one succeeds. + +- The properties `dns.server` and `dns.search` (comma delimited lists) are checked. +The servers can either be IP addresses or hostnames (which are resolved using Java's built in DNS support). +- On Unix/Solaris, `/etc/resolv.conf` is parsed. +- On Windows, if https://github.com/java-native-access/jna[JNA] is available on the classpath, the `GetAdaptersAddresses` API is used. +- On Android the `ConnectivityManager` is used (requires initialization using `org.xbill.DNS.config.AndroidResolverConfigProvider.setContext`). +- The `sun.net.dns.ResolverConfiguration` class is queried if enabled. +As of Java 16 the JVM flag `--add-opens java.base/sun.net.dns=ALL-UNNAMED` (classpath) or `--add-opens java.base/sun.net.dns=org.dnsjava` (modules) is also required. +- If available and no servers have been found yet, https://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html[JNDI-DNS] is used. +- If still no servers have been found yet, use the fallback properties. +This can be used to query e.g. a well-known public DNS server instead of localhost. +- As a last resort, `localhost` is used as the nameserver, and the search path is empty. + +== Additional documentation + +Javadoc documentation can be built with `mvn javadoc:javadoc` or viewed online at https://javadoc.io/doc/dnsjava/dnsjava[javadoc.io]. +See the link:EXAMPLES.md[examples] for some basic usage information. + +== License + +dnsjava is placed under the link:LICENSE[BSD-3-Clause license]. + +== History + +dnsjava was started as an excuse to learn Java. +It was useful for testing new features in BIND without rewriting the C resolver. +It was then cleaned up and extended in order to be used as a testing framework for DNS interoperability testing. +The high level API and caching resolver were added to make it useful to a wider audience. +The authoritative only server was added as proof of concept. + +=== dnsjava on GitHub + +This repository has been a mirror of the dnsjava project at Sourceforge since 2014 to maintain the Maven build for publishing to https://search.maven.org/artifact/dnsjava/dnsjava[Maven Central]. +As of 2019-05-15, GitHub is https://sourceforge.net/p/dnsjava/mailman/message/36666800/[officially] the new home of dnsjava. +The mailto:dnsjava-users@lists.sourceforge.net[dnsjava-users] mailing list (https://sourceforge.net/p/dnsjava/mailman/dnsjava-users/[archive]) still exists but is mostly inactive. + +Please use the GitHub https://github.com/dnsjava/dnsjava/issues[issue tracker] and send - well tested - pull requests. + +== Authors + +- Brian Wellington (@bwelling), March 12, 2004 +- Various contributors, see the link:Changelog[Changelog] +- Ingo Bauersachs (@ibauersachs), current maintainer + +== Final notes + +- Thanks to Network Associates, Inc. for sponsoring some of the original dnsjava work in 1999-2000. +- Thanks to Nominum, Inc. for sponsoring some work on dnsjava from 2000 through 2017. diff --git a/README.md b/README.md deleted file mode 100644 index aa55b2dab..000000000 --- a/README.md +++ /dev/null @@ -1,109 +0,0 @@ -[![Build Status](https://travis-ci.org/dnsjava/dnsjava.svg?branch=master)](https://travis-ci.org/dnsjava/dnsjava) -[![Coverage Status](https://coveralls.io/repos/dnsjava/dnsjava/badge.svg)](https://coveralls.io/r/dnsjava/dnsjava) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dnsjava/dnsjava/badge.svg)](https://search.maven.org/artifact/dnsjava/dnsjava) -[![Javadocs](http://javadoc.io/badge/dnsjava/dnsjava.svg)](http://javadoc.io/doc/dnsjava/dnsjava) - -# dnsjava - -## Overview - -dnsjava is an implementation of DNS in Java. It supports all defined record -types (including the DNSSEC types), and unknown types. It can be used for -queries, zone transfers, and dynamic updates. It includes a cache which can be -used by clients, and an authoritative only server. It supports TSIG -authenticated messages, partial DNSSEC verification, and EDNS0. It is fully -thread safe. It can be used to replace the native DNS support in Java. - -dnsjava was started as an excuse to learn Java. It was useful for testing new -features in BIND without rewriting the C resolver. It was then cleaned up and -extended in order to be used as a testing framework for DNS interoperability -testing. The high level API and caching resolver were added to make it useful -to a wider audience. The authoritative only server was added as proof of -concept. - -## dnsjava on Github - -This repository has been a mirror of the dnsjava project at Sourceforge -since 2014 to maintain the Maven build for publishing to -[Maven Central](https://search.maven.org/artifact/dnsjava/dnsjava). -As of 2019-05-15, Github is -[officially](https://sourceforge.net/p/dnsjava/mailman/message/36666800/) -the new home of dnsjava. - -Please use the Github [issue tracker](https://github.com/dnsjava/dnsjava/issues) and send - well tested - pull -requests. The -[dnsjava-users@lists.sourceforge.net](mailto:dnsjava-users@lists.sourceforge.net) -mailing list still exists. - -## Author - -- Brian Wellington (@bwelling), March 12, 2004 -- Various contributors, see [Changelog](Changelog) - -## Getting started - -Run `mvn package` from the toplevel directory to build dnsjava. JDK 1.4 -or higher is required. - -### Replacing the standard Java DNS functionality: - -Java versions from 1.4 to 1.8 can load DNS service providers at runtime. The -functionality was [removed in JDK 9](https://bugs.openjdk.java.net/browse/JDK-8134577), -a replacement is [requested](https://bugs.openjdk.java.net/browse/JDK-8192780), -but so far has not been implemented. - -To load the dnsjava service provider, build dnsjava on a JDK that still -supports the SPI and set the system property: - - sun.net.spi.nameservice.provider.1=dns,dnsjava - -This instructs the JVM to use the dnsjava service provide for DNS at the -highest priority. - - -## Testing dnsjava - -[Matt Rutherford](mailto:rutherfo@cs.colorado.edu) contributed a number of unit -tests, which are in the tests subdirectory. The hierarchy under tests -mirrors the org.xbill.DNS classes. To run the unit tests, execute -`mvn test`. The tests require JUnit. - -Some high-level test programs are in `org/xbill/DNS/tests`. - - -## Limitations - -There's no standard way to determine what the local nameserver or DNS search -path is at runtime from within the JVM. dnsjava attempts several methods -until one succeeds. - -- The properties `dns.server` and `dns.search` (comma delimited lists) are - checked. The servers can either be IP addresses or hostnames (which are - resolved using Java's built in DNS support). -- The `sun.net.dns.ResolverConfiguration` class is queried. -- On Unix, `/etc/resolv.conf` is parsed. -- On Windows, `ipconfig`/`winipcfg` is called and its output parsed. This may - fail for non-English versions on Windows. -- As a last resort, `localhost` is used as the nameserver, and the search - path is empty. - -The underlying platform must use an ASCII encoding of characters. This means -that dnsjava will not work on OS/390, for example. - - -## Additional documentation - -Javadoc documentation can be built with `mvn javadoc:javadoc` or viewed online -at [javadoc.io](http://javadoc.io/doc/dnsjava/dnsjava). See the -[examples](EXAMPLES.md) for some basic usage information. - - -## License - -dnsjava is placed under the [BSD license](LICENSE). Several files are also under -additional licenses; see the individual files for details. - -## Final notes -- Thanks to Network Associates, Inc. for sponsoring some of the original - dnsjava work in 1999-2000. -- Thanks to Nominum, Inc. for sponsoring some work on dnsjava from 2000 through 2017. diff --git a/TODO.dnssec.md b/TODO.dnssec.md new file mode 100644 index 000000000..7f0e73a01 --- /dev/null +++ b/TODO.dnssec.md @@ -0,0 +1,69 @@ +CNAME Handling +-------------- +The CNAME handling is terribly inefficient. A recursive nameserver is required +to deliver all intermediate results in the response to the original query. The +code however still splits up the query into each part and performs a query for +each CNAME till the end of the chain is reached. +This should be changed to follow the chain in the response of the original +query, but is not so easy because the validation only has the keys for each +original query. +A possible workaround would be to synthesize the intermediate responses from +the original query. Easy for positive responses, but for NXDOMAIN - which +NSEC(3)s are to be included...? + +DNAME Handling +-------------- +A DNAME causes validation failures during priming because the synthesized +CNAME is not considered valid. Some unit-tests are failing due to this. + +API +--- +- Provide the final failure reason as a (localizable) string + +Code Coverage / Bugs +-------------------- +- The code still has some untested parts: + - Wildcard/ENT DS delegations!!! + - ANY responses, especially wildcard expansion + - Insecure NSEC3 NODATA responses + - Wildcard NODATA responses might pass too broad cases + - Behavior if all NSEC3s are not understandable + - NXDOMAIN when a NSEC would prove that a wildcard exists + - Exceptions thrown by the head resolver + - Bogus/Insecure handling of CNAME answer to DS query + - Async calling of the validator + - Passthrough without validation if the CD flag is set + - Various cases in dsReponseToKeForNodata + - longestCommonName + - Various NSEC NODATA cases + - Unsupported algorithm or digest ID cases + - NSEC3 iteration count configuration + - NSEC3 with unsupported hash algorithm + - Multiple NSEC3s for a zone + - NSEC3: proveClosestEncloser + - NSEC3: proveNodata + - NSEC3: proveNoDS + - Implement http://tools.ietf.org/html/rfc4509#section-3 to prevent downgrade attacks + - http://tools.ietf.org/html/rfc6840#section-4.3 (CNAME bit check) + - http://tools.ietf.org/html/rfc6840#section-4.4 (Insecure Delegation Proofs) + - http://tools.ietf.org/html/rfc6840#section-5.4 (Caution about Local Policy and Multiple RRSIGs) + - Refuse DNAME wildcards (RFC4597) + - Test validating against a non-Bind9 head solver + - Rate limit queries to be able to validate against Google's public resolvers + +Unit Tests +---------- +- The tests currently rely on an online connection to a recursive server and + external zones. They must be able to run offline. +- Some tests will start to fail after June 9, 2013 because the signature date + is compared against the current system time. This must be changed to take + the test authoring time. To make this possible DNSJAVA must probably be + changed. + +DNSJAVA +------- +- Fix the Maven project definition to build correctly with a local lib folder + as it is not officially distributed on Maven central +- Version 2.1.5 contains a bug in the Name constructor and needs at least + SVN rev. 1686 +- Remove local-repo once 2.1.6 appears on Maven central diff --git a/USAGE b/USAGE deleted file mode 100644 index cb74210f9..000000000 --- a/USAGE +++ /dev/null @@ -1,60 +0,0 @@ -dnsjava v2.0 - -dnsjava provides several command line programs, which are documented here. -For examples of API usage, see examples.html. - -- dig: - A clone of dig (as distributed with BIND) - dig @server [-x] name type [class] [-p port] [-k name/secret] [-t] \ - [-i] [-e n] [-d] - -x : reverse lookup, name must be a dotted quad - -k : use TSIG transaction security - -t : use TCP by default - -i : ignore truncation errors - -e n: Use EDNS level n (only 0 is defined) - -d : Set the DNSSEC OK bit - -- update: - A dynamic update client with some extra functionality. This can be - used either interactively or by specifying a file containing commands - to be executed. Running 'help' lists all other commands. - update [file] - - -- jnamed: - A basic authoritative only (non-caching, non-recursive) server. It's - not very good, but it's also a whole lot better than it used to be. - - The config file (jnamed.conf by default) supports the following - directives: - primary - secondary - cache - key [algorithm] - address - port - - If no addresses are specified, jnamed will listen on all addresses, - using a wildcard socket. If no ports are specified, jnamed will - listen on port 53. - - The following is an example: - primary internal /etc/namedb/internal.db - secondary xbill.org 127.0.0.1 - cache /etc/namedb/cache.db - key xbill.org 1234 - address 127.0.0.1 - port 12345 - - To run: - jnamed [config_file] - - jnamed should not be used for production, and should probably - not be used for testing. If the above documentation is not enough, - please do not ask for more, because it really should not be used. - -- lookup: - A simple program that looks up records associated with names. - If no type is specified, address lookups are done. - - lookup [-t type] name ... diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 000000000..18729d1c0 --- /dev/null +++ b/USAGE.md @@ -0,0 +1,73 @@ +dnsjava Command Line Tools +========================== + +dnsjava provides several command line programs, which are documented here. +For examples of API usage, see the [examples](EXAMPLES.md). To run them, +at least `dnsjava` and `slf4j-api` need to be on the classpath. A basic +invocation could thus look as follows: + + java -cp dnsjava.jar;slf4-api.jar org.xbill.DNS.tools.Tools [tool] + +dig +--- +A basic, incomplete clone of dig (as distributed with BIND) + + dig @server [-x] name type [class] [-p port] [-k name/secret] [-t] \ + [-i] [-e n] [-d] + -x : reverse lookup, name must be a dotted quad + -k : use TSIG transaction security + -t : use TCP by default + -i : ignore truncation errors + -e n: Use EDNS level n (only 0 is defined) + -d : Set the DNSSEC OK bit + +update +------ +A dynamic update client with some extra functionality. This can be +used either interactively or by specifying a file containing commands +to be executed. Running 'help' lists all other commands. + + update [file] + +jnamed +------ +A basic authoritative only (non-caching, non-recursive) server. It's +not very good, but it's also a whole lot better than it used to be. + +The config file (`jnamed.conf` by default) supports the following +directives: + + primary + secondary + cache + key [algorithm] + address + port + +If no addresses are specified, jnamed will listen on all addresses, +using a wildcard socket. If no ports are specified, jnamed will +listen on port 53. + +The following is an example: + + primary internal /etc/namedb/internal.db + secondary xbill.org 127.0.0.1 + cache /etc/namedb/cache.db + key xbill.org 1234 + address 127.0.0.1 + port 12345 + +To run: + + jnamed [config_file] + +jnamed should not be used for production, and should probably +not be used for testing. If the above documentation is not enough, +please do not ask for more, because it really should not be used. + +lookup +------ +A simple program that looks up records associated with names. +If no type is specified, address lookups (A) are done. + + lookup [-t type] name ... diff --git a/checkstyle/checkstyle-config.xml b/checkstyle/checkstyle-config.xml new file mode 100644 index 000000000..f99d54197 --- /dev/null +++ b/checkstyle/checkstyle-config.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/checkstyle/checkstyle-suppressions.xml b/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 000000000..dded17094 --- /dev/null +++ b/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/checkstyle/header.template.txt b/checkstyle/header.template.txt new file mode 100644 index 000000000..1113d25c2 --- /dev/null +++ b/checkstyle/header.template.txt @@ -0,0 +1 @@ +// SPDX-License-Identifier: BSD-3-Clause diff --git a/pom.xml b/pom.xml index 63cfa39b6..678cd3d00 100644 --- a/pom.xml +++ b/pom.xml @@ -1,28 +1,28 @@ 4.0.0 dnsjava dnsjava bundle - 3.0.0-SNAPSHOT + 3.6.5-SNAPSHOT dnsjava dnsjava is an implementation of DNS in Java. It supports all defined record types (including the DNSSEC types), and unknown types. It can be used for queries, zone transfers, and dynamic updates. It includes a cache which can be used by clients, and a minimal implementation of a server. It supports TSIG authenticated messages, partial DNSSEC verification, and EDNS0. - http://www.dnsjava.org + https://github.com/dnsjava/dnsjava dnsjava.org - http://www.dnsjava.org + https://github.com/dnsjava/dnsjava - BSD-2-Clause - https://opensource.org/licenses/BSD-2-Clause + BSD-3-Clause + https://opensource.org/licenses/BSD-3-Clause repo @@ -37,20 +37,119 @@ bwelling Brian Wellington + + ibauersachs + Ingo Bauersachs + UTF-8 - 1.8 - 5.4.2 + 8 + true + false + + + 5.14.2 + + 4.11.0 + 1.7.36 + 1.18.42 + 5.18.1 + 1.83 + + 4.5.24 + + 1.7 + 2.30.0 + + ${project.build.directory}/site/jacoco/jacoco.xml + ${project.build.directory}/delombok + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.2.0 + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.8.0 + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.9.0 + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.4 + + + + org.apache.maven.plugins + maven-install-plugin + 3.1.4 + + + + org.apache.maven.plugins + maven-release-plugin + 3.3.1 + + + + org.apache.maven.plugins + maven-resources-plugin + 3.4.0 + + + + org.apache.maven.plugins + maven-site-plugin + 3.21.0 + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.1 + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.2.1 + + + initialize + + read-project-properties + + + + + + sonar-project.properties + + + + org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.8 sign-artifacts @@ -60,72 +159,152 @@ + + + --pinentry-mode + loopback + + false + org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.14.1 - ${target.jdk} - ${target.jdk} - -Xlint:unchecked + + -Xlint:all,-serial,-processing + -Xpkginfo:always + + + + org.projectlombok + lombok + ${lombok.version} + + + org.openjdk.jcstress + jcstress-core + 0.16 + + + * + * + + + + org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.4.0 org.apache.felix maven-bundle-plugin - 4.2.0 + + 5.1.9 true + true + lombok - dnsjava is an implementation of DNS in Java + dnsjava + dnsjava is an implementation of DNS in Java org.xbill.dns - org.dnsjava + https://javadoc.io/doc/dnsjava/dnsjava + <_noclassforname>true + <_donotcopy>android|sun + <_nouses>true + <_noee>true org.xbill.DNS.* + !android.*,!sun.*,. - !org.xbill.DNS*,!sun.*,android.os;resolution:=optional,* + !org.xbill.DNS*, + !sun.*, + !lombok, + !android.*, + javax.naming.*;resolution:=optional, + org.slf4j;version="[1.7,3)", + com.sun.jna.*;resolution:=optional;version="[5,6)", + * - JavaSE-1.8 - BSD-2-Clause;link="https://raw.githubusercontent.com/dnsjava/dnsjava/master/LICENSE" + BSD-3-Clause;link="https://raw.githubusercontent.com/dnsjava/dnsjava/master/LICENSE" - <_removeheaders>Bnd-*, Tool, Require-Capability, Include-Resource + <_removeheaders>Bnd-*, Tool, Require-Capability, Include-Resource, Private-Package + <_snapshot>SNAPSHOT {maven-resources}, META-INF/LICENSE=LICENSE org.xbill.DNS.tools.Tools + <_fixupmessages>"Classes found in the wrong directory";is:=ignore + true + + org.projectlombok + lombok-maven-plugin + 1.18.20.0 + + ${project.build.sourceDirectory} + false + ${delombok.output} + + + + generate-sources + + delombok + + + + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.12.0 + ${target.jdk} + ${delombok.output} true dnsjava documentation