dnsruby 1.70.0 → 1.74.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/dependabot.yml +11 -0
  4. data/.github/workflows/ci.yml +10 -7
  5. data/RELEASE_NOTES.md +50 -0
  6. data/cache_delay.rb +20 -0
  7. data/demo/rubydig.rb +2 -0
  8. data/dnsruby.gemspec +10 -8
  9. data/fd.ruby +33 -0
  10. data/lib/dnsruby/bit_mapping.rb +2 -2
  11. data/lib/dnsruby/cache.rb +0 -2
  12. data/lib/dnsruby/code_mappers.rb +3 -2
  13. data/lib/dnsruby/config.rb +2 -5
  14. data/lib/dnsruby/hosts.rb +0 -7
  15. data/lib/dnsruby/ipv6.rb +5 -5
  16. data/lib/dnsruby/message/encoder.rb +1 -1
  17. data/lib/dnsruby/message/message.rb +2 -2
  18. data/lib/dnsruby/packet_sender.rb +19 -4
  19. data/lib/dnsruby/recursor.rb +7 -6
  20. data/lib/dnsruby/resolver.rb +40 -17
  21. data/lib/dnsruby/resource/CAA.rb +5 -2
  22. data/lib/dnsruby/resource/DNSKEY.rb +15 -8
  23. data/lib/dnsruby/resource/GPOS.rb +1 -1
  24. data/lib/dnsruby/resource/IN.rb +2 -2
  25. data/lib/dnsruby/resource/NSEC.rb +2 -2
  26. data/lib/dnsruby/resource/NXT.rb +1 -1
  27. data/lib/dnsruby/resource/OPT.rb +2 -3
  28. data/lib/dnsruby/resource/TXT.rb +1 -1
  29. data/lib/dnsruby/select_thread.rb +43 -27
  30. data/lib/dnsruby/single_verifier.rb +1 -1
  31. data/lib/dnsruby/version.rb +1 -1
  32. data/test/spec_helper.rb +22 -3
  33. data/test/tc_caa.rb +13 -0
  34. data/test/tc_dns.rb +13 -13
  35. data/test/tc_ecdsa.rb +27 -0
  36. data/test/tc_escapedchars.rb +8 -9
  37. data/test/tc_gpos.rb +1 -1
  38. data/test/tc_hs.rb +0 -5
  39. data/test/tc_message.rb +2 -2
  40. data/test/tc_name.rb +2 -2
  41. data/test/tc_nxt.rb +0 -1
  42. data/test/tc_recur.rb +4 -7
  43. data/test/tc_res_config.rb +29 -0
  44. data/test/tc_resolv.rb +11 -4
  45. data/test/tc_resolver.rb +93 -3
  46. data/test/tc_rr-unknown.rb +1 -1
  47. data/test/tc_rr.rb +4 -4
  48. data/test/tc_single_resolver.rb +9 -17
  49. data/test/tc_tcp_pipelining.rb +3 -3
  50. data/test/tc_tkey.rb +1 -7
  51. data/test/tc_update.rb +0 -1
  52. metadata +60 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4dc686d217ec8e77f7abacc6b43390f892920c36a99f88f30f10dd55945b262b
4
- data.tar.gz: 4763e50a42d153c3921c40cdadd273259fa882007da3dbc43b2098d50d895be1
3
+ metadata.gz: 31fc09cb1cf4b1c2174c67a16e96601eb25c6509e6525c3cef9159da5fd656c2
4
+ data.tar.gz: e96495f35da0ff50636052f3f27623b4fc620572c0c4109f6c47a5a81eb77801
5
5
  SHA512:
6
- metadata.gz: 0550b91ea605ae46381df4eb119b7457cef365f0981b90185479b5e490fede56fd72323b9c98b4e4f886bf16925fc6b268c6d0f753196d5cb6f1daaea05499e1
7
- data.tar.gz: 3a16635ca644a6d4654bdaa9d83c7c5e0bba69289ffa59e2ad69994099b03b2e15bc2eeaf521200104d860e5127a1066bb41e39f5492c8ee010438a9c81c9a93
6
+ metadata.gz: 84c58dc7dbf8d3c33d11ee7ea10d076d806cf1700c327b5e84fbebac08484fba8179734dbe9631c85daec38518b29ca3d8e3a29629d71a69a955f615bdf60346
7
+ data.tar.gz: 06b7c82e2b1e979f1c18aaa9567596f6724a7fb5eae8da688921958506ab9cc9ed1657f168699ec6a0112c3c25afcba419d333b7f3b4f618537fa6e6f1ee91c7
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: alexdalitz
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "bundler" # See documentation for possible values
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
@@ -6,8 +6,14 @@ jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
+ fail-fast: false
9
10
  matrix:
