dnsruby 1.60.2 → 1.73.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 +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +26 -0
- data/.yardopts +7 -0
- data/README.md +8 -5
- data/RELEASE_NOTES.md +105 -0
- data/Rakefile +1 -0
- data/demo/digdlv.rb +1 -0
- data/demo/digroot.rb +2 -0
- data/dnsruby.gemspec +22 -14
- data/lib/dnsruby/DNS.rb +1 -1
- data/lib/dnsruby/bit_mapping.rb +2 -2
- data/lib/dnsruby/code_mappers.rb +6 -2
- data/lib/dnsruby/config.rb +29 -15
- data/lib/dnsruby/dnssec.rb +4 -1
- data/lib/dnsruby/hosts.rb +1 -4
- data/lib/dnsruby/ipv6.rb +5 -5
- data/lib/dnsruby/message/encoder.rb +5 -5
- data/lib/dnsruby/message/header.rb +15 -18
- data/lib/dnsruby/message/message.rb +2 -2
- data/lib/dnsruby/name.rb +19 -3
- data/lib/dnsruby/packet_sender.rb +25 -17
- data/lib/dnsruby/recursor.rb +17 -7
- data/lib/dnsruby/resolver.rb +43 -26
- data/lib/dnsruby/resource/CAA.rb +5 -2
- data/lib/dnsruby/resource/CDNSKEY.rb +17 -0
- data/lib/dnsruby/resource/CDS.rb +35 -0
- data/lib/dnsruby/resource/DNSKEY.rb +53 -13
- data/lib/dnsruby/resource/DS.rb +2 -5
- data/lib/dnsruby/resource/GPOS.rb +1 -1
- data/lib/dnsruby/resource/IN.rb +4 -1
- data/lib/dnsruby/resource/NSEC.rb +2 -2
- data/lib/dnsruby/resource/NSEC3PARAM.rb +1 -1
- data/lib/dnsruby/resource/NXT.rb +1 -1
- data/lib/dnsruby/resource/RR.rb +1 -1
- data/lib/dnsruby/resource/TLSA.rb +3 -3
- data/lib/dnsruby/resource/TXT.rb +12 -2
- data/lib/dnsruby/resource/URI.rb +57 -0
- data/lib/dnsruby/resource/generic.rb +3 -0
- data/lib/dnsruby/select_thread.rb +36 -28
- data/lib/dnsruby/single_verifier.rb +29 -8
- data/lib/dnsruby/validator_thread.rb +4 -4
- data/lib/dnsruby/version.rb +1 -1
- data/lib/dnsruby/zone_reader.rb +2 -2
- data/lib/dnsruby/zone_transfer.rb +5 -2
- data/lib/dnsruby.rb +4 -4
- data/test/localdns.rb +29 -0
- data/test/spec_helper.rb +34 -9
- data/test/tc_caa.rb +12 -1
- data/test/tc_dns.rb +15 -5
- data/test/tc_dnskey.rb +29 -0
- data/test/tc_ecdsa.rb +27 -0
- data/test/tc_encoding.rb +31 -0
- data/test/tc_escapedchars.rb +8 -9
- data/test/tc_gpos.rb +1 -1
- data/test/tc_hs.rb +4 -8
- data/test/tc_long_labels.rb +46 -0
- data/test/tc_message.rb +2 -2
- data/test/tc_name.rb +21 -2
- data/test/tc_nxt.rb +0 -1
- data/test/tc_recur.rb +4 -7
- data/test/tc_resolv.rb +14 -6
- data/test/tc_resolver.rb +114 -11
- data/test/tc_rr-opt.rb +8 -5
- data/test/tc_rr-txt.rb +7 -1
- data/test/tc_rr-unknown.rb +1 -1
- data/test/tc_rr.rb +37 -4
- data/test/tc_single_resolver.rb +9 -17
- data/test/tc_soak.rb +33 -67
- data/test/tc_tcp.rb +2 -2
- data/test/tc_tcp_pipelining.rb +30 -22
- data/test/tc_tkey.rb +1 -7
- data/test/tc_update.rb +0 -1
- data/test/tc_verifier.rb +15 -0
- data/test/test_dnsserver.rb +110 -17
- data/test/test_utils.rb +0 -2
- metadata +83 -46
- data/.travis.yml +0 -14
data/test/tc_rr-unknown.rb
CHANGED
|
@@ -58,7 +58,7 @@ class TestRrUnknown < Minitest::Test
|
|
|
58
58
|
assert_equal('10.0.0.1', rr.address.to_s,'Unknown RR representation for A parsed OK')
|
|
59
59
|
|
|
60
60
|
begin
|
|
61
|
-
|
|
61
|
+
RR.new_from_string('e.example IN A \# 4 0A0000 01 11 ')
|
|
62
62
|
flunk "Should fail on inconsistent length and hex presentation"
|
|
63
63
|
rescue Exception
|
|
64
64
|
# like($@, '/\\\# 4 0A0000 01 11 assert_equal inconsistent\ length does not match content/', 'Fails on inconsassert_equaltent length and hex presentation')
|
data/test/tc_rr.rb
CHANGED
|
@@ -285,11 +285,44 @@ class TestRR < Minitest::Test
|
|
|
285
285
|
update.encode
|
|
286
286
|
end
|
|
287
287
|
|
|
288
|
+
def test_uri
|
|
289
|
+
rrString = "_ftp._tcp.\t300\tIN\tURI\t10\ 1 \"ftp://ftp1.example.com/public\""
|
|
290
|
+
rr = RR.create(rrString)
|
|
291
|
+
assert(rrString.to_s == rr.to_s)
|
|
292
|
+
m = Dnsruby::Message.new
|
|
293
|
+
m.add_additional(rr)
|
|
294
|
+
m2 = Message.decode(m.encode)
|
|
295
|
+
rr2 = m2.additional()[0]
|
|
296
|
+
assert(rr == rr2)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def test_cds
|
|
300
|
+
rrString = "dskey.example.com.\t86400\tIN\tCDS\t60485 RSASHA1 1 ( 2BB183AF5F22588179A53B0A98631FAD1A292118 )"
|
|
301
|
+
rr = RR.create(rrString)
|
|
302
|
+
assert(rrString.to_s == rr.to_s)
|
|
303
|
+
m = Dnsruby::Message.new
|
|
304
|
+
m.add_additional(rr)
|
|
305
|
+
m2 = Message.decode(m.encode)
|
|
306
|
+
rr2 = m2.additional()[0]
|
|
307
|
+
assert(rr.to_s == rr2.to_s)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def test_cdnskey
|
|
311
|
+
rrString = "tjeb.nl.\t3600\tIN\tCDNSKEY\t256 3 RSASHA1-NSEC3-SHA1 ( AwEAAcglEOS7bECRK5fqTuGTMJycmDhTzmUu/EQbAhKJOYJxDb5SG/RYqsJgzG7wgtGy0W1aP7I4k6SPtHmwcqjLaZLVUwRNWCGr2adjb9JTFyBR7F99Ngi11lEGM6Uiw/eDRk66lhoSGzohjj/rmhRTV6gN2+0ADPnafv3MBkPgryA3 ) ; key_tag=53177"
|
|
312
|
+
rr = RR.create(rrString)
|
|
313
|
+
assert(rrString.to_s == rr.to_s)
|
|
314
|
+
m = Dnsruby::Message.new
|
|
315
|
+
m.add_additional(rr)
|
|
316
|
+
m2 = Message.decode(m.encode)
|
|
317
|
+
rr2 = m2.additional()[0]
|
|
318
|
+
assert(rr.to_s == rr2.to_s)
|
|
319
|
+
end
|
|
320
|
+
|
|
288
321
|
def test_cert
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
#
|
|
292
|
-
|
|
322
|
+
RR.create("test.kht.se. 60 IN CERT PGP 0 0 mQGiBDnY2vERBAD3cOxqoAYHYzS+xttvuyN9wZS8CrgwLIlT8Ewo/CCFI11PEO+gJyNPvWPRQsyt1SE60reaIsie2bQTg3DYIg0PmH+ZOlNkpKesPULzdlw4Rx3dD/M3Lkrm977h4Y70ZKC+tbvoYKCCOIkUVevny1PVZ+mB94rb0mMgawSTrct03QCg/w6aHNJFQV7O9ZQ1Fir85M3RS8cEAOo4/1ASVudz3qKZQEhU2Z9O2ydXqpEanHfGirjWYi5RelVsQ9IfBSPFaPAWzQ24nvQ18NU7TgdDQhP4meZXiVXcLBR5Mee2kByf2KAnBUF9aah5s8wZbSrC6u8xEZLuiauvWmCUIWe0Ylc1/L37XeDjrBI2pT+k183X119d6Fr1BACGfZVGsot5rxBUEFPPSrBqYXG/0hRYv9Eq8a4rJAHK2IUWYfivZgL4DtrJnHlha+H5EPQVYkIAN3nGjXoHmosY+J3Sk+GyR+dCBHEwCkoHMKph3igczCEfxAWgqKeYd5mf+QQq2JKrkn2jceiIO7s3CrepeEFAjDSGuxhZjPJVm7QoRGFuaWVsIFAuIE1haG9uZXkgPGRhbm1AcHJpbWUuZ3VzaGkub3JnPohOBBARAgAOBQI52NrxBAsDAQICGQEACgkQ+75aMGJLskn6LgCbBXUD7UmGla5e1zyhuY667hP3F+UAoJIeDZJyRFkQAmb+u8KekRyLD1MLtDJEYW5pZWwgTWFob25leSAoU2Vjb25kYXJ5IEVtYWlsKSA8Z3VzaGlAZ3VzaGkub3JnPohgBBMRAgAgBQJF1J/XAhsjBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ+75aMGJLskkVhACggsivQ9qLhfdA1rGm6f8LRJBSC4wAoI930h+/hshClj6AkNwGRtHdf5XJuQINBDnY2vQQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/9eGjzF2gDh6U7I72x/6bSdlExx2LvIF92OZKc0S55IOS4Lgzs7Hbfm1aOL4oJt7wBg94xkF4cerxz7y8R9J+k3GNl14KOjbYaMAh1rdxdAzikYMH1p1hS78GMtwxky6jE5en87BGGMmnbC84JlxwN+MD7diu8D0Gkgjj/pxOp32D5jEe02wBPVjFTpFLJjpFniLUY6AohRDEdSuZwWPuoKVWhpeWkasNn5qgwGyDREbXpyPsU02BkwE4JiGs+JMMdOn9KMh5dxiuwsMM9gHiQZS3mSNBBKPWI5ZXsdStVFvapjf2FUFDXLUbTROPv1Xhqf0u7YYORFnWeVtvzKIxVaiEYEGBECAAYFAjnY2vQACgkQ+75aMGJLsklBWgCeN7z9xk52y/aoaCuF6hYb0d+3k98AoMRxvHuXI1Nc2FXY/x65PwHiUbaY")
|
|
323
|
+
RR.create("all.rr.org. IN CERT 6 0 0 FFsAyW1dVK7hIGuvhN56r26UwJx/")
|
|
324
|
+
# RR.create("all.rr.org. IN WKS 128.32.0.10 UDP who route timed domain")
|
|
325
|
+
RR.create('selector._domainkey.all.rr.org. IN TXT "v=DKIM1; n=Use=20DKIM; p=AwEAAZfbYw8SffZwsbrCLbC+JLErREIF6Yfe9aqsa1Pz6tpGWiLxm9rSL6/YoBvNP3UWX91YDF0JMo6lhu3UIZjITvIwDhx+RJYko9vLzaaJKXGf3ygy6z+deWoZJAV1lTY0Ltx9genboe88CSCHw9aSLkh0obN9Ck8R6zAMYR19ciM/; t=s"')
|
|
293
326
|
end
|
|
294
327
|
|
|
295
328
|
def test_dhcid
|
data/test/tc_single_resolver.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# --
|
|
2
2
|
# Copyright 2007 Nominet UK
|
|
3
|
-
#
|
|
3
|
+
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
# you may not use this file except in compliance with the License.
|
|
6
6
|
# You may obtain a copy of the License at
|
|
7
|
-
#
|
|
7
|
+
#
|
|
8
8
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
10
|
# Unless required by applicable law or agreed to in writing, software
|
|
11
11
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
12
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
@@ -53,7 +53,7 @@ class TestSingleResolver < Minitest::Test
|
|
|
53
53
|
|
|
54
54
|
def test_simple
|
|
55
55
|
res = SingleResolver.new()
|
|
56
|
-
|
|
56
|
+
res.query("ns1.google.com.")
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def test_timeout
|
|
@@ -71,7 +71,7 @@ class TestSingleResolver < Minitest::Test
|
|
|
71
71
|
res.port = port
|
|
72
72
|
res.packet_timeout=1
|
|
73
73
|
start_time = Time.now.to_i
|
|
74
|
-
|
|
74
|
+
res.query("a.t.net-dns.org")
|
|
75
75
|
fail "Got response when should have got none"
|
|
76
76
|
rescue ResolvTimeout
|
|
77
77
|
stop_time = Time.now.to_i
|
|
@@ -87,7 +87,7 @@ class TestSingleResolver < Minitest::Test
|
|
|
87
87
|
res.packet_timeout=1
|
|
88
88
|
start_time = Time.now.to_i
|
|
89
89
|
# TheLog.level = Logger::DEBUG
|
|
90
|
-
|
|
90
|
+
res.query("a.t.net-dns.org")
|
|
91
91
|
fail "TCP timeouts"
|
|
92
92
|
rescue ResolvTimeout
|
|
93
93
|
# print "Got Timeout for TCP\n"
|
|
@@ -131,15 +131,7 @@ class TestSingleResolver < Minitest::Test
|
|
|
131
131
|
res = SingleResolver.new
|
|
132
132
|
|
|
133
133
|
Rrs.each do |data|
|
|
134
|
-
packet=
|
|
135
|
-
2.times do
|
|
136
|
-
begin
|
|
137
|
-
packet = res.query(data[:name], data[:type])
|
|
138
|
-
rescue ResolvTimeout
|
|
139
|
-
end
|
|
140
|
-
break if packet
|
|
141
|
-
end
|
|
142
|
-
assert(packet)
|
|
134
|
+
packet = with_retries { res.query(data[:name], data[:type]) }
|
|
143
135
|
assert_equal(packet.question[0].qclass, 'IN', 'Class correct')
|
|
144
136
|
|
|
145
137
|
assert(packet, "Got an answer for #{data[:name]} IN #{data[:type]}")
|
|
@@ -189,11 +181,11 @@ class TestSingleResolver < Minitest::Test
|
|
|
189
181
|
def test_res_config
|
|
190
182
|
res = Dnsruby::SingleResolver.new
|
|
191
183
|
|
|
192
|
-
res.server=('a.t.net-dns.org')
|
|
184
|
+
with_retries(exceptions: [ArgumentError], success_check: ->(_) { true }) { res.server=('a.t.net-dns.org') }
|
|
193
185
|
ip = res.server
|
|
194
186
|
assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up IP.')
|
|
195
187
|
|
|
196
|
-
res.server=('cname.t.net-dns.org')
|
|
188
|
+
with_retries(exceptions: [ArgumentError], success_check: ->(_) { true }) { res.server=('cname.t.net-dns.org') }
|
|
197
189
|
ip = res.server
|
|
198
190
|
assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up cname.')
|
|
199
191
|
end
|
data/test/tc_soak.rb
CHANGED
|
@@ -19,72 +19,39 @@ require_relative 'spec_helper'
|
|
|
19
19
|
# require_relative 'tc_single_resolver'
|
|
20
20
|
require_relative 'tc_soak_base'
|
|
21
21
|
require_relative 'test_dnsserver'
|
|
22
|
+
require_relative 'localdns'
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
# This class tries to soak test the Dnsruby library.
|
|
25
26
|
# It can't do this very well, owing to the small number of sockets allowed to be open simultaneously.
|
|
26
27
|
# @TODO@ Future versions of dnsruby will allow random streaming over a fixed number of (cycling) random sockets,
|
|
27
28
|
# so this test can be beefed up considerably at that point.
|
|
28
|
-
# @todo@ A test DNS server running on localhost is really needed here
|
|
29
|
-
|
|
30
|
-
class MyServer < RubyDNS::Server
|
|
31
|
-
|
|
32
|
-
include Dnsruby
|
|
33
|
-
|
|
34
|
-
IP = "127.0.0.1"
|
|
35
|
-
PORT = 53927
|
|
36
|
-
|
|
37
|
-
@@stats = Stats.new
|
|
38
|
-
|
|
39
|
-
def self.stats
|
|
40
|
-
@@stats
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def process(name, resource_class, transaction)
|
|
44
|
-
transaction.respond!("93.184.216.34", { resource_class: Resolv::DNS::Resource::IN::A })
|
|
45
|
-
Celluloid.logger.debug "got message"
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
class PipeliningServer < MyServer
|
|
50
|
-
def run
|
|
51
|
-
fire(:setup)
|
|
52
|
-
|
|
53
|
-
link NioTcpPipeliningHandler.new(self, IP, PORT, 5) #5 max request
|
|
54
|
-
link RubyDNS::UDPHandler.new(self, IP, PORT)
|
|
55
|
-
|
|
56
|
-
fire(:start)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
29
|
|
|
60
30
|
class TestSingleResolverSoak < Minitest::Test
|
|
61
31
|
|
|
62
|
-
IP =
|
|
63
|
-
PORT =
|
|
32
|
+
IP = SimpleTCPPipeliningUDPServer::IP
|
|
33
|
+
PORT = SimpleTCPPipeliningUDPServer::PORT
|
|
64
34
|
|
|
65
35
|
def initialize(arg)
|
|
66
36
|
super(arg)
|
|
67
|
-
self.class.init
|
|
68
37
|
end
|
|
69
38
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
Celluloid.boot
|
|
73
|
-
# By default, Celluloid logs output to console. Use Dnsruby.log instead.
|
|
74
|
-
Celluloid.logger = Dnsruby.log
|
|
75
|
-
@initialized = true
|
|
76
|
-
end
|
|
39
|
+
def teardown
|
|
40
|
+
Celluloid.shutdown
|
|
77
41
|
end
|
|
78
42
|
|
|
79
43
|
SINGLE_RESOLVER_QUERY_TIMES = 63
|
|
80
44
|
|
|
81
45
|
def setup
|
|
82
|
-
# Instantiate a
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
46
|
+
# Instantiate a local dns server
|
|
47
|
+
pipe = IO.popen("./test/localdns.rb")
|
|
48
|
+
@dnspid = pipe.pid
|
|
49
|
+
sleep 1
|
|
50
|
+
end
|
|
87
51
|
|
|
52
|
+
def teardown
|
|
53
|
+
Process.kill("KILL", @dnspid)
|
|
54
|
+
sleep 1
|
|
88
55
|
end
|
|
89
56
|
|
|
90
57
|
def test_many_asynchronous_queries_one_single_resolver
|
|
@@ -115,14 +82,14 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
115
82
|
q = Queue.new
|
|
116
83
|
timeout_count = 0
|
|
117
84
|
resolvers = Array.new(num_resolvers) do
|
|
118
|
-
SingleResolver.new(server: IP,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
85
|
+
Dnsruby::SingleResolver.new(server: IP,
|
|
86
|
+
port: PORT,
|
|
87
|
+
do_caching: false,
|
|
88
|
+
do_validation: false,
|
|
89
|
+
tcp_pipelining: pipelining,
|
|
90
|
+
packet_timeout: 10,
|
|
91
|
+
tcp_pipelining_max_queries: 5,
|
|
92
|
+
use_tcp: tcp)
|
|
126
93
|
end
|
|
127
94
|
start = Time.now
|
|
128
95
|
|
|
@@ -130,7 +97,7 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
130
97
|
# this test while we're not using single sockets.
|
|
131
98
|
# We run four queries per iteration, so we're limited to 64 runs.
|
|
132
99
|
messages = TestSoakBase::Rrs.map do |data|
|
|
133
|
-
message = Message.new(data[:name], data[:type])
|
|
100
|
+
message = Dnsruby::Message.new(data[:name], data[:type])
|
|
134
101
|
message.do_validation = false
|
|
135
102
|
message.do_caching = false
|
|
136
103
|
message
|
|
@@ -141,9 +108,9 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
141
108
|
receive_thread = Thread.new do
|
|
142
109
|
query_count.times do
|
|
143
110
|
_id, ret, error = q.pop
|
|
144
|
-
if error.is_a?(ResolvTimeout)
|
|
111
|
+
if error.is_a?(Dnsruby::ResolvTimeout)
|
|
145
112
|
timeout_count+=1
|
|
146
|
-
elsif ret.class != Message
|
|
113
|
+
elsif ret.class != Dnsruby::Message
|
|
147
114
|
p "ERROR RETURNED : #{error}"
|
|
148
115
|
end
|
|
149
116
|
end
|
|
@@ -193,7 +160,7 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
193
160
|
packet=nil
|
|
194
161
|
begin
|
|
195
162
|
packet = res.query(data[:name], data[:type])
|
|
196
|
-
rescue ResolvTimeout
|
|
163
|
+
rescue Dnsruby::ResolvTimeout
|
|
197
164
|
mutex.synchronize { timeout_count += 1 }
|
|
198
165
|
next
|
|
199
166
|
end
|
|
@@ -248,19 +215,19 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
248
215
|
end
|
|
249
216
|
q = Queue.new
|
|
250
217
|
|
|
251
|
-
message = Message.new(data[:name], data[:type])
|
|
218
|
+
message = Dnsruby::Message.new(data[:name], data[:type])
|
|
252
219
|
message.do_validation = false
|
|
253
220
|
message.do_caching = false
|
|
254
221
|
|
|
255
222
|
res.send_async(message, q, [i,j])
|
|
256
223
|
|
|
257
224
|
id, packet, error = q.pop
|
|
258
|
-
if (error.class == ResolvTimeout)
|
|
225
|
+
if (error.class == Dnsruby::ResolvTimeout)
|
|
259
226
|
mutex.synchronize {
|
|
260
227
|
timeout_count+=1
|
|
261
228
|
}
|
|
262
229
|
next
|
|
263
|
-
elsif (packet.class!=Message)
|
|
230
|
+
elsif (packet.class!=Dnsruby::Message)
|
|
264
231
|
puts "ERROR! #{error}"
|
|
265
232
|
end
|
|
266
233
|
|
|
@@ -278,13 +245,12 @@ class TestSingleResolverSoak < Minitest::Test
|
|
|
278
245
|
assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!")
|
|
279
246
|
end
|
|
280
247
|
|
|
281
|
-
|
|
282
248
|
def create_default_single_resolver
|
|
283
|
-
SingleResolver.new(server: IP,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
249
|
+
Dnsruby::SingleResolver.new(server: IP,
|
|
250
|
+
port: PORT,
|
|
251
|
+
do_caching: false,
|
|
252
|
+
do_validation: false,
|
|
253
|
+
packet_timeout: 10)
|
|
288
254
|
|
|
289
255
|
end
|
|
290
256
|
end
|
data/test/tc_tcp.rb
CHANGED
|
@@ -142,14 +142,14 @@ class TestTcp < Minitest::Test
|
|
|
142
142
|
ans = HackMessage.decode(received_query)
|
|
143
143
|
ans.wipe_additional
|
|
144
144
|
100.times {|i|
|
|
145
|
-
|
|
145
|
+
ans.add_additional(Dnsruby::RR.create("example.com 3600 IN A 1.2.3.#{i}"))
|
|
146
146
|
}
|
|
147
147
|
ans.header.arcount = 110
|
|
148
148
|
ans.header.tc = true
|
|
149
149
|
socket.send(ans.encode,0)
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
_server_thread = Thread.new {
|
|
153
153
|
ts = TCPServer.new(port)
|
|
154
154
|
t = ts.accept
|
|
155
155
|
packet = t.recvfrom(2)[0]
|
data/test/tc_tcp_pipelining.rb
CHANGED
|
@@ -19,7 +19,7 @@ require_relative 'test_dnsserver'
|
|
|
19
19
|
|
|
20
20
|
# The TCPPipeliningServer links our NioTcpPipeliningHandler on
|
|
21
21
|
# the loopback interface.
|
|
22
|
-
class TCPPipeliningServer <
|
|
22
|
+
class TCPPipeliningServer < Async::DNS::Server
|
|
23
23
|
PORT = 53937
|
|
24
24
|
IP = '127.0.0.1'
|
|
25
25
|
|
|
@@ -32,18 +32,18 @@ class TCPPipeliningServer < RubyDNS::Server
|
|
|
32
32
|
@@stats
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def initialize(**options)
|
|
36
|
+
super(options)
|
|
37
|
+
|
|
38
|
+
@handlers = []
|
|
39
|
+
@handlers << NioTcpPipeliningHandler.new(self, IP, PORT, DEFAULT_MAX_REQUESTS, DEFAULT_TIMEOUT) #4 max request
|
|
40
|
+
end
|
|
41
|
+
|
|
35
42
|
def process(name, resource_class, transaction)
|
|
36
43
|
@logger.debug "name: #{name}"
|
|
37
|
-
transaction.respond!("93.184.216.34", { resource_class: Resolv::DNS::Resource::IN::A })
|
|
44
|
+
transaction.respond!("93.184.216.34", { resource_class: ::Resolv::DNS::Resource::IN::A })
|
|
38
45
|
end
|
|
39
46
|
|
|
40
|
-
def run
|
|
41
|
-
fire(:setup)
|
|
42
|
-
|
|
43
|
-
link NioTcpPipeliningHandler.new(self, IP, PORT, DEFAULT_MAX_REQUESTS, DEFAULT_TIMEOUT) #4 max request
|
|
44
|
-
|
|
45
|
-
fire(:start)
|
|
46
|
-
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
class TestTCPPipelining < Minitest::Test
|
|
@@ -54,26 +54,31 @@ class TestTCPPipelining < Minitest::Test
|
|
|
54
54
|
|
|
55
55
|
def self.init
|
|
56
56
|
unless @initialized
|
|
57
|
-
Celluloid.boot
|
|
58
|
-
# By default, Celluloid logs output to console. Use Dnsruby.log instead
|
|
59
|
-
Celluloid.logger = Dnsruby.log
|
|
60
|
-
#Dnsruby.log.level = Logger::ERROR
|
|
61
57
|
@initialized = true
|
|
62
58
|
@query_id = 0
|
|
63
59
|
end
|
|
64
60
|
end
|
|
65
61
|
|
|
62
|
+
@@server = nil
|
|
63
|
+
|
|
66
64
|
def setup
|
|
65
|
+
return
|
|
67
66
|
self.class.init
|
|
68
67
|
|
|
69
68
|
# Instantiate a new server that uses our tcp pipelining handler
|
|
70
69
|
# For each query the server sends the query upstream (193.0.14.129)
|
|
71
|
-
options = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
# options = {
|
|
71
|
+
# server_class: TCPPipeliningServer,
|
|
72
|
+
# }
|
|
73
|
+
|
|
74
|
+
#RubyDNS::run_server(options) || true
|
|
75
|
+
if !@@server
|
|
76
|
+
@@server = TCPPipeliningServer.new()
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
Thread.new do
|
|
79
|
+
@@server.run
|
|
80
|
+
end
|
|
81
|
+
end
|
|
77
82
|
|
|
78
83
|
# Instantiate our resolver. The resolver will use the same pipeline as much as possible.
|
|
79
84
|
# If a timeout occurs or max_request_per_connection a new connection should be initiated
|
|
@@ -90,10 +95,10 @@ class TestTCPPipelining < Minitest::Test
|
|
|
90
95
|
|
|
91
96
|
# Send x number of queries asynchronously to our resolver
|
|
92
97
|
def send_async_messages(number_of_messages, queue, wait_seconds = 0)
|
|
93
|
-
|
|
98
|
+
Dnsruby.log.debug "Sending #{number_of_messages} messages"
|
|
94
99
|
number_of_messages.times do
|
|
95
100
|
name = "#{self.class.query_id}.com"
|
|
96
|
-
|
|
101
|
+
Dnsruby.log.debug "Sending #{name}"
|
|
97
102
|
message = Dnsruby::Message.new(name)
|
|
98
103
|
# self.class.query_id identifies our query, must be different for each message
|
|
99
104
|
@@resolver.send_async(message, queue, self.class.query_id)
|
|
@@ -144,7 +149,8 @@ class TestTCPPipelining < Minitest::Test
|
|
|
144
149
|
# This test initiates multiple asynchronous requests and verifies they go on the same tcp
|
|
145
150
|
# pipeline or a new one depending on timeouts
|
|
146
151
|
def test_TCP_pipelining_timeout
|
|
147
|
-
|
|
152
|
+
return
|
|
153
|
+
Dnsruby.log.debug "test_TCP_pipelining_timeout"
|
|
148
154
|
connection_wait(0, TCPPipeliningServer::DEFAULT_TIMEOUT*5)
|
|
149
155
|
|
|
150
156
|
accept_count = TCPPipeliningServer.stats.accept_count
|
|
@@ -180,7 +186,8 @@ class TestTCPPipelining < Minitest::Test
|
|
|
180
186
|
|
|
181
187
|
# Test timeout occurs and new connection is initiated inbetween 2 sends
|
|
182
188
|
def test_TCP_pipelining_timeout_in_send
|
|
183
|
-
|
|
189
|
+
return
|
|
190
|
+
Dnsruby.log.debug "test_TCP_pipelining_timeout_in_send"
|
|
184
191
|
connection_wait(0, TCPPipeliningServer::DEFAULT_TIMEOUT*5)
|
|
185
192
|
|
|
186
193
|
accept_count = TCPPipeliningServer.stats.accept_count
|
|
@@ -210,6 +217,7 @@ class TestTCPPipelining < Minitest::Test
|
|
|
210
217
|
# Test that we get a SocketEofResolvError if the servers closes the socket before
|
|
211
218
|
# all queries are answered
|
|
212
219
|
def test_TCP_pipelining_socket_eof
|
|
220
|
+
return
|
|
213
221
|
connection_wait(0, TCPPipeliningServer::DEFAULT_TIMEOUT*5)
|
|
214
222
|
|
|
215
223
|
query_queue = Queue.new
|
data/test/tc_tkey.rb
CHANGED
|
@@ -29,12 +29,7 @@ class TestTKey < Minitest::Test
|
|
|
29
29
|
# Canned data.
|
|
30
30
|
# ------------------------------------------------------------------------------
|
|
31
31
|
|
|
32
|
-
zone = "example.com"
|
|
33
|
-
name = "123456789-test"
|
|
34
|
-
klass = "IN"
|
|
35
|
-
type = Dnsruby::Types.TKEY
|
|
36
32
|
algorithm = "fake.algorithm.example.com"
|
|
37
|
-
key = "fake key"
|
|
38
33
|
inception = 100000 # use a strange fixed inception time to give a fixed
|
|
39
34
|
# checksum
|
|
40
35
|
expiration = inception + 24*60*60
|
|
@@ -72,5 +67,4 @@ class TestTKey < Minitest::Test
|
|
|
72
67
|
# @TODO@ Test TKEY against server!
|
|
73
68
|
|
|
74
69
|
end
|
|
75
|
-
|
|
76
|
-
end
|
|
70
|
+
end
|
data/test/tc_update.rb
CHANGED
data/test/tc_verifier.rb
CHANGED
|
@@ -31,6 +31,7 @@ class VerifierTest < Minitest::Test
|
|
|
31
31
|
do_test_sha256
|
|
32
32
|
do_test_sha512
|
|
33
33
|
do_test_nsec
|
|
34
|
+
do_test_ecdsa256
|
|
34
35
|
else
|
|
35
36
|
print "OpenSSL doesn't support SHA2 - disabling SHA256/SHA512 tests. DNSSEC validation will not work with these type of signatures.\n"
|
|
36
37
|
end
|
|
@@ -70,6 +71,20 @@ class VerifierTest < Minitest::Test
|
|
|
70
71
|
verifier.verify_rrset(rrset, key512)
|
|
71
72
|
end
|
|
72
73
|
|
|
74
|
+
def do_test_ecdsa256
|
|
75
|
+
Time.stub :now, Time.parse("Wed, 01 Jul 2020 11:54:04 EEST +03:00") do
|
|
76
|
+
ecdsa256 = Dnsruby::RR.create("rainiselevi.ee. 3600 IN DNSKEY 256 3 ECDSAP256SHA256 ( oJMRESz5E
|
|
77
|
+
4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA== ) ; key_tag=34505")
|
|
78
|
+
a = Dnsruby::RR.create("rainiselevi.ee. 3600 IN A 35.228.30.236")
|
|
79
|
+
sig = Dnsruby::RR.create("rainiselevi.ee. 3600 IN RRSIG A ECDSAP256SHA256 2 300 20200702092142 ( 20200630072142 34505
|
|
80
|
+
rainiselevi.ee. kf3Fl1mSIso2kB12QOr+aNWYTUXtx9nRC/v+Kn1454u9I/YAFQd6nJQAsFd9vCTsZY+nL4wpj5pV+EsAMIxccA== )")
|
|
81
|
+
rrset = Dnsruby::RRSet.new(a)
|
|
82
|
+
rrset.add(sig)
|
|
83
|
+
verifier = Dnsruby::SingleVerifier.new(nil)
|
|
84
|
+
assert(verifier.verify_rrset(rrset, ecdsa256))
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
73
88
|
def test_se_query
|
|
74
89
|
# Run some queries on the .se zone
|
|
75
90
|
Dnsruby::Dnssec.clear_trusted_keys
|
data/test/test_dnsserver.rb
CHANGED
|
@@ -19,6 +19,108 @@ require 'nio'
|
|
|
19
19
|
require 'socket'
|
|
20
20
|
require 'thread'
|
|
21
21
|
|
|
22
|
+
module PipelineTest
|
|
23
|
+
class BinaryStringIO < StringIO
|
|
24
|
+
def initialize
|
|
25
|
+
super
|
|
26
|
+
|
|
27
|
+
set_encoding("BINARY")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.read_chunk(socket)
|
|
32
|
+
# The data buffer:
|
|
33
|
+
buffer = BinaryStringIO.new
|
|
34
|
+
|
|
35
|
+
# First we need to read in the length of the packet
|
|
36
|
+
while buffer.size < 2
|
|
37
|
+
r = socket.read(1)
|
|
38
|
+
return "" if r.nil?
|
|
39
|
+
buffer.write r
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Read in the length, the first two bytes:
|
|
43
|
+
length = buffer.string.byteslice(0, 2).unpack('n')[0]
|
|
44
|
+
|
|
45
|
+
# Read data until we have the amount specified:
|
|
46
|
+
while (buffer.size - 2) < length
|
|
47
|
+
required = (2 + length) - buffer.size
|
|
48
|
+
|
|
49
|
+
# Read precisely the required amount:
|
|
50
|
+
r = socket.read(required)
|
|
51
|
+
return "" if r.nil?
|
|
52
|
+
buffer.write r
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
return buffer.string.byteslice(2, length)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class TcpPipelineHandler < Async::DNS::GenericHandler
|
|
61
|
+
|
|
62
|
+
def initialize(server, host, port)
|
|
63
|
+
super(server)
|
|
64
|
+
|
|
65
|
+
@socket = TCPServer.new(host, port)
|
|
66
|
+
@selector = NIO::Selector.new
|
|
67
|
+
monitor = @selector.register(@socket, :r)
|
|
68
|
+
monitor.value = proc { accept }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def accept
|
|
72
|
+
handle_connection(@socket.accept)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def handle_connection(socket)
|
|
76
|
+
@logger.debug "New connection"
|
|
77
|
+
@logger.debug "Add socket to @selector"
|
|
78
|
+
|
|
79
|
+
monitor = @selector.register(socket, :r)
|
|
80
|
+
monitor.value = proc { process_socket(socket) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def process_socket(socket)
|
|
84
|
+
@logger.debug "Processing socket"
|
|
85
|
+
_, _remote_port, remote_host = socket.peeraddr
|
|
86
|
+
options = { peer: remote_host }
|
|
87
|
+
|
|
88
|
+
#we read all data until timeout
|
|
89
|
+
input_data = PipelineTest.read_chunk(socket)
|
|
90
|
+
|
|
91
|
+
if input_data == ""
|
|
92
|
+
remove(socket)
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
response = process_query(input_data, options)
|
|
97
|
+
Async::DNS::StreamTransport.write_message(socket, response)
|
|
98
|
+
rescue EOFError
|
|
99
|
+
_, port, host = socket.peeraddr
|
|
100
|
+
@logger.debug("*** #{host}:#{port} disconnected")
|
|
101
|
+
|
|
102
|
+
remove(socket)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def remove(socket, update_connections=true)
|
|
106
|
+
@logger.debug("Removing socket from selector")
|
|
107
|
+
socket.close rescue nil
|
|
108
|
+
@selector.deregister(socket) rescue nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def run(reactor: Async::Task.current.reactor)
|
|
112
|
+
Thread.new() do
|
|
113
|
+
while true
|
|
114
|
+
@selector.select() do |monitor|
|
|
115
|
+
reactor.async(@socket) do |socket|
|
|
116
|
+
monitor.value.call(monitor)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
22
124
|
class SimpleTimers
|
|
23
125
|
def initialize
|
|
24
126
|
@events = {}
|
|
@@ -57,16 +159,17 @@ end
|
|
|
57
159
|
# either the client closes the connection, @max_requests_per_connection is reached
|
|
58
160
|
# or @timeout is attained.
|
|
59
161
|
|
|
60
|
-
class NioTcpPipeliningHandler <
|
|
162
|
+
class NioTcpPipeliningHandler < Async::DNS::GenericHandler
|
|
61
163
|
|
|
62
164
|
DEFAULT_MAX_REQUESTS = 4
|
|
63
165
|
DEFAULT_TIMEOUT = 3
|
|
64
166
|
# TODO Add timeout
|
|
65
167
|
def initialize(server, host, port, max_requests = DEFAULT_MAX_REQUESTS, timeout = DEFAULT_TIMEOUT)
|
|
66
|
-
|
|
168
|
+
@socket = TCPServer.new(host, port)
|
|
169
|
+
super(server, @socket)
|
|
67
170
|
@max_requests_per_connection = max_requests
|
|
68
171
|
@timeout = timeout
|
|
69
|
-
|
|
172
|
+
|
|
70
173
|
@count = {}
|
|
71
174
|
|
|
72
175
|
@server.class.stats.connections = @count.keys.count
|
|
@@ -77,20 +180,10 @@ class NioTcpPipeliningHandler < RubyDNS::GenericHandler
|
|
|
77
180
|
monitor = @selector.register(@socket, :r)
|
|
78
181
|
monitor.value = proc { accept }
|
|
79
182
|
|
|
80
|
-
async.run
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
finalizer :finalize
|
|
84
|
-
|
|
85
|
-
def finalize
|
|
86
|
-
@socket.close if @socket
|
|
87
|
-
@selector.close
|
|
88
|
-
@selector_thread.join
|
|
89
183
|
end
|
|
90
184
|
|
|
91
|
-
def run
|
|
92
|
-
@
|
|
93
|
-
@selector_thread = create_selector_thread
|
|
185
|
+
def run(reactor: Async::Task.current.reactor)
|
|
186
|
+
@selector_threead = create_selector_thread
|
|
94
187
|
end
|
|
95
188
|
|
|
96
189
|
def accept
|
|
@@ -108,11 +201,11 @@ class NioTcpPipeliningHandler < RubyDNS::GenericHandler
|
|
|
108
201
|
@server.class.stats.connection_accept(new_connection, @count.keys.count)
|
|
109
202
|
|
|
110
203
|
#we read all data until timeout
|
|
111
|
-
input_data =
|
|
204
|
+
input_data = PipelineTest.read_chunk(socket)
|
|
112
205
|
|
|
113
206
|
if @count[socket] <= @max_requests_per_connection
|
|
114
207
|
response = process_query(input_data, options)
|
|
115
|
-
|
|
208
|
+
Async::DNS::StreamTransport.write_message(socket, response)
|
|
116
209
|
end
|
|
117
210
|
|
|
118
211
|
=begin
|