dnsruby 1.60.1 → 1.61.4

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 (49) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +6 -7
  3. data/.yardopts +7 -0
  4. data/README.md +7 -4
  5. data/RELEASE_NOTES.md +46 -0
  6. data/Rakefile +1 -0
  7. data/demo/digroot.rb +2 -0
  8. data/dnsruby.gemspec +15 -8
  9. data/lib/dnsruby/code_mappers.rb +3 -0
  10. data/lib/dnsruby/config.rb +24 -4
  11. data/lib/dnsruby/dnssec.rb +4 -0
  12. data/lib/dnsruby/message/encoder.rb +2 -2
  13. data/lib/dnsruby/message/header.rb +15 -15
  14. data/lib/dnsruby/name.rb +18 -2
  15. data/lib/dnsruby/packet_sender.rb +14 -2
  16. data/lib/dnsruby/recursor.rb +10 -1
  17. data/lib/dnsruby/resolver.rb +12 -0
  18. data/lib/dnsruby/resource/CAA.rb +6 -4
  19. data/lib/dnsruby/resource/CDNSKEY.rb +17 -0
  20. data/lib/dnsruby/resource/CDS.rb +35 -0
  21. data/lib/dnsruby/resource/DNSKEY.rb +33 -6
  22. data/lib/dnsruby/resource/IN.rb +4 -1
  23. data/lib/dnsruby/resource/NAPTR.rb +9 -7
  24. data/lib/dnsruby/resource/URI.rb +57 -0
  25. data/lib/dnsruby/resource/generic.rb +3 -0
  26. data/lib/dnsruby/select_thread.rb +1 -2
  27. data/lib/dnsruby/single_verifier.rb +27 -4
  28. data/lib/dnsruby/update.rb +65 -48
  29. data/lib/dnsruby/validator_thread.rb +4 -4
  30. data/lib/dnsruby/version.rb +1 -1
  31. data/lib/dnsruby/zone_transfer.rb +5 -1
  32. data/test/localdns.rb +29 -0
  33. data/test/tc_caa.rb +0 -1
  34. data/test/tc_dns.rb +11 -1
  35. data/test/tc_dnskey.rb +29 -0
  36. data/test/tc_encoding.rb +31 -0
  37. data/test/tc_hs.rb +4 -3
  38. data/test/tc_long_labels.rb +46 -0
  39. data/test/tc_name.rb +19 -0
  40. data/test/tc_resolv.rb +5 -4
  41. data/test/tc_resolver.rb +27 -1
  42. data/test/tc_rr-opt.rb +9 -3
  43. data/test/tc_rr.rb +33 -0
  44. data/test/tc_soak.rb +33 -67
  45. data/test/tc_tcp_pipelining.rb +27 -19
  46. data/test/tc_update.rb +12 -0
  47. data/test/tc_verifier.rb +15 -0
  48. data/test/test_dnsserver.rb +110 -17
  49. metadata +50 -44
@@ -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
@@ -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
@@ -613,6 +621,7 @@ module Dnsruby
613
621
  }
614
622
  end
615
623
  resolver = Resolver.new({:nameserver=>nameservers})
624
+ resolver.dnssec = @dnssec
616
625
  servers = []
