Logo Search packages:      
Sourcecode: unbound version File versions  Download package

static int processQueryResponse ( struct module_qstate qstate,
struct iter_qstate iq,
int  id 
) [static]

Process the query response. All queries end up at this state first. This process generally consists of analyzing the response and routing the event to the next state (either bouncing it back to a request state, or terminating the processing for this event).

Parameters:
qstate,: query state.
iq,: iterator query state.
id,: module id.
Returns:
true if the event requires more immediate processing, false if not. This is generally only true when forwarding the request to the final state (i.e., on answer).

Definition at line 1198 of file iterator.c.

References comm_reply::addr, comm_reply::addrlen, BIT_RD, cache_fill_missing(), iter_qstate::chase_flags, iter_qstate::deleg_msg, delegpt_from_message(), delegpt_log(), module_env::detach_subs, iter_qstate::dnssec_expected, iter_qstate::dp, module_qstate::env, error_response(), final_state(), fptr_ok, fptr_whitelist_modenv_detach_subs(), handle_cname_response(), module_env::infra_cache, infra_set_lame(), INIT_REQUEST_STATE, iter_dns_store(), iter_indicates_dnssec(), iter_msg_from_zone(), iter_msg_has_dnssec(), log_dns_msg(), log_err(), log_warn(), delegpt::name, delegpt::namelen, next_state(), module_env::now, iter_qstate::num_current_queries, iter_qstate::num_target_queries, outbound_list_clear(), iter_qstate::outlist, iter_qstate::qchase, query_info::qclass, dns_msg::qinfo, query_info::qname, query_info::qname_len, iter_qstate::query_restart_count, QUERYTARGETS_STATE, iter_qstate::referral_count, module_qstate::region, dns_msg::rep, module_qstate::reply, iter_qstate::response, RESPONSE_TYPE_ANSWER, RESPONSE_TYPE_CNAME, response_type_from_server(), RESPONSE_TYPE_LAME, RESPONSE_TYPE_REFERRAL, RESPONSE_TYPE_THROWAWAY, RESPONSE_TYPE_UNTYPED, VERB_ALGO, VERB_DETAIL, verbose(), and verbosity.

Referenced by iter_handle().

