dnsruby 1.30 → 1.31

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.
@@ -66,7 +66,7 @@ module Dnsruby
66
66
  @cache.delete(key)
67
67
  }
68
68
  end
69
- class CacheKey
69
+ class CacheKey # :nodoc: all
70
70
  attr_accessor :qname, :qtype, :qclass
71
71
  def initialize(*args)
72
72
  self.qclass = Classes.IN
@@ -85,7 +85,7 @@ module Dnsruby
85
85
  return "#{qname.inspect.downcase} #{qclass} #{qtype}"
86
86
  end
87
87
  end
88
- class CacheData
88
+ class CacheData # :nodoc: all
89
89
  attr_reader :expiration
90
90
  def message=(m)
91
91
  @expiration = get_expiration(m)
@@ -14,48 +14,6 @@
14
14
  #limitations under the License.
15
15
  #++
16
16
  module Dnsruby
17
- class Recursor
18
- class AddressCache # :nodoc: all
19
- # Like an array, but stores the expiration of each record.
20
- def initialize(*args)
21
- @hash = Hash.new # stores addresses against their expiration
22
- @mutex = Mutex.new # This class is thread-safe
23
- end
24
- def push(item)
25
- address, ttl = item
26
- expiration = Time.now + ttl
27
- @mutex.synchronize {
28
- @hash[address] = expiration
29
- }
30
- end
31
- def values
32
- ret =[]
33
- keys_to_delete = []
34
- @mutex.synchronize {
35
- @hash.keys.each {|address|
36
- if (@hash[address] > Time.now)
37
- ret.push(address)
38
- else
39
- keys_to_delete.push(address)
40
- end
41
- }
42
- keys_to_delete.each {|key|
43
- @hash.delete(key)
44
- }
45
- }
46
- return ret
47
- end
48
- def length
49
- @mutex.synchronize {
50
- return @hash.length
51
- }
52
- end
53
- def each()
54
- values.each {|v|
55
- yield v
56
- }
57
- end
58
- end
59
17
  #Dnsruby::Recursor - Perform recursive dns lookups
60
18
  #
61
19
  # require 'Dnsruby'
@@ -152,6 +110,48 @@ module Dnsruby
152
110
  #;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms
153
111
  # ;;; FINALLY, THE ANSWER!
154
112
  # Now,DNSSEC validation is performed (unless disabled).
113
+ class Recursor
114
+ class AddressCache # :nodoc: all
115
+ # Like an array, but stores the expiration of each record.
116
+ def initialize(*args)
117
+ @hash = Hash.new # stores addresses against their expiration
118
+ @mutex = Mutex.new # This class is thread-safe
119
+ end
120
+ def push(item)
121
+ address, ttl = item
122
+ expiration = Time.now + ttl
123
+ @mutex.synchronize {
124
+ @hash[address] = expiration
125
+ }
126
+ end
127
+ def values
128
+ ret =[]
129
+ keys_to_delete = []
130
+ @mutex.synchronize {
131
+ @hash.keys.each {|address|
132
+ if (@hash[address] > Time.now)
133
+ ret.push(address)
134
+ else
135
+ keys_to_delete.push(address)
136
+ end
137
+ }
138
+ keys_to_delete.each {|key|
139
+ @hash.delete(key)
140
+ }
141
+ }
142
+ return ret
143
+ end
144
+ def length
145
+ @mutex.synchronize {
146
+ return @hash.length
147
+ }
148
+ end
149
+ def each()
150
+ values.each {|v|
151
+ yield v
152
+ }
153
+ end
154
+ end
155
155
  attr_accessor :nameservers, :callback, :recurse, :ipv6_ok
156
156
  attr_reader :hints
157
157
  # The resolver to use for the queries
@@ -149,7 +149,7 @@ module Dnsruby
149
149
 
150
150
 
151
151
  @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet
152
- @@default_resolver = nil
152
+ @@default_resolver = Resolver.new
153
153
  # This method defines the choice of Resolver or Recursor, when the validator
154
154
  # is checking responses.
155
155
  # If set to true, then a Recursor will be used to query for the DNSSEC records.
@@ -282,6 +282,16 @@ module Dnsruby
282
282
  exception = NotImp.new
283
283
  elsif (rcode==RCode.REFUSED)
284
284
  exception = Refused.new
