1933707f3Ssthen /* 2933707f3Ssthen * services/mesh.c - deal with mesh of query states and handle events for that. 3933707f3Ssthen * 4933707f3Ssthen * Copyright (c) 2007, NLnet Labs. All rights reserved. 5933707f3Ssthen * 6933707f3Ssthen * This software is open source. 7933707f3Ssthen * 8933707f3Ssthen * Redistribution and use in source and binary forms, with or without 9933707f3Ssthen * modification, are permitted provided that the following conditions 10933707f3Ssthen * are met: 11933707f3Ssthen * 12933707f3Ssthen * Redistributions of source code must retain the above copyright notice, 13933707f3Ssthen * this list of conditions and the following disclaimer. 14933707f3Ssthen * 15933707f3Ssthen * Redistributions in binary form must reproduce the above copyright notice, 16933707f3Ssthen * this list of conditions and the following disclaimer in the documentation 17933707f3Ssthen * and/or other materials provided with the distribution. 18933707f3Ssthen * 19933707f3Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may 20933707f3Ssthen * be used to endorse or promote products derived from this software without 21933707f3Ssthen * specific prior written permission. 22933707f3Ssthen * 23933707f3Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 245d76a658Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 255d76a658Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 265d76a658Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 275d76a658Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 285d76a658Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 295d76a658Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 305d76a658Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 315d76a658Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 325d76a658Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 335d76a658Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34933707f3Ssthen */ 35933707f3Ssthen 36933707f3Ssthen /** 37933707f3Ssthen * \file 38933707f3Ssthen * 39933707f3Ssthen * This file contains functions to assist in dealing with a mesh of 40933707f3Ssthen * query states. This mesh is supposed to be thread-specific. 41933707f3Ssthen * It consists of query states (per qname, qtype, qclass) and connections 42933707f3Ssthen * between query states and the super and subquery states, and replies to 43933707f3Ssthen * send back to clients. 44933707f3Ssthen */ 45933707f3Ssthen #include "config.h" 46933707f3Ssthen #include "services/mesh.h" 47933707f3Ssthen #include "services/outbound_list.h" 48933707f3Ssthen #include "services/cache/dns.h" 49eaf2578eSsthen #include "services/cache/rrset.h" 502bdc0ed1Ssthen #include "services/cache/infra.h" 51933707f3Ssthen #include "util/log.h" 52933707f3Ssthen #include "util/net_help.h" 53933707f3Ssthen #include "util/module.h" 54933707f3Ssthen #include "util/regional.h" 55933707f3Ssthen #include "util/data/msgencode.h" 56933707f3Ssthen #include "util/timehist.h" 57933707f3Ssthen #include "util/fptr_wlist.h" 58933707f3Ssthen #include "util/alloc.h" 59933707f3Ssthen #include "util/config_file.h" 602308e98cSsthen #include "util/edns.h" 61a58bff56Ssthen #include "sldns/sbuffer.h" 6277079be7Ssthen #include "sldns/wire2str.h" 6377079be7Ssthen #include "services/localzone.h" 6477079be7Ssthen #include "util/data/dname.h" 652be9e038Ssthen #include "respip/respip.h" 66f6b99bafSsthen #include "services/listen_dnsport.h" 678b7325afSsthen #include "util/timeval_func.h" 68933707f3Ssthen 690bdb4f62Ssthen #ifdef CLIENT_SUBNET 700bdb4f62Ssthen #include "edns-subnet/subnetmod.h" 710bdb4f62Ssthen #include "edns-subnet/edns-subnet.h" 720bdb4f62Ssthen #endif 73f46c52bfSsthen #ifdef HAVE_SYS_TYPES_H 74f46c52bfSsthen # include <sys/types.h> 75f46c52bfSsthen #endif 76f46c52bfSsthen #ifdef HAVE_NETDB_H 77f46c52bfSsthen #include <netdb.h> 78f46c52bfSsthen #endif 790bdb4f62Ssthen 80eaf2578eSsthen /** 812be9e038Ssthen * Compare two response-ip client info entries for the purpose of mesh state 822be9e038Ssthen * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise 832be9e038Ssthen * 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but 842be9e038Ssthen * in practice it should be only used to mean they are different). 852be9e038Ssthen * We cannot share the mesh state for two queries if different response-ip 862be9e038Ssthen * actions can apply in the end, even if those queries are otherwise identical. 872be9e038Ssthen * For this purpose we compare tag lists and tag action lists; they should be 882be9e038Ssthen * identical to share the same state. 892be9e038Ssthen * For tag data, we don't look into the data content, as it can be 902be9e038Ssthen * expensive; unless tag data are not defined for both or they point to the 912be9e038Ssthen * exact same data in memory (i.e., they come from the same ACL entry), we 922be9e038Ssthen * consider these data different. 932be9e038Ssthen * Likewise, if the client info is associated with views, we don't look into 942be9e038Ssthen * the views. They are considered different unless they are exactly the same 952be9e038Ssthen * even if the views only differ in the names. 962be9e038Ssthen */ 972be9e038Ssthen static int 982be9e038Ssthen client_info_compare(const struct respip_client_info* ci_a, 992be9e038Ssthen const struct respip_client_info* ci_b) 1002be9e038Ssthen { 1012be9e038Ssthen int cmp; 1022be9e038Ssthen 1032be9e038Ssthen if(!ci_a && !ci_b) 1042be9e038Ssthen return 0; 1052be9e038Ssthen if(ci_a && !ci_b) 1062be9e038Ssthen return -1; 1072be9e038Ssthen if(!ci_a && ci_b) 1082be9e038Ssthen return 1; 1092be9e038Ssthen if(ci_a->taglen != ci_b->taglen) 1102be9e038Ssthen return (ci_a->taglen < ci_b->taglen) ? -1 : 1; 111a3167c07Ssthen if(ci_a->taglist && !ci_b->taglist) 112a3167c07Ssthen return -1; 113a3167c07Ssthen if(!ci_a->taglist && ci_b->taglist) 114a3167c07Ssthen return 1; 115a3167c07Ssthen if(ci_a->taglist && ci_b->taglist) { 1162be9e038Ssthen cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen); 1172be9e038Ssthen if(cmp != 0) 1182be9e038Ssthen return cmp; 119a3167c07Ssthen } 1202be9e038Ssthen if(ci_a->tag_actions_size != ci_b->tag_actions_size) 1212be9e038Ssthen return (ci_a->tag_actions_size < ci_b->tag_actions_size) ? 1222be9e038Ssthen -1 : 1; 123a3167c07Ssthen if(ci_a->tag_actions && !ci_b->tag_actions) 124a3167c07Ssthen return -1; 125a3167c07Ssthen if(!ci_a->tag_actions && ci_b->tag_actions) 126a3167c07Ssthen return 1; 127a3167c07Ssthen if(ci_a->tag_actions && ci_b->tag_actions) { 1282be9e038Ssthen cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions, 1292be9e038Ssthen ci_a->tag_actions_size); 1302be9e038Ssthen if(cmp != 0) 1312be9e038Ssthen return cmp; 132a3167c07Ssthen } 1332be9e038Ssthen if(ci_a->tag_datas != ci_b->tag_datas) 1342be9e038Ssthen return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; 1352be9e038Ssthen if(ci_a->view != ci_b->view) 1362be9e038Ssthen return ci_a->view < ci_b->view ? -1 : 1; 1372be9e038Ssthen /* For the unbound daemon these should be non-NULL and identical, 1382be9e038Ssthen * but we check that just in case. */ 1392be9e038Ssthen if(ci_a->respip_set != ci_b->respip_set) 1402be9e038Ssthen return ci_a->respip_set < ci_b->respip_set ? -1 : 1; 1412be9e038Ssthen return 0; 1422be9e038Ssthen } 1432be9e038Ssthen 144933707f3Ssthen int 145933707f3Ssthen mesh_state_compare(const void* ap, const void* bp) 146933707f3Ssthen { 147933707f3Ssthen struct mesh_state* a = (struct mesh_state*)ap; 148933707f3Ssthen struct mesh_state* b = (struct mesh_state*)bp; 1492be9e038Ssthen int cmp; 150933707f3Ssthen 15177079be7Ssthen if(a->unique < b->unique) 15277079be7Ssthen return -1; 15377079be7Ssthen if(a->unique > b->unique) 15477079be7Ssthen return 1; 15577079be7Ssthen 156933707f3Ssthen if(a->s.is_priming && !b->s.is_priming) 157933707f3Ssthen return -1; 158933707f3Ssthen if(!a->s.is_priming && b->s.is_priming) 159933707f3Ssthen return 1; 160933707f3Ssthen 16157dceb2aSbrad if(a->s.is_valrec && !b->s.is_valrec) 16257dceb2aSbrad return -1; 16357dceb2aSbrad if(!a->s.is_valrec && b->s.is_valrec) 16457dceb2aSbrad return 1; 16557dceb2aSbrad 166933707f3Ssthen if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD)) 167933707f3Ssthen return -1; 168933707f3Ssthen if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD)) 169933707f3Ssthen return 1; 170933707f3Ssthen 171933707f3Ssthen if((a->s.query_flags&BIT_CD) && !(b->s.query_flags&BIT_CD)) 172933707f3Ssthen return -1; 173933707f3Ssthen if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD)) 174933707f3Ssthen return 1; 175933707f3Ssthen 1762be9e038Ssthen cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo); 1772be9e038Ssthen if(cmp != 0) 1782be9e038Ssthen return cmp; 1792be9e038Ssthen return client_info_compare(a->s.client_info, b->s.client_info); 180933707f3Ssthen } 181933707f3Ssthen 182933707f3Ssthen int 183933707f3Ssthen mesh_state_ref_compare(const void* ap, const void* bp) 184933707f3Ssthen { 185933707f3Ssthen struct mesh_state_ref* a = (struct mesh_state_ref*)ap; 186933707f3Ssthen struct mesh_state_ref* b = (struct mesh_state_ref*)bp; 187933707f3Ssthen return mesh_state_compare(a->s, b->s); 188933707f3Ssthen } 189933707f3Ssthen 190933707f3Ssthen struct mesh_area* 191933707f3Ssthen mesh_create(struct module_stack* stack, struct module_env* env) 192933707f3Ssthen { 193933707f3Ssthen struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area)); 194933707f3Ssthen if(!mesh) { 195933707f3Ssthen log_err("mesh area alloc: out of memory"); 196933707f3Ssthen return NULL; 197933707f3Ssthen } 198933707f3Ssthen mesh->histogram = timehist_setup(); 1995d76a658Ssthen mesh->qbuf_bak = sldns_buffer_new(env->cfg->msg_buffer_size); 200933707f3Ssthen if(!mesh->histogram || !mesh->qbuf_bak) { 201933707f3Ssthen free(mesh); 202933707f3Ssthen log_err("mesh area alloc: out of memory"); 203933707f3Ssthen return NULL; 204933707f3Ssthen } 205933707f3Ssthen mesh->mods = *stack; 206933707f3Ssthen mesh->env = env; 207933707f3Ssthen rbtree_init(&mesh->run, &mesh_state_compare); 208933707f3Ssthen rbtree_init(&mesh->all, &mesh_state_compare); 209933707f3Ssthen mesh->num_reply_addrs = 0; 210933707f3Ssthen mesh->num_reply_states = 0; 211933707f3Ssthen mesh->num_detached_states = 0; 212933707f3Ssthen mesh->num_forever_states = 0; 213933707f3Ssthen mesh->stats_jostled = 0; 214933707f3Ssthen mesh->stats_dropped = 0; 215eaf2578eSsthen mesh->ans_expired = 0; 2168b7325afSsthen mesh->ans_cachedb = 0; 217933707f3Ssthen mesh->max_reply_states = env->cfg->num_queries_per_thread; 218933707f3Ssthen mesh->max_forever_states = (mesh->max_reply_states+1)/2; 219933707f3Ssthen #ifndef S_SPLINT_S 220933707f3Ssthen mesh->jostle_max.tv_sec = (time_t)(env->cfg->jostle_time / 1000); 221933707f3Ssthen mesh->jostle_max.tv_usec = (time_t)((env->cfg->jostle_time % 1000) 222933707f3Ssthen *1000); 223933707f3Ssthen #endif 224933707f3Ssthen return mesh; 225933707f3Ssthen } 226933707f3Ssthen 227933707f3Ssthen /** help mesh delete delete mesh states */ 228933707f3Ssthen static void 22977079be7Ssthen mesh_delete_helper(rbnode_type* n) 230933707f3Ssthen { 231933707f3Ssthen struct mesh_state* mstate = (struct mesh_state*)n->key; 232933707f3Ssthen /* perform a full delete, not only 'cleanup' routine, 233933707f3Ssthen * because other callbacks expect a clean state in the mesh. 234933707f3Ssthen * For 're-entrant' calls */ 235933707f3Ssthen mesh_state_delete(&mstate->s); 236933707f3Ssthen /* but because these delete the items from the tree, postorder 237933707f3Ssthen * traversal and rbtree rebalancing do not work together */ 238933707f3Ssthen } 239933707f3Ssthen 240933707f3Ssthen void 241933707f3Ssthen mesh_delete(struct mesh_area* mesh) 242933707f3Ssthen { 243933707f3Ssthen if(!mesh) 244933707f3Ssthen return; 245933707f3Ssthen /* free all query states */ 246933707f3Ssthen while(mesh->all.count) 247933707f3Ssthen mesh_delete_helper(mesh->all.root); 248933707f3Ssthen timehist_delete(mesh->histogram); 2495d76a658Ssthen sldns_buffer_free(mesh->qbuf_bak); 250933707f3Ssthen free(mesh); 251933707f3Ssthen } 252933707f3Ssthen 253933707f3Ssthen void 254933707f3Ssthen mesh_delete_all(struct mesh_area* mesh) 255933707f3Ssthen { 256933707f3Ssthen /* free all query states */ 257933707f3Ssthen while(mesh->all.count) 258933707f3Ssthen mesh_delete_helper(mesh->all.root); 259933707f3Ssthen mesh->stats_dropped += mesh->num_reply_addrs; 260933707f3Ssthen /* clear mesh area references */ 261933707f3Ssthen rbtree_init(&mesh->run, &mesh_state_compare); 262933707f3Ssthen rbtree_init(&mesh->all, &mesh_state_compare); 263933707f3Ssthen mesh->num_reply_addrs = 0; 264933707f3Ssthen mesh->num_reply_states = 0; 265933707f3Ssthen mesh->num_detached_states = 0; 266933707f3Ssthen mesh->num_forever_states = 0; 267933707f3Ssthen mesh->forever_first = NULL; 268933707f3Ssthen mesh->forever_last = NULL; 269933707f3Ssthen mesh->jostle_first = NULL; 270933707f3Ssthen mesh->jostle_last = NULL; 271933707f3Ssthen } 272933707f3Ssthen 2735d76a658Ssthen int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf) 274933707f3Ssthen { 275933707f3Ssthen struct mesh_state* m = mesh->jostle_first; 276933707f3Ssthen /* free space is available */ 277933707f3Ssthen if(mesh->num_reply_states < mesh->max_reply_states) 278933707f3Ssthen return 1; 279933707f3Ssthen /* try to kick out a jostle-list item */ 280933707f3Ssthen if(m && m->reply_list && m->list_select == mesh_jostle_list) { 281933707f3Ssthen /* how old is it? */ 282933707f3Ssthen struct timeval age; 283933707f3Ssthen timeval_subtract(&age, mesh->env->now_tv, 284933707f3Ssthen &m->reply_list->start_time); 285933707f3Ssthen if(timeval_smaller(&mesh->jostle_max, &age)) { 286933707f3Ssthen /* its a goner */ 287933707f3Ssthen log_nametypeclass(VERB_ALGO, "query jostled out to " 288933707f3Ssthen "make space for a new one", 289933707f3Ssthen m->s.qinfo.qname, m->s.qinfo.qtype, 290933707f3Ssthen m->s.qinfo.qclass); 291933707f3Ssthen /* backup the query */ 2925d76a658Ssthen if(qbuf) sldns_buffer_copy(mesh->qbuf_bak, qbuf); 293933707f3Ssthen /* notify supers */ 294933707f3Ssthen if(m->super_set.count > 0) { 295933707f3Ssthen verbose(VERB_ALGO, "notify supers of failure"); 296933707f3Ssthen m->s.return_msg = NULL; 297933707f3Ssthen m->s.return_rcode = LDNS_RCODE_SERVFAIL; 298933707f3Ssthen mesh_walk_supers(mesh, m); 299933707f3Ssthen } 300933707f3Ssthen mesh->stats_jostled ++; 301933707f3Ssthen mesh_state_delete(&m->s); 302933707f3Ssthen /* restore the query - note that the qinfo ptr to 303933707f3Ssthen * the querybuffer is then correct again. */ 3045d76a658Ssthen if(qbuf) sldns_buffer_copy(qbuf, mesh->qbuf_bak); 305933707f3Ssthen return 1; 306933707f3Ssthen } 307933707f3Ssthen } 308933707f3Ssthen /* no space for new item */ 309933707f3Ssthen return 0; 310933707f3Ssthen } 311933707f3Ssthen 312eaf2578eSsthen struct dns_msg* 313eaf2578eSsthen mesh_serve_expired_lookup(struct module_qstate* qstate, 314eaf2578eSsthen struct query_info* lookup_qinfo) 315eaf2578eSsthen { 316eaf2578eSsthen hashvalue_type h; 317eaf2578eSsthen struct lruhash_entry* e; 318eaf2578eSsthen struct dns_msg* msg; 319eaf2578eSsthen struct reply_info* data; 320eaf2578eSsthen struct msgreply_entry* key; 321eaf2578eSsthen time_t timenow = *qstate->env->now; 322eaf2578eSsthen int must_validate = (!(qstate->query_flags&BIT_CD) 323eaf2578eSsthen || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; 324eaf2578eSsthen /* Lookup cache */ 325eaf2578eSsthen h = query_info_hash(lookup_qinfo, qstate->query_flags); 326eaf2578eSsthen e = slabhash_lookup(qstate->env->msg_cache, h, lookup_qinfo, 0); 327eaf2578eSsthen if(!e) return NULL; 328eaf2578eSsthen 329eaf2578eSsthen key = (struct msgreply_entry*)e->key; 330eaf2578eSsthen data = (struct reply_info*)e->data; 331eaf2578eSsthen msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow, 332eaf2578eSsthen qstate->env->cfg->serve_expired, qstate->env->scratch); 333eaf2578eSsthen if(!msg) 334eaf2578eSsthen goto bail_out; 335eaf2578eSsthen 336eaf2578eSsthen /* Check CNAME chain (if any) 337eaf2578eSsthen * This is part of tomsg above; no need to check now. */ 338eaf2578eSsthen 339eaf2578eSsthen /* Check security status of the cached answer. 340eaf2578eSsthen * tomsg above has a subset of these checks, so we are leaving 341eaf2578eSsthen * these as is. 342eaf2578eSsthen * In case of bogus or revalidation we don't care to reply here. */ 343eaf2578eSsthen if(must_validate && (msg->rep->security == sec_status_bogus || 344eaf2578eSsthen msg->rep->security == sec_status_secure_sentinel_fail)) { 345eaf2578eSsthen verbose(VERB_ALGO, "Serve expired: bogus answer found in cache"); 346eaf2578eSsthen goto bail_out; 347eaf2578eSsthen } else if(msg->rep->security == sec_status_unchecked && must_validate) { 348eaf2578eSsthen verbose(VERB_ALGO, "Serve expired: unchecked entry needs " 349eaf2578eSsthen "validation"); 350eaf2578eSsthen goto bail_out; /* need to validate cache entry first */ 351eaf2578eSsthen } else if(msg->rep->security == sec_status_secure && 352eaf2578eSsthen !reply_all_rrsets_secure(msg->rep) && must_validate) { 353eaf2578eSsthen verbose(VERB_ALGO, "Serve expired: secure entry" 354eaf2578eSsthen " changed status"); 355eaf2578eSsthen goto bail_out; /* rrset changed, re-verify */ 356eaf2578eSsthen } 357eaf2578eSsthen 358eaf2578eSsthen lock_rw_unlock(&e->lock); 359eaf2578eSsthen return msg; 360eaf2578eSsthen 361eaf2578eSsthen bail_out: 362eaf2578eSsthen lock_rw_unlock(&e->lock); 363eaf2578eSsthen return NULL; 364eaf2578eSsthen } 365eaf2578eSsthen 366eaf2578eSsthen 367eaf2578eSsthen /** Init the serve expired data structure */ 368eaf2578eSsthen static int 369eaf2578eSsthen mesh_serve_expired_init(struct mesh_state* mstate, int timeout) 370eaf2578eSsthen { 371eaf2578eSsthen struct timeval t; 372eaf2578eSsthen 373eaf2578eSsthen /* Create serve_expired_data if not there yet */ 374eaf2578eSsthen if(!mstate->s.serve_expired_data) { 375eaf2578eSsthen mstate->s.serve_expired_data = (struct serve_expired_data*) 376eaf2578eSsthen regional_alloc_zero( 377eaf2578eSsthen mstate->s.region, sizeof(struct serve_expired_data)); 378eaf2578eSsthen if(!mstate->s.serve_expired_data) 379eaf2578eSsthen return 0; 380eaf2578eSsthen } 381eaf2578eSsthen 382eaf2578eSsthen /* Don't overwrite the function if already set */ 383eaf2578eSsthen mstate->s.serve_expired_data->get_cached_answer = 384eaf2578eSsthen mstate->s.serve_expired_data->get_cached_answer? 385eaf2578eSsthen mstate->s.serve_expired_data->get_cached_answer: 386191f22c6Ssthen &mesh_serve_expired_lookup; 387eaf2578eSsthen 388eaf2578eSsthen /* In case this timer already popped, start it again */ 3892bdc0ed1Ssthen if(!mstate->s.serve_expired_data->timer && timeout != -1) { 390eaf2578eSsthen mstate->s.serve_expired_data->timer = comm_timer_create( 391eaf2578eSsthen mstate->s.env->worker_base, mesh_serve_expired_callback, mstate); 392eaf2578eSsthen if(!mstate->s.serve_expired_data->timer) 393eaf2578eSsthen return 0; 394eaf2578eSsthen #ifndef S_SPLINT_S 395eaf2578eSsthen t.tv_sec = timeout/1000; 396eaf2578eSsthen t.tv_usec = (timeout%1000)*1000; 397eaf2578eSsthen #endif 398eaf2578eSsthen comm_timer_set(mstate->s.serve_expired_data->timer, &t); 399eaf2578eSsthen } 400eaf2578eSsthen return 1; 401eaf2578eSsthen } 402eaf2578eSsthen 403933707f3Ssthen void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, 4042be9e038Ssthen struct respip_client_info* cinfo, uint16_t qflags, 4050bdb4f62Ssthen struct edns_data* edns, struct comm_reply* rep, uint16_t qid, 4060bdb4f62Ssthen int rpz_passthru) 407933707f3Ssthen { 40877079be7Ssthen struct mesh_state* s = NULL; 409e21c60efSsthen int unique = unique_mesh_state(edns->opt_list_in, mesh->env); 410933707f3Ssthen int was_detached = 0; 411933707f3Ssthen int was_noreply = 0; 412933707f3Ssthen int added = 0; 413eaf2578eSsthen int timeout = mesh->env->cfg->serve_expired? 414eaf2578eSsthen mesh->env->cfg->serve_expired_client_timeout:0; 415550cf4a9Ssthen struct sldns_buffer* r_buffer = rep->c->buffer; 416*98bc733bSsthen uint16_t mesh_flags = qflags&(BIT_RD|BIT_CD); 417550cf4a9Ssthen if(rep->c->tcp_req_info) { 418550cf4a9Ssthen r_buffer = rep->c->tcp_req_info->spool_buffer; 419550cf4a9Ssthen } 4202bdc0ed1Ssthen if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, 4212bdc0ed1Ssthen edns->cookie_valid, mesh->env->cfg)) { 4222bdc0ed1Ssthen verbose(VERB_ALGO, "Too many queries waiting from the IP. " 4232bdc0ed1Ssthen "dropping incoming query."); 4242bdc0ed1Ssthen comm_point_drop_reply(rep); 4252bdc0ed1Ssthen mesh->stats_dropped++; 4262bdc0ed1Ssthen return; 4272bdc0ed1Ssthen } 42877079be7Ssthen if(!unique) 429*98bc733bSsthen s = mesh_area_find(mesh, cinfo, qinfo, mesh_flags, 0, 0); 430933707f3Ssthen /* does this create a new reply state? */ 431933707f3Ssthen if(!s || s->list_select == mesh_no_list) { 432933707f3Ssthen if(!mesh_make_new_space(mesh, rep->c->buffer)) { 433933707f3Ssthen verbose(VERB_ALGO, "Too many queries. dropping " 434933707f3Ssthen "incoming query."); 435933707f3Ssthen comm_point_drop_reply(rep); 436933707f3Ssthen mesh->stats_dropped++; 437933707f3Ssthen return; 438933707f3Ssthen } 439933707f3Ssthen /* for this new reply state, the reply address is free, 440933707f3Ssthen * so the limit of reply addresses does not stop reply states*/ 441933707f3Ssthen } else { 442933707f3Ssthen /* protect our memory usage from storing reply addresses */ 443933707f3Ssthen if(mesh->num_reply_addrs > mesh->max_reply_states*16) { 444933707f3Ssthen verbose(VERB_ALGO, "Too many requests queued. " 445933707f3Ssthen "dropping incoming query."); 446933707f3Ssthen comm_point_drop_reply(rep); 447eaf2578eSsthen mesh->stats_dropped++; 448933707f3Ssthen return; 449933707f3Ssthen } 450933707f3Ssthen } 451933707f3Ssthen /* see if it already exists, if not, create one */ 452933707f3Ssthen if(!s) { 453933707f3Ssthen #ifdef UNBOUND_DEBUG 45477079be7Ssthen struct rbnode_type* n; 455933707f3Ssthen #endif 4562be9e038Ssthen s = mesh_state_create(mesh->env, qinfo, cinfo, 457*98bc733bSsthen mesh_flags, 0, 0); 458933707f3Ssthen if(!s) { 459933707f3Ssthen log_err("mesh_state_create: out of memory; SERVFAIL"); 46077079be7Ssthen if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, 4619982a05dSsthen LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv)) 462e21c60efSsthen edns->opt_list_inplace_cb_out = NULL; 463550cf4a9Ssthen error_encode(r_buffer, LDNS_RCODE_SERVFAIL, 464933707f3Ssthen qinfo, qid, qflags, edns); 465933707f3Ssthen comm_point_send_reply(rep); 466933707f3Ssthen return; 467933707f3Ssthen } 4688b7325afSsthen /* set detached (it is now) */ 4698b7325afSsthen mesh->num_detached_states++; 47077079be7Ssthen if(unique) 47177079be7Ssthen mesh_state_make_unique(s); 4720bdb4f62Ssthen s->s.rpz_passthru = rpz_passthru; 47377079be7Ssthen /* copy the edns options we got from the front */ 474e21c60efSsthen if(edns->opt_list_in) { 475e21c60efSsthen s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, 47677079be7Ssthen s->s.region); 47777079be7Ssthen if(!s->s.edns_opts_front_in) { 4788b7325afSsthen log_err("edns_opt_copy_region: out of memory; SERVFAIL"); 47977079be7Ssthen if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, 4809982a05dSsthen NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv)) 481e21c60efSsthen edns->opt_list_inplace_cb_out = NULL; 482550cf4a9Ssthen error_encode(r_buffer, LDNS_RCODE_SERVFAIL, 48377079be7Ssthen qinfo, qid, qflags, edns); 48477079be7Ssthen comm_point_send_reply(rep); 4858b7325afSsthen mesh_state_delete(&s->s); 48677079be7Ssthen return; 48777079be7Ssthen } 48877079be7Ssthen } 48977079be7Ssthen 490933707f3Ssthen #ifdef UNBOUND_DEBUG 491933707f3Ssthen n = 492229e174cSsthen #else 493229e174cSsthen (void) 494933707f3Ssthen #endif 495933707f3Ssthen rbtree_insert(&mesh->all, &s->node); 496933707f3Ssthen log_assert(n != NULL); 497933707f3Ssthen added = 1; 498933707f3Ssthen } 499eaf2578eSsthen if(!s->reply_list && !s->cb_list) { 500933707f3Ssthen was_noreply = 1; 501eaf2578eSsthen if(s->super_set.count == 0) { 502eaf2578eSsthen was_detached = 1; 503eaf2578eSsthen } 504eaf2578eSsthen } 505933707f3Ssthen /* add reply to s */ 50677079be7Ssthen if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) { 507933707f3Ssthen log_err("mesh_new_client: out of memory; SERVFAIL"); 508eaf2578eSsthen goto servfail_mem; 509933707f3Ssthen } 510f6b99bafSsthen if(rep->c->tcp_req_info) { 511f6b99bafSsthen if(!tcp_req_info_add_meshstate(rep->c->tcp_req_info, mesh, s)) { 512f6b99bafSsthen log_err("mesh_new_client: out of memory add tcpreqinfo"); 513f6b99bafSsthen goto servfail_mem; 514f6b99bafSsthen } 515f6b99bafSsthen } 5162c144df0Ssthen if(rep->c->use_h2) { 5172c144df0Ssthen http2_stream_add_meshstate(rep->c->h2_stream, mesh, s); 5182c144df0Ssthen } 519eaf2578eSsthen /* add serve expired timer if required and not already there */ 520eaf2578eSsthen if(timeout && !mesh_serve_expired_init(s, timeout)) { 521eaf2578eSsthen log_err("mesh_new_client: out of memory initializing serve expired"); 522eaf2578eSsthen goto servfail_mem; 523eaf2578eSsthen } 5242bdc0ed1Ssthen #ifdef USE_CACHEDB 5252bdc0ed1Ssthen if(!timeout && mesh->env->cfg->serve_expired && 5262bdc0ed1Ssthen !mesh->env->cfg->serve_expired_client_timeout && 5272bdc0ed1Ssthen (mesh->env->cachedb_enabled && 5282bdc0ed1Ssthen mesh->env->cfg->cachedb_check_when_serve_expired)) { 5292bdc0ed1Ssthen if(!mesh_serve_expired_init(s, -1)) { 5302bdc0ed1Ssthen log_err("mesh_new_client: out of memory initializing serve expired"); 5312bdc0ed1Ssthen goto servfail_mem; 5322bdc0ed1Ssthen } 5332bdc0ed1Ssthen } 5342bdc0ed1Ssthen #endif 5352bdc0ed1Ssthen infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now, 5362bdc0ed1Ssthen mesh->env->cfg); 537933707f3Ssthen /* update statistics */ 538933707f3Ssthen if(was_detached) { 539933707f3Ssthen log_assert(mesh->num_detached_states > 0); 540933707f3Ssthen mesh->num_detached_states--; 541933707f3Ssthen } 542933707f3Ssthen if(was_noreply) { 543933707f3Ssthen mesh->num_reply_states ++; 544933707f3Ssthen } 545933707f3Ssthen mesh->num_reply_addrs++; 546933707f3Ssthen if(s->list_select == mesh_no_list) { 547933707f3Ssthen /* move to either the forever or the jostle_list */ 548933707f3Ssthen if(mesh->num_forever_states < mesh->max_forever_states) { 549933707f3Ssthen mesh->num_forever_states ++; 550933707f3Ssthen mesh_list_insert(s, &mesh->forever_first, 551933707f3Ssthen &mesh->forever_last); 552933707f3Ssthen s->list_select = mesh_forever_list; 553933707f3Ssthen } else { 554933707f3Ssthen mesh_list_insert(s, &mesh->jostle_first, 555933707f3Ssthen &mesh->jostle_last); 556933707f3Ssthen s->list_select = mesh_jostle_list; 557933707f3Ssthen } 558933707f3Ssthen } 559933707f3Ssthen if(added) 560933707f3Ssthen mesh_run(mesh, s, module_event_new, NULL); 561eaf2578eSsthen return; 562eaf2578eSsthen 563eaf2578eSsthen servfail_mem: 564eaf2578eSsthen if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s, 5659982a05dSsthen NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv)) 566e21c60efSsthen edns->opt_list_inplace_cb_out = NULL; 567eaf2578eSsthen error_encode(r_buffer, LDNS_RCODE_SERVFAIL, 568eaf2578eSsthen qinfo, qid, qflags, edns); 569*98bc733bSsthen if(rep->c->use_h2) 570*98bc733bSsthen http2_stream_remove_mesh_state(rep->c->h2_stream); 571eaf2578eSsthen comm_point_send_reply(rep); 572eaf2578eSsthen if(added) 573eaf2578eSsthen mesh_state_delete(&s->s); 574eaf2578eSsthen return; 575933707f3Ssthen } 576933707f3Ssthen 577933707f3Ssthen int 578933707f3Ssthen mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, 5795d76a658Ssthen uint16_t qflags, struct edns_data* edns, sldns_buffer* buf, 5800bdb4f62Ssthen uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru) 581933707f3Ssthen { 58277079be7Ssthen struct mesh_state* s = NULL; 583e21c60efSsthen int unique = unique_mesh_state(edns->opt_list_in, mesh->env); 584eaf2578eSsthen int timeout = mesh->env->cfg->serve_expired? 585eaf2578eSsthen mesh->env->cfg->serve_expired_client_timeout:0; 586933707f3Ssthen int was_detached = 0; 587933707f3Ssthen int was_noreply = 0; 588933707f3Ssthen int added = 0; 589*98bc733bSsthen uint16_t mesh_flags = qflags&(BIT_RD|BIT_CD); 59077079be7Ssthen if(!unique) 591*98bc733bSsthen s = mesh_area_find(mesh, NULL, qinfo, mesh_flags, 0, 0); 5922be9e038Ssthen 593933707f3Ssthen /* there are no limits on the number of callbacks */ 594933707f3Ssthen 595933707f3Ssthen /* see if it already exists, if not, create one */ 596933707f3Ssthen if(!s) { 597933707f3Ssthen #ifdef UNBOUND_DEBUG 59877079be7Ssthen struct rbnode_type* n; 599933707f3Ssthen #endif 6002be9e038Ssthen s = mesh_state_create(mesh->env, qinfo, NULL, 601*98bc733bSsthen mesh_flags, 0, 0); 602933707f3Ssthen if(!s) { 603933707f3Ssthen return 0; 604933707f3Ssthen } 6058b7325afSsthen /* set detached (it is now) */ 6068b7325afSsthen mesh->num_detached_states++; 60777079be7Ssthen if(unique) 60877079be7Ssthen mesh_state_make_unique(s); 6090bdb4f62Ssthen s->s.rpz_passthru = rpz_passthru; 610e21c60efSsthen if(edns->opt_list_in) { 611e21c60efSsthen s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, 61277079be7Ssthen s->s.region); 61377079be7Ssthen if(!s->s.edns_opts_front_in) { 6148b7325afSsthen mesh_state_delete(&s->s); 61577079be7Ssthen return 0; 61677079be7Ssthen } 61777079be7Ssthen } 618933707f3Ssthen #ifdef UNBOUND_DEBUG 619933707f3Ssthen n = 620229e174cSsthen #else 621229e174cSsthen (void) 622933707f3Ssthen #endif 623933707f3Ssthen rbtree_insert(&mesh->all, &s->node); 624933707f3Ssthen log_assert(n != NULL); 625933707f3Ssthen added = 1; 626933707f3Ssthen } 627eaf2578eSsthen if(!s->reply_list && !s->cb_list) { 628933707f3Ssthen was_noreply = 1; 629eaf2578eSsthen if(s->super_set.count == 0) { 630eaf2578eSsthen was_detached = 1; 631eaf2578eSsthen } 632eaf2578eSsthen } 633933707f3Ssthen /* add reply to s */ 634933707f3Ssthen if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) { 635933707f3Ssthen if(added) 636933707f3Ssthen mesh_state_delete(&s->s); 637933707f3Ssthen return 0; 638933707f3Ssthen } 639eaf2578eSsthen /* add serve expired timer if not already there */ 640eaf2578eSsthen if(timeout && !mesh_serve_expired_init(s, timeout)) { 6418b7325afSsthen if(added) 6428b7325afSsthen mesh_state_delete(&s->s); 643eaf2578eSsthen return 0; 644eaf2578eSsthen } 6452bdc0ed1Ssthen #ifdef USE_CACHEDB 6462bdc0ed1Ssthen if(!timeout && mesh->env->cfg->serve_expired && 6472bdc0ed1Ssthen !mesh->env->cfg->serve_expired_client_timeout && 6482bdc0ed1Ssthen (mesh->env->cachedb_enabled && 6492bdc0ed1Ssthen mesh->env->cfg->cachedb_check_when_serve_expired)) { 6502bdc0ed1Ssthen if(!mesh_serve_expired_init(s, -1)) { 6512bdc0ed1Ssthen if(added) 6522bdc0ed1Ssthen mesh_state_delete(&s->s); 6532bdc0ed1Ssthen return 0; 6542bdc0ed1Ssthen } 6552bdc0ed1Ssthen } 6562bdc0ed1Ssthen #endif 657933707f3Ssthen /* update statistics */ 658933707f3Ssthen if(was_detached) { 659933707f3Ssthen log_assert(mesh->num_detached_states > 0); 660933707f3Ssthen mesh->num_detached_states--; 661933707f3Ssthen } 662933707f3Ssthen if(was_noreply) { 663933707f3Ssthen mesh->num_reply_states ++; 664933707f3Ssthen } 665933707f3Ssthen mesh->num_reply_addrs++; 666933707f3Ssthen if(added) 667933707f3Ssthen mesh_run(mesh, s, module_event_new, NULL); 668933707f3Ssthen return 1; 669933707f3Ssthen } 670933707f3Ssthen 671bdfc4d55Sflorian /* Internal backend routine of mesh_new_prefetch(). It takes one additional 672bdfc4d55Sflorian * parameter, 'run', which controls whether to run the prefetch state 673bdfc4d55Sflorian * immediately. When this function is called internally 'run' could be 674bdfc4d55Sflorian * 0 (false), in which case the new state is only made runnable so it 675bdfc4d55Sflorian * will not be run recursively on top of the current state. */ 676bdfc4d55Sflorian static void mesh_schedule_prefetch(struct mesh_area* mesh, 6770bdb4f62Ssthen struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, 6780bdb4f62Ssthen int rpz_passthru) 679bdfc4d55Sflorian { 680*98bc733bSsthen /* Explicitly set the BIT_RD regardless of the client's flags. This is 681*98bc733bSsthen * for a prefetch query (no client attached) but it needs to be treated 682*98bc733bSsthen * as a recursion query. */ 683*98bc733bSsthen uint16_t mesh_flags = BIT_RD|(qflags&BIT_CD); 6842be9e038Ssthen struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, 685*98bc733bSsthen mesh_flags, 0, 0); 686933707f3Ssthen #ifdef UNBOUND_DEBUG 68777079be7Ssthen struct rbnode_type* n; 688933707f3Ssthen #endif 689933707f3Ssthen /* already exists, and for a different purpose perhaps. 690933707f3Ssthen * if mesh_no_list, keep it that way. */ 691933707f3Ssthen if(s) { 692933707f3Ssthen /* make it ignore the cache from now on */ 693933707f3Ssthen if(!s->s.blacklist) 694933707f3Ssthen sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); 695933707f3Ssthen if(s->s.prefetch_leeway < leeway) 696933707f3Ssthen s->s.prefetch_leeway = leeway; 697933707f3Ssthen return; 698933707f3Ssthen } 699933707f3Ssthen if(!mesh_make_new_space(mesh, NULL)) { 700933707f3Ssthen verbose(VERB_ALGO, "Too many queries. dropped prefetch."); 701933707f3Ssthen mesh->stats_dropped ++; 702933707f3Ssthen return; 703933707f3Ssthen } 70477079be7Ssthen 705*98bc733bSsthen s = mesh_state_create(mesh->env, qinfo, NULL, mesh_flags, 0, 0); 706933707f3Ssthen if(!s) { 707933707f3Ssthen log_err("prefetch mesh_state_create: out of memory"); 708933707f3Ssthen return; 709933707f3Ssthen } 710933707f3Ssthen #ifdef UNBOUND_DEBUG 711933707f3Ssthen n = 712229e174cSsthen #else 713229e174cSsthen (void) 714933707f3Ssthen #endif 715933707f3Ssthen rbtree_insert(&mesh->all, &s->node); 716933707f3Ssthen log_assert(n != NULL); 717933707f3Ssthen /* set detached (it is now) */ 718933707f3Ssthen mesh->num_detached_states++; 719933707f3Ssthen /* make it ignore the cache */ 720933707f3Ssthen sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); 721933707f3Ssthen s->s.prefetch_leeway = leeway; 722933707f3Ssthen 723933707f3Ssthen if(s->list_select == mesh_no_list) { 724933707f3Ssthen /* move to either the forever or the jostle_list */ 725933707f3Ssthen if(mesh->num_forever_states < mesh->max_forever_states) { 726933707f3Ssthen mesh->num_forever_states ++; 727933707f3Ssthen mesh_list_insert(s, &mesh->forever_first, 728933707f3Ssthen &mesh->forever_last); 729933707f3Ssthen s->list_select = mesh_forever_list; 730933707f3Ssthen } else { 731933707f3Ssthen mesh_list_insert(s, &mesh->jostle_first, 732933707f3Ssthen &mesh->jostle_last); 733933707f3Ssthen s->list_select = mesh_jostle_list; 734933707f3Ssthen } 735933707f3Ssthen } 7360bdb4f62Ssthen s->s.rpz_passthru = rpz_passthru; 737bdfc4d55Sflorian 738bdfc4d55Sflorian if(!run) { 739bdfc4d55Sflorian #ifdef UNBOUND_DEBUG 740bdfc4d55Sflorian n = 741bdfc4d55Sflorian #else 742bdfc4d55Sflorian (void) 743bdfc4d55Sflorian #endif 744bdfc4d55Sflorian rbtree_insert(&mesh->run, &s->run_node); 745bdfc4d55Sflorian log_assert(n != NULL); 746bdfc4d55Sflorian return; 747bdfc4d55Sflorian } 748bdfc4d55Sflorian 749933707f3Ssthen mesh_run(mesh, s, module_event_new, NULL); 750933707f3Ssthen } 751933707f3Ssthen 7520bdb4f62Ssthen #ifdef CLIENT_SUBNET 7530bdb4f62Ssthen /* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic 7540bdb4f62Ssthen * like passing along the comm_reply info. This will be faked into an EDNS 7550bdb4f62Ssthen * option for processing by the subnet module if the client has not already 7560bdb4f62Ssthen * attached its own ECS data. */ 7570bdb4f62Ssthen static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, 7580bdb4f62Ssthen struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, 7598b7325afSsthen int rpz_passthru, struct sockaddr_storage* addr, struct edns_option* edns_list) 760eaf2578eSsthen { 7610bdb4f62Ssthen struct mesh_state* s = NULL; 7620bdb4f62Ssthen struct edns_option* opt = NULL; 7630bdb4f62Ssthen #ifdef UNBOUND_DEBUG 7640bdb4f62Ssthen struct rbnode_type* n; 7650bdb4f62Ssthen #endif 766*98bc733bSsthen /* Explicitly set the BIT_RD regardless of the client's flags. This is 767*98bc733bSsthen * for a prefetch query (no client attached) but it needs to be treated 768*98bc733bSsthen * as a recursion query. */ 769*98bc733bSsthen uint16_t mesh_flags = BIT_RD|(qflags&BIT_CD); 7700bdb4f62Ssthen if(!mesh_make_new_space(mesh, NULL)) { 7710bdb4f62Ssthen verbose(VERB_ALGO, "Too many queries. dropped prefetch."); 7720bdb4f62Ssthen mesh->stats_dropped ++; 7730bdb4f62Ssthen return; 7740bdb4f62Ssthen } 7750bdb4f62Ssthen 776*98bc733bSsthen s = mesh_state_create(mesh->env, qinfo, NULL, mesh_flags, 0, 0); 7770bdb4f62Ssthen if(!s) { 7780bdb4f62Ssthen log_err("prefetch_subnet mesh_state_create: out of memory"); 7790bdb4f62Ssthen return; 7800bdb4f62Ssthen } 7810bdb4f62Ssthen mesh_state_make_unique(s); 7820bdb4f62Ssthen 7830bdb4f62Ssthen opt = edns_opt_list_find(edns_list, mesh->env->cfg->client_subnet_opcode); 7840bdb4f62Ssthen if(opt) { 7850bdb4f62Ssthen /* Use the client's ECS data */ 7860bdb4f62Ssthen if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code, 7870bdb4f62Ssthen opt->opt_len, opt->opt_data, s->s.region)) { 7880bdb4f62Ssthen log_err("prefetch_subnet edns_opt_list_append: out of memory"); 7890bdb4f62Ssthen return; 7900bdb4f62Ssthen } 7910bdb4f62Ssthen } else { 7928b7325afSsthen /* Store the client's address. Later in the subnet module, 7938b7325afSsthen * it is decided whether to include an ECS option or not. 7948b7325afSsthen */ 7958b7325afSsthen s->s.client_addr = *addr; 7960bdb4f62Ssthen } 7970bdb4f62Ssthen #ifdef UNBOUND_DEBUG 7980bdb4f62Ssthen n = 7990bdb4f62Ssthen #else 8000bdb4f62Ssthen (void) 8010bdb4f62Ssthen #endif 8020bdb4f62Ssthen rbtree_insert(&mesh->all, &s->node); 8030bdb4f62Ssthen log_assert(n != NULL); 8040bdb4f62Ssthen /* set detached (it is now) */ 8050bdb4f62Ssthen mesh->num_detached_states++; 8060bdb4f62Ssthen /* make it ignore the cache */ 8070bdb4f62Ssthen sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); 8080bdb4f62Ssthen s->s.prefetch_leeway = leeway; 8090bdb4f62Ssthen 8100bdb4f62Ssthen if(s->list_select == mesh_no_list) { 8110bdb4f62Ssthen /* move to either the forever or the jostle_list */ 8120bdb4f62Ssthen if(mesh->num_forever_states < mesh->max_forever_states) { 8130bdb4f62Ssthen mesh->num_forever_states ++; 8140bdb4f62Ssthen mesh_list_insert(s, &mesh->forever_first, 8150bdb4f62Ssthen &mesh->forever_last); 8160bdb4f62Ssthen s->list_select = mesh_forever_list; 8170bdb4f62Ssthen } else { 8180bdb4f62Ssthen mesh_list_insert(s, &mesh->jostle_first, 8190bdb4f62Ssthen &mesh->jostle_last); 8200bdb4f62Ssthen s->list_select = mesh_jostle_list; 8210bdb4f62Ssthen } 8220bdb4f62Ssthen } 8230bdb4f62Ssthen s->s.rpz_passthru = rpz_passthru; 8240bdb4f62Ssthen 8250bdb4f62Ssthen if(!run) { 8260bdb4f62Ssthen #ifdef UNBOUND_DEBUG 8270bdb4f62Ssthen n = 8280bdb4f62Ssthen #else 8290bdb4f62Ssthen (void) 8300bdb4f62Ssthen #endif 8310bdb4f62Ssthen rbtree_insert(&mesh->run, &s->run_node); 8320bdb4f62Ssthen log_assert(n != NULL); 8330bdb4f62Ssthen return; 8340bdb4f62Ssthen } 8350bdb4f62Ssthen 8360bdb4f62Ssthen mesh_run(mesh, s, module_event_new, NULL); 8370bdb4f62Ssthen } 8380bdb4f62Ssthen #endif /* CLIENT_SUBNET */ 8390bdb4f62Ssthen 8400bdb4f62Ssthen void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, 8410bdb4f62Ssthen uint16_t qflags, time_t leeway, int rpz_passthru, 8428b7325afSsthen struct sockaddr_storage* addr, struct edns_option* opt_list) 8430bdb4f62Ssthen { 8448b7325afSsthen (void)addr; 8450bdb4f62Ssthen (void)opt_list; 8460bdb4f62Ssthen #ifdef CLIENT_SUBNET 8478b7325afSsthen if(addr) 8480bdb4f62Ssthen mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, 8498b7325afSsthen rpz_passthru, addr, opt_list); 8500bdb4f62Ssthen else 8510bdb4f62Ssthen #endif 8520bdb4f62Ssthen mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, 8530bdb4f62Ssthen rpz_passthru); 854eaf2578eSsthen } 855eaf2578eSsthen 856933707f3Ssthen void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, 857933707f3Ssthen struct comm_reply* reply, int what) 858933707f3Ssthen { 859933707f3Ssthen enum module_ev event = module_event_reply; 860933707f3Ssthen e->qstate->reply = reply; 861933707f3Ssthen if(what != NETEVENT_NOERROR) { 862933707f3Ssthen event = module_event_noreply; 863933707f3Ssthen if(what == NETEVENT_CAPSFAIL) 864933707f3Ssthen event = module_event_capsfail; 865933707f3Ssthen } 866933707f3Ssthen mesh_run(mesh, e->qstate->mesh_info, event, e); 867933707f3Ssthen } 868933707f3Ssthen 869933707f3Ssthen struct mesh_state* 870933707f3Ssthen mesh_state_create(struct module_env* env, struct query_info* qinfo, 8712be9e038Ssthen struct respip_client_info* cinfo, uint16_t qflags, int prime, 8722be9e038Ssthen int valrec) 873933707f3Ssthen { 874933707f3Ssthen struct regional* region = alloc_reg_obtain(env->alloc); 875933707f3Ssthen struct mesh_state* mstate; 876933707f3Ssthen int i; 877933707f3Ssthen if(!region) 878933707f3Ssthen return NULL; 879933707f3Ssthen mstate = (struct mesh_state*)regional_alloc(region, 880933707f3Ssthen sizeof(struct mesh_state)); 881933707f3Ssthen if(!mstate) { 882933707f3Ssthen alloc_reg_release(env->alloc, region); 883933707f3Ssthen return NULL; 884933707f3Ssthen } 885933707f3Ssthen memset(mstate, 0, sizeof(*mstate)); 886933707f3Ssthen mstate->node = *RBTREE_NULL; 887933707f3Ssthen mstate->run_node = *RBTREE_NULL; 888933707f3Ssthen mstate->node.key = mstate; 889933707f3Ssthen mstate->run_node.key = mstate; 890933707f3Ssthen mstate->reply_list = NULL; 891933707f3Ssthen mstate->list_select = mesh_no_list; 892933707f3Ssthen mstate->replies_sent = 0; 893933707f3Ssthen rbtree_init(&mstate->super_set, &mesh_state_ref_compare); 894933707f3Ssthen rbtree_init(&mstate->sub_set, &mesh_state_ref_compare); 895933707f3Ssthen mstate->num_activated = 0; 89677079be7Ssthen mstate->unique = NULL; 897933707f3Ssthen /* init module qstate */ 898933707f3Ssthen mstate->s.qinfo.qtype = qinfo->qtype; 899933707f3Ssthen mstate->s.qinfo.qclass = qinfo->qclass; 90077079be7Ssthen mstate->s.qinfo.local_alias = NULL; 901933707f3Ssthen mstate->s.qinfo.qname_len = qinfo->qname_len; 902933707f3Ssthen mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname, 903933707f3Ssthen qinfo->qname_len); 904933707f3Ssthen if(!mstate->s.qinfo.qname) { 905933707f3Ssthen alloc_reg_release(env->alloc, region); 906933707f3Ssthen return NULL; 907933707f3Ssthen } 9082be9e038Ssthen if(cinfo) { 9092be9e038Ssthen mstate->s.client_info = regional_alloc_init(region, cinfo, 9102be9e038Ssthen sizeof(*cinfo)); 9112be9e038Ssthen if(!mstate->s.client_info) { 9122be9e038Ssthen alloc_reg_release(env->alloc, region); 9132be9e038Ssthen return NULL; 9142be9e038Ssthen } 9152be9e038Ssthen } 916933707f3Ssthen /* remove all weird bits from qflags */ 917933707f3Ssthen mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD)); 918933707f3Ssthen mstate->s.is_priming = prime; 91957dceb2aSbrad mstate->s.is_valrec = valrec; 920933707f3Ssthen mstate->s.reply = NULL; 921933707f3Ssthen mstate->s.region = region; 922933707f3Ssthen mstate->s.curmod = 0; 923933707f3Ssthen mstate->s.return_msg = 0; 924933707f3Ssthen mstate->s.return_rcode = LDNS_RCODE_NOERROR; 925933707f3Ssthen mstate->s.env = env; 926933707f3Ssthen mstate->s.mesh_info = mstate; 927933707f3Ssthen mstate->s.prefetch_leeway = 0; 928eaf2578eSsthen mstate->s.serve_expired_data = NULL; 92977079be7Ssthen mstate->s.no_cache_lookup = 0; 93077079be7Ssthen mstate->s.no_cache_store = 0; 931bdfc4d55Sflorian mstate->s.need_refetch = 0; 9322308e98cSsthen mstate->s.was_ratelimited = 0; 933d1e2768aSsthen mstate->s.qstarttime = *env->now; 934bdfc4d55Sflorian 935933707f3Ssthen /* init modules */ 936933707f3Ssthen for(i=0; i<env->mesh->mods.num; i++) { 937933707f3Ssthen mstate->s.minfo[i] = NULL; 938933707f3Ssthen mstate->s.ext_state[i] = module_state_initial; 939933707f3Ssthen } 94077079be7Ssthen /* init edns option lists */ 94177079be7Ssthen mstate->s.edns_opts_front_in = NULL; 94277079be7Ssthen mstate->s.edns_opts_back_out = NULL; 94377079be7Ssthen mstate->s.edns_opts_back_in = NULL; 94477079be7Ssthen mstate->s.edns_opts_front_out = NULL; 94577079be7Ssthen 946933707f3Ssthen return mstate; 947933707f3Ssthen } 948933707f3Ssthen 94977079be7Ssthen void 95077079be7Ssthen mesh_state_make_unique(struct mesh_state* mstate) 95177079be7Ssthen { 95277079be7Ssthen mstate->unique = mstate; 95377079be7Ssthen } 95477079be7Ssthen 955933707f3Ssthen void 956933707f3Ssthen mesh_state_cleanup(struct mesh_state* mstate) 957933707f3Ssthen { 958933707f3Ssthen struct mesh_area* mesh; 959933707f3Ssthen int i; 960933707f3Ssthen if(!mstate) 961933707f3Ssthen return; 962933707f3Ssthen mesh = mstate->s.env->mesh; 963eaf2578eSsthen /* Stop and delete the serve expired timer */ 964eaf2578eSsthen if(mstate->s.serve_expired_data && mstate->s.serve_expired_data->timer) { 965eaf2578eSsthen comm_timer_delete(mstate->s.serve_expired_data->timer); 966eaf2578eSsthen mstate->s.serve_expired_data->timer = NULL; 967eaf2578eSsthen } 968933707f3Ssthen /* drop unsent replies */ 969933707f3Ssthen if(!mstate->replies_sent) { 970f6b99bafSsthen struct mesh_reply* rep = mstate->reply_list; 971933707f3Ssthen struct mesh_cb* cb; 972f6b99bafSsthen /* in tcp_req_info, the mstates linked are removed, but 973f6b99bafSsthen * the reply_list is now NULL, so the remove-from-empty-list 974f6b99bafSsthen * takes no time and also it does not do the mesh accounting */ 975f6b99bafSsthen mstate->reply_list = NULL; 976f6b99bafSsthen for(; rep; rep=rep->next) { 9772bdc0ed1Ssthen infra_wait_limit_dec(mesh->env->infra_cache, 9782bdc0ed1Ssthen &rep->query_reply, mesh->env->cfg); 979*98bc733bSsthen if(rep->query_reply.c->use_h2) 980*98bc733bSsthen http2_stream_remove_mesh_state(rep->h2_stream); 981933707f3Ssthen comm_point_drop_reply(&rep->query_reply); 982eaf2578eSsthen log_assert(mesh->num_reply_addrs > 0); 983933707f3Ssthen mesh->num_reply_addrs--; 984933707f3Ssthen } 98520237c55Ssthen while((cb = mstate->cb_list)!=NULL) { 98620237c55Ssthen mstate->cb_list = cb->next; 987933707f3Ssthen fptr_ok(fptr_whitelist_mesh_cb(cb->cb)); 988933707f3Ssthen (*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL, 9892308e98cSsthen sec_status_unchecked, NULL, 0); 990eaf2578eSsthen log_assert(mesh->num_reply_addrs > 0); 991933707f3Ssthen mesh->num_reply_addrs--; 992933707f3Ssthen } 993933707f3Ssthen } 994933707f3Ssthen 995933707f3Ssthen /* de-init modules */ 996933707f3Ssthen for(i=0; i<mesh->mods.num; i++) { 997933707f3Ssthen fptr_ok(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear)); 998933707f3Ssthen (*mesh->mods.mod[i]->clear)(&mstate->s, i); 999933707f3Ssthen mstate->s.minfo[i] = NULL; 1000933707f3Ssthen mstate->s.ext_state[i] = module_finished; 1001933707f3Ssthen } 1002933707f3Ssthen alloc_reg_release(mstate->s.env->alloc, mstate->s.region); 1003933707f3Ssthen } 1004933707f3Ssthen 1005933707f3Ssthen void 1006933707f3Ssthen mesh_state_delete(struct module_qstate* qstate) 1007933707f3Ssthen { 1008933707f3Ssthen struct mesh_area* mesh; 1009933707f3Ssthen struct mesh_state_ref* super, ref; 1010933707f3Ssthen struct mesh_state* mstate; 1011933707f3Ssthen if(!qstate) 1012933707f3Ssthen return; 1013933707f3Ssthen mstate = qstate->mesh_info; 1014933707f3Ssthen mesh = mstate->s.env->mesh; 1015933707f3Ssthen mesh_detach_subs(&mstate->s); 1016933707f3Ssthen if(mstate->list_select == mesh_forever_list) { 1017933707f3Ssthen mesh->num_forever_states --; 1018933707f3Ssthen mesh_list_remove(mstate, &mesh->forever_first, 1019933707f3Ssthen &mesh->forever_last); 1020933707f3Ssthen } else if(mstate->list_select == mesh_jostle_list) { 1021933707f3Ssthen mesh_list_remove(mstate, &mesh->jostle_first, 1022933707f3Ssthen &mesh->jostle_last); 1023933707f3Ssthen } 1024933707f3Ssthen if(!mstate->reply_list && !mstate->cb_list 1025933707f3Ssthen && mstate->super_set.count == 0) { 1026933707f3Ssthen log_assert(mesh->num_detached_states > 0); 1027933707f3Ssthen mesh->num_detached_states--; 1028933707f3Ssthen } 1029933707f3Ssthen if(mstate->reply_list || mstate->cb_list) { 1030933707f3Ssthen log_assert(mesh->num_reply_states > 0); 1031933707f3Ssthen mesh->num_reply_states--; 1032933707f3Ssthen } 1033933707f3Ssthen ref.node.key = &ref; 1034933707f3Ssthen ref.s = mstate; 1035933707f3Ssthen RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) { 1036933707f3Ssthen (void)rbtree_delete(&super->s->sub_set, &ref); 1037933707f3Ssthen } 1038933707f3Ssthen (void)rbtree_delete(&mesh->run, mstate); 1039933707f3Ssthen (void)rbtree_delete(&mesh->all, mstate); 1040933707f3Ssthen mesh_state_cleanup(mstate); 1041933707f3Ssthen } 1042933707f3Ssthen 1043933707f3Ssthen /** helper recursive rbtree find routine */ 1044933707f3Ssthen static int 1045933707f3Ssthen find_in_subsub(struct mesh_state* m, struct mesh_state* tofind, size_t *c) 1046933707f3Ssthen { 1047933707f3Ssthen struct mesh_state_ref* r; 1048933707f3Ssthen if((*c)++ > MESH_MAX_SUBSUB) 1049933707f3Ssthen return 1; 1050933707f3Ssthen RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) { 1051933707f3Ssthen if(r->s == tofind || find_in_subsub(r->s, tofind, c)) 1052933707f3Ssthen return 1; 1053933707f3Ssthen } 1054933707f3Ssthen return 0; 1055933707f3Ssthen } 1056933707f3Ssthen 1057933707f3Ssthen /** find cycle for already looked up mesh_state */ 1058933707f3Ssthen static int 1059933707f3Ssthen mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m) 1060933707f3Ssthen { 1061933707f3Ssthen struct mesh_state* cyc_m = qstate->mesh_info; 1062933707f3Ssthen size_t counter = 0; 1063933707f3Ssthen if(!dep_m) 1064933707f3Ssthen return 0; 1065933707f3Ssthen if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m, &counter)) { 1066933707f3Ssthen if(counter > MESH_MAX_SUBSUB) 1067933707f3Ssthen return 2; 1068933707f3Ssthen return 1; 1069933707f3Ssthen } 1070933707f3Ssthen return 0; 1071933707f3Ssthen } 1072933707f3Ssthen 1073933707f3Ssthen void mesh_detach_subs(struct module_qstate* qstate) 1074933707f3Ssthen { 1075933707f3Ssthen struct mesh_area* mesh = qstate->env->mesh; 1076933707f3Ssthen struct mesh_state_ref* ref, lookup; 1077933707f3Ssthen #ifdef UNBOUND_DEBUG 107877079be7Ssthen struct rbnode_type* n; 1079933707f3Ssthen #endif 1080933707f3Ssthen lookup.node.key = &lookup; 1081933707f3Ssthen lookup.s = qstate->mesh_info; 1082933707f3Ssthen RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) { 1083933707f3Ssthen #ifdef UNBOUND_DEBUG 1084933707f3Ssthen n = 1085229e174cSsthen #else 1086229e174cSsthen (void) 1087933707f3Ssthen #endif 1088933707f3Ssthen rbtree_delete(&ref->s->super_set, &lookup); 1089933707f3Ssthen log_assert(n != NULL); /* must have been present */ 1090933707f3Ssthen if(!ref->s->reply_list && !ref->s->cb_list 1091933707f3Ssthen && ref->s->super_set.count == 0) { 1092933707f3Ssthen mesh->num_detached_states++; 1093933707f3Ssthen log_assert(mesh->num_detached_states + 1094933707f3Ssthen mesh->num_reply_states <= mesh->all.count); 1095933707f3Ssthen } 1096933707f3Ssthen } 1097933707f3Ssthen rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare); 1098933707f3Ssthen } 1099933707f3Ssthen 11002be9e038Ssthen int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, 11012be9e038Ssthen uint16_t qflags, int prime, int valrec, struct module_qstate** newq, 11022be9e038Ssthen struct mesh_state** sub) 1103933707f3Ssthen { 1104933707f3Ssthen /* find it, if not, create it */ 1105933707f3Ssthen struct mesh_area* mesh = qstate->env->mesh; 11062be9e038Ssthen *sub = mesh_area_find(mesh, NULL, qinfo, qflags, 11072be9e038Ssthen prime, valrec); 11082be9e038Ssthen if(mesh_detect_cycle_found(qstate, *sub)) { 1109933707f3Ssthen verbose(VERB_ALGO, "attach failed, cycle detected"); 1110933707f3Ssthen return 0; 1111933707f3Ssthen } 11122be9e038Ssthen if(!*sub) { 1113933707f3Ssthen #ifdef UNBOUND_DEBUG 111477079be7Ssthen struct rbnode_type* n; 1115933707f3Ssthen #endif 1116933707f3Ssthen /* create a new one */ 11172be9e038Ssthen *sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, 11182be9e038Ssthen valrec); 11192be9e038Ssthen if(!*sub) { 1120933707f3Ssthen log_err("mesh_attach_sub: out of memory"); 1121933707f3Ssthen return 0; 1122933707f3Ssthen } 1123933707f3Ssthen #ifdef UNBOUND_DEBUG 1124933707f3Ssthen n = 1125229e174cSsthen #else 1126229e174cSsthen (void) 1127933707f3Ssthen #endif 11282be9e038Ssthen rbtree_insert(&mesh->all, &(*sub)->node); 1129933707f3Ssthen log_assert(n != NULL); 1130933707f3Ssthen /* set detached (it is now) */ 1131933707f3Ssthen mesh->num_detached_states++; 1132933707f3Ssthen /* set new query state to run */ 1133933707f3Ssthen #ifdef UNBOUND_DEBUG 1134933707f3Ssthen n = 1135229e174cSsthen #else 1136229e174cSsthen (void) 1137933707f3Ssthen #endif 11382be9e038Ssthen rbtree_insert(&mesh->run, &(*sub)->run_node); 1139933707f3Ssthen log_assert(n != NULL); 11402be9e038Ssthen *newq = &(*sub)->s; 1141933707f3Ssthen } else 1142933707f3Ssthen *newq = NULL; 11432be9e038Ssthen return 1; 11442be9e038Ssthen } 11452be9e038Ssthen 11462be9e038Ssthen int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, 11472be9e038Ssthen uint16_t qflags, int prime, int valrec, struct module_qstate** newq) 11482be9e038Ssthen { 11492be9e038Ssthen struct mesh_area* mesh = qstate->env->mesh; 11502be9e038Ssthen struct mesh_state* sub = NULL; 11512be9e038Ssthen int was_detached; 11522be9e038Ssthen if(!mesh_add_sub(qstate, qinfo, qflags, prime, valrec, newq, &sub)) 11532be9e038Ssthen return 0; 11543dcb24b8Ssthen was_detached = (sub->super_set.count == 0); 1155933707f3Ssthen if(!mesh_state_attachment(qstate->mesh_info, sub)) 1156933707f3Ssthen return 0; 11573dcb24b8Ssthen /* if it was a duplicate attachment, the count was not zero before */ 11583dcb24b8Ssthen if(!sub->reply_list && !sub->cb_list && was_detached && 11593dcb24b8Ssthen sub->super_set.count == 1) { 1160933707f3Ssthen /* it used to be detached, before this one got added */ 1161933707f3Ssthen log_assert(mesh->num_detached_states > 0); 1162933707f3Ssthen mesh->num_detached_states--; 1163933707f3Ssthen } 1164933707f3Ssthen /* *newq will be run when inited after the current module stops */ 1165933707f3Ssthen return 1; 1166933707f3Ssthen } 1167933707f3Ssthen 1168933707f3Ssthen int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub) 1169933707f3Ssthen { 1170933707f3Ssthen #ifdef UNBOUND_DEBUG 117177079be7Ssthen struct rbnode_type* n; 1172933707f3Ssthen #endif 1173933707f3Ssthen struct mesh_state_ref* subref; /* points to sub, inserted in super */ 1174933707f3Ssthen struct mesh_state_ref* superref; /* points to super, inserted in sub */ 1175933707f3Ssthen if( !(subref = regional_alloc(super->s.region, 1176933707f3Ssthen sizeof(struct mesh_state_ref))) || 1177933707f3Ssthen !(superref = regional_alloc(sub->s.region, 1178933707f3Ssthen sizeof(struct mesh_state_ref))) ) { 1179933707f3Ssthen log_err("mesh_state_attachment: out of memory"); 1180933707f3Ssthen return 0; 1181933707f3Ssthen } 1182933707f3Ssthen superref->node.key = superref; 1183933707f3Ssthen superref->s = super; 1184933707f3Ssthen subref->node.key = subref; 1185933707f3Ssthen subref->s = sub; 11863dcb24b8Ssthen if(!rbtree_insert(&sub->super_set, &superref->node)) { 11873dcb24b8Ssthen /* this should not happen, iterator and validator do not 11883dcb24b8Ssthen * attach subqueries that are identical. */ 11893dcb24b8Ssthen /* already attached, we are done, nothing todo. 11903dcb24b8Ssthen * since superref and subref already allocated in region, 11913dcb24b8Ssthen * we cannot free them */ 11923dcb24b8Ssthen return 1; 11933dcb24b8Ssthen } 1194933707f3Ssthen #ifdef UNBOUND_DEBUG 1195933707f3Ssthen n = 1196229e174cSsthen #else 1197229e174cSsthen (void) 1198933707f3Ssthen #endif 1199933707f3Ssthen rbtree_insert(&super->sub_set, &subref->node); 12003dcb24b8Ssthen log_assert(n != NULL); /* we checked above if statement, the reverse 12013dcb24b8Ssthen administration should not fail now, unless they are out of sync */ 1202933707f3Ssthen return 1; 1203933707f3Ssthen } 1204933707f3Ssthen 1205933707f3Ssthen /** 1206933707f3Ssthen * callback results to mesh cb entry 1207933707f3Ssthen * @param m: mesh state to send it for. 1208933707f3Ssthen * @param rcode: if not 0, error code. 1209933707f3Ssthen * @param rep: reply to send (or NULL if rcode is set). 1210933707f3Ssthen * @param r: callback entry 12119982a05dSsthen * @param start_time: the time to pass to callback functions, it is 0 or 12129982a05dSsthen * a value from one of the packets if the mesh state had packets. 1213933707f3Ssthen */ 1214933707f3Ssthen static void 1215933707f3Ssthen mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, 12169982a05dSsthen struct mesh_cb* r, struct timeval* start_time) 1217933707f3Ssthen { 1218933707f3Ssthen int secure; 1219933707f3Ssthen char* reason = NULL; 12202308e98cSsthen int was_ratelimited = m->s.was_ratelimited; 1221933707f3Ssthen /* bogus messages are not made into servfail, sec_status passed 1222933707f3Ssthen * to the callback function */ 1223933707f3Ssthen if(rep && rep->security == sec_status_secure) 1224933707f3Ssthen secure = 1; 1225933707f3Ssthen else secure = 0; 1226933707f3Ssthen if(!rep && rcode == LDNS_RCODE_NOERROR) 1227933707f3Ssthen rcode = LDNS_RCODE_SERVFAIL; 12288b7325afSsthen if(!rcode && rep && (rep->security == sec_status_bogus || 122920237c55Ssthen rep->security == sec_status_secure_sentinel_fail)) { 12302bdc0ed1Ssthen if(!(reason = errinf_to_str_bogus(&m->s, NULL))) 1231933707f3Ssthen rcode = LDNS_RCODE_SERVFAIL; 1232933707f3Ssthen } 1233933707f3Ssthen /* send the reply */ 1234933707f3Ssthen if(rcode) { 123577079be7Ssthen if(rcode == LDNS_RCODE_SERVFAIL) { 123677079be7Ssthen if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, 12379982a05dSsthen rep, rcode, &r->edns, NULL, m->s.region, start_time)) 1238e21c60efSsthen r->edns.opt_list_inplace_cb_out = NULL; 123977079be7Ssthen } else { 124077079be7Ssthen if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, 12419982a05dSsthen &r->edns, NULL, m->s.region, start_time)) 1242e21c60efSsthen r->edns.opt_list_inplace_cb_out = NULL; 124377079be7Ssthen } 1244933707f3Ssthen fptr_ok(fptr_whitelist_mesh_cb(r->cb)); 12452308e98cSsthen (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL, 12462308e98cSsthen was_ratelimited); 1247933707f3Ssthen } else { 1248933707f3Ssthen size_t udp_size = r->edns.udp_size; 12495d76a658Ssthen sldns_buffer_clear(r->buf); 1250933707f3Ssthen r->edns.edns_version = EDNS_ADVERTISED_VERSION; 1251933707f3Ssthen r->edns.udp_size = EDNS_ADVERTISED_SIZE; 1252933707f3Ssthen r->edns.ext_rcode = 0; 1253933707f3Ssthen r->edns.bits &= EDNS_DO; 1254d896b962Ssthen if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) 1255d896b962Ssthen r->edns.edns_present = 0; 125677079be7Ssthen 125777079be7Ssthen if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, 12589982a05dSsthen LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region, start_time) || 12592ee382b6Ssthen !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 1260933707f3Ssthen r->qflags, r->buf, 0, 1, 1261933707f3Ssthen m->s.env->scratch, udp_size, &r->edns, 1262933707f3Ssthen (int)(r->edns.bits & EDNS_DO), secure)) 1263933707f3Ssthen { 1264933707f3Ssthen fptr_ok(fptr_whitelist_mesh_cb(r->cb)); 1265933707f3Ssthen (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, 12662308e98cSsthen sec_status_unchecked, NULL, 0); 1267933707f3Ssthen } else { 1268933707f3Ssthen fptr_ok(fptr_whitelist_mesh_cb(r->cb)); 1269933707f3Ssthen (*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf, 12708b7325afSsthen (rep?rep->security:sec_status_unchecked), 12718b7325afSsthen reason, was_ratelimited); 1272933707f3Ssthen } 1273933707f3Ssthen } 1274933707f3Ssthen free(reason); 1275eaf2578eSsthen log_assert(m->s.env->mesh->num_reply_addrs > 0); 1276933707f3Ssthen m->s.env->mesh->num_reply_addrs--; 1277933707f3Ssthen } 1278933707f3Ssthen 1279e21c60efSsthen static inline int 1280e21c60efSsthen mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m) 1281e21c60efSsthen { 1282e21c60efSsthen struct respip_action_info const* respip_info = m->s.respip_action_info; 1283d896b962Ssthen return (respip_info == NULL 1284e21c60efSsthen ? 0 1285e21c60efSsthen : (respip_info->rpz_used 1286e21c60efSsthen && !respip_info->rpz_disabled 1287d896b962Ssthen && respip_info->action == respip_truncate)) 1288d896b962Ssthen || m->s.tcp_required; 1289e21c60efSsthen } 1290e21c60efSsthen 1291e21c60efSsthen static inline int 12928b7325afSsthen mesh_is_udp(struct mesh_reply const* r) 12938b7325afSsthen { 1294e21c60efSsthen return r->query_reply.c->type == comm_udp; 1295e21c60efSsthen } 1296e21c60efSsthen 12978b7325afSsthen static inline void 12988b7325afSsthen mesh_find_and_attach_ede_and_reason(struct mesh_state* m, 12998b7325afSsthen struct reply_info* rep, struct mesh_reply* r) 13008b7325afSsthen { 13018b7325afSsthen /* OLD note: 13028b7325afSsthen * During validation the EDE code can be received via two 13038b7325afSsthen * code paths. One code path fills the reply_info EDE, and 13048b7325afSsthen * the other fills it in the errinf_strlist. These paths 13058b7325afSsthen * intersect at some points, but where is opaque due to 13068b7325afSsthen * the complexity of the validator. At the time of writing 13078b7325afSsthen * we make the choice to prefer the EDE from errinf_strlist 13088b7325afSsthen * but a compelling reason to do otherwise is just as valid 13098b7325afSsthen * NEW note: 13108b7325afSsthen * The compelling reason is that with caching support, the value 13118b7325afSsthen * in the reply_info is cached. 13128b7325afSsthen * The reason members of the reply_info struct should be 13138b7325afSsthen * updated as they are already cached. No reason to 13148b7325afSsthen * try and find the EDE information in errinf anymore. 13158b7325afSsthen */ 13168b7325afSsthen if(rep->reason_bogus != LDNS_EDE_NONE) { 13178b7325afSsthen edns_opt_list_append_ede(&r->edns.opt_list_out, 13188b7325afSsthen m->s.region, rep->reason_bogus, rep->reason_bogus_str); 13198b7325afSsthen } 13208b7325afSsthen } 13218b7325afSsthen 1322933707f3Ssthen /** 1323933707f3Ssthen * Send reply to mesh reply entry 1324933707f3Ssthen * @param m: mesh state to send it for. 1325933707f3Ssthen * @param rcode: if not 0, error code. 1326933707f3Ssthen * @param rep: reply to send (or NULL if rcode is set). 1327933707f3Ssthen * @param r: reply entry 1328f6b99bafSsthen * @param r_buffer: buffer to use for reply entry. 1329933707f3Ssthen * @param prev: previous reply, already has its answer encoded in buffer. 1330f6b99bafSsthen * @param prev_buffer: buffer for previous reply. 1331933707f3Ssthen */ 1332933707f3Ssthen static void 1333933707f3Ssthen mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, 1334f6b99bafSsthen struct mesh_reply* r, struct sldns_buffer* r_buffer, 1335f6b99bafSsthen struct mesh_reply* prev, struct sldns_buffer* prev_buffer) 1336933707f3Ssthen { 1337933707f3Ssthen struct timeval end_time; 1338933707f3Ssthen struct timeval duration; 1339933707f3Ssthen int secure; 1340eba819a2Ssthen /* briefly set the replylist to null in case the 1341eba819a2Ssthen * meshsendreply calls tcpreqinfo sendreply that 1342eba819a2Ssthen * comm_point_drops because of size, and then the 1343eba819a2Ssthen * null stops the mesh state remove and thus 1344eba819a2Ssthen * reply_list modification and accounting */ 1345eba819a2Ssthen struct mesh_reply* rlist = m->reply_list; 1346e21c60efSsthen 1347e21c60efSsthen /* rpz: apply actions */ 1348e21c60efSsthen rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) 1349e21c60efSsthen ? (rcode|BIT_TC) : rcode; 1350e21c60efSsthen 1351933707f3Ssthen /* examine security status */ 1352933707f3Ssthen if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || 1353933707f3Ssthen m->s.env->cfg->ignore_cd) && rep && 135420237c55Ssthen (rep->security <= sec_status_bogus || 135520237c55Ssthen rep->security == sec_status_secure_sentinel_fail)) { 1356933707f3Ssthen rcode = LDNS_RCODE_SERVFAIL; 1357933707f3Ssthen if(m->s.env->cfg->stat_extended) 1358933707f3Ssthen m->s.env->mesh->ans_bogus++; 1359933707f3Ssthen } 1360933707f3Ssthen if(rep && rep->security == sec_status_secure) 1361933707f3Ssthen secure = 1; 1362933707f3Ssthen else secure = 0; 1363933707f3Ssthen if(!rep && rcode == LDNS_RCODE_NOERROR) 1364933707f3Ssthen rcode = LDNS_RCODE_SERVFAIL; 13652c144df0Ssthen if(r->query_reply.c->use_h2) { 13662c144df0Ssthen r->query_reply.c->h2_stream = r->h2_stream; 13672c144df0Ssthen /* Mesh reply won't exist for long anymore. Make it impossible 13682c144df0Ssthen * for HTTP/2 stream to refer to mesh state, in case 13692c144df0Ssthen * connection gets cleanup before HTTP/2 stream close. */ 13702c144df0Ssthen r->h2_stream->mesh_state = NULL; 13712c144df0Ssthen } 1372933707f3Ssthen /* send the reply */ 1373eba819a2Ssthen /* We don't reuse the encoded answer if: 1374eba819a2Ssthen * - either the previous or current response has a local alias. We could 1375eba819a2Ssthen * compare the alias records and still reuse the previous answer if they 1376eba819a2Ssthen * are the same, but that would be complicated and error prone for the 1377eba819a2Ssthen * relatively minor case. So we err on the side of safety. 1378eba819a2Ssthen * - there are registered callback functions for the given rcode, as these 1379eba819a2Ssthen * need to be called for each reply. */ 1380eba819a2Ssthen if(((rcode != LDNS_RCODE_SERVFAIL && 1381eba819a2Ssthen !m->s.env->inplace_cb_lists[inplace_cb_reply]) || 1382eba819a2Ssthen (rcode == LDNS_RCODE_SERVFAIL && 1383eba819a2Ssthen !m->s.env->inplace_cb_lists[inplace_cb_reply_servfail])) && 1384eba819a2Ssthen prev && prev_buffer && prev->qflags == r->qflags && 138577079be7Ssthen !prev->local_alias && !r->local_alias && 1386933707f3Ssthen prev->edns.edns_present == r->edns.edns_present && 1387933707f3Ssthen prev->edns.bits == r->edns.bits && 13882ee382b6Ssthen prev->edns.udp_size == r->edns.udp_size && 1389e21c60efSsthen edns_opt_list_compare(prev->edns.opt_list_out, r->edns.opt_list_out) == 0 && 1390e21c60efSsthen edns_opt_list_compare(prev->edns.opt_list_inplace_cb_out, r->edns.opt_list_inplace_cb_out) == 0 1391e21c60efSsthen ) { 1392933707f3Ssthen /* if the previous reply is identical to this one, fix ID */ 1393f6b99bafSsthen if(prev_buffer != r_buffer) 1394f6b99bafSsthen sldns_buffer_copy(r_buffer, prev_buffer); 1395f6b99bafSsthen sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t)); 1396f6b99bafSsthen sldns_buffer_write_at(r_buffer, 12, r->qname, 1397f6b99bafSsthen m->s.qinfo.qname_len); 1398eba819a2Ssthen m->reply_list = NULL; 1399933707f3Ssthen comm_point_send_reply(&r->query_reply); 1400eba819a2Ssthen m->reply_list = rlist; 1401933707f3Ssthen } else if(rcode) { 1402933707f3Ssthen m->s.qinfo.qname = r->qname; 140377079be7Ssthen m->s.qinfo.local_alias = r->local_alias; 140477079be7Ssthen if(rcode == LDNS_RCODE_SERVFAIL) { 140577079be7Ssthen if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, 14069982a05dSsthen rep, rcode, &r->edns, &r->query_reply, m->s.region, &r->start_time)) 1407e21c60efSsthen r->edns.opt_list_inplace_cb_out = NULL; 140877079be7Ssthen } else { 140977079be7Ssthen if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, 14109982a05dSsthen &r->edns, &r->query_reply, m->s.region, &r->start_time)) 1411e21c60efSsthen r->edns.opt_list_inplace_cb_out = NULL; 141277079be7Ssthen } 14138b7325afSsthen /* Send along EDE EDNS0 option when SERVFAILing; usually 14148b7325afSsthen * DNSSEC validation failures */ 14158b7325afSsthen /* Since we are SERVFAILing here, CD bit and rep->security 14168b7325afSsthen * is already handled. */ 14178b7325afSsthen if(m->s.env->cfg->ede && rep) { 14188b7325afSsthen mesh_find_and_attach_ede_and_reason(m, rep, r); 14190bdb4f62Ssthen } 1420f6b99bafSsthen error_encode(r_buffer, rcode, &m->s.qinfo, r->qid, 1421f6b99bafSsthen r->qflags, &r->edns); 1422eba819a2Ssthen m->reply_list = NULL; 1423933707f3Ssthen comm_point_send_reply(&r->query_reply); 1424eba819a2Ssthen m->reply_list = rlist; 1425933707f3Ssthen } else { 1426933707f3Ssthen size_t udp_size = r->edns.udp_size; 1427933707f3Ssthen r->edns.edns_version = EDNS_ADVERTISED_VERSION; 1428933707f3Ssthen r->edns.udp_size = EDNS_ADVERTISED_SIZE; 1429933707f3Ssthen r->edns.ext_rcode = 0; 1430933707f3Ssthen r->edns.bits &= EDNS_DO; 1431d896b962Ssthen if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) 1432d896b962Ssthen r->edns.edns_present = 0; 1433933707f3Ssthen m->s.qinfo.qname = r->qname; 143477079be7Ssthen m->s.qinfo.local_alias = r->local_alias; 14358b7325afSsthen 14368b7325afSsthen /* Attach EDE without SERVFAIL if the validation failed. 14378b7325afSsthen * Need to explicitly check for rep->security otherwise failed 14388b7325afSsthen * validation paths may attach to a secure answer. */ 14398b7325afSsthen if(m->s.env->cfg->ede && rep && 14408b7325afSsthen (rep->security <= sec_status_bogus || 14418b7325afSsthen rep->security == sec_status_secure_sentinel_fail)) { 14428b7325afSsthen mesh_find_and_attach_ede_and_reason(m, rep, r); 14438b7325afSsthen } 14448b7325afSsthen 144577079be7Ssthen if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, 14469982a05dSsthen LDNS_RCODE_NOERROR, &r->edns, &r->query_reply, m->s.region, &r->start_time) || 14472ee382b6Ssthen !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 1448f6b99bafSsthen r->qflags, r_buffer, 0, 1, m->s.env->scratch, 1449f6b99bafSsthen udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), 1450f6b99bafSsthen secure)) 1451933707f3Ssthen { 145277079be7Ssthen if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, 14539982a05dSsthen rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) 1454e21c60efSsthen r->edns.opt_list_inplace_cb_out = NULL; 14550bdb4f62Ssthen /* internal server error (probably malloc failure) so no 14560bdb4f62Ssthen * EDE (RFC8914) needed */ 1457f6b99bafSsthen error_encode(r_buffer, LDNS_RCODE_SERVFAIL, 1458f6b99bafSsthen &m->s.qinfo, r->qid, r->qflags, &r->edns); 1459933707f3Ssthen } 1460eba819a2Ssthen m->reply_list = NULL; 1461933707f3Ssthen comm_point_send_reply(&r->query_reply); 1462eba819a2Ssthen m->reply_list = rlist; 1463933707f3Ssthen } 14642bdc0ed1Ssthen infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, 14652bdc0ed1Ssthen m->s.env->cfg); 1466933707f3Ssthen /* account */ 1467eaf2578eSsthen log_assert(m->s.env->mesh->num_reply_addrs > 0); 1468933707f3Ssthen m->s.env->mesh->num_reply_addrs--; 1469933707f3Ssthen end_time = *m->s.env->now_tv; 1470933707f3Ssthen timeval_subtract(&duration, &end_time, &r->start_time); 14715d76a658Ssthen verbose(VERB_ALGO, "query took " ARG_LL "d.%6.6d sec", 1472229e174cSsthen (long long)duration.tv_sec, (int)duration.tv_usec); 1473933707f3Ssthen m->s.env->mesh->replies_sent++; 1474933707f3Ssthen timeval_add(&m->s.env->mesh->replies_sum_wait, &duration); 1475933707f3Ssthen timehist_insert(m->s.env->mesh->histogram, &duration); 1476933707f3Ssthen if(m->s.env->cfg->stat_extended) { 1477f6b99bafSsthen uint16_t rc = FLAGS_GET_RCODE(sldns_buffer_read_u16_at( 1478f6b99bafSsthen r_buffer, 2)); 1479933707f3Ssthen if(secure) m->s.env->mesh->ans_secure++; 1480933707f3Ssthen m->s.env->mesh->ans_rcode[ rc ] ++; 1481f6b99bafSsthen if(rc == 0 && LDNS_ANCOUNT(sldns_buffer_begin(r_buffer)) == 0) 1482933707f3Ssthen m->s.env->mesh->ans_nodata++; 1483933707f3Ssthen } 148477079be7Ssthen /* Log reply sent */ 148577079be7Ssthen if(m->s.env->cfg->log_replies) { 148645872187Ssthen log_reply_info(NO_VERBOSE, &m->s.qinfo, 148745872187Ssthen &r->query_reply.client_addr, 1488f46c52bfSsthen r->query_reply.client_addrlen, duration, 0, r_buffer, 14892bdc0ed1Ssthen (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL), 1490f46c52bfSsthen r->query_reply.c->type); 149177079be7Ssthen } 1492933707f3Ssthen } 1493933707f3Ssthen 1494933707f3Ssthen void mesh_query_done(struct mesh_state* mstate) 1495933707f3Ssthen { 1496a3167c07Ssthen struct mesh_reply* r; 1497933707f3Ssthen struct mesh_reply* prev = NULL; 1498f6b99bafSsthen struct sldns_buffer* prev_buffer = NULL; 1499933707f3Ssthen struct mesh_cb* c; 1500933707f3Ssthen struct reply_info* rep = (mstate->s.return_msg? 1501933707f3Ssthen mstate->s.return_msg->rep:NULL); 15029982a05dSsthen struct timeval tv = {0, 0}; 15038b7325afSsthen int i = 0; 1504eaf2578eSsthen /* No need for the serve expired timer anymore; we are going to reply. */ 1505eaf2578eSsthen if(mstate->s.serve_expired_data) { 1506eaf2578eSsthen comm_timer_delete(mstate->s.serve_expired_data->timer); 1507eaf2578eSsthen mstate->s.serve_expired_data->timer = NULL; 1508eaf2578eSsthen } 1509eaf2578eSsthen if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL || 1510eaf2578eSsthen (rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) { 1511e21c60efSsthen /* we are SERVFAILing; check for expired answer here */ 1512eaf2578eSsthen mesh_serve_expired_callback(mstate); 1513eaf2578eSsthen if((mstate->reply_list || mstate->cb_list) 15142308e98cSsthen && mstate->s.env->cfg->log_servfail 15152308e98cSsthen && !mstate->s.env->cfg->val_log_squelch) { 15162308e98cSsthen char* err = errinf_to_str_servfail(&mstate->s); 15172bdc0ed1Ssthen if(err) { log_err("%s", err); } 15182308e98cSsthen } 1519eaf2578eSsthen } 1520a3167c07Ssthen for(r = mstate->reply_list; r; r = r->next) { 15212bdc0ed1Ssthen struct timeval old; 15222bdc0ed1Ssthen timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); 15232bdc0ed1Ssthen if(mstate->s.env->cfg->discard_timeout != 0 && 15242bdc0ed1Ssthen ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > 15252bdc0ed1Ssthen mstate->s.env->cfg->discard_timeout) { 15262bdc0ed1Ssthen /* Drop the reply, it is too old */ 15272bdc0ed1Ssthen /* briefly set the reply_list to NULL, so that the 15282bdc0ed1Ssthen * tcp req info cleanup routine that calls the mesh 15292bdc0ed1Ssthen * to deregister the meshstate for it is not done 15302bdc0ed1Ssthen * because the list is NULL and also accounting is not 15312bdc0ed1Ssthen * done there, but instead we do that here. */ 15322bdc0ed1Ssthen struct mesh_reply* reply_list = mstate->reply_list; 15332bdc0ed1Ssthen verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); 15342bdc0ed1Ssthen infra_wait_limit_dec(mstate->s.env->infra_cache, 15352bdc0ed1Ssthen &r->query_reply, mstate->s.env->cfg); 15362bdc0ed1Ssthen mstate->reply_list = NULL; 1537*98bc733bSsthen if(r->query_reply.c->use_h2) 1538*98bc733bSsthen http2_stream_remove_mesh_state(r->h2_stream); 15392bdc0ed1Ssthen comm_point_drop_reply(&r->query_reply); 15402bdc0ed1Ssthen mstate->reply_list = reply_list; 15412bdc0ed1Ssthen mstate->s.env->mesh->stats_dropped++; 15422bdc0ed1Ssthen continue; 15432bdc0ed1Ssthen } 15442bdc0ed1Ssthen 15458b7325afSsthen i++; 15469982a05dSsthen tv = r->start_time; 15479982a05dSsthen 15482be9e038Ssthen /* if a response-ip address block has been stored the 15492be9e038Ssthen * information should be logged for each client. */ 15502be9e038Ssthen if(mstate->s.respip_action_info && 15512be9e038Ssthen mstate->s.respip_action_info->addrinfo) { 1552eaf2578eSsthen respip_inform_print(mstate->s.respip_action_info, 15532be9e038Ssthen r->qname, mstate->s.qinfo.qtype, 15542be9e038Ssthen mstate->s.qinfo.qclass, r->local_alias, 155545872187Ssthen &r->query_reply.client_addr, 155645872187Ssthen r->query_reply.client_addrlen); 15572be9e038Ssthen } 15582be9e038Ssthen 15592be9e038Ssthen /* if this query is determined to be dropped during the 15602be9e038Ssthen * mesh processing, this is the point to take that action. */ 1561eaf2578eSsthen if(mstate->s.is_drop) { 1562a3167c07Ssthen /* briefly set the reply_list to NULL, so that the 1563a3167c07Ssthen * tcp req info cleanup routine that calls the mesh 1564a3167c07Ssthen * to deregister the meshstate for it is not done 1565a3167c07Ssthen * because the list is NULL and also accounting is not 1566a3167c07Ssthen * done there, but instead we do that here. */ 1567a3167c07Ssthen struct mesh_reply* reply_list = mstate->reply_list; 15682bdc0ed1Ssthen infra_wait_limit_dec(mstate->s.env->infra_cache, 15692bdc0ed1Ssthen &r->query_reply, mstate->s.env->cfg); 1570a3167c07Ssthen mstate->reply_list = NULL; 1571*98bc733bSsthen if(r->query_reply.c->use_h2) { 1572*98bc733bSsthen http2_stream_remove_mesh_state(r->h2_stream); 1573*98bc733bSsthen } 15742be9e038Ssthen comm_point_drop_reply(&r->query_reply); 1575a3167c07Ssthen mstate->reply_list = reply_list; 1576eaf2578eSsthen } else { 1577f6b99bafSsthen struct sldns_buffer* r_buffer = r->query_reply.c->buffer; 1578550cf4a9Ssthen if(r->query_reply.c->tcp_req_info) { 1579f6b99bafSsthen r_buffer = r->query_reply.c->tcp_req_info->spool_buffer; 1580550cf4a9Ssthen prev_buffer = NULL; 1581550cf4a9Ssthen } 15822be9e038Ssthen mesh_send_reply(mstate, mstate->s.return_rcode, rep, 1583f6b99bafSsthen r, r_buffer, prev, prev_buffer); 1584550cf4a9Ssthen if(r->query_reply.c->tcp_req_info) { 1585f6b99bafSsthen tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); 1586550cf4a9Ssthen r_buffer = NULL; 1587550cf4a9Ssthen } 1588*98bc733bSsthen /* mesh_send_reply removed mesh state from 1589*98bc733bSsthen * http2_stream. */ 1590933707f3Ssthen prev = r; 1591f6b99bafSsthen prev_buffer = r_buffer; 1592933707f3Ssthen } 15932be9e038Ssthen } 15948b7325afSsthen /* Account for each reply sent. */ 15958b7325afSsthen if(i > 0 && mstate->s.respip_action_info && 15968b7325afSsthen mstate->s.respip_action_info->addrinfo && 15978b7325afSsthen mstate->s.env->cfg->stat_extended && 15988b7325afSsthen mstate->s.respip_action_info->rpz_used) { 15998b7325afSsthen if(mstate->s.respip_action_info->rpz_disabled) 16008b7325afSsthen mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i; 16018b7325afSsthen if(mstate->s.respip_action_info->rpz_cname_override) 16028b7325afSsthen mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i; 16038b7325afSsthen else 16048b7325afSsthen mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action( 16058b7325afSsthen mstate->s.respip_action_info->action)] += i; 16068b7325afSsthen } 16078b7325afSsthen if(!mstate->s.is_drop && i > 0) { 16088b7325afSsthen if(mstate->s.env->cfg->stat_extended 16098b7325afSsthen && mstate->s.is_cachedb_answer) { 16108b7325afSsthen mstate->s.env->mesh->ans_cachedb += i; 16118b7325afSsthen } 16128b7325afSsthen } 16138b7325afSsthen 16148b7325afSsthen /* Mesh area accounting */ 1615a3167c07Ssthen if(mstate->reply_list) { 1616a3167c07Ssthen mstate->reply_list = NULL; 1617a3167c07Ssthen if(!mstate->reply_list && !mstate->cb_list) { 1618a3167c07Ssthen /* was a reply state, not anymore */ 1619a3167c07Ssthen log_assert(mstate->s.env->mesh->num_reply_states > 0); 1620a3167c07Ssthen mstate->s.env->mesh->num_reply_states--; 1621a3167c07Ssthen } 1622a3167c07Ssthen if(!mstate->reply_list && !mstate->cb_list && 1623a3167c07Ssthen mstate->super_set.count == 0) 1624a3167c07Ssthen mstate->s.env->mesh->num_detached_states++; 1625a3167c07Ssthen } 1626933707f3Ssthen mstate->replies_sent = 1; 16278b7325afSsthen 162820237c55Ssthen while((c = mstate->cb_list) != NULL) { 162920237c55Ssthen /* take this cb off the list; so that the list can be 163020237c55Ssthen * changed, eg. by adds from the callback routine */ 163120237c55Ssthen if(!mstate->reply_list && mstate->cb_list && !c->next) { 163220237c55Ssthen /* was a reply state, not anymore */ 1633eaf2578eSsthen log_assert(mstate->s.env->mesh->num_reply_states > 0); 163420237c55Ssthen mstate->s.env->mesh->num_reply_states--; 163520237c55Ssthen } 163620237c55Ssthen mstate->cb_list = c->next; 163720237c55Ssthen if(!mstate->reply_list && !mstate->cb_list && 163820237c55Ssthen mstate->super_set.count == 0) 163920237c55Ssthen mstate->s.env->mesh->num_detached_states++; 16409982a05dSsthen mesh_do_callback(mstate, mstate->s.return_rcode, rep, c, &tv); 1641933707f3Ssthen } 1642933707f3Ssthen } 1643933707f3Ssthen 1644933707f3Ssthen void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate) 1645933707f3Ssthen { 1646933707f3Ssthen struct mesh_state_ref* ref; 1647933707f3Ssthen RBTREE_FOR(ref, struct mesh_state_ref*, &mstate->super_set) 1648933707f3Ssthen { 1649933707f3Ssthen /* make super runnable */ 1650933707f3Ssthen (void)rbtree_insert(&mesh->run, &ref->s->run_node); 1651933707f3Ssthen /* callback the function to inform super of result */ 1652933707f3Ssthen fptr_ok(fptr_whitelist_mod_inform_super( 1653933707f3Ssthen mesh->mods.mod[ref->s->s.curmod]->inform_super)); 1654933707f3Ssthen (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s, 1655933707f3Ssthen ref->s->s.curmod, &ref->s->s); 16562308e98cSsthen /* copy state that is always relevant to super */ 16572308e98cSsthen copy_state_to_super(&mstate->s, ref->s->s.curmod, &ref->s->s); 1658933707f3Ssthen } 1659933707f3Ssthen } 1660933707f3Ssthen 1661933707f3Ssthen struct mesh_state* mesh_area_find(struct mesh_area* mesh, 16622be9e038Ssthen struct respip_client_info* cinfo, struct query_info* qinfo, 16632be9e038Ssthen uint16_t qflags, int prime, int valrec) 1664933707f3Ssthen { 1665933707f3Ssthen struct mesh_state key; 1666933707f3Ssthen struct mesh_state* result; 1667933707f3Ssthen 1668933707f3Ssthen key.node.key = &key; 1669933707f3Ssthen key.s.is_priming = prime; 167057dceb2aSbrad key.s.is_valrec = valrec; 1671933707f3Ssthen key.s.qinfo = *qinfo; 1672933707f3Ssthen key.s.query_flags = qflags; 167377079be7Ssthen /* We are searching for a similar mesh state when we DO want to 167477079be7Ssthen * aggregate the state. Thus unique is set to NULL. (default when we 167577079be7Ssthen * desire aggregation).*/ 167677079be7Ssthen key.unique = NULL; 16772be9e038Ssthen key.s.client_info = cinfo; 1678933707f3Ssthen 1679933707f3Ssthen result = (struct mesh_state*)rbtree_search(&mesh->all, &key); 1680933707f3Ssthen return result; 1681933707f3Ssthen } 1682933707f3Ssthen 1683933707f3Ssthen int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, 168477079be7Ssthen sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, 1685933707f3Ssthen uint16_t qid, uint16_t qflags) 1686933707f3Ssthen { 1687933707f3Ssthen struct mesh_cb* r = regional_alloc(s->s.region, 1688933707f3Ssthen sizeof(struct mesh_cb)); 1689933707f3Ssthen if(!r) 1690933707f3Ssthen return 0; 1691933707f3Ssthen r->buf = buf; 1692933707f3Ssthen log_assert(fptr_whitelist_mesh_cb(cb)); /* early failure ifmissing*/ 1693933707f3Ssthen r->cb = cb; 1694933707f3Ssthen r->cb_arg = cb_arg; 1695933707f3Ssthen r->edns = *edns; 1696e21c60efSsthen if(edns->opt_list_in && !(r->edns.opt_list_in = 1697e21c60efSsthen edns_opt_copy_region(edns->opt_list_in, s->s.region))) 16982ee382b6Ssthen return 0; 1699e21c60efSsthen if(edns->opt_list_out && !(r->edns.opt_list_out = 1700e21c60efSsthen edns_opt_copy_region(edns->opt_list_out, s->s.region))) 1701e21c60efSsthen return 0; 1702e21c60efSsthen if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out = 1703e21c60efSsthen edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region))) 1704e21c60efSsthen return 0; 1705933707f3Ssthen r->qid = qid; 1706933707f3Ssthen r->qflags = qflags; 1707933707f3Ssthen r->next = s->cb_list; 1708933707f3Ssthen s->cb_list = r; 1709933707f3Ssthen return 1; 1710933707f3Ssthen 1711933707f3Ssthen } 1712933707f3Ssthen 1713933707f3Ssthen int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, 171477079be7Ssthen struct comm_reply* rep, uint16_t qid, uint16_t qflags, 171577079be7Ssthen const struct query_info* qinfo) 1716933707f3Ssthen { 1717933707f3Ssthen struct mesh_reply* r = regional_alloc(s->s.region, 1718933707f3Ssthen sizeof(struct mesh_reply)); 1719933707f3Ssthen if(!r) 1720933707f3Ssthen return 0; 1721933707f3Ssthen r->query_reply = *rep; 1722933707f3Ssthen r->edns = *edns; 1723e21c60efSsthen if(edns->opt_list_in && !(r->edns.opt_list_in = 1724e21c60efSsthen edns_opt_copy_region(edns->opt_list_in, s->s.region))) 17252ee382b6Ssthen return 0; 1726e21c60efSsthen if(edns->opt_list_out && !(r->edns.opt_list_out = 1727e21c60efSsthen edns_opt_copy_region(edns->opt_list_out, s->s.region))) 1728e21c60efSsthen return 0; 1729e21c60efSsthen if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out = 1730e21c60efSsthen edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region))) 1731e21c60efSsthen return 0; 1732933707f3Ssthen r->qid = qid; 1733933707f3Ssthen r->qflags = qflags; 1734933707f3Ssthen r->start_time = *s->s.env->now_tv; 1735933707f3Ssthen r->next = s->reply_list; 173677079be7Ssthen r->qname = regional_alloc_init(s->s.region, qinfo->qname, 1737933707f3Ssthen s->s.qinfo.qname_len); 1738933707f3Ssthen if(!r->qname) 1739933707f3Ssthen return 0; 17402c144df0Ssthen if(rep->c->use_h2) 17412c144df0Ssthen r->h2_stream = rep->c->h2_stream; 1742*98bc733bSsthen else r->h2_stream = NULL; 174377079be7Ssthen 174477079be7Ssthen /* Data related to local alias stored in 'qinfo' (if any) is ephemeral 174577079be7Ssthen * and can be different for different original queries (even if the 174677079be7Ssthen * replaced query name is the same). So we need to make a deep copy 174777079be7Ssthen * and store the copy for each reply info. */ 174877079be7Ssthen if(qinfo->local_alias) { 174977079be7Ssthen struct packed_rrset_data* d; 175077079be7Ssthen struct packed_rrset_data* dsrc; 175177079be7Ssthen r->local_alias = regional_alloc_zero(s->s.region, 175277079be7Ssthen sizeof(*qinfo->local_alias)); 175377079be7Ssthen if(!r->local_alias) 175477079be7Ssthen return 0; 175577079be7Ssthen r->local_alias->rrset = regional_alloc_init(s->s.region, 175677079be7Ssthen qinfo->local_alias->rrset, 175777079be7Ssthen sizeof(*qinfo->local_alias->rrset)); 175877079be7Ssthen if(!r->local_alias->rrset) 175977079be7Ssthen return 0; 176077079be7Ssthen dsrc = qinfo->local_alias->rrset->entry.data; 176177079be7Ssthen 176277079be7Ssthen /* In the current implementation, a local alias must be 176377079be7Ssthen * a single CNAME RR (see worker_handle_request()). */ 176477079be7Ssthen log_assert(!qinfo->local_alias->next && dsrc->count == 1 && 176577079be7Ssthen qinfo->local_alias->rrset->rk.type == 176677079be7Ssthen htons(LDNS_RR_TYPE_CNAME)); 17678240c1b9Ssthen /* we should make a local copy for the owner name of 17688240c1b9Ssthen * the RRset */ 17698240c1b9Ssthen r->local_alias->rrset->rk.dname_len = 17708240c1b9Ssthen qinfo->local_alias->rrset->rk.dname_len; 17718240c1b9Ssthen r->local_alias->rrset->rk.dname = regional_alloc_init( 17728240c1b9Ssthen s->s.region, qinfo->local_alias->rrset->rk.dname, 17738240c1b9Ssthen qinfo->local_alias->rrset->rk.dname_len); 17748240c1b9Ssthen if(!r->local_alias->rrset->rk.dname) 17758240c1b9Ssthen return 0; 177677079be7Ssthen 1777550cf4a9Ssthen /* the rrset is not packed, like in the cache, but it is 1778e21c60efSsthen * individually allocated with an allocator from localzone. */ 1779550cf4a9Ssthen d = regional_alloc_zero(s->s.region, sizeof(*d)); 178077079be7Ssthen if(!d) 178177079be7Ssthen return 0; 178277079be7Ssthen r->local_alias->rrset->entry.data = d; 1783550cf4a9Ssthen if(!rrset_insert_rr(s->s.region, d, dsrc->rr_data[0], 1784550cf4a9Ssthen dsrc->rr_len[0], dsrc->rr_ttl[0], "CNAME local alias")) 178577079be7Ssthen return 0; 178677079be7Ssthen } else 178777079be7Ssthen r->local_alias = NULL; 178877079be7Ssthen 1789933707f3Ssthen s->reply_list = r; 1790933707f3Ssthen return 1; 1791933707f3Ssthen } 1792933707f3Ssthen 1793bdfc4d55Sflorian /* Extract the query info and flags from 'mstate' into '*qinfop' and '*qflags'. 1794bdfc4d55Sflorian * Since this is only used for internal refetch of otherwise-expired answer, 1795bdfc4d55Sflorian * we simply ignore the rare failure mode when memory allocation fails. */ 1796bdfc4d55Sflorian static void 1797bdfc4d55Sflorian mesh_copy_qinfo(struct mesh_state* mstate, struct query_info** qinfop, 1798bdfc4d55Sflorian uint16_t* qflags) 1799bdfc4d55Sflorian { 1800bdfc4d55Sflorian struct regional* region = mstate->s.env->scratch; 1801bdfc4d55Sflorian struct query_info* qinfo; 1802bdfc4d55Sflorian 1803bdfc4d55Sflorian qinfo = regional_alloc_init(region, &mstate->s.qinfo, sizeof(*qinfo)); 1804bdfc4d55Sflorian if(!qinfo) 1805bdfc4d55Sflorian return; 1806bdfc4d55Sflorian qinfo->qname = regional_alloc_init(region, qinfo->qname, 1807bdfc4d55Sflorian qinfo->qname_len); 1808bdfc4d55Sflorian if(!qinfo->qname) 1809bdfc4d55Sflorian return; 1810bdfc4d55Sflorian *qinfop = qinfo; 1811bdfc4d55Sflorian *qflags = mstate->s.query_flags; 1812bdfc4d55Sflorian } 1813bdfc4d55Sflorian 1814933707f3Ssthen /** 1815933707f3Ssthen * Continue processing the mesh state at another module. 1816bdfc4d55Sflorian * Handles module to modules transfer of control. 1817933707f3Ssthen * Handles module finished. 1818933707f3Ssthen * @param mesh: the mesh area. 1819933707f3Ssthen * @param mstate: currently active mesh state. 1820933707f3Ssthen * Deleted if finished, calls _done and _supers to 1821933707f3Ssthen * send replies to clients and inform other mesh states. 1822933707f3Ssthen * This in turn may create additional runnable mesh states. 1823933707f3Ssthen * @param s: state at which the current module exited. 1824933707f3Ssthen * @param ev: the event sent to the module. 1825933707f3Ssthen * returned is the event to send to the next module. 1826933707f3Ssthen * @return true if continue processing at the new module. 1827933707f3Ssthen * false if not continued processing is needed. 1828933707f3Ssthen */ 1829933707f3Ssthen static int 1830933707f3Ssthen mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, 1831933707f3Ssthen enum module_ext_state s, enum module_ev* ev) 1832933707f3Ssthen { 1833933707f3Ssthen mstate->num_activated++; 1834933707f3Ssthen if(mstate->num_activated > MESH_MAX_ACTIVATION) { 1835933707f3Ssthen /* module is looping. Stop it. */ 1836bdfc4d55Sflorian log_err("internal error: looping module (%s) stopped", 1837bdfc4d55Sflorian mesh->mods.mod[mstate->s.curmod]->name); 1838ebf5bb73Ssthen log_query_info(NO_VERBOSE, "pass error for qstate", 1839933707f3Ssthen &mstate->s.qinfo); 1840933707f3Ssthen s = module_error; 1841933707f3Ssthen } 1842933707f3Ssthen if(s == module_wait_module || s == module_restart_next) { 1843933707f3Ssthen /* start next module */ 1844933707f3Ssthen mstate->s.curmod++; 1845933707f3Ssthen if(mesh->mods.num == mstate->s.curmod) { 1846933707f3Ssthen log_err("Cannot pass to next module; at last module"); 1847933707f3Ssthen log_query_info(VERB_QUERY, "pass error for qstate", 1848933707f3Ssthen &mstate->s.qinfo); 1849933707f3Ssthen mstate->s.curmod--; 1850933707f3Ssthen return mesh_continue(mesh, mstate, module_error, ev); 1851933707f3Ssthen } 1852933707f3Ssthen if(s == module_restart_next) { 18532be9e038Ssthen int curmod = mstate->s.curmod; 18542be9e038Ssthen for(; mstate->s.curmod < mesh->mods.num; 18552be9e038Ssthen mstate->s.curmod++) { 1856933707f3Ssthen fptr_ok(fptr_whitelist_mod_clear( 1857933707f3Ssthen mesh->mods.mod[mstate->s.curmod]->clear)); 1858933707f3Ssthen (*mesh->mods.mod[mstate->s.curmod]->clear) 1859933707f3Ssthen (&mstate->s, mstate->s.curmod); 1860933707f3Ssthen mstate->s.minfo[mstate->s.curmod] = NULL; 1861933707f3Ssthen } 18622be9e038Ssthen mstate->s.curmod = curmod; 18632be9e038Ssthen } 1864933707f3Ssthen *ev = module_event_pass; 1865933707f3Ssthen return 1; 1866933707f3Ssthen } 1867e024f976Ssthen if(s == module_wait_subquery && mstate->sub_set.count == 0) { 1868e024f976Ssthen log_err("module cannot wait for subquery, subquery list empty"); 1869e024f976Ssthen log_query_info(VERB_QUERY, "pass error for qstate", 1870e024f976Ssthen &mstate->s.qinfo); 1871e024f976Ssthen s = module_error; 1872e024f976Ssthen } 1873933707f3Ssthen if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) { 1874933707f3Ssthen /* error is bad, handle pass back up below */ 1875933707f3Ssthen mstate->s.return_rcode = LDNS_RCODE_SERVFAIL; 1876933707f3Ssthen } 1877bdfc4d55Sflorian if(s == module_error) { 1878933707f3Ssthen mesh_query_done(mstate); 1879933707f3Ssthen mesh_walk_supers(mesh, mstate); 1880933707f3Ssthen mesh_state_delete(&mstate->s); 1881933707f3Ssthen return 0; 1882933707f3Ssthen } 1883bdfc4d55Sflorian if(s == module_finished) { 1884bdfc4d55Sflorian if(mstate->s.curmod == 0) { 1885bdfc4d55Sflorian struct query_info* qinfo = NULL; 18868b7325afSsthen struct edns_option* opt_list = NULL; 18878b7325afSsthen struct sockaddr_storage addr; 1888bdfc4d55Sflorian uint16_t qflags; 18890bdb4f62Ssthen int rpz_p = 0; 1890bdfc4d55Sflorian 18918b7325afSsthen #ifdef CLIENT_SUBNET 18928b7325afSsthen struct edns_option* ecs; 18938b7325afSsthen if(mstate->s.need_refetch && mstate->reply_list && 18948b7325afSsthen modstack_find(&mesh->mods, "subnetcache") != -1 && 18958b7325afSsthen mstate->s.env->unique_mesh) { 18968b7325afSsthen addr = mstate->reply_list->query_reply.client_addr; 18978b7325afSsthen } else 18988b7325afSsthen #endif 18998b7325afSsthen memset(&addr, 0, sizeof(addr)); 19008b7325afSsthen 1901bdfc4d55Sflorian mesh_query_done(mstate); 1902bdfc4d55Sflorian mesh_walk_supers(mesh, mstate); 1903bdfc4d55Sflorian 1904bdfc4d55Sflorian /* If the answer to the query needs to be refetched 1905bdfc4d55Sflorian * from an external DNS server, we'll need to schedule 1906bdfc4d55Sflorian * a prefetch after removing the current state, so 1907bdfc4d55Sflorian * we need to make a copy of the query info here. */ 19080bdb4f62Ssthen if(mstate->s.need_refetch) { 1909bdfc4d55Sflorian mesh_copy_qinfo(mstate, &qinfo, &qflags); 19108b7325afSsthen #ifdef CLIENT_SUBNET 19118b7325afSsthen /* Make also a copy of the ecs option if any */ 19128b7325afSsthen if((ecs = edns_opt_list_find( 19138b7325afSsthen mstate->s.edns_opts_front_in, 19148b7325afSsthen mstate->s.env->cfg->client_subnet_opcode)) != NULL) { 19158b7325afSsthen (void)edns_opt_list_append(&opt_list, 19168b7325afSsthen ecs->opt_code, ecs->opt_len, 19178b7325afSsthen ecs->opt_data, 19188b7325afSsthen mstate->s.env->scratch); 19198b7325afSsthen } 19208b7325afSsthen #endif 19210bdb4f62Ssthen rpz_p = mstate->s.rpz_passthru; 19220bdb4f62Ssthen } 1923bdfc4d55Sflorian 1924bdfc4d55Sflorian if(qinfo) { 19258b7325afSsthen mesh_state_delete(&mstate->s); 19268b7325afSsthen mesh_new_prefetch(mesh, qinfo, qflags, 0, 19278b7325afSsthen rpz_p, 19288b7325afSsthen addr.ss_family!=AF_UNSPEC?&addr:NULL, 19298b7325afSsthen opt_list); 19308b7325afSsthen } else { 19318b7325afSsthen mesh_state_delete(&mstate->s); 1932bdfc4d55Sflorian } 1933bdfc4d55Sflorian return 0; 1934bdfc4d55Sflorian } 1935933707f3Ssthen /* pass along the locus of control */ 1936933707f3Ssthen mstate->s.curmod --; 1937933707f3Ssthen *ev = module_event_moddone; 1938933707f3Ssthen return 1; 1939933707f3Ssthen } 1940933707f3Ssthen return 0; 1941933707f3Ssthen } 1942933707f3Ssthen 1943933707f3Ssthen void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate, 1944933707f3Ssthen enum module_ev ev, struct outbound_entry* e) 1945933707f3Ssthen { 1946933707f3Ssthen enum module_ext_state s; 1947933707f3Ssthen verbose(VERB_ALGO, "mesh_run: start"); 1948933707f3Ssthen while(mstate) { 1949933707f3Ssthen /* run the module */ 1950933707f3Ssthen fptr_ok(fptr_whitelist_mod_operate( 1951933707f3Ssthen mesh->mods.mod[mstate->s.curmod]->operate)); 1952933707f3Ssthen (*mesh->mods.mod[mstate->s.curmod]->operate) 1953933707f3Ssthen (&mstate->s, ev, mstate->s.curmod, e); 1954933707f3Ssthen 1955933707f3Ssthen /* examine results */ 1956933707f3Ssthen mstate->s.reply = NULL; 1957933707f3Ssthen regional_free_all(mstate->s.env->scratch); 1958933707f3Ssthen s = mstate->s.ext_state[mstate->s.curmod]; 1959933707f3Ssthen verbose(VERB_ALGO, "mesh_run: %s module exit state is %s", 1960933707f3Ssthen mesh->mods.mod[mstate->s.curmod]->name, strextstate(s)); 1961933707f3Ssthen e = NULL; 1962933707f3Ssthen if(mesh_continue(mesh, mstate, s, &ev)) 1963933707f3Ssthen continue; 1964933707f3Ssthen 1965933707f3Ssthen /* run more modules */ 1966933707f3Ssthen ev = module_event_pass; 1967933707f3Ssthen if(mesh->run.count > 0) { 1968933707f3Ssthen /* pop random element off the runnable tree */ 1969933707f3Ssthen mstate = (struct mesh_state*)mesh->run.root->key; 1970933707f3Ssthen (void)rbtree_delete(&mesh->run, mstate); 1971933707f3Ssthen } else mstate = NULL; 1972933707f3Ssthen } 1973933707f3Ssthen if(verbosity >= VERB_ALGO) { 1974933707f3Ssthen mesh_stats(mesh, "mesh_run: end"); 1975933707f3Ssthen mesh_log_list(mesh); 1976933707f3Ssthen } 1977933707f3Ssthen } 1978933707f3Ssthen 1979933707f3Ssthen void 1980933707f3Ssthen mesh_log_list(struct mesh_area* mesh) 1981933707f3Ssthen { 1982933707f3Ssthen char buf[30]; 1983933707f3Ssthen struct mesh_state* m; 1984933707f3Ssthen int num = 0; 1985933707f3Ssthen RBTREE_FOR(m, struct mesh_state*, &mesh->all) { 198657dceb2aSbrad snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s", 1987933707f3Ssthen num++, (m->s.is_priming)?"p":"", /* prime */ 198857dceb2aSbrad (m->s.is_valrec)?"v":"", /* prime */ 1989933707f3Ssthen (m->s.query_flags&BIT_RD)?"RD":"", 1990933707f3Ssthen (m->s.query_flags&BIT_CD)?"CD":"", 1991933707f3Ssthen (m->super_set.count==0)?"d":"", /* detached */ 1992933707f3Ssthen (m->sub_set.count!=0)?"c":"", /* children */ 1993933707f3Ssthen m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/ 1994933707f3Ssthen (m->cb_list)?"cb":"" /* callbacks */ 1995933707f3Ssthen ); 1996933707f3Ssthen log_query_info(VERB_ALGO, buf, &m->s.qinfo); 1997933707f3Ssthen } 1998933707f3Ssthen } 1999933707f3Ssthen 2000933707f3Ssthen void 2001933707f3Ssthen mesh_stats(struct mesh_area* mesh, const char* str) 2002933707f3Ssthen { 2003933707f3Ssthen verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, " 2004933707f3Ssthen "%u detached), %u waiting replies, %u recursion replies " 2005933707f3Ssthen "sent, %d replies dropped, %d states jostled out", 2006933707f3Ssthen str, (unsigned)mesh->all.count, 2007933707f3Ssthen (unsigned)mesh->num_reply_states, 2008933707f3Ssthen (unsigned)mesh->num_detached_states, 2009933707f3Ssthen (unsigned)mesh->num_reply_addrs, 2010933707f3Ssthen (unsigned)mesh->replies_sent, 2011933707f3Ssthen (unsigned)mesh->stats_dropped, 2012933707f3Ssthen (unsigned)mesh->stats_jostled); 2013933707f3Ssthen if(mesh->replies_sent > 0) { 2014933707f3Ssthen struct timeval avg; 2015933707f3Ssthen timeval_divide(&avg, &mesh->replies_sum_wait, 2016933707f3Ssthen mesh->replies_sent); 2017933707f3Ssthen log_info("average recursion processing time " 20185d76a658Ssthen ARG_LL "d.%6.6d sec", 20195d76a658Ssthen (long long)avg.tv_sec, (int)avg.tv_usec); 2020933707f3Ssthen log_info("histogram of recursion processing times"); 2021933707f3Ssthen timehist_log(mesh->histogram, "recursions"); 2022933707f3Ssthen } 2023933707f3Ssthen } 2024933707f3Ssthen 2025933707f3Ssthen void 2026933707f3Ssthen mesh_stats_clear(struct mesh_area* mesh) 2027933707f3Ssthen { 2028933707f3Ssthen if(!mesh) 2029933707f3Ssthen return; 2030933707f3Ssthen mesh->replies_sent = 0; 2031933707f3Ssthen mesh->replies_sum_wait.tv_sec = 0; 2032933707f3Ssthen mesh->replies_sum_wait.tv_usec = 0; 2033933707f3Ssthen mesh->stats_jostled = 0; 2034933707f3Ssthen mesh->stats_dropped = 0; 2035933707f3Ssthen timehist_clear(mesh->histogram); 2036933707f3Ssthen mesh->ans_secure = 0; 2037933707f3Ssthen mesh->ans_bogus = 0; 2038eaf2578eSsthen mesh->ans_expired = 0; 20398b7325afSsthen mesh->ans_cachedb = 0; 2040eaf2578eSsthen memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM); 2041eaf2578eSsthen memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM); 2042933707f3Ssthen mesh->ans_nodata = 0; 2043933707f3Ssthen } 2044933707f3Ssthen 2045933707f3Ssthen size_t 2046933707f3Ssthen mesh_get_mem(struct mesh_area* mesh) 2047933707f3Ssthen { 2048933707f3Ssthen struct mesh_state* m; 2049933707f3Ssthen size_t s = sizeof(*mesh) + sizeof(struct timehist) + 2050933707f3Ssthen sizeof(struct th_buck)*mesh->histogram->num + 20515d76a658Ssthen sizeof(sldns_buffer) + sldns_buffer_capacity(mesh->qbuf_bak); 2052933707f3Ssthen RBTREE_FOR(m, struct mesh_state*, &mesh->all) { 2053933707f3Ssthen /* all, including m itself allocated in qstate region */ 2054933707f3Ssthen s += regional_get_mem(m->s.region); 2055933707f3Ssthen } 2056933707f3Ssthen return s; 2057933707f3Ssthen } 2058933707f3Ssthen 2059933707f3Ssthen int 2060933707f3Ssthen mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, 206157dceb2aSbrad uint16_t flags, int prime, int valrec) 2062933707f3Ssthen { 2063933707f3Ssthen struct mesh_area* mesh = qstate->env->mesh; 206477079be7Ssthen struct mesh_state* dep_m = NULL; 20652be9e038Ssthen dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); 2066933707f3Ssthen return mesh_detect_cycle_found(qstate, dep_m); 2067933707f3Ssthen } 2068933707f3Ssthen 2069933707f3Ssthen void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp, 2070933707f3Ssthen struct mesh_state** lp) 2071933707f3Ssthen { 2072933707f3Ssthen /* insert as last element */ 2073933707f3Ssthen m->prev = *lp; 2074933707f3Ssthen m->next = NULL; 2075933707f3Ssthen if(*lp) 2076933707f3Ssthen (*lp)->next = m; 2077933707f3Ssthen else *fp = m; 2078933707f3Ssthen *lp = m; 2079933707f3Ssthen } 2080933707f3Ssthen 2081933707f3Ssthen void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp, 2082933707f3Ssthen struct mesh_state** lp) 2083933707f3Ssthen { 2084933707f3Ssthen if(m->next) 2085933707f3Ssthen m->next->prev = m->prev; 2086933707f3Ssthen else *lp = m->prev; 2087933707f3Ssthen if(m->prev) 2088933707f3Ssthen m->prev->next = m->next; 2089933707f3Ssthen else *fp = m->next; 2090933707f3Ssthen } 2091f6b99bafSsthen 2092f6b99bafSsthen void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, 2093f6b99bafSsthen struct comm_point* cp) 2094f6b99bafSsthen { 2095f6b99bafSsthen struct mesh_reply* n, *prev = NULL; 2096f6b99bafSsthen n = m->reply_list; 2097f6b99bafSsthen /* when in mesh_cleanup, it sets the reply_list to NULL, so that 2098f6b99bafSsthen * there is no accounting twice */ 2099f6b99bafSsthen if(!n) return; /* nothing to remove, also no accounting needed */ 2100f6b99bafSsthen while(n) { 2101f6b99bafSsthen if(n->query_reply.c == cp) { 2102f6b99bafSsthen /* unlink it */ 2103f6b99bafSsthen if(prev) prev->next = n->next; 2104f6b99bafSsthen else m->reply_list = n->next; 2105f6b99bafSsthen /* delete it, but allocated in m region */ 2106eaf2578eSsthen log_assert(mesh->num_reply_addrs > 0); 2107f6b99bafSsthen mesh->num_reply_addrs--; 21082bdc0ed1Ssthen infra_wait_limit_dec(mesh->env->infra_cache, 21092bdc0ed1Ssthen &n->query_reply, mesh->env->cfg); 2110f6b99bafSsthen 2111f6b99bafSsthen /* prev = prev; */ 2112f6b99bafSsthen n = n->next; 2113f6b99bafSsthen continue; 2114f6b99bafSsthen } 2115f6b99bafSsthen prev = n; 2116f6b99bafSsthen n = n->next; 2117f6b99bafSsthen } 2118f6b99bafSsthen /* it was not detached (because it had a reply list), could be now */ 2119f6b99bafSsthen if(!m->reply_list && !m->cb_list 2120f6b99bafSsthen && m->super_set.count == 0) { 2121f6b99bafSsthen mesh->num_detached_states++; 2122f6b99bafSsthen } 2123f6b99bafSsthen /* if not replies any more in mstate, it is no longer a reply_state */ 2124f6b99bafSsthen if(!m->reply_list && !m->cb_list) { 2125f6b99bafSsthen log_assert(mesh->num_reply_states > 0); 2126f6b99bafSsthen mesh->num_reply_states--; 2127f6b99bafSsthen } 2128f6b99bafSsthen } 2129eaf2578eSsthen 2130eaf2578eSsthen 2131eaf2578eSsthen static int 2132eaf2578eSsthen apply_respip_action(struct module_qstate* qstate, 2133eaf2578eSsthen const struct query_info* qinfo, struct respip_client_info* cinfo, 2134eaf2578eSsthen struct respip_action_info* actinfo, struct reply_info* rep, 2135eaf2578eSsthen struct ub_packed_rrset_key** alias_rrset, 2136eaf2578eSsthen struct reply_info** encode_repp, struct auth_zones* az) 2137eaf2578eSsthen { 2138eaf2578eSsthen if(qinfo->qtype != LDNS_RR_TYPE_A && 2139eaf2578eSsthen qinfo->qtype != LDNS_RR_TYPE_AAAA && 2140eaf2578eSsthen qinfo->qtype != LDNS_RR_TYPE_ANY) 2141eaf2578eSsthen return 1; 2142eaf2578eSsthen 2143eaf2578eSsthen if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo, 21440bdb4f62Ssthen alias_rrset, 0, qstate->region, az, NULL)) 2145eaf2578eSsthen return 0; 2146eaf2578eSsthen 2147eaf2578eSsthen /* xxx_deny actions mean dropping the reply, unless the original reply 2148eaf2578eSsthen * was redirected to response-ip data. */ 2149eaf2578eSsthen if((actinfo->action == respip_deny || 2150eaf2578eSsthen actinfo->action == respip_inform_deny) && 2151eaf2578eSsthen *encode_repp == rep) 2152eaf2578eSsthen *encode_repp = NULL; 2153eaf2578eSsthen 2154eaf2578eSsthen return 1; 2155eaf2578eSsthen } 2156eaf2578eSsthen 2157eaf2578eSsthen void 2158eaf2578eSsthen mesh_serve_expired_callback(void* arg) 2159eaf2578eSsthen { 2160eaf2578eSsthen struct mesh_state* mstate = (struct mesh_state*) arg; 2161eaf2578eSsthen struct module_qstate* qstate = &mstate->s; 2162eba819a2Ssthen struct mesh_reply* r; 2163eaf2578eSsthen struct mesh_area* mesh = qstate->env->mesh; 2164eaf2578eSsthen struct dns_msg* msg; 2165eaf2578eSsthen struct mesh_cb* c; 2166eaf2578eSsthen struct mesh_reply* prev = NULL; 2167eaf2578eSsthen struct sldns_buffer* prev_buffer = NULL; 2168eaf2578eSsthen struct sldns_buffer* r_buffer = NULL; 2169eaf2578eSsthen struct reply_info* partial_rep = NULL; 2170eaf2578eSsthen struct ub_packed_rrset_key* alias_rrset = NULL; 2171eaf2578eSsthen struct reply_info* encode_rep = NULL; 2172eaf2578eSsthen struct respip_action_info actinfo; 2173eaf2578eSsthen struct query_info* lookup_qinfo = &qstate->qinfo; 2174eaf2578eSsthen struct query_info qinfo_tmp; 21759982a05dSsthen struct timeval tv = {0, 0}; 2176eaf2578eSsthen int must_validate = (!(qstate->query_flags&BIT_CD) 2177eaf2578eSsthen || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; 21788b7325afSsthen int i = 0; 2179eaf2578eSsthen if(!qstate->serve_expired_data) return; 2180eaf2578eSsthen verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data"); 2181eaf2578eSsthen comm_timer_delete(qstate->serve_expired_data->timer); 2182eaf2578eSsthen qstate->serve_expired_data->timer = NULL; 21839982a05dSsthen /* If is_drop or no_cache_lookup (modules that handle their own cache e.g., 21849982a05dSsthen * subnetmod) ignore stale data from the main cache. */ 21859982a05dSsthen if(qstate->no_cache_lookup || qstate->is_drop) { 2186eaf2578eSsthen verbose(VERB_ALGO, 2187eaf2578eSsthen "Serve expired: Not allowed to look into cache for stale"); 2188eaf2578eSsthen return; 2189eaf2578eSsthen } 2190eaf2578eSsthen /* The following while is used instead of the `goto lookup_cache` 2191eaf2578eSsthen * like in the worker. */ 2192eaf2578eSsthen while(1) { 2193eaf2578eSsthen fptr_ok(fptr_whitelist_serve_expired_lookup( 2194eaf2578eSsthen qstate->serve_expired_data->get_cached_answer)); 2195191f22c6Ssthen msg = (*qstate->serve_expired_data->get_cached_answer)(qstate, 2196eaf2578eSsthen lookup_qinfo); 2197eaf2578eSsthen if(!msg) 2198eaf2578eSsthen return; 2199eaf2578eSsthen /* Reset these in case we pass a second time from here. */ 2200eaf2578eSsthen encode_rep = msg->rep; 2201eaf2578eSsthen memset(&actinfo, 0, sizeof(actinfo)); 2202eaf2578eSsthen actinfo.action = respip_none; 2203eaf2578eSsthen alias_rrset = NULL; 2204eaf2578eSsthen if((mesh->use_response_ip || mesh->use_rpz) && 2205eaf2578eSsthen !partial_rep && !apply_respip_action(qstate, &qstate->qinfo, 2206eaf2578eSsthen qstate->client_info, &actinfo, msg->rep, &alias_rrset, &encode_rep, 2207eaf2578eSsthen qstate->env->auth_zones)) { 2208eaf2578eSsthen return; 2209eaf2578eSsthen } else if(partial_rep && 2210eaf2578eSsthen !respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep, 2211eaf2578eSsthen qstate->client_info, must_validate, &encode_rep, qstate->region, 2212eaf2578eSsthen qstate->env->auth_zones)) { 2213eaf2578eSsthen return; 2214eaf2578eSsthen } 2215eaf2578eSsthen if(!encode_rep || alias_rrset) { 2216eaf2578eSsthen if(!encode_rep) { 2217eaf2578eSsthen /* Needs drop */ 2218eaf2578eSsthen return; 2219eaf2578eSsthen } else { 2220eaf2578eSsthen /* A partial CNAME chain is found. */ 2221eaf2578eSsthen partial_rep = encode_rep; 2222eaf2578eSsthen } 2223eaf2578eSsthen } 2224eaf2578eSsthen /* We've found a partial reply ending with an 2225eaf2578eSsthen * alias. Replace the lookup qinfo for the 2226eaf2578eSsthen * alias target and lookup the cache again to 2227eaf2578eSsthen * (possibly) complete the reply. As we're 2228eaf2578eSsthen * passing the "base" reply, there will be no 2229eaf2578eSsthen * more alias chasing. */ 2230eaf2578eSsthen if(partial_rep) { 2231eaf2578eSsthen memset(&qinfo_tmp, 0, sizeof(qinfo_tmp)); 2232eaf2578eSsthen get_cname_target(alias_rrset, &qinfo_tmp.qname, 2233eaf2578eSsthen &qinfo_tmp.qname_len); 2234eaf2578eSsthen if(!qinfo_tmp.qname) { 2235eaf2578eSsthen log_err("Serve expired: unexpected: invalid answer alias"); 2236eaf2578eSsthen return; 2237eaf2578eSsthen } 2238eaf2578eSsthen qinfo_tmp.qtype = qstate->qinfo.qtype; 2239eaf2578eSsthen qinfo_tmp.qclass = qstate->qinfo.qclass; 2240eaf2578eSsthen lookup_qinfo = &qinfo_tmp; 2241eaf2578eSsthen continue; 2242eaf2578eSsthen } 2243eaf2578eSsthen break; 2244eaf2578eSsthen } 2245eaf2578eSsthen 2246eaf2578eSsthen if(verbosity >= VERB_ALGO) 2247eaf2578eSsthen log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); 2248eaf2578eSsthen 2249a3167c07Ssthen for(r = mstate->reply_list; r; r = r->next) { 22502bdc0ed1Ssthen struct timeval old; 22512bdc0ed1Ssthen timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); 22522bdc0ed1Ssthen if(mstate->s.env->cfg->discard_timeout != 0 && 22532bdc0ed1Ssthen ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > 22542bdc0ed1Ssthen mstate->s.env->cfg->discard_timeout) { 22552bdc0ed1Ssthen /* Drop the reply, it is too old */ 22562bdc0ed1Ssthen /* briefly set the reply_list to NULL, so that the 22572bdc0ed1Ssthen * tcp req info cleanup routine that calls the mesh 22582bdc0ed1Ssthen * to deregister the meshstate for it is not done 22592bdc0ed1Ssthen * because the list is NULL and also accounting is not 22602bdc0ed1Ssthen * done there, but instead we do that here. */ 22612bdc0ed1Ssthen struct mesh_reply* reply_list = mstate->reply_list; 22622bdc0ed1Ssthen verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); 22632bdc0ed1Ssthen infra_wait_limit_dec(mstate->s.env->infra_cache, 22642bdc0ed1Ssthen &r->query_reply, mstate->s.env->cfg); 22652bdc0ed1Ssthen mstate->reply_list = NULL; 2266*98bc733bSsthen if(r->query_reply.c->use_h2) 2267*98bc733bSsthen http2_stream_remove_mesh_state(r->h2_stream); 22682bdc0ed1Ssthen comm_point_drop_reply(&r->query_reply); 22692bdc0ed1Ssthen mstate->reply_list = reply_list; 22702bdc0ed1Ssthen mstate->s.env->mesh->stats_dropped++; 22712bdc0ed1Ssthen continue; 22722bdc0ed1Ssthen } 22732bdc0ed1Ssthen 22748b7325afSsthen i++; 22759982a05dSsthen tv = r->start_time; 22769982a05dSsthen 2277eaf2578eSsthen /* If address info is returned, it means the action should be an 2278eaf2578eSsthen * 'inform' variant and the information should be logged. */ 2279eaf2578eSsthen if(actinfo.addrinfo) { 2280eaf2578eSsthen respip_inform_print(&actinfo, r->qname, 2281eaf2578eSsthen qstate->qinfo.qtype, qstate->qinfo.qclass, 228245872187Ssthen r->local_alias, &r->query_reply.client_addr, 228345872187Ssthen r->query_reply.client_addrlen); 2284eaf2578eSsthen } 2285eaf2578eSsthen 22860bdb4f62Ssthen /* Add EDE Stale Answer (RCF8914). Ignore global ede as this is 22870bdb4f62Ssthen * warning instead of an error */ 22880bdb4f62Ssthen if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired && 22890bdb4f62Ssthen qstate->env->cfg->ede) { 22900bdb4f62Ssthen edns_opt_list_append_ede(&r->edns.opt_list_out, 22910bdb4f62Ssthen mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL); 22920bdb4f62Ssthen } 22930bdb4f62Ssthen 2294eaf2578eSsthen r_buffer = r->query_reply.c->buffer; 2295eaf2578eSsthen if(r->query_reply.c->tcp_req_info) 2296eaf2578eSsthen r_buffer = r->query_reply.c->tcp_req_info->spool_buffer; 2297eaf2578eSsthen mesh_send_reply(mstate, LDNS_RCODE_NOERROR, msg->rep, 2298eaf2578eSsthen r, r_buffer, prev, prev_buffer); 2299eaf2578eSsthen if(r->query_reply.c->tcp_req_info) 2300eaf2578eSsthen tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); 2301*98bc733bSsthen /* mesh_send_reply removed mesh state from http2_stream. */ 23022bdc0ed1Ssthen infra_wait_limit_dec(mstate->s.env->infra_cache, 23032bdc0ed1Ssthen &r->query_reply, mstate->s.env->cfg); 2304eaf2578eSsthen prev = r; 2305eaf2578eSsthen prev_buffer = r_buffer; 2306eaf2578eSsthen } 23078b7325afSsthen /* Account for each reply sent. */ 23088b7325afSsthen if(i > 0) { 23098b7325afSsthen mesh->ans_expired += i; 23108b7325afSsthen if(actinfo.addrinfo && qstate->env->cfg->stat_extended && 23118b7325afSsthen actinfo.rpz_used) { 23128b7325afSsthen if(actinfo.rpz_disabled) 23138b7325afSsthen qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i; 23148b7325afSsthen if(actinfo.rpz_cname_override) 23158b7325afSsthen qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i; 23168b7325afSsthen else 23178b7325afSsthen qstate->env->mesh->rpz_action[ 23188b7325afSsthen respip_action_to_rpz_action(actinfo.action)] += i; 23198b7325afSsthen } 23208b7325afSsthen } 23218b7325afSsthen 23228b7325afSsthen /* Mesh area accounting */ 2323a3167c07Ssthen if(mstate->reply_list) { 2324a3167c07Ssthen mstate->reply_list = NULL; 2325a3167c07Ssthen if(!mstate->reply_list && !mstate->cb_list) { 2326a3167c07Ssthen log_assert(mesh->num_reply_states > 0); 2327a3167c07Ssthen mesh->num_reply_states--; 2328a3167c07Ssthen if(mstate->super_set.count == 0) { 2329a3167c07Ssthen mesh->num_detached_states++; 2330a3167c07Ssthen } 2331a3167c07Ssthen } 2332a3167c07Ssthen } 23338b7325afSsthen 2334eaf2578eSsthen while((c = mstate->cb_list) != NULL) { 2335eaf2578eSsthen /* take this cb off the list; so that the list can be 2336eaf2578eSsthen * changed, eg. by adds from the callback routine */ 2337eaf2578eSsthen if(!mstate->reply_list && mstate->cb_list && !c->next) { 2338eaf2578eSsthen /* was a reply state, not anymore */ 2339eaf2578eSsthen log_assert(qstate->env->mesh->num_reply_states > 0); 2340eaf2578eSsthen qstate->env->mesh->num_reply_states--; 2341eaf2578eSsthen } 2342eaf2578eSsthen mstate->cb_list = c->next; 2343eaf2578eSsthen if(!mstate->reply_list && !mstate->cb_list && 2344eaf2578eSsthen mstate->super_set.count == 0) 2345eaf2578eSsthen qstate->env->mesh->num_detached_states++; 23469982a05dSsthen mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c, &tv); 2347eaf2578eSsthen } 2348eaf2578eSsthen } 23497dd170e2Ssthen 23502bdc0ed1Ssthen void 23512bdc0ed1Ssthen mesh_respond_serve_expired(struct mesh_state* mstate) 23522bdc0ed1Ssthen { 23532bdc0ed1Ssthen if(!mstate->s.serve_expired_data) 23542bdc0ed1Ssthen mesh_serve_expired_init(mstate, -1); 23552bdc0ed1Ssthen mesh_serve_expired_callback(mstate); 23562bdc0ed1Ssthen } 23572bdc0ed1Ssthen 23587dd170e2Ssthen int mesh_jostle_exceeded(struct mesh_area* mesh) 23597dd170e2Ssthen { 23607dd170e2Ssthen if(mesh->all.count < mesh->max_reply_states) 23617dd170e2Ssthen return 0; 23627dd170e2Ssthen return 1; 23637dd170e2Ssthen } 2364