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
@@ -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 (dName) #:nodoc: all
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") [0]
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 (arg==nil)
208
- # Get default config
209
- config = Config.new
210
- # @server = config.nameserver[0]
211
- elsif (arg.kind_of? String)
212
- @server=arg
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 (((attr.to_s == "src_address")||(attr.to_s == "src_address6")) &&
219
- ((arg[attr] == nil) || (arg[attr] == "")))
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
- i = IPv4.create(@server)
234
+ IPv4.create(@server)
239
235
  # @src_address = '0.0.0.0'
240
236
  @ipv6=false
241
237
  rescue Exception
242
238
  begin
243
- i = IPv6.create(@server)
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
- query_packet = make_query_packet(msg, use_tcp)
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 = TCPSocket.new(@server, @port, src_address, src_port)
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)
@@ -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=(1)
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
- id, result, error = q.pop
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=(0)
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=(0)
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 => e
642
+ rescue ResolvTimeout, IOError
633
643
  # TheLog.debug(";; nameserver #{levelns.to_s} didn't respond")
634
644
  # next
635
645
  TheLog.debug("No response!")
@@ -128,7 +128,9 @@ module Dnsruby
128
128
  # The current Config
129
129
  attr_reader :config
130
130
 
131
- # Does this Resolver cache answers, and attempt to retrieve answer from the cache?
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 has
306
- # ignore_truncation set to false).
307
- # Note that the Message is left untouched - this means that no OPT records are
308
- # added, even if the UDP transport for the server is specified at more than 512
309
- # bytes. If it is desired to use EDNS for this packet, then you should call
310
- # the Dnsruby::PacketSender#prepare_for_dnssec(msg), or
311
- # Dnsruby::PacketSender#add_opt_rr(msg)
312
- # The return value from this method is the [response, error] tuple. Either of
313
- # 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.
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
- log_and_raise("Query id not on outstanding list! #{outstanding.length} items. #{id} not on #{outstanding}")
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 :
@@ -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
- attr_accessor :flag
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
- # @key=Base64.decode64(key_text)
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
- pkey = OpenSSL::PKey::RSA.new
343
- pkey.e = exponent
344
- pkey.n = modulus
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
- pkey = OpenSSL::PKey::DSA.new
364
- pkey.p = p
365
- pkey.q = q
366
- pkey.g = g
367
- pkey.pub_key = y
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
- pkey
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
@@ -14,11 +14,8 @@
14
14
  # limitations under the License.
15
15
  # ++
16
16
  require 'base64'
17
- begin
18
- require 'Digest/sha2'
19
- rescue LoadError
20
- require 'digest/sha2'
21
- end
17
+ require 'digest/sha2'
18
+
22
19
  module Dnsruby
23
20
  class RR
24
21
  # RFC4034, section 4
@@ -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
@@ -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,