285
+ elsif (rcode==RCode.NOTZONE)
286
+ exception = NotZone.new
287
+ elsif (rcode==RCode.NOTAUTH)
288
+ exception = NotAuth.new
289
+ elsif (rcode==RCode.NXRRSET)
290
+ exception = NXRRSet.new
291
+ elsif (rcode==RCode.YXRRSET)
292
+ exception = YXRRSet.new
293
+ elsif (rcode==RCode.YXDOMAIN)
294
+ exception = YXDomain.new
285
295
  elsif (rcode >= RCode.BADSIG && rcode <= RCode.BADALG)
286
296
  return VerifyError.new # @TODO@
287
297
  end
@@ -28,6 +28,7 @@ module Dnsruby
28
28
  #* Name#labels
29
29
  #
30
30
  class Name
31
+ include Comparable
31
32
  MaxNameLength=255
32
33
  #--
33
34
  # A Name is a collection of Labels. Each label is presentation-formatted
@@ -116,6 +117,24 @@ module Dnsruby
116
117
  return (labels[0].string == '*')
117
118
  end
118
119
 
120
+ # Return the canonical form of this name (RFC 4034 section 6.2)
121
+ def canonical
122
+ #
123
+ return MessageEncoder.new {|msg|
124
+ msg.put_name(self, true)
125
+ }.to_s
126
+
127
+ end
128
+
129
+ def <=>(other)
130
+ # return -1 if other less than us, +1 if greater than us
131
+ return 0 if (canonical == other.canonical)
132
+ if (canonically_before(other))
133
+ return +1
134
+ end
135
+ return -1
136
+ end
137
+
119
138
  def canonically_before(n)
120
139
  if (!(Name === n))
121
140
  n = Name.create(n)
@@ -52,6 +52,7 @@ module Dnsruby
52
52
  self.protocol=3
53
53
  self.flags=ZONE_KEY
54
54
  @algorithm=Algorithms.RSASHA1
55
+ @public_key = nil
55
56
  end
56
57
 
57
58
  def protocol=(p)
@@ -149,6 +150,7 @@ module Dnsruby
149
150
  # until we come to " )" at the end, and then gsub
150
151
  # the white space out
151
152
  # Also, brackets may or may not be present
153
+ # Not to mention comments! ";"
152
154
  buf = ""
153
155
  index = 3
154
156
  end_index = data.length - 1
@@ -157,7 +159,13 @@ module Dnsruby
157
159
  index = 4
158
160
  end
159
161
  (index..end_index).each {|i|
160
- buf += data[i]
162
+ if (comment_index = data[i].index(";"))
163
+ buf += data[i].slice(0, comment_index)
164
+ # @TODO@ We lose the comments here - we should really keep them for when we write back to string format?
165
+ break
166
+ else
167
+ buf += data[i]
168
+ end
161
169
  }
162
170
  self.key=(buf)
163
171
  end
@@ -238,7 +246,7 @@ module Dnsruby
238
246
  end
239
247
 
240
248
  def public_key
241
- if (@public_key.nil?)
249
+ if (!@public_key)
242
250
  if [Algorithms.RSASHA1,
243
251
  Algorithms.RSASHA1_NSEC3_SHA1].include?(@algorithm)
244
252
  @public_key = rsa_key
@@ -75,6 +75,9 @@ module Dnsruby
75
75
  # from the wire, already decoded
76
76
  types =t
77
77
  elsif (t.instance_of?String)
78
+ if t[t.length-1, t.length]!=")"
79
+ t = t + " )"
80
+ end
78
81
  # List of mnemonics
79
82
  types=[]
80
83
  mnemonics = t.split(" ")
