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


# This class does verification/validation from a single point - signed root,
# DLV, trust anchors. Dnssec controls a set of these to perform validation for
# the client.
# This class should only be used by Dnsruby
module Dnsruby
  class SingleVerifier # :nodoc: all
    class VerifierType
      ROOT = 0
      ANCHOR = 1
      DLV = 2
    end
    def initialize(vtype)
      @verifier_type = vtype
      @added_dlv_key = false
      # The DNSKEY RRs for the signed root (when it exists)
      @root_anchors = KeyCache.new

      # The set of trust anchors.
      # If the root is unsigned, then these must be initialised with at least
      # one trusted key by the client application, if verification is to be performed.
      @trust_anchors = KeyCache.new

      @dlv_registries = []

      # The set of keys which are trusted.
      @trusted_keys = KeyCache.new

      # The set of keys which have been indicated by a DS RRSet which has been
      # signed by a trusted key. Although we have not yet located these keys, we
      # have the details (tag and digest) which can identify the keys when we
      # see them. At that point, they will be added to our trusted keys.
      @discovered_ds_store = []
      # The configured_ds_store is the set of DS records which have been configured
      # by the client as trust anchors. Use Dnssec#add_trust_anchor to add these
      @configured_ds_store = []
    end
    
    def set_hints(hints)
      @@hints = hints
    end

    def get_recursor
      if (!defined?@@recursor)
        if (defined?@@hints)
          Recursor.set_hints(@@hints, Resolver.new)
          @@recursor = Recursor.new()
        else
          @@recursor = Recursor.new
        end
      end
      return @@recursor
    end

    def get_dlv_resolver # :nodoc:
      #      if (Dnssec.do_validation_with_recursor?)
      #        return Recursor.new
      #      else
      if (Dnssec.default_resolver)
        return Dnssec.default_resolver
      else
        return Resolver.new
      end
      #      end
    end
    def add_dlv_key(key)
      # Is this a ZSK or a KSK?
      # If it is a KSK, then get the ZSK from the zone
      if (key.sep_key?)
        get_dlv_key(key)
      end
    end
    def get_dlv_key(ksk) # :nodoc:
      # Using the KSK, get the ZSK for the DLV registry
      if (!@res && (@verifier_type == VerifierType::DLV))
        @res = get_dlv_resolver
      end
      #      print "Sending query : res.dnssec = #{@res.dnssec}"
      ret = nil
      begin
        ret = @res.query_no_validation_or_recursion("dlv.isc.org.", Types.DNSKEY)
        if (!ret)
          raise ResolvError.new("Couldn't get response from Recursor")
        end
      rescue ResolvError => e
        #        print "ERROR - Couldn't find the DLV key\n"
        TheLog.error("Couldn't find the DLV key\n")
        return
      end
      key_rrset = ret.answer.rrset("dlv.isc.org", Types.DNSKEY)
      begin
        verify(key_rrset, ksk)
        add_trusted_key(key_rrset)
        #        print "Successfully added DLV key\n"
        TheLog.info("Successfully added DLV key")
        @added_dlv_key = true
      rescue VerifyError => e
        #        print "Error verifying DLV key : #{e}\n"
        TheLog.error("Error verifying DLV key : #{e}")
      end
    end
    def add_trust_anchor(t)
      add_trust_anchor_with_expiration(t, Time.utc(2035,"jan",1,20,15,1).to_i)
    end
    # Add the
    def add_trust_anchor_with_expiration(k, expiration)
      if (k.type == Types.DNSKEY)
        #        k.flags = k.flags | RR::IN::DNSKEY::SEP_KEY
        @trust_anchors.add_key_with_expiration(k, expiration)
        #        print "Adding trust anchor for #{k.name}\n"
        TheLog.info("Adding trust anchor for #{k.name}")
      elsif ((k.type == Types.DS) || ((k.type == Types.DLV) && (@verifier_type == VerifierType::DLV)))
        @configured_ds_store.push(k)
      end
    end

    def remove_trust_anchor(t)
      @trust_anchors.delete(t)
    end
    # Wipes the cache of trusted keys
    def clear_trust_anchors
      @trust_anchors = KeyCache.new
    end

    def trust_anchors
      return @trust_anchors.keys + @configured_ds_store
    end

    # Check that the RRSet and RRSIG record are compatible
    def check_rr_data(rrset, sigrec)#:nodoc: all
      #Each RR MUST have the same owner name as the RRSIG RR;
      if (rrset.name.canonical != sigrec.name.canonical)
        raise VerifyError.new("RRSET should have same owner name as RRSIG for verification (rrsert=#{rrset.name}, sigrec=#{sigrec.name}")
      end

      #Each RR MUST have the same class as the RRSIG RR;
      if (rrset.klass != sigrec.klass)
        raise VerifyError.new("RRSET should have same DNS class as RRSIG for verification")
      end

      #Each RR in the RRset MUST have the RR type listed in the
      #RRSIG RR's Type Covered field;
      if (rrset.type != sigrec.type_covered)
        raise VerifyError.new("RRSET should have same type as RRSIG for verification")
      end

      #      #Each RR in the RRset MUST have the TTL listed in the
      #      #RRSIG Original TTL Field;
      #      if (rrset.ttl  != sigrec.original_ttl)
      #        raise VerifyError.new("RRSET should have same ttl as RRSIG original_ttl for verification (should be #{sigrec.original_ttl} but was #{rrset.ttl}")
      #      end

      # Now check that we are in the validity period for the RRSIG
      now = Time.now.to_i
      if ((sigrec.expiration < now) || (sigrec.inception > now))
        raise VerifyError.new("Signature record not in validity period")
      end
    end

    # Add the specified keys to the trusted key cache.
    # k can be a KeyCache, or an RRSet of DNSKEYs.
    def add_trusted_key(k)
      @trusted_keys.add(k)
    end

    def add_root_ds(ds)
      @configured_ds_store.push(ds)
    end

    # Wipes the cache of trusted keys
    def clear_trusted_keys
      @trusted_keys = KeyCache.new
      @res = nil
      @discovered_ds_store = []
      @configured_ds_store = []
    end

    def trusted_keys
      discovered_ds = []
      @discovered_ds_store.each {|rrset|
        rrset.rrs.each {|rr|
          discovered_ds.push(rr)
        }
      }
      return @trusted_keys.keys + @configured_ds_store + discovered_ds
    end

    # Check that the key fits a signed DS record key details
    # If so, then add the key to the trusted keys
    def check_ds(key, ds_rrset)#:nodoc: all
      expiration = 0
      found = false
      ds_rrset.sigs.each { |sig|
        if ((sig.type_covered == Types.DS) || ((sig.type_covered == Types.DLV)&& (@verifier_type==VerifierType::DLV)))
          if (sig.inception <= Time.now.to_i)
            # Check sig.expiration, sig.algorithm
            if (sig.expiration > expiration)
              expiration = sig.expiration
            end
          end
        end
      }
      if (expiration > 0)
        ds_rrset.rrs.each { |ds|
          if ((ds.type === Types.DS) || ((ds.type == Types.DLV) && (@verifier_type == VerifierType::DLV)))
            if (ds.check_key(key))
              @trusted_keys.add_key_with_expiration(key, expiration)
              found = true
            end
          end
        }
      end
      return found
    end

    # Verify the specified message (or RRSet) using the set of trusted keys.
    # If keys is a DNSKEY, or an Array or RRSet of DNSKEYs, then keys
    # is added to the set of trusted keys before the message (or RRSet) is
    # verified.
    #
    # If msg is a Dnsruby::Message, then any signed DNSKEY or DS RRSets are
    # processed first, and any new keys are added to the trusted key set
    # before the other RRSets are checked.
    #
    # msg can be a Dnsruby::Message or Dnsruby::RRSet.
    # keys may be nil, or a KeyCache or an RRSet of Dnsruby::RR::DNSKEY
    #
    # Returns true if the message verifies OK, and false otherwise.
    def verify(msg, keys = nil)
      if (msg.kind_of?RRSet)
        if (msg.type == Types.DNSKEY)
          return verify_key_rrset(msg, keys)
        end
        if ((msg.type == Types.DS) || (msg.type == Types.DLV))
          return verify_ds_rrset(msg, keys)

        end
        return verify_rrset(msg, keys)
      end
      # Use the set of trusted keys to check any RRSets we can, ideally
      # those of other DNSKEY RRSets first. Then, see if we can use any of the
      # new total set of keys to check the rest of the rrsets.
      # Return true if we can verify the whole message.

      msg.each_section do |section|
        #        print "Checking section : #{section}\n"
        ds_rrsets = section.rrsets(Types.DS)
        if ((!ds_rrsets || ds_rrsets.length == 0) && (@verifier_type == VerifierType::DLV))
          ds_rrsets = section.rrsets(Types.DLV)
        end
        ds_rrsets.each {|ds_rrset|
          if ((ds_rrset && ds_rrset.rrs.length > 0) && !verify_ds_rrset(ds_rrset, keys, msg))
            raise VerifyError.new("Failed to verify DS RRSet")
            #            return false
          end
        }

        key_rrsets = section.rrsets(Types.DNSKEY)
        key_rrsets.each {|key_rrset|
          if ((key_rrset && key_rrset.rrs.length > 0) && !verify_key_rrset(key_rrset, keys))
            raise VerifyError.new("Failed to verify DNSKEY RRSet")
            #            return false
          end
        }
      end

      verify_nsecs(msg)

      # Then, look through all the remaining RRSets, and verify them all (unless not necessary).
      msg.section_rrsets.each do |section, rrsets|
        rrsets.each do |rrset|
          # If delegation NS or glue AAAA/A, then don't expect RRSIG.
          # Otherwise, expect RRSIG and fail verification if RRSIG is not present

          if ((section == "authority") && (rrset.type == Types.NS))
            # Check for delegation
            dsrrset = msg.authority.rrsets('DS')[0]
            if ((msg.answer.size == 0) && (!dsrrset) && (rrset.type == Types.NS)) # (isDelegation)
              # Now check NSEC(3) records for absence of DS and SOA
              nsec = msg.authority.rrsets('NSEC')[0]
              if (!nsec || (nsec.length == 0))
                nsec = msg.authority.rrsets('NSEC3')[0]
              end
              if (nsec && (nsec.rrs.length > 0))
                if (!(nsec.rrs()[0].types.include?'DS') || !(nsec.rrs()[0].types.include?'SOA'))
                  next # delegation which we expect to be unsigned - so don't verify it!
                end
              end
            end
            # If NS records delegate the name to the child's nameservers, then they MUST NOT be signed
            if (rrset.type == Types.NS)
              #              all_delegate = true
              #              rrset.rrs.each {|rr|
              #                name = Name.create(rr.nsdname)
              #                name.absolute = true
              #                if (!(name.subdomain_of?(rr.name)))
              #                  all_delegate = false
              #                end
              #              }
              #              if (all_delegate && rrset.sigs.length == 0)
              #                next
              #              end
              if ((rrset.name.canonical == msg.question()[0].qname.canonical) && (rrset.sigs.length == 0))
                next
              end
            end
          end

          if (section == "additional")
            # check for glue
            # if the ownername (in the addtional section) of the glue address is the same or longer as the ownername of the NS record, it is glue
            if (msg.additional.size > 0)
              arec = msg.additional.rrsets('A')[0]
              if (!arec || arec.rrs.length == 0)
                arec = msg.additional.rrsets('AAAA')[0]
              end
              ns_rrsets = msg.additional.rrsets('NS')
              ns_rrsets.each {|ns_rrset|
                if (ns_rrset.length > 0)
                  nsname = ns_rrset.rrs()[0].name
                  if (arec && arec.rrs().length > 0)
                    aname = arec.rrs()[0].name
                    if (nsname.subdomain_of?aname)
                      next
                    end
                  end
                end
              }
            end
          end
          # If records are in additional, and no RRSIG, that's Ok - just don't use them!
          if ((section == "additional") && (rrset.sigs.length == 0))
            # @TODO@ Make sure that we don't cache these records!
            next
          end
          # else verify RRSet
          #          print "About to verify #{rrset.name}, #{rrset.type}\n"
          if (!verify_rrset(rrset, keys))
            #            print "FAILED TO VERIFY RRSET #{rrset.name}, #{rrset.type}\n"
            TheLog.debug("Failed to verify rrset")
            return false
          end
        end
      end
      return true
    end

    def verify_nsecs(msg) # :nodoc:
      # NSEC(3) handling. Get NSEC(3)s in four cases : (RFC 4035, section 3.1.3)
      # a) No data - <SNAME, SCLASS> matches, but no <SNAME, SCLASS, STYPE) (§3.1.3.1)
      #     - will expect NSEC in Authority (and associated RRSIG)
      #     - NOERROR returned
      # b) Name error - no RRSets that match <SNAME, SCLASS> either exactly or through wildcard expansion   (§3.1.3.2)
      #     - NSEC wil prove i) no exact match for <SNAME, SCLASS>, and ii) no RRSets that could match through wildcard expansion
      #     - this may be proved in one or more NSECs (and associated RRSIGs)
      #     - NXDOMAIN returned - should ensure we verify!
      # c) Wildcard answer - No <SNAME, SCLASS> direct matches, but matches <SNAME, SCLASS, STYPE> through wildcard expansion (§3.1.3.3)
      #     - Answer section must include wildcard-expanded answer (and associated RRSIGs)
      #     - label count in answer RRSIG indicates wildcard RRSet was expanded (less labels than in owner name)
      #     - Authority section must include NSEC (and RRSIGs) proving that zone does not contain a closer match
      #     - NOERROR returned
      # d) Wildcard no data - No <SNAME, SCLASS> direct. <SNAME, SCLASS> yes but <SNAME, SCLASS, STYPE> no through wildcard expansion (§3.1.3.4)
      #     - Authority section contains NSECs (and RRSIGs) for :
      #         i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched <SNAME, SCLASS> via wildcard expansion
      #         ii) NSEC proving no RRSets in zone that would have been closer match for <SNAME, SCLASS>
      #     - this may be proved by one or more NSECs (and associated RRSIGs)
      #     - NOERROR returned
      #
      # Otherwise no NSECs should be returned.

      # So, check for NSEC records in response, and work out what type of answer we have.
      # Then, if NSECs are present, make sure that we prove what they said they would.
      # What if the message *should* have no NSEC records? That can only be known by the validator.
      # We will assume that the validator has checked the (non)-existence of NSEC records - we should not
      # get upset if there aren't any. However, if there are, then we should verify that they say the right thing
      qtype = msg.question()[0].qtype
      return if (msg.rcode == RCode.NOERROR && ((qtype == Types.ANY) || (qtype == Types.NSEC) || (qtype == Types.NSEC3)))
      if ((msg.rrsets('NSEC').length > 0) || (msg.rrsets('NSEC3').length > 0))
        if (msg.rcode == RCode.NXDOMAIN)
          #          print "Checking NSECs for Name Error\n"
          #Name error - NSEC wil prove i) no exact match for <SNAME, SCLASS>, and ii) no RRSets that could match through wildcard expansion
          #           - this may be proved in one or more NSECs (and associated RRSIGs)
          check_name_in_nsecs(msg)
          return check_no_wildcard_expansion(msg)
        elsif (msg.rcode == RCode.NOERROR)
          if (msg.answer.length > 0)
            #            print "Checking NSECs for wildcard expansion\n"
            # wildcard expansion answer - check NSECs!
            # We want to make sure that the NSEC tells us that there is no closer match for this name
            # @TODO@ We need to make replace the RRSIG name with the wildcard name before we can verify it correctly.
            check_num_rrsig_labels(msg)
            return check_name_in_nsecs(msg, msg.question()[0].qtype, true)
          else
            # Either no data or wildcard no data - check to see which
            # Should be able to tell this by checking the number of labels in the NSEC records.
            # Sort these two last cases out!
            isWildcardNoData = false
            [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets|
              nsec_rrsets.each {|nsec_rrset|
                nsec_rrset.rrs.each {|nsec|
                  #                  print "Checking nsec to see if wildcard : #{nsec}\n"
                  if (nsec.name.wild? ||(nsec.name.labels.length < msg.question()[0].qname.labels.length))
                    isWildcardNoData = true
                  end
                }
              }
            }

            if (isWildcardNoData)
              #              print "Checking NSECs for wildcard no data\n"
              # Check NSECs -
              #         i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched <SNAME, SCLASS> via wildcard expansion
              check_name_not_in_wildcard_nsecs(msg)
              #         ii) NSEC proving no RRSets in zone that would have been closer match for <SNAME, SCLASS>
              return check_name_in_and_type_not_in_nsecs(msg)
            else # (isNoData)
              #              print "Checking NSECs for No data\n"
              # Check NSEC types covered to make sure this type not present.
              return check_name_in_and_type_not_in_nsecs(msg)
            end
          end
        else
          # Anything we should do here?
        end
      end

    end

    def check_num_rrsig_labels(msg) # :nodoc:
      # Check that the number of labels in the RRSIG is less than the number
      # of labels in the answer name
      answer_rrset = msg.answer.rrset(msg.question()[0].qname, msg.question()[0].qtype)
      if (answer_rrset.length == 0)
        raise VerifyError.new("Expected wildcard expanded answer for #{msg.question()[0].qname}")
      end
      rrsig = answer_rrset.sigs()[0]
      if (rrsig.labels >= msg.question()[0].qname.labels.length)
        raise VerifyError.new("RRSIG does not prove wildcard expansion for #{msg.question()[0].qname}")
      end
    end

    def check_no_wildcard_expansion(msg) # :nodoc:
      # @TODO@ Do this for NSEC3 records!!!
      proven_no_wildcards = false
      name = msg.question()[0].qname
      [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets|
        nsec_rrsets.each {|nsecs|
          nsecs.rrs.each {|nsec|
            #            print "Checking NSEC : #{nsec}\n"
            next if (nsec.name.wild?)
            if (check_record_proves_no_wildcard(msg, nsec))
              proven_no_wildcards = true
            end
          }
        }
      }
      if (!proven_no_wildcards)
        #        print "No proof that no RRSets could match through wildcard expansion\n"
        raise VerifyError.new("No proof that no RRSets could match through wildcard expansion")
      end

    end
    
    def check_record_proves_no_wildcard(msg, nsec) # :nodoc:
      # Check that the NSEC goes from the SOA to a zone canonically after a wildcard
      #      print "Checking wildcard proof for #{nsec.name}\n"
      soa_rrset = msg.authority.rrset(nsec.name, 'SOA')
      if (soa_rrset.length > 0)
        #        print "Found SOA for #{nsec.name}\n"
        wildcard_name = Name.create("*." + nsec.name.to_s)
        #        print "Checking #{wildcard_name}\n"
        if (wildcard_name.canonically_before(nsec.next_domain))
          return true
        end
      end
      return false
    end

    def check_name_in_nsecs(msg, qtype=nil, expected_qtype = false) # :nodoc:
      # Check these NSECs to make sure that this name cannot be in the zone
      # and that no RRSets could match through wildcard expansion
      # @TODO@ Get this right for NSEC3 too!
      name = msg.question()[0].qname
      proven_name_in_nsecs = false
      type_covered_checked = false
      [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets|
        nsec_rrsets.each {|nsecs|
          nsecs.rrs.each {|nsec|
            #            print "Checking NSEC : #{nsec}\n"
            next if (nsec.name.wild?)
            if nsec.check_name_in_range(name)
              proven_name_in_nsecs = true
              qtype_present = false
              if (qtype)
                if (nsec.types.include?qtype)
                  qtype_present = true
                end
                if (qtype_present != expected_qtype)
                  #                  print "#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''} include #{qtype} type\n"
                  raise VerifyError.new("#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''}include #{qtype} type")
                  #              return false
                end
                type_covered_checked = true
              end
            end
          }
        }
      }
      if (!proven_name_in_nsecs)
        #        print "No proof for non-existence for #{name}\n"
        raise VerifyError.new("No proof for non-existence for #{name}")
      end
      if (qtype && !type_covered_checked)
        #        print "Tyes covered wrong for #{name}\n"
        raise VerifyError.new("Types covered wrong for #{name}")
      end
    end

    def check_name_in_and_type_not_in_nsecs(msg) # :nodoc:
      check_name_in_nsecs(msg, msg.question()[0].qtype, false)
    end

    def check_name_not_in_wildcard_nsecs(msg) # :nodoc:
      # @TODO@ Do this for NSEC3 records too!
      name = msg.question()[0].qname
      qtype = msg.question()[0].qtype
      done= false
      [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets|
        nsec_rrsets.each {|nsecs|
          nsecs.rrs.each {|nsec|
            #            print "Checking NSEC : #{nsec}\n"
            next if !nsec.name.wild?
            # Check the wildcard expansion
            # We want to see that the name is in the wildcard range, and that the type
            # is not in the types for the NSEC
            if nsec.check_name_in_wildcard_range(name)
              #              print "Wildcard expansion in #{nsec} includes #{name}\n"
              raise VerifyError.new("Wildcard expansion in #{nsec} includes #{name}")
              #            return false
            end
            if (nsec.types.include?qtype)
              #              print "#{qtype} present in wildcard #{nsec}\n"
              raise VerifyError.new("#{qtype} present in wildcard #{nsec}")
              #            return false
            end
            done = true
          }
        }
      }
      return if done
      #      print("Expected wildcard expansion in #{msg}\n")
      raise VerifyError.new("Expected wildcard expansion in #{msg}")
      #      return false
    end

    def verify_ds_rrset(ds_rrset, keys = nil, msg = nil) # :nodoc:
      #      print "verify_ds_rrset #{ds_rrset}\n"
      if (ds_rrset && ds_rrset.num_sigs > 0)
        if (verify_rrset(ds_rrset, keys))
          # Need to handle DS RRSets (with RRSIGs) not just DS records.
          #            ds_rrset.rrs.each do |ds|
          # Work out which key this refers to, and add it to the trusted key store
          found = false
          if (msg)
            msg.each_section do |section|
              section.rrsets('DNSKEY').each {|rrset|
                rrset.rrs.each do |rr|
                  if (check_ds(rr, ds_rrset))
                    found = true
                  end
                end
              }
            end
          end
          get_keys_to_check().each {|key|
            if (check_ds(key, ds_rrset))
              found = true
            end
          }
          # If we couldn't find the trusted key, then we should store the
          # key tag and digest in a @@discovered_ds_store.
          # Each time we see a new key (which has been signed) then we should
          # check if it is sitting on the discovered_ds_store.
          # If it is, then we should add it to the trusted_keys and remove the
          # DS from the discovered_ds_store
          if (!found)
            @discovered_ds_store.push(ds_rrset)
          end
          #            end
          return true
        else
          return false
        end
      end
      return false # no DS rrset to verify
    end

    def verify_key_rrset(key_rrset, keys = nil) # :nodoc:
      #      print "verify_key_rrset\n"
      verified = false
      if (key_rrset && key_rrset.num_sigs > 0)
        if (verify_rrset(key_rrset, keys))
          #            key_rrset.rrs.each do |rr|
          #          print "Adding keys : "
          #          key_rrset.rrs.each {|rr| print "#{rr.key_tag}, "}
          #          print "\n"
          @trusted_keys.add(key_rrset) # rr)
          verified = true
        end
        check_ds_stores(key_rrset)
      end
      return verified
    end

    def check_ds_stores(key_rrset) # :nodoc:
      # See if the keys match any of the to_be_trusted_keys
      key_rrset.rrs.each do |key|
        @configured_ds_store.each do |ds|
          if (ds.check_key(key))
            @trusted_keys.add_key_with_expiration(key, key_rrset.sigs()[0].expiration)
          end
        end
        @discovered_ds_store.each do |tbtk|
          # Check that the RRSet is still valid!!
          # Should we get it out of the main cache?
          if ((tbtk.sigs()[0].expiration < Time.now.to_i))
            @discovered_ds_store.delete(tbtk)
          else
            tbtk.rrs.each {|ds|
              if (ds.check_key(key))
                @trusted_keys.add_key_with_expiration(key, tbtk.sigs()[0].expiration)
                @discovered_ds_store.delete(tbtk)
              end
            }
          end
        end
        #            end
      end

    end

    def get_keys_to_check # :nodoc:
      keys_to_check = @trust_anchors.keys + @trusted_keys.keys
      return keys_to_check
    end

    # Find the first matching DNSKEY and RRSIG record in the two sets.
    def get_matching_key(keys, sigrecs)#:nodoc: all
      # There can be multiple signatures in the RRSet - which one should we choose?
      if ((keys == nil) || (sigrecs == nil))
        return nil, nil
      end
      if ((RR::DNSKEY === keys) || (RR::DS === keys) ||
            ((RR::DLV === keys) && (@verifier_type == VerifierType::DLV)))
        keys = [keys]
      end
      enumerator = keys
      if (enumerator.class == RRSet)
        enumerator = enumerator.rrs
      end
      enumerator.each {|key|
        if ((key.revoked?)) # || (key.bad_flags?))
          next
        end

        sigrecs.each {|sig|
          if ((key.key_tag == sig.key_tag) && (key.algorithm == sig.algorithm))
            #                        print "Found key #{key.key_tag}\n"
            return key, sig
          end
        }
      }
      return nil, nil
    end

    # Verify the signature of an rrset encoded with the specified KeyCache
    # or RRSet. If no signature is included, false is returned.
    #
    # Returns true if the RRSet verified, false otherwise.
    def verify_rrset(rrset, keys = nil)
      #      print "Verify_rrset #{rrset.name}, #{rrset.type}\n"
      sigrecs = rrset.sigs
      if (rrset.rrs.length == 0)
        raise VerifyError.new("No RRSet to verify")
      end
      if (rrset.num_sigs == 0)
        raise VerifyError.new("No signatures in the RRSet : #{rrset.name}, #{rrset.type}")
      end
      sigrecs.each do |sigrec|
        check_rr_data(rrset, sigrec)
      end
      raise ArgumentError.new("Expecting DNSKEY, DLV, DS, RRSet, Array or nil for keys : got #{keys.class} instead") if
      (keys && (![Array, RR::IN::DNSKEY, RR::IN::DLV, RR::IN::DS].include?keys.class) && (keys.class != RRSet))

      keyrec = nil
      sigrec = nil
      if (rrset.type == Types.DNSKEY)
        if (keys && !(Array === keys) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV))))
          rrset.rrs.each do |key|
            keys.rrs.each do |ds|
              if (ds.check_key(key))
                @trusted_keys.add_key_with_expiration(key, rrset.sigs()[0].expiration)
              end
            end
          end
        else
          check_ds_stores(rrset)
        end
      end
      if ((keys.nil?) || ((keys.class != Array) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV)))))
        keyrec, sigrec = get_matching_key(get_keys_to_check, sigrecs)
      else
        keyrec, sigrec = get_matching_key(keys, sigrecs)
      end

      #      return false if !keyrec
      if (!keyrec)
        #        print "Couldn't find signing key! #{rrset.name}, #{rrset.type},\n "
        raise VerifyError.new("Signing key not found")
      end

      # RFC 4034
      #3.1.8.1.  Signature Calculation

      if (keyrec.sep_key? && !keyrec.zone_key?)
        Dnsruby.log.error("DNSKEY with SEP flag set and Zone Key flag not set was used to verify RRSIG over RRSET - this is not allowed by RFC4034 section 2.1.1")
        #        return false
        raise VerifyError.new("DNSKEY with SEP flag set and Zone Key flag not set")
      end

      #Any DNS names in the RDATA field of each RR MUST be in
      #canonical form; and
      #The RRset MUST be sorted in canonical order.
      rrset = rrset.sort_canonical

      sig_data = sigrec.sig_data

      #RR(i) = owner | type | class | TTL | RDATA length | RDATA
      rrset.each do |rec|
        old_ttl = rec.ttl
        rec.ttl = sigrec.original_ttl
        data = MessageEncoder.new { |msg|
          msg.put_rr(rec, true)
        }.to_s # @TODO@ worry about wildcards here?
        rec.ttl = old_ttl
        if (RUBY_VERSION >= "1.9")
          data.force_encoding("ASCII-8BIT")
        end
        sig_data += data
      end

      # Now calculate the signature
      verified = false
      if [Algorithms.RSASHA1,
          Algorithms.RSASHA1_NSEC3_SHA1].include?(sigrec.algorithm)
        verified = keyrec.public_key.verify(OpenSSL::Digest::SHA1.new, sigrec.signature, sig_data)
      elsif (sigrec.algorithm == Algorithms.RSASHA256)
        verified = keyrec.public_key.verify(OpenSSL::Digest::SHA256.new, sigrec.signature, sig_data)
      elsif (sigrec.algorithm == Algorithms.RSASHA512)
        verified = keyrec.public_key.verify(OpenSSL::Digest::SHA512.new, sigrec.signature, sig_data)
      elsif [Algorithms.DSA,
          Algorithms.DSA_NSEC3_SHA1].include?(sigrec.algorithm)
        # we are ignoring T for now
        # t = sigrec.signature[0]
        # t = t.getbyte(0) if t.class == String
        r = RR::get_num(sigrec.signature[1, 20])
        s = RR::get_num(sigrec.signature[21, 20])
        r_asn1 = OpenSSL::ASN1::Integer.new(r)
        s_asn1 = OpenSSL::ASN1::Integer.new(s)

        asn1 = OpenSSL::ASN1::Sequence.new([r_asn1, s_asn1]).to_der
        verified = keyrec.public_key.verify(OpenSSL::Digest::DSS1.new, asn1, sig_data)
      else
        raise RuntimeError.new("Algorithm #{sigrec.algorithm.code} unsupported by Dnsruby")
      end

      if (!verified)
        raise VerifyError.new("Signature failed to cryptographically verify")
      end
      # Sort out the TTLs - set it to the minimum valid ttl
      expiration_diff = (sigrec.expiration.to_i - Time.now.to_i).abs
      rrset.ttl = ([rrset.ttl, sigrec.ttl, sigrec.original_ttl,
          expiration_diff].sort)[0]
      #            print "VERIFIED OK\n"
      return true
    end

    def find_closest_dlv_anchor_for(name) # :nodoc:
      # To find the closest anchor, query DLV.isc.org for [a.b.c.d], then [a.b.c], [a.b], etc.
      # once closest anchor found, simply run follow_chain from that anchor

      # @TODO@ REALLY NEED AGGRESSIVE NEGATIVE CACHING HERE!!
      # i.e. don't look up zones which we *know* we don't have a DLV anchor for
      
      n = Name.create(name)
      root = Name.create(".")
      while (n != root)
        # Try to find name in DLV, and return it if possible
        dlv_rrset = query_dlv_for(n)
        if (dlv_rrset)
          key_rrset = get_zone_key_from_dlv_rrset(dlv_rrset, n)
          return key_rrset
        end
        # strip the name
        n = n.strip_label
      end
      return false
    end

    def get_zone_key_from_dlv_rrset(dlv_rrset, name) # :nodoc:
      # We want to return the key for the zone i.e. DS/DNSKEY for .se, NOT DLV for se.dlv.isc.org
      # So, we have the DLv record. Now use it to add the zone's DNSKEYs to the trusted key set.
      res = get_nameservers_for(name)
      if (!res)
        if (Dnssec.do_validation_with_recursor?)
          res = get_recursor
        else
          if(Dnssec.default_resolver)
            res = Dnssec.default_resolver
          else
            res = Resolver.new
          end
        end
      end
      #      query = Message.new(name, Types.DNSKEY)
      #      query.do_validation = false
      ret = nil
      begin
        #        ret = res.send_message(query)
        ret = res.query_no_validation_or_recursion(name, Types.DNSKEY)
        if (!ret)
          raise ResolvError.new("Couldn't get DNSKEY from Recursor")
        end
      rescue ResolvError => e
        #        print "Error getting zone key from DLV RR for #{name} : #{e}\n"
        TheLog.error("Error getting zone key from DLV RR for #{name} : #{e}")
        return false
      end
      key_rrset = ret.answer.rrset(name, Types.DNSKEY)
      begin
        verify(key_rrset, dlv_rrset)
        #        Cache.add(ret)
        return key_rrset
      rescue VerifyError => e
        #        print "Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}\n"
        TheLog.debug("Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}")
      end
      return false
    end

    def query_dlv_for(name) # :nodoc:
      # See if there is a record for name in dlv.isc.org
      if (!@res && (@verifier_type == VerifierType::DLV))
        @res = get_dlv_resolver
      end
      begin
        name_to_query = name.to_s+".dlv.isc.org"
        #        query = Message.new(name_to_query, Types.DLV)
        #        @res.single_resolvers()[0].prepare_for_dnssec(query)
        #        query.do_validation = false
        ret = nil
        begin
          #          ret = @res.send_message(query)
          ret = @res.query_no_validation_or_recursion(name_to_query, Types.DLV)
          if (!ret)
            raise ResolvError.new("Couldn't get DLV record from Recursor")
          end
        rescue ResolvError => e
          #          print "Error getting DLV record for #{name} : #{e}\n"
          TheLog.info("Error getting DLV record for #{name} : #{e}")
          return nil
        end
        dlv_rrset = ret.answer.rrset(name_to_query,Types.DLV)
        if (dlv_rrset.rrs.length > 0)
          begin
            verify(dlv_rrset)
            #            Cache.add(ret)
            return dlv_rrset
          rescue VerifyError => e
            #            print "Error verifying DLV records for #{name}, #{e}\n"
            TheLog.info("Error verifying DLV records for #{name}, #{e}")
          end
        end
      rescue NXDomain
        #        print "NXDomain for DLV lookup for #{name}\n"
        return nil
      end
      return nil
    end

    def find_closest_anchor_for(name) # :nodoc:
      # Check if we have an anchor for name.
      # If not, strip off first label and try again
      # If we get to root, then return false
      name = "." if name == ""
      n = Name.create(name)
      root = Name.create(".")
      while (true) # n != root)
        # Try the trusted keys first, then the DS set
        (@trust_anchors.keys + @trusted_keys.keys + @configured_ds_store + @discovered_ds_store).each {|key|
          return key if key.name.canonical == n.canonical
        }
        break if (n.to_s == root.to_s)
        # strip the name
        n = n.strip_label
      end
      return false
    end

    # @TODO@ Handle REVOKED keys! (RFC 5011)
    # Remember that revoked keys will have a different key_tag than pre-revoked.
    # So, if we see a revoked key, we should go through our key store for
    # that authority and remove any keys with the pre-revoked key_tag.

    def follow_chain(anchor, name) # :nodoc:
      # Follow the chain from the anchor to name, returning the appropriate
      # key at the end, or false.
      #
      # i.e. anchor = se, name = foo.example.se
      #   get anchor for example.se with se anchor
      #   get anchor for foo.example.se with example.se anchor
      next_key = anchor
      next_step = anchor.name
      parent = next_step
      #      print "Follow chain from #{anchor.name} to #{name}\n"
      TheLog.debug("Follow chain from #{anchor.name} to #{name}")

      #      res = nil
      res = Dnssec.default_resolver
      #      while ((next_step != name) || (next_key.type != Types.DNSKEY))
      while (true)
        #        print "In loop for parent=#{parent}, next step = #{next_step}\n"
        dont_move_on = false
        if (next_key.type != Types.DNSKEY)
          dont_move_on = true
        end
        next_key, res = get_anchor_for(next_step, parent, next_key, res)
        if (next_step.canonical.to_s == name.canonical.to_s)
          #      print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n"
          return next_key
        end
        return false if (!next_key)
        # Add the next label on
        if (!dont_move_on)
          parent = next_step
          next_step = Name.new(name.labels[name.labels.length-1-next_step.labels.length,1] +
              next_step.labels , name.absolute?)
          #          print "Next parent = #{parent}, next_step = #{next_step}, next_key.type = #{next_key.type.string}\n"
        end
      end

      #      print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n"

      return next_key
    end

    def get_anchor_for(child, parent, current_anchor, parent_res = nil) # :nodoc:
      #      print "Trying to discover anchor for #{child} from #{parent}\n"
      TheLog.debug("Trying to discover anchor for #{child} from #{parent} using #{current_anchor}, #{parent_res}")
      # We wish to return a DNSKEY which the caller can use to verify name
      # We are either given a key or a ds record from the parent zone
      # If given a DNSKEY, then find a DS record signed by that key for the child zone
      # Use the DS record to find a valid key in the child zone
      # Return it

      # Find NS RRSet for parent
      child_res = nil
      if (Dnssec.do_validation_with_recursor?)
        parent_res = get_recursor
        child_res = get_recursor
      end
      begin
        if (child!=parent)
          if (!parent_res)
            #                      print "No res passed - try to get nameservers for #{parent}\n"
            parent_res = get_nameservers_for(parent)
            if (!parent_res)
              if (Dnssec.do_validation_with_recursor?)
                parent_res = get_recursor
              else
                if (Dnssec.default_resolver)
                  parent_res = Dnssec.default_resolver
                else
                  parent_res = Resolver.new
                end
              end
            end
          end
          # Use that Resolver to query for DS record and NS for children
          ds_rrset = current_anchor
          if (current_anchor.type == Types.DNSKEY)
            #            print "Trying to find DS records for #{child} from servers for #{parent}\n"
            TheLog.debug("Trying to find DS records for #{child} from servers for #{parent}")
            ds_ret = nil
            begin
              ds_ret = parent_res.query_no_validation_or_recursion(child, Types.DS)
              if (!ds_ret)
                raise ResolvError.new("Couldn't get DS records from Recursor")
              end
            rescue ResolvError => e
              #              print "Error getting DS record for #{child} : #{e}\n"
              TheLog.error("Error getting DS record for #{child} : #{e}")
              return false, nil
            end
            ds_rrset = ds_ret.answer.rrset(child, Types.DS)
            if (ds_rrset.rrs.length == 0)
              # @TODO@ Check NSEC(3) records - still need to verify there are REALLY no ds records!
              #              print "NO DS RECORDS RETURNED FOR #{parent}\n"
              #              child_res = parent_res
            else
              begin
                if (verify(ds_rrset, current_anchor) || verify(ds_rrset))
                  # Try to make the resolver from the authority/additional NS RRSets in DS response
                  if (!Dnssec.do_validation_with_recursor?)
                    child_res = get_nameservers_from_message(child, ds_ret)
                  end
                end
              rescue VerifyError => e
                #                print "FAILED TO VERIFY DS RRSET FOR #{child}\n"
                TheLog.info("FAILED TO VERIFY DS RRSET FOR #{child}")
                return false, nil
              end
            end
          end
        end
        # Make Resolver using all child NSs
        if (!child_res)
          child_res = get_nameservers_for(child, parent_res)
        end
        if (!child_res)
          if (Dnssec.do_validation_with_recursor?)
            child_res = get_recursor
          else
            if (Dnssec.default_resolver)
              child_res = Dnssec.default_resolver
            else
              if (Dnssec.default_resolver)
                child_res = Dnssec.default_resolver
              else
                child_res = Resolver.new
              end
            end
          end
        end
        # Query for DNSKEY record, and verify against DS in parent.
        # Need to get resolver NOT to verify this message - we verify it afterwards
        #        print "Trying to find DNSKEY records for #{child} from servers for #{child}\n"
        TheLog.info("Trying to find DNSKEY records for #{child} from servers for #{child}")
        #        query = Message.new(child, Types.DNSKEY)
        #        query.do_validation = false
        key_ret = nil
        begin
          #          key_ret = child_res.send_message(query)
          key_ret = child_res.query_no_validation_or_recursion(child, Types.DNSKEY)
          if (!key_ret)
            raise ResolvError.new("Couldn't get info from Recursor")
          end
        rescue ResolvError => e
          #          print "Error getting DNSKEY for #{child} : #{e}\n"
          TheLog.error("Error getting DNSKEY for #{child} : #{e}")
          return false, nil
        end
        verified = true
        key_rrset = key_ret.answer.rrset(child, Types.DNSKEY)
        if (key_rrset.rrs.length == 0)
          # @TODO@ Still need to check NSEC records to make *sure* no key rrs returned!
          #          print "NO DNSKEY RECORDS RETURNED FOR #{child}\n"
          TheLog.debug("NO DNSKEY RECORDS RETURNED FOR #{child}")
          #        end
          verified = false
        else
          # Should check that the matching key's zone flag is set (RFC 4035 section 5.2)
          key_rrset.rrs.each {|k|
            if (!k.zone_key?)
              #              print "Discovered DNSKEY is not a zone key - ignoring\n"
              TheLog.debug("Discovered DNSKEY is not a zone key - ignoring")
              return false, child_res
            end
          }
          begin
            verify(key_rrset, ds_rrset)
          rescue VerifyError => e
            begin
              verify(key_rrset)
            rescue VerifyError =>e
              verified = false
            end
          end
        end
        
        # Try to make the resolver from the authority/additional NS RRSets in DNSKEY response
        new_res = get_nameservers_from_message(child,  key_ret) # @TODO@ ?
        if (!new_res)
          new_res = child_res
        end
        if (!verified)
          TheLog.info("Failed to verify DNSKEY for #{child}")
          return false, nil # new_res
        end
        #        Cache.add(key_ret)
        return key_rrset, new_res
      rescue VerifyError => e
        #        print "Verification error : #{e}\n"
        TheLog.info("Verification error : #{e}\n")
        return false, nil # new_res
      end
    end

    def get_nameservers_for(name, res = nil) # :nodoc:
      # @TODO@ !!!
      if (Dnssec.do_validation_with_recursor?)
        return get_recursor
      else
        if (Dnssec.default_resolver)
          return Dnssec.default_resolver
        else
          return Resolver.new
        end
      end
    end

    def get_nameservers_from_message(name, ns_ret) # :nodoc:
      if (Dnssec.default_resolver)
        return Dnssec.default_resolver
      end

      ns_rrset = ns_ret.answer.rrset(name, Types.NS)
      if (!ns_rrset || ns_rrset.length == 0)
        ns_rrset = ns_ret.authority.rrset(name, Types.NS) # @TODO@ Is ths OK?
      end
      if (!ns_rrset || ns_rrset.length == 0 || ns_rrset.name.canonical != name.canonical)
        return nil
      end
      if (ns_rrset.sigs.length > 0)
        #                verify_rrset(ns_rrset) # @TODO@ ??
      end
      #      Cache.add(ns_ret)
      ns_additional = []
      ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.A) }
      nameservers = []
      add_nameservers(ns_rrset, ns_additional, nameservers) # if (ns_additional.length > 0)
      ns_additional = []
      ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.AAAA) }
      add_nameservers(ns_rrset, ns_additional, nameservers) if (ns_additional.length > 0)
      # Make Resolver using all NSs
      if (nameservers.length == 0)
        #        print "Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}\n"
        TheLog.info("Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}")
        return  nil # @TODO@ Could return a recursor here?
        #return Recursor.new
      end
      res = Resolver.new()
      res.nameserver=(nameservers)
      # Set the retry_delay to be (at least) the number of nameservers
      # Otherwise, the queries will be sent at a rate of more than one a second!
      res.retry_delay = nameservers.length * 2
      res.dnssec = true
      return res
    end

    def add_nameservers(ns_rrset, ns_additional, nameservers) # :nodoc:
      # Want to go through all of the ns_rrset NS records,
      #      print "Checking #{ns_rrset.rrs.length} NS records against #{ns_additional.length} address records\n"
      ns_rrset.rrs.sort_by {rand}.each {|ns_rr|
        #   and see if we can find any of the names in the A/AAAA records in ns_additional
        found_addr = false
        ns_additional.each {|addr_rr|
          if (ns_rr.nsdname.canonical == addr_rr.name.canonical)
            #            print "Found address #{addr_rr.address} for #{ns_rr.nsdname}\n"
            nameservers.push(addr_rr.address.to_s)
            found_addr = true
            break
            # If we can, then we add the server A/AAAA address to nameservers
          end
          # If we can't, then we add the server NS name to nameservers

        }
        if (!found_addr)
          #          print "Couldn't find address - adding #{ns_rr.nsdname}\n"
          nameservers.push(ns_rr.nsdname)
        end

      }
    end

    def validate_no_rrsigs(msg) # :nodoc:
      #      print "Validating unsigned response\n"
      # WHAT IF THERE ARE NO RRSIGS IN MSG?
      # Then we need to check that we do not expect any RRSIGs
      if (!msg.question()[0] && msg.answer.length == 0)
        #        print "Returning Message insecure OK\n"
        msg.security_level = Message::SecurityLevel.INSECURE
        return true
      end
      qname = msg.question()[0].qname
      closest_anchor = find_closest_anchor_for(qname)
      #      print "Found closest anchor :#{closest_anchor}\n"
      if (closest_anchor)
        actual_anchor = follow_chain(closest_anchor, qname)
        #        print "Actual anchor : #{actual_anchor}\n"
        if (actual_anchor)
          #          print("Anchor exists for #{qname}, but no signatures in #{msg}\n")
          TheLog.error("Anchor exists for #{qname}, but no signatures in #{msg}")
          msg.security_level = Message::SecurityLevel.BOGUS
          return false
        end
      end
      if ((@verifier_type == VerifierType::DLV) &&
            @added_dlv_key)
        # Remember to check DLV registry as well (if appropriate!)
        #        print "Checking DLV for closest anchor\n"
        dlv_anchor = find_closest_dlv_anchor_for(qname)
        #        print "Found DLV closest anchor :#{dlv_anchor}\n"
        if (dlv_anchor)
          actual_anchor = follow_chain(dlv_anchor, qname)
          #          print "Actual anchor : #{actual_anchor}\n"
          if (actual_anchor)
            #            print("DLV Anchor exists for #{qname}, but no signatures in #{msg}\n")
            TheLog.error("DLV Anchor exists for #{qname}, but no signatures in #{msg}")
            msg.security_level = Message::SecurityLevel.BOGUS
            return false
          end

        end
      end
      #      print "Returning Message insecure OK\n"
      msg.security_level = Message::SecurityLevel.INSECURE
      return true
    end

    def validate(msg, query)
      if (msg.rrsets('RRSIG').length == 0)
        return validate_no_rrsigs(msg)
      end

      # See if it is a child of any of our trust anchors.
      # If it is, then see if we have a trusted key for it
      # If we don't, then see if we can get to it from the closest
      # trust anchor
      # Otherwise, try DLV (if configured)
      #
      #
      # So - find closest existing trust anchor
      error = nil
      msg.security_level = Message::SecurityLevel.INDETERMINATE
      qname = msg.question()[0].qname
      closest_anchor = find_closest_anchor_for(qname)
      TheLog.debug("Closest anchor for #{qname} is #{closest_anchor} - trying to follow down")
      error = try_to_follow_from_anchor(closest_anchor, msg, qname)

      if ((msg.security_level.code < Message::SecurityLevel::SECURE) &&
            (@verifier_type == VerifierType::DLV) &&
            @added_dlv_key)
        # If we can't find anything, and we're set to check DLV, then
        # check the DLV registry and work down from there.
        dlv_anchor = find_closest_dlv_anchor_for(qname)
        if (dlv_anchor)
          #          print "Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}\n"
          TheLog.debug("Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}")
          error = try_to_follow_from_anchor(dlv_anchor, msg, qname)
        else
          #          print "Couldn't find DLV anchor for #{qname}\n"
          TheLog.debug("Couldn't find DLV anchor for #{qname}")
        end
      end
      if (msg.security_level.code != Message::SecurityLevel::SECURE)
        begin
          #          print "Trying to verify one last time\n"

          if verify(msg) # Just make sure we haven't picked the keys up anywhere
            msg.security_level = Message::SecurityLevel.SECURE
            return true
          end
        rescue VerifyError => e
          #          print "Verify failed : #{e}\n"
        end
      end
      if (error)
        raise error
      end
      if (msg.security_level.code > Message::SecurityLevel::UNCHECKED)
        return true
      else
        return false
      end
    end

    def try_to_follow_from_anchor(closest_anchor, msg, qname) # :nodoc:
      error = nil
      if (closest_anchor)
        # Then try to descend to the level we're interested in
        actual_anchor = follow_chain(closest_anchor, qname)
        if (!actual_anchor)
          TheLog.debug("Unable to follow chain from anchor : #{closest_anchor.name}")
          msg.security_level = Message::SecurityLevel.INSECURE
        else
          actual_anchor_keys = ""
          actual_anchor.rrs.each {|rr| actual_anchor_keys += ", #{rr.key_tag}"}
          TheLog.debug("Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}")
          #          print "Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}\n"
          begin
            if (verify(msg, actual_anchor))
              TheLog.debug("Validated #{qname}")
              msg.security_level = Message::SecurityLevel.SECURE
            end
          rescue VerifyError => e
            TheLog.info("BOGUS #{qname}! Error : #{e}")
            #            print "BOGUS #{qname}! Error : #{e}\n"
            msg.security_level = Message::SecurityLevel.BOGUS
            error = e
          end
        end
      else
        #        print "Unable to find an anchor for #{qname}\n"
        msg.security_level = Message::SecurityLevel.INSECURE
      end
      return error
    end

  end
end