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
data/test/tc_escapedchars.rb
CHANGED
|
@@ -20,6 +20,14 @@ class TestEscapedChars < Minitest::Test
|
|
|
20
20
|
|
|
21
21
|
include Dnsruby
|
|
22
22
|
|
|
23
|
+
def setup
|
|
24
|
+
@original_label_limit = Name::Label.class_variable_get(:@@max_length)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def teardown
|
|
28
|
+
Name::Label.set_max_length(@original_label_limit)
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
def test_one
|
|
24
32
|
Name::Label.set_max_length(150)
|
|
25
33
|
#
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class FinalReviewFixesTest < Minitest::Test
|
|
4
|
+
def test_source_port_replacement_is_published_after_enumeration
|
|
5
|
+
sender = Dnsruby::PacketSender.new(server: '127.0.0.1', src_port: 60_001)
|
|
6
|
+
ports = Object.new
|
|
7
|
+
seen = []
|
|
8
|
+
ports.define_singleton_method(:each) do |&block|
|
|
9
|
+
[60_002, 60_003].each do |port|
|
|
10
|
+
seen << sender.get_next_src_port
|
|
11
|
+
block.call(port)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
sender.src_port = ports
|
|
15
|
+
assert_equal([60_001, 60_001], seen)
|
|
16
|
+
assert_equal([60_002, 60_003], sender.src_port)
|
|
17
|
+
assert_raises(ArgumentError) { sender.src_port = [] }
|
|
18
|
+
assert_equal([60_002, 60_003], sender.src_port)
|
|
19
|
+
assert_raises(ArgumentError) { sender.src_port = [60_004, 53] }
|
|
20
|
+
assert_equal([60_002, 60_003], sender.src_port)
|
|
21
|
+
sender.src_port << 60_004
|
|
22
|
+
assert_equal([60_002, 60_003, 60_004], sender.src_port)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_compression_preserves_label_boundaries_and_case
|
|
26
|
+
names = ['a\\.b.example.', 'a.b.example.', 'A.B.example.', 'other.example.']
|
|
27
|
+
message = Dnsruby::Message.new
|
|
28
|
+
names.each { |name| message.add_question(name, 'A') }
|
|
29
|
+
decoded = Dnsruby::Message.decode(message.encode)
|
|
30
|
+
assert_equal(names.map { |name| Dnsruby::Name.create(name).labels.map(&:string) },
|
|
31
|
+
decoded.question.map { |question| question.qname.labels.map(&:string) })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_txt_round_trip_preserves_utf8_and_decimal_escape_octets
|
|
35
|
+
['café', 'é' * 127, '"café" "\\255"'].each do |text|
|
|
36
|
+
record = Dnsruby::RR.create(name: 'text.example.', type: 'TXT', strings: text)
|
|
37
|
+
unless text.start_with?('"')
|
|
38
|
+
assert_equal(text, record.data)
|
|
39
|
+
assert_equal(text.encoding, record.data.encoding)
|
|
40
|
+
end
|
|
41
|
+
message = Dnsruby::Message.new
|
|
42
|
+
message.add_answer(record)
|
|
43
|
+
decoded = Dnsruby::Message.decode(message.encode).answer.first
|
|
44
|
+
assert_equal(record.strings.map(&:b), decoded.strings)
|
|
45
|
+
reparsed = Dnsruby::RR.create(decoded.to_s)
|
|
46
|
+
assert_equal(decoded.strings, reparsed.strings)
|
|
47
|
+
end
|
|
48
|
+
commented = Dnsruby::RR.create(name: 'text.example.', type: 'TXT', strings: '"café" ; comment')
|
|
49
|
+
assert_equal('café', commented.data)
|
|
50
|
+
assert_equal(Encoding::UTF_8, commented.data.encoding)
|
|
51
|
+
record = Dnsruby::RR.create(name: 'text.example.', type: 'TXT', strings: 'é' * 128)
|
|
52
|
+
assert_equal('é' * 128, record.data)
|
|
53
|
+
message = Dnsruby::Message.new
|
|
54
|
+
message.add_answer(record)
|
|
55
|
+
assert_raises(Dnsruby::EncodeError) { message.encode }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_txt_can_split_a_utf8_sequence_at_a_wire_string_boundary
|
|
59
|
+
text = 'é' * 128
|
|
60
|
+
presentation = '"'.b + text.b.byteslice(0, 255) + '" "' + text.b.byteslice(255, 1) + '"'
|
|
61
|
+
record = Dnsruby::RR.create(name: 'text.example.', type: 'TXT', strings: presentation)
|
|
62
|
+
assert_equal(text.b, record.data)
|
|
63
|
+
assert_equal([255, 1], record.strings.map(&:bytesize))
|
|
64
|
+
message = Dnsruby::Message.new
|
|
65
|
+
message.add_answer(record)
|
|
66
|
+
decoded = Dnsruby::Message.decode(message.encode).answer.first
|
|
67
|
+
assert_equal(text.b, decoded.data)
|
|
68
|
+
assert_equal(record.strings, decoded.strings)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_ds_family_preserves_private_algorithm_and_unknown_digest_numbers
|
|
72
|
+
%w[DS CDS DLV].each do |type|
|
|
73
|
+
record = Dnsruby::RR.create("example. 60 IN #{type} 12345 253 253 01020304")
|
|
74
|
+
message = Dnsruby::Message.new
|
|
75
|
+
message.add_answer(record)
|
|
76
|
+
decoded = Dnsruby::Message.decode(message.encode).answer.first
|
|
77
|
+
assert_equal(253, decoded.algorithm.code)
|
|
78
|
+
assert_equal(253, decoded.digest_type.code)
|
|
79
|
+
assert_equal(record.digestbin, decoded.digestbin)
|
|
80
|
+
assert_raises(Dnsruby::DecodeError) { record.algorithm = 256 }
|
|
81
|
+
assert_raises(Dnsruby::DecodeError) { record.digest_type = -1 }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_zone_reader_qualifies_relative_origins_soa_and_literal_final_dots
|
|
86
|
+
reader = Dnsruby::ZoneReader.new('example.', 60)
|
|
87
|
+
reader.process_line('$ORIGIN sub')
|
|
88
|
+
soa = Dnsruby::RR.create(reader.process_line('@ IN SOA ns hostmaster 1 60 60 60 60'))
|
|
89
|
+
assert_equal('sub.example.', soa.name.to_s(true))
|
|
90
|
+
assert_equal('ns.sub.example.', soa.mname.to_s(true))
|
|
91
|
+
assert_equal('hostmaster.sub.example.', soa.rname.to_s(true))
|
|
92
|
+
record = Dnsruby::RR.create(reader.process_line('owner\\. IN CNAME target\\.'))
|
|
93
|
+
assert_equal(['owner.', 'sub', 'example'], record.name.labels.map(&:string))
|
|
94
|
+
assert_equal(['target.', 'sub', 'example'], record.cname.labels.map(&:string))
|
|
95
|
+
reader.process_line('$ORIGIN elsewhere.')
|
|
96
|
+
record = Dnsruby::RR.create(reader.process_line('@ IN NS ns'))
|
|
97
|
+
assert_equal('elsewhere.', record.name.to_s(true))
|
|
98
|
+
assert_equal('ns.elsewhere.', record.nsdname.to_s(true))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_zone_reader_preserves_backslashes_when_replacing_names
|
|
102
|
+
origin = Dnsruby::Name.new(['slash\\origin', 'example'].map { |label| Dnsruby::Name::Label.new(label) }, true)
|
|
103
|
+
reader = Dnsruby::ZoneReader.new(origin, 60)
|
|
104
|
+
target = Dnsruby::Name.new([Dnsruby::Name::Label.new('label\\&')], false)
|
|
105
|
+
record = Dnsruby::RR.create(reader.process_line("#{target.to_s} IN CNAME @"))
|
|
106
|
+
assert_equal(target.labels + origin.labels, record.name.labels)
|
|
107
|
+
assert_equal(origin, record.cname)
|
|
108
|
+
record = Dnsruby::RR.create(reader.process_line('@ IN NS ns'))
|
|
109
|
+
assert_equal(origin, record.name)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_positive_cache_scope_separates_upstream_and_request_context
|
|
113
|
+
sender = Dnsruby::PacketSender.new(server: '192.0.2.53', dnssec: false)
|
|
114
|
+
query = Dnsruby::Message.new('answer.example.', 'A')
|
|
115
|
+
sender.make_query_packet(query)
|
|
116
|
+
scope = sender.positive_cache_scope(query)
|
|
117
|
+
query.header.id = (query.header.id + 1) % 65_536
|
|
118
|
+
assert_equal(scope, sender.positive_cache_scope(query))
|
|
119
|
+
cache = Dnsruby::Cache.new
|
|
120
|
+
response = Dnsruby::Message.new('answer.example.', 'A')
|
|
121
|
+
response.add_answer(Dnsruby::RR.create('answer.example. 60 IN A 192.0.2.1'))
|
|
122
|
+
cache.add(response, scope: scope)
|
|
123
|
+
assert(cache.find('answer.example.', 'A', scope: sender.positive_cache_scope(query)).cached)
|
|
124
|
+
sender.port = 5353
|
|
125
|
+
assert_nil(cache.find('answer.example.', 'A', scope: sender.positive_cache_scope(query)))
|
|
126
|
+
sender.port = 53
|
|
127
|
+
sender.server = '192.0.2.54'
|
|
128
|
+
assert_nil(cache.find('answer.example.', 'A', scope: sender.positive_cache_scope(query)))
|
|
129
|
+
sender.server = '192.0.2.53'
|
|
130
|
+
query.get_opt.dnssec_ok = true
|
|
131
|
+
assert_nil(cache.find('answer.example.', 'A', scope: sender.positive_cache_scope(query)))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_truncated_answers_are_never_cached
|
|
135
|
+
cache = Dnsruby::Cache.new
|
|
136
|
+
response = Dnsruby::Message.new('answer.example.', 'A')
|
|
137
|
+
response.add_answer(Dnsruby::RR.create('answer.example. 60 IN A 192.0.2.1'))
|
|
138
|
+
response.header.tc = true
|
|
139
|
+
assert_nil(cache.add(response))
|
|
140
|
+
assert_nil(cache.find('answer.example.', 'A'))
|
|
141
|
+
response.header.tc = false
|
|
142
|
+
refute_nil(cache.add(response))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_referral_keeps_one_enclosing_delegation_and_sibling_glue
|
|
146
|
+
recursor = Dnsruby::Recursor.allocate
|
|
147
|
+
response = Dnsruby::Message.new('www.child.example.', 'A')
|
|
148
|
+
response.add_authority(Dnsruby::RR.create('child.example. 60 IN NS ns.sibling.example.'))
|
|
149
|
+
response.add_authority(Dnsruby::RR.create('other.example. 10 IN NS ns.other.example.'))
|
|
150
|
+
response.add_additional(Dnsruby::RR.create('ns.sibling.example. 120 IN A 192.0.2.53'))
|
|
151
|
+
response.add_additional(Dnsruby::RR.create('ns.other.example. 120 IN A 192.0.2.54'))
|
|
152
|
+
zone, authorities, ttl = recursor.referral_authorities(response, 'example.', 'www.child.example.', 'IN')
|
|
153
|
+
assert_equal('child.example.', zone)
|
|
154
|
+
assert_equal(['ns.sibling.example.'], authorities.keys)
|
|
155
|
+
assert_equal(['192.0.2.53'], authorities.values.first.values)
|
|
156
|
+
assert_equal(60, ttl)
|
|
157
|
+
relative = recursor.referral_authorities(response, 'example.', 'www.child.example', 'IN')
|
|
158
|
+
assert_equal(zone, relative.first)
|
|
159
|
+
at_delegation = recursor.referral_authorities(response, 'example.', 'child.example', 'IN')
|
|
160
|
+
assert_equal(zone, at_delegation.first)
|
|
161
|
+
assert_nil(recursor.referral_authorities(response, 'child.example.', 'www.child.example.', 'IN'))
|
|
162
|
+
assert_nil(recursor.referral_authorities(response, 'example.', 'www.unrelated.example.', 'IN'))
|
|
163
|
+
response.authority.clear
|
|
164
|
+
response.add_authority(Dnsruby::RR.create('child.example. 60 IN SOA ns.child.example. hostmaster.child.example. 1 60 60 60 60'))
|
|
165
|
+
assert_nil(recursor.referral_authorities(response, 'example.', 'www.child.example.', 'IN'))
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
require 'minitest/mock'
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'timeout'
|
|
5
|
+
|
|
6
|
+
class FinalTransportContractsTest < Minitest::Test
|
|
7
|
+
def test_single_resolver_replacement_preserves_concurrent_settings
|
|
8
|
+
resolver = Dnsruby::SingleResolver.new(server: '192.0.2.1', packet_timeout: 5)
|
|
9
|
+
entered, release = Queue.new, Queue.new
|
|
10
|
+
replacement = nil
|
|
11
|
+
original = Dnsruby::PacketSender.method(:new)
|
|
12
|
+
construct = lambda do |*args|
|
|
13
|
+
sender = original.call(*args)
|
|
14
|
+
if sender.server.to_s == '192.0.2.2'
|
|
15
|
+
entered << true
|
|
16
|
+
release.pop
|
|
17
|
+
end
|
|
18
|
+
sender
|
|
19
|
+
end
|
|
20
|
+
Dnsruby::PacketSender.stub(:new, construct) do
|
|
21
|
+
begin
|
|
22
|
+
replacement = Thread.new { resolver.server = '192.0.2.2' }
|
|
23
|
+
Timeout.timeout(5) { entered.pop }
|
|
24
|
+
resolver.packet_timeout = 9
|
|
25
|
+
resolver.port = 5353
|
|
26
|
+
resolver.src_address = '192.0.2.10'
|
|
27
|
+
resolver.dnssec = false
|
|
28
|
+
ensure
|
|
29
|
+
release << true
|
|
30
|
+
Timeout.timeout(5) { replacement&.value }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
sender = resolver.single_resolvers.first
|
|
34
|
+
assert_equal('192.0.2.2', resolver.server)
|
|
35
|
+
[:packet_timeout, :port, :src_address, :dnssec].each do |field|
|
|
36
|
+
assert_equal(resolver.public_send(field), sender.public_send(field), field)
|
|
37
|
+
end
|
|
38
|
+
ensure
|
|
39
|
+
replacement&.kill if replacement&.alive?
|
|
40
|
+
resolver&.close
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_tcp_reply_reports_its_message_size
|
|
44
|
+
# The selector is process-global; isolate its in-memory socket fixture.
|
|
45
|
+
source = <<~'RUBY'
|
|
46
|
+
require 'dnsruby'
|
|
47
|
+
selector_class = Dnsruby::SelectThread
|
|
48
|
+
selector_class.class_variable_set(:@@mutex, Mutex.new)
|
|
49
|
+
selector = selector_class.send(:allocate)
|
|
50
|
+
query = Dnsruby::Message.new('metadata.example.')
|
|
51
|
+
query.do_validation = query.do_caching = false
|
|
52
|
+
response = query.clone
|
|
53
|
+
response.header.qr = true
|
|
54
|
+
response.add_answer(Dnsruby::RR.create('metadata.example. 60 IN A 192.0.2.1'))
|
|
55
|
+
wire = response.encode
|
|
56
|
+
socket = Object.new
|
|
57
|
+
socket.define_singleton_method(:getsockopt) { |*| Struct.new(:data).new([Socket::SOCK_STREAM].pack('i')) }
|
|
58
|
+
socket.define_singleton_method(:closed?) { false }
|
|
59
|
+
selector.define_singleton_method(:tcp_read) { |_| wire }
|
|
60
|
+
settings = selector_class::QuerySettings.new(query.encode, query, false, Queue.new, :metadata,
|
|
61
|
+
socket, '192.0.2.53', 53, Dnsruby::TransportClock.now + 1, 512, nil)
|
|
62
|
+
selector_class.class_variable_set(:@@query_hash, {metadata: settings})
|
|
63
|
+
selector_class.class_variable_set(:@@socket_hash, {socket => [:metadata]})
|
|
64
|
+
decoded, = selector.get_incoming_data(socket, 512)
|
|
65
|
+
raise 'Wrong TCP size' unless decoded.answersize == wire.bytesize
|
|
66
|
+
raise 'Wrong TCP peer' unless decoded.answerip == '192.0.2.53'
|
|
67
|
+
RUBY
|
|
68
|
+
out, err, status = Open3.capture3(RbConfig.ruby, '-Ilib', '-e', source,
|
|
69
|
+
chdir: File.expand_path('..', __dir__))
|
|
70
|
+
assert(status.success?, "#{out}\n#{err}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_zone_transfer_preserves_authenticated_server_errors
|
|
74
|
+
[Dnsruby::RCode::BADTIME, 22].each do |error_code|
|
|
75
|
+
key = Dnsruby::RR.create(name: 'transfer-key.example.', type: 'TSIG', klass: 'ANY',
|
|
76
|
+
key: ['test-only transfer secret'].pack('m0'), algorithm: 'hmac-sha256')
|
|
77
|
+
transfer = Dnsruby::ZoneTransfer.new
|
|
78
|
+
transfer.tsig = key
|
|
79
|
+
request = Dnsruby::Message.new('example.', 'AXFR')
|
|
80
|
+
sink = Object.new
|
|
81
|
+
sink.define_singleton_method(:write_nonblock) { |data| data.bytesize }
|
|
82
|
+
transfer.send_message(sink, request)
|
|
83
|
+
response = Dnsruby::Message.new('example.', 'AXFR')
|
|
84
|
+
response.header.qr = true
|
|
85
|
+
response.header.rcode = Dnsruby::RCode::NOTAUTH
|
|
86
|
+
signer = key.dup
|
|
87
|
+
signer.error = error_code
|
|
88
|
+
server_time = Time.now.to_i + 3600
|
|
89
|
+
if error_code == Dnsruby::RCode::BADTIME
|
|
90
|
+
signer.other_data = [server_time >> 32, server_time & 0xffffffff].pack('nN')
|
|
91
|
+
signer.other_size = 6
|
|
92
|
+
end
|
|
93
|
+
signer.apply(response, request)
|
|
94
|
+
wire = response.encode
|
|
95
|
+
frame = [wire.bytesize].pack('n') + wire
|
|
96
|
+
socket = Object.new
|
|
97
|
+
socket.define_singleton_method(:read_nonblock) { |size| frame.slice!(0, size) }
|
|
98
|
+
error = assert_raises(Dnsruby::TsigError) { transfer.receive_message(socket) }
|
|
99
|
+
assert_equal(:Verified, transfer.last_tsigstate)
|
|
100
|
+
assert_includes(error.message, error_code == Dnsruby::RCode::BADTIME ? 'BADTIME' : 'BADTRUNC')
|
|
101
|
+
assert_includes(error.message, server_time.to_s) if error_code == Dnsruby::RCode::BADTIME
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|