@@ -171,7 +174,7 @@ module Dnsruby
171
174
  0.step(65536,256) { |step|
172
175
  # Gather up the RR types for this set of 256
173
176
  types_to_go = []
174
- while (type_codes[0] < step)
177
+ while (!type_codes.empty? && type_codes[0] < step)
175
178
  types_to_go.push(type_codes[0])
176
179
  # And delete them from type_codes
177
180
  type_codes=type_codes.last(type_codes.length-1)
@@ -13,6 +13,7 @@
13
13
  #See the License for the specific language governing permissions and
14
14
  #limitations under the License.
15
15
  #++
16
+ require 'digest/sha1'
16
17
  module Base32
17
18
  module_function
18
19
  def encode32hex(str)
@@ -73,9 +74,6 @@ module Dnsruby
73
74
  #The Salt Length field defines the length of the Salt field in octets,
74
75
  #ranging in value from 0 to 255.
75
76
  attr_reader :salt_length
76
- #The Salt field is appended to the original owner name before hashing
77
- #in order to defend against pre-calculated dictionary attacks.
78
- attr_accessor :salt
79
77
  #The Hash Length field defines the length of the Next Hashed Owner
80
78
  #Name field, ranging in value from 1 to 255 octets.
81
79
  attr_reader :hash_length
@@ -96,6 +94,61 @@ module Dnsruby
96
94
  return false
97
95
  end
98
96
 
97
+ def calculate_hash
98
+ return NSEC3.calculate_hash(@name, @iterations, @salt, @hash_alg)
99
+ end
100
+
101
+ def NSEC3.calculate_hash(name, iterations, salt, hash_alg)
102
+ # RFC5155
103
+ #5. Calculation of the Hash
104
+
105
+ # Define H(x) to be the hash of x using the Hash Algorithm selected by
106
+ # the NSEC3 RR, k to be the number of Iterations, and || to indicate
107
+ # concatenation. Then define:
108
+ #
109
+ # IH(salt, x, 0) = H(x || salt), and
110
+ #
111
+ # IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
112
+ #
113
+ # Then the calculated hash of an owner name is
114
+ #
115
+ # IH(salt, owner name, iterations),
116
+ #
117
+ # where the owner name is in the canonical form, defined as:
118
+ #
119
+ # The wire format of the owner name where:
120
+ #
121
+ # 1. The owner name is fully expanded (no DNS name compression) and
122
+ # fully qualified;
123
+ # 2. All uppercase US-ASCII letters are replaced by the corresponding
124
+ # lowercase US-ASCII letters;
125
+ # 3. If the owner name is a wildcard name, the owner name is in its
126
+ # original unexpanded form, including the "*" label (no wildcard
127
+ # substitution);
128
+ #
129
+ # This form is as defined in Section 6.2 of [RFC 4034].
130
+ #
131
+
132
+ n = Name.create(name)
133
+ out = n.canonical
134
+ (0..iterations).each {
135
+ out =NSEC3.h(out + salt, hash_alg);
136
+ }
137
+ return Base32.encode32hex(out).downcase
138
+ end
139
+
140
+ def h(x) # :nodoc: all
141
+ return NSEC3.h(x, @hash_alg)
142
+ end
143
+
144
+ def NSEC3.h(x, hash_alg) # :nodoc: all
145
+ if Nsec3HashAlgorithms.SHA_1 == hash_alg
146
+ return Digest::SHA1.digest(x)
147
+ end
148
+ TheLog.error("Unknown hash algorithm #{hash_alg} used for NSEC3 hash")
149
+ return "Unknown NSEC3 hash algorithm"
150
+ end
151
+
99
152
  def hash_alg=(a)
100
153
  if (a.instance_of?String)
101
154
  if (a.length == 1)
@@ -103,7 +156,7 @@ module Dnsruby
103
156
  end
104
157
  end
105
158
  begin
106
- alg = Algorithms.new(a)
159
+ alg = Nsec3HashAlgorithms.new(a)
107
160
  @hash_alg = alg
108
161
  rescue ArgumentError => e
109
162
  raise DecodeError.new(e)
@@ -118,27 +171,28 @@ module Dnsruby
118
171
  self.types=(@types + [t])
119
172
  end
120
173
 
174
+ OPT_OUT = 1
121
175
  def flags=(f)
122
- if (f==0 || f==1)
176
+ if (f==0 || f==OPT_OUT)
123
177
  @flags=f
124
178
  else
125
179
  raise DecodeError.new("Unknown NSEC3 flags field - #{f}")
126
180
  end
127
181
  end
128
-
182
+
129
183
  #If the Opt-Out flag is set, the NSEC3 record covers zero or more
130
184
  #unsigned delegations.
131
185
  def opt_out?
132
- return (@flags==1)
133
- end
134
-
135
- def salt_length=(l)
136
- if ((l < 0) || (l > 255))
137
- raise DecodeError.new("NSEC3 salt length must be between 0 and 255")
138
- end
139
- @salt_length = l
186
+ return (@flags==OPT_OUT)
140
187
  end
141
188
 
189
+ # def salt_length=(l)
190
+ # if ((l < 0) || (l > 255))
191
+ # raise DecodeError.new("NSEC3 salt length must be between 0 and 255")
192
+ # end
193
+ # @salt_length = l
194
+ # end
195
+ #
142
196
  def hash_length=(l)
143
197
  if ((l < 0) || (l > 255))
144
198
  raise DecodeError.new("NSEC3 hash length must be between 0 and 255")
@@ -151,34 +205,52 @@ module Dnsruby
151
205
  self.hash_alg=(hash_alg)
152
206
  self.flags=(flags)
153
207
  self.iterations=(iterations)
154
- self.salt_length=(salt_length)
208
+ # self.salt_length=(salt_length)
155
209
  self.salt=(salt)
156
210
  self.hash_length=(hash_length)
157
211
  self.next_hashed=(next_hashed)
158
212
  self.types=(types)
159
213
  end
160
214
 
161
- def decode_salt(input)
215
+ #The Salt field is appended to the original owner name before hashing
216
+ #in order to defend against pre-calculated dictionary attacks.
217
+ def salt
218
+ return NSEC3.encode_salt(@salt)
219
+ end
220
+
221
+ def salt=(s)
222
+ @salt = NSEC3.decode_salt(s)
223
+ @salt_length = @salt.length
224
+ end
225
+
226
+ def NSEC3.decode_salt(input)
162
227
  if (input == "-")
163
- @salt = []
228
+ return []
164
229
  end
165
- # @TODO@
166
- @salt = input
230
+ return [input].pack("H*")
167
231
  end
168
232
 
169
- def encode_salt(s)
170
- if (s.length == 0)
233
+ def NSEC3.encode_salt(s)
234
+ if (!s || s.length == 0)
171
235
  return "-"
172
236
  end
173
- return s
237
+ return s.unpack("H*")[0]
174
238
  end
175
239
 
176
240
  def decode_next_hashed(input)
177
- @next_hashed = Base32.decode32hex(input)
241
+ @next_hashed = NSEC3.decode_next_hashed(input)
242
+ end
243
+
244
+ def NSEC3.decode_next_hashed(input)
245
+ return Base32.decode32hex(input)
178
246
  end
179
247
 
180
248
  def encode_next_hashed(n)
181
- return Base32.encode32hex(n)
249
+ return NSEC3.encode_next_hashed(n)
250
+ end
251
+
252
+ def NSEC3.encode_next_hashed(n)
253
+ return Base32.encode32hex(n).downcase
182
254
  end
183
255
 
184
256
  def from_string(input)
@@ -187,8 +259,9 @@ module Dnsruby
187
259
  self.hash_alg=(data[0]).to_i
188
260
  self.flags=(data[1]).to_i
189
261
  self.iterations=(data[2]).to_i
190
- self.salt=decode_salt(data[3])
191
- self.salt_length=(@salt.length)
262
+ # self.salt=NSEC3.decode_salt(data[3])
263
+ self.salt=(data[3])
264
+ # self.salt_length=(@salt.length)
192
265
 
193
266
  len = data[0].length + data[1].length + data[2].length + data[3].length + 4
194
267
  # There may or may not be brackets around next_hashed
@@ -203,9 +276,9 @@ module Dnsruby
203
276
  self.hash_length=(@next_hashed.length)
204
277
  len2 = data2[0].length + 1
205
278
  self.types = next_hashed_and_types[len2, next_hashed_and_types.length - len2]
206
- # self.types=data2[1]
207
- # # len = data[0].length + data[1].length + data[2].length + data[3].length + data[5].length + 7
208
- # # self.types=(input[len, input.length-len])
279
+ # self.types=data2[1]
280
+ # # len = data[0].length + data[1].length + data[2].length + data[3].length + data[5].length + 7
281
+ # # self.types=(input[len, input.length-len])
209
282
  end
210
283
  end
211
284
 
@@ -215,7 +288,8 @@ module Dnsruby
215
288
  @types.each do |t|
216
289
  type_strings.push(t.string)
217
290
  end
218
- salt = encode_salt(@salt)
291
+ # salt = NSEC3.encode_salt(@salt)
292
+ salt = salt()
219
293
  next_hashed = encode_next_hashed(@next_hashed)
220
294
  types = type_strings.join(" ")
221
295
  return "#{@hash_alg.code} #{@flags} #{@iterations} #{salt} ( #{next_hashed} #{types} )"
@@ -225,8 +299,15 @@ module Dnsruby
225
299
  end
226
300
 
227
301
  def encode_rdata(msg, canonical=false) #:nodoc: all
228
- msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, @salt_length)
229
- msg.put_bytes(@salt)
302
+ s = salt()
303
+ sl = s.length()
304
+ if (s == "-")
305
+ sl = 0
306
+ end
307
+ msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl)
308
+ if (sl > 0)
309
+ msg.put_bytes(s)
310
+ end
230
311
  msg.put_pack("c", @hash_length)
231
312
  msg.put_bytes(@next_hashed)
232
313
  types = NSEC.encode_types(self)