diff --git a/daemon/worker.c b/daemon/worker.c index ff798ae7e..efe97d6d6 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1669,6 +1669,7 @@ worker_init(struct worker* worker, struct config_file *cfg, worker->env.mesh = mesh_create(&worker->daemon->mods, &worker->env); worker->env.detach_subs = &mesh_detach_subs; worker->env.attach_sub = &mesh_attach_sub; + worker->env.add_sub = &mesh_add_sub; worker->env.kill_sub = &mesh_state_delete; worker->env.detect_cycle = &mesh_detect_cycle; worker->env.scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size); diff --git a/doc/Changelog b/doc/Changelog index dba5b82d3..d9fd9cc11 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +2 May 2017: Ralph + - Added mesh_add_sub to add detached mesh entries. + - Use mesh_add_sub for key tag signaling query. + 2 May 2017: Wouter - Added test for leak of stub information. - Fix sldns wire2str printout of RR type CAA tags. diff --git a/services/mesh.c b/services/mesh.c index 4b0a03939..f04ae16dd 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -821,26 +821,26 @@ void mesh_detach_subs(struct module_qstate* qstate) rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare); } -int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq) +int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, struct module_qstate** newq, + struct mesh_state** sub) { /* find it, if not, create it */ struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* sub = mesh_area_find(mesh, NULL, qinfo, qflags, + *sub = mesh_area_find(mesh, NULL, qinfo, qflags, prime, valrec); - int was_detached; - if(mesh_detect_cycle_found(qstate, sub)) { + if(mesh_detect_cycle_found(qstate, *sub)) { verbose(VERB_ALGO, "attach failed, cycle detected"); return 0; } - if(!sub) { + if(!*sub) { #ifdef UNBOUND_DEBUG struct rbnode_type* n; #endif /* create a new one */ - sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, + *sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, valrec); - if(!sub) { + if(!*sub) { log_err("mesh_attach_sub: out of memory"); return 0; } @@ -849,7 +849,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, #else (void) #endif - rbtree_insert(&mesh->all, &sub->node); + rbtree_insert(&mesh->all, &(*sub)->node); log_assert(n != NULL); /* set detached (it is now) */ mesh->num_detached_states++; @@ -859,11 +859,22 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, #else (void) #endif - rbtree_insert(&mesh->run, &sub->run_node); + rbtree_insert(&mesh->run, &(*sub)->run_node); log_assert(n != NULL); - *newq = &sub->s; + *newq = &(*sub)->s; } else *newq = NULL; + return 1; +} + +int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, struct module_qstate** newq) +{ + struct mesh_area* mesh = qstate->env->mesh; + struct mesh_state* sub = NULL; + int was_detached; + if(!mesh_add_sub(qstate, qinfo, qflags, prime, valrec, newq, &sub)) + return 0; was_detached = (sub->super_set.count == 0); if(!mesh_state_attachment(qstate->mesh_info, sub)) return 0; diff --git a/services/mesh.h b/services/mesh.h index 1c7794532..67749accb 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -370,6 +370,35 @@ void mesh_detach_subs(struct module_qstate* qstate); int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, uint16_t qflags, int prime, int valrec, struct module_qstate** newq); +/** + * Add detached query. + * Creates it if it does not exist already. + * Does not make super/sub references. + * Performs a cycle detection - for double check - and fails if there is one. + * Updates stat items in mesh_area structure. + * Pass if it is priming query or not. + * return: + * o if error (malloc) happened. + * o need to initialise the new state (module init; it is a new state). + * so that the next run of the query with this module is successful. + * o no init needed, attachment successful. + * o added subquery, created if it did not exist already. + * + * @param qstate: the state to find mesh state, and that wants to receive + * the results from the new subquery. + * @param qinfo: what to query for (copied). + * @param qflags: what flags to use (RD / CD flag or not). + * @param prime: if it is a (stub) priming query. + * @param valrec: if it is a validation recursion query (lookup of key, DS). + * @param newq: If the new subquery needs initialisation, it is returned, + * otherwise NULL is returned. + * @param sub: The added mesh state, created if it did not exist already. + * @return: false on error, true if success (and init may be needed). + */ +int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, struct module_qstate** newq, + struct mesh_state** sub); + /** * Query state is done, send messages to reply entries. * Encode messages using reply entry values and the querystate (with original diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 03244a123..7c0b0bfcd 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -306,6 +306,16 @@ fptr_whitelist_modenv_attach_sub(int (*fptr)( return 0; } +int +fptr_whitelist_modenv_add_sub(int (*fptr)( + struct module_qstate* qstate, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, struct module_qstate** newq, + struct mesh_state** sub)) +{ + if(fptr == &mesh_add_sub) return 1; + return 0; +} + int fptr_whitelist_modenv_kill_sub(void (*fptr)(struct module_qstate* newq)) { diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index 653f8f0e7..39e3f2d7f 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -233,6 +233,15 @@ int fptr_whitelist_modenv_attach_sub(int (*fptr)( struct module_qstate* qstate, struct query_info* qinfo, uint16_t qflags, int prime, int valrec, struct module_qstate** newq)); +/** + * Check function pointer whitelist for module_env add_sub callback values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_modenv_add_sub(int (*fptr)(struct module_qstate* qstate, + struct query_info* qinfo, uint16_t qflags, int prime, int valrec, + struct module_qstate** newq, struct mesh_state** sub)); /** * Check function pointer whitelist for module_env kill_sub callback values. * diff --git a/util/module.h b/util/module.h index 82b50ccd7..6e75539d9 100644 --- a/util/module.h +++ b/util/module.h @@ -382,6 +382,37 @@ struct module_env { struct query_info* qinfo, uint16_t qflags, int prime, int valrec, struct module_qstate** newq); + /** + * Add detached query. + * Creates it if it does not exist already. + * Does not make super/sub references. + * Performs a cycle detection - for double check - and fails if there is + * one. + * Updates stat items in mesh_area structure. + * Pass if it is priming query or not. + * return: + * o if error (malloc) happened. + * o need to initialise the new state (module init; it is a new state). + * so that the next run of the query with this module is successful. + * o no init needed, attachment successful. + * o added subquery, created if it did not exist already. + * + * @param qstate: the state to find mesh state, and that wants to receive + * the results from the new subquery. + * @param qinfo: what to query for (copied). + * @param qflags: what flags to use (RD / CD flag or not). + * @param prime: if it is a (stub) priming query. + * @param valrec: if it is a validation recursion query (lookup of key, DS). + * @param newq: If the new subquery needs initialisation, it is returned, + * otherwise NULL is returned. + * @param sub: The added mesh state, created if it did not exist already. + * @return: false on error, true if success (and init may be needed). + */ + int (*add_sub)(struct module_qstate* qstate, + struct query_info* qinfo, uint16_t qflags, int prime, + int valrec, struct module_qstate** newq, + struct mesh_state** sub); + /** * Kill newly attached sub. If attach_sub returns newq for * initialisation, but that fails, then this routine will cleanup and diff --git a/validator/validator.c b/validator/validator.c index 650166046..e8b6317f6 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -365,14 +365,17 @@ already_validated(struct dns_msg* ret_msg) * @param qtype: query type. * @param qclass: query class. * @param flags: additional flags, such as the CD bit (BIT_CD), or 0. + * @param newq: If the subquery is newly created, it is returned, + * otherwise NULL is returned + * @param detached: true if this qstate should not attach to the subquery * @return false on alloc failure. */ static int generate_request(struct module_qstate* qstate, int id, uint8_t* name, - size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags) + size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags, + struct module_qstate** newq, int detached) { struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; - struct module_qstate* newq; struct query_info ask; int valrec; ask.qname = name; @@ -381,65 +384,41 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name, ask.qclass = qclass; ask.local_alias = NULL; log_query_info(VERB_ALGO, "generate request", &ask); - fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); /* enable valrec flag to avoid recursion to the same validation * routine, this lookup is simply a lookup. DLVs need validation */ if(qtype == LDNS_RR_TYPE_DLV) valrec = 0; else valrec = 1; - if(!(*qstate->env->attach_sub)(qstate, &ask, - (uint16_t)(BIT_RD|flags), 0, valrec, &newq)){ - log_err("Could not generate request: out of memory"); - return 0; + if(detached) { + struct mesh_state* sub = NULL; + fptr_ok(fptr_whitelist_modenv_add_sub( + qstate->env->add_sub)); + if(!(*qstate->env->add_sub)(qstate, &ask, + (uint16_t)(BIT_RD|flags), 0, valrec, newq, &sub)){ + log_err("Could not generate request: out of memory"); + return 0; + } + } + else { + fptr_ok(fptr_whitelist_modenv_attach_sub( + qstate->env->attach_sub)); + if(!(*qstate->env->attach_sub)(qstate, &ask, + (uint16_t)(BIT_RD|flags), 0, valrec, newq)){ + log_err("Could not generate request: out of memory"); + return 0; + } } /* newq; validator does not need state created for that * query, and its a 'normal' for iterator as well */ - if(newq) { + if(*newq) { /* add our blacklist to the query blacklist */ - sock_list_merge(&newq->blacklist, newq->region, + sock_list_merge(&(*newq)->blacklist, (*newq)->region, vq->chain_blacklist); } qstate->ext_state[id] = module_wait_subquery; return 1; } -/** - * Prime trust anchor for use. - * Generate and dispatch a priming query for the given trust anchor. - * The trust anchor can be DNSKEY or DS and does not have to be signed. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param id: module id. - * @param toprime: what to prime. - * @return false on a processing error. - */ -static int -prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, - int id, struct trust_anchor* toprime) -{ - int ret = generate_request(qstate, id, toprime->name, toprime->namelen, - LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD); - if(!ret) { - log_err("Could not prime trust anchor: out of memory"); - return 0; - } - /* ignore newq; validator does not need state created for that - * query, and its a 'normal' for iterator as well */ - vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing - from the validator inform_super() routine */ - /* store trust anchor name for later lookup when prime returns */ - vq->trust_anchor_name = regional_alloc_init(qstate->region, - toprime->name, toprime->namelen); - vq->trust_anchor_len = toprime->namelen; - vq->trust_anchor_labs = toprime->namelabs; - if(!vq->trust_anchor_name) { - log_err("Could not prime trust anchor: out of memory"); - return 0; - } - return 1; -} - /** * Generate, send and detach key tag signaling query. * @@ -462,6 +441,8 @@ generate_keytag_query(struct module_qstate* qstate, int id, uint8_t dnamebuf[LDNS_MAX_DOMAINLEN+1]; /* +1 for label length byte */ size_t dnamebuf_len = sizeof(dnamebuf); uint8_t* keytagdname; + struct module_qstate* newq = NULL; + enum module_ext_state ext_state = qstate->ext_state[id]; numtag = anchor_list_keytags(ta, tags, MAX_LABEL_TAGS); if(numtag == 0) @@ -486,19 +467,63 @@ generate_keytag_query(struct module_qstate* qstate, int id, log_nametypeclass(VERB_ALGO, "keytag query", keytagdname, LDNS_RR_TYPE_NULL, ta->dclass); if(!generate_request(qstate, id, keytagdname, dnamebuf_len, - LDNS_RR_TYPE_NULL, ta->dclass, 0)) { + LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) { log_err("failed to generate key tag signaling request"); return 0; } - /* We are not interrested in the response, detach this (and all other!) - * subqueries. */ - fptr_ok(fptr_whitelist_modenv_detach_subs(qstate->env->detach_subs)); - (*qstate->env->detach_subs)(qstate); + /* Not interrested in subquery response. Restore the ext_state, + * that might be changed by generate_request() */ + qstate->ext_state[id] = ext_state; return 1; } +/** + * Prime trust anchor for use. + * Generate and dispatch a priming query for the given trust anchor. + * The trust anchor can be DNSKEY or DS and does not have to be signed. + * + * @param qstate: query state. + * @param vq: validator query state. + * @param id: module id. + * @param toprime: what to prime. + * @return false on a processing error. + */ +static int +prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, + int id, struct trust_anchor* toprime) +{ + struct module_qstate* newq = NULL; + int ret = generate_request(qstate, id, toprime->name, toprime->namelen, + LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD, &newq, 0); + + if(newq && qstate->env->cfg->trust_anchor_signaling && + !generate_keytag_query(qstate, id, toprime)) { + log_err("keytag signaling query failed"); + return 0; + } + + if(!ret) { + log_err("Could not prime trust anchor: out of memory"); + return 0; + } + /* ignore newq; validator does not need state created for that + * query, and its a 'normal' for iterator as well */ + vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing + from the validator inform_super() routine */ + /* store trust anchor name for later lookup when prime returns */ + vq->trust_anchor_name = regional_alloc_init(qstate->region, + toprime->name, toprime->namelen); + vq->trust_anchor_len = toprime->namelen; + vq->trust_anchor_labs = toprime->namelabs; + if(!vq->trust_anchor_name) { + log_err("Could not prime trust anchor: out of memory"); + return 0; + } + return 1; +} + /** * Validate if the ANSWER and AUTHORITY sections contain valid rrsets. * They must be validly signed with the given key. @@ -1570,6 +1595,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) uint8_t* target_key_name, *current_key_name; size_t target_key_len; int strip_lab; + struct module_qstate* newq = NULL; log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase); /* We know that state.key_entry is not 0 or bad key -- if it were, @@ -1582,7 +1608,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) if(key_entry_isnull(vq->key_entry)) { if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { + vq->qchase.qclass, BIT_CD, &newq, 0)) { log_err("mem error generating DNSKEY request"); return val_error(qstate, id); } @@ -1654,7 +1680,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) vq->key_entry->name) != 0) { if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { + vq->qchase.qclass, BIT_CD, &newq, 0)) { log_err("mem error generating DNSKEY request"); return val_error(qstate, id); } @@ -1683,7 +1709,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) } if(!generate_request(qstate, id, target_key_name, target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, - BIT_CD)) { + BIT_CD, &newq, 0)) { log_err("mem error generating DS request"); return val_error(qstate, id); } @@ -1693,7 +1719,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) /* Otherwise, it is time to query for the DNSKEY */ if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { + vq->qchase.qclass, BIT_CD, &newq, 0)) { log_err("mem error generating DNSKEY request"); return val_error(qstate, id); } @@ -1907,6 +1933,7 @@ val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq, { uint8_t* nm; size_t nm_len; + struct module_qstate* newq = NULL; /* there must be a DLV configured */ log_assert(qstate->env->anchors->dlv_anchor); /* this bool is true to avoid looping in the DLV checks */ @@ -2008,7 +2035,7 @@ val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq, vq->state = VAL_DLVLOOKUP_STATE; if(!generate_request(qstate, id, vq->dlv_lookup_name, vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { + vq->qchase.qclass, 0, &newq, 0)) { return val_error(qstate, id); } @@ -2188,6 +2215,7 @@ static int processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve, int id) { + struct module_qstate* newq = NULL; /* see if this we are ready to continue normal resolution */ /* we may need more DLV lookups */ if(vq->dlv_status==dlv_error) @@ -2236,7 +2264,7 @@ processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { + vq->qchase.qclass, BIT_CD, &newq, 0)) { log_err("mem error generating DNSKEY request"); return val_error(qstate, id); } @@ -2278,7 +2306,7 @@ processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, if(!generate_request(qstate, id, vq->dlv_lookup_name, vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { + vq->qchase.qclass, 0, &newq, 0)) { return val_error(qstate, id); } @@ -2918,12 +2946,6 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, ta->dclass); } - if(qstate->env->cfg->trust_anchor_signaling && - !generate_keytag_query(qstate, id, ta)) { - log_err("keytag signaling query failed"); - return; - } - if(ta->autr) { if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) { /* trust anchor revoked, restart with less anchors */