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.
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.