dnsruby 1.73.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +16 -4
- data/DNSSEC +3 -2
- data/EXAMPLES +1 -1
- data/README.md +370 -64
- data/RELEASE_NOTES.md +314 -1
- data/Rakefile +0 -3
- data/demo/axfr.rb +24 -6
- data/dnsruby.gemspec +6 -4
- data/lib/dnsruby/cache.rb +279 -49
- data/lib/dnsruby/code_mapper.rb +35 -8
- data/lib/dnsruby/code_mappers.rb +63 -49
- data/lib/dnsruby/config.rb +77 -4
- data/lib/dnsruby/deadline_queue.rb +75 -0
- data/lib/dnsruby/dnssec.rb +148 -45
- data/lib/dnsruby/ipv6.rb +45 -5
- data/lib/dnsruby/key_cache.rb +202 -35
- data/lib/dnsruby/message/decoder.rb +85 -19
- data/lib/dnsruby/message/encoder.rb +27 -16
- data/lib/dnsruby/message/header.rb +8 -12
- data/lib/dnsruby/message/message.rb +53 -22
- data/lib/dnsruby/message/section.rb +12 -34
- data/lib/dnsruby/name.rb +5 -2
- data/lib/dnsruby/packet_sender.rb +664 -207
- data/lib/dnsruby/presentation.rb +147 -0
- data/lib/dnsruby/query_control.rb +132 -0
- data/lib/dnsruby/recursor.rb +798 -341
- data/lib/dnsruby/resolver.rb +472 -176
- data/lib/dnsruby/resource/CAA.rb +67 -16
- data/lib/dnsruby/resource/DLV.rb +6 -0
- data/lib/dnsruby/resource/DNSKEY.rb +108 -16
- data/lib/dnsruby/resource/DS.rb +27 -28
- data/lib/dnsruby/resource/HTTPS.rb +16 -0
- data/lib/dnsruby/resource/IN.rb +21 -2
- data/lib/dnsruby/resource/MX.rb +2 -2
- data/lib/dnsruby/resource/NSEC.rb +6 -10
- data/lib/dnsruby/resource/NSEC3.rb +55 -19
- data/lib/dnsruby/resource/NSEC3PARAM.rb +4 -6
- data/lib/dnsruby/resource/OPT.rb +2 -3
- data/lib/dnsruby/resource/RR.rb +108 -65
- data/lib/dnsruby/resource/RRSIG.rb +5 -5
- data/lib/dnsruby/resource/RRSet.rb +38 -22
- data/lib/dnsruby/resource/SRV.rb +2 -2
- data/lib/dnsruby/resource/SVCB.rb +775 -0
- data/lib/dnsruby/resource/TSIG.rb +266 -231
- data/lib/dnsruby/resource/TXT.rb +34 -42
- data/lib/dnsruby/resource/domain_name.rb +1 -1
- data/lib/dnsruby/resource/generic.rb +31 -0
- data/lib/dnsruby/resource/generic_cache.rb +384 -0
- data/lib/dnsruby/resource/generic_constants.rb +79 -0
- data/lib/dnsruby/select_thread.rb +986 -291
- data/lib/dnsruby/serial_time.rb +25 -0
- data/lib/dnsruby/single_resolver.rb +25 -6
- data/lib/dnsruby/single_verifier.rb +1620 -530
- data/lib/dnsruby/transport_clock.rb +9 -0
- data/lib/dnsruby/update.rb +8 -6
- data/lib/dnsruby/validation_budget.rb +143 -0
- data/lib/dnsruby/validator_thread.rb +139 -58
- data/lib/dnsruby/version.rb +1 -1
- data/lib/dnsruby/zone_reader.rb +196 -129
- data/lib/dnsruby/zone_transfer.rb +157 -25
- data/lib/dnsruby.rb +7 -2
- data/test/ci_watchdog.rb +33 -0
- data/test/fixtures/org-ds-2026-11-05.zone +5 -0
- data/test/fixtures/root-dnskey-2026-11-05.zone +7 -0
- data/test/fork_test_helper.rb +37 -0
- data/test/local_dns_server.rb +57 -0
- data/test/soak_server.rb +152 -0
- data/test/socket_helpers.rb +26 -0
- data/test/spec_helper.rb +8 -13
- data/test/tc_attempt_configuration.rb +137 -0
- data/test/tc_caa.rb +2 -2
- data/test/tc_cache_keys.rb +36 -0
- data/test/tc_cancelled_send_capacity.rb +158 -0
- data/test/tc_dnssec_alias_validation.rb +289 -0
- data/test/tc_dnssec_budget.rb +489 -0
- data/test/tc_dnssec_cache_bounds.rb +197 -0
- data/test/tc_dnssec_cache_lifetime.rb +615 -0
- data/test/tc_dnssec_denial_proofs.rb +285 -0
- data/test/tc_dnssec_recursion_limits.rb +186 -0
- data/test/tc_dnssec_recursor_review_fixes.rb +351 -0
- data/test/tc_dnssec_trust_anchors.rb +286 -0
- data/test/tc_dnssec_trust_races.rb +147 -0
- data/test/tc_dnssec_wildcard_proofs.rb +249 -0
- data/test/tc_ds.rb +5 -5
- data/test/tc_edns_retry_regressions.rb +255 -0
- data/test/tc_escapedchars.rb +8 -0
- data/test/tc_final_review_fixes.rb +167 -0
- data/test/tc_final_transport_contracts.rb +104 -0
- data/test/tc_generic_cache.rb +852 -0
- data/test/tc_header.rb +26 -0
- data/test/tc_ipv6.rb +120 -0
- data/test/tc_key_cache_identity.rb +382 -0
- data/test/tc_malformed_tcp.rb +712 -0
- data/test/tc_name.rb +99 -5
- data/test/tc_nameserver_bootstrap.rb +322 -0
- data/test/tc_nsec.rb +4 -45
- data/test/tc_nsec3.rb +17 -1
- data/test/tc_pipeline_retirement.rb +358 -0
- data/test/tc_pipeline_retry.rb +462 -0
- data/test/tc_pipeline_send.rb +663 -0
- data/test/tc_protocol_review_fixes.rb +95 -0
- data/test/tc_query_control.rb +90 -0
- data/test/tc_record_codec_contracts.rb +107 -0
- data/test/tc_record_regressions.rb +225 -0
- data/test/tc_recur.rb +1 -0
- data/test/tc_recursor_address_fallback.rb +169 -0
- data/test/tc_recursor_limits.rb +139 -0
- data/test/tc_release_auxiliary_fixes.rb +288 -0
- data/test/tc_release_configuration_fixes.rb +612 -0
- data/test/tc_release_dnssec_completion.rb +405 -0
- data/test/tc_release_dnssec_fixes.rb +305 -0
- data/test/tc_release_negative_cache.rb +327 -0
- data/test/tc_release_recursor_cache.rb +212 -0
- data/test/tc_release_review_transport.rb +372 -0
- data/test/tc_release_review_transport_native.rb +56 -0
- data/test/tc_release_transport.rb +906 -0
- data/test/tc_release_transport_fixes.rb +196 -0
- data/test/tc_release_zone_defaults.rb +157 -0
- data/test/tc_release_zone_ttls.rb +39 -0
- data/test/tc_res_config.rb +53 -2
- data/test/tc_res_opt.rb +21 -1
- data/test/tc_resolver.rb +34 -19
- data/test/tc_resolver_concurrency.rb +383 -0
- data/test/tc_resolver_configuration_contracts.rb +31 -0
- data/test/tc_resolver_validation.rb +211 -0
- data/test/tc_review_cache.rb +163 -0
- data/test/tc_review_code_mapper.rb +279 -0
- data/test/tc_review_control_deadlines.rb +169 -0
- data/test/tc_review_followup_transport.rb +583 -0
- data/test/tc_review_packet_contracts.rb +369 -0
- data/test/tc_review_presentation.rb +213 -0
- data/test/tc_review_record_compatibility.rb +101 -0
- data/test/tc_review_transport_fixes.rb +195 -0
- data/test/tc_review_transport_native.rb +415 -0
- data/test/tc_review_validation_priming.rb +178 -0
- data/test/tc_rr-opt.rb +5 -2
- data/test/tc_rr-unknown.rb +19 -0
- data/test/tc_rr.rb +14 -0
- data/test/tc_rrset_grouping.rb +50 -0
- data/test/tc_rrset_review_fixes.rb +58 -0
- data/test/tc_rrsig.rb +21 -1
- data/test/tc_scoped_ipv6.rb +153 -0
- data/test/tc_scoped_ipv6_select.rb +157 -0
- data/test/tc_scoped_ipv6_transport.rb +329 -0
- data/test/tc_selector_wakeup.rb +42 -0
- data/test/tc_soak.rb +118 -196
- data/test/tc_socket_helpers.rb +90 -0
- data/test/tc_supporting_lookup_limits.rb +252 -0
- data/test/tc_svcb.rb +1043 -0
- data/test/tc_svcb_signed_target.rb +145 -0
- data/test/tc_svcb_transport.rb +120 -0
- data/test/tc_tcp.rb +69 -43
- data/test/tc_tcp_pipelining.rb +95 -204
- data/test/tc_transport_fork.rb +179 -0
- data/test/tc_tsig.rb +2 -2
- data/test/tc_tsig_api.rb +251 -0
- data/test/tc_tsig_rfc8945.rb +357 -0
- data/test/tc_v2_release_dnssec.rb +442 -0
- data/test/tc_v2_release_transport.rb +304 -0
- data/test/tc_v2_zone_transfer_native.rb +58 -0
- data/test/tc_zone_reader.rb +217 -1
- data/test/tcp_pipelining_server.rb +150 -0
- data/test/ts_offline.rb +77 -6
- data/test/ts_online.rb +2 -1
- metadata +127 -14
- data/.coveralls.yml +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ce202a48aff5609526c43e70ca92eebfeff66a78aa041f78e4915c1fd5a73e7
|
|
4
|
+
data.tar.gz: a0daa3de8611482fe3352c36aa6966504ab66dea196d4587604bc75af615cceb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca80e16585009122a9026e86932b57a21431259ebbb59addca7bb256b23eaef8bc050f2ae21575bac49e3d872e986adb1a11442c176de65d2586c5e68ee94b01
|
|
7
|
+
data.tar.gz: 199c5067d450e346e1d3ffb4e16cd2160d5c0cb1e6d53e93b9e0f58ae97beb733f85e34c35be8b7c398c8cf7389fc68b0f1466b1024d7d9ac085f52f9255418e
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -5,22 +5,34 @@ on: [push, pull_request]
|
|
|
5
5
|
jobs:
|
|
6
6
|
build:
|
|
7
7
|
runs-on: ubuntu-latest
|
|
8
|
+
timeout-minutes: 15
|
|
8
9
|
strategy:
|
|
9
10
|
fail-fast: false
|
|
10
11
|
matrix:
|
|
11
|
-
ruby: [ '3.2', '3.3', '3.4' ] # , 'ruby-head' ]
|
|
12
|
+
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4', '4.0' ] # , 'ruby-head' ]
|
|
12
13
|
rubyopt: ['']
|
|
13
14
|
include:
|
|
14
15
|
- ruby: '3.4'
|
|
15
16
|
rubyopt: "--enable-frozen-string-literal --debug-frozen-string-literal"
|
|
16
17
|
|
|
17
|
-
name: Ruby ${{ matrix.ruby }} tests
|
|
18
|
+
name: Ruby ${{ matrix.ruby }} tests${{ matrix.rubyopt != '' && ' (frozen strings)' || '' }}
|
|
18
19
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
20
|
+
- uses: actions/checkout@v7
|
|
20
21
|
- name: Setup Ruby
|
|
21
22
|
uses: ruby/setup-ruby@v1
|
|
22
23
|
with:
|
|
23
24
|
ruby-version: ${{ matrix.ruby }}
|
|
24
25
|
bundler-cache: true
|
|
25
26
|
- name: Run tests ${{ matrix.rubyopt }}
|
|
26
|
-
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
env:
|
|
29
|
+
DNSRUBY_TEST_TIMEOUT: '120'
|
|
30
|
+
run: timeout --kill-after=10s 8m bundle exec rake test RUBYOPT="${{ matrix.rubyopt }}"
|
|
31
|
+
- name: Upload coverage
|
|
32
|
+
if: matrix.ruby == '3.4' && matrix.rubyopt == ''
|
|
33
|
+
uses: coverallsapp/github-action@v2
|
|
34
|
+
with:
|
|
35
|
+
file: coverage/.resultset.json
|
|
36
|
+
format: simplecov
|
|
37
|
+
# Reporting outages must not change the test result.
|
|
38
|
+
fail-on-error: false
|
data/DNSSEC
CHANGED
|
@@ -9,11 +9,12 @@ The dnssec security status of a message is stored in Message#security_level (def
|
|
|
9
9
|
|
|
10
10
|
It is possible to tell Dnsruby to use a Recursor or a defined (or system default) Resolver to perform the validation. The default is to use a Recursor, as many systems are behind dodgy servers which mangle the DNS records. Using a Recursor means that only authoritative nameservers are queried for the DNSSEC records.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Dnsruby includes the IANA root DS trust anchors for KSK-2017 (key tag 20326) and KSK-2024 (key tag 38696). KSK-2024 began signing the root DNSKEY RRSet on 11 October 2026. The revoked KSK-2010 (key tag 19036) is no longer trusted. Applications may also configure local DNSKEY or DS trust anchors using Dnsruby::Dnssec.add_trust_anchor. Obtain trust anchors through a trusted channel; IANA publishes the root anchors and their validity periods at https://www.iana.org/dnssec/files. Dnsruby does not automatically update these configured anchors using RFC 5011, so they must be kept current through software or application updates.
|
|
13
13
|
|
|
14
14
|
It is possible to turn off dnssec validation on a per-message basis. Simply set Message#do_validation to false.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Resolver#do_validation defaults to false; set it to true to enable local DNSSEC validation. The dnssec flag controls requesting DNSSEC records and is distinct from local validation. Recursor#query attempts validation unless its no_validation argument is true. Missing root anchors affect queries that need to establish trust from the root; an application using local trust anchors or previously authenticated cached keys may behave differently.
|
|
17
17
|
|
|
18
18
|
Dnsruby maintains a cache of responses, and a cache of trusted keys. Once the initial keys have been downloaded, and a set of trusted keys built up, very little overhead is required to enjoy the benefits of DNSSEC. There is, however, some initial cost (to build up the caches).
|
|
19
19
|
|
|
20
|
+
Dnsruby::Dnssec.clear_trusted_keys clears learned DNSKEYs and all DS/DLV anchors, including the built-in root DS anchors; configured DNSKEY anchors are retained. To remove local anchors and built-in root trust, call clear_trust_anchors followed by clear_trusted_keys. Use remove_trust_anchor or clear_trust_anchors to remove local anchors explicitly. Dnsruby::Dnssec.reset restores the built-in root anchors, clears local anchors and cached keys, and resets the validation policy and resolver settings to their defaults.
|
data/EXAMPLES
CHANGED
|
@@ -48,7 +48,7 @@ tsig = Dnsruby::RR.create({
|
|
|
48
48
|
:type => "TSIG",
|
|
49
49
|
:ttl => 0,
|
|
50
50
|
:klass => "ANY",
|
|
51
|
-
:algorithm => "hmac-
|
|
51
|
+
:algorithm => "hmac-sha256", # must match the server's key configuration
|
|
52
52
|
:fudge => 300,
|
|
53
53
|
:key => "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw==",
|
|
54
54
|
:error => 0
|
data/README.md
CHANGED
|
@@ -1,96 +1,402 @@
|
|
|
1
|
-
[]
|
|
1
|
+
[](https://github.com/alexdalitz/dnsruby/actions/workflows/ci.yml)
|
|
2
2
|
[](https://coveralls.io/r/alexdalitz/dnsruby?branch=master)
|
|
3
3
|
|
|
4
4
|
<img src="http://caerkettontech.com/dnsruby/DNSRuby-colour-mid.png" width="200" height="200" />
|
|
5
5
|
|
|
6
|
-
Dnsruby
|
|
7
|
-
=======
|
|
6
|
+
# Dnsruby
|
|
8
7
|
|
|
9
|
-
Dnsruby is a pure Ruby DNS client library
|
|
10
|
-
|
|
8
|
+
Dnsruby is a pure Ruby DNS client library. Its stub resolver sends queries to
|
|
9
|
+
configured DNS servers, with an API based on Ruby's `Resolv` library and extended
|
|
10
|
+
to support DNSSEC, dynamic updates and zone transfers. It aims to comply with
|
|
11
|
+
the DNS RFCs.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
complete DNS implementation.
|
|
13
|
+
**Upgrading from 1.74.0:** [Read the v2 upgrade guide](#upgrading-to-v2) for changed
|
|
14
|
+
defaults and compatibility notes.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
queries. It is therefore suitable for high volume DNS applications.
|
|
16
|
+
## Getting started
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
Install the gem:
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
```sh
|
|
21
|
+
gem install dnsruby
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or add it to your Gemfile and run `bundle install`:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem 'dnsruby'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To query the nameservers configured on your system:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require 'dnsruby'
|
|
34
|
+
|
|
35
|
+
resolver = Dnsruby::Resolver.new
|
|
36
|
+
begin
|
|
37
|
+
response = resolver.query('example.com', 'A')
|
|
38
|
+
response.answer.each { |record| puts record }
|
|
39
|
+
ensure
|
|
40
|
+
resolver.close
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The second argument selects the record type: for example, `'AAAA'`, `'MX'` or
|
|
45
|
+
`'TXT'`. If you leave it out, the query asks for an A record. Reuse a resolver
|
|
46
|
+
for multiple queries, then close it when you have finished.
|
|
47
|
+
|
|
48
|
+
See the [API documentation](https://www.rubydoc.info/gems/dnsruby/),
|
|
49
|
+
[usage articles in the wiki](https://github.com/alexdalitz/dnsruby/wiki), and
|
|
50
|
+
[demo programs](demo) for more examples.
|
|
51
|
+
|
|
52
|
+
### Dependencies
|
|
53
|
+
|
|
54
|
+
Dnsruby requires **Ruby 3.0 or later**. RubyGems installs the `base64`, `logger`
|
|
55
|
+
and `simpleidn` dependencies. TSIG signing and DNSSEC validation also require
|
|
56
|
+
Ruby's OpenSSL library.
|
|
57
|
+
|
|
58
|
+
### Demo code
|
|
59
|
+
|
|
60
|
+
The [demo folder](demo) includes `rubydig`, a basic dig-style command-line tool,
|
|
61
|
+
and examples of resolving many names concurrently.
|
|
62
|
+
|
|
63
|
+
## Usage help
|
|
64
|
+
|
|
65
|
+
### IPv6 nameservers and address formatting
|
|
66
|
+
|
|
67
|
+
An IPv6 link-local nameserver needs an interface name or index after `%`, so
|
|
68
|
+
the operating system knows which network interface to use:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
resolver = Dnsruby::Resolver.new(nameserver: 'fe80::1%en0')
|
|
72
|
+
begin
|
|
73
|
+
response = resolver.query('example.com')
|
|
74
|
+
ensure
|
|
75
|
+
resolver.close
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Replace the address and `en0` with your DNS server's address and local interface.
|
|
80
|
+
The same format works in `resolv.conf`, for example `nameserver fe80::1%en0`.
|
|
81
|
+
The interface suffix is only for local routing; do not put it in an AAAA record.
|
|
25
82
|
|
|
26
|
-
|
|
83
|
+
IPv6 text output is unchanged from 1.74.0. To request the standard RFC 5952
|
|
84
|
+
format explicitly, use:
|
|
27
85
|
|
|
28
|
-
|
|
86
|
+
```ruby
|
|
87
|
+
Dnsruby::IPv6.create('2001:DB8::1').to_s_canonical # => '2001:db8::1'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Features
|
|
91
|
+
|
|
92
|
+
- DNSSEC validation.
|
|
93
|
+
- Zone transfers (AXFR and IXFR), including TSIG-signed transfers.
|
|
94
|
+
- Dynamic updates, including TSIG-signed updates.
|
|
95
|
+
- Unknown record types through the generic record API (RFC 3597).
|
|
96
|
+
- Record types including A, AAAA, AFSDB, ANY, CAA, CERT, CNAME, DNAME, GPOS,
|
|
97
|
+
HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NXT, OPT, PTR, PX,
|
|
98
|
+
RP, RT, SOA, SPF, SRV, SVCB, HTTPS, TKEY, TSIG, TXT, WKS, X25, DNSKEY, RRSIG,
|
|
99
|
+
NSEC, NSEC3, NSEC3PARAM, DS and DLV.
|
|
29
100
|
|
|
30
|
-
|
|
101
|
+
## Upgrading to v2
|
|
31
102
|
|
|
32
|
-
|
|
103
|
+
Version 2 adds SVCB and HTTPS records and improves concurrent queries, TCP
|
|
104
|
+
connections, DNSSEC validation, caching and zone-file parsing. Start with the
|
|
105
|
+
checks below; the [release notes](RELEASE_NOTES.md#compatibility-with-1740)
|
|
106
|
+
describe the compatibility changes in full.
|
|
33
107
|
|
|
34
|
-
|
|
35
|
-
------------
|
|
108
|
+
### Check these before upgrading
|
|
36
109
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
110
|
+
1. **Use Ruby 3.0 or later.** Older Ruby versions are no longer supported.
|
|
111
|
+
2. **Check TSIG keys if you sign updates or transfers.** An omitted algorithm
|
|
112
|
+
now means HMAC-SHA256, rather than HMAC-MD5. The application and server must
|
|
113
|
+
agree. See [updating TSIG keys](#updating-tsig-keys).
|
|
114
|
+
3. **Check saved records of unknown types.** Automatically generated record
|
|
115
|
+
classes no longer have permanent Ruby names, so saving them with Marshal or
|
|
116
|
+
YAML is unsupported. Ordinary named classes such as `Dnsruby::RR::IN::A`
|
|
117
|
+
are unaffected. For a small, fixed set of application-defined types,
|
|
118
|
+
`Dnsruby::RR::Generic.create(type, klass)` can register permanent classes
|
|
119
|
+
before records are created. See the [persistence notes](RELEASE_NOTES.md#compatibility-with-1740)
|
|
120
|
+
for details.
|
|
121
|
+
4. **Review DNSSEC results and DS creation if you use them.** Legacy SHA-1
|
|
122
|
+
signing algorithms are no longer accepted by resolver validation, and new
|
|
123
|
+
DS records use SHA-256 by default. See [DNSSEC algorithm policy](#dnssec-algorithm-policy).
|
|
124
|
+
5. **Review any custom retry or transfer settings.** Retries are capped at
|
|
125
|
+
three per question, server address and transport. A zone transfer now has a
|
|
126
|
+
30-second limit for reading or writing each complete DNS message. See
|
|
127
|
+
[retries](#response-caching-and-retries) and [transfer deadlines](#zone-transfer-deadlines).
|
|
44
128
|
|
|
45
|
-
|
|
46
|
-
|
|
129
|
+
The default UDP payload size is also smaller: **1232 bytes, down from 4096**.
|
|
130
|
+
This reduces fragmentation but can cause more large replies to retry over TCP.
|
|
131
|
+
An explicit `udp_size` setting still works. Existing IPv6 text output is preserved.
|
|
47
132
|
|
|
48
|
-
|
|
49
|
-
These examples include a basic dig tool (rubydig) and a tool to
|
|
50
|
-
concurrently resolve many names, amongst others.
|
|
133
|
+
### Updating TSIG keys
|
|
51
134
|
|
|
52
|
-
|
|
53
|
-
|
|
135
|
+
The key name, secret and algorithm must match the DNS server's configuration.
|
|
136
|
+
SHA-256 is the default in v2. If the server currently uses MD5, migrate both
|
|
137
|
+
sides to SHA-256 or explicitly name the old algorithm during the transition.
|
|
54
138
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
successfully you may need to have the bundler gem installed and
|
|
58
|
-
run `bundle` or `bundle install` from the project root to install
|
|
59
|
-
a suitable version of minitest.
|
|
139
|
+
For BIND, generate a SHA-256 key with
|
|
140
|
+
[tsig-keygen](https://bind9.readthedocs.io/en/stable/manpages.html#tsig-keygen-tsig-key-generation-tool):
|
|
60
141
|
|
|
61
|
-
|
|
62
|
-
|
|
142
|
+
```sh
|
|
143
|
+
tsig-keygen -a hmac-sha256 dnsruby-key.example.
|
|
63
144
|
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
145
|
+
|
|
146
|
+
Install the key on the server, grant the required update or transfer permissions,
|
|
147
|
+
and reload its configuration. Update any scripts that generate keys as well.
|
|
148
|
+
Then configure the application with the same name and base64-encoded secret:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
resolver.tsig = ['dnsruby-key.example.', secret, 'hmac-sha256']
|
|
67
152
|
```
|
|
68
|
-
If you get the following error when running rake test tasks,
|
|
69
|
-
then you may need to preface the command with bundle exec to
|
|
70
|
-
ensure that the gem versions specified in Gemfile.lock are used
|
|
71
|
-
at runtime:
|
|
72
153
|
|
|
154
|
+
Or set the key on an individual message:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
message.set_tsig(name: 'dnsruby-key.example.', key: secret,
|
|
158
|
+
algorithm: 'hmac-sha256')
|
|
73
159
|
```
|
|
74
|
-
|
|
160
|
+
|
|
161
|
+
These explicit SHA-256 settings also work in 1.74.0, so you can migrate the keys
|
|
162
|
+
before upgrading Dnsruby. Keep the generated secret private.
|
|
163
|
+
|
|
164
|
+
If you must keep an existing MD5 key temporarily, add the algorithm explicitly:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
resolver.tsig = [key_name, secret, 'hmac-md5']
|
|
75
168
|
```
|
|
76
169
|
|
|
77
|
-
|
|
78
|
-
|
|
170
|
+
In particular, replace existing two-element settings such as
|
|
171
|
+
`resolver.tsig = [key_name, secret]`: they now select SHA-256. For a message,
|
|
172
|
+
use `algorithm: 'hmac-md5'` in the `set_tsig` hash.
|
|
173
|
+
|
|
174
|
+
Explicit MD5 remains available for compatibility, although
|
|
175
|
+
[RFC 8945 section 6](https://www.rfc-editor.org/rfc/rfc8945.html#section-6)
|
|
176
|
+
prohibits its use. Dnsruby does not fall back to MD5 automatically. Selecting it
|
|
177
|
+
logs one warning per process; set `Dnsruby.log.level = Logger::WARN` before
|
|
178
|
+
configuring TSIG to see it. The default log level is FATAL.
|
|
179
|
+
|
|
180
|
+
### DNSSEC algorithm policy
|
|
181
|
+
|
|
182
|
+
Dnsruby validates signatures made with RSA/SHA-256 (8), RSA/SHA-512 (10),
|
|
183
|
+
ECDSA P-256 (13), ECDSA P-384 (14), Ed25519 (15), and Ed448 (16). Ed25519 and Ed448 require version
|
|
184
|
+
3.0 or later of the `openssl` gem and an OpenSSL provider that supports it
|
|
185
|
+
(normally OpenSSL 1.1.1 or later). On older Ruby releases, including Ruby 3.0,
|
|
186
|
+
you may need to install a newer compatible `openssl` gem. An unavailable
|
|
187
|
+
algorithm is treated as unsupported.
|
|
188
|
+
|
|
189
|
+
Resolver validation treats RSA/SHA-1 signing algorithms 5 and 7 as unsupported,
|
|
190
|
+
as required by [RFC 9905](https://www.rfc-editor.org/rfc/rfc9905.html#section-2).
|
|
191
|
+
An answer whose authenticated chain relies only on these algorithms is
|
|
192
|
+
`INSECURE`: it is not protected by an accepted DNSSEC chain. A signature using
|
|
193
|
+
a supported algorithm must still validate where one is required; a failed
|
|
194
|
+
check does not become an insecure result.
|
|
195
|
+
|
|
196
|
+
Explicit `Dnsruby::Dnssec.verify` calls retain the ability to verify historical
|
|
197
|
+
RSA/SHA-1 signatures. Within an authenticated set of DS records, SHA-256 digests
|
|
198
|
+
take preference over SHA-1.
|
|
199
|
+
|
|
200
|
+
`Dnsruby::RR::DS.from_key(key)` now creates a SHA-256 DS record. New DS creation
|
|
201
|
+
rejects SHA-1 digests and signing algorithms 5/7. Existing records remain
|
|
202
|
+
readable, and `digest_key` and `check_key` still support historical verification.
|
|
203
|
+
|
|
204
|
+
### DNSSEC validation limits
|
|
205
|
+
|
|
206
|
+
Dnsruby limits the time and work spent validating an answer, including extra
|
|
207
|
+
lookups needed to establish trust. This prevents an unusually expensive reply
|
|
208
|
+
from occupying a validator indefinitely.
|
|
209
|
+
|
|
210
|
+
The defaults for one validation are:
|
|
211
|
+
|
|
212
|
+
- **Five seconds** of elapsed time, including supporting lookups.
|
|
213
|
+
- **64 signature verification attempts.**
|
|
214
|
+
- **100,000 work units**, counting operations such as key/signature comparisons
|
|
215
|
+
and NSEC3 hashes used to check that a name or record is absent.
|
|
216
|
+
|
|
217
|
+
Reaching a limit raises `Dnsruby::ValidationLimitError`, a subclass of
|
|
218
|
+
`Dnsruby::VerifyError`. Validation fails; the answer is not accepted as insecure.
|
|
219
|
+
If you need to validate unusually large amounts of trusted data, set the limits
|
|
220
|
+
before starting queries:
|
|
79
221
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
222
|
+
```ruby
|
|
223
|
+
Dnsruby::Dnssec.validation_limits = {
|
|
224
|
+
max_signature_verifications: 128,
|
|
225
|
+
max_work: 200_000,
|
|
226
|
+
timeout: 10.0
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Omitted settings use their defaults, and all values must be positive. Validation
|
|
231
|
+
also stops when the query times out or the resolver closes. Cancellation is
|
|
232
|
+
checked between operations and while waiting on the network; an OpenSSL
|
|
233
|
+
operation already in progress is allowed to finish.
|
|
234
|
+
|
|
235
|
+
At most four workers validate asynchronous responses at once, with 64 more
|
|
236
|
+
responses waiting. Further responses fail with `ValidationLimitError`.
|
|
237
|
+
|
|
238
|
+
### Response caching and retries
|
|
239
|
+
|
|
240
|
+
Dnsruby now caches negative answers: **NXDOMAIN** means the name does not exist,
|
|
241
|
+
and **NODATA** means there is no record of the requested type. It uses the shorter
|
|
242
|
+
of the authority's SOA TTL and SOA MINIMUM as the cache lifetime, shortened
|
|
243
|
+
further when record or DNSSEC lifetimes require it. Completed lookup failures
|
|
244
|
+
are cached for one second, so repeated requests do not immediately repeat the
|
|
245
|
+
same failed lookup.
|
|
246
|
+
|
|
247
|
+
These caches have size limits and keep results separate for different resolver
|
|
248
|
+
settings and DNSSEC trust policies. Cancelled queries, raw messages, dynamic
|
|
249
|
+
updates and TSIG-signed transactions do not populate them. To disable them, set
|
|
250
|
+
`resolver.do_caching = false`; a Recursor uses the setting on its supplied
|
|
251
|
+
Resolver. `PacketSender.clear_caches` and `Recursor.clear_caches` also invalidate
|
|
252
|
+
these entries.
|
|
253
|
+
|
|
254
|
+
Positive answers are kept separate for each upstream server, port, source
|
|
255
|
+
configuration and query options, including EDNS. Truncated replies are never
|
|
256
|
+
reused from the cache, so a later query can still retry over TCP when needed.
|
|
257
|
+
Each send attempt uses one snapshot of its transport configuration; subsequent
|
|
258
|
+
attempts can use newer settings. Adding an explicit source port replaces the
|
|
259
|
+
default `[0]` pool (random source ports). A pool cannot mix zero and explicit ports.
|
|
260
|
+
|
|
261
|
+
For each question, Dnsruby makes at most **three attempts per server address per
|
|
262
|
+
transport**, including retries without optional EDNS support. UDP and TCP have
|
|
263
|
+
separate limits. Setting `retry_times` above three is still accepted, but does
|
|
264
|
+
not increase this limit.
|
|
265
|
+
|
|
266
|
+
Recursor caches also have size limits. Cached delegations expire when their NS
|
|
267
|
+
records expire, even if the associated nameserver addresses have longer TTLs.
|
|
268
|
+
Their reusable lifetimes are also bounded by the parent route through which
|
|
269
|
+
they were learned, so a child cannot keep that route alive indefinitely. An
|
|
270
|
+
unrelated parent-cache eviction does not discard an already admitted child.
|
|
271
|
+
Root NS TTLs govern bootstrap reuse, and missing addresses are resolved even
|
|
272
|
+
when a priming reply includes addresses for only some root nameservers.
|
|
273
|
+
Fresh nameserver addresses with a zero TTL remain usable for the current
|
|
274
|
+
lookup, but are not retained in shared caches.
|
|
275
|
+
When you supply a Resolver to `Recursor.new`, its transport and source-address
|
|
276
|
+
settings apply to the recursive queries. Its `query_timeout` limits the whole
|
|
277
|
+
lookup, including nameserver discovery and validation. TSIG keys remain tied
|
|
278
|
+
to the configured peer; they are not passed to arbitrary authoritative servers.
|
|
279
|
+
|
|
280
|
+
Learned DNSKEY and DS records also have size limits. Configured trust anchors
|
|
281
|
+
are kept until you remove them or their requested expiration time arrives;
|
|
282
|
+
this applies to both DNSKEY and DS anchors.
|
|
283
|
+
|
|
284
|
+
### Recursive lookup limits
|
|
285
|
+
|
|
286
|
+
Each `Recursor` lookup has a 30-second deadline, including root bootstrap,
|
|
287
|
+
nameserver address discovery and DNSSEC lookups. A shorter Resolver
|
|
288
|
+
`query_timeout` or inherited deadline takes precedence. The recursive deadline
|
|
289
|
+
also applies when the Resolver's `query_timeout` is zero.
|
|
290
|
+
|
|
291
|
+
For ordinary questions, `Recursor` follows CNAME and DNAME aliases and returns
|
|
292
|
+
the alias chain together with the target answer under the original question.
|
|
293
|
+
Alias lookups share the same deadline and work limits. Questions explicitly
|
|
294
|
+
requesting CNAME, DNAME or ANY return the received records directly. When
|
|
295
|
+
validation is enabled, the DNSSEC limits above are shared across the entire
|
|
296
|
+
recursive lookup, including parallel address discovery and the final answer.
|
|
297
|
+
|
|
298
|
+
The lookup shares limits of 128 transmission attempts and 256 discovery work
|
|
299
|
+
units across its supporting queries. Work units count recursive steps and
|
|
300
|
+
nameserver candidates. At most eight extra threads discover nameserver
|
|
301
|
+
addresses; further discovery runs within an existing thread. Root address
|
|
302
|
+
queries run in batches of at most eight. Repeated address dependencies on the
|
|
303
|
+
same discovery path are skipped.
|
|
304
|
+
|
|
305
|
+
Set these limits before starting queries when your workload needs more time
|
|
306
|
+
or discovery work:
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
recursor.recursion_limits = {
|
|
310
|
+
timeout: 60.0,
|
|
311
|
+
max_queries: 256,
|
|
312
|
+
max_steps: 512,
|
|
313
|
+
max_workers: 8
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Omitted settings use their defaults. The timeout must be positive and finite;
|
|
318
|
+
other limits must be positive integers. Exhausted work or transmission limits
|
|
319
|
+
raise `Dnsruby::RecursionLimitError`, a subclass of `Dnsruby::ResolvError`, and
|
|
320
|
+
are not cached as failures of the individual question. An expired recursive
|
|
321
|
+
deadline raises `Dnsruby::ResolvTimeout`.
|
|
322
|
+
|
|
323
|
+
### Zone transfer deadlines
|
|
324
|
+
|
|
325
|
+
Zone transfers now have a deadline for each complete DNS message read or
|
|
326
|
+
written. This is a total time limit for that message, not a timer that restarts
|
|
327
|
+
each time more bytes arrive.
|
|
328
|
+
|
|
329
|
+
| Setting | Default | What it limits |
|
|
330
|
+
| --- | --- | --- |
|
|
331
|
+
| `connect_timeout` | 5 seconds | Opening a connection to a server. |
|
|
332
|
+
| `io_timeout` | 30 seconds | Reading or writing one complete DNS message. |
|
|
333
|
+
| `transfer_timeout` | `nil` (no overall limit) | One complete attempt against a server, including the connection and an IXFR-to-AXFR fallback. |
|
|
334
|
+
|
|
335
|
+
Increase `io_timeout` for a slow connection, or set `transfer_timeout` to bound
|
|
336
|
+
the complete attempt. Both accept positive durations in seconds; use `nil` for
|
|
337
|
+
`transfer_timeout` to remove the overall limit. If an attempt fails, the next
|
|
338
|
+
configured server may be tried.
|
|
339
|
+
|
|
340
|
+
Use an authoritative server that allows transfers from your application. If it
|
|
341
|
+
requires TSIG, configure `transfer.tsig` with the matching key before transferring.
|
|
342
|
+
|
|
343
|
+
```ruby
|
|
344
|
+
transfer = Dnsruby::ZoneTransfer.new
|
|
345
|
+
transfer.server = '192.0.2.53' # Replace with your zone's authoritative server
|
|
346
|
+
transfer.io_timeout = 60
|
|
347
|
+
transfer.transfer_timeout = 300
|
|
348
|
+
records = transfer.transfer('example.org')
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Transfers handle partial reads and writes, check that replies match the request,
|
|
352
|
+
and require matching opening and closing SOA records. Multi-message TSIG
|
|
353
|
+
verification remains supported.
|
|
354
|
+
|
|
355
|
+
## Unit tests
|
|
356
|
+
|
|
357
|
+
From the project root, install the development dependencies and run the tests:
|
|
358
|
+
|
|
359
|
+
```sh
|
|
360
|
+
bundle install
|
|
361
|
+
bundle exec rake test_offline # Tests that do not need public DNS access
|
|
362
|
+
bundle exec rake test_online # Tests that need network access
|
|
363
|
+
bundle exec rake test # Both suites
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Using `bundle exec` selects the dependency versions required by the project,
|
|
367
|
+
including minitest. Tests that need OpenSSL are skipped if it is unavailable.
|
|
368
|
+
|
|
369
|
+
### Longer concurrency tests
|
|
370
|
+
|
|
371
|
+
The separate `soak` task exercises concurrent UDP and TCP queries against its
|
|
372
|
+
own local test server. It needs no public DNS access, privileged port or
|
|
373
|
+
separately running server, and is not included in the standard suites.
|
|
374
|
+
|
|
375
|
+
Start with a small workload:
|
|
376
|
+
|
|
377
|
+
```sh
|
|
378
|
+
SOAK_THREADS=4 SOAK_ROUNDS=3 SOAK_ASYNC_ROUNDS=3 SOAK_TIMEOUT=30 bundle exec rake soak
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
For the default workload of 81,512 queries across eight cases, allow enough open
|
|
382
|
+
files and a longer per-case deadline on a busy machine:
|
|
383
|
+
|
|
384
|
+
```sh
|
|
385
|
+
ulimit -n 4096
|
|
386
|
+
SOAK_TIMEOUT=300 bundle exec rake soak
|
|
387
|
+
```
|
|
84
388
|
|
|
85
|
-
|
|
389
|
+
Increase `SOAK_ROUNDS` and `SOAK_ASYNC_ROUNDS` for longer runs. The tests check
|
|
390
|
+
errors, wrong responses and duplicate completions. They do not measure memory
|
|
391
|
+
or file-descriptor growth over a multi-day run. The older RubyDNS/NIO helpers
|
|
392
|
+
remain available but are not used by this task.
|
|
86
393
|
|
|
87
|
-
Contact/Links
|
|
88
|
-
-------
|
|
394
|
+
## Contact/Links
|
|
89
395
|
|
|
90
|
-
| Link
|
|
91
|
-
|
|
92
|
-
| Author
|
|
93
|
-
|
|
|
396
|
+
| Link | Address |
|
|
397
|
+
| --- | --- |
|
|
398
|
+
| Author | alex@caerkettontech.com |
|
|
399
|
+
| GitHub | https://github.com/alexdalitz/dnsruby |
|
|
94
400
|
| Google Group | https://groups.google.com/forum/#!forum/dnsruby |
|
|
95
|
-
|
|
|
401
|
+
| RubyGems | https://rubygems.org/gems/dnsruby/ |
|
|
96
402
|
| Documentation | https://www.rubydoc.info/gems/dnsruby/ |
|