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

int worker_handle_request ( struct comm_point c,
void *  arg,
int  error,
struct comm_reply repinfo 
)

handles callbacks from listening event interface

Definition at line 680 of file worker.c.

References daemon::acl, acl_deny, acl_list_lookup(), acl_refuse, comm_reply::addr, comm_reply::addrlen, mesh_area::all, answer_chaos(), answer_from_cache(), answer_norec_from_cache(), attach_edns_record(), edns_data::bits, comm_point::buffer, daemon::cfg, comm_point_drop_reply(), rbtree_t::count, worker::daemon, lruhash_entry::data, EDNS_ADVERTISED_SIZE, EDNS_ADVERTISED_VERSION, EDNS_DO, edns_data::edns_present, EDNS_RCODE_BADVERS, edns_data::edns_version, worker::env, error_encode(), edns_data::ext_rcode, config_file::harden_short_bufsize, daemon::local_zones, local_zones_answer(), lruhash_entry::lock, log_addr(), log_buf(), module_env::mesh, mesh_new_client(), module_env::msg_cache, NETEVENT_NOERROR, NORMAL_UDP_SIZE, server_stats::num_queries, server_stats::num_query_list_exceeded, mesh_area::num_reply_addrs, parse_edns_from_pkt(), query_info::qclass, query_info::qtype, query_info_hash(), query_info_parse(), worker::request_size, worker::scratchpad, server_stats_querymiss(), slabhash_lookup(), worker::stats, comm_point::type, edns_data::udp_size, VERB_ALGO, VERB_QUERY, verbose(), worker_check_request(), and worker_mem_report().

Referenced by fptr_whitelist_comm_point(), and worker_init().

