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.
Files changed (79) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/dependabot.yml +11 -0
  4. data/.github/workflows/ci.yml +26 -0
  5. data/.yardopts +7 -0
  6. data/README.md +8 -5
  7. data/RELEASE_NOTES.md +105 -0
  8. data/Rakefile +1 -0
  9. data/demo/digdlv.rb +1 -0
  10. data/demo/digroot.rb +2 -0
  11. data/dnsruby.gemspec +22 -14
  12. data/lib/dnsruby/DNS.rb +1 -1
  13. data/lib/dnsruby/bit_mapping.rb +2 -2
  14. data/lib/dnsruby/code_mappers.rb +6 -2
  15. data/lib/dnsruby/config.rb +29 -15
  16. data/lib/dnsruby/dnssec.rb +4 -1
  17. data/lib/dnsruby/hosts.rb +1 -4
  18. data/lib/dnsruby/ipv6.rb +5 -5
  19. data/lib/dnsruby/message/encoder.rb +5 -5
  20. data/lib/dnsruby/message/header.rb +15 -18
  21. data/lib/dnsruby/message/message.rb +2 -2
  22. data/lib/dnsruby/name.rb +19 -3
  23. data/lib/dnsruby/packet_sender.rb +25 -17
  24. data/lib/dnsruby/recursor.rb +17 -7
  25. data/lib/dnsruby/resolver.rb +43 -26
  26. data/lib/dnsruby/resource/CAA.rb +5 -2
  27. data/lib/dnsruby/resource/CDNSKEY.rb +17 -0
  28. data/lib/dnsruby/resource/CDS.rb +35 -0
  29. data/lib/dnsruby/resource/DNSKEY.rb +53 -13
  30. data/lib/dnsruby/resource/DS.rb +2 -5
  31. data/lib/dnsruby/resource/GPOS.rb +1 -1
  32. data/lib/dnsruby/resource/IN.rb +4 -1
  33. data/lib/dnsruby/resource/NSEC.rb +2 -2
  34. data/lib/dnsruby/resource/NSEC3PARAM.rb +1 -1
  35. data/lib/dnsruby/resource/NXT.rb +1 -1
  36. data/lib/dnsruby/resource/RR.rb +1 -1
  37. data/lib/dnsruby/resource/TLSA.rb +3 -3
  38. data/lib/dnsruby/resource/TXT.rb +12 -2
  39. data/lib/dnsruby/resource/URI.rb +57 -0
  40. data/lib/dnsruby/resource/generic.rb +3 -0
  41. data/lib/dnsruby/select_thread.rb +36 -28
  42. data/lib/dnsruby/single_verifier.rb +29 -8
  43. data/lib/dnsruby/validator_thread.rb +4 -4
  44. data/lib/dnsruby/version.rb +1 -1
  45. data/lib/dnsruby/zone_reader.rb +2 -2
  46. data/lib/dnsruby/zone_transfer.rb +5 -2
  47. data/lib/dnsruby.rb +4 -4
  48. data/test/localdns.rb +29 -0
  49. data/test/spec_helper.rb +34 -9
  50. data/test/tc_caa.rb +12 -1
  51. data/test/tc_dns.rb +15 -5
  52. data/test/tc_dnskey.rb +29 -0
  53. data/test/tc_ecdsa.rb +27 -0
  54. data/test/tc_encoding.rb +31 -0
  55. data/test/tc_escapedchars.rb +8 -9
  56. data/test/tc_gpos.rb +1 -1
  57. data/test/tc_hs.rb +4 -8
  58. data/test/tc_long_labels.rb +46 -0
  59. data/test/tc_message.rb +2 -2
  60. data/test/tc_name.rb +21 -2
  61. data/test/tc_nxt.rb +0 -1
  62. data/test/tc_recur.rb +4 -7
  63. data/test/tc_resolv.rb +14 -6
  64. data/test/tc_resolver.rb +114 -11
  65. data/test/tc_rr-opt.rb +8 -5
  66. data/test/tc_rr-txt.rb +7 -1
  67. data/test/tc_rr-unknown.rb +1 -1
  68. data/test/tc_rr.rb +37 -4
  69. data/test/tc_single_resolver.rb +9 -17
  70. data/test/tc_soak.rb +33 -67
  71. data/test/tc_tcp.rb +2 -2
  72. data/test/tc_tcp_pipelining.rb +30 -22
  73. data/test/tc_tkey.rb +1 -7
  74. data/test/tc_update.rb +0 -1
  75. data/test/tc_verifier.rb +15 -0
  76. data/test/test_dnsserver.rb +110 -17
  77. data/test/test_utils.rb +0 -2
  78. metadata +83 -46
  79. data/.travis.yml +0 -14
