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
|
@@ -408,7 +408,7 @@ module Dnsruby
|
|
|
408
408
|
end
|
|
409
409
|
|
|
410
410
|
def to_s
|
|
411
|
-
s = '' # the output string to return
|
|
411
|
+
s = +'' # the output string to return
|
|
412
412
|
|
|
413
413
|
if @answerfrom && (! @answerfrom.empty?)
|
|
414
414
|
s << ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
|
@@ -457,7 +457,7 @@ module Dnsruby
|
|
|
457
457
|
|
|
458
458
|
|
|
459
459
|
def old_to_s
|
|
460
|
-
retval = ''
|
|
460
|
+
retval = +''
|
|
461
461
|
|
|
462
462
|
if (@answerfrom != nil && @answerfrom != '')
|
|
463
463
|
retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
data/lib/dnsruby/name.rb
CHANGED
|
@@ -26,7 +26,8 @@ module Dnsruby
|
|
|
26
26
|
# * Name#wild?
|
|
27
27
|
# * Name#subdomain_of?(other)
|
|
28
28
|
# * Name#labels
|
|
29
|
-
#
|
|
29
|
+
#
|
|
30
|
+
require 'simpleidn'
|
|
30
31
|
class Name
|
|
31
32
|
include Comparable
|
|
32
33
|
MaxNameLength=255
|
|
@@ -52,6 +53,7 @@ module Dnsruby
|
|
|
52
53
|
if (arg=="")
|
|
53
54
|
return Name.new([],false)
|
|
54
55
|
end
|
|
56
|
+
arg = punycode(arg)
|
|
55
57
|
return Name.new(split_escaped(arg), /\.\z/ =~ arg ? true : false)
|
|
56
58
|
# return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
|
|
57
59
|
when Array
|
|
@@ -61,6 +63,20 @@ module Dnsruby
|
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
# Convert IDN domain from Unicode UTF-8 to ASCII punycode
|
|
67
|
+
# @param [Object|String] d Unicode domain with emoji inside
|
|
68
|
+
# @return [String] ASCII punycode domain
|
|
69
|
+
# @example
|
|
70
|
+
# Dnsruby::Name.punycode('🏳.cf')
|
|
71
|
+
# => "xn--en8h.cf"
|
|
72
|
+
def self.punycode(d)
|
|
73
|
+
begin
|
|
74
|
+
return SimpleIDN.to_ascii(d)
|
|
75
|
+
rescue
|
|
76
|
+
return d
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
64
80
|
def self.split_escaped(arg) #:nodoc: all
|
|
65
81
|
encodedlabels = name2encodedlabels(arg)
|
|
66
82
|
return encodedlabels
|
|
@@ -243,7 +259,7 @@ module Dnsruby
|
|
|
243
259
|
# in: dName a string with a domain name in presentation format (1035
|
|
244
260
|
# sect 5.1)
|
|
245
261
|
# out: an array of labels in wire format.
|
|
246
|
-
def self.name2encodedlabels
|
|
262
|
+
def self.name2encodedlabels(dName) #:nodoc: all
|
|
247
263
|
# Check for "\" in the name : If there, then decode properly - otherwise, cheat and split on "."
|
|
248
264
|
if (dName.index("\\"))
|
|
249
265
|
names=[]
|
|
@@ -317,7 +333,7 @@ module Dnsruby
|
|
|
317
333
|
i=0;
|
|
318
334
|
|
|
319
335
|
while (i < length )
|
|
320
|
-
c=presentation.unpack("x#{i}C1")
|
|
336
|
+
c=presentation.unpack("x#{i}C1")[0]
|
|
321
337
|
if (c == 46) # ord('.')
|
|
322
338
|
endstring = presentation[i+1, presentation.length-(i+1)]
|
|
323
339
|
return Label.new(wire),endstring
|
|
@@ -204,26 +204,22 @@ module Dnsruby
|
|
|
204
204
|
@tcp_pipelining_max_queries = :infinite
|
|
205
205
|
@use_counts = {}
|
|
206
206
|
|
|
207
|
-
if
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
elsif (arg.kind_of? Name)
|
|
214
|
-
@server=arg
|
|
215
|
-
elsif (arg.kind_of? Hash)
|
|
207
|
+
if arg.nil?
|
|
208
|
+
elsif arg.kind_of? String
|
|
209
|
+
@server = arg
|
|
210
|
+
elsif arg.kind_of? Name
|
|
211
|
+
@server = arg
|
|
212
|
+
elsif arg.kind_of? Hash
|
|
216
213
|
arg.keys.each do |attr|
|
|
217
214
|
begin
|
|
218
|
-
if ((
|
|
219
|
-
(
|
|
215
|
+
if ((attr.to_s == "src_address" || attr.to_s == "src_address6") &&
|
|
216
|
+
(arg[attr] == nil || arg[attr] == ""))
|
|
220
217
|
else
|
|
221
|
-
send(attr.to_s+"=", arg[attr])
|
|
218
|
+
send(attr.to_s + "=", arg[attr])
|
|
222
219
|
end
|
|
223
220
|
rescue Exception => e
|
|
224
221
|
Dnsruby.log.error { "PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n" }
|
|
225
222
|
end
|
|
226
|
-
# end
|
|
227
223
|
end
|
|
228
224
|
end
|
|
229
225
|
# Check server is IP
|
|
@@ -235,12 +231,12 @@ module Dnsruby
|
|
|
235
231
|
|
|
236
232
|
def check_ipv6
|
|
237
233
|
begin
|
|
238
|
-
|
|
234
|
+
IPv4.create(@server)
|
|
239
235
|
# @src_address = '0.0.0.0'
|
|
240
236
|
@ipv6=false
|
|
241
237
|
rescue Exception
|
|
242
238
|
begin
|
|
243
|
-
|
|
239
|
+
IPv6.create(@server)
|
|
244
240
|
# @src_address6 = '::'
|
|
245
241
|
@ipv6=true
|
|
246
242
|
rescue Exception
|
|
@@ -331,7 +327,14 @@ module Dnsruby
|
|
|
331
327
|
client_query_id = Time.now + rand(10000) # is this safe?!
|
|
332
328
|
end
|
|
333
329
|
|
|
334
|
-
|
|
330
|
+
begin
|
|
331
|
+
query_packet = make_query_packet(msg, use_tcp)
|
|
332
|
+
rescue EncodeError => err
|
|
333
|
+
Dnsruby.log.error { "#{err}" }
|
|
334
|
+
st = SelectThread.instance
|
|
335
|
+
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
336
|
+
return
|
|
337
|
+
end
|
|
335
338
|
|
|
336
339
|
if (msg.do_caching && (msg.class != Update))
|
|
337
340
|
# Check the cache!!
|
|
@@ -449,7 +452,7 @@ module Dnsruby
|
|
|
449
452
|
socket, new_socket = tcp_pipeline_socket(src_port)
|
|
450
453
|
src_port = @tcp_pipeline_local_port
|
|
451
454
|
else
|
|
452
|
-
socket =
|
|
455
|
+
socket = Socket.tcp(@server, @port, src_address, src_port, connect_timeout: @packet_timeout)
|
|
453
456
|
new_socket = true
|
|
454
457
|
end
|
|
455
458
|
rescue Errno::EBADF, Errno::ENETUNREACH => e
|
|
@@ -622,6 +625,11 @@ module Dnsruby
|
|
|
622
625
|
# Should send error back up to Resolver here, and then NOT QUERY AGAIN!!!
|
|
623
626
|
return sig_value
|
|
624
627
|
end
|
|
628
|
+
if ((response.header.get_header_rcode == RCode.FORMERR) &&
|
|
629
|
+
(query.header.arcount == 0))
|
|
630
|
+
# Raise an error
|
|
631
|
+
return true
|
|
632
|
+
end
|
|
625
633
|
# Should check that question section is same as question that was sent! RFC 5452
|
|
626
634
|
# If it's not an update...
|
|
627
635
|
if (query.class == Update)
|
data/lib/dnsruby/recursor.rb
CHANGED
|
@@ -153,7 +153,7 @@ module Dnsruby
|
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
attr_accessor :nameservers, :callback, :recurse, :ipv6_ok
|
|
156
|
-
attr_reader :hints
|
|
156
|
+
attr_reader :hints, :dnssec
|
|
157
157
|
# The resolver to use for the queries
|
|
158
158
|
attr_accessor :resolver
|
|
159
159
|
|
|
@@ -164,6 +164,11 @@ module Dnsruby
|
|
|
164
164
|
@@zones_cache = nil
|
|
165
165
|
@@nameservers = nil
|
|
166
166
|
|
|
167
|
+
def dnssec=(dnssec_on)
|
|
168
|
+
@dnssec = dnssec_on
|
|
169
|
+
@resolver.dnssec = dnssec_on
|
|
170
|
+
end
|
|
171
|
+
|
|
167
172
|
def initialize(res = nil)
|
|
168
173
|
if (res)
|
|
169
174
|
@resolver = res
|
|
@@ -174,6 +179,7 @@ module Dnsruby
|
|
|
174
179
|
@resolver = Resolver.new
|
|
175
180
|
end
|
|
176
181
|
end
|
|
182
|
+
@resolver.dnssec = @dnssec
|
|
177
183
|
@ipv6_ok = false
|
|
178
184
|
end
|
|
179
185
|
# Initialize the hint servers. Recursive queries need a starting name
|
|
@@ -196,6 +202,7 @@ module Dnsruby
|
|
|
196
202
|
@resolver = resolver
|
|
197
203
|
if (resolver.single_resolvers.length == 0)
|
|
198
204
|
resolver = Resolver.new()
|
|
205
|
+
resolver.dnssec = @dnssec
|
|
199
206
|
end
|
|
200
207
|
if (hints && hints.length > 0)
|
|
201
208
|
resolver.nameservers=hints
|
|
@@ -220,7 +227,7 @@ module Dnsruby
|
|
|
220
227
|
# Nice idea.
|
|
221
228
|
|
|
222
229
|
# if (!@@hints || @@hints.length == 0)
|
|
223
|
-
resolver.recurse=
|
|
230
|
+
resolver.recurse = true
|
|
224
231
|
packet=resolver.query_no_validation_or_recursion(".", "NS", "IN")
|
|
225
232
|
hints = Hash.new
|
|
226
233
|
if (packet)
|
|
@@ -257,7 +264,7 @@ module Dnsruby
|
|
|
257
264
|
}
|
|
258
265
|
}
|
|
259
266
|
(hints.length * 2).times {
|
|
260
|
-
|
|
267
|
+
_id, result, _error = q.pop
|
|
261
268
|
if (result)
|
|
262
269
|
result.answer.each {|rr|
|
|
263
270
|
TheLog.debug(";; NS address: " + rr.inspect+"\n")
|
|
@@ -296,7 +303,7 @@ module Dnsruby
|
|
|
296
303
|
end
|
|
297
304
|
|
|
298
305
|
# Disable recursion flag.
|
|
299
|
-
resolver.recurse=
|
|
306
|
+
resolver.recurse = false
|
|
300
307
|
# end
|
|
301
308
|
|
|
302
309
|
# return $self->nameservers( map { @{ $_ } } values %{ $self->{'hints'} } );
|
|
@@ -372,6 +379,7 @@ module Dnsruby
|
|
|
372
379
|
end
|
|
373
380
|
|
|
374
381
|
def Recursor.clear_caches(resolver = Resolver.new)
|
|
382
|
+
resolver.dnssec = @dnssec
|
|
375
383
|
Recursor.set_hints(Hash.new, resolver)
|
|
376
384
|
@@zones_cache = Hash.new # key zone_name, values Hash of servers and AddressCaches
|
|
377
385
|
@@zones_cache["."] = @@hints
|
|
@@ -398,7 +406,7 @@ module Dnsruby
|
|
|
398
406
|
@@mutex.synchronize {
|
|
399
407
|
self.hints=(Hash.new) unless @@hints
|
|
400
408
|
}
|
|
401
|
-
@resolver.recurse=
|
|
409
|
+
@resolver.recurse = false
|
|
402
410
|
# Make sure the authority cache is clean.
|
|
403
411
|
# It is only used to store A and AAAA records of
|
|
404
412
|
# the suposedly authoritative name servers.
|
|
@@ -492,7 +500,7 @@ module Dnsruby
|
|
|
492
500
|
return nil
|
|
493
501
|
end
|
|
494
502
|
|
|
495
|
-
known_zone.sub
|
|
503
|
+
known_zone = known_zone.sub(/\.*$/, ".")
|
|
496
504
|
|
|
497
505
|
ns = [] # Array of AddressCaches (was array of array of addresses)
|
|
498
506
|
@@mutex.synchronize{
|
|
@@ -613,6 +621,8 @@ module Dnsruby
|
|
|
613
621
|
}
|
|
614
622
|
end
|
|
615
623
|
resolver = Resolver.new({:nameserver=>nameservers})
|
|
624
|
+
resolver.dnssec = @dnssec
|
|
625
|
+
resolver.recurse = false
|
|
616
626
|
servers = []
|
|
617
627
|
resolver.single_resolvers.each {|s|
|
|
618
628
|
servers.push(s.server)
|
|
@@ -629,7 +639,7 @@ module Dnsruby
|
|
|
629
639
|
packet = resolver.send_message(query)
|
|
630
640
|
# @TODO@ Now prune unrelated RRSets (RFC 5452 section 6)
|
|
631
641
|
prune_rrsets_to_rfc5452(packet, known_zone)
|
|
632
|
-
rescue ResolvTimeout, IOError
|
|
642
|
+
rescue ResolvTimeout, IOError
|
|
633
643
|
# TheLog.debug(";; nameserver #{levelns.to_s} didn't respond")
|
|
634
644
|
# next
|
|
635
645
|
TheLog.debug("No response!")
|
data/lib/dnsruby/resolver.rb
CHANGED
|
@@ -128,7 +128,9 @@ module Dnsruby
|
|
|
128
128
|
# The current Config
|
|
129
129
|
attr_reader :config
|
|
130
130
|
|
|
131
|
-
#
|
|
131
|
+
# Defines whether we will cache responses, or pass every request to the
|
|
132
|
+
# upstream resolver. This is only really useful when querying authoritative
|
|
133
|
+
# servers (as the upstream recursive resolver is likely to cache)
|
|
132
134
|
attr_reader :do_caching
|
|
133
135
|
|
|
134
136
|
# The array of SingleResolvers used for sending query messages
|
|
@@ -171,11 +173,6 @@ module Dnsruby
|
|
|
171
173
|
# requirements.
|
|
172
174
|
attr_accessor :do_validation
|
|
173
175
|
|
|
174
|
-
# Defines whether we will cache responses, or pass every request to the
|
|
175
|
-
# upstream resolver. This is only really useful when querying authoritative
|
|
176
|
-
# servers (as the upstream recursive resolver is likely to cache)
|
|
177
|
-
attr_accessor :do_caching
|
|
178
|
-
|
|
179
176
|
# --
|
|
180
177
|
# @TODO@ add load_balance? i.e. Target nameservers in a random, rather than pre-determined, order?
|
|
181
178
|
# This is best done when configuring the Resolver, as it will re-order servers based on their response times.
|
|
@@ -302,15 +299,17 @@ module Dnsruby
|
|
|
302
299
|
# is sent (TSIG signatures will be applied if configured on the Resolver).
|
|
303
300
|
# Retries are handled as the Resolver is configured to do.
|
|
304
301
|
# Incoming responses to the query are not cached or validated (although TCP
|
|
305
|
-
# fallback will be performed if the TC bit is set and the (Single)Resolver
|
|
306
|
-
# ignore_truncation set to false).
|
|
307
|
-
# Note that the Message
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
#
|
|
302
|
+
# fallback will be performed if the TC bit is set and the (Single)Resolver
|
|
303
|
+
# has ignore_truncation set to false).
|
|
304
|
+
# Note that the Message may be modified before sending: if the configured
|
|
305
|
+
# udp_size is >512 bytes, DNSSEC is not set, and no OPT record is already
|
|
306
|
+
# present, then an OPT record will be added automatically to support EDNS
|
|
307
|
+
# per RFC 6891.
|
|
308
|
+
# If it is desired to customize the OPT record or use EDNS in other cases,
|
|
309
|
+
# then you should call the Dnsruby::PacketSender#prepare_for_dnssec(msg),
|
|
310
|
+
# or Dnsruby::PacketSender#add_opt_rr(msg).
|
|
311
|
+
# The return value from this method is the [response, error] tuple. Either
|
|
312
|
+
# of these values may be nil - it is up to the client to check.
|
|
314
313
|
#
|
|
315
314
|
# example :
|
|
316
315
|
#
|
|
@@ -568,7 +567,7 @@ module Dnsruby
|
|
|
568
567
|
def add_server(server)# :nodoc:
|
|
569
568
|
@configured = true
|
|
570
569
|
res = PacketSender.new(server)
|
|
571
|
-
log_and_raise("Can't create server #{server}", ArgumentError) unless res
|
|
570
|
+
Dnsruby.log_and_raise("Can't create server #{server}", ArgumentError) unless res
|
|
572
571
|
update_internal_res(res)
|
|
573
572
|
@single_res_mutex.synchronize { @single_resolvers.push(res) }
|
|
574
573
|
end
|
|
@@ -644,7 +643,7 @@ module Dnsruby
|
|
|
644
643
|
a = Resolver.get_ports_from(p)
|
|
645
644
|
a.each do |x|
|
|
646
645
|
if (@src_port.length > 0) && (x == 0)
|
|
647
|
-
log_and_raise("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values",
|
|
646
|
+
Dnsruby.log_and_raise("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values",
|
|
648
647
|
ArgumentError)
|
|
649
648
|
end
|
|
650
649
|
@src_port.push(x)
|
|
@@ -668,7 +667,7 @@ module Dnsruby
|
|
|
668
667
|
return ! ((p == 0) && (src_port.length > 0))
|
|
669
668
|
else
|
|
670
669
|
Dnsruby.log.error("Illegal port (#{p})")
|
|
671
|
-
log_and_raise("Illegal port #{p}", ArgumentError)
|
|
670
|
+
Dnsruby.log_and_raise("Illegal port #{p}", ArgumentError)
|
|
672
671
|
end
|
|
673
672
|
end
|
|
674
673
|
|
|
@@ -837,7 +836,7 @@ module Dnsruby
|
|
|
837
836
|
timeouts[base + offset]=[res, retry_count]
|
|
838
837
|
else
|
|
839
838
|
if timeouts.has_key?(base + retry_delay + offset)
|
|
840
|
-
log_and_raise('Duplicate timeout key!')
|
|
839
|
+
Dnsruby.log_and_raise('Duplicate timeout key!')
|
|
841
840
|
end
|
|
842
841
|
timeouts[base + retry_delay + offset]=[res, retry_count]
|
|
843
842
|
end
|
|
@@ -878,7 +877,7 @@ module Dnsruby
|
|
|
878
877
|
end
|
|
879
878
|
|
|
880
879
|
unless client_queue.kind_of?(Queue)
|
|
881
|
-
log_and_raise('Wrong type for client_queue in Resolver# send_async')
|
|
880
|
+
Dnsruby.log_and_raise('Wrong type for client_queue in Resolver# send_async')
|
|
882
881
|
# @TODO@ Handle different queue tuples - push this to generic send_error method
|
|
883
882
|
client_queue.push([client_query_id, ArgumentError.new('Wrong type of client_queue passed to Dnsruby::Resolver# send_async - should have been Queue, was #{client_queue.class}')])
|
|
884
883
|
return
|
|
@@ -891,6 +890,21 @@ module Dnsruby
|
|
|
891
890
|
return
|
|
892
891
|
end
|
|
893
892
|
|
|
893
|
+
# According to RFC 6891, a UDP payload size >512 bytes requires an
|
|
894
|
+
# EDNS OPT RR in the message.
|
|
895
|
+
if @parent.udp_size > 512 && !msg.get_opt
|
|
896
|
+
TheLog.debug("Automatically adding EDNS OPT RR for udp_size=#{ @parent.udp_size }")
|
|
897
|
+
msg.add_additional(Dnsruby::RR::OPT.new(@parent.udp_size))
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
begin
|
|
901
|
+
msg.encode
|
|
902
|
+
rescue EncodeError => err
|
|
903
|
+
Dnsruby.log.error { "Can't encode " + msg.to_s + " : #{err}" }
|
|
904
|
+
client_queue.push([client_query_id, err])
|
|
905
|
+
return
|
|
906
|
+
end
|
|
907
|
+
|
|
894
908
|
tick_needed = false
|
|
895
909
|
# add to our data structures
|
|
896
910
|
# @mutex.synchronize{
|
|
@@ -1051,13 +1065,13 @@ module Dnsruby
|
|
|
1051
1065
|
# @TODO@ Also, should have option to speak only to configured resolvers (not follow authoritative chain)
|
|
1052
1066
|
#
|
|
1053
1067
|
if queue.empty?
|
|
1054
|
-
log_and_raise('Severe internal error - Queue empty in handle_queue_event')
|
|
1068
|
+
Dnsruby.log_and_raise('Severe internal error - Queue empty in handle_queue_event')
|
|
1055
1069
|
end
|
|
1056
1070
|
event_id, event_type, response, error = queue.pop
|
|
1057
1071
|
# We should remove this packet from the list of outstanding packets for this query
|
|
1058
1072
|
_resolver, _msg, client_query_id, _retry_count = id
|
|
1059
1073
|
if id != event_id
|
|
1060
|
-
log_and_raise("Serious internal error!! #{id} expected, #{event_id} received")
|
|
1074
|
+
Dnsruby.log_and_raise("Serious internal error!! #{id} expected, #{event_id} received")
|
|
1061
1075
|
end
|
|
1062
1076
|
# @mutex.synchronize{
|
|
1063
1077
|
@parent.single_res_mutex.synchronize {
|
|
@@ -1070,7 +1084,7 @@ module Dnsruby
|
|
|
1070
1084
|
if event_type == Resolver::EventType::RECEIVED ||
|
|
1071
1085
|
event_type == Resolver::EventType::ERROR
|
|
1072
1086
|
unless outstanding.include?(id)
|
|
1073
|
-
|
|
1087
|
+
Dnsruby.log.error("Query id not on outstanding list! #{outstanding.length} items. #{id} not on #{outstanding}")
|
|
1074
1088
|
end
|
|
1075
1089
|
outstanding.delete(id)
|
|
1076
1090
|
end
|
|
@@ -1124,6 +1138,9 @@ module Dnsruby
|
|
|
1124
1138
|
increment_resolver_priority(resolver) unless response.cached
|
|
1125
1139
|
stop_querying(client_query_id)
|
|
1126
1140
|
# @TODO@ Does the client want notified at this point?
|
|
1141
|
+
elsif error.kind_of?(EncodeError)
|
|
1142
|
+
Dnsruby.log.debug{'Encode error - sending to client'}
|
|
1143
|
+
send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error)
|
|
1127
1144
|
else
|
|
1128
1145
|
# - if it was any other error, then remove that server from the list for that query
|
|
1129
1146
|
# If a Too Many Open Files error, then don't remove, but let retry work.
|
|
@@ -1197,7 +1214,7 @@ module Dnsruby
|
|
|
1197
1214
|
# @mutex.synchronize{
|
|
1198
1215
|
_query, _client_queue, s_queue, _outstanding = @query_list[client_query_id]
|
|
1199
1216
|
if s_queue != select_queue
|
|
1200
|
-
log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1217
|
+
Dnsruby.log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1201
1218
|
end
|
|
1202
1219
|
stop_querying(client_query_id)
|
|
1203
1220
|
# @TODO@ Does the client want notified at this point?
|
|
@@ -1210,7 +1227,7 @@ module Dnsruby
|
|
|
1210
1227
|
# @mutex.synchronize {
|
|
1211
1228
|
_query, client_queue, s_queue, _outstanding = @query_list[client_query_id]
|
|
1212
1229
|
if s_queue != select_queue
|
|
1213
|
-
log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1230
|
+
Dnsruby.log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1214
1231
|
end
|
|
1215
1232
|
if response.rcode == RCode.NXDOMAIN
|
|
1216
1233
|
send_result(client_queue, client_query_id, select_queue, response, NXDomain.new)
|
|
@@ -1226,7 +1243,7 @@ module Dnsruby
|
|
|
1226
1243
|
_resolver, _msg, client_query_id, _retry_count = query_id
|
|
1227
1244
|
_query, client_queue, s_queue, _outstanding = @query_list[client_query_id]
|
|
1228
1245
|
if s_queue != select_queue
|
|
1229
|
-
log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1246
|
+
Dnsruby.log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}")
|
|
1230
1247
|
end
|
|
1231
1248
|
# For some errors, we immediately send result. For others, should we retry?
|
|
1232
1249
|
# Either :
|
data/lib/dnsruby/resource/CAA.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Dnsruby
|
|
|
26
26
|
# The value for the property_tag
|
|
27
27
|
attr_accessor :property_value
|
|
28
28
|
# The value for the flag
|
|
29
|
-
|
|
29
|
+
attr_writer :flag
|
|
30
30
|
|
|
31
31
|
def from_hash(hash) #:nodoc: all
|
|
32
32
|
@property_tag = hash[:property_tag]
|
|
@@ -43,7 +43,10 @@ module Dnsruby
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def from_string(input) #:nodoc: all
|
|
46
|
-
matches = (/(\d+) (issuewild|issue|iodef) "(.+)"$/).match(input)
|
|
46
|
+
matches = (/(\d+) (issuewild|issuemail|issue|iodef|contactemail|contactphone) "(.+)"$/i).match(input)
|
|
47
|
+
if matches.nil?
|
|
48
|
+
raise DecodeError.new("Cannot parse record: #{input[0...1000]}")
|
|
49
|
+
end
|
|
47
50
|
@flag = matches[1]
|
|
48
51
|
@property_tag = matches[2]
|
|
49
52
|
@property_value = matches[3]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Dnsruby
|
|
2
|
+
class RR
|
|
3
|
+
# RFC4034, section 2
|
|
4
|
+
# DNSSEC uses public key cryptography to sign and authenticate DNS
|
|
5
|
+
# resource record sets (RRsets). The public keys are stored in DNSKEY
|
|
6
|
+
# resource records and are used in the DNSSEC authentication process
|
|
7
|
+
# described in [RFC4035]: A zone signs its authoritative RRsets by
|
|
8
|
+
# using a private key and stores the corresponding public key in a
|
|
9
|
+
# DNSKEY RR. A resolver can then use the public key to validate
|
|
10
|
+
# signatures covering the RRsets in the zone, and thus to authenticate
|
|
11
|
+
# them.
|
|
12
|
+
class CDNSKEY < DNSKEY
|
|
13
|
+
ClassValue = nil #:nodoc: all
|
|
14
|
+
TypeValue = Types::CDNSKEY #:nodoc: all
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# --
|
|
2
|
+
# Copyright 2018 Caerketton Tech Ltd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
# ++
|
|
16
|
+
module Dnsruby
|
|
17
|
+
class RR
|
|
18
|
+
# RFC4034, section 4
|
|
19
|
+
# The DS Resource Record refers to a DNSKEY RR and is used in the DNS
|
|
20
|
+
# DNSKEY authentication process. A DS RR refers to a DNSKEY RR by
|
|
21
|
+
# storing the key tag, algorithm number, and a digest of the DNSKEY RR.
|
|
22
|
+
# Note that while the digest should be sufficient to identify the
|
|
23
|
+
# public key, storing the key tag and key algorithm helps make the
|
|
24
|
+
# identification process more efficient. By authenticating the DS
|
|
25
|
+
# record, a resolver can authenticate the DNSKEY RR to which the DS
|
|
26
|
+
# record points. The key authentication process is described in
|
|
27
|
+
# [RFC4035].
|
|
28
|
+
|
|
29
|
+
class CDS < DS
|
|
30
|
+
|
|
31
|
+
ClassValue = nil #:nodoc: all
|
|
32
|
+
TypeValue = Types::CDS #:nodoc: all
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -294,11 +294,11 @@ module Dnsruby
|
|
|
294
294
|
begin
|
|
295
295
|
key_text.gsub!(/\n/, "")
|
|
296
296
|
key_text.gsub!(/ /, "")
|
|
297
|
-
|
|
298
|
-
@key=key_text.unpack("m*")[0]
|
|
297
|
+
@key=Base64.decode64(key_text)
|
|
299
298
|
public_key
|
|
300
299
|
get_new_key_tag
|
|
301
|
-
rescue Exception
|
|
300
|
+
rescue Exception => e
|
|
301
|
+
Dnsruby.log.error(e)
|
|
302
302
|
raise ArgumentError.new("Key #{key_text} invalid")
|
|
303
303
|
end
|
|
304
304
|
end
|
|
@@ -313,6 +313,8 @@ module Dnsruby
|
|
|
313
313
|
elsif [Algorithms.DSA,
|
|
314
314
|
Algorithms.DSA_NSEC3_SHA1].include?(@algorithm)
|
|
315
315
|
@public_key = dsa_key
|
|
316
|
+
elsif [Algorithms.ECDSAP256SHA256, Algorithms.ECDSAP384SHA384].include?(@algorithm)
|
|
317
|
+
@public_key = ec_key(Algorithms.ECDSAP256SHA256 == @algorithm ? 'prime256v1' : 'secp384r1')
|
|
316
318
|
end
|
|
317
319
|
end
|
|
318
320
|
# @TODO@ Support other key encodings!
|
|
@@ -339,9 +341,12 @@ module Dnsruby
|
|
|
339
341
|
modulus = RR::get_num(@key[pos, @key.length])
|
|
340
342
|
@key_length = (@key.length - pos) * 8
|
|
341
343
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
data_sequence = OpenSSL::ASN1::Sequence([
|
|
345
|
+
OpenSSL::ASN1::Integer(modulus),
|
|
346
|
+
OpenSSL::ASN1::Integer(exponent)
|
|
347
|
+
])
|
|
348
|
+
asn1 = OpenSSL::ASN1::Sequence(data_sequence)
|
|
349
|
+
pkey = OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
345
350
|
return pkey
|
|
346
351
|
end
|
|
347
352
|
|
|
@@ -360,14 +365,49 @@ module Dnsruby
|
|
|
360
365
|
pos += pgy_len
|
|
361
366
|
@key_length = (pgy_len * 8)
|
|
362
367
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
asn1 = OpenSSL::ASN1::Sequence.new(
|
|
369
|
+
[
|
|
370
|
+
OpenSSL::ASN1::Sequence.new(
|
|
371
|
+
[
|
|
372
|
+
OpenSSL::ASN1::ObjectId.new('DSA'),
|
|
373
|
+
OpenSSL::ASN1::Sequence.new(
|
|
374
|
+
[
|
|
375
|
+
OpenSSL::ASN1::Integer.new(p),
|
|
376
|
+
OpenSSL::ASN1::Integer.new(q),
|
|
377
|
+
OpenSSL::ASN1::Integer.new(g)
|
|
378
|
+
]
|
|
379
|
+
)
|
|
380
|
+
]
|
|
381
|
+
),
|
|
382
|
+
OpenSSL::ASN1::BitString.new(OpenSSL::ASN1::Integer.new(y).to_der)
|
|
383
|
+
]
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
OpenSSL::PKey::DSA.new(asn1.to_der)
|
|
387
|
+
end
|
|
368
388
|
|
|
369
|
-
|
|
389
|
+
# RFC6605, section 4
|
|
390
|
+
# ECDSA public keys consist of a single value, called "Q" in FIPS
|
|
391
|
+
# 186-3. In DNSSEC keys, Q is a simple bit string that represents the
|
|
392
|
+
# uncompressed form of a curve point, "x | y".
|
|
393
|
+
def ec_key(curve = 'prime256v1')
|
|
394
|
+
group = OpenSSL::PKey::EC::Group.new(curve)
|
|
395
|
+
# DNSSEC pub does not have first octet that determines whether it's uncompressed
|
|
396
|
+
# or compressed form, but it's required by OpenSSL to parse EC point correctly
|
|
397
|
+
dnskey_bn = OpenSSL::BN.new("\x04" + @key, 2)
|
|
398
|
+
key_point = OpenSSL::PKey::EC::Point.new(group, dnskey_bn)
|
|
399
|
+
|
|
400
|
+
asn1 = OpenSSL::ASN1::Sequence.new(
|
|
401
|
+
[
|
|
402
|
+
OpenSSL::ASN1::Sequence.new([
|
|
403
|
+
OpenSSL::ASN1::ObjectId.new("id-ecPublicKey"),
|
|
404
|
+
OpenSSL::ASN1::ObjectId.new(group.curve_name)
|
|
405
|
+
]),
|
|
406
|
+
OpenSSL::ASN1::BitString.new(key_point.to_octet_string(:uncompressed))
|
|
407
|
+
]
|
|
408
|
+
)
|
|
409
|
+
OpenSSL::PKey::EC.new(asn1.to_der)
|
|
370
410
|
end
|
|
371
411
|
end
|
|
372
412
|
end
|
|
373
|
-
end
|
|
413
|
+
end
|
data/lib/dnsruby/resource/DS.rb
CHANGED
data/lib/dnsruby/resource/IN.rb
CHANGED
|
@@ -19,7 +19,11 @@ module Dnsruby
|
|
|
19
19
|
Types::NS => NS,
|
|
20
20
|
Types::CNAME => CNAME,
|
|
21
21
|
Types::DNAME => DNAME,
|
|
22
|
+
Types::URI => URI,
|
|
23
|
+
Types::DS => DS,
|
|
24
|
+
Types::CDS => CDS,
|
|
22
25
|
Types::DNSKEY => DNSKEY,
|
|
26
|
+
Types::CDNSKEY => CDNSKEY,
|
|
23
27
|
Types::SOA => SOA,
|
|
24
28
|
Types::PTR => PTR,
|
|
25
29
|
Types::HINFO => HINFO,
|
|
@@ -45,7 +49,6 @@ module Dnsruby
|
|
|
45
49
|
Types::ANY => ANY,
|
|
46
50
|
Types::RRSIG => RRSIG,
|
|
47
51
|
Types::NSEC => NSEC,
|
|
48
|
-
Types::DS => DS,
|
|
49
52
|
Types::NSEC3 => NSEC3,
|
|
50
53
|
Types::NSEC3PARAM => NSEC3PARAM,
|
|
51
54
|
Types::DLV => DLV,
|