{
      struct worker* worker = (struct worker*)arg;
      int ret;
      hashvalue_t h;
      struct lruhash_entry* e;
      struct query_info qinfo;
      struct edns_data edns;
      enum acl_access acl;

      if(error != NETEVENT_NOERROR) {
            /* some bad tcp query DNS formats give these error calls */
            verbose(VERB_ALGO, "handle request called with err=%d", error);
            return 0;
      }
      acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr, 
            repinfo->addrlen);
      if(acl == acl_deny) {
            comm_point_drop_reply(repinfo);
            return 0;
      } else if(acl == acl_refuse) {
            ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
            ldns_buffer_write_at(c->buffer, 4, 
                  (uint8_t*)"\0\0\0\0\0\0\0\0", 8);
            LDNS_QR_SET(ldns_buffer_begin(c->buffer));
            LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
                  LDNS_RCODE_REFUSED);
            log_addr(VERB_ALGO, "refused query from",
                  &repinfo->addr, repinfo->addrlen);
            log_buf(VERB_ALGO, "refuse", c->buffer);
            return 1;
      }
      if((ret=worker_check_request(c->buffer, worker)) != 0) {
            verbose(VERB_ALGO, "worker check request: bad query.");
            if(ret != -1) {
                  LDNS_QR_SET(ldns_buffer_begin(c->buffer));
                  LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
                  return 1;
            }
            comm_point_drop_reply(repinfo);
            return 0;
      }
      worker->stats.num_queries++;
      /* see if query is in the cache */
      if(!query_info_parse(&qinfo, c->buffer)) {
            verbose(VERB_ALGO, "worker parse request: formerror.");
            LDNS_QR_SET(ldns_buffer_begin(c->buffer));
            LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
                  LDNS_RCODE_FORMERR);
            return 1;
      }
      if(qinfo.qtype == LDNS_RR_TYPE_AXFR || 
            qinfo.qtype == LDNS_RR_TYPE_IXFR) {
            verbose(VERB_ALGO, "worker request: refused zone transfer.");
            LDNS_QR_SET(ldns_buffer_begin(c->buffer));
            LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
                  LDNS_RCODE_REFUSED);
            return 1;
      }
      if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
            verbose(VERB_ALGO, "worker parse edns: formerror.");
            LDNS_QR_SET(ldns_buffer_begin(c->buffer));
            LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
            return 1;
      }
      if(edns.edns_present && edns.edns_version != 0) {
            edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
            edns.edns_version = EDNS_ADVERTISED_VERSION;
            edns.udp_size = EDNS_ADVERTISED_SIZE;
            edns.bits &= EDNS_DO;
            verbose(VERB_ALGO, "query with bad edns version.");
            error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
                  *(uint16_t*)ldns_buffer_begin(c->buffer),
                  ldns_buffer_read_u16_at(c->buffer, 2), NULL);
            attach_edns_record(c->buffer, &edns);
            return 1;
      }
      if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
            worker->daemon->cfg->harden_short_bufsize) {
            verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
                  (int)edns.udp_size);
            edns.udp_size = NORMAL_UDP_SIZE;
      }
      if(edns.edns_present && edns.udp_size < LDNS_HEADER_SIZE) {
            verbose(VERB_ALGO, "worker request: edns is too small.");
            LDNS_QR_SET(ldns_buffer_begin(c->buffer));
            LDNS_TC_SET(ldns_buffer_begin(c->buffer));
            LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
                  LDNS_RCODE_SERVFAIL);
            ldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
            ldns_buffer_write_at(c->buffer, 4, 
                  (uint8_t*)"\0\0\0\0\0\0\0\0", 8);
            ldns_buffer_flip(c->buffer);
            return 1;
      }
      if(c->type != comm_udp)
            edns.udp_size = 65535; /* max size for TCP replies */
      if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
            &edns, c->buffer)) {
            return 1;
      }
      if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns, 
            c->buffer, worker->scratchpad)) {
            return (ldns_buffer_limit(c->buffer) != 0);
      }
      h = query_info_hash(&qinfo);
      if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
            /* answer from cache - we have acquired a readlock on it */
            if(answer_from_cache(worker, &qinfo, 
                  (struct reply_info*)e->data, 
                  *(uint16_t*)ldns_buffer_begin(c->buffer), 
                  ldns_buffer_read_u16_at(c->buffer, 2), repinfo, 
                  &edns)) {
                  lock_rw_unlock(&e->lock);
                  return 1;
            }
            verbose(VERB_ALGO, "answer from the cache failed");
            lock_rw_unlock(&e->lock);
      }
      if(!LDNS_RD_WIRE(ldns_buffer_begin(c->buffer))) {
            if(answer_norec_from_cache(worker, &qinfo,
                  *(uint16_t*)ldns_buffer_begin(c->buffer), 
                  ldns_buffer_read_u16_at(c->buffer, 2), repinfo, 
                  &edns)) {
                  return 1;
            }
            verbose(VERB_ALGO, "answer norec from cache -- "
                  "need to validate or not primed");
      }
      ldns_buffer_rewind(c->buffer);
      server_stats_querymiss(&worker->stats, worker);

      /* grab a work request structure for this new request */
      if(worker->env.mesh->all.count > worker->request_size) {
            verbose(VERB_ALGO, "Too many requests active. "
                  "dropping incoming query.");
            worker->stats.num_query_list_exceeded++;
            comm_point_drop_reply(repinfo);
            return 0;
      } else if(worker->env.mesh->num_reply_addrs>worker->request_size*16) {
            verbose(VERB_ALGO, "Too many requests queued. "
                  "dropping incoming query.");
            worker->stats.num_query_list_exceeded++;
            comm_point_drop_reply(repinfo);
            return 0;
      }
      mesh_new_client(worker->env.mesh, &qinfo, 
            ldns_buffer_read_u16_at(c->buffer, 2),
            &edns, repinfo, *(uint16_t*)ldns_buffer_begin(c->buffer));
      worker_mem_report(worker, NULL);
      return 0;
}


Generated by  Doxygen 1.6.0   Back to index