@@ -152,7 +152,7 @@ module Dnsruby
152
152
  end
153
153
 
154
154
  def self.encode_types(nsec)
155
- output = ''
155
+ output = +''
156
156
  # types represents all 65536 possible RR types.
157
157
  # Split up types into sets of 256 different types.
158
158
  type_codes = []
@@ -171,7 +171,7 @@ module Dnsruby
171
171
 
172
172
  unless types_to_go.empty?
173
173
  # Then create the bitmap for them
174
- bitmap = ''
174
+ bitmap = +''
175
175
  # keep on adding them until there's none left
176
176
  pos = 0
177
177
  bitmap_pos = 0
@@ -85,7 +85,7 @@ module Dnsruby
85
85
  # end
86
86
  #
87
87
  def from_data(data) #:nodoc: all
88
- hash_alg, flags, iterations, salt_length, salt = data
88
+ hash_alg, flags, iterations, _salt_length, salt = data
89
89
  self.hash_alg=(hash_alg)
90
90
  self.flags=(flags)
91
91
  self.iterations=(iterations)
@@ -95,7 +95,7 @@ class NXT < RR
95
95
  next_domain = Name.create(next_domain) if next_domain.is_a?(String)
96
96
  types = TypeBitmap.from_type_codes(types) if types.is_a?(Array)
97
97
 
98
- binary_string = ''.force_encoding('ASCII-8BIT')
98
+ binary_string = ''.b
99
99
  binary_string << next_domain.canonical
100
100
  binary_string << BitMapping.reverse_binary_string_bits(types.to_binary_string)
101
101
  binary_string
@@ -350,7 +350,7 @@ class RR
350
350
  end
351
351
 
352
352
  def hash # :nodoc:
353
- vars = (self.instance_variables - [:@ttl]).sort
353
+ vars = (self.instance_variables - [:@ttl, :@rdata]).sort
354
354
  vars.inject(0) do |hash_value, var_name|
355
355
  hash_value ^ self.instance_variable_get(var_name).hash
356
356
  end
@@ -33,8 +33,8 @@ module Dnsruby
33
33
  # 255 Private use
34
34
  attr_accessor :matching_type
35
35
  # sec 2.1.4
36
- attr_accessor :data
37
- attr_accessor :databin
36
+ attr_reader :data
37
+ attr_reader :databin
38
38
 
39
39
  def verify
40
40
  raise ArgumentError, "usage with invalid value: #{@usage}" if @usage < 0 || @usage > 255
@@ -71,7 +71,7 @@ module Dnsruby
71
71
  if @matching_type == 0 && @selector == 0 && @databin
72
72
  begin
73
73
  cert = OpenSSL::X509::Certificate.new(@databin)
74
- rescue => e
74
+ rescue
75
75
  raise ArgumentError, 'data is invalid cert '
76
76
  end
77
77
  end
@@ -63,7 +63,7 @@ module Dnsruby
63
63
  unquoted = false
64
64
  seen_strings = false
65
65
  pos = 0