{
      int dnsseclame = 0;
      enum response_type type;
      iq->num_current_queries--;
      if(iq->response == NULL) {
            verbose(VERB_ALGO, "query response was timeout");
            return next_state(iq, QUERYTARGETS_STATE);
      }
      type = response_type_from_server((int)(iq->chase_flags&BIT_RD),
            iq->response, &iq->qchase, iq->dp);
      if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD)) {
            /* When forwarding (RD bit is set), we handle referrals 
             * differently. No queries should be sent elsewhere */
            type = RESPONSE_TYPE_ANSWER;
      }
      if(iq->dnssec_expected && !(iq->chase_flags&BIT_RD) 
            && type != RESPONSE_TYPE_LAME 
            && type != RESPONSE_TYPE_THROWAWAY 
            && type != RESPONSE_TYPE_UNTYPED) {
            /* a possible answer, see if it is missing DNSSEC */
            /* but not when forwarding, so we dont mark fwder lame */
            /* also make sure the answer is from the zone we expected,
             * otherwise, (due to parent,child on same server), we
             * might mark the server,zone lame inappropriately */
            if(!iter_msg_has_dnssec(iq->response) &&
                  iter_msg_from_zone(iq->response, iq->dp, type,
                        iq->qchase.qclass)) {
                  type = RESPONSE_TYPE_LAME;
                  dnsseclame = 1;
            }
      }

      /* handle each of the type cases */
      if(type == RESPONSE_TYPE_ANSWER) {
            /* ANSWER type responses terminate the query algorithm, 
             * so they sent on their */
            verbose(VERB_DETAIL, "query response was ANSWER");
            if(!iter_dns_store(qstate->env, &iq->response->qinfo,
                  iq->response->rep, 0))
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
            /* close down outstanding requests to be discarded */
            outbound_list_clear(&iq->outlist);
            iq->num_current_queries = 0;
            fptr_ok(fptr_whitelist_modenv_detach_subs(
                  qstate->env->detach_subs));
            (*qstate->env->detach_subs)(qstate);
            iq->num_target_queries = 0;
            return final_state(iq);
      } else if(type == RESPONSE_TYPE_REFERRAL) {
            /* REFERRAL type responses get a reset of the 
             * delegation point, and back to the QUERYTARGETS_STATE. */
            verbose(VERB_DETAIL, "query response was REFERRAL");

            /* Store the referral under the current query */
            if(!iter_dns_store(qstate->env, &iq->response->qinfo,
                  iq->response->rep, 1))
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);

            /* Reset the event state, setting the current delegation 
             * point to the referral. */
            iq->deleg_msg = iq->response;
            iq->dp = delegpt_from_message(iq->response, qstate->region);
            if(!iq->dp)
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
            if(!cache_fill_missing(qstate->env, iq->qchase.qclass, 
                  qstate->region, iq->dp))
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
            delegpt_log(VERB_ALGO, iq->dp);
            /* Count this as a referral. */
            iq->referral_count++;
            /* see if the next dp is a trust anchor, or a DS was sent
             * along, indicating dnssec is expected for next zone */
            iq->dnssec_expected = iter_indicates_dnssec(qstate->env, 
                  iq->dp, iq->response, iq->qchase.qclass);

            /* stop current outstanding queries. 
             * FIXME: should the outstanding queries be waited for and
             * handled? Say by a subquery that inherits the outbound_entry.
             */
            outbound_list_clear(&iq->outlist);
            iq->num_current_queries = 0;
            fptr_ok(fptr_whitelist_modenv_detach_subs(
                  qstate->env->detach_subs));
            (*qstate->env->detach_subs)(qstate);
            iq->num_target_queries = 0;
            verbose(VERB_ALGO, "cleared outbound list for next round");
            return next_state(iq, QUERYTARGETS_STATE);
      } else if(type == RESPONSE_TYPE_CNAME) {
            uint8_t* sname = NULL;
            size_t snamelen = 0;
            /* CNAME type responses get a query restart (i.e., get a 
             * reset of the query state and go back to INIT_REQUEST_STATE).
             */
            verbose(VERB_DETAIL, "query response was CNAME");
            if(verbosity >= VERB_ALGO)
                  log_dns_msg("cname msg", &iq->response->qinfo, 
                        iq->response->rep);
            /* Process the CNAME response. */
            if(!handle_cname_response(qstate, iq, iq->response, 
                  &sname, &snamelen))
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
            /* cache the CNAME response under the current query */
            /* NOTE : set referral=1, so that rrsets get stored but not 
             * the partial query answer (CNAME only). */
            if(!iter_dns_store(qstate->env, &iq->response->qinfo,
                  iq->response->rep, 1))
                  return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
            /* set the current request's qname to the new value. */
            iq->qchase.qname = sname;
            iq->qchase.qname_len = snamelen;
            /* Clear the query state, since this is a query restart. */
            iq->deleg_msg = NULL;
            iq->dp = NULL;
            iq->dnssec_expected = 0;
            /* Note the query restart. */
            iq->query_restart_count++;

            /* stop current outstanding queries. 
             * FIXME: should the outstanding queries be waited for and
             * handled? Say by a subquery that inherits the outbound_entry.
             */
            outbound_list_clear(&iq->outlist);
            iq->num_current_queries = 0;
            fptr_ok(fptr_whitelist_modenv_detach_subs(
                  qstate->env->detach_subs));
            (*qstate->env->detach_subs)(qstate);
            iq->num_target_queries = 0;
            verbose(VERB_ALGO, "cleared outbound list for query restart");
            /* go to INIT_REQUEST_STATE for new qname. */
            return next_state(iq, INIT_REQUEST_STATE);
      } else if(type == RESPONSE_TYPE_LAME) {
            /* Cache the LAMEness. */
            verbose(VERB_DETAIL, "query response was %sLAME",
                  dnsseclame?"DNSSEC ":"");
            if(qstate->reply) {
                  /* need addr for lameness cache, but we may have
                   * gotten this from cache, so test to be sure */
                  if(!infra_set_lame(qstate->env->infra_cache, 
                        &qstate->reply->addr, qstate->reply->addrlen, 
                        iq->dp->name, iq->dp->namelen, 
                        *qstate->env->now, dnsseclame))
                        log_err("mark host lame: out of memory");
            } else log_err("%slame response from cache",
                  dnsseclame?"DNSSEC ":"");
      } else if(type == RESPONSE_TYPE_THROWAWAY) {
            /* LAME and THROWAWAY responses are handled the same way. 
             * In this case, the event is just sent directly back to 
             * the QUERYTARGETS_STATE without resetting anything, 
             * because, clearly, the next target must be tried. */
            verbose(VERB_DETAIL, "query response was THROWAWAY");
      } else {
            log_warn("A query response came back with an unknown type: %d",
                  (int)type);
      }

      /* LAME, THROWAWAY and "unknown" all end up here.
       * Recycle to the QUERYTARGETS state to hopefully try a 
       * different target. */
      return next_state(iq, QUERYTARGETS_STATE);
}


Generated by  Doxygen 1.6.0   Back to index