dnsruby 1.74.0 → 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 +302 -1
- data/Rakefile +0 -3
- data/demo/axfr.rb +24 -6
- data/demo/rubydig.rb +0 -2
- data/dnsruby.gemspec +5 -4
- data/lib/dnsruby/cache.rb +279 -47
- data/lib/dnsruby/code_mapper.rb +35 -8
- data/lib/dnsruby/code_mappers.rb +63 -49
- data/lib/dnsruby/config.rb +75 -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 +645 -203
- data/lib/dnsruby/presentation.rb +147 -0
- data/lib/dnsruby/query_control.rb +132 -0
- data/lib/dnsruby/recursor.rb +795 -339
- data/lib/dnsruby/resolver.rb +468 -186
- 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 +19 -0
- 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/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 +979 -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 +42 -20
- data/test/tc_res_opt.rb +21 -1
- data/test/tc_resolver.rb +21 -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 +105 -10
- data/.coveralls.yml +0 -2
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require_relative 'tc_pipeline_send'
|
|
2
|
+
require_relative 'tc_review_transport_native'
|
|
3
|
+
|
|
4
|
+
class TestReleaseTransportFixes < Minitest::Test
|
|
5
|
+
def isolated(source)
|
|
6
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, '-Ilib', '-e',
|
|
7
|
+
TestPipelineSend::FIXTURE + "\nTimeout.timeout(5) do\n" + source + "\nend\n",
|
|
8
|
+
chdir: File.expand_path('..', __dir__))
|
|
9
|
+
assert(status.success?, "#{stdout}\n#{stderr}")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_published_configuration_serves_queries_during_nameserver_construction
|
|
13
|
+
isolated(<<~'RUBY')
|
|
14
|
+
resolver = Dnsruby::Resolver.new(nameserver: '127.0.0.1', dnssec: false)
|
|
15
|
+
sent = Queue.new
|
|
16
|
+
resolver.single_resolvers.first.define_singleton_method(:send_async) { |*| sent << true }
|
|
17
|
+
original = Dnsruby::PacketSender.method(:new)
|
|
18
|
+
entered, release = Queue.new, Queue.new
|
|
19
|
+
Dnsruby::PacketSender.define_singleton_method(:new) do |*args|
|
|
20
|
+
entered << true
|
|
21
|
+
release.pop
|
|
22
|
+
original.call(*args)
|
|
23
|
+
end
|
|
24
|
+
[:add_server, :nameserver=].each do |operation|
|
|
25
|
+
configuring = Thread.new { resolver.public_send(operation, '127.0.0.2') }
|
|
26
|
+
entered.pop
|
|
27
|
+
begin
|
|
28
|
+
caller = Thread.new { resolver.send_async(query, Queue.new) }
|
|
29
|
+
raise 'query waited for replacement configuration' unless caller.join(1)
|
|
30
|
+
caller.value
|
|
31
|
+
raise 'published sender was not used' unless sent.pop(true)
|
|
32
|
+
ensure
|
|
33
|
+
release << true
|
|
34
|
+
configuring.value
|
|
35
|
+
caller&.join
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
resolver.close
|
|
39
|
+
RUBY
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_failed_initial_configuration_waits_for_recovery_publication
|
|
43
|
+
isolated(<<~'RUBY')
|
|
44
|
+
resolver = Dnsruby::Resolver.new(nameserver: [], dnssec: false)
|
|
45
|
+
raise 'expected failed initialization' unless resolver.instance_variable_get(:@configuration_error)
|
|
46
|
+
original = Dnsruby::PacketSender.method(:new)
|
|
47
|
+
entered, release, sent = Queue.new, Queue.new, Queue.new
|
|
48
|
+
Dnsruby::PacketSender.define_singleton_method(:new) do |*args|
|
|
49
|
+
entered << true
|
|
50
|
+
release.pop
|
|
51
|
+
writer = original.call(*args)
|
|
52
|
+
writer.define_singleton_method(:send_async) { |*| sent << true }
|
|
53
|
+
writer
|
|
54
|
+
end
|
|
55
|
+
configuring = Thread.new { resolver.nameserver = '127.0.0.1' }
|
|
56
|
+
entered.pop
|
|
57
|
+
caller = Thread.new { resolver.send_async(query, Queue.new) }
|
|
58
|
+
begin
|
|
59
|
+
raise 'query admitted before recovered senders were published' if caller.join(0.05)
|
|
60
|
+
ensure
|
|
61
|
+
release << true
|
|
62
|
+
configuring.value
|
|
63
|
+
end
|
|
64
|
+
caller.value
|
|
65
|
+
raise 'recovered sender was not used' unless sent.pop(true)
|
|
66
|
+
raise 'configuration error survived publication' if resolver.instance_variable_get(:@configuration_error)
|
|
67
|
+
resolver.close
|
|
68
|
+
RUBY
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_single_resolver_timeout_setters_update_the_existing_sender
|
|
72
|
+
isolated(<<~'RUBY')
|
|
73
|
+
resolver = Dnsruby::SingleResolver.new(server: '127.0.0.1', packet_timeout: 2)
|
|
74
|
+
writer = resolver.single_resolvers.first
|
|
75
|
+
[:packet_timeout=, :query_timeout=].zip([3, 4]).each do |setter, timeout|
|
|
76
|
+
resolver.public_send(setter, timeout)
|
|
77
|
+
raise 'sender was replaced' unless resolver.single_resolvers.first.equal?(writer)
|
|
78
|
+
raise 'stale packet timeout' unless writer.packet_timeout == timeout &&
|
|
79
|
+
resolver.packet_timeout == timeout && resolver.query_timeout == timeout
|
|
80
|
+
end
|
|
81
|
+
resolver.close
|
|
82
|
+
RUBY
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_selector_waits_for_an_empty_resolvers_exact_query_deadline
|
|
86
|
+
isolated(<<~'RUBY')
|
|
87
|
+
now = 100.0
|
|
88
|
+
Dnsruby::TransportClock.define_singleton_method(:now) { now }
|
|
89
|
+
SELECTOR.singleton_class.remove_method(:notify_queue_observers)
|
|
90
|
+
resolver = Dnsruby::Resolver.new
|
|
91
|
+
resolver.single_resolvers = []
|
|
92
|
+
resolver.query_timeout = 0.05
|
|
93
|
+
results = Queue.new
|
|
94
|
+
resolver.send_async(query, results, :deadline)
|
|
95
|
+
wakeup = FrameSocket.new
|
|
96
|
+
K.class_variable_set(:@@wakeup_sockets, [wakeup, wakeup])
|
|
97
|
+
K.class_variable_get(:@@sockets) << wakeup
|
|
98
|
+
waits = []
|
|
99
|
+
IO.define_singleton_method(:select) do |_read, _write, _errors, timeout|
|
|
100
|
+
waits << timeout
|
|
101
|
+
now += timeout
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
SELECTOR.do_select
|
|
105
|
+
raise 'deadline was rounded to a polling interval' unless (waits.first - 0.05).abs < 0.000001
|
|
106
|
+
result = results.pop(true)
|
|
107
|
+
raise 'wrong completion' unless result.first == :deadline && result.last.is_a?(Dnsruby::ResolvTimeout)
|
|
108
|
+
raise 'duplicate completion' unless results.empty?
|
|
109
|
+
RUBY
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_selector_schedules_retries_at_their_exact_deadline
|
|
113
|
+
isolated(<<~'RUBY')
|
|
114
|
+
now = 100.0
|
|
115
|
+
Dnsruby::TransportClock.define_singleton_method(:now) { now }
|
|
116
|
+
SELECTOR.singleton_class.remove_method(:notify_queue_observers)
|
|
117
|
+
resolver = Dnsruby::Resolver.new(nameserver: '127.0.0.1', dnssec: false,
|
|
118
|
+
retry_times: 2, retry_delay: 0.025, query_timeout: 0.2)
|
|
119
|
+
sent = []
|
|
120
|
+
resolver.single_resolvers.first.define_singleton_method(:send_async) { |*| sent << now }
|
|
121
|
+
results = Queue.new
|
|
122
|
+
resolver.send_async(query, results, :retries)
|
|
123
|
+
wakeup = FrameSocket.new
|
|
124
|
+
K.class_variable_set(:@@wakeup_sockets, [wakeup, wakeup])
|
|
125
|
+
K.class_variable_get(:@@sockets) << wakeup
|
|
126
|
+
IO.define_singleton_method(:select) do |_read, _write, _errors, timeout|
|
|
127
|
+
now += timeout
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
SELECTOR.do_select
|
|
131
|
+
raise 'missing or duplicate attempt' unless sent.length == 2
|
|
132
|
+
raise 'retry missed its deadline' unless (sent.last - 100.05).abs < 0.000001
|
|
133
|
+
raise 'query did not finish' unless results.pop(true).last.is_a?(Dnsruby::ResolvTimeout)
|
|
134
|
+
raise 'duplicate completion' unless results.empty?
|
|
135
|
+
RUBY
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_idle_expiration_never_closes_a_pipeline_with_an_outstanding_query
|
|
139
|
+
isolated(<<~'RUBY')
|
|
140
|
+
now = 100.0
|
|
141
|
+
Dnsruby::TransportClock.define_singleton_method(:now) { now }
|
|
142
|
+
socket = FrameSocket.new
|
|
143
|
+
writer = sender(socket)
|
|
144
|
+
writer.tcp_idle_timeout = 2
|
|
145
|
+
first = settings(socket, :first, query, writer)
|
|
146
|
+
SELECTOR.add_to_select(first)
|
|
147
|
+
SELECTOR.claim_query(first, true)
|
|
148
|
+
now = 101.0
|
|
149
|
+
SELECTOR.send(:expire_idle_sockets)
|
|
150
|
+
raise 'closed before idle deadline' if socket.closed?
|
|
151
|
+
second = settings(socket, :second, query, writer)
|
|
152
|
+
SELECTOR.add_to_select(second)
|
|
153
|
+
now = 110.0
|
|
154
|
+
SELECTOR.send(:expire_idle_sockets)
|
|
155
|
+
raise 'closed an active pipeline' if socket.closed?
|
|
156
|
+
SELECTOR.claim_query(second, true)
|
|
157
|
+
now = 112.0
|
|
158
|
+
SELECTOR.send(:expire_idle_sockets)
|
|
159
|
+
raise 'idle connection retained' unless socket.closed?
|
|
160
|
+
assert_clean(socket)
|
|
161
|
+
raise 'idle timer retained' unless K.class_variable_get(:@@socket_idle_deadlines).empty?
|
|
162
|
+
RUBY
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class ReviewTransportNativeTest < Minitest::Test
|
|
167
|
+
def test_total_query_timeout_is_not_rounded_to_half_a_second
|
|
168
|
+
port, = udp_server { |_| [] }
|
|
169
|
+
resolver = resolver_for(port, packet_timeout: 2, query_timeout: 0.05, do_caching: false)
|
|
170
|
+
started = Dnsruby::TransportClock.now
|
|
171
|
+
assert_raises(Dnsruby::ResolvTimeout) { query(resolver) }
|
|
172
|
+
assert_operator(Dnsruby::TransportClock.now - started, :<, 0.3)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_idle_pipeline_closes_and_the_next_query_reconnects
|
|
176
|
+
server = TCPServer.new('127.0.0.1', 0)
|
|
177
|
+
@sockets << server
|
|
178
|
+
clients = Queue.new
|
|
179
|
+
worker do
|
|
180
|
+
loop do
|
|
181
|
+
client = server.accept
|
|
182
|
+
@sockets << client
|
|
183
|
+
clients << client
|
|
184
|
+
request = read_frame(client)
|
|
185
|
+
write_frame(client, response(request, 'connected'))
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
resolver = resolver_for(server.addr[1], use_tcp: true, tcp_pipelining: true,
|
|
189
|
+
tcp_pipelining_max_queries: :infinite, tcp_idle_timeout: 0.05, do_caching: false)
|
|
190
|
+
assert_equal(['connected'], query(resolver).answer.first.strings)
|
|
191
|
+
first = clients.pop
|
|
192
|
+
assert_nil(Timeout.timeout(1) { first.read(1) }, 'client should close the idle stream')
|
|
193
|
+
assert_equal(['connected'], query(resolver).answer.first.strings)
|
|
194
|
+
refute_same(first, clients.pop)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class ReleaseZoneDefaultsTest < Minitest::Test
|
|
4
|
+
def record(reader, line)
|
|
5
|
+
Dnsruby::RR.create(reader.process_line(line))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_ttl_directive_survives_explicit_record_ttls_and_origin_changes
|
|
9
|
+
reader = Dnsruby::ZoneReader.new('example.', 900)
|
|
10
|
+
reader.process_line('$TTL 1h')
|
|
11
|
+
assert_equal(60, record(reader, 'one 60 IN A 192.0.2.1').ttl)
|
|
12
|
+
assert_equal(3600, record(reader, 'two IN A 192.0.2.2').ttl)
|
|
13
|
+
assert_equal(120, record(reader, 'three IN 2m A 192.0.2.3').ttl)
|
|
14
|
+
reader.process_line('$ORIGIN sub')
|
|
15
|
+
assert_equal(3600, record(reader, 'four IN A 192.0.2.4').ttl)
|
|
16
|
+
reader.process_line('$TTL 0')
|
|
17
|
+
assert_equal(60, record(reader, 'five 60 IN A 192.0.2.5').ttl)
|
|
18
|
+
assert_equal(0, record(reader, 'six IN A 192.0.2.6').ttl)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_previous_ttl_inheritance_remains_available_without_a_directive
|
|
22
|
+
reader = Dnsruby::ZoneReader.new('example.', 900)
|
|
23
|
+
assert_equal(900, record(reader, 'one IN A 192.0.2.1').ttl)
|
|
24
|
+
record(reader, 'two 60 IN A 192.0.2.2')
|
|
25
|
+
assert_equal(60, record(reader, 'three IN A 192.0.2.3').ttl)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_single_quoted_text_survives_both_header_orders_and_omitted_owner
|
|
29
|
+
%w[TXT SPF TYPE16 TYPE99].each do |type|
|
|
30
|
+
reader = Dnsruby::ZoneReader.new('example.')
|
|
31
|
+
["www IN 1h #{type} 'one ; (two)'", "www 1h IN #{type} 'one ; (two)'",
|
|
32
|
+
" CLASS1 3600 #{type} 'one ; (two)'"].each do |line|
|
|
33
|
+
rr = record(reader, line)
|
|
34
|
+
assert_equal(['one ; (two)'], rr.strings)
|
|
35
|
+
assert_equal(3600, rr.ttl)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_single_quoted_continuation_and_double_quoted_text
|
|
41
|
+
reader = Dnsruby::ZoneReader.new('example.')
|
|
42
|
+
assert_nil(reader.process_line("www IN 60 TXT ( 'one ;'\n"))
|
|
43
|
+
rr = record(reader, " 'two (three)' )")
|
|
44
|
+
assert_equal(['one ;', 'two (three)'], rr.strings)
|
|
45
|
+
assert_equal(['one ; (two)'], record(reader, 'www IN 1h TXT "one ; (two)"').strings)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_rdata_domain_names_are_qualified_against_the_origin
|
|
49
|
+
reader = Dnsruby::ZoneReader.new('example.', 60)
|
|
50
|
+
examples = {
|
|
51
|
+
'KX 10 mail' => :exchange,
|
|
52
|
+
'NSEC next A NSEC RRSIG' => :next_domain,
|
|
53
|
+
'NXT next A NXT' => :next_domain,
|
|
54
|
+
'RRSIG A 8 2 60 20300101000000 20200101000000 1234 signer AQID' => :signers_name,
|
|
55
|
+
'IPSECKEY 10 3 2 gateway AQID' => :gateway
|
|
56
|
+
}
|
|
57
|
+
examples.each do |rdata, field|
|
|
58
|
+
rr = record(reader, "host IN #{rdata}")
|
|
59
|
+
assert(rr.public_send(field).absolute?, rdata)
|
|
60
|
+
assert(rr.public_send(field).subdomain_of?(Dnsruby::Name.create('example.')), rdata)
|
|
61
|
+
message = Dnsruby::Message.new
|
|
62
|
+
message.add_answer(rr)
|
|
63
|
+
decoded = Dnsruby::Message.decode(message.encode).answer.first
|
|
64
|
+
assert_equal(rr.public_send(field), decoded.public_send(field), rdata)
|
|
65
|
+
end
|
|
66
|
+
hip = record(reader, 'host IN HIP 2 00112233 AQID rendezvous other.example.net.')
|
|
67
|
+
assert_equal(['rendezvous.example.', 'other.example.net.'], hip.rsvs.map { |name| name.to_s(true) })
|
|
68
|
+
ipv4 = record(reader, 'host IN IPSECKEY 10 1 2 192.0.2.1 AQID')
|
|
69
|
+
assert_equal('192.0.2.1', ipv4.gateway.to_s)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_relative_rdata_names_preserve_escaped_final_dots
|
|
73
|
+
reader = Dnsruby::ZoneReader.new('example.', 60)
|
|
74
|
+
rr = record(reader, 'host IN NSEC next\\. A NSEC RRSIG')
|
|
75
|
+
assert_equal(['next.', 'example'], rr.next_domain.labels.map(&:string))
|
|
76
|
+
rr = record(reader, 'host IN KX 10 mail\\.')
|
|
77
|
+
assert_equal(['mail.', 'example'], rr.exchange.labels.map(&:string))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_compression_snapshots_mutable_label_strings
|
|
81
|
+
text = +'before'
|
|
82
|
+
name = Dnsruby::Name.new([Dnsruby::Name::Label.new(text), Dnsruby::Name::Label.new('example')], true)
|
|
83
|
+
encoder = Dnsruby::MessageEncoder.new
|
|
84
|
+
encoder.put_name(name)
|
|
85
|
+
text.replace('after')
|
|
86
|
+
encoder.put_name(name)
|
|
87
|
+
encoder.put_name(Dnsruby::Name.create('before.example.'))
|
|
88
|
+
decoder = Dnsruby::MessageDecoder.new(encoder.to_s)
|
|
89
|
+
assert_equal(['before.example.', 'after.example.', 'before.example.'],
|
|
90
|
+
3.times.map { decoder.get_name.to_s(true) })
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_generic_rdata_bypasses_all_type_specific_name_and_interval_rewrites
|
|
94
|
+
examples = [
|
|
95
|
+
'MX 10 Mail.Other.', 'KX 10 Mail.Other.', 'NS Ns.Other.',
|
|
96
|
+
'AFSDB 1 Host.Other.', 'NAPTR 10 20 "s" "SIP+D2U" "" Replacement.Other.',
|
|
97
|
+
'RT 10 Host.Other.', 'SRV 0 5 5060 Service.Other.', 'CNAME Alias.Other.',
|
|
98
|
+
'MB Mail.Other.', 'MG Mail.Other.', 'MR Mail.Other.', 'PTR Pointer.Other.',
|
|
99
|
+
'DNAME Target.Other.',
|
|
100
|
+
'SOA Ns.Other. Hostmaster.Other. 1 3600 600 86400 300',
|
|
101
|
+
'MINFO One.Other. Two.Other.', 'PX 10 One.Other. Two.Other.',
|
|
102
|
+
'RP Mailbox.Other. Text.Other.', 'NSEC Next.Other. A NSEC RRSIG',
|
|
103
|
+
'NXT Next.Other. A NXT',
|
|
104
|
+
'RRSIG NSEC 8 2 60 20300101000000 20200101000000 1234 Signer.Other. AQID',
|
|
105
|
+
'IPSECKEY 10 3 2 Gateway.Other. AQID',
|
|
106
|
+
'HIP 2 00112233 AQID Rendezvous.Other.',
|
|
107
|
+
'SVCB 1 Target.Other. alpn=h2', 'HTTPS 1 Target.Other. port=443'
|
|
108
|
+
]
|
|
109
|
+
examples.each do |presentation|
|
|
110
|
+
original = Dnsruby::RR.create("owner.origin. 60 IN #{presentation}")
|
|
111
|
+
bytes = rdata_bytes(original)
|
|
112
|
+
generic = "\\# #{bytes.bytesize} #{bytes.unpack1('H*').upcase}"
|
|
113
|
+
[original.type.to_s, "TYPE#{original.type.code}"].each do |type|
|
|
114
|
+
reader = Dnsruby::ZoneReader.new('origin.')
|
|
115
|
+
normalized = reader.process_line("owner IN 1m #{type} #{generic}")
|
|
116
|
+
assert_equal(generic, normalized.split(/\s+/, 5).last.strip, presentation)
|
|
117
|
+
parsed = Dnsruby::RR.create(normalized)
|
|
118
|
+
assert_equal('owner.origin.', parsed.name.to_s(true), presentation)
|
|
119
|
+
assert_equal(60, parsed.ttl, presentation)
|
|
120
|
+
assert_equal(bytes, rdata_bytes(parsed), presentation)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_generic_rdata_preserves_zone_defaults_continuations_and_prefix_contract
|
|
126
|
+
original = Dnsruby::RR.create('owner.origin. 60 IN NSEC Next.Other. A NSEC RRSIG')
|
|
127
|
+
bytes = rdata_bytes(original)
|
|
128
|
+
hex = bytes.unpack1('H*')
|
|
129
|
+
reader = Dnsruby::ZoneReader.new('origin.')
|
|
130
|
+
reader.process_line('$TTL 2h')
|
|
131
|
+
assert_nil(reader.process_line("owner IN 1m TYPE47 ( \\# #{bytes.bytesize} #{hex[0, 8]}"))
|
|
132
|
+
explicit = record(reader, "#{hex[8..]} )")
|
|
133
|
+
inherited = record(reader, " IN TYPE47 \\# #{bytes.bytesize} #{hex}")
|
|
134
|
+
assert_equal([60, 7200], [explicit.ttl, inherited.ttl])
|
|
135
|
+
assert_equal('owner.origin.', inherited.name.to_s(true))
|
|
136
|
+
assert_equal(bytes, rdata_bytes(explicit))
|
|
137
|
+
assert_equal(bytes, rdata_bytes(inherited))
|
|
138
|
+
|
|
139
|
+
signature = Dnsruby::RR.create('owner.origin. 60 IN RRSIG NSEC 8 2 60 20300101000000 20200101000000 1234 Signer.Other. AQID')
|
|
140
|
+
signature_bytes = rdata_bytes(signature)
|
|
141
|
+
generic = "\\# #{signature_bytes.bytesize} #{signature_bytes.unpack1('H*')}"
|
|
142
|
+
line, prefix, owner = reader.normalise_line("owner 60 IN TYPE46 #{generic}", true)
|
|
143
|
+
assert_equal('ZZNSEC', prefix)
|
|
144
|
+
assert_equal('owner.origin.', owner)
|
|
145
|
+
assert_equal(generic, line.split(/\s+/, 5).last.strip)
|
|
146
|
+
assert_equal(signature_bytes, rdata_bytes(Dnsruby::RR.create(line)))
|
|
147
|
+
empty = record(reader, 'empty IN TYPE65000 \\# 0')
|
|
148
|
+
assert_equal(7200, empty.ttl)
|
|
149
|
+
assert_equal(''.b, rdata_bytes(empty))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def rdata_bytes(rr)
|
|
155
|
+
Dnsruby::MessageEncoder.new { |encoder| rr.encode_rdata(encoder, false) }.to_s
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class ReleaseZoneTtlsTest < Minitest::Test
|
|
4
|
+
def test_time_suffixes_in_directives_records_and_soa_intervals
|
|
5
|
+
reader = Dnsruby::ZoneReader.new('example.')
|
|
6
|
+
reader.process_line('$TTL 1H30m')
|
|
7
|
+
inherited = Dnsruby::RR.create(reader.process_line('www IN A 192.0.2.1'))
|
|
8
|
+
assert_equal(5400, inherited.ttl)
|
|
9
|
+
explicit = Dnsruby::RR.create(reader.process_line('mail 2h IN A 192.0.2.2'))
|
|
10
|
+
assert_equal(7200, explicit.ttl)
|
|
11
|
+
soa = Dnsruby::RR.create(reader.process_line('@ IN 1h SOA ns.example. hostmaster.example. 1 2h 10m 1w 5m'))
|
|
12
|
+
assert_equal([3600, 7200, 600, 604800, 300], [soa.ttl, soa.refresh, soa.retry, soa.expire, soa.minimum])
|
|
13
|
+
assert_equal(3630, reader.get_ttl('1h30'))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_class_before_ttl_and_ttl_before_class_are_equivalent
|
|
17
|
+
%w[3600 1h 1H 30m30m].each do |ttl|
|
|
18
|
+
expected = Dnsruby::RR.create(Dnsruby::ZoneReader.new('example.').process_line("www #{ttl} IN A 192.0.2.1"))
|
|
19
|
+
actual = Dnsruby::RR.create(Dnsruby::ZoneReader.new('example.').process_line("www IN #{ttl} A 192.0.2.1"))
|
|
20
|
+
assert_equal(expected, actual)
|
|
21
|
+
assert_equal(expected.ttl, actual.ttl)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_class_before_ttl_preserves_public_normalisation_hook_and_quoted_data
|
|
26
|
+
reader_class = Class.new(Dnsruby::ZoneReader) do
|
|
27
|
+
attr_reader :normalised
|
|
28
|
+
def normalise_line(*args)
|
|
29
|
+
@normalised = true
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
reader = reader_class.new('example.')
|
|
34
|
+
rr = Dnsruby::RR.create(reader.process_line('www IN 1h TXT "one ; two"'))
|
|
35
|
+
assert(reader.normalised)
|
|
36
|
+
assert_equal(3600, rr.ttl)
|
|
37
|
+
assert_equal(['one ; two'], rr.strings)
|
|
38
|
+
end
|
|
39
|
+
end
|
data/test/tc_res_config.rb
CHANGED
|
@@ -15,8 +15,22 @@
|
|
|
15
15
|
# ++
|
|
16
16
|
|
|
17
17
|
require_relative 'spec_helper'
|
|
18
|
+
require 'minitest/mock'
|
|
19
|
+
require_relative 'local_dns_server'
|
|
18
20
|
|
|
19
21
|
class TestResolverConfig < Minitest::Test
|
|
22
|
+
include LocalDnsServer
|
|
23
|
+
|
|
24
|
+
BAD_SERVER = 'invalid-nameserver.test.'.freeze
|
|
25
|
+
|
|
26
|
+
def without_external_nameserver_lookup
|
|
27
|
+
original = Dnsruby::Config.method(:resolve_server)
|
|
28
|
+
lookup = lambda do |server|
|
|
29
|
+
raise ArgumentError, 'deliberately unusable nameserver fixture' if server == BAD_SERVER
|
|
30
|
+
original.call(server)
|
|
31
|
+
end
|
|
32
|
+
Dnsruby::Config.stub(:resolve_server, lookup) { yield }
|
|
33
|
+
end
|
|
20
34
|
|
|
21
35
|
GoodInput = {
|
|
22
36
|
"port" => 54,
|
|
@@ -73,7 +87,7 @@ class TestResolverConfig < Minitest::Test
|
|
|
73
87
|
end
|
|
74
88
|
|
|
75
89
|
def test_single_resolver
|
|
76
|
-
[Dnsruby::SingleResolver.new()].each {|res|
|
|
90
|
+
[Dnsruby::SingleResolver.new('127.0.0.1')].each {|res|
|
|
77
91
|
# [Dnsruby::SingleResolver.new({:nameserver => ["127.0.0.1"]}),
|
|
78
92
|
# Dnsruby::SingleResolver.new({:nameserver => ["::1"]})].each {|res|
|
|
79
93
|
GoodInput.each do | param, value |
|
|
@@ -85,7 +99,7 @@ class TestResolverConfig < Minitest::Test
|
|
|
85
99
|
end
|
|
86
100
|
|
|
87
101
|
def test_dns
|
|
88
|
-
res = Dnsruby::DNS.new
|
|
102
|
+
res = Dnsruby::DNS.new(nameserver: ['127.0.0.1'])
|
|
89
103
|
LookupInput.each do | param, value |
|
|
90
104
|
res.config.send(param+"=", value)
|
|
91
105
|
assert_equal(res.config.send(param), value, "setting #param sticks");
|
|
@@ -93,32 +107,40 @@ class TestResolverConfig < Minitest::Test
|
|
|
93
107
|
end
|
|
94
108
|
|
|
95
109
|
def test_bad_ns
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# OK
|
|
110
|
+
without_external_nameserver_lookup do
|
|
111
|
+
resolver = Dnsruby::Resolver.new(nameserver: BAD_SERVER, query_timeout: 0.1, do_caching: false)
|
|
112
|
+
assert_raises(Dnsruby::ResolvTimeout) { resolver.query('example.test.') }
|
|
113
|
+
ensure
|
|
114
|
+
resolver&.close
|
|
102
115
|
end
|
|
103
116
|
end
|
|
104
117
|
|
|
105
118
|
def test_bad_ns_post_init
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
resolver.nameserver = [
|
|
109
|
-
|
|
110
|
-
resolver.query("example.com")
|
|
111
|
-
rescue ArgumentError
|
|
119
|
+
resolver = Dnsruby::Resolver.new(nameserver: '127.0.0.1')
|
|
120
|
+
without_external_nameserver_lookup do
|
|
121
|
+
assert_raises(ArgumentError) { resolver.nameserver = [BAD_SERVER] }
|
|
122
|
+
assert_equal(['127.0.0.1'], resolver.single_resolvers.map(&:server))
|
|
112
123
|
end
|
|
124
|
+
ensure
|
|
125
|
+
resolver&.close
|
|
113
126
|
end
|
|
114
127
|
|
|
115
128
|
def test_one_good_ns_rest_bad
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
without_external_nameserver_lookup do
|
|
130
|
+
with_local_dns_server(:udp, answer: 'example.test. 60 IN A 192.0.2.42') do |resolver|
|
|
131
|
+
resolver.recurse = false
|
|
132
|
+
resolver.nameserver = [BAD_SERVER, '127.0.0.1']
|
|
133
|
+
assert_equal(['127.0.0.1'], resolver.single_resolvers.map(&:server))
|
|
134
|
+
response = resolver.query('example.test.')
|
|
135
|
+
assert_equal('192.0.2.42', response.answer.first.address.to_s)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_actual_servfail_is_reported
|
|
141
|
+
with_local_dns_server(:udp, rcode: Dnsruby::RCode::SERVFAIL) do |resolver|
|
|
142
|
+
error = assert_raises(Dnsruby::ServFail) { resolver.query('example.test.') }
|
|
143
|
+
assert_equal('SERVFAIL', error.response.rcode.to_s)
|
|
122
144
|
end
|
|
123
145
|
end
|
|
124
146
|
end
|
data/test/tc_res_opt.rb
CHANGED
|
@@ -15,11 +15,31 @@
|
|
|
15
15
|
# ++
|
|
16
16
|
|
|
17
17
|
require_relative 'spec_helper'
|
|
18
|
+
require 'minitest/mock'
|
|
18
19
|
|
|
19
20
|
class TestResOpt < Minitest::Test
|
|
20
21
|
|
|
21
22
|
include Dnsruby
|
|
22
23
|
|
|
24
|
+
def test_source_port_selection_uses_secure_randomness_with_existing_ranges
|
|
25
|
+
sender = PacketSender.new(server: '127.0.0.1')
|
|
26
|
+
[0, 15_535].each do |offset|
|
|
27
|
+
SecureRandom.stub(:random_number, lambda { |count| assert_equal(15_536, count); offset }) do
|
|
28
|
+
assert_equal(50_000 + offset, sender.get_next_src_port)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
sender.src_port = [56_353, 60_001, 65_535]
|
|
32
|
+
3.times do |index|
|
|
33
|
+
SecureRandom.stub(:random_number, lambda { |count| assert_equal(3, count); index }) do
|
|
34
|
+
assert_equal(sender.src_port[index], sender.get_next_src_port)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
sender.src_port = 56_353
|
|
38
|
+
assert_equal(56_353, sender.get_next_src_port)
|
|
39
|
+
ensure
|
|
40
|
+
sender&.close
|
|
41
|
+
end
|
|
42
|
+
|
|
23
43
|
def test_dns_file
|
|
24
44
|
|
|
25
45
|
# .txt because this test will run under windows, unlike the other file
|
|
@@ -184,4 +204,4 @@ class TestResOpt < Minitest::Test
|
|
|
184
204
|
# Dnsruby.log.level=Logger::ERROR
|
|
185
205
|
end
|
|
186
206
|
end
|
|
187
|
-
end
|
|
207
|
+
end
|
data/test/tc_resolver.rb
CHANGED
|
@@ -14,17 +14,14 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
# ++
|
|
16
16
|
require_relative 'spec_helper'
|
|
17
|
+
require_relative 'local_dns_server'
|
|
17
18
|
|
|
18
19
|
require 'socket'
|
|
19
20
|
|
|
20
|
-
# @TODO@ We also need a test server so we can control behaviour of server to test
|
|
21
|
-
# different aspects of retry strategy.
|
|
22
|
-
# Of course, with Ruby's limit of 256 open sockets per process, we'd need to run
|
|
23
|
-
# the server in a different Ruby process.
|
|
24
|
-
|
|
25
21
|
class TestResolver < Minitest::Test
|
|
26
22
|
|
|
27
23
|
include Dnsruby
|
|
24
|
+
include LocalDnsServer
|
|
28
25
|
|
|
29
26
|
Thread::abort_on_exception = true
|
|
30
27
|
|
|
@@ -170,13 +167,19 @@ class TestResolver < Minitest::Test
|
|
|
170
167
|
# end
|
|
171
168
|
#
|
|
172
169
|
def test_nxdomain
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
170
|
+
with_local_dns_server(:udp, rcode: RCode.NXDOMAIN) do |resolver|
|
|
171
|
+
message = Message.new(BAD_DOMAIN_NAME, Types.A)
|
|
172
|
+
message.do_caching = false
|
|
173
|
+
message.do_validation = false
|
|
174
|
+
q = Queue.new
|
|
175
|
+
resolver.send_async(message, q, 1)
|
|
176
|
+
id, response, error = q.pop
|
|
177
|
+
assert_equal(1, id)
|
|
178
|
+
assert_error_is_exception(error, NXDomain)
|
|
179
|
+
assert_valid_response(response)
|
|
180
|
+
assert_equal(RCode.NXDOMAIN, response.rcode)
|
|
181
|
+
assert_equal(message.header.id, response.header.id)
|
|
182
|
+
end
|
|
180
183
|
end
|
|
181
184
|
|
|
182
185
|
def test_timeouts
|
|
@@ -319,16 +322,16 @@ class TestResolver < Minitest::Test
|
|
|
319
322
|
|
|
320
323
|
def test_custom_udp_size
|
|
321
324
|
[false, true].each do |dnssec_setting|
|
|
322
|
-
[512, 513].each do |udp_size_setting|
|
|
325
|
+
[512, 513, 1220, 1232, 4096].each do |udp_size_setting|
|
|
323
326
|
res = Dnsruby::Resolver.new(:udp_size => udp_size_setting, :dnssec => dnssec_setting)
|
|
324
327
|
sent_msg = capture_sent_message(res, "example.com")
|
|
325
328
|
|
|
326
329
|
# The OPT RR is only present if the udp_size is > 512
|
|
327
330
|
opt = sent_msg.get_opt
|
|
328
331
|
if dnssec_setting
|
|
329
|
-
# DNSSEC
|
|
332
|
+
# The resolver's minimum UDP size with DNSSEC enabled is 1220.
|
|
330
333
|
assert(opt)
|
|
331
|
-
assert_equal(
|
|
334
|
+
assert_equal([1220, udp_size_setting].max, opt.klass.code)
|
|
332
335
|
else
|
|
333
336
|
if udp_size_setting <= 512
|
|
334
337
|
assert_nil(opt)
|
|
@@ -339,7 +342,7 @@ class TestResolver < Minitest::Test
|
|
|
339
342
|
end
|
|
340
343
|
if opt
|
|
341
344
|
assert_equal(0, opt.version)
|
|
342
|
-
assert_equal(0, opt.flags)
|
|
345
|
+
assert_equal(dnssec_setting ? 0x8000 : 0, opt.flags)
|
|
343
346
|
end
|
|
344
347
|
end
|
|
345
348
|
end
|
|
@@ -358,7 +361,7 @@ class TestResolver < Minitest::Test
|
|
|
358
361
|
assert_equal(1, sent_msg.additional.length) # No duplicate added
|
|
359
362
|
assert_equal(pre_size, opt.klass.code) # Original payload size unchanged
|
|
360
363
|
assert_equal(0, opt.version)
|
|
361
|
-
assert_equal(0, opt.flags)
|
|
364
|
+
assert_equal(dnssec_setting ? 0x8000 : 0, opt.flags)
|
|
362
365
|
end
|
|
363
366
|
end
|
|
364
367
|
end
|
|
@@ -369,7 +372,7 @@ class TestRawQuery < Minitest::Test
|
|
|
369
372
|
|
|
370
373
|
KEY_NAME = 'key-name'
|
|
371
374
|
KEY = '0123456789'
|
|
372
|
-
ALGO = 'hmac-
|
|
375
|
+
ALGO = 'hmac-sha256'
|
|
373
376
|
|
|
374
377
|
class CustomError < RuntimeError; end
|
|
375
378
|
|
|
@@ -491,4 +494,3 @@ class TestRawQuery < Minitest::Test
|
|
|
491
494
|
res.query("a.root-servers.net.", "A")
|
|
492
495
|
end
|
|
493
496
|
end
|
|
494
|
-
|