/** * Create commpoint (as return address) for a fake incoming query. */ static void fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) { struct comm_reply repinfo; memset(&repinfo, 0, sizeof(repinfo)); repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in); if(todo->addrlen != 0) { repinfo.addrlen = todo->addrlen; memcpy(&repinfo.addr, &todo->addr, todo->addrlen); } repinfo.c->fd = -1; repinfo.c->ev = (struct internal_event*)runtime; repinfo.c->buffer = sldns_buffer_new(runtime->bufsize); if(todo->match->match_transport == transport_tcp) repinfo.c->type = comm_tcp; else repinfo.c->type = comm_udp; fill_buffer_with_reply(repinfo.c->buffer, todo->match, NULL, 0); log_info("testbound: incoming QUERY"); log_pkt("query pkt", todo->match->reply_list->reply_pkt, todo->match->reply_list->reply_len); /* call the callback for incoming queries */ if((*runtime->callback_query)(repinfo.c, runtime->cb_arg, NETEVENT_NOERROR, &repinfo)) { /* send immediate reply */ comm_point_send_reply(&repinfo); } /* clear it again, in case copy not done properly */ memset(&repinfo, 0, sizeof(repinfo)); }
/** Reply to client and perform prefetch to keep cache up to date */ static void reply_and_prefetch(struct worker* worker, struct query_info* qinfo, uint16_t flags, struct comm_reply* repinfo, time_t leeway) { /* first send answer to client to keep its latency * as small as a cachereply */ comm_point_send_reply(repinfo); server_stats_prefetch(&worker->stats, worker); /* create the prefetch in the mesh as a normal lookup without * client addrs waiting, which has the cache blacklisted (to bypass * the cache and go to the network for the data). */ /* this (potentially) runs the mesh for the new query */ mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + PREFETCH_EXPIRY_ADD); }
/** * Send reply to mesh reply entry * @param m: mesh state to send it for. * @param rcode: if not 0, error code. * @param rep: reply to send (or NULL if rcode is set). * @param r: reply entry * @param prev: previous reply, already has its answer encoded in buffer. */ static void mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct mesh_reply* r, struct mesh_reply* prev) { struct timeval end_time; struct timeval duration; int secure; /* examine security status */ if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || m->s.env->cfg->ignore_cd) && rep && rep->security <= sec_status_bogus) { rcode = LDNS_RCODE_SERVFAIL; if(m->s.env->cfg->stat_extended) m->s.env->mesh->ans_bogus++; } if(rep && rep->security == sec_status_secure) secure = 1; else secure = 0; if(!rep && rcode == LDNS_RCODE_NOERROR) rcode = LDNS_RCODE_SERVFAIL; /* send the reply */ if(prev && prev->qflags == r->qflags && prev->edns.edns_present == r->edns.edns_present && prev->edns.bits == r->edns.bits && prev->edns.udp_size == r->edns.udp_size) { /* if the previous reply is identical to this one, fix ID */ if(prev->query_reply.c->buffer != r->query_reply.c->buffer) ldns_buffer_copy(r->query_reply.c->buffer, prev->query_reply.c->buffer); ldns_buffer_write_at(r->query_reply.c->buffer, 0, &r->qid, sizeof(uint16_t)); ldns_buffer_write_at(r->query_reply.c->buffer, 12, r->qname, m->s.qinfo.qname_len); comm_point_send_reply(&r->query_reply); } else if(rcode) { m->s.qinfo.qname = r->qname; error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); comm_point_send_reply(&r->query_reply); } else { size_t udp_size = r->edns.udp_size; r->edns.edns_version = EDNS_ADVERTISED_VERSION; r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; m->s.qinfo.qname = r->qname; if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->query_reply.c->buffer, 0, 1, m->s.env->scratch, udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), secure)) { error_encode(r->query_reply.c->buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } comm_point_send_reply(&r->query_reply); } /* account */ m->s.env->mesh->num_reply_addrs--; end_time = *m->s.env->now_tv; timeval_subtract(&duration, &end_time, &r->start_time); verbose(VERB_ALGO, "query took %d.%6.6d sec", (int)duration.tv_sec, (int)duration.tv_usec); m->s.env->mesh->replies_sent++; timeval_add(&m->s.env->mesh->replies_sum_wait, &duration); timehist_insert(m->s.env->mesh->histogram, &duration); if(m->s.env->cfg->stat_extended) { uint16_t rc = FLAGS_GET_RCODE(ldns_buffer_read_u16_at(r-> query_reply.c->buffer, 2)); if(secure) m->s.env->mesh->ans_secure++; m->s.env->mesh->ans_rcode[ rc ] ++; if(rc == 0 && LDNS_ANCOUNT(ldns_buffer_begin(r-> query_reply.c->buffer)) == 0) m->s.env->mesh->ans_nodata++; } }
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, uint16_t qid) { /* do not use CD flag from user for mesh state, we want the CD-query * to receive validation anyway, to protect out cache contents and * avoid bad-data in this cache that a downstream validator cannot * remove from this cache */ struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&BIT_RD, 0); int was_detached = 0; int was_noreply = 0; int added = 0; /* does this create a new reply state? */ if(!s || s->list_select == mesh_no_list) { if(!mesh_make_new_space(mesh, rep->c->buffer)) { verbose(VERB_ALGO, "Too many queries. dropping " "incoming query."); comm_point_drop_reply(rep); mesh->stats_dropped ++; return; } /* for this new reply state, the reply address is free, * so the limit of reply addresses does not stop reply states*/ } else { /* protect our memory usage from storing reply addresses */ if(mesh->num_reply_addrs > mesh->max_reply_states*16) { verbose(VERB_ALGO, "Too many requests queued. " "dropping incoming query."); mesh->stats_dropped++; comm_point_drop_reply(rep); return; } } /* see if it already exists, if not, create one */ if(!s) { #ifdef UNBOUND_DEBUG struct rbnode_t* n; #endif s = mesh_state_create(mesh->env, qinfo, qflags&BIT_RD, 0); if(!s) { log_err("mesh_state_create: out of memory; SERVFAIL"); error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, qid, qflags, edns); comm_point_send_reply(rep); return; } #ifdef UNBOUND_DEBUG n = #endif rbtree_insert(&mesh->all, &s->node); log_assert(n != NULL); /* set detached (it is now) */ mesh->num_detached_states++; added = 1; } if(!s->reply_list && !s->cb_list && s->super_set.count == 0) was_detached = 1; if(!s->reply_list && !s->cb_list) was_noreply = 1; /* add reply to s */ if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) { log_err("mesh_new_client: out of memory; SERVFAIL"); error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, qid, qflags, edns); comm_point_send_reply(rep); if(added) mesh_state_delete(&s->s); return; } /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); mesh->num_detached_states--; } if(was_noreply) { mesh->num_reply_states ++; } mesh->num_reply_addrs++; if(s->list_select == mesh_no_list) { /* move to either the forever or the jostle_list */ if(mesh->num_forever_states < mesh->max_forever_states) { mesh->num_forever_states ++; mesh_list_insert(s, &mesh->forever_first, &mesh->forever_last); s->list_select = mesh_forever_list; } else { mesh_list_insert(s, &mesh->jostle_first, &mesh->jostle_last); s->list_select = mesh_jostle_list; } } if(added) mesh_run(mesh, s, module_event_new, NULL); }