# --
# Copyright 2007 Nominet UK
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ++

module Dnsruby
  #  Takes care of the validation for the SelectThread. If queries need to be
  #  made in order to validate the response, then a separate thread is fired up
  #  to do this.
  class ValidatorThread # :nodoc: all
    #     include Singleton
    def initialize(*args)
      @client_id, @client_queue, @response, @error, @query, @st, @res = args
      #  Create the validation thread, and a queue to receive validation requests
      #  Actually, need to have a thread per validator, as they make recursive calls.
      #       @@mutex = Mutex.new
      #       @@validation_queue = Queue.new
      #       @@validator_thread = Thread.new{
      #         do_validate
      #       }
    end
    def run
      #  ONLY START THE NEW THREAD IF VALIDATION NEED OCCUR!!
      if (should_validate)
        Thread.new{
          do_validate
        }
      else
        do_validate
      end
    end


    #     def add_to_queue(item)
    #       print "ADding to validator queue\n"
    # #      @@mutex.synchronize{
    #         @@validation_queue.push(item)
    # #      }
    #     end
    def do_validate
      #       while (true)
      #         item = nil
      #         print "Waiting to pop validation item\n"
      # #        @@mutex.synchronize{
      #           item = @@validation_queue.pop
      # #        }
      #       print "Popped validation request\n"
      #         client_id, client_queue, response, err, query, st, res = item
      validated_ok = validate(@query, @response, @res)

      validated_ok = false if (@error && !(NXDomain === @error))

      cache_if_valid(@query, @response)

      #  Now send the response back to the client...
      #       print "#{Time.now} : Got result for #{@query.question()[0].qname}, #{@query.question()[0].qtype}\n"
      if (validated_ok)
        @st.push_validation_response_to_select(@client_id, @client_queue, @response, nil, @query, @res)
      else
        @st.push_validation_response_to_select(@client_id, @client_queue, @response,
          @response.security_error, @query, @res)
      end


      #       end
    end


    def should_validate
      return ValidatorThread.requires_validation?(@query, @response, @error, @res)
    end

    def ValidatorThread.requires_validation?(query, response, error, res)
      #  @error will be nil for DNS RCODE errors - it will be true for TsigError. really?!
      if ((!error || (error.instance_of?NXDomain)) && query.do_validation)
        if (res.dnssec)
          if (response.security_level != Message::SecurityLevel::SECURE)
            return true
          end
        end
      end
      return false

    end

    def validate(query, response, res)
      if (should_validate)
        begin
          #  So, we really need to be able to take the response out of the select thread, along
          #  with the responsibility for sending the answer to the client.
          #  Should we have a validator thread? Or a thread per validation?
          #  Then, select thread gets response. It performs basic checks here.
          #  After basic checks, the select-thread punts the response (along with queues, etc.)
          #  to the validator thread.
          #  The validator validates it (or just releases it with no validation), and then
          #  sends the request to the client via the client queue.
          Dnssec.validate_with_query(query,response)
          return true
        rescue VerifyError => e
          response.security_error = e
          #  Response security_level should already be set
          return false
        end
      end
      return true
    end

    def cache_if_valid(query, response)
      return if @error
      PacketSender.cache(query, response)
    end
  end
end
