dnsruby 1.36 → 1.37

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/demo/axfr.rb +16 -0
  2. data/demo/check_soa.rb +17 -0
  3. data/demo/check_zone.rb +16 -0
  4. data/demo/digdlv.rb +16 -0
  5. data/demo/digitar.rb +16 -0
  6. data/demo/digroot.rb +92 -0
  7. data/demo/example_recurse.rb +16 -0
  8. data/demo/mresolv.rb +17 -0
  9. data/demo/mx.rb +16 -0
  10. data/demo/rubydig.rb +16 -0
  11. data/demo/trace_dns.rb +16 -0
  12. data/html/created.rid +1 -1
  13. data/html/fr_class_index.html +0 -6
  14. data/html/fr_method_index.html +332 -348
  15. data/lib/Dnsruby/Cache.rb +18 -1
  16. data/lib/Dnsruby/iana_ports.rb +17 -1
  17. data/lib/Dnsruby/key_cache.rb +16 -0
  18. data/lib/Dnsruby/resource/delete_me.rhtml +6 -0
  19. data/lib/Dnsruby/select_thread.rb.michael.rb +602 -0
  20. data/lib/Dnsruby/single_verifier.rb +17 -0
  21. data/lib/Dnsruby/validator_thread.rb +16 -0
  22. data/lib/Dnsruby/zone_transfer.rb +16 -0
  23. data/test/resolv.conf +16 -1
  24. data/test/tc_auth.rb +49 -0
  25. data/test/tc_cache.rb +16 -0
  26. data/test/tc_dlv.rb +16 -0
  27. data/test/tc_dnskey.rb +16 -0
  28. data/test/tc_ds.rb +16 -0
  29. data/test/tc_itar.rb +16 -0
  30. data/test/tc_nsec.rb +16 -0
  31. data/test/tc_nsec3.rb +16 -0
  32. data/test/tc_nsec3param.rb +16 -0
  33. data/test/tc_queue.rb +16 -0
  34. data/test/tc_recur.rb +16 -0
  35. data/test/tc_rrset.rb +16 -0
  36. data/test/tc_rrsig.rb +16 -0
  37. data/test/tc_soak_base.rb +16 -0
  38. data/test/tc_validator.rb +16 -0
  39. data/test/tc_verifier.rb +16 -0
  40. data/test/ts_queue.rb +3 -0
  41. metadata +7 -7
  42. data/lib/Dnsruby/event_machine_interface.rb +0 -266
  43. data/test/tc_event_machine_deferrable.rb +0 -85
  44. data/test/tc_event_machine_res.rb +0 -174
  45. data/test/tc_event_machine_single_res.rb +0 -101
  46. data/test/tc_event_machine_soak.rb +0 -98
data/lib/Dnsruby/Cache.rb CHANGED
@@ -1,4 +1,21 @@
1
- # This class implements a cache.
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).
@@ -1,4 +1,20 @@
1
- # This Array contains a list of the ports which should not be used by Dnsruby
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,
@@ -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,6 @@
1
+ <%#
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+ %>
5
+
6
+ <%= "delete_me" %>
@@ -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.