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.
- data/EXAMPLES +9 -5
- data/demo/digdlv.rb +1 -0
- data/demo/digitar.rb +1 -0
- data/html/created.rid +1 -1
- data/html/fr_class_index.html +6 -2
- data/html/fr_method_index.html +275 -273
- data/lib/Dnsruby/Cache.rb +2 -2
- data/lib/Dnsruby/Recursor.rb +42 -42
- data/lib/Dnsruby/dnssec.rb +1 -1
- data/lib/Dnsruby/message.rb +10 -0
- data/lib/Dnsruby/name.rb +19 -0
- data/lib/Dnsruby/resource/DNSKEY.rb +10 -2
- data/lib/Dnsruby/resource/NSEC.rb +4 -1
- data/lib/Dnsruby/resource/NSEC3.rb +113 -32
- data/lib/Dnsruby/resource/NSEC3PARAM.rb +33 -20
- data/lib/Dnsruby/resource/RRSIG.rb +6 -0
- data/lib/Dnsruby/resource/resource.rb +13 -2
- data/lib/Dnsruby/select_thread.rb +1 -2
- data/lib/Dnsruby/single_verifier.rb +2 -4
- data/lib/Dnsruby/update.rb +2 -2
- data/lib/dnsruby.rb +39 -1
- data/test/tc_dnskey.rb +5 -0
- data/test/tc_nsec.rb +5 -0
- data/test/tc_nsec3.rb +37 -12
- data/test/tc_nsec3param.rb +9 -1
- data/test/tc_rrsig.rb +5 -0
- data/test/tc_tkey.rb +1 -0
- metadata +2 -2
data/lib/Dnsruby/Cache.rb
CHANGED
@@ -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)
|
data/lib/Dnsruby/Recursor.rb
CHANGED
@@ -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
|
data/lib/Dnsruby/dnssec.rb
CHANGED
@@ -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 =
|
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.
|
data/lib/Dnsruby/message.rb
CHANGED
@@ -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
|
data/lib/Dnsruby/name.rb
CHANGED
@@ -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
|
-
|
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 (
|
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 =
|
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==
|
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==
|
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
|
-
|
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
|
-
|
228
|
+
return []
|
164
229
|
end
|
165
|
-
|
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 =
|
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
|
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.
|
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
|
-
|
229
|
-
|
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)
|