10
- ruby: [ '2.6', '2.7', '3.0', '3.1', '3.2' ] # , 'ruby-head' ]
11
+ ruby: [ '3.2.10', '3.3.11', '3.4.10', '4.0.5' ] # , 'ruby-head' ]
12
+ rubyopt: ['']
13
+ include:
14
+ - ruby: '3.4'
15
+ rubyopt: "--enable-frozen-string-literal --debug-frozen-string-literal"
16
+
11
17
  name: Ruby ${{ matrix.ruby }} tests
12
18
  steps:
13
19
  - uses: actions/checkout@v2
@@ -15,9 +21,6 @@ jobs:
15
21
  uses: ruby/setup-ruby@v1
16
22
  with:
17
23
  ruby-version: ${{ matrix.ruby }}
18
- - name: Bundle install
19
- run: |
20
- gem install bundler
21
- bundle install --jobs 4 --retry 3
22
- - name: Run tests
23
- run: bundle exec rake test
24
+ bundler-cache: true
25
+ - name: Run tests ${{ matrix.rubyopt }}
26
+ run: bundle exec rake test RUBYOPT="${{ matrix.rubyopt }}"
data/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Release Notes
2
2
 
3
+ ## v1.74.0
4
+
5
+ * Resolve all configured nameservers in parallel
6
+ * Only raise ArgumentError from Resolver if _all_ configured nameservers are invalid - otherwise, just log the bad one(s) and continue
7
+ * print out extra OPT data
8
+ * Fix wakeup socket deletion on stopping of select thread
9
+ * Make sure to add nameservers if using config_info in Resolver
10
+ * Fix for RRSET pruning and potential cache poisoning - thanks Alexander Tan
11
+ * Fix WKS record protocol field encoding and decoding size according to RFC 1035 Section 3.4.2 where PROTOCOL is an 8 bit (#220)
12
+ * Updating workflows to include Ruby 4, and get rdoc working for it
13
+
14
+ ## v1.73.1
15
+
16
+ * fix(caa): add support for issuevmc (#214) - thanks Mark McDonnell!
17
+
18
+ ## v1.73.0
19
+
20
+ * Ruby 3.4 updates - thanks @zarqman!
21
+ * Enable setting up a custom UDP size when DNSSEC validation is disabled
22
+ * EDNS OPT RR for udp_size > 512 - thanks @mrideout!
23
+ * Test code improvements - thanks @mrideout!
24
+ * base64 gem version fixes
25
+ * Fix for file descriptor leak on truncated UDP messages
26
+
27
+ ## v1.72.4
28
+
29
+ * allow parsing case insensitive property tags for CAA records - thanks ruby-dev!
30
+ * Add logger as a dependency for Ruby 3.5.0+ support - thanks Alexandre ZANNI!
31
+
32
+ ## v1.72.3
33
+
34
+ * Fix cached answer response time - thanks Andre Meij!
35
+ * Add base64 as a dependency for Ruby 3.4.0+ support - thanks Alexandre ZANNI!
36
+
37
+ ## v1.72.2
38
+
39
+ * Invalid CAA handling fixes - thanks Noah Smethwick and Ryan Doherty!
40
+
41
+ ## v.1.72.1
42
+
43
+ * Add issuemail CAA record support - thanks Ryan Doherty!
44
+
45
+ ## v.1.72.0
46
+
47
+ * Fix compatibility with the `--enable-string-literal` Ruby option - thanks Jean byroot Boussier!
48
+
49
+ ## v.1.71.0
50
+
51
+ * Fix assigning public_key in ECDSA - thanks Jiří Kubíček!
52
+
3
53
  ## v.1.70.0
4
54
 
5
55
  * OpenSSL 3 support
data/cache_delay.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "dnsruby"
2
+ Dnsruby::TheLog.level = Logger::DEBUG
3
+
4
+ def testme
5
+ domain1 = "example.com"
6
+ domain2 = "example.net"
7
+
8
+ start_time = Time.now
9
+ resolver = Dnsruby::Resolver.new
10
+ resolver.query(domain1)
11
+ puts "First query time: #{Time.now - start_time} seconds"
12
+
13
+ start_time = Time.now
14
+ resolver = Dnsruby::Resolver.new
15
+ resolver.query(domain2)
16
+ puts "Second query time: #{Time.now - start_time} seconds"
17
+ end
18
+
19
+ testme
20
+ testme
data/demo/rubydig.rb CHANGED
@@ -60,6 +60,8 @@ if ARGV[0] =~ /^@/
60
60
  else
61
61
  puts "Setting nameserver : #{nameserver}"
62
62
  resolver.nameserver = (nameserver.sub(/^@/, ''))
63
+ resolver.dnssec = true
64
+ resolver.do_validation = true
63
65
  puts "nameservers = #{resolver.config.nameserver}"
64
66
  zone_transfer.server = (nameserver.sub(/^@/, ''))
65
67
  end
data/dnsruby.gemspec CHANGED
@@ -15,7 +15,10 @@ SPEC = Gem::Specification.new do |s|
15
15
  stub resolver. It aims to comply with all DNS RFCs, including
16
16
  DNSSEC NSEC3 support.'
17
17
  s.license = "Apache License, Version 2.0"
18
-
18
+
19
+ # Add explicit Ruby version requirement
20
+ s.required_ruby_version = '>= 2.8.0'
21
+
19
22
  s.files = `git ls-files -z`.split("\x0")
20
23
 
21
24
  s.post_install_message = \
@@ -36,16 +39,15 @@ DNSSEC NSEC3 support.'
36
39
  }
37
40
 
38
41
  s.add_development_dependency 'rake', '>= 13.0.6'
39
- s.add_development_dependency 'minitest', '~> 5.18.0'
42
+ s.add_development_dependency 'minitest', '~> 5.25'
40
43
  s.add_development_dependency 'rubydns', '>= 2.0.2'
41
44
  s.add_development_dependency 'nio4r', '>= 2.5.8'
42
45
  s.add_development_dependency 'minitest-display', '>= 0.3.1'
43
- s.add_development_dependency('yard', '>= 0.9')
44
- # s.add_development_dependency('io-event', '>=1.1.7')
45
-
46
- if RUBY_VERSION >= "1.9.3"
47
- s.add_development_dependency 'coveralls', '~> 0.8.23'
48
- end
46
+ s.add_development_dependency 'rdoc', '>= 6.5', '< 8.0'
47
+ s.add_development_dependency 'yard', '>= 0.9'
48
+ s.add_development_dependency 'coveralls', '~> 0.8.23'
49
49
 
50
+ s.add_runtime_dependency 'base64', '>= 0.2'
51
+ s.add_runtime_dependency 'logger', '~> 1.6'
50
52
  s.add_runtime_dependency 'simpleidn', '~> 0.2.1'
51
53
  end
data/fd.ruby ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'dnsruby'
6
+ require 'thread'
7
+
8
+ #Dnsruby.log.level = Logger::DEBUG
9
+
10
+ # speed up the repro
11
+ NFILES = 30
12
+ nfiles, _ = Process.getrlimit(Process::RLIMIT_NOFILE)
13
+ Process.setrlimit(Process::RLIMIT_NOFILE, NFILES) if nfiles > NFILES
14
+
15
+ NAMESERVERS = ["192.31.80.30"]
16
+
17
+ Thread.new {
18
+ res = Dnsruby::Resolver.new(nameserver: NAMESERVERS, do_caching: false, query_timeout: 5)
19
+ loop do
20
+ begin
21
+ res.query("blahblahblah.com.edgekey.net", "CNAME")
22
+ rescue Dnsruby::ResolvError
23
+ end
24
+ sleep 0.2
25
+ end
26
+ }
27
+
28
+ loop do
29
+ # system("ls -l /proc/#{Process.pid}/fd/")
30
+ system("lsof -p #{Process.pid} | wc -l")
31
+ File.open("/") { |_| }
32
+ sleep 1
33
+ end
@@ -15,7 +15,7 @@ module Dnsruby
15
15
 
16
16
  # Converts from a binary string to a number, e.g. "\x01\x00" => 256
17
17
  def binary_string_to_number(string)
18
- string = string.clone.force_encoding(Encoding::ASCII_8BIT)
18
+ string = string.b
19
19
  string.bytes.inject(0) do |number, byte|
20
20
  number * 256 + byte.ord
21
21
  end
@@ -25,7 +25,7 @@ module Dnsruby
25
25
  # Converts a number to a binary encoded string, e.g. 256 => "\x01\x00"
26
26
  def number_to_binary_string(number, min_length = 0)
27
27
  assert_non_negative(number)
28
- binary_string = ''.force_encoding(Encoding::ASCII_8BIT)
28
+ binary_string = ''.b
29
29
 
30
30
  while number > 0
31
31
  byte_value = number & 0xFF
data/lib/dnsruby/cache.rb CHANGED
@@ -68,14 +68,12 @@ module Dnsruby
68
68
  # This method "fixes up" the response, so that the header and ttls are OK
69
69
  # The resolver will still need to copy the flags and ID across from the query
70
70
  def find(qname, qtype, qclass = Classes.IN)
71
- # print "CACHE find : #{qname}, #{qtype}\n"
72
71
  qn = Name.create(qname)
73
72
  qn.absolute = true
74
73
  key = CacheKey.new(qn, qtype, qclass).to_s
75
74
  @mutex.synchronize {
76
75
  data = @cache[key]
77
76
  if (!data)
78
- # print "CACHE lookup failed\n"
79
77
  return nil
80
78
  end
81
79
  if (data.expiration <= Time.now.to_i)
@@ -70,7 +70,8 @@ module Dnsruby
70
70
  # (RFC3597)
71
71
  # See typesbyval and typesbyname, these beasts have the same functionality
72
72
  def Classes.classesbyname(name) #:nodoc: all
73
- name.upcase!;
73
+ name = name.upcase
74
+
74
75
  if to_code(name)
75
76
  return to_code(name)
76
77
  end
@@ -202,7 +203,7 @@ module Dnsruby
202
203
  # mnemonic. If the TYPE mapping is not specified the generic mnemonic
203
204
  # TYPE### is returned.
204
205
  def Types.typesbyname(name) #:nodoc: all
205
- name.upcase!
206
+ name = name.upcase
206
207
 
207
208
  if to_code(name)
208
209
  return to_code(name)
@@ -294,6 +294,8 @@ module Dnsruby
294
294
  end
295
295
  }
296
296
  if (!found)
297
+ # Log the error, in case Resolver is resolving a set of nameservers, and swallows the exception
298
+ TheLog.error{"Recursor can't locate #{server}"}
297
299
  raise ArgumentError.new("Recursor can't locate #{server}")
298
300
  end
299
301
  end
@@ -318,11 +320,6 @@ module Dnsruby
318
320
  f.each {|line|
319
321
  line.sub!(/[#;].*/, '')
320
322
  keyword, *args = line.split(/\s+/)
321
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.8")
322
- args.each { |arg|
323
- arg.untaint
324
- }
325
- end
326
323
  next unless keyword
327
324
  case keyword
328
325
  when 'port'
data/lib/dnsruby/hosts.rb CHANGED
@@ -57,19 +57,12 @@ module Dnsruby
57
57
  line.sub!(/#.*/, '')
58
58
  addr, hostname, *aliases = line.split(/\s+/)
59
59
  next unless addr
60
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.8")
61
- addr.untaint
62
- hostname.untaint
63
- end
64
60
  @addr2name[addr] = [] unless @addr2name.include? addr
65
61
  @addr2name[addr] << hostname
66
62
  @addr2name[addr] += aliases
67
63
  @name2addr[hostname] = [] unless @name2addr.include? hostname
68
64
  @name2addr[hostname] << addr
69
65
  aliases.each {|n|
70
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.8")
71
- n.untaint
72
- end
73
66
  @name2addr[n] = [] unless @name2addr.include? n
74
67
  @name2addr[n] << addr
75
68
  }
data/lib/dnsruby/ipv6.rb CHANGED
@@ -57,14 +57,14 @@ module Dnsruby
57
57
  when IPv6
58
58
  return arg
59
59
  when String
60
- address = ''
60
+ address = +''
61
61
  if Regex_8Hex =~ arg
62
62
  arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
63
63
  elsif Regex_CompressedHex =~ arg
64
64
  prefix = $1
65
65
  suffix = $2
66
- a1 = ''
67
- a2 = ''
66
+ a1 = +''
67
+ a2 = +''
68
68
  prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
69
69
  suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
70
70
  omitlen = 16 - a1.length - a2.length
@@ -80,8 +80,8 @@ module Dnsruby
80
80
  elsif Regex_CompressedHex4Dec =~ arg
81
81
  prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
82
82
  if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
83
- a1 = ''
84
- a2 = ''
83
+ a1 = +''
84
+ a2 = +''
85
85
  prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
86
86
  suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
87
87
  omitlen = 12 - a1.length - a2.length
@@ -1,7 +1,7 @@
1
1
  module Dnsruby
2
2
  class MessageEncoder #:nodoc: all
3
3
  def initialize
4
- @data = ''
4
+ @data = +''
5
5
  @names = {}
6
6
  yield self if block_given?
7
7
  end
@@ -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"
@@ -204,6 +204,7 @@ module Dnsruby
204
204
  @tcp_pipelining_max_queries = :infinite
205
205
  @use_counts = {}
206
206
 
207
+ ignore_config_resolv_errors = false;
207
208
  if arg.nil?
208
209
  elsif arg.kind_of? String
209
210
  @server = arg
@@ -212,10 +213,14 @@ module Dnsruby
212
213
  elsif arg.kind_of? Hash
213
214
  arg.keys.each do |attr|
214
215
  begin
215
- if ((attr.to_s == "src_address" || attr.to_s == "src_address6") &&
216
- (arg[attr] == nil || arg[attr] == ""))
216
+ if (attr.to_s == "ignore_config_resolv_errors")
217
+ ignore_config_resolv_errors = arg[attr]
217
218
  else
218
- send(attr.to_s + "=", arg[attr])
219
+ if ((attr.to_s == "src_address" || attr.to_s == "src_address6") &&
220
+ (arg[attr] == nil || arg[attr] == ""))
221
+ else
222
+ send(attr.to_s + "=", arg[attr])
223
+ end
219
224
  end
220
225
  rescue Exception => e
221
226
  Dnsruby.log.error { "PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n" }
@@ -223,7 +228,17 @@ module Dnsruby
223
228
  end
224
229
  end
225
230
  # Check server is IP
226
- @server=Config.resolve_server(@server)
231
+ # We should allow the call of PacketSender.new to flag just to log an error if this fails
232
+ # and then return null
233
+ if ignore_config_resolv_errors
234
+ begin
235
+ @server=Config.resolve_server(@server)
236
+ rescue ArgumentError
237
+ return
238
+ end
239
+ else
240
+ @server=Config.resolve_server(@server)
241
+ end
227
242
 
228
243
  check_ipv6
229
244
  # ResolverRegister::register_single_resolver(self)
@@ -500,7 +500,7 @@ module Dnsruby
500
500
  return nil
501
501
  end
502
502
 
503
- known_zone.sub!(/\.*$/, ".")
503
+ known_zone = known_zone.sub(/\.*$/, ".")
504
504
 
505
505
  ns = [] # Array of AddressCaches (was array of array of addresses)
506
506
  @@mutex.synchronize{
@@ -678,7 +678,8 @@ module Dnsruby
678
678
  if (of.length <= known_zone.length)
679
679
  TheLog.debug(";; _dorecursion() Deadbeat name server did not provide new information.")
680
680
  next
681
- elsif (of =~ /#{known_zone}/)
681
+ # elsif (of =~ /#{known_zone}/)
682
+ elsif (of == known_zone || known_zone.length == 1 || of.end_with?(".#{known_zone}"))
682
683
  TheLog.debug(";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].")
683
684
  auth[server] ||= AddressCache.new #[] @TODO@ If there is no additional record for this, then we want to use the authority!
684
685
  if (rr.type == Types.NS)
@@ -688,7 +689,7 @@ module Dnsruby
688
689
  end
689
690
  end
690
691
  else
691
- TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?")
692
+ TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom.to_s + "] thinks " + of.to_s + " is closer than " + known_zone.to_s + "?")
692
693
  return nil
693
694
  end
694
695
  else
@@ -750,9 +751,9 @@ module Dnsruby
750
751
  # Now prune the response of any unrelated rrsets (RFC5452 section6)
751
752
  # "One very simple way to achieve this is to only accept data if it is
752
753
  # part of the domain for which the query was intended."
753
- if (!packet.header.aa)
754
- return
755
- end
754
+ # if (!packet.header.aa)
755
+ # return
756
+ # end
756
757
  if (!packet.question()[0])
757
758
  return
758
759
  end
@@ -299,15 +299,17 @@ module Dnsruby
299
299
  # is sent (TSIG signatures will be applied if configured on the Resolver).
300
300
  # Retries are handled as the Resolver is configured to do.
301
301
  # Incoming responses to the query are not cached or validated (although TCP
302
- # fallback will be performed if the TC bit is set and the (Single)Resolver has
303
- # ignore_truncation set to false).
304
- # Note that the Message is left untouched - this means that no OPT records are
305
- # added, even if the UDP transport for the server is specified at more than 512
306
- # bytes. If it is desired to use EDNS for this packet, then you should call
307
- # the Dnsruby::PacketSender#prepare_for_dnssec(msg), or
308
- # Dnsruby::PacketSender#add_opt_rr(msg)
309
- # The return value from this method is the [response, error] tuple. Either of
310
- # these values may be nil - it is up to the client to check.
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.
311
313
  #
312
314
  # example :
313
315
  #
@@ -399,7 +401,7 @@ module Dnsruby
399
401
  if @single_resolvers.length == 0
400
402
  Thread.start {
401
403
  sleep(@query_timeout == 0 ? 1 : @query_timeout)
402
- client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no nameservers configured')])
404
+ client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no valid nameservers configured')])
403
405
  }
404
406
  end
405
407
  client_query_id
@@ -434,7 +436,7 @@ module Dnsruby
434
436
  # * :tcp_pipelining
435
437
  # * :tcp_pipelining_max_queries - can be a number or :infinite symbol
436
438
  def initialize(*args)
437
- # @TODO@ Should we allow :namesver to be an RRSet of NS records? Would then need to randomly order them?
439
+ # @TODO@ Should we allow :nameserver to be an RRSet of NS records? Would then need to randomly order them?
438
440
  @resolver_ruby = nil
439
441
  @src_address = nil
440
442
  @src_address6 = nil
@@ -451,6 +453,7 @@ module Dnsruby
451
453
  begin
452
454
  if key == :config_info
453
455
  @config.set_config_info(args[0][:config_info])
456
+ add_config_nameservers
454
457
  elsif key == :nameserver
455
458
  set_config_nameserver(args[0][:nameserver])
456
459
  elsif key == :nameservers
@@ -479,10 +482,11 @@ module Dnsruby
479
482
  @config.get_ready
480
483
  end
481
484
  @configured = true
482
- @single_res_mutex.synchronize {
483
485
  # Add the Config nameservers
484
- @config.nameserver.each do |ns|
485
- res = PacketSender.new({
486
+ threads = []
487
+ @config.nameserver.each do |ns|
488
+ threads << Thread.new do
489
+ PacketSender.new({
486
490
  server: ns,
487
491
  port: @port,
488
492
  dnssec: @dnssec,
@@ -497,10 +501,22 @@ module Dnsruby
497
501
  src_address6: @src_address6,
498
502
  src_port: @src_port,
499
503
  recurse: @recurse,
500
- udp_size: @udp_size})
501
- @single_resolvers.push(res) if res
504
+ udp_size: @udp_size,
505
+ ignore_config_resolv_errors: true})
502
506
  end
503
- }
507
+ end
508
+
509
+ new_resolvers = threads.map(&:value).compact
510
+ new_resolvers = new_resolvers.select do |resolver|
511
+ !resolver.nil? && !resolver.server.nil?
512
+ end
513
+ # if new_resolvers.empty?
514
+ # raise ArgumentError.new("No valid nameservers found in config")
515
+ # end
516
+ @single_res_mutex.synchronize { @single_resolvers.concat(new_resolvers) }
517
+ if @single_resolvers.nil? || @single_resolvers.empty?
518
+ raise ArgumentError.new("No valid nameservers found")
519
+ end
504
520
  end
505
521
 
506
522
  def set_config_nameserver(n)
@@ -888,6 +904,13 @@ module Dnsruby
888
904
  return
889
905
  end
890
906
 
907
+ # According to RFC 6891, a UDP payload size >512 bytes requires an
908
+ # EDNS OPT RR in the message.
909
+ if @parent.udp_size > 512 && !msg.get_opt
910
+ TheLog.debug("Automatically adding EDNS OPT RR for udp_size=#{ @parent.udp_size }")
911
+ msg.add_additional(Dnsruby::RR::OPT.new(@parent.udp_size))
912
+ end
913
+
891
914
  begin
892
915
  msg.encode
893
916
  rescue EncodeError => err
@@ -21,7 +21,7 @@ module Dnsruby
21
21
  ClassValue = nil #:nodoc: all
22
22
  TypeValue= Types::CAA #:nodoc: all
23
23
 
24
- # The property tag for the record (issue|issuewild|iodef)
24
+ # The property tag for the record (issue|issuewild|issuemail|issuevmc|iodef|contactemail|contactphone)
25
25
  attr_accessor :property_tag
26
26
  # The value for the property_tag
27
27
  attr_accessor :property_value
@@ -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|contactemail|contactphone) "(.+)"$/).match(input)
46
+ matches = (/(\d+) (issuewild|issuemail|issuevmc|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]
@@ -383,7 +383,7 @@ module Dnsruby
383
383
  ]
384
384
  )
385
385
 
386
- pkey = OpenSSL::PKey::DSA.new(asn1.to_der)
386
+ OpenSSL::PKey::DSA.new(asn1.to_der)
387
387
  end
388
388
 
389
389
  # RFC6605, section 4
@@ -392,15 +392,22 @@ module Dnsruby
392
392
  # uncompressed form of a curve point, "x | y".
393
393
  def ec_key(curve = 'prime256v1')
394
394
  group = OpenSSL::PKey::EC::Group.new(curve)
395
- pkey = OpenSSL::PKey::EC.new(group)
396
395
  # DNSSEC pub does not have first octet that determines whether it's uncompressed
397
396
  # or compressed form, but it's required by OpenSSL to parse EC point correctly
398
- public_key_with_prefix = "\x04" + @key.to_s
399
- pkey = OpenSSL::PKey::EC.new(group, public_key_with_prefix)
400
-
401
- pkey
402
-
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)
403
410
  end
404
411
  end
405
412
  end
406
- end
413
+ end
@@ -104,7 +104,7 @@ module Dnsruby
104
104
  end
105
105
 
106
106
  def self.build_rdata(longitude, latitude, altitude)
107
- binary_string = ''.force_encoding('ASCII-8BIT')
107
+ binary_string = ''.b
108
108
 
109
109
  binary_string << longitude.length.chr
110
110
  binary_string << longitude
@@ -87,13 +87,13 @@ module Dnsruby
87
87
 
88
88
  def encode_rdata(msg, canonical=false) #:nodoc: all
89
89
  msg.put_bytes(@address.address)
90
- msg.put_pack("n", @protocol)
90
+ msg.put_pack("C", @protocol)
91
91
  msg.put_bytes(@bitmap)
92
92
  end
93
93
 
94
94
  def self.decode_rdata(msg) #:nodoc: all
95
95
  address = IPv4.new(msg.get_bytes(4))
96
- protocol, = msg.get_unpack("n")
96
+ protocol, = msg.get_unpack("C")
97
97
  bitmap = msg.get_bytes
98
98
  return self.new(address, protocol, bitmap)
99
99
  end