66
- input.sub!(/^\s*\(\s*/, "")
66
+ input = input.sub(/^\s*\(\s*/, "")
67
67
  input.sub!(/\s*\)\s*$/, "")
68
68
  input.each_char {|c|
69
69
  if (((c == "'") || (c == '"')) && (!in_escaped) && (!unquoted))
@@ -82,7 +82,17 @@ module Dnsruby
82
82
  end
83
83
  else
84
84
  if (seen_strings && !in_string)
85
- next
85
+ if (c == ";")
86
+ # Comment in zone file!
87
+ return strings
88
+ end
89
+ if (c != " " && c != "\t")
90
+ in_string = true
91
+ count+=1
92
+ strings[count] = ""
93
+ else
94
+ next
95
+ end
86
96
  end
87
97
  if (pos == 0)
88
98
  unquoted = true
@@ -0,0 +1,57 @@
1
+ module Dnsruby
2
+ class RR
3
+ class URI < RR
4
+ ClassValue = nil #:nodoc: all
5
+ TypeValue= Types::URI #:nodoc: all
6
+
7
+ # The NAPTR RR order field
8
+ attr_accessor :priority
9
+
10
+ # The NAPTR RR order field
11
+ attr_accessor :weight
12
+
13
+ # The NAPTR RR order field
14
+ attr_accessor :target
15
+
16
+ def from_hash(hash) #:nodoc: all
17
+ @priority = hash[:priority]
18
+ @weight = hash[:weight]
19
+ @target = hash[:target]
20
+ end
21
+
22
+ def from_data(data) #:nodoc: all
23
+ @priority, @weight, @target = data
24
+ end
25
+
26
+ def from_string(input) #:nodoc: all
27
+ if (input.strip.length > 0)
28
+ values = input.split(" ")
29
+ @priority = values [0].to_i
30
+ @weight = values [1].to_i
31
+ @target = values [2].gsub!("\"", "")
32
+ end
33
+ end
34
+
35
+ def rdata_to_string #:nodoc: all
36
+ "#{@priority} #{@weight} \"#{@target}\""
37
+ end
38
+
39
+ def encode_rdata(msg, canonical=false) #:nodoc: all
40
+ if (@priority != nil)
41
+ msg.put_pack('n', @priority)
42
+ msg.put_pack('n', @weight)
43
+ msg.put_bytes(@target)
44
+ end
45
+ end
46
+
47
+ def self.decode_rdata(msg) #:nodoc: all
48
+ priority, = msg.get_unpack('n')
49
+ weight, = msg.get_unpack('n')
50
+ target = msg.get_bytes
51
+ return self.new([priority, weight, target])
52
+ end
53
+
54
+
55
+ end
56
+ end
57
+ end
@@ -152,9 +152,12 @@ require 'dnsruby/resource/OPT'
152
152
  require 'dnsruby/resource/TSIG'
153
153
  require 'dnsruby/resource/TKEY'
154
154
  require 'dnsruby/resource/DNSKEY'
155
+ require 'dnsruby/resource/CDNSKEY'
155
156
  require 'dnsruby/resource/RRSIG'
156
157
  require 'dnsruby/resource/NSEC'
157
158
  require 'dnsruby/resource/DS'
159
+ require 'dnsruby/resource/CDS'
160
+ require 'dnsruby/resource/URI'
158
161
  require 'dnsruby/resource/NSEC3'
159
162
  require 'dnsruby/resource/NSEC3PARAM'
160
163
  require 'dnsruby/resource/DLV'
@@ -138,9 +138,13 @@ module Dnsruby
138
138
  @@sockets << query_settings.socket
139
139
  @@socket_is_persistent[query_settings.socket] = query_settings.is_persistent_socket
140
140
  }
141
+ wake_up_select_thread
142
+ end
143
+
144
+ def wake_up_select_thread
141
145
  begin
142
146
  @@wakeup_sockets[0].send("wakeup!", 0)
143
- rescue Exception => e
147
+ rescue Exception
144
148
  # do nothing
145
149
  end
146
150
  end
@@ -190,12 +194,11 @@ module Dnsruby
190
194
  end
191
195
  # next if (timeout < 0)
192
196
  begin
193
- ready, write, errors = IO.select(sockets, nil, nil, timeout)
197
+ ready, _write, _errors = IO.select(sockets, nil, nil, timeout)
194
198
  rescue SelectWakeup
195
199
  # If SelectWakeup, then just restart this loop - the select call will be made with the new data
196
200
  next
197
- rescue IOError => e
198
- # print "IO Error =: #{e}\n"
201
+ rescue IOError, EncodeError
199
202
  exceptions = clean_up_closed_sockets
200
203
  exceptions.each { |exception| send_exception_to_client(*exception) }
201
204
 
@@ -248,7 +251,7 @@ module Dnsruby
248
251
  # Removes closed sockets from @@sockets, and returns an array containing 1
249
252
  # exception for each closed socket contained in @@socket_hash.
250
253
  def clean_up_closed_sockets
251
- exceptions = @@mutex.synchronize do
254
+ @@mutex.synchronize do
252
255
  closed_sockets_in_hash = @@sockets.select(&:closed?).select { |s| @@socket_hash[s] }
253
256
  @@sockets.delete_if { | socket | socket.closed? }
254
257
  closed_sockets_in_hash.each_with_object([]) do |socket, exceptions|
@@ -257,6 +260,7 @@ module Dnsruby
257
260
  end
258
261
  end
259
262
  end
263
+ exceptions
260
264
  end
261
265
 
262
266
  def process_error(errors)
@@ -295,7 +299,6 @@ module Dnsruby
295
299
  @@mutex.synchronize do
296
300
  ids = get_active_ids(@@query_hash, msg.header.id)
297
301
  return if ids.empty? # should be only one
298
- query_settings = @@query_hash[ids[0]].clone
299
302
  end
300
303
 
301
304
  answerip = msg.answerip.downcase
@@ -369,21 +372,25 @@ module Dnsruby
369
372
  end
370
373
 
371
374
  def remove_id(id)
372
-
373
375
  @@mutex.synchronize do
374
- socket = @@query_hash[id].socket
375
- @@timeouts.delete(id)
376
- @@query_hash.delete(id)
377
- @@socket_hash[socket].delete(id)
378
-
379
- decrement_remaining_queries(socket) if persistent?(socket)
380
-
381
- if !persistent?(socket) || max_attained?(socket)
382
- @@sockets.delete(socket)
383
- @@socket_hash.delete(socket)
384
- Dnsruby.log.debug("Closing socket #{socket}")
385
- socket.close rescue nil
386
- end
376
+ remove_id_from_mutex_synchronized_block(id)
377
+ end
378
+ end
379
+
380
+ # THIS MUST BE CALLED FROM INSIDE A @@mutex SYNCHRONISED BLOCK!
381
+ def remove_id_from_mutex_synchronized_block(id)
382
+ socket = @@query_hash[id].socket
383
+ @@timeouts.delete(id)
384
+ @@query_hash.delete(id)
385
+ @@socket_hash[socket].delete(id)
386
+
387
+ decrement_remaining_queries(socket) if persistent?(socket)
388
+
389
+ if !persistent?(socket) || max_attained?(socket)
390
+ @@sockets.delete(socket)
391
+ @@socket_hash.delete(socket)
392
+ Dnsruby.log.debug("Closing socket #{socket}")
393
+ socket.close rescue nil
387
394
  end
388
395
  end
389
396
 
@@ -415,12 +422,12 @@ module Dnsruby
415
422
  # Keep buffer for all TCP sockets, and return
416
423
  # to select after reading available data. Once all data has been received,
417
424
  # then process message.
418
- buf=""
425
+ buf = +""
419
426
  expected_length = 0
420
427
  @@mutex.synchronize {
421
428
  buf, expected_length = @@tcp_buffers[socket]
422
429
  if (!buf)
423
- buf = ""
430
+ buf = +""
424
431
  expected_length = 2
425
432
  @@tcp_buffers[socket]=[buf, expected_length]
426
433
  end
@@ -444,7 +451,7 @@ module Dnsruby
444
451
 
445
452
  return false
446
453
  end
447
- buf << input
454
+ buf << input if input
448
455
  rescue
449
456
  # Oh well - better luck next time!
450
457
  return false
@@ -456,7 +463,7 @@ module Dnsruby
456
463
  # We just read the data_length field. Now we need to start reading that many bytes.
457
464
  @@mutex.synchronize {
458
465
  answersize = buf.unpack('n')[0]
459
- @@tcp_buffers[socket] = ["", answersize]
466
+ @@tcp_buffers[socket] = [+"", answersize]
460
467
  }
461
468
  return tcp_read(socket)
462
469
  else
@@ -547,12 +554,10 @@ module Dnsruby
547
554
  query_header_id = @@query_hash[id].query.header.id
548
555
  if (query_header_id == e.partial_message.header.id)
549
556
  # process the response
550
- client_queue = nil
551
- res = nil
552
- query=nil
553
557
  client_queue = @@query_hash[id].client_queue
554
558
  res = @@query_hash[id].single_resolver
555
559
  query = @@query_hash[id].query
560
+ remove_id_from_mutex_synchronized_block(id)
556
561
 
557
562
  # NOW RESEND OVER TCP!
558
563
  Thread.new {
@@ -642,6 +647,7 @@ module Dnsruby
642
647
  do_select
643
648
  }
644
649
  end
650
+ wake_up_select_thread
645
651
  end
646
652
 
647
653
  def push_response_to_select(client_id, client_queue, msg, query, res)
@@ -662,6 +668,7 @@ module Dnsruby
662
668
  do_select
663
669
  }
664
670
  end
671
+ wake_up_select_thread
665
672
  end
666
673
 
667
674
  def push_validation_response_to_select(client_id, client_queue, msg, err, query, res)
@@ -678,6 +685,7 @@ module Dnsruby
678
685
  do_select
679
686
  }
