dnsruby 1.36 → 1.37
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|