617
626
  resolver.single_resolvers.each {|s|
618
627
  servers.push(s.server)
@@ -487,6 +487,7 @@ module Dnsruby
487
487
  @config.nameserver.each do |ns|
488
488
  res = PacketSender.new({
489
489
  server: ns,
490
+ port: @port,
490
491
  dnssec: @dnssec,
491
492
  use_tcp: @use_tcp,
492
493
  no_tcp: @no_tcp,
@@ -890,6 +891,14 @@ module Dnsruby
890
891
  return
891
892
  end
892
893
 
894
+ begin
895
+ msg.encode
896
+ rescue EncodeError => err
897
+ Dnsruby.log.error { "Can't encode " + msg.to_s + " : #{err}" }
898
+ client_queue.push([client_query_id, err])
899
+ return
900
+ end
901
+
893
902
  tick_needed = false
894
903
  # add to our data structures
895
904
  # @mutex.synchronize{
@@ -1123,6 +1132,9 @@ module Dnsruby
1123
1132
  increment_resolver_priority(resolver) unless response.cached
1124
1133
  stop_querying(client_query_id)
1125
1134
  # @TODO@ Does the client want notified at this point?
1135
+ elsif error.kind_of?(EncodeError)
1136
+ Dnsruby.log.debug{'Encode error - sending to client'}
1137
+ send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error)
1126
1138
  else
1127
1139
  # - if it was any other error, then remove that server from the list for that query
1128
1140
  # If a Too Many Open Files error, then don't remove, but let retry work.
@@ -54,15 +54,17 @@ module Dnsruby
54
54
  end
55
55
 
56
56
  def encode_rdata(msg, canonical=false) #:nodoc: all
57
- msg.put_pack('n', flag)
57
+ msg.put_pack('C', flag)
58
58
  msg.put_string(@property_tag)
59
- msg.put_string(@property_value)
59
+ # We don't put a length byte on the final string.
60
+ msg.put_bytes(@property_value)
60
61
  end
61
62
 
62
63
  def self.decode_rdata(msg) #:nodoc: all
63
- flag, = msg.get_unpack('n')
64
+ flag, = msg.get_unpack('C')
64
65
  property_tag = msg.get_string
65
- property_value = msg.get_string
66
+ # The final string has no length byte - its length is implicit as the remainder of the packet length
67
+ property_value = msg.get_bytes
66
68
  return self.new("#{flag} #{property_tag} \"#{property_value}\"")
67
69
  end
68
70
  end
@@ -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
@@ -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!
@@ -340,8 +342,12 @@ module Dnsruby
340
342
  @key_length = (@key.length - pos) * 8
341
343
 
342
344
  pkey = OpenSSL::PKey::RSA.new
343
- pkey.e = exponent
344
- pkey.n = modulus
345
+ begin
346
+ pkey.set_key(modulus, exponent, nil) # use set_key, present in later versions of openssl gem
347
+ rescue NoMethodError
348
+ pkey.e = exponent # set_key not available in earlier versions, use this approach instead
349
+ pkey.n = modulus
350
+ end
345
351
  return pkey
346
352
  end
347
353
 
@@ -361,10 +367,31 @@ module Dnsruby
361
367
  @key_length = (pgy_len * 8)
362
368
 
363
369
  pkey = OpenSSL::PKey::DSA.new
364
- pkey.p = p
365
- pkey.q = q
366
- pkey.g = g
367
- pkey.pub_key = y
370
+ begin
371
+ pkey.set_pgq(p,g,q)
372
+ pkey.set_key(y, nil) # use set_pgq and set_key, present in later versions of openssl gem
373
+ rescue NoMethodError
374
+ pkey.p = p # set_key not available in earlier versions, use this approach instead
375
+ pkey.q = q
376
+ pkey.g = g
377
+ pkey.pub_key = y
378
+ end
379
+
380
+ pkey
381
+ end
382
+
383
+ # RFC6605, section 4
384
+ # ECDSA public keys consist of a single value, called "Q" in FIPS
385
+ # 186-3. In DNSSEC keys, Q is a simple bit string that represents the
386
+ # uncompressed form of a curve point, "x | y".
387
+ def ec_key(curve = 'prime256v1')
388
+ group = OpenSSL::PKey::EC::Group.new(curve)
389
+ pkey = OpenSSL::PKey::EC.new(group)
390
+
391
+ # DNSSEC pub does not have first octet that determines whether it's uncompressed
392
+ # or compressed form, but it's required by OpenSSL to parse EC point correctly
393
+ point_from_pub = "\x04" + @key.to_s # octet string, \x04 prefix determines uncompressed
394
+ pkey.public_key = OpenSSL::PKey::EC::Point.new(group, point_from_pub)
368
395
 
369
396
  pkey
370
397
  end
@@ -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,
@@ -52,7 +52,7 @@ module Dnsruby
52
52
  end
53
53
 
54
54
  def from_string(input) #:nodoc: all
55
- if (input.length > 0)
55
+ if (input.strip.length > 0)
56
56
  values = input.split(" ")
57
57
  @order = values [0].to_i
58
58
  @preference = values [1].to_i
@@ -76,12 +76,14 @@ module Dnsruby
76
76
  end
77
77
 
78
78
  def encode_rdata(msg, canonical=false) #:nodoc: all
79
- msg.put_pack('n', @order)
80
- msg.put_pack('n', @preference)
81
- msg.put_string(@flags)
82
- msg.put_string(@service)
83
- msg.put_string(@regexp)
84
- msg.put_name(@replacement, true)
79
+ if (@order != nil)
80
+ msg.put_pack('n', @order)
81
+ msg.put_pack('n', @preference)
82
+ msg.put_string(@flags)
83
+ msg.put_string(@service)
84
+ msg.put_string(@regexp)
85
+ msg.put_name(@replacement, true)
86
+ end
85
87
  end
86
88
 
87
89
  def self.decode_rdata(msg) #:nodoc: all
@@ -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'
@@ -24,7 +24,6 @@ require 'set'
24
24
  require 'singleton'
25
25
  require 'dnsruby/validator_thread.rb'
26
26
  module Dnsruby
27
- Thread::abort_on_exception = true
28
27
  class SelectThread #:nodoc: all
29
28
  class SelectWakeup < RuntimeError; end
30
29
  include Singleton
@@ -195,7 +194,7 @@ module Dnsruby
195
194
  rescue SelectWakeup
196
195
  # If SelectWakeup, then just restart this loop - the select call will be made with the new data
197
196
  next
198
- rescue IOError => e
197
+ rescue IOError, EncodeError => e
199
198
  # print "IO Error =: #{e}\n"
200
199
  exceptions = clean_up_closed_sockets
201
200
  exceptions.each { |exception| send_exception_to_client(*exception) }
@@ -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?
@@ -796,6 +800,19 @@ module Dnsruby
796
800
 
797
801
  asn1 = OpenSSL::ASN1::Sequence.new([r_asn1, s_asn1]).to_der
798
802
  verified = keyrec.public_key.verify(OpenSSL::Digest::DSS1.new, asn1, sig_data)
803
+ elsif [Algorithms.ECDSAP256SHA256, Algorithms.ECDSAP384SHA384].include?(sigrec.algorithm)
804
+ byte_size = (keyrec.public_key.group.degree + 7) / 8
805
+ sig_bytes = sigrec.signature[0..(byte_size - 1)]
806
+ sig_char = sigrec.signature[byte_size..-1] || ''
807
+ asn1 = OpenSSL::ASN1::Sequence.new([sig_bytes, sig_char].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
808
+
809
+ digest_obj = if sigrec.algorithm == Algorithms.ECDSAP384SHA384
810
+ OpenSSL::Digest::SHA384.new
811
+ else
812
+ OpenSSL::Digest::SHA256.new
813
+ end
814
+
815
+ verified = keyrec.public_key.dsa_verify_asn1(digest_obj.digest(sig_data), asn1)
799
816
  else
800
817
  raise RuntimeError.new("Algorithm #{sigrec.algorithm.code} unsupported by Dnsruby")
801
818
  end
@@ -848,6 +865,7 @@ module Dnsruby
848
865
  end
849
866
  end
850
867
  end
868
+ res.dnssec = true
851
869
  # query = Message.new(name, Types.DNSKEY)
852
870
  # query.do_validation = false
853
871
  ret = nil
@@ -1011,6 +1029,7 @@ module Dnsruby
1011
1029
  end
1012
1030
  end
1013
1031
  end
1032
+ parent_res.dnssec = true
1014
1033
  end
1015
1034
  # Use that Resolver to query for DS record and NS for children
1016
1035
  ds_rrset = current_anchor
@@ -1068,6 +1087,7 @@ module Dnsruby
1068
1087
  child_res = Resolver.new
1069
1088
  end
1070
1089
  end
1090
+ child_res.dnssec = true
1071
1091
  end
1072
1092
  end
1073
1093
  # Query for DNSKEY record, and verify against DS in parent.
@@ -1143,11 +1163,14 @@ module Dnsruby
1143
1163
  if (Dnssec.do_validation_with_recursor?)
1144
1164
  return get_recursor
1145
1165
  else
1166
+ resolver = nil
1146
1167
  if (Dnssec.default_resolver)
1147
- return Dnssec.default_resolver
1168
+ resolver = Dnssec.default_resolver
1148
1169
  else
1149
- return Resolver.new
1170
+ resolver = Resolver.new
1150
1171
  end
1172
+ resolver.dnssec = true
1173
+ return resolver
1151
1174
  end
1152
1175
  end
1153
1176
 
@@ -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.
@@ -20,81 +20,81 @@ module Dnsruby
20
20
 
21
21
  # The first example below shows a complete program; subsequent examples
22
22
  # show only the creation of the update packet.
23
- #
23
+ #
24
24
  # == Add a new host
25
- #
25
+ #
26
26
  # require 'Dnsruby'
27
- #
27
+ #
28
28
  # # Create the update packet.
29
29
  # update = Dnsruby::Update.new('example.com')
30
- #
30
+ #
31
31
  # # Prerequisite is that no A records exist for the name.
32
32
  # update.absent('foo.example.com.', 'A')
33
- #
33
+ #
34
34
  # # Add two A records for the name.
35
35
  # update.add('foo.example.com.', 'A', 86400, '192.168.1.2')
36
36
  # update.add('foo.example.com.', 'A', 86400, '172.16.3.4')
37
- #
37
+ #
38
38
  # # Send the update to the zone's primary master.
39
39
  # res = Dnsruby::Resolver.new({:nameserver => 'primary-master.example.com'})
40
- #
40
+ #
41
41
  # begin
42
42
  # reply = res.send_message(update)
43
43
  # print "Update succeeded\n"
44
44
  # rescue Exception => e
45
45
  # print 'Update failed: #{e}\n'
46
46
  # end
47
- #
47
+ #
48
48
  # == Add an MX record for a name that already exists
49
- #
49
+ #
50
50
  # update = Dnsruby::Update.new('example.com')
51
51
  # update.present('example.com')
52
- # update.add('example.com', Dnsruby::Types.MX, 10, 'mailhost.example.com')
53
- #
52
+ # update.add('example.com', Dnsruby::Types.MX, 86400, 10, 'mailhost.example.com')
53
+ #
54
54
  # == Add a TXT record for a name that doesn't exist
55
- #
55
+ #
56
56
  # update = Dnsruby::Update.new('example.com')
57
57
  # update.absent('info.example.com')
58
58
  # update.add('info.example.com', Types.TXT, 86400, "yabba dabba doo"')
59
- #
59
+ #
60
60
  # == Delete all A records for a name
61
- #
61
+ #
62
62
  # update = Dnsruby::Update.new('example.com')
63
63
  # update.present('foo.example.com', 'A')
64
64
  # update.delete('foo.example.com', 'A')
65
- #
65
+ #
66
66
  # == Delete all RRs for a name
67
- #
67
+ #
68
68
  # update = Dnsruby::Update.new('example.com')
69
69
  # update.present('byebye.example.com')
70
70
  # update.delete('byebye.example.com')
71
- #
71
+ #
72
72
  # == Perform a signed update
73
- #
73
+ #
74
74
  # key_name = 'tsig-key'
75
75
  # key = 'awwLOtRfpGE+rRKF2+DEiw=='
76
- #
76
+ #
77
77
  # update = Dnsruby::Update.new('example.com')
78
- # update.add('foo.example.com', 'A', 86400, 10.1.2.3'))
79
- # update.add('bar.example.com', 'A', 86400, 10.4.5.6'))
78
+ # update.add('foo.example.com', 'A', 86400, '10.1.2.3'))
79
+ # update.add('bar.example.com', 'A', 86400, '10.4.5.6'))
80
80
  # res.tsig=(key_name,key)
81
- #
81
+ #
82
82
  class Update < Message
83
83
  # Returns a Dnsruby::Update object suitable for performing a DNS
84
84
  # dynamic update. Specifically, it creates a message with the header
85
85
  # opcode set to UPDATE and the zone record type to SOA (per RFC 2136,
86
86
  # Section 2.3).
87
- #
87
+ #
88
88
  # Programs must use the push method to add RRs to the prerequisite,
89
89
  # update, and additional sections before performing the update.
90
- #
90
+ #
91
91
  # Arguments are the zone name and the class. If the zone is omitted,
92
92
  # the default domain will be taken from the resolver configuration.
93
93
  # If the class is omitted, it defaults to IN.
94
94
  # packet = Dnsruby::Update.new
95
95
  # packet = Dnsruby::Update.new('example.com')
96
96
  # packet = Dnsruby::Update.new('example.com', 'HS')
97
- #
97
+ #
98
98
  def initialize(zone=nil, klass=nil)
99
99
 
100
100
  # sort out the zone section (RFC2136, section 2.3)
@@ -115,25 +115,25 @@ module Dnsruby
115
115
  end
116
116
 
117
117
  # Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4)
118
- #
118
+ #
119
119
  # (1) RRset exists (value independent). At least one RR with a
120
120
  # specified NAME and TYPE (in the zone and class specified by
121
121
  # the Zone Section) must exist.
122
- #
122
+ #
123
123
  # update.present(name, type)
124
- #
124
+ #
125
125
  # (2) RRset exists (value dependent). A set of RRs with a
126
126
  # specified NAME and TYPE exists and has the same members
127
127
  # with the same RDATAs as the RRset specified here in this
128
128
  # Section.
129
- #
129
+ #
130
130
  # update.present(name, type, rdata)
131
- #
131
+ #
132
132
  # (4) Name is in use. At least one RR with a specified NAME (in
133
133
  # the zone and class specified by the Zone Section) must exist.
134
134
  # Note that this prerequisite is NOT satisfied by empty
135
135
  # nonterminals.
136
- #
136
+ #
137
137
  # update.present(name)
138
138
  def present(*args)
139
139
  ttl = 0
@@ -159,18 +159,18 @@ module Dnsruby
159
159
 
160
160
  # Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4)
161
161
  # Can be called with one arg :
162
- #
162
+ #
163
163
  # update.absent(name)
164
164
  # (5) Name is not in use. No RR of any type is owned by a
165
165
  # specified NAME. Note that this prerequisite IS satisfied by
166
166
  # empty nonterminals.
167
- #
167
+ #
168
168
  # Or with two :
169
- #
169
+ #
170
170
  # update.absent(name, type)
171
171
  # (3) RRset does not exist. No RRs with a specified NAME and TYPE
172
172
  # (in the zone and class denoted by the Zone Section) can exist.
173
- #
173
+ #
174
174
  def absent(*args)
175
175
  ttl = 0
176
176
  rdata = ""
@@ -191,16 +191,16 @@ module Dnsruby
191
191
 
192
192
  # Ways to create the update records (add, delete, RFC2136, section 2.5)
193
193
  # " 2.5.1 - Add To An RRset
194
- #
194
+ #
195
195
  # RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH
196
196
  # and RDATA are those being added, and CLASS is the same as the zone
197
197
  # class. Any duplicate RRs will be silently ignored by the primary
198
198
  # master."
199
- #
199
+ #
200
200
  # update.add(rr)
201
201
  # update.add([rr1, rr2])
202
202
  # update.add(name, type, ttl, rdata)
203
- #
203
+ #
204
204
  def add(*args)
205
205
  zoneclass=zone()[0].zclass
206
206
  case args[0]
@@ -244,17 +244,17 @@ module Dnsruby
244
244
  end
245
245
 
246
246
  # Ways to create the update records (add, delete, RFC2136, section 2.5)
247
- #
247
+ #
248
248
  # 2.5.2 - Delete An RRset
249
249
  # update.delete(name, type)
250
- #
251
- #
250
+ #
251
+ #
252
252
  # 2.5.3 - Delete All RRsets From A Name
253
253
  # update.delete(name)
254
- #
254
+ #
255
255
  # 2.5.4 - Delete An RR From An RRset
256
256
  # update.delete(name, type, rdata)
257
- #
257
+ #
258
258
  def delete(*args)
259
259
  ttl = 0
260
260
  klass = Classes.ANY
@@ -268,11 +268,28 @@ module Dnsruby
268
268
  resource = RR.create("#{args[0]} #{ttl} #{klass} #{args[1]} #{rdata}")
269
269
  add_update(resource)
270
270
  when 3 # name, type, rdata
271
- resource = RR.create("#{args[0]} #{ttl} IN #{args[1]} #{args[2]}")
271
+ name = args[0]
272
+ type = args[1]
273
+ rdata = args[2]
274
+ if (Types.new(type) == Types.TXT)
275
+ instring = "#{name} #{ttl} IN #{type} ";
276
+ if (String === rdata)
277
+ instring += " '#{rdata}'"
278
+ elsif (Array === rdata)
279
+ rdata.length.times {|rcounter|
280
+ instring += " '#{rdata[rcounter]}' "
281
+ }
282
+ else
283
+ instring += rdata
284
+ end
285
+ resource = RR.create(instring)
286
+ else
287
+ resource = RR.create("#{name} #{ttl} IN #{type} #{rdata}")
288
+ end
272
289
  resource.klass = Classes.NONE
273
290
  add_update(resource)
274
291
  end
275
292
  return resource
276
293
  end
277
294
  end
278
- end
295
+ end