680
687
  end
688
+ wake_up_select_thread
681
689
  end
682
690
 
683
691
  def send_queued_exceptions
@@ -732,7 +740,7 @@ module Dnsruby
732
740
  }
733
741
 
734
742
  responses.each do |item|
735
- client_id, client_queue, msg, err, query, res = item
743
+ client_id, client_queue, msg, err, _query, _res = item
736
744
  # push_to_client(client_id, client_queue, msg, err)
737
745
  client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err])
738
746
  notify_queue_observers(client_queue, client_id)
@@ -65,6 +65,7 @@ module Dnsruby
65
65
  @@recursor = Recursor.new
66
66
  end
67
67
  end
68
+ @@recursor.dnssec = true
68
69
  return @@recursor
69
70
  end
70
71
 
@@ -72,12 +73,15 @@ module Dnsruby
72
73
  # if (Dnssec.do_validation_with_recursor?)
73
74
  # return Recursor.new
74
75
  # else
76
+ resolver = nil
75
77
  if (Dnssec.default_resolver)
76
- return Dnssec.default_resolver
78
+ resolver = Dnssec.default_resolver
77
79
  else
78
- return Resolver.new
80
+ resolver = Resolver.new
79
81
  end
80
82
  # end
83
+ resolver.dnssec = true
84
+ return resolver
81
85
  end
