diff options
Diffstat (limited to 'contrib/unbound/util/module.c')
| -rw-r--r-- | contrib/unbound/util/module.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/contrib/unbound/util/module.c b/contrib/unbound/util/module.c new file mode 100644 index 000000000000..d2c21b49d604 --- /dev/null +++ b/contrib/unbound/util/module.c @@ -0,0 +1,417 @@ +/* + * util/module.c - module interface + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file + * Implementation of module.h. + */ + +#include "config.h" +#include "util/module.h" +#include "sldns/wire2str.h" +#include "util/config_file.h" +#include "util/regional.h" +#include "util/data/dname.h" +#include "util/net_help.h" + +const char* +strextstate(enum module_ext_state s) +{ + switch(s) { + case module_state_initial: return "module_state_initial"; + case module_wait_reply: return "module_wait_reply"; + case module_wait_module: return "module_wait_module"; + case module_restart_next: return "module_restart_next"; + case module_wait_subquery: return "module_wait_subquery"; + case module_error: return "module_error"; + case module_finished: return "module_finished"; + } + return "bad_extstate_value"; +} + +const char* +strmodulevent(enum module_ev e) +{ + switch(e) { + case module_event_new: return "module_event_new"; + case module_event_pass: return "module_event_pass"; + case module_event_reply: return "module_event_reply"; + case module_event_noreply: return "module_event_noreply"; + case module_event_capsfail: return "module_event_capsfail"; + case module_event_moddone: return "module_event_moddone"; + case module_event_error: return "module_event_error"; + } + return "bad_event_value"; +} + +void errinf(struct module_qstate* qstate, const char* str) +{ + errinf_ede(qstate, str, LDNS_EDE_NONE); +} + +void errinf_ede(struct module_qstate* qstate, + const char* str, sldns_ede_code reason_bogus) +{ + struct errinf_strlist* p; + if(!str || (qstate->env->cfg->val_log_level < 2 && + !qstate->env->cfg->log_servfail)) { + return; + } + p = (struct errinf_strlist*)regional_alloc(qstate->region, sizeof(*p)); + if(!p) { + log_err("malloc failure in validator-error-info string"); + return; + } + p->next = NULL; + p->str = regional_strdup(qstate->region, str); + p->reason_bogus = reason_bogus; + if(!p->str) { + log_err("malloc failure in validator-error-info string"); + return; + } + /* add at end */ + if(qstate->errinf) { + struct errinf_strlist* q = qstate->errinf; + while(q->next) + q = q->next; + q->next = p; + } else qstate->errinf = p; +} + +void errinf_origin(struct module_qstate* qstate, struct sock_list *origin) +{ + struct sock_list* p; + if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) + return; + for(p=origin; p; p=p->next) { + char buf[256]; + if(p == origin) + snprintf(buf, sizeof(buf), "from "); + else snprintf(buf, sizeof(buf), "and "); + if(p->len == 0) + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), + "cache"); + else + addr_to_str(&p->addr, p->len, buf+strlen(buf), + sizeof(buf)-strlen(buf)); + errinf(qstate, buf); + } +} + +char* errinf_to_str_bogus(struct module_qstate* qstate, struct regional* region) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + char dname[LDNS_MAX_DOMAINLEN]; + char t[16], c[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); + sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c); + left -= strlen(p); p += strlen(p); + if(!qstate->errinf) + snprintf(p, left, " misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, " %s", s->str); + left -= strlen(p); p += strlen(p); + } + if(region) + p = regional_strdup(region, buf); + else + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + +/* Try to find the latest (most specific) dnssec failure */ +sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate) +{ + struct errinf_strlist* s; + sldns_ede_code ede = LDNS_EDE_NONE; + for(s=qstate->errinf; s; s=s->next) { + if(s->reason_bogus == LDNS_EDE_NONE) continue; + if(ede != LDNS_EDE_NONE + && ede != LDNS_EDE_DNSSEC_BOGUS + && s->reason_bogus == LDNS_EDE_DNSSEC_BOGUS) continue; + ede = s->reason_bogus; + } + return ede; +} + +char* errinf_to_str_servfail(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + char dname[LDNS_MAX_DOMAINLEN]; + char t[16], c[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); + sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c); + left -= strlen(p); p += strlen(p); + if(!qstate->errinf) + snprintf(p, left, " misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, " %s", s->str); + left -= strlen(p); p += strlen(p); + } + p = regional_strdup(qstate->region, buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + +char* errinf_to_str_misc(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + if(!qstate->errinf) + snprintf(p, left, "misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, "%s%s", (s==qstate->errinf?"":" "), s->str); + left -= strlen(p); p += strlen(p); + } + p = regional_strdup(qstate->region, buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + +void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) +{ + char buf[1024]; + char dname[LDNS_MAX_DOMAINLEN]; + char t[16], c[16]; + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr) + return; + sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t)); + sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c)); + dname_str(rr->rk.dname, dname); + snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c); + errinf(qstate, buf); +} + +void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname) +{ + char b[1024]; + char buf[LDNS_MAX_DOMAINLEN]; + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname) + return; + dname_str(dname, buf); + snprintf(b, sizeof(b), "%s %s", str, buf); + errinf(qstate, b); +} + +int +edns_known_options_init(struct module_env* env) +{ + env->edns_known_options_num = 0; + env->edns_known_options = (struct edns_known_option*)calloc( + MAX_KNOWN_EDNS_OPTS, sizeof(struct edns_known_option)); + if(!env->edns_known_options) return 0; + return 1; +} + +void +edns_known_options_delete(struct module_env* env) +{ + free(env->edns_known_options); + env->edns_known_options = NULL; + env->edns_known_options_num = 0; +} + +int +edns_register_option(uint16_t opt_code, int bypass_cache_stage, + int no_aggregation, struct module_env* env) +{ + size_t i; + if(env->worker) { + log_err("invalid edns registration: " + "trying to register option after module init phase"); + return 0; + } + + /** + * Checking if we are full first is faster but it does not provide + * the option to change the flags when the array is full. + * It only impacts unbound initialization, leave it for now. + */ + /* Check if the option is already registered. */ + for(i=0; i<env->edns_known_options_num; i++) + if(env->edns_known_options[i].opt_code == opt_code) + break; + /* If it is not yet registered check if we have space to add a new one. */ + if(i == env->edns_known_options_num) { + if(env->edns_known_options_num >= MAX_KNOWN_EDNS_OPTS) { + log_err("invalid edns registration: maximum options reached"); + return 0; + } + env->edns_known_options_num++; + } + env->edns_known_options[i].opt_code = opt_code; + env->edns_known_options[i].bypass_cache_stage = bypass_cache_stage; + env->edns_known_options[i].no_aggregation = no_aggregation; + return 1; +} + +int +inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg, + struct module_env* env, int id) +{ + struct inplace_cb* callback; + struct inplace_cb** prevp; + if(env->worker) { + log_err("invalid edns callback registration: " + "trying to register callback after module init phase"); + return 0; + } + + callback = (struct inplace_cb*)calloc(1, sizeof(*callback)); + if(callback == NULL) { + log_err("out of memory during edns callback registration."); + return 0; + } + callback->id = id; + callback->next = NULL; + callback->cb = cb; + callback->cb_arg = cbarg; + + prevp = (struct inplace_cb**) &env->inplace_cb_lists[type]; + /* append at end of list */ + while(*prevp != NULL) + prevp = &((*prevp)->next); + *prevp = callback; + return 1; +} + +void +inplace_cb_delete(struct module_env* env, enum inplace_cb_list_type type, + int id) +{ + struct inplace_cb* temp = env->inplace_cb_lists[type]; + struct inplace_cb* prev = NULL; + + while(temp) { + if(temp->id == id) { + if(!prev) { + env->inplace_cb_lists[type] = temp->next; + free(temp); + temp = env->inplace_cb_lists[type]; + } + else { + prev->next = temp->next; + free(temp); + temp = prev->next; + } + } + else { + prev = temp; + temp = temp->next; + } + } +} + +struct edns_known_option* +edns_option_is_known(uint16_t opt_code, struct module_env* env) +{ + size_t i; + for(i=0; i<env->edns_known_options_num; i++) + if(env->edns_known_options[i].opt_code == opt_code) + return env->edns_known_options + i; + return NULL; +} + +int +edns_bypass_cache_stage(struct edns_option* list, struct module_env* env) +{ + size_t i; + for(; list; list=list->next) + for(i=0; i<env->edns_known_options_num; i++) + if(env->edns_known_options[i].opt_code == list->opt_code && + env->edns_known_options[i].bypass_cache_stage == 1) + return 1; + return 0; +} + +int +unique_mesh_state(struct edns_option* list, struct module_env* env) +{ + size_t i; + if(env->unique_mesh) + return 1; + for(; list; list=list->next) + for(i=0; i<env->edns_known_options_num; i++) + if(env->edns_known_options[i].opt_code == list->opt_code && + env->edns_known_options[i].no_aggregation == 1) + return 1; + return 0; +} + +void +log_edns_known_options(enum verbosity_value level, struct module_env* env) +{ + size_t i; + char str[32], *s; + size_t slen; + if(env->edns_known_options_num > 0 && verbosity >= level) { + verbose(level, "EDNS known options:"); + verbose(level, " Code: Bypass_cache_stage: Aggregate_mesh:"); + for(i=0; i<env->edns_known_options_num; i++) { + s = str; + slen = sizeof(str); + (void)sldns_wire2str_edns_option_code_print(&s, &slen, + env->edns_known_options[i].opt_code); + verbose(level, " %-8.8s %-19s %-15s", str, + env->edns_known_options[i].bypass_cache_stage?"YES":"NO", + env->edns_known_options[i].no_aggregation?"NO":"YES"); + } + } +} + +void +copy_state_to_super(struct module_qstate* qstate, int ATTR_UNUSED(id), + struct module_qstate* super) +{ + /* Overwrite super's was_ratelimited only when it was not set */ + if(!super->was_ratelimited) { + super->was_ratelimited = qstate->was_ratelimited; + } +} |
