dnsruby 1.36 → 1.37
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/demo/axfr.rb +16 -0
- data/demo/check_soa.rb +17 -0
- data/demo/check_zone.rb +16 -0
- data/demo/digdlv.rb +16 -0
- data/demo/digitar.rb +16 -0
- data/demo/digroot.rb +92 -0
- data/demo/example_recurse.rb +16 -0
- data/demo/mresolv.rb +17 -0
- data/demo/mx.rb +16 -0
- data/demo/rubydig.rb +16 -0
- data/demo/trace_dns.rb +16 -0
- data/html/created.rid +1 -1
- data/html/fr_class_index.html +0 -6
- data/html/fr_method_index.html +332 -348
- data/lib/Dnsruby/Cache.rb +18 -1
- data/lib/Dnsruby/iana_ports.rb +17 -1
- data/lib/Dnsruby/key_cache.rb +16 -0
- data/lib/Dnsruby/resource/delete_me.rhtml +6 -0
- data/lib/Dnsruby/select_thread.rb.michael.rb +602 -0
- data/lib/Dnsruby/single_verifier.rb +17 -0
- data/lib/Dnsruby/validator_thread.rb +16 -0
- data/lib/Dnsruby/zone_transfer.rb +16 -0
- data/test/resolv.conf +16 -1
- data/test/tc_auth.rb +49 -0
- data/test/tc_cache.rb +16 -0
- data/test/tc_dlv.rb +16 -0
- data/test/tc_dnskey.rb +16 -0
- data/test/tc_ds.rb +16 -0
- data/test/tc_itar.rb +16 -0
- data/test/tc_nsec.rb +16 -0
- data/test/tc_nsec3.rb +16 -0
- data/test/tc_nsec3param.rb +16 -0
- data/test/tc_queue.rb +16 -0
- data/test/tc_recur.rb +16 -0
- data/test/tc_rrset.rb +16 -0
- data/test/tc_rrsig.rb +16 -0
- data/test/tc_soak_base.rb +16 -0
- data/test/tc_validator.rb +16 -0
- data/test/tc_verifier.rb +16 -0
- data/test/ts_queue.rb +3 -0
- metadata +7 -7
- data/lib/Dnsruby/event_machine_interface.rb +0 -266
- data/test/tc_event_machine_deferrable.rb +0 -85
- data/test/tc_event_machine_res.rb +0 -174
- data/test/tc_event_machine_single_res.rb +0 -101
- data/test/tc_event_machine_soak.rb +0 -98
data/lib/Dnsruby/Cache.rb
CHANGED
@@ -1,4 +1,21 @@
|
|
1
|
-
|
1
|
+
#--
|
2
|
+
#Copyright 2007 Nominet UK
|
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
|
+
|
17
|
+
|
18
|
+
## This class implements a cache.
|
2
19
|
# It stores data under qname-qclass-qtype tuples.
|
3
20
|
# Each tuple indexes a CacheData object (which
|
4
21
|
# stores a Message, and an expiration).
|
data/lib/Dnsruby/iana_ports.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
-
|
1
|
+
#--
|
2
|
+
#Copyright 2007 Nominet UK
|
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
|
+
|
17
|
+
## This Array contains a list of the ports which should not be used by Dnsruby
|
2
18
|
module Dnsruby
|
3
19
|
class Iana # :nodoc: all
|
4
20
|
IANA_PORTS = [1,
|
data/lib/Dnsruby/key_cache.rb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
#--
|
2
|
+
#Copyright 2007 Nominet UK
|
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
|
+
|
1
17
|
module Dnsruby
|
2
18
|
class KeyCache #:nodoc: all
|
3
19
|
# Cache includes expiration time for keys
|
@@ -0,0 +1,602 @@
|
|
1
|
+
#--
|
2
|
+
#Copyright 2007 Nominet UK
|
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
|
+
require 'socket'
|
17
|
+
#require 'thread'
|
18
|
+
begin
|
19
|
+
require 'fastthread'
|
20
|
+
rescue LoadError
|
21
|
+
require 'thread'
|
22
|
+
end
|
23
|
+
require 'singleton'
|
24
|
+
require 'Dnsruby/validator_thread.rb'
|
25
|
+
module Dnsruby
|
26
|
+
Thread::abort_on_exception = true
|
27
|
+
class SelectThread #:nodoc: all
|
28
|
+
class SelectWakeup < RuntimeError; end
|
29
|
+
include Singleton
|
30
|
+
# This singleton class runs a continuous select loop which
|
31
|
+
# listens for responses on all of the in-use sockets.
|
32
|
+
# When a new query is sent, the thread is woken up, and
|
33
|
+
# the socket is added to the select loop (and the new timeout
|
34
|
+
# calculated).
|
35
|
+
# Note that a combination of the socket and the packet ID is
|
36
|
+
# sufficient to uniquely identify the query to the select thread.
|
37
|
+
#
|
38
|
+
# But how do we find the response queue for a particular query?
|
39
|
+
# Hash of client_id->[query, client_queue, socket]
|
40
|
+
# and socket->[client_id]
|
41
|
+
#
|
42
|
+
# @todo@ should we implement some of cancel function?
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
@@mutex = Mutex.new
|
46
|
+
@@mutex.synchronize {
|
47
|
+
@@in_select=false
|
48
|
+
# @@notifier,@@notified=IO.pipe
|
49
|
+
@@sockets = [] # @@notified]
|
50
|
+
@@timeouts = Hash.new
|
51
|
+
# @@mutex.synchronize do
|
52
|
+
@@query_hash = Hash.new
|
53
|
+
@@socket_hash = Hash.new
|
54
|
+
@@observers = Hash.new
|
55
|
+
@@tick_observers = []
|
56
|
+
@@queued_exceptions=[]
|
57
|
+
@@queued_responses=[]
|
58
|
+
@@queued_validation_responses=[]
|
59
|
+
@@wakeup_sockets = Socket::socketpair(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
|
60
|
+
# end
|
61
|
+
# Now start the select thread
|
62
|
+
@@select_thread = Thread.new {
|
63
|
+
do_select
|
64
|
+
}
|
65
|
+
# # Start the validator thread
|
66
|
+
# @@validator = ValidatorThread.instance
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
class QuerySettings
|
71
|
+
attr_accessor :query_bytes, :query, :ignore_truncation, :client_queue,
|
72
|
+
:client_query_id, :socket, :dest_server, :dest_port, :endtime, :udp_packet_size,
|
73
|
+
:single_resolver
|
74
|
+
# new(query_bytes, query, ignore_truncation, client_queue, client_query_id,
|
75
|
+
# socket, dest_server, dest_port, endtime, , udp_packet_size, single_resolver)
|
76
|
+
def initialize(*args)
|
77
|
+
@query_bytes = args[0]
|
78
|
+
@query = args[1]
|
79
|
+
@ignore_truncation=args[2]
|
80
|
+
@client_queue = args[3]
|
81
|
+
@client_query_id = args[4]
|
82
|
+
@socket = args[5]
|
83
|
+
@dest_server = args[6]
|
84
|
+
@dest_port=args[7]
|
85
|
+
@endtime = args[8]
|
86
|
+
@udp_packet_size = args[9]
|
87
|
+
@single_resolver = args[10]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_to_select(query_settings)
|
92
|
+
# Add the query to sockets, and then wake the select thread up
|
93
|
+
@@mutex.synchronize {
|
94
|
+
check_select_thread_synchronized
|
95
|
+
# @TODO@ This assumes that all client_query_ids are unique!
|
96
|
+
# Would be a good idea at least to check this...
|
97
|
+
@@query_hash[query_settings.client_query_id]=query_settings
|
98
|
+
@@socket_hash[query_settings.socket]=[query_settings.client_query_id] # @todo@ If we use persistent sockets then we need to update this array
|
99
|
+
@@timeouts[query_settings.client_query_id]=query_settings.endtime
|
100
|
+
@@sockets.push(query_settings.socket)
|
101
|
+
}
|
102
|
+
@@wakeup_sockets[0].send("wakeup!", 0)
|
103
|
+
end
|
104
|
+
|
105
|
+
def check_select_thread_synchronized
|
106
|
+
if (!@@select_thread.alive?)
|
107
|
+
Dnsruby.log.debug{"Restarting select thread"}
|
108
|
+
@@select_thread = Thread.new {
|
109
|
+
do_select
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def select_thread_alive?
|
115
|
+
ret=true
|
116
|
+
@@mutex.synchronize{
|
117
|
+
ret = @@select_thread.alive?
|
118
|
+
}
|
119
|
+
return ret
|
120
|
+
end
|
121
|
+
|
122
|
+
def do_select
|
123
|
+
unused_loop_count = 0
|
124
|
+
last_tick_time = Time.now - 10
|
125
|
+
while true do
|
126
|
+
if (last_tick_time < (Time.now - 0.5))
|
127
|
+
send_tick_to_observers # ONLY NEED TO SEND THIS TWICE A SECOND - NOT EVERY SELECT!!!
|
128
|
+
last_tick_time = Time.now
|
129
|
+
end
|
130
|
+
send_queued_exceptions
|
131
|
+
send_queued_responses
|
132
|
+
send_queued_validation_responses
|
133
|
+
timeout = tick_time = 0.1 # We provide a timer service to various Dnsruby classes
|
134
|
+
sockets=[]
|
135
|
+
timeouts=[]
|
136
|
+
has_observer = false
|
137
|
+
@@mutex.synchronize {
|
138
|
+
sockets = @@sockets
|
139
|
+
timeouts = @@timeouts.values
|
140
|
+
has_observer = !@@observers.empty?
|
141
|
+
}
|
142
|
+
sockets << @@wakeup_sockets[1]
|
143
|
+
if (timeouts.length > 0)
|
144
|
+
timeouts.sort!
|
145
|
+
timeout = timeouts[0] - Time.now
|
146
|
+
if (timeout <= 0)
|
147
|
+
process_timeouts
|
148
|
+
timeout = 0
|
149
|
+
next
|
150
|
+
end
|
151
|
+
end
|
152
|
+
ready=nil
|
153
|
+
if (has_observer && (timeout > tick_time))
|
154
|
+
timeout = tick_time
|
155
|
+
end
|
156
|
+
# next if (timeout < 0)
|
157
|
+
begin
|
158
|
+
ready, write, errors = IO.select(sockets, nil, nil, timeout)
|
159
|
+
rescue SelectWakeup
|
160
|
+
# If SelectWakeup, then just restart this loop - the select call will be made with the new data
|
161
|
+
next
|
162
|
+
end
|
163
|
+
if ready && ready.include?(@@wakeup_sockets[1])
|
164
|
+
ready.delete(@@wakeup_sockets[1])
|
165
|
+
wakeup_msg = @@wakeup_sockets[1].recv(20)
|
166
|
+
end
|
167
|
+
if (ready == nil)
|
168
|
+
# proces the timeouts
|
169
|
+
process_timeouts
|
170
|
+
unused_loop_count+=1
|
171
|
+
else
|
172
|
+
process_ready(ready)
|
173
|
+
unused_loop_count=0
|
174
|
+
# process_error(errors)
|
175
|
+
end
|
176
|
+
@@mutex.synchronize{
|
177
|
+
if (unused_loop_count > 10 && @@query_hash.empty? && @@observers.empty?)
|
178
|
+
Dnsruby.log.debug{"Stopping select loop"}
|
179
|
+
return
|
180
|
+
end
|
181
|
+
}
|
182
|
+
# }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def process_error(errors)
|
187
|
+
Dnsruby.log.debug{"Error! #{errors.inspect}"}
|
188
|
+
# @todo@ Process errors [can we do this in single socket environment?]
|
189
|
+
end
|
190
|
+
|
191
|
+
# @@query_hash[query_settings.client_query_id]=query_settings
|
192
|
+
# @@socket_hash[query_settings.socket]=[query_settings.client_query_id] # @todo@ If we use persistent sockets then we need to update this array
|
193
|
+
def process_ready(ready)
|
194
|
+
ready.each do |socket|
|
195
|
+
query_settings = nil
|
196
|
+
@@mutex.synchronize{
|
197
|
+
# Can do this if we have a query per socket, but not otherwise...
|
198
|
+
c_q_id = @@socket_hash[socket][0] # @todo@ If we use persistent sockets then this won't work
|
199
|
+
query_settings = @@query_hash[c_q_id]
|
200
|
+
}
|
201
|
+
next if !query_settings
|
202
|
+
udp_packet_size = query_settings.udp_packet_size
|
203
|
+
msg, bytes = get_incoming_data(socket, udp_packet_size)
|
204
|
+
if (msg!=nil)
|
205
|
+
# Check that the IP we received from was the IP we sent to!
|
206
|
+
answerip = msg.answerip.downcase
|
207
|
+
answerfrom = msg.answerfrom.downcase
|
208
|
+
dest_server = query_settings.dest_server
|
209
|
+
if (dest_server && (dest_server != '0.0.0.0') &&
|
210
|
+
(answerip != query_settings.dest_server.downcase) &&
|
211
|
+
(answerfrom != query_settings.dest_server.downcase))
|
212
|
+
Dnsruby.log.warn("Unsolicited response received from #{answerip} instead of #{query_settings.dest_server}")
|
213
|
+
else
|
214
|
+
send_response_to_client(msg, bytes, socket)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
ready.delete(socket)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def send_response_to_client(msg, bytes, socket)
|
222
|
+
# Figure out which client_ids we were expecting on this socket, then see if any header ids match up
|
223
|
+
# @TODO@ Can get rid of this, as we only have one query per socket.
|
224
|
+
client_ids=[]
|
225
|
+
@@mutex.synchronize{
|
226
|
+
client_ids = @@socket_hash[socket]
|
227
|
+
}
|
228
|
+
# get the queries associated with them
|
229
|
+
client_ids.each do |id|
|
230
|
+
query_header_id=nil
|
231
|
+
@@mutex.synchronize{
|
232
|
+
query_header_id = @@query_hash[id].query.header.id
|
233
|
+
}
|
234
|
+
if (query_header_id == msg.header.id)
|
235
|
+
# process the response
|
236
|
+
client_queue = nil
|
237
|
+
res = nil
|
238
|
+
query=nil
|
239
|
+
@@mutex.synchronize{
|
240
|
+
client_queue = @@query_hash[id].client_queue
|
241
|
+
res = @@query_hash[id].single_resolver
|
242
|
+
query = @@query_hash[id].query
|
243
|
+
}
|
244
|
+
tcp = (socket.class == TCPSocket)
|
245
|
+
# At this point, we should check if the response is OK
|
246
|
+
if (ret = res.check_response(msg, bytes, query, client_queue, id, tcp))
|
247
|
+
remove_id(id)
|
248
|
+
exception = msg.get_exception
|
249
|
+
if (ret.instance_of?TsigError)
|
250
|
+
exception = ret
|
251
|
+
end
|
252
|
+
Dnsruby.log.debug{"Pushing response to client queue"}
|
253
|
+
push_to_client(id, client_queue, msg, exception, query, res)
|
254
|
+
# client_queue.push([id, msg, exception])
|
255
|
+
# notify_queue_observers(client_queue, id)
|
256
|
+
else
|
257
|
+
# Sending query again - don't return response
|
258
|
+
end
|
259
|
+
return
|
260
|
+
end
|
261
|
+
end
|
262
|
+
# If not, then we have an error
|
263
|
+
Dnsruby.log.error{"Stray packet - " + msg.inspect + "\n from " + socket.inspect}
|
264
|
+
print("Stray packet - " + msg.question()[0].qname.to_s + " from " + msg.answerip.to_s + ", #{client_ids.length} client_ids\n")
|
265
|
+
end
|
266
|
+
|
267
|
+
def remove_id(id)
|
268
|
+
socket=nil
|
269
|
+
@@mutex.synchronize{
|
270
|
+
socket = @@query_hash[id].socket
|
271
|
+
@@timeouts.delete(id)
|
272
|
+
@@query_hash.delete(id)
|
273
|
+
@@socket_hash.delete(socket)
|
274
|
+
@@sockets.delete(socket) # @TODO@ Not if persistent!
|
275
|
+
}
|
276
|
+
Dnsruby.log.debug{"Closing socket #{socket}"}
|
277
|
+
socket.close # @TODO@ Not if persistent!
|
278
|
+
end
|
279
|
+
|
280
|
+
def process_timeouts
|
281
|
+
time_now = Time.now
|
282
|
+
timeouts={}
|
283
|
+
@@mutex.synchronize {
|
284
|
+
timeouts = @@timeouts
|
285
|
+
}
|
286
|
+
timeouts.each do |client_id, timeout|
|
287
|
+
if (timeout < time_now)
|
288
|
+
send_exception_to_client(ResolvTimeout.new("Query timed out"), nil, client_id)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def tcp_read(socket, len)
|
294
|
+
buf=""
|
295
|
+
while (buf.length < len) do
|
296
|
+
input = socket.recv(len-buf.length)
|
297
|
+
if (input=="")
|
298
|
+
TheLog.info("Bad response from server - no bytes read - ignoring")
|
299
|
+
return false
|
300
|
+
end
|
301
|
+
buf += input
|
302
|
+
end
|
303
|
+
return buf
|
304
|
+
end
|
305
|
+
|
306
|
+
def get_incoming_data(socket, packet_size)
|
307
|
+
answerfrom,answerip,answerport,answersize=nil
|
308
|
+
ans,buf = nil
|
309
|
+
begin
|
310
|
+
if (socket.class == TCPSocket)
|
311
|
+
# @todo@ Ruby Bug #9061 stops this working right
|
312
|
+
# We'd like to do a socket.recvfrom, but that raises an Exception
|
313
|
+
# on Windows for TCPSocket for Ruby 1.8.5 (and 1.8.6).
|
314
|
+
# So, we need to do something different for TCP than UDP. *sigh*
|
315
|
+
# @TODO@ This workaround will only work if there is exactly one socket per query
|
316
|
+
# - *not* ideal TCP use!
|
317
|
+
@@mutex.synchronize{
|
318
|
+
client_id = @@socket_hash[socket][0]
|
319
|
+
answerfrom = @@query_hash[client_id].dest_server
|
320
|
+
answerip = answerfrom
|
321
|
+
answerport = @@query_hash[client_id].dest_port
|
322
|
+
}
|
323
|
+
buf = tcp_read(socket, 2)
|
324
|
+
if (!buf)
|
325
|
+
handle_recvfrom_failure(socket, "")
|
326
|
+
return
|
327
|
+
end
|
328
|
+
answersize = buf.unpack('n')[0]
|
329
|
+
buf = tcp_read(socket,answersize)
|
330
|
+
if (!buf)
|
331
|
+
handle_recvfrom_failure(socket, "")
|
332
|
+
return
|
333
|
+
end
|
334
|
+
else
|
335
|
+
if (ret = socket.recvfrom(packet_size))
|
336
|
+
buf = ret[0]
|
337
|
+
answerport=ret[1][1]
|
338
|
+
answerfrom=ret[1][2]
|
339
|
+
answerip=ret[1][3]
|
340
|
+
answersize=(buf.length)
|
341
|
+
else
|
342
|
+
# recvfrom failed - why?
|
343
|
+
Dnsruby.log.error{"Error - recvfrom failed from #{socket}"}
|
344
|
+
handle_recvfrom_failure(socket, "")
|
345
|
+
return
|
346
|
+
end
|
347
|
+
end
|
348
|
+
rescue Exception => e
|
349
|
+
Dnsruby.log.error{"Error - recvfrom failed from #{socket}, exception : #{e}"}
|
350
|
+
handle_recvfrom_failure(socket, e)
|
351
|
+
return
|
352
|
+
end
|
353
|
+
Dnsruby.log.debug{";; answer from #{answerfrom} : #{answersize} bytes\n"}
|
354
|
+
|
355
|
+
begin
|
356
|
+
ans = Message.decode(buf)
|
357
|
+
rescue Exception => e
|
358
|
+
# print "DECODE ERROR\n"
|
359
|
+
Dnsruby.log.error{"Decode error! #{e.class}, #{e}\nfor msg (length=#{buf.length}) : #{buf}"}
|
360
|
+
# @TODO@ Should know this from the socket!
|
361
|
+
client_id=get_client_id_from_answerfrom(socket, answerip, answerport)
|
362
|
+
if (client_id != nil)
|
363
|
+
send_exception_to_client(e, socket, client_id)
|
364
|
+
else
|
365
|
+
Dnsruby.log.error{"Decode error from #{answerfrom} but can't determine packet id"}
|
366
|
+
end
|
367
|
+
return
|
368
|
+
end
|
369
|
+
|
370
|
+
if (ans!= nil)
|
371
|
+
Dnsruby.log.debug{"#{ans}"}
|
372
|
+
ans.answerfrom=(answerfrom)
|
373
|
+
ans.answersize=(answersize)
|
374
|
+
ans.answerip =(answerip)
|
375
|
+
end
|
376
|
+
return ans, buf
|
377
|
+
end
|
378
|
+
|
379
|
+
def handle_recvfrom_failure(socket, exception)
|
380
|
+
# No way to notify the client about this error, unless there was only one connection on the socket
|
381
|
+
# Not a problem, as there only will ever be one connection on the socket (Kaminsky attack mitigation)
|
382
|
+
ids_for_socket = []
|
383
|
+
@@mutex.synchronize{
|
384
|
+
ids_for_socket = @@socket_hash[socket]
|
385
|
+
}
|
386
|
+
if (ids_for_socket.length == 1)
|
387
|
+
answerfrom=nil
|
388
|
+
@@mutex.synchronize{
|
389
|
+
query_settings = @@query_hash[ids_for_socket[0]]
|
390
|
+
answerfrom=query_settings.dest_server
|
391
|
+
}
|
392
|
+
send_exception_to_client(OtherResolvError.new("recvfrom failed from #{answerfrom}; #{exception}"), socket, ids_for_socket[0])
|
393
|
+
else
|
394
|
+
Dnsruby.log.fatal{"Recvfrom failed from #{socket}, no way to tell query id"}
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def get_client_id_from_answerfrom(socket, answerip, answerport)
|
399
|
+
# @TODO@ Can get rid of this, as there is only one query per socket
|
400
|
+
client_id=nil
|
401
|
+
# Figure out client id from answerfrom
|
402
|
+
@@mutex.synchronize{
|
403
|
+
ids = @@socket_hash[socket]
|
404
|
+
ids.each do |id|
|
405
|
+
# Does this id speak to this dest_server?
|
406
|
+
query_settings = @@query_hash[id]
|
407
|
+
if (answerip == query_settings.dest_server && answerport == query_settings.dest_port)
|
408
|
+
# We have a match
|
409
|
+
# - @TODO@ as long as we're not speaking to the same server on two ports!
|
410
|
+
client_id = id
|
411
|
+
break
|
412
|
+
end
|
413
|
+
end
|
414
|
+
}
|
415
|
+
return client_id
|
416
|
+
end
|
417
|
+
|
418
|
+
def send_exception_to_client(err, socket, client_id, msg=nil)
|
419
|
+
# find the client response queue
|
420
|
+
client_queue = nil
|
421
|
+
@@mutex.synchronize {
|
422
|
+
client_queue = @@query_hash[client_id].client_queue
|
423
|
+
}
|
424
|
+
remove_id(client_id)
|
425
|
+
# push_to_client(client_id, client_queue, msg, err)
|
426
|
+
client_queue.push([client_id, Resolver::EventType::ERROR, msg, err])
|
427
|
+
notify_queue_observers(client_queue, client_id)
|
428
|
+
end
|
429
|
+
|
430
|
+
def push_exception_to_select(client_id, client_queue, err, msg)
|
431
|
+
@@mutex.synchronize{
|
432
|
+
@@queued_exceptions.push([client_id, client_queue, err, msg])
|
433
|
+
}
|
434
|
+
# Make sure select loop is running!
|
435
|
+
if (@@select_thread && @@select_thread.alive?)
|
436
|
+
else
|
437
|
+
@@select_thread = Thread.new {
|
438
|
+
do_select
|
439
|
+
}
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
def push_response_to_select(client_id, client_queue, msg, query, res)
|
444
|
+
# This needs to queue the response TO THE SELECT THREAD, which then needs
|
445
|
+
# to send it out from its normal loop.
|
446
|
+
Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"}
|
447
|
+
@@mutex.synchronize{
|
448
|
+
@@queued_responses.push([client_id, client_queue, msg, nil, query, res])
|
449
|
+
}
|
450
|
+
# Make sure select loop is running!
|
451
|
+
if (@@select_thread && @@select_thread.alive?)
|
452
|
+
else
|
453
|
+
@@select_thread = Thread.new {
|
454
|
+
do_select
|
455
|
+
}
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def push_validation_response_to_select(client_id, client_queue, msg, err, query, res)
|
460
|
+
# This needs to queue the response TO THE SELECT THREAD, which then needs
|
461
|
+
# to send it out from its normal loop.
|
462
|
+
Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"}
|
463
|
+
@@mutex.synchronize{
|
464
|
+
@@queued_validation_responses.push([client_id, client_queue, msg, err, query, res])
|
465
|
+
}
|
466
|
+
# Make sure select loop is running!
|
467
|
+
if (@@select_thread && @@select_thread.alive?)
|
468
|
+
else
|
469
|
+
@@select_thread = Thread.new {
|
470
|
+
do_select
|
471
|
+
}
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
def send_queued_exceptions
|
476
|
+
exceptions = []
|
477
|
+
@@mutex.synchronize{
|
478
|
+
exceptions = @@queued_exceptions
|
479
|
+
@@queued_exceptions = []
|
480
|
+
}
|
481
|
+
|
482
|
+
exceptions.each do |item|
|
483
|
+
client_id, client_queue, err, msg = item
|
484
|
+
# push_to_client(client_id, client_queue, msg, err)
|
485
|
+
client_queue.push([client_id, Resolver::EventType::ERROR, msg, err])
|
486
|
+
notify_queue_observers(client_queue, client_id)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def send_queued_responses
|
491
|
+
responses = []
|
492
|
+
@@mutex.synchronize{
|
493
|
+
responses = @@queued_responses
|
494
|
+
@@queued_responses = []
|
495
|
+
}
|
496
|
+
|
497
|
+
responses.each do |item|
|
498
|
+
client_id, client_queue, msg, err, query, res = item
|
499
|
+
# push_to_client(client_id, client_queue, msg, err)
|
500
|
+
client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err])
|
501
|
+
notify_queue_observers(client_queue, client_id)
|
502
|
+
# Do we need to validate this? The response has come from the cache -
|
503
|
+
# validate it only if it has not been validated already
|
504
|
+
# So, if we need to validate it, send it to the validation thread
|
505
|
+
# Otherwise, send VALIDATED to the requester.
|
506
|
+
if (((msg.security_level == Message::SecurityLevel::UNCHECKED) ||
|
507
|
+
(msg.security_level == Message::SecurityLevel::INDETERMINATE)) &&
|
508
|
+
(ValidatorThread.requires_validation?(query, msg, err, res)))
|
509
|
+
validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res)
|
510
|
+
validator.run
|
511
|
+
else
|
512
|
+
PacketSender.cache(query, msg) # The validator won't cache it, so we'd better do it now
|
513
|
+
client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err])
|
514
|
+
notify_queue_observers(client_queue, client_id)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
def send_queued_validation_responses
|
520
|
+
responses = []
|
521
|
+
@@mutex.synchronize{
|
522
|
+
responses = @@queued_validation_responses
|
523
|
+
@@queued_validation_responses = []
|
524
|
+
}
|
525
|
+
|
526
|
+
responses.each do |item|
|
527
|
+
client_id, client_queue, msg, err, query, res = item
|
528
|
+
# push_to_client(client_id, client_queue, msg, err)
|
529
|
+
client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err])
|
530
|
+
notify_queue_observers(client_queue, client_id)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
def push_to_client(client_id, client_queue, msg, err, query, res)
|
535
|
+
# @TODO@ Really need to let the client know that we have received a valid response!
|
536
|
+
# Can do that by calling notify_observers here, but with an identifier which
|
537
|
+
# defines the response to be a "Response received - validating. Please stop sending"
|
538
|
+
# type of response.
|
539
|
+
client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err])
|
540
|
+
notify_queue_observers(client_queue, client_id)
|
541
|
+
|
542
|
+
if (!err || (err.instance_of?(NXDomain)))
|
543
|
+
#
|
544
|
+
# This method now needs to push the response to the validator,
|
545
|
+
# which will then take responsibility for delivering it to the client.
|
546
|
+
# The validator will need access to the queue observers -
|
547
|
+
validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res)
|
548
|
+
validator.run
|
549
|
+
# @@validator.add_to_queue([client_id, client_queue, msg, err, query, self, res])
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def add_observer(client_queue, observer)
|
554
|
+
@@mutex.synchronize {
|
555
|
+
@@observers[client_queue]=observer
|
556
|
+
check_select_thread_synchronized # Is this really necessary? The client should start the thread by sending a query, really...
|
557
|
+
if (!@@tick_observers.include?observer)
|
558
|
+
@@tick_observers.push(observer)
|
559
|
+
end
|
560
|
+
}
|
561
|
+
end
|
562
|
+
|
563
|
+
def remove_observer(client_queue, observer)
|
564
|
+
@@mutex.synchronize {
|
565
|
+
if (@@observers[client_queue]==observer)
|
566
|
+
# @@observers.delete(observer)
|
567
|
+
@@observers.delete(client_queue)
|
568
|
+
else
|
569
|
+
if (@@observers[client_queue] == nil)
|
570
|
+
end
|
571
|
+
Dnsruby.log.error{"remove_observer called with wrong observer for queue"}
|
572
|
+
raise ArgumentError.new("remove_observer called with wrong observer for queue")
|
573
|
+
end
|
574
|
+
if (!@@observers.values.include?observer)
|
575
|
+
@@tick_observers.delete(observer)
|
576
|
+
end
|
577
|
+
}
|
578
|
+
end
|
579
|
+
|
580
|
+
def notify_queue_observers(client_queue, client_query_id)
|
581
|
+
# If any observers are known for this query queue then notify them
|
582
|
+
observer=nil
|
583
|
+
@@mutex.synchronize {
|
584
|
+
observer = @@observers[client_queue]
|
585
|
+
}
|
586
|
+
if (observer)
|
587
|
+
observer.handle_queue_event(client_queue, client_query_id)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def send_tick_to_observers
|
592
|
+
# If any observers are known then send them a tick
|
593
|
+
tick_observers=nil
|
594
|
+
@@mutex.synchronize {
|
595
|
+
tick_observers = @@tick_observers
|
596
|
+
}
|
597
|
+
tick_observers.each do |observer|
|
598
|
+
observer.tick
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
@@ -1,3 +1,20 @@
|
|
1
|
+
#--
|
2
|
+
#Copyright 2007 Nominet UK
|
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
|
+
|
17
|
+
|
1
18
|
# This class does verification/validation from a single point - signed root,
|
2
19
|
# DLV, trust anchors. Dnssec controls a set of these to perform validation for
|
3
20
|
# the client.
|