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
|
@@ -20,7 +20,7 @@ require 'dnsruby/resource/resource'
|
|
|
20
20
|
module Dnsruby
|
|
21
21
|
# ===Defines a DNS packet.
|
|
22
22
|
#
|
|
23
|
-
# RFC 1035 Section 4.1, RFC 2136 Section 2, RFC
|
|
23
|
+
# RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 8945
|
|
24
24
|
#
|
|
25
25
|
# ===Sections
|
|
26
26
|
# Message objects have five sections:
|
|
@@ -102,7 +102,23 @@ module Dnsruby
|
|
|
102
102
|
# * Dnsruby::Message.new('example.com', Dnsruby::Types.PTR, 'HS')
|
|
103
103
|
#
|
|
104
104
|
def initialize(*args)
|
|
105
|
-
|
|
105
|
+
initialize_fields(Header.new)
|
|
106
|
+
type = Types::A
|
|
107
|
+
klass = Classes::IN
|
|
108
|
+
if (args.length > 0)
|
|
109
|
+
name = args[0]
|
|
110
|
+
if (args.length > 1)
|
|
111
|
+
type = Types.new(args[1])
|
|
112
|
+
if (args.length > 2)
|
|
113
|
+
klass = Classes.new(args[2])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
add_question(name, type, klass)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private def initialize_fields(header)
|
|
121
|
+
@header = header
|
|
106
122
|
# @question = Section.new(self)
|
|
107
123
|
@question = []
|
|
108
124
|
@answer = Section.new(self)
|
|
@@ -119,18 +135,6 @@ module Dnsruby
|
|
|
119
135
|
@security_level = SecurityLevel.UNCHECKED
|
|
120
136
|
@security_error = nil
|
|
121
137
|
@cached = false
|
|
122
|
-
type = Types::A
|
|
123
|
-
klass = Classes::IN
|
|
124
|
-
if (args.length > 0)
|
|
125
|
-
name = args[0]
|
|
126
|
-
if (args.length > 1)
|
|
127
|
-
type = Types.new(args[1])
|
|
128
|
-
if (args.length > 2)
|
|
129
|
-
klass = Classes.new(args[2])
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
add_question(name, type, klass)
|
|
133
|
-
end
|
|
134
138
|
end
|
|
135
139
|
|
|
136
140
|
# The question section, an array of Dnsruby::Question objects.
|
|
@@ -365,6 +369,8 @@ module Dnsruby
|
|
|
365
369
|
# Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG
|
|
366
370
|
# object, or it can be a (name, key) tuple, or it can be a hash which takes
|
|
367
371
|
# Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
|
|
372
|
+
# Defaults to hmac-sha256; the algorithm must match the server's key.
|
|
373
|
+
# You can select hmac-md5 for legacy servers, which logs a warning.
|
|
368
374
|
def set_tsig(*args)
|
|
369
375
|
if args.length == 1
|
|
370
376
|
if args[0].instance_of?(RR::TSIG)
|
|
@@ -541,8 +547,11 @@ module Dnsruby
|
|
|
541
547
|
def encode(canonical=false)
|
|
542
548
|
if @tsigkey && (@tsigstate == :Unsigned) && !@signing
|
|
543
549
|
@signing = true
|
|
544
|
-
|
|
545
|
-
|
|
550
|
+
begin
|
|
551
|
+
sign!
|
|
552
|
+
ensure
|
|
553
|
+
@signing = false
|
|
554
|
+
end
|
|
546
555
|
end
|
|
547
556
|
|
|
548
557
|
return MessageEncoder.new { |msg|
|
|
@@ -562,28 +571,41 @@ module Dnsruby
|
|
|
562
571
|
|
|
563
572
|
# Decode the encoded message
|
|
564
573
|
def Message.decode(m)
|
|
565
|
-
|
|
574
|
+
# Incoming packets already contain an ID; do not allocate and randomize
|
|
575
|
+
# a query header only to replace it with the decoded response header.
|
|
576
|
+
o = Message.allocate
|
|
577
|
+
o.send(:initialize_fields, nil)
|
|
578
|
+
received_at = Time.now.to_i
|
|
566
579
|
begin
|
|
567
580
|
MessageDecoder.new(m) {|msg|
|
|
568
581
|
o.header = Header.new(msg)
|
|
582
|
+
update = o.header.opcode == OpCode.Update
|
|
569
583
|
o.header.qdcount.times {
|
|
570
584
|
question = msg.get_question
|
|
571
585
|
o.question << question
|
|
572
586
|
}
|
|
573
587
|
o.header.ancount.times {
|
|
574
|
-
rr = msg.get_rr
|
|
588
|
+
rr = msg.get_rr(update_section: update ? :prerequisite : false)
|
|
589
|
+
rr.instance_variable_set(:@dnsruby_received_at, received_at)
|
|
590
|
+
if rr.type == Types::TSIG
|
|
591
|
+
raise DecodeError.new('TSIG must be the last record in the additional section')
|
|
592
|
+
end
|
|
575
593
|
o.answer << rr
|
|
576
594
|
}
|
|
577
595
|
o.header.nscount.times {
|
|
578
|
-
rr = msg.get_rr
|
|
596
|
+
rr = msg.get_rr(update_section: update ? :update : false)
|
|
597
|
+
rr.instance_variable_set(:@dnsruby_received_at, received_at)
|
|
598
|
+
if rr.type == Types::TSIG
|
|
599
|
+
raise DecodeError.new('TSIG must be the last record in the additional section')
|
|
600
|
+
end
|
|
579
601
|
o.authority << rr
|
|
580
602
|
}
|
|
581
603
|
o.header.arcount.times { |count|
|
|
582
604
|
start = msg.index
|
|
583
|
-
rr = msg.get_rr
|
|
605
|
+
rr = msg.get_rr(update_section: false)
|
|
606
|
+
rr.instance_variable_set(:@dnsruby_received_at, received_at)
|
|
584
607
|
if rr.type == Types::TSIG
|
|
585
608
|
if count != o.header.arcount-1
|
|
586
|
-
Dnsruby.log.Error('Incoming message has TSIG record before last record')
|
|
587
609
|
raise DecodeError.new('TSIG record present before last record')
|
|
588
610
|
end
|
|
589
611
|
o.tsigstart = start # needed for TSIG verification
|
|
@@ -595,6 +617,7 @@ module Dnsruby
|
|
|
595
617
|
# So we got a decode error
|
|
596
618
|
# However, we might have been able to fill in many parts of the message
|
|
597
619
|
# So let's raise the DecodeError, but add the partially completed message
|
|
620
|
+
o.header ||= Header.new
|
|
598
621
|
e.partial_message = o
|
|
599
622
|
raise e
|
|
600
623
|
end
|
|
@@ -602,7 +625,15 @@ module Dnsruby
|
|
|
602
625
|
end
|
|
603
626
|
|
|
604
627
|
def clone
|
|
605
|
-
Message.decode(self.encode)
|
|
628
|
+
copy = Message.decode(self.encode)
|
|
629
|
+
[answer, authority, additional].zip([copy.answer, copy.authority, copy.additional]) do |original, cloned|
|
|
630
|
+
original.zip(cloned) do |record, clone|
|
|
631
|
+
[:@dnsruby_received_at, :@dnsruby_authenticated_expiration, :@dnsruby_learned_trust].each do |field|
|
|
632
|
+
clone.instance_variable_set(field, record.instance_variable_get(field)) if record.instance_variable_defined?(field)
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
copy
|
|
606
637
|
end
|
|
607
638
|
|
|
608
639
|
# In dynamic update packets, the question section is known as zone and
|
|
@@ -18,11 +18,7 @@ class Section < Array
|
|
|
18
18
|
end
|
|
19
19
|
type_ok && (rr.klass == klass) && (rr.name.to_s(true).downcase == name.to_s().downcase)
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
rrs.each do |rr|
|
|
23
|
-
rrset.add(rr)
|
|
24
|
-
end
|
|
25
|
-
rrset
|
|
21
|
+
RRSet.new(rrs)
|
|
26
22
|
end
|
|
27
23
|
|
|
28
24
|
# Return an array of all the rrsets in the section
|
|
@@ -30,38 +26,20 @@ class Section < Array
|
|
|
30
26
|
if type && !(Types === type)
|
|
31
27
|
type = Types.new(type)
|
|
32
28
|
end
|
|
33
|
-
|
|
29
|
+
groups = {}
|
|
30
|
+
budget = ValidationBudget.current if defined?(ValidationBudget)
|
|
34
31
|
each do |rr|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# end
|
|
40
|
-
if (type)
|
|
41
|
-
# if this is an rrsig type, then :
|
|
42
|
-
# only include it if the type_covered is the type requested,
|
|
43
|
-
# OR if the type requested is an RRSIG
|
|
44
|
-
if rr.type == Types::RRSIG
|
|
45
|
-
if (rr.type_covered == type) || (type == Types::RRSIG)
|
|
46
|
-
else
|
|
47
|
-
next
|
|
48
|
-
end
|
|
49
|
-
# next if ((rr.type_covered != type) || (type != Types.RRSIG))
|
|
50
|
-
elsif rr.type != type
|
|
51
|
-
next
|
|
52
|
-
end
|
|
53
|
-
end
|
|
32
|
+
budget&.check!(1)
|
|
33
|
+
next if !include_opt && rr.type == Types::OPT
|
|
34
|
+
covered_type = rr.type == Types::RRSIG ? rr.type_covered : rr.type
|
|
35
|
+
next if type && rr.type != type && covered_type != type
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
unless found_rrset
|
|
61
|
-
ret.push(RRSet.new(rr))
|
|
62
|
-
end
|
|
37
|
+
# Hash insertion order preserves the section's first occurrence of each
|
|
38
|
+
# set. A signature joins the set it covers even when it appears first.
|
|
39
|
+
key = [rr.name.canonical, rr.klass.code, covered_type.code]
|
|
40
|
+
(groups[key] ||= []) << rr
|
|
63
41
|
end
|
|
64
|
-
|
|
42
|
+
groups.values.map { |records| RRSet.new(records) }
|
|
65
43
|
end
|
|
66
44
|
|
|
67
45
|
def ==(other)
|
data/lib/dnsruby/name.rb
CHANGED
|
@@ -54,7 +54,7 @@ module Dnsruby
|
|
|
54
54
|
return Name.new([],false)
|
|
55
55
|
end
|
|
56
56
|
arg = punycode(arg)
|
|
57
|
-
return Name.new(split_escaped(arg),
|
|
57
|
+
return Name.new(split_escaped(arg), /(?<!\\)(?:\\\\)*\.\z/.match?(arg))
|
|
58
58
|
# return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
|
|
59
59
|
when Array
|
|
60
60
|
return Name.new(arg, /\.\z/ =~ (arg.last ? ((arg.last.kind_of?String)?arg.last : arg.last.string) : arg.last) ? true : false)
|
|
@@ -93,7 +93,10 @@ module Dnsruby
|
|
|
93
93
|
# This method should only be called internally.
|
|
94
94
|
# Use Name::create to create a new Name
|
|
95
95
|
def initialize(labels, absolute=true) #:nodoc: all
|
|
96
|
-
|
|
96
|
+
# The 255-octet limit (RFC 1035 section 2.3.4, RFC 2181 section 11) is
|
|
97
|
+
# on the wire form, so count each label's length octet, plus the root's
|
|
98
|
+
# zero octet.
|
|
99
|
+
total_length=labels.length+1
|
|
97
100
|
labels.each do |l|
|
|
98
101
|
if (!l.kind_of?Label)
|
|
99
102
|
raise ArgumentError.new("Name::new called with non-labels. Use Name::create instead?")
|