82
86
  def add_dlv_key(key)
83
87
  # Is this a ZSK or a KSK?
@@ -458,7 +462,6 @@ module Dnsruby
458
462
  def check_no_wildcard_expansion(msg) # :nodoc:
459
463
  # @TODO@ Do this for NSEC3 records!!!
460
464
  proven_no_wildcards = false
461
- name = msg.question()[0].qname
462
465
  [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets|
463
466
  nsec_rrsets.each {|nsecs|
464
467
  nsecs.rrs.each {|nsec|
@@ -770,7 +773,7 @@ module Dnsruby
770
773
  }.to_s # @TODO@ worry about wildcards here?
771
774
  rec.ttl = old_ttl
772
775
  if (RUBY_VERSION >= "1.9")
773
- data.force_encoding("ASCII-8BIT")
776
+ data.force_encoding(Encoding::BINARY)
774
777
  end
775
778
  sig_data += data
776
779
  end
@@ -796,6 +799,19 @@ module Dnsruby
796
799
 
797
800
  asn1 = OpenSSL::ASN1::Sequence.new([r_asn1, s_asn1]).to_der
798
801
  verified = keyrec.public_key.verify(OpenSSL::Digest::DSS1.new, asn1, sig_data)
802
+ elsif [Algorithms.ECDSAP256SHA256, Algorithms.ECDSAP384SHA384].include?(sigrec.algorithm)
803
+ byte_size = (keyrec.public_key.group.degree + 7) / 8
804
+ sig_bytes = sigrec.signature[0..(byte_size - 1)]
805
+ sig_char = sigrec.signature[byte_size..-1] || ''
806
+ asn1 = OpenSSL::ASN1::Sequence.new([sig_bytes, sig_char].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
807
+
808
+ digest_obj = if sigrec.algorithm == Algorithms.ECDSAP384SHA384
809
+ OpenSSL::Digest::SHA384.new
810
+ else
811
+ OpenSSL::Digest::SHA256.new
812
+ end
813
+
814
+ verified = keyrec.public_key.dsa_verify_asn1(digest_obj.digest(sig_data), asn1)
799
815
  else
800
816
  raise RuntimeError.new("Algorithm #{sigrec.algorithm.code} unsupported by Dnsruby")
801
817
  end
@@ -848,6 +864,7 @@ module Dnsruby
848
864
  end
849
865
  end
850
866
  end
867
+ res.dnssec = true
851
868
  # query = Message.new(name, Types.DNSKEY)
852
869
  # query.do_validation = false
853
870
  ret = nil
@@ -1011,6 +1028,7 @@ module Dnsruby
1011
1028
  end
1012
1029
  end
1013
1030
  end
1031
+ parent_res.dnssec = true
1014
1032
  end
1015
1033
  # Use that Resolver to query for DS record and NS for children
1016
1034
  ds_rrset = current_anchor
@@ -1068,6 +1086,7 @@ module Dnsruby
1068
1086
  child_res = Resolver.new
1069
1087
  end
1070
1088
  end
1089
+ child_res.dnssec = true
1071
1090
  end
1072
1091
  end
1073
1092
  # Query for DNSKEY record, and verify against DS in parent.
@@ -1143,11 +1162,14 @@ module Dnsruby
1143
1162
  if (Dnssec.do_validation_with_recursor?)
1144
1163
  return get_recursor
1145
1164
  else
1165
+ resolver = nil
1146
1166
  if (Dnssec.default_resolver)
1147
- return Dnssec.default_resolver
1167
+ resolver = Dnssec.default_resolver
1148
1168
  else
1149
- return Resolver.new
1169
+ resolver = Resolver.new
1150
1170
  end
1171
+ resolver.dnssec = true
1172
+ return resolver
1151
1173
  end
1152
1174
  end
1153
1175
 
@@ -1306,8 +1328,7 @@ module Dnsruby
1306
1328
  msg.security_level = Message::SecurityLevel.SECURE
1307
1329
  return true
1308
1330
  end
1309
- rescue VerifyError => e
1310
- # print "Verify failed : #{e}\n"
1331
+ rescue VerifyError
1311
1332
  end
1312
1333
  end
1313
1334
  if (error)
@@ -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.
@@ -109,7 +109,7 @@ module Dnsruby
109
109
  return true
110
110
  rescue VerifyError => e
111
111
  response.security_error = e
112
- response.security_level = BOGUS
112
+ response.security_level = Message::SecurityLevel.BOGUS
113
113
  # Response security_level should already be set
114
114
  return false
115
115
  end
@@ -1,3 +1,3 @@
1
1
  module Dnsruby
2
- VERSION = '1.60.2'
2
+ VERSION = '1.73.0'
3
3
  end
@@ -68,7 +68,7 @@ module Dnsruby
68
68
  end
69
69
  zone.push(rr)
70
70
  end
71
- rescue Exception => e
71
+ rescue Exception
72
72
  raise ParseException.new("Error reading line #{io.lineno} of #{io.inspect} : [#{line}]")
73
73
  end
74
74
  end
@@ -303,7 +303,7 @@ module Dnsruby
303
303
  (split.length - 2).times {|i| line += "#{split[i+2]} "}
304
304
  line += "\n"
305
305
  split = line.split
306
- rescue Error => e
306
+ rescue Error
307
307
  end
308
308
 
309
309
  # Add the type so we can load the zone one RRSet at a time.
@@ -33,6 +33,8 @@ module Dnsruby
33
33
  attr_reader :tsig
34
34
  # Returns the tsigstate of the last transfer (nil if no TSIG signed transfer has occurred)
35
35
  attr_reader :last_tsigstate
36
+ # Sets the connect timeout in seconds
37
+ attr_accessor :connect_timeout
36
38
 
37
39
  # Sets the TSIG to sign the zone transfer with.
38
40
  # Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key)
@@ -54,6 +56,7 @@ module Dnsruby
54
56
  @tsig = nil
55
57
  @axfr = nil
56
58
  @src_address = nil
59
+ @connect_timeout = 5
57
60
  end
58
61
 
59
62
  # Perform a zone transfer (RFC1995)
@@ -107,7 +110,8 @@ module Dnsruby
107
110
  def do_transfer(zone, server) #:nodoc: all
108
111
  @transfer_type = Types.new(@transfer_type)
109
112
  @state = :InitialSoa
110
- socket = TCPSocket.new(server, @port, @src_address)
113
+ socket = Socket.tcp(server, @port, @src_address, connect_timeout: @connect_timeout)
114
+ # socket = TCPSocket.new(server, @port, @src_address)
111
115
  begin
112
116
  # Send an initial query
113
117
  msg = Message.new(zone, @transfer_type, @klass)
@@ -240,7 +244,6 @@ module Dnsruby
240
244
  end
241
245
 
242
246
  def parseRR(rec) #:nodoc: all
243
- name = rec.name
244
247
  type = rec.type
245
248
  delta = Delta.new
246
249
 
data/lib/dnsruby.rb CHANGED
@@ -25,10 +25,6 @@ require 'dnsruby/DNS'
25
25
  require 'dnsruby/hosts'
26
26
  require 'dnsruby/update'
27
27
  require 'dnsruby/zone_transfer'
28
- require 'dnsruby/dnssec'
29
- require 'dnsruby/zone_reader'
30
- require 'dnsruby/resolv'
31
-
32
28
 
33
29
  # = Dnsruby library
34
30
  # Dnsruby is a thread-aware DNS stub resolver library written in Ruby.
@@ -239,3 +235,7 @@ module Dnsruby
239
235
  class ZoneSerialError < ResolvError
240
236
  end
241
237
  end
238
+
239
+ require 'dnsruby/dnssec'
240
+ require 'dnsruby/zone_reader'
241
+ require 'dnsruby/resolv'
data/test/localdns.rb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ require_relative "test_dnsserver"
6
+
7
+ class SimpleTCPPipeliningUDPServer < Async::DNS::Server
8
+ PORT = 53938
9
+ IP = '127.0.0.1'
10
+
11
+ def initialize(**options)
12
+ super(options)
13
+
14
+ @handlers << TcpPipelineHandler.new(self, IP, PORT)
15
+ @handlers << Async::DNS::UDPServerHandler.new(self, IP, PORT)
16
+
17
+ end
18
+
19
+ def process(name, resource_class, transaction)
20
+ @logger.debug "name: #{name}"
21
+ transaction.respond!("93.184.216.34", { resource_class: ::Resolv::DNS::Resource::IN::A })
22
+ end
23
+
24
+ end
25
+
26
+
27
+ if __FILE__ == $0
28
+ RubyDNS::run_server(server_class: SimpleTCPPipeliningUDPServer)
29
+ end
data/test/spec_helper.rb CHANGED
@@ -1,22 +1,32 @@
1
+ $VERBOSE = true
2
+
3
+ if Warning.respond_to?(:[]=)
4
+ Warning[:deprecated] = true
5
+ end
6
+
1
7
  if ENV['RUN_EXTRA_TASK'] == 'TRUE'
2
- require 'coveralls'
3
- Coveralls.wear!
8
+ unless "test".frozen?
9
+ # Coverall setup term-ansi-color which isn't yet frozen string literal compatible
10
+ # Ref: https://github.com/flori/term-ansicolor/pull/38
11
+ require 'coveralls'
12
+ Coveralls.wear!
13
+ end
4
14
 
5
15
  require 'simplecov'
6
16
 
7
- # SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
8
- # [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter])
9
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
10
- SimpleCov.start do
11
- add_filter 'test/'
12
- end
17
+ # # SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
18
+ # # [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter])
19
+ # SimpleCov.formatter = Coveralls::SimpleCov::Formatter
20
+ # SimpleCov.start do
21
+ # add_filter 'test/'
22
+ # end
13
23
  end
14
24
 
15
25
  require 'minitest'
16
26
  require 'minitest/autorun'
17
27
  require 'minitest/display'
18
28
 
19
- MiniTest::Display.options = {
29
+ Minitest::Display.options = {
20
30
  suite_names: true,
21
31
  color: true,
22
32
  print: {
@@ -33,3 +43,18 @@ MiniTest::Display.options = {
33
43
  require_relative '../lib/dnsruby'
34
44
  require_relative 'test_utils'
35
45
  end.()
46
+
47
+ def with_retries(max_attempts: 5, exceptions: [Dnsruby::ServFail, Dnsruby::ResolvTimeout], success_check: ->(result) { result }, &block)
48
+ attempts = 0
49
+ while attempts < max_attempts
50
+ begin
51
+ result = block.call
52
+ return result if success_check.call(result) # e.g., for nil-check: ->(r) { r }
53
+ rescue *exceptions => e
54
+ puts "Retry #{attempts + 1}/#{max_attempts}: #{e.class} - #{e.message}" if ENV['DEBUG_TESTS']
55
+ end
56
+ sleep(1)
57
+ attempts += 1
58
+ end
59
+ raise Minitest::Assertion, "Failed after #{max_attempts} retries"
60
+ end
data/test/tc_caa.rb CHANGED
@@ -16,7 +16,6 @@
16
16
  # ++
17
17
 
18
18
  require_relative 'spec_helper'
19
- require 'pry'
20
19
 
21
20
  class TestCAA < Minitest::Test
22
21
 
@@ -26,6 +25,7 @@ class TestCAA < Minitest::Test
26
25
  {'foo.com. IN CAA 0 issue "ca.example.net"' => [0, 'issue', 'ca.example.net'],
27
26
  'foo.com. IN CAA 1 issue "ca.example.net"' => [1, 'issue', 'ca.example.net'],
28
27
  'foo.com. IN CAA 0 issuewild "ca.example.net"' => [0, 'issuewild', 'ca.example.net'],
28
+ 'foo.com. IN CAA 0 issuemail "ca.example.net"' => [0, 'issuemail', 'ca.example.net'],
29
29
  'foo.com. IN CAA 0 iodef "mailto:security@example.com"' => [0, 'iodef', 'mailto:security@example.com'],
30
30
  'foo.com. IN CAA 0 issue "ca.example.net; account=230123"' => [0, 'issue', 'ca.example.net; account=230123']
31
31
  }.each do |text, data|
@@ -45,5 +45,16 @@ class TestCAA < Minitest::Test
45
45
  end
46
46
  end
47
47
 
48
+ def test_caa_error
49
+ {
50
+ 'foo.com. IN CAA 0 ca.example.net "issue"' => [0, 'ca.example.net', 'issue'],
51
+ 'foo.com. IN CAA 0 Issue "ca.example.net"' => [0, 'Issue', 'ca.example.net']
52
+ }.each do |text, data|
53
+ assert_raises DecodeError do
54
+ RR.create(text)
55
+ end
56
+ end
57
+ end
58
+
48
59
  end
49
60