diff options
author | anonimal <anonimal@i2pmail.org> | 2017-06-28 21:07:24 +0000 |
---|---|---|
committer | anonimal <anonimal@i2pmail.org> | 2018-03-18 15:52:19 +0000 |
commit | 84c5a9ba481d7a33cc0fd0ca43867b61d127d907 (patch) | |
tree | f05d3d3f107da02005b4a61f0e5074c113a7165c /external/unbound/services | |
parent | Merge pull request #3416 (diff) | |
download | monero-84c5a9ba481d7a33cc0fd0ca43867b61d127d907.tar.xz |
Unbound: remove unbound from in-tree source
We'll instead use a git submodule to pull from our unbound repo.
Diffstat (limited to 'external/unbound/services')
20 files changed, 0 insertions, 12998 deletions
diff --git a/external/unbound/services/cache/dns.c b/external/unbound/services/cache/dns.c deleted file mode 100644 index a8fde9f28..000000000 --- a/external/unbound/services/cache/dns.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * services/cache/dns.c - Cache services for DNS using msg and rrset caches. - * - * 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 - * - * This file contains the DNS cache. - */ -#include "config.h" -#include "iterator/iter_delegpt.h" -#include "validator/val_nsec.h" -#include "services/cache/dns.h" -#include "services/cache/rrset.h" -#include "util/data/msgreply.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/module.h" -#include "util/net_help.h" -#include "util/regional.h" -#include "util/config_file.h" -#include "sldns/sbuffer.h" - -/** store rrsets in the rrset cache. - * @param env: module environment with caches. - * @param rep: contains list of rrsets to store. - * @param now: current time. - * @param leeway: during prefetch how much leeway to update TTLs. - * This makes rrsets (other than type NS) timeout sooner so they get - * updated with a new full TTL. - * Type NS does not get this, because it must not be refreshed from the - * child domain, but keep counting down properly. - * @param pside: if from parentside discovered NS, so that its NS is okay - * in a prefetch situation to be updated (without becoming sticky). - * @param qrep: update rrsets here if cache is better - * @param region: for qrep allocs. - */ -static void -store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, - time_t leeway, int pside, struct reply_info* qrep, - struct regional* region) -{ - size_t i; - /* see if rrset already exists in cache, if not insert it. */ - for(i=0; i<rep->rrset_count; i++) { - rep->ref[i].key = rep->rrsets[i]; - rep->ref[i].id = rep->rrsets[i]->id; - /* update ref if it was in the cache */ - switch(rrset_cache_update(env->rrset_cache, &rep->ref[i], - env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)== - LDNS_RR_TYPE_NS && !pside)?0:leeway))) { - case 0: /* ref unchanged, item inserted */ - break; - case 2: /* ref updated, cache is superior */ - if(region) { - struct ub_packed_rrset_key* ck; - lock_rw_rdlock(&rep->ref[i].key->entry.lock); - /* if deleted rrset, do not copy it */ - if(rep->ref[i].key->id == 0) - ck = NULL; - else ck = packed_rrset_copy_region( - rep->ref[i].key, region, now); - lock_rw_unlock(&rep->ref[i].key->entry.lock); - if(ck) { - /* use cached copy if memory allows */ - qrep->rrsets[i] = ck; - } - } - /* no break: also copy key item */ - case 1: /* ref updated, item inserted */ - rep->rrsets[i] = rep->ref[i].key; - } - } -} - -void -dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region) -{ - struct msgreply_entry* e; - time_t ttl = rep->ttl; - size_t i; - - /* store RRsets */ - for(i=0; i<rep->rrset_count; i++) { - rep->ref[i].key = rep->rrsets[i]; - rep->ref[i].id = rep->rrsets[i]->id; - } - - /* there was a reply_info_sortref(rep) here but it seems to be - * unnecessary, because the cache gets locked per rrset. */ - reply_info_set_ttls(rep, *env->now); - store_rrsets(env, rep, *env->now, leeway, pside, qrep, region); - if(ttl == 0) { - /* we do not store the message, but we did store the RRs, - * which could be useful for delegation information */ - verbose(VERB_ALGO, "TTL 0: dropped msg from cache"); - free(rep); - return; - } - - /* store msg in the cache */ - reply_info_sortref(rep); - if(!(e = query_info_entrysetup(qinfo, rep, hash))) { - log_err("store_msg: malloc failed"); - return; - } - slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc); -} - -/** find closest NS or DNAME and returns the rrset (locked) */ -static struct ub_packed_rrset_key* -find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, - uint16_t qclass, time_t now, uint16_t searchtype, int stripfront) -{ - struct ub_packed_rrset_key *rrset; - uint8_t lablen; - - if(stripfront) { - /* strip off so that DNAMEs have strict subdomain match */ - lablen = *qname; - qname += lablen + 1; - qnamelen -= lablen + 1; - } - - /* snip off front part of qname until the type is found */ - while(qnamelen > 0) { - if((rrset = rrset_cache_lookup(env->rrset_cache, qname, - qnamelen, searchtype, qclass, 0, now, 0))) - return rrset; - - /* snip off front label */ - lablen = *qname; - qname += lablen + 1; - qnamelen -= lablen + 1; - } - return NULL; -} - -/** add addr to additional section */ -static void -addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region, - struct dns_msg* msg, time_t now) -{ - if((msg->rep->rrsets[msg->rep->rrset_count] = - packed_rrset_copy_region(rrset, region, now))) { - msg->rep->ar_numrrsets++; - msg->rep->rrset_count++; - } -} - -/** lookup message in message cache */ -static struct msgreply_entry* -msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, time_t now, int wr) -{ - struct lruhash_entry* e; - struct query_info k; - hashvalue_type h; - - k.qname = qname; - k.qname_len = qnamelen; - k.qtype = qtype; - k.qclass = qclass; - k.local_alias = NULL; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, wr); - - if(!e) return NULL; - if( now > ((struct reply_info*)e->data)->ttl ) { - lock_rw_unlock(&e->lock); - return NULL; - } - return (struct msgreply_entry*)e->key; -} - -/** find and add A and AAAA records for nameservers in delegpt */ -static int -find_add_addrs(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp, time_t now, - struct dns_msg** msg) -{ - struct delegpt_ns* ns; - struct msgreply_entry* neg; - struct ub_packed_rrset_key* akey; - for(ns = dp->nslist; ns; ns = ns->next) { - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, 0)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - if(msg) - addr_to_additional(akey, region, *msg, now); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - if(msg) - addr_to_additional(akey, region, *msg, now); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - } - return 1; -} - -/** find and add A and AAAA records for missing nameservers in delegpt */ -int -cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp) -{ - struct delegpt_ns* ns; - struct msgreply_entry* neg; - struct ub_packed_rrset_key* akey; - time_t now = *env->now; - for(ns = dp->nslist; ns; ns = ns->next) { - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "found in cache", - ns->name, LDNS_RR_TYPE_A, qclass); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) { - lock_rw_unlock(&akey->entry.lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "found in cache", - ns->name, LDNS_RR_TYPE_AAAA, qclass); - lock_rw_unlock(&akey->entry.lock); - } else { - /* BIT_CD on false because delegpt lookup does - * not use dns64 translation */ - neg = msg_cache_lookup(env, ns->name, ns->namelen, - LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); - if(neg) { - delegpt_add_neg_msg(dp, neg); - lock_rw_unlock(&neg->entry.lock); - } - } - } - return 1; -} - -/** find and add DS or NSEC to delegation msg */ -static void -find_add_ds(struct module_env* env, struct regional* region, - struct dns_msg* msg, struct delegpt* dp, time_t now) -{ - /* Lookup the DS or NSEC at the delegation point. */ - struct ub_packed_rrset_key* rrset = rrset_cache_lookup( - env->rrset_cache, dp->name, dp->namelen, LDNS_RR_TYPE_DS, - msg->qinfo.qclass, 0, now, 0); - if(!rrset) { - /* NOTE: this won't work for alternate NSEC schemes - * (opt-in, NSEC3) */ - rrset = rrset_cache_lookup(env->rrset_cache, dp->name, - dp->namelen, LDNS_RR_TYPE_NSEC, msg->qinfo.qclass, - 0, now, 0); - /* Note: the PACKED_RRSET_NSEC_AT_APEX flag is not used. - * since this is a referral, we need the NSEC at the parent - * side of the zone cut, not the NSEC at apex side. */ - if(rrset && nsec_has_type(rrset, LDNS_RR_TYPE_DS)) { - lock_rw_unlock(&rrset->entry.lock); - rrset = NULL; /* discard wrong NSEC */ - } - } - if(rrset) { - /* add it to auth section. This is the second rrset. */ - if((msg->rep->rrsets[msg->rep->rrset_count] = - packed_rrset_copy_region(rrset, region, now))) { - msg->rep->ns_numrrsets++; - msg->rep->rrset_count++; - } - lock_rw_unlock(&rrset->entry.lock); - } -} - -struct dns_msg* -dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, struct regional* region, size_t capacity) -{ - struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, - sizeof(struct dns_msg)); - if(!msg) - return NULL; - msg->qinfo.qname = regional_alloc_init(region, qname, qnamelen); - if(!msg->qinfo.qname) - return NULL; - msg->qinfo.qname_len = qnamelen; - msg->qinfo.qtype = qtype; - msg->qinfo.qclass = qclass; - msg->qinfo.local_alias = NULL; - /* non-packed reply_info, because it needs to grow the array */ - msg->rep = (struct reply_info*)regional_alloc_zero(region, - sizeof(struct reply_info)-sizeof(struct rrset_ref)); - if(!msg->rep) - return NULL; - if(capacity > RR_COUNT_MAX) - return NULL; /* integer overflow protection */ - msg->rep->flags = BIT_QR; /* with QR, no AA */ - msg->rep->qdcount = 1; - msg->rep->rrsets = (struct ub_packed_rrset_key**) - regional_alloc(region, - capacity*sizeof(struct ub_packed_rrset_key*)); - if(!msg->rep->rrsets) - return NULL; - return msg; -} - -int -dns_msg_authadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now) -{ - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = - packed_rrset_copy_region(rrset, region, now))) - return 0; - msg->rep->ns_numrrsets++; - return 1; -} - -/** add rrset to answer section */ -static int -dns_msg_ansadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now) -{ - if(!(msg->rep->rrsets[msg->rep->rrset_count++] = - packed_rrset_copy_region(rrset, region, now))) - return 0; - msg->rep->an_numrrsets++; - return 1; -} - -struct delegpt* -dns_cache_find_delegation(struct module_env* env, uint8_t* qname, - size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region, struct dns_msg** msg, time_t now) -{ - /* try to find closest NS rrset */ - struct ub_packed_rrset_key* nskey; - struct packed_rrset_data* nsdata; - struct delegpt* dp; - - nskey = find_closest_of_type(env, qname, qnamelen, qclass, now, - LDNS_RR_TYPE_NS, 0); - if(!nskey) /* hope the caller has hints to prime or something */ - return NULL; - nsdata = (struct packed_rrset_data*)nskey->entry.data; - /* got the NS key, create delegation point */ - dp = delegpt_create(region); - if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) { - lock_rw_unlock(&nskey->entry.lock); - log_err("find_delegation: out of memory"); - return NULL; - } - /* create referral message */ - if(msg) { - /* allocate the array to as much as we could need: - * NS rrset + DS/NSEC rrset + - * A rrset for every NS RR - * AAAA rrset for every NS RR - */ - *msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, - 2 + nsdata->count*2); - if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) { - lock_rw_unlock(&nskey->entry.lock); - log_err("find_delegation: out of memory"); - return NULL; - } - } - if(!delegpt_rrset_add_ns(dp, region, nskey, 0)) - log_err("find_delegation: addns out of memory"); - lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/ - /* find and add DS/NSEC (if any) */ - if(msg) - find_add_ds(env, region, *msg, dp, now); - /* find and add A entries */ - if(!find_add_addrs(env, qclass, region, dp, now, msg)) - log_err("find_delegation: addrs out of memory"); - return dp; -} - -/** allocate dns_msg from query_info and reply_info */ -static struct dns_msg* -gen_dns_msg(struct regional* region, struct query_info* q, size_t num) -{ - struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, - sizeof(struct dns_msg)); - if(!msg) - return NULL; - memcpy(&msg->qinfo, q, sizeof(struct query_info)); - msg->qinfo.qname = regional_alloc_init(region, q->qname, q->qname_len); - if(!msg->qinfo.qname) - return NULL; - /* allocate replyinfo struct and rrset key array separately */ - msg->rep = (struct reply_info*)regional_alloc(region, - sizeof(struct reply_info) - sizeof(struct rrset_ref)); - if(!msg->rep) - return NULL; - if(num > RR_COUNT_MAX) - return NULL; /* integer overflow protection */ - msg->rep->rrsets = (struct ub_packed_rrset_key**) - regional_alloc(region, - num * sizeof(struct ub_packed_rrset_key*)); - if(!msg->rep->rrsets) - return NULL; - return msg; -} - -struct dns_msg* -tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, - struct regional* region, time_t now, struct regional* scratch) -{ - struct dns_msg* msg; - size_t i; - if(now > r->ttl) - return NULL; - msg = gen_dns_msg(region, q, r->rrset_count); - if(!msg) - return NULL; - msg->rep->flags = r->flags; - msg->rep->qdcount = r->qdcount; - msg->rep->ttl = r->ttl - now; - if(r->prefetch_ttl > now) - msg->rep->prefetch_ttl = r->prefetch_ttl - now; - else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = r->security; - msg->rep->an_numrrsets = r->an_numrrsets; - msg->rep->ns_numrrsets = r->ns_numrrsets; - msg->rep->ar_numrrsets = r->ar_numrrsets; - msg->rep->rrset_count = r->rrset_count; - msg->rep->authoritative = r->authoritative; - if(!rrset_array_lock(r->ref, r->rrset_count, now)) - return NULL; - if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons( - LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons( - LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(q, r)) { - /* cname chain is now invalid, reconstruct msg */ - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) { - /* message rrsets have changed status, revalidate */ - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - for(i=0; i<msg->rep->rrset_count; i++) { - msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], - region, now); - if(!msg->rep->rrsets[i]) { - rrset_array_unlock(r->ref, r->rrset_count); - return NULL; - } - } - if(env) - rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, - r->rrset_count); - else - rrset_array_unlock(r->ref, r->rrset_count); - return msg; -} - -/** synthesize RRset-only response from cached RRset item */ -static struct dns_msg* -rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) -{ - struct dns_msg* msg; - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - if(now > d->ttl) - return NULL; - msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */ - if(!msg) - return NULL; - msg->rep->flags = BIT_QR; /* reply, no AA, no error */ - msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ - msg->rep->qdcount = 1; - msg->rep->ttl = d->ttl - now; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = sec_status_unchecked; - msg->rep->an_numrrsets = 1; - msg->rep->ns_numrrsets = 0; - msg->rep->ar_numrrsets = 0; - msg->rep->rrset_count = 1; - msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); - if(!msg->rep->rrsets[0]) /* copy CNAME */ - return NULL; - return msg; -} - -/** synthesize DNAME+CNAME response from cached DNAME item */ -static struct dns_msg* -synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) -{ - struct dns_msg* msg; - struct ub_packed_rrset_key* ck; - struct packed_rrset_data* newd, *d = (struct packed_rrset_data*) - rrset->entry.data; - uint8_t* newname, *dtarg = NULL; - size_t newlen, dtarglen; - if(now > d->ttl) - return NULL; - /* only allow validated (with DNSSEC) DNAMEs used from cache - * for insecure DNAMEs, query again. */ - if(d->security != sec_status_secure) - return NULL; - msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ - if(!msg) - return NULL; - msg->rep->flags = BIT_QR; /* reply, no AA, no error */ - msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ - msg->rep->qdcount = 1; - msg->rep->ttl = d->ttl - now; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - msg->rep->security = sec_status_unchecked; - msg->rep->an_numrrsets = 1; - msg->rep->ns_numrrsets = 0; - msg->rep->ar_numrrsets = 0; - msg->rep->rrset_count = 1; - msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); - if(!msg->rep->rrsets[0]) /* copy DNAME */ - return NULL; - /* synth CNAME rrset */ - get_cname_target(rrset, &dtarg, &dtarglen); - if(!dtarg) - return NULL; - newlen = q->qname_len + dtarglen - rrset->rk.dname_len; - if(newlen > LDNS_MAX_DOMAINLEN) { - msg->rep->flags |= LDNS_RCODE_YXDOMAIN; - return msg; - } - newname = (uint8_t*)regional_alloc(region, newlen); - if(!newname) - return NULL; - /* new name is concatenation of qname front (without DNAME owner) - * and DNAME target name */ - memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len); - memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen); - /* create rest of CNAME rrset */ - ck = (struct ub_packed_rrset_key*)regional_alloc(region, - sizeof(struct ub_packed_rrset_key)); - if(!ck) - return NULL; - memset(&ck->entry, 0, sizeof(ck->entry)); - msg->rep->rrsets[1] = ck; - ck->entry.key = ck; - ck->rk.type = htons(LDNS_RR_TYPE_CNAME); - ck->rk.rrset_class = rrset->rk.rrset_class; - ck->rk.flags = 0; - ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len); - if(!ck->rk.dname) - return NULL; - ck->rk.dname_len = q->qname_len; - ck->entry.hash = rrset_key_hash(&ck->rk); - newd = (struct packed_rrset_data*)regional_alloc_zero(region, - sizeof(struct packed_rrset_data) + sizeof(size_t) + - sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t) - + newlen); - if(!newd) - return NULL; - ck->entry.data = newd; - newd->ttl = 0; /* 0 for synthesized CNAME TTL */ - newd->count = 1; - newd->rrsig_count = 0; - newd->trust = rrset_trust_ans_noAA; - newd->rr_len = (size_t*)((uint8_t*)newd + - sizeof(struct packed_rrset_data)); - newd->rr_len[0] = newlen + sizeof(uint16_t); - packed_rrset_ptr_fixup(newd); - newd->rr_ttl[0] = newd->ttl; - msg->rep->ttl = newd->ttl; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl); - sldns_write_uint16(newd->rr_data[0], newlen); - memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen); - msg->rep->an_numrrsets ++; - msg->rep->rrset_count ++; - return msg; -} - -/** Fill TYPE_ANY response with some data from cache */ -static struct dns_msg* -fill_any(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region) -{ - time_t now = *env->now; - struct dns_msg* msg = NULL; - uint16_t lookup[] = {LDNS_RR_TYPE_A, LDNS_RR_TYPE_AAAA, - LDNS_RR_TYPE_MX, LDNS_RR_TYPE_SOA, LDNS_RR_TYPE_NS, - LDNS_RR_TYPE_DNAME, 0}; - int i, num=6; /* number of RR types to look up */ - log_assert(lookup[num] == 0); - - for(i=0; i<num; i++) { - /* look up this RR for inclusion in type ANY response */ - struct ub_packed_rrset_key* rrset = rrset_cache_lookup( - env->rrset_cache, qname, qnamelen, lookup[i], - qclass, 0, now, 0); - struct packed_rrset_data *d; - if(!rrset) - continue; - - /* only if rrset from answer section */ - d = (struct packed_rrset_data*)rrset->entry.data; - if(d->trust == rrset_trust_add_noAA || - d->trust == rrset_trust_auth_noAA || - d->trust == rrset_trust_add_AA || - d->trust == rrset_trust_auth_AA) { - lock_rw_unlock(&rrset->entry.lock); - continue; - } - - /* create msg if none */ - if(!msg) { - msg = dns_msg_create(qname, qnamelen, qtype, qclass, - region, (size_t)(num-i)); - if(!msg) { - lock_rw_unlock(&rrset->entry.lock); - return NULL; - } - } - - /* add RRset to response */ - if(!dns_msg_ansadd(msg, region, rrset, now)) { - lock_rw_unlock(&rrset->entry.lock); - return NULL; - } - lock_rw_unlock(&rrset->entry.lock); - } - return msg; -} - -struct dns_msg* -dns_cache_lookup(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch) -{ - struct lruhash_entry* e; - struct query_info k; - hashvalue_type h; - time_t now = *env->now; - struct ub_packed_rrset_key* rrset; - - /* lookup first, this has both NXdomains and ANSWER responses */ - k.qname = qname; - k.qname_len = qnamelen; - k.qtype = qtype; - k.qclass = qclass; - k.local_alias = NULL; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - if(e) { - struct msgreply_entry* key = (struct msgreply_entry*)e->key; - struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg = tomsg(env, &key->key, data, region, now, - scratch); - if(msg) { - lock_rw_unlock(&e->lock); - return msg; - } - /* could be msg==NULL; due to TTL or not all rrsets available */ - lock_rw_unlock(&e->lock); - } - - /* see if a DNAME exists. Checked for first, to enforce that DNAMEs - * are more important, the CNAME is resynthesized and thus - * consistent with the DNAME */ - if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, - LDNS_RR_TYPE_DNAME, 1))) { - /* synthesize a DNAME+CNAME message based on this */ - struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* see if we have CNAME for this domain, - * but not for DS records (which are part of the parent) */ - if( qtype != LDNS_RR_TYPE_DS && - (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, - LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { - struct dns_msg* msg = rrset_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* construct DS, DNSKEY, DLV messages from rrset cache. */ - if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY || - qtype == LDNS_RR_TYPE_DLV) && - (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, - qtype, qclass, 0, now, 0))) { - /* if the rrset is from the additional section, and the - * signatures have fallen off, then do not synthesize a msg - * instead, allow a full query for signed results to happen. - * Forego all rrset data from additional section, because - * some signatures may not be present and cause validation - * failure. - */ - struct packed_rrset_data *d = (struct packed_rrset_data*) - rrset->entry.data; - if(d->trust != rrset_trust_add_noAA && - d->trust != rrset_trust_add_AA && - (qtype == LDNS_RR_TYPE_DS || - (d->trust != rrset_trust_auth_noAA - && d->trust != rrset_trust_auth_AA) )) { - struct dns_msg* msg = rrset_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; - } - } - lock_rw_unlock(&rrset->entry.lock); - } - - /* stop downwards cache search on NXDOMAIN. - * Empty nonterminals are NOERROR, so an NXDOMAIN for foo - * means bla.foo also does not exist. The DNSSEC proofs are - * the same. We search upwards for NXDOMAINs. */ - if(env->cfg->harden_below_nxdomain) - while(!dname_is_root(k.qname)) { - dname_remove_label(&k.qname, &k.qname_len); - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - if(!e && k.qtype != LDNS_RR_TYPE_A && - env->cfg->qname_minimisation) { - k.qtype = LDNS_RR_TYPE_A; - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - } - if(e) { - struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg; - if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN - && data->security == sec_status_secure - && (msg=tomsg(env, &k, data, region, now, scratch))){ - lock_rw_unlock(&e->lock); - msg->qinfo.qname=qname; - msg->qinfo.qname_len=qnamelen; - /* check that DNSSEC really works out */ - msg->rep->security = sec_status_unchecked; - return msg; - } - lock_rw_unlock(&e->lock); - } - k.qtype = qtype; - } - - /* fill common RR types for ANY response to avoid requery */ - if(qtype == LDNS_RR_TYPE_ANY) { - return fill_any(env, qname, qnamelen, qtype, qclass, region); - } - - return NULL; -} - -int -dns_cache_store(struct module_env* env, struct query_info* msgqinf, - struct reply_info* msgrep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags) -{ - struct reply_info* rep = NULL; - /* alloc, malloc properly (not in region, like msg is) */ - rep = reply_info_copy(msgrep, env->alloc, NULL); - if(!rep) - return 0; - /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. - * the env->now is added to message and RRsets in this routine. */ - /* the leeway is used to invalidate other rrsets earlier */ - - if(is_referral) { - /* store rrsets */ - struct rrset_ref ref; - size_t i; - for(i=0; i<rep->rrset_count; i++) { - packed_rrset_ttl_add((struct packed_rrset_data*) - rep->rrsets[i]->entry.data, *env->now); - ref.key = rep->rrsets[i]; - ref.id = rep->rrsets[i]->id; - /*ignore ret: it was in the cache, ref updated */ - /* no leeway for typeNS */ - (void)rrset_cache_update(env->rrset_cache, &ref, - env->alloc, *env->now + - ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS - && !pside) ? 0:leeway)); - } - free(rep); - return 1; - } else { - /* store msg, and rrsets */ - struct query_info qinf; - hashvalue_type h; - - qinf = *msgqinf; - qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len); - if(!qinf.qname) { - reply_info_parsedelete(rep, env->alloc); - return 0; - } - /* fixup flags to be sensible for a reply based on the cache */ - /* this module means that RA is available. It is an answer QR. - * Not AA from cache. Not CD in cache (depends on client bit). */ - rep->flags |= (BIT_RA | BIT_QR); - rep->flags &= ~(BIT_AA | BIT_CD); - h = query_info_hash(&qinf, flags); - dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep, - region); - /* qname is used inside query_info_entrysetup, and set to - * NULL. If it has not been used, free it. free(0) is safe. */ - free(qinf.qname); - } - return 1; -} - -int -dns_cache_prefetch_adjust(struct module_env* env, struct query_info* qinfo, - time_t adjust, uint16_t flags) -{ - struct msgreply_entry* msg; - msg = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len, - qinfo->qtype, qinfo->qclass, flags, *env->now, 1); - if(msg) { - struct reply_info* rep = (struct reply_info*)msg->entry.data; - if(rep) { - rep->prefetch_ttl += adjust; - lock_rw_unlock(&msg->entry.lock); - return 1; - } - lock_rw_unlock(&msg->entry.lock); - } - return 0; -} diff --git a/external/unbound/services/cache/dns.h b/external/unbound/services/cache/dns.h deleted file mode 100644 index 0dfb68874..000000000 --- a/external/unbound/services/cache/dns.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * services/cache/dns.h - Cache services for DNS using msg and rrset caches. - * - * 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 - * - * This file contains the DNS cache. - */ - -#ifndef SERVICES_CACHE_DNS_H -#define SERVICES_CACHE_DNS_H -#include "util/storage/lruhash.h" -#include "util/data/msgreply.h" -struct module_env; -struct query_info; -struct reply_info; -struct regional; -struct delegpt; - -/** - * Region allocated message reply - */ -struct dns_msg { - /** query info */ - struct query_info qinfo; - /** reply info - ptr to packed repinfo structure */ - struct reply_info *rep; -}; - -/** - * Allocate a dns_msg with malloc/alloc structure and store in dns cache. - * - * @param env: environment, with alloc structure and dns cache. - * @param qinf: query info, the query for which answer is stored. - * this is allocated in a region, and will be copied to malloc area - * before insertion. - * @param rep: reply in dns_msg from dns_alloc_msg for example. - * this is allocated in a region, and will be copied to malloc area - * before insertion. - * @param is_referral: If true, then the given message to be stored is a - * referral. The cache implementation may use this as a hint. - * It will store only the RRsets, not the message. - * @param leeway: TTL value, if not 0, other rrsets are considered expired - * that many seconds before actual TTL expiry. - * @param pside: if true, information came from a server which was fetched - * from the parentside of the zonecut. This means that the type NS - * can be updated to full TTL even in prefetch situations. - * @param region: region to allocate better entries from cache into. - * (used when is_referral is false). - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @return 0 on alloc error (out of memory). - */ -int dns_cache_store(struct module_env* env, struct query_info* qinf, - struct reply_info* rep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags); - -/** - * Store message in the cache. Stores in message cache and rrset cache. - * Both qinfo and rep should be malloced and are put in the cache. - * They should not be used after this call, as they are then in shared cache. - * Does not return errors, they are logged and only lead to less cache. - * - * @param env: module environment with the DNS cache. - * @param qinfo: query info - * @param hash: hash over qinfo. - * @param rep: reply info, together with qinfo makes up the message. - * Adjusts the reply info TTLs to absolute time. - * @param leeway: TTL value, if not 0, other rrsets are considered expired - * that many seconds before actual TTL expiry. - * @param pside: if true, information came from a server which was fetched - * from the parentside of the zonecut. This means that the type NS - * can be updated to full TTL even in prefetch situations. - * @param qrep: message that can be altered with better rrs from cache. - * @param region: to allocate into for qmsg. - */ -void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region); - -/** - * Find a delegation from the cache. - * @param env: module environment with the DNS cache. - * @param qname: query name. - * @param qnamelen: length of qname. - * @param qtype: query type. - * @param qclass: query class. - * @param region: where to allocate result delegation. - * @param msg: if not NULL, delegation message is returned here, synthesized - * from the cache. - * @param timenow: the time now, for checking if TTL on cache entries is OK. - * @return new delegation or NULL on error or if not found in cache. - */ -struct delegpt* dns_cache_find_delegation(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct regional* region, struct dns_msg** msg, time_t timenow); - -/** - * generate dns_msg from cached message - * @param env: module environment with the DNS cache. NULL if the LRU from cache - * does not need to be touched. - * @param q: query info, contains qname that will make up the dns message. - * @param r: reply info that, together with qname, will make up the dns message. - * @param region: where to allocate dns message. - * @param now: the time now, for check if TTL on cache entry is ok. - * @param scratch: where to allocate temporary data. - * */ -struct dns_msg* tomsg(struct module_env* env, struct query_info* q, - struct reply_info* r, struct regional* region, time_t now, - struct regional* scratch); - -/** - * Find cached message - * @param env: module environment with the DNS cache. - * @param qname: query name. - * @param qnamelen: length of qname. - * @param qtype: query type. - * @param qclass: query class. - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @param region: where to allocate result. - * @param scratch: where to allocate temporary data. - * @return new response message (alloced in region, rrsets do not have IDs). - * or NULL on error or if not found in cache. - * TTLs are made relative to the current time. - */ -struct dns_msg* dns_cache_lookup(struct module_env* env, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch); - -/** - * find and add A and AAAA records for missing nameservers in delegpt - * @param env: module environment with rrset cache - * @param qclass: which class to look in. - * @param region: where to store new dp info. - * @param dp: delegation point to fill missing entries. - * @return false on alloc failure. - */ -int cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp); - -/** - * Utility, create new, unpacked data structure for cache response. - * QR bit set, no AA. Query set as indicated. Space for number of rrsets. - * @param qname: query section name - * @param qnamelen: len of qname - * @param qtype: query section type - * @param qclass: query section class - * @param region: where to alloc. - * @param capacity: number of rrsets space to create in the array. - * @return new dns_msg struct or NULL on mem fail. - */ -struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, struct regional* region, size_t capacity); - -/** - * Add rrset to authority section in unpacked dns_msg message. Must have enough - * space left, does not grow the array. - * @param msg: msg to put it in. - * @param region: region to alloc in - * @param rrset: to add in authority section - * @param now: now. - * @return true if worked, false on fail - */ -int dns_msg_authadd(struct dns_msg* msg, struct regional* region, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Adjust the prefetch_ttl for a cached message. This adds a value to the - * prefetch ttl - postponing the time when it will be prefetched for future - * incoming queries. - * @param env: module environment with caches and time. - * @param qinfo: query info for the query that needs adjustment. - * @param adjust: time in seconds to add to the prefetch_leeway. - * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. - * @return false if not in cache. true if added. - */ -int dns_cache_prefetch_adjust(struct module_env* env, struct query_info* qinfo, - time_t adjust, uint16_t flags); - -#endif /* SERVICES_CACHE_DNS_H */ diff --git a/external/unbound/services/cache/infra.c b/external/unbound/services/cache/infra.c deleted file mode 100644 index 314c85ef5..000000000 --- a/external/unbound/services/cache/infra.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - * services/cache/infra.c - infrastructure cache, server rtt and capabilities - * - * 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 - * - * This file contains the infrastructure cache. - */ -#include "config.h" -#include "sldns/rrdef.h" -#include "sldns/str2wire.h" -#include "services/cache/infra.h" -#include "util/storage/slabhash.h" -#include "util/storage/lookup3.h" -#include "util/data/dname.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/config_file.h" -#include "iterator/iterator.h" - -/** Timeout when only a single probe query per IP is allowed. */ -#define PROBE_MAXRTO 12000 /* in msec */ - -/** number of timeouts for a type when the domain can be blocked ; - * even if another type has completely rtt maxed it, the different type - * can do this number of packets (until those all timeout too) */ -#define TIMEOUT_COUNT_MAX 3 - -/** ratelimit value for delegation point */ -int infra_dp_ratelimit = 0; - -/** ratelimit value for client ip addresses, - * in queries per second. */ -int infra_ip_ratelimit = 0; - -size_t -infra_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct infra_key* key = (struct infra_key*)k; - return sizeof(*key) + sizeof(struct infra_data) + key->namelen - + lock_get_mem(&key->entry.lock); -} - -int -infra_compfunc(void* key1, void* key2) -{ - struct infra_key* k1 = (struct infra_key*)key1; - struct infra_key* k2 = (struct infra_key*)key2; - int r = sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen); - if(r != 0) - return r; - if(k1->namelen != k2->namelen) { - if(k1->namelen < k2->namelen) - return -1; - return 1; - } - return query_dname_compare(k1->zonename, k2->zonename); -} - -void -infra_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct infra_key* key = (struct infra_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key->zonename); - free(key); -} - -void -infra_deldatafunc(void* d, void* ATTR_UNUSED(arg)) -{ - struct infra_data* data = (struct infra_data*)d; - free(data); -} - -size_t -rate_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct rate_key* key = (struct rate_key*)k; - return sizeof(*key) + sizeof(struct rate_data) + key->namelen - + lock_get_mem(&key->entry.lock); -} - -int -rate_compfunc(void* key1, void* key2) -{ - struct rate_key* k1 = (struct rate_key*)key1; - struct rate_key* k2 = (struct rate_key*)key2; - if(k1->namelen != k2->namelen) { - if(k1->namelen < k2->namelen) - return -1; - return 1; - } - return query_dname_compare(k1->name, k2->name); -} - -void -rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct rate_key* key = (struct rate_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key->name); - free(key); -} - -void -rate_deldatafunc(void* d, void* ATTR_UNUSED(arg)) -{ - struct rate_data* data = (struct rate_data*)d; - free(data); -} - -/** find or create element in domainlimit tree */ -static struct domain_limit_data* domain_limit_findcreate( - struct infra_cache* infra, char* name) -{ - uint8_t* nm; - int labs; - size_t nmlen; - struct domain_limit_data* d; - - /* parse name */ - nm = sldns_str2wire_dname(name, &nmlen); - if(!nm) { - log_err("could not parse %s", name); - return NULL; - } - labs = dname_count_labels(nm); - - /* can we find it? */ - d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits, - nm, nmlen, labs, LDNS_RR_CLASS_IN); - if(d) { - free(nm); - return d; - } - - /* create it */ - d = (struct domain_limit_data*)calloc(1, sizeof(*d)); - if(!d) { - free(nm); - return NULL; - } - d->node.node.key = &d->node; - d->node.name = nm; - d->node.len = nmlen; - d->node.labs = labs; - d->node.dclass = LDNS_RR_CLASS_IN; - d->lim = -1; - d->below = -1; - if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen, - labs, LDNS_RR_CLASS_IN)) { - log_err("duplicate element in domainlimit tree"); - free(nm); - free(d); - return NULL; - } - return d; -} - -/** insert rate limit configuration into lookup tree */ -static int infra_ratelimit_cfg_insert(struct infra_cache* infra, - struct config_file* cfg) -{ - struct config_str2list* p; - struct domain_limit_data* d; - for(p = cfg->ratelimit_for_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); - if(!d) - return 0; - d->lim = atoi(p->str2); - } - for(p = cfg->ratelimit_below_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); - if(!d) - return 0; - d->below = atoi(p->str2); - } - return 1; -} - -struct infra_cache* -infra_create(struct config_file* cfg) -{ - struct infra_cache* infra = (struct infra_cache*)calloc(1, - sizeof(struct infra_cache)); - size_t maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ - sizeof(struct infra_data)+INFRA_BYTES_NAME); - infra->hosts = slabhash_create(cfg->infra_cache_slabs, - INFRA_HOST_STARTSIZE, maxmem, &infra_sizefunc, &infra_compfunc, - &infra_delkeyfunc, &infra_deldatafunc, NULL); - if(!infra->hosts) { - free(infra); - return NULL; - } - infra->host_ttl = cfg->host_ttl; - name_tree_init(&infra->domain_limits); - infra_dp_ratelimit = cfg->ratelimit; - if(cfg->ratelimit != 0) { - infra->domain_rates = slabhash_create(cfg->ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ratelimit_size, - &rate_sizefunc, &rate_compfunc, &rate_delkeyfunc, - &rate_deldatafunc, NULL); - if(!infra->domain_rates) { - infra_delete(infra); - return NULL; - } - /* insert config data into ratelimits */ - if(!infra_ratelimit_cfg_insert(infra, cfg)) { - infra_delete(infra); - return NULL; - } - name_tree_init_parents(&infra->domain_limits); - } - infra_ip_ratelimit = cfg->ip_ratelimit; - infra->client_ip_rates = slabhash_create(cfg->ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, - &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL); - if(!infra->client_ip_rates) { - infra_delete(infra); - return NULL; - } - return infra; -} - -/** delete domain_limit entries */ -static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - if(n) { - free(((struct domain_limit_data*)n)->node.name); - free(n); - } -} - -void -infra_delete(struct infra_cache* infra) -{ - if(!infra) - return; - slabhash_delete(infra->hosts); - slabhash_delete(infra->domain_rates); - traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); - slabhash_delete(infra->client_ip_rates); - free(infra); -} - -struct infra_cache* -infra_adjust(struct infra_cache* infra, struct config_file* cfg) -{ - size_t maxmem; - if(!infra) - return infra_create(cfg); - infra->host_ttl = cfg->host_ttl; - maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ - sizeof(struct infra_data)+INFRA_BYTES_NAME); - if(maxmem != slabhash_get_size(infra->hosts) || - cfg->infra_cache_slabs != infra->hosts->size) { - infra_delete(infra); - infra = infra_create(cfg); - } - return infra; -} - -/** calculate the hash value for a host key - * set use_port to a non-0 number to use the port in - * the hash calculation; 0 to ignore the port.*/ -static hashvalue_type -hash_addr(struct sockaddr_storage* addr, socklen_t addrlen, - int use_port) -{ - hashvalue_type h = 0xab; - /* select the pieces to hash, some OS have changing data inside */ - if(addr_is_ip6(addr, addrlen)) { - struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; - h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h); - if(use_port){ - h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); - } - h = hashlittle(&in6->sin6_addr, INET6_SIZE, h); - } else { - struct sockaddr_in* in = (struct sockaddr_in*)addr; - h = hashlittle(&in->sin_family, sizeof(in->sin_family), h); - if(use_port){ - h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); - } - h = hashlittle(&in->sin_addr, INET_SIZE, h); - } - return h; -} - -/** calculate infra hash for a key */ -static hashvalue_type -hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name) -{ - return dname_query_hash(name, hash_addr(addr, addrlen, 1)); -} - -/** lookup version that does not check host ttl (you check it) */ -struct lruhash_entry* -infra_lookup_nottl(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, int wr) -{ - struct infra_key k; - k.addrlen = addrlen; - memcpy(&k.addr, addr, addrlen); - k.namelen = namelen; - k.zonename = name; - k.entry.hash = hash_infra(addr, addrlen, name); - k.entry.key = (void*)&k; - k.entry.data = NULL; - return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr); -} - -/** init the data elements */ -static void -data_entry_init(struct infra_cache* infra, struct lruhash_entry* e, - time_t timenow) -{ - struct infra_data* data = (struct infra_data*)e->data; - data->ttl = timenow + infra->host_ttl; - rtt_init(&data->rtt); - data->edns_version = 0; - data->edns_lame_known = 0; - data->probedelay = 0; - data->isdnsseclame = 0; - data->rec_lame = 0; - data->lame_type_A = 0; - data->lame_other = 0; - data->timeout_A = 0; - data->timeout_AAAA = 0; - data->timeout_other = 0; -} - -/** - * Create and init a new entry for a host - * @param infra: infra structure with config parameters. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name. - * @param tm: time now. - * @return: the new entry or NULL on malloc failure. - */ -static struct lruhash_entry* -new_entry(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, time_t tm) -{ - struct infra_data* data; - struct infra_key* key = (struct infra_key*)malloc(sizeof(*key)); - if(!key) - return NULL; - data = (struct infra_data*)malloc(sizeof(struct infra_data)); - if(!data) { - free(key); - return NULL; - } - key->zonename = memdup(name, namelen); - if(!key->zonename) { - free(key); - free(data); - return NULL; - } - key->namelen = namelen; - lock_rw_init(&key->entry.lock); - key->entry.hash = hash_infra(addr, addrlen, name); - key->entry.key = (void*)key; - key->entry.data = (void*)data; - key->addrlen = addrlen; - memcpy(&key->addr, addr, addrlen); - data_entry_init(infra, &key->entry, tm); - return &key->entry; -} - -int -infra_host(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, time_t timenow, - int* edns_vs, uint8_t* edns_lame_known, int* to) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 0); - struct infra_data* data; - int wr = 0; - if(e && ((struct infra_data*)e->data)->ttl < timenow) { - /* it expired, try to reuse existing entry */ - int old = ((struct infra_data*)e->data)->rtt.rto; - uint8_t tA = ((struct infra_data*)e->data)->timeout_A; - uint8_t tAAAA = ((struct infra_data*)e->data)->timeout_AAAA; - uint8_t tother = ((struct infra_data*)e->data)->timeout_other; - lock_rw_unlock(&e->lock); - e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1); - if(e) { - /* if its still there we have a writelock, init */ - /* re-initialise */ - /* do not touch lameness, it may be valid still */ - data_entry_init(infra, e, timenow); - wr = 1; - /* TOP_TIMEOUT remains on reuse */ - if(old >= USEFUL_SERVER_TOP_TIMEOUT) { - ((struct infra_data*)e->data)->rtt.rto - = USEFUL_SERVER_TOP_TIMEOUT; - ((struct infra_data*)e->data)->timeout_A = tA; - ((struct infra_data*)e->data)->timeout_AAAA = tAAAA; - ((struct infra_data*)e->data)->timeout_other = tother; - } - } - } - if(!e) { - /* insert new entry */ - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - data = (struct infra_data*)e->data; - *edns_vs = data->edns_version; - *edns_lame_known = data->edns_lame_known; - *to = rtt_timeout(&data->rtt); - slabhash_insert(infra->hosts, e->hash, e, data, NULL); - return 1; - } - /* use existing entry */ - data = (struct infra_data*)e->data; - *edns_vs = data->edns_version; - *edns_lame_known = data->edns_lame_known; - *to = rtt_timeout(&data->rtt); - if(*to >= PROBE_MAXRTO && rtt_notimeout(&data->rtt)*4 <= *to) { - /* delay other queries, this is the probe query */ - if(!wr) { - lock_rw_unlock(&e->lock); - e = infra_lookup_nottl(infra, addr,addrlen,nm,nmlen, 1); - if(!e) { /* flushed from cache real fast, no use to - allocate just for the probedelay */ - return 1; - } - data = (struct infra_data*)e->data; - } - /* add 999 to round up the timeout value from msec to sec, - * then add a whole second so it is certain that this probe - * has timed out before the next is allowed */ - data->probedelay = timenow + ((*to)+1999)/1000; - } - lock_rw_unlock(&e->lock); - return 1; -} - -int -infra_set_lame(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, time_t timenow, - int dnsseclame, int reclame, uint16_t qtype) -{ - struct infra_data* data; - struct lruhash_entry* e; - int needtoinsert = 0; - e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1); - if(!e) { - /* insert it */ - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) { - log_err("set_lame: malloc failure"); - return 0; - } - needtoinsert = 1; - } else if( ((struct infra_data*)e->data)->ttl < timenow) { - /* expired, reuse existing entry */ - data_entry_init(infra, e, timenow); - } - /* got an entry, now set the zone lame */ - data = (struct infra_data*)e->data; - /* merge data (if any) */ - if(dnsseclame) - data->isdnsseclame = 1; - if(reclame) - data->rec_lame = 1; - if(!dnsseclame && !reclame && qtype == LDNS_RR_TYPE_A) - data->lame_type_A = 1; - if(!dnsseclame && !reclame && qtype != LDNS_RR_TYPE_A) - data->lame_other = 1; - /* done */ - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return 1; -} - -void -infra_update_tcp_works(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm, - size_t nmlen) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - if(!e) - return; /* doesn't exist */ - data = (struct infra_data*)e->data; - if(data->rtt.rto >= RTT_MAX_TIMEOUT) - /* do not disqualify this server altogether, it is better - * than nothing */ - data->rtt.rto = RTT_MAX_TIMEOUT-1000; - lock_rw_unlock(&e->lock); -} - -int -infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, int qtype, - int roundtrip, int orig_rtt, time_t timenow) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - int needtoinsert = 0; - int rto = 1; - if(!e) { - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - needtoinsert = 1; - } else if(((struct infra_data*)e->data)->ttl < timenow) { - data_entry_init(infra, e, timenow); - } - /* have an entry, update the rtt */ - data = (struct infra_data*)e->data; - if(roundtrip == -1) { - rtt_lost(&data->rtt, orig_rtt); - if(qtype == LDNS_RR_TYPE_A) { - if(data->timeout_A < TIMEOUT_COUNT_MAX) - data->timeout_A++; - } else if(qtype == LDNS_RR_TYPE_AAAA) { - if(data->timeout_AAAA < TIMEOUT_COUNT_MAX) - data->timeout_AAAA++; - } else { - if(data->timeout_other < TIMEOUT_COUNT_MAX) - data->timeout_other++; - } - } else { - /* if we got a reply, but the old timeout was above server - * selection height, delete the timeout so the server is - * fully available again */ - if(rtt_unclamped(&data->rtt) >= USEFUL_SERVER_TOP_TIMEOUT) - rtt_init(&data->rtt); - rtt_update(&data->rtt, roundtrip); - data->probedelay = 0; - if(qtype == LDNS_RR_TYPE_A) - data->timeout_A = 0; - else if(qtype == LDNS_RR_TYPE_AAAA) - data->timeout_AAAA = 0; - else data->timeout_other = 0; - } - if(data->rtt.rto > 0) - rto = data->rtt.rto; - - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return rto; -} - -long long infra_get_host_rto(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm, - size_t nmlen, struct rtt_info* rtt, int* delay, time_t timenow, - int* tA, int* tAAAA, int* tother) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 0); - struct infra_data* data; - long long ttl = -2; - if(!e) return -1; - data = (struct infra_data*)e->data; - if(data->ttl >= timenow) { - ttl = (long long)(data->ttl - timenow); - memmove(rtt, &data->rtt, sizeof(*rtt)); - if(timenow < data->probedelay) - *delay = (int)(data->probedelay - timenow); - else *delay = 0; - } - *tA = (int)data->timeout_A; - *tAAAA = (int)data->timeout_AAAA; - *tother = (int)data->timeout_other; - lock_rw_unlock(&e->lock); - return ttl; -} - -int -infra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* nm, size_t nmlen, int edns_version, - time_t timenow) -{ - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - nm, nmlen, 1); - struct infra_data* data; - int needtoinsert = 0; - if(!e) { - if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) - return 0; - needtoinsert = 1; - } else if(((struct infra_data*)e->data)->ttl < timenow) { - data_entry_init(infra, e, timenow); - } - /* have an entry, update the rtt, and the ttl */ - data = (struct infra_data*)e->data; - /* do not update if noEDNS and stored is yesEDNS */ - if(!(edns_version == -1 && (data->edns_version != -1 && - data->edns_lame_known))) { - data->edns_version = edns_version; - data->edns_lame_known = 1; - } - - if(needtoinsert) - slabhash_insert(infra->hosts, e->hash, e, e->data, NULL); - else { lock_rw_unlock(&e->lock); } - return 1; -} - -int -infra_get_lame_rtt(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, uint16_t qtype, - int* lame, int* dnsseclame, int* reclame, int* rtt, time_t timenow) -{ - struct infra_data* host; - struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, - name, namelen, 0); - if(!e) - return 0; - host = (struct infra_data*)e->data; - *rtt = rtt_unclamped(&host->rtt); - if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay - && rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) { - /* single probe for this domain, and we are not probing */ - /* unless the query type allows a probe to happen */ - if(qtype == LDNS_RR_TYPE_A) { - if(host->timeout_A >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } else if(qtype == LDNS_RR_TYPE_AAAA) { - if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } else { - if(host->timeout_other >= TIMEOUT_COUNT_MAX) - *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - } - } - if(timenow > host->ttl) { - /* expired entry */ - /* see if this can be a re-probe of an unresponsive server */ - /* minus 1000 because that is outside of the RTTBAND, so - * blacklisted servers stay blacklisted if this is chosen */ - if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) { - lock_rw_unlock(&e->lock); - *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; - *lame = 0; - *dnsseclame = 0; - *reclame = 0; - return 1; - } - lock_rw_unlock(&e->lock); - return 0; - } - /* check lameness first */ - if(host->lame_type_A && qtype == LDNS_RR_TYPE_A) { - lock_rw_unlock(&e->lock); - *lame = 1; - *dnsseclame = 0; - *reclame = 0; - return 1; - } else if(host->lame_other && qtype != LDNS_RR_TYPE_A) { - lock_rw_unlock(&e->lock); - *lame = 1; - *dnsseclame = 0; - *reclame = 0; - return 1; - } else if(host->isdnsseclame) { - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 1; - *reclame = 0; - return 1; - } else if(host->rec_lame) { - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 0; - *reclame = 1; - return 1; - } - /* no lameness for this type of query */ - lock_rw_unlock(&e->lock); - *lame = 0; - *dnsseclame = 0; - *reclame = 0; - return 1; -} - -int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, - size_t namelen) -{ - int labs = dname_count_labels(name); - struct domain_limit_data* d = (struct domain_limit_data*) - name_tree_lookup(&infra->domain_limits, name, namelen, labs, - LDNS_RR_CLASS_IN); - if(!d) return infra_dp_ratelimit; - - if(d->node.labs == labs && d->lim != -1) - return d->lim; /* exact match */ - - /* find 'below match' */ - if(d->node.labs == labs) - d = (struct domain_limit_data*)d->node.parent; - while(d) { - if(d->below != -1) - return d->below; - d = (struct domain_limit_data*)d->node.parent; - } - return infra_dp_ratelimit; -} - -size_t ip_rate_sizefunc(void* k, void* ATTR_UNUSED(d)) -{ - struct ip_rate_key* key = (struct ip_rate_key*)k; - return sizeof(*key) + sizeof(struct ip_rate_data) - + lock_get_mem(&key->entry.lock); -} - -int ip_rate_compfunc(void* key1, void* key2) -{ - struct ip_rate_key* k1 = (struct ip_rate_key*)key1; - struct ip_rate_key* k2 = (struct ip_rate_key*)key2; - return sockaddr_cmp_addr(&k1->addr, k1->addrlen, - &k2->addr, k2->addrlen); -} - -void ip_rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) -{ - struct ip_rate_key* key = (struct ip_rate_key*)k; - if(!key) - return; - lock_rw_destroy(&key->entry.lock); - free(key); -} - -/** find data item in array, for write access, caller unlocks */ -static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, - uint8_t* name, size_t namelen, int wr) -{ - struct rate_key key; - hashvalue_type h = dname_query_hash(name, 0xab); - memset(&key, 0, sizeof(key)); - key.name = name; - key.namelen = namelen; - key.entry.hash = h; - return slabhash_lookup(infra->domain_rates, h, &key, wr); -} - -/** find data item in array for ip addresses */ -struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, int wr) -{ - struct ip_rate_key key; - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); - memset(&key, 0, sizeof(key)); - key.addr = repinfo->addr; - key.addrlen = repinfo->addrlen; - key.entry.hash = h; - return slabhash_lookup(infra->client_ip_rates, h, &key, wr); -} - -/** create rate data item for name, number 1 in now */ -static void infra_create_ratedata(struct infra_cache* infra, - uint8_t* name, size_t namelen, time_t timenow) -{ - hashvalue_type h = dname_query_hash(name, 0xab); - struct rate_key* k = (struct rate_key*)calloc(1, sizeof(*k)); - struct rate_data* d = (struct rate_data*)calloc(1, sizeof(*d)); - if(!k || !d) { - free(k); - free(d); - return; /* alloc failure */ - } - k->namelen = namelen; - k->name = memdup(name, namelen); - if(!k->name) { - free(k); - free(d); - return; /* alloc failure */ - } - lock_rw_init(&k->entry.lock); - k->entry.hash = h; - k->entry.key = k; - k->entry.data = d; - d->qps[0] = 1; - d->timestamp[0] = timenow; - slabhash_insert(infra->domain_rates, h, &k->entry, d, NULL); -} - -/** create rate data item for ip address */ -static void infra_ip_create_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow) -{ - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); - struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); - struct ip_rate_data* d = (struct ip_rate_data*)calloc(1, sizeof(*d)); - if(!k || !d) { - free(k); - free(d); - return; /* alloc failure */ - } - k->addr = repinfo->addr; - k->addrlen = repinfo->addrlen; - lock_rw_init(&k->entry.lock); - k->entry.hash = h; - k->entry.key = k; - k->entry.data = d; - d->qps[0] = 1; - d->timestamp[0] = timenow; - slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); -} - -/** find the second and return its rate counter, if none, remove oldest */ -static int* infra_rate_find_second(void* data, time_t t) -{ - struct rate_data* d = (struct rate_data*)data; - int i, oldest; - for(i=0; i<RATE_WINDOW; i++) { - if(d->timestamp[i] == t) - return &(d->qps[i]); - } - /* remove oldest timestamp, and insert it at t with 0 qps */ - oldest = 0; - for(i=0; i<RATE_WINDOW; i++) { - if(d->timestamp[i] < d->timestamp[oldest]) - oldest = i; - } - d->timestamp[oldest] = t; - d->qps[oldest] = 0; - return &(d->qps[oldest]); -} - -int infra_rate_max(void* data, time_t now) -{ - struct rate_data* d = (struct rate_data*)data; - int i, max = 0; - for(i=0; i<RATE_WINDOW; i++) { - if(now-d->timestamp[i] <= RATE_WINDOW) { - if(d->qps[i] > max) - max = d->qps[i]; - } - } - return max; -} - -int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - int lim, max; - struct lruhash_entry* entry; - - if(!infra_dp_ratelimit) - return 1; /* not enabled */ - - /* find ratelimit */ - lim = infra_find_ratelimit(infra, name, namelen); - - /* find or insert ratedata */ - entry = infra_find_ratedata(infra, name, namelen, 1); - if(entry) { - int premax = infra_rate_max(entry->data, timenow); - int* cur = infra_rate_find_second(entry->data, timenow); - (*cur)++; - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - if(premax < lim && max >= lim) { - char buf[257]; - dname_str(name, buf); - verbose(VERB_OPS, "ratelimit exceeded %s %d", buf, lim); - } - return (max < lim); - } - - /* create */ - infra_create_ratedata(infra, name, namelen, timenow); - return (1 < lim); -} - -void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - struct lruhash_entry* entry; - int* cur; - if(!infra_dp_ratelimit) - return; /* not enabled */ - entry = infra_find_ratedata(infra, name, namelen, 1); - if(!entry) return; /* not cached */ - cur = infra_rate_find_second(entry->data, timenow); - if((*cur) > 0) - (*cur)--; - lock_rw_unlock(&entry->lock); -} - -int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) -{ - struct lruhash_entry* entry; - int lim, max; - if(!infra_dp_ratelimit) - return 0; /* not enabled */ - - /* find ratelimit */ - lim = infra_find_ratelimit(infra, name, namelen); - - /* find current rate */ - entry = infra_find_ratedata(infra, name, namelen, 0); - if(!entry) - return 0; /* not cached */ - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - return (max >= lim); -} - -size_t -infra_get_mem(struct infra_cache* infra) -{ - size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts); - if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates); - if(infra->client_ip_rates) s += slabhash_get_mem(infra->client_ip_rates); - /* ignore domain_limits because walk through tree is big */ - return s; -} - -int infra_ip_ratelimit_inc(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow) -{ - int max; - struct lruhash_entry* entry; - - /* not enabled */ - if(!infra_ip_ratelimit) { - return 1; - } - /* find or insert ratedata */ - entry = infra_find_ip_ratedata(infra, repinfo, 1); - if(entry) { - int premax = infra_rate_max(entry->data, timenow); - int* cur = infra_rate_find_second(entry->data, timenow); - (*cur)++; - max = infra_rate_max(entry->data, timenow); - lock_rw_unlock(&entry->lock); - - if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) { - char client_ip[128]; - addr_to_str((struct sockaddr_storage *)&repinfo->addr, - repinfo->addrlen, client_ip, sizeof(client_ip)); - verbose(VERB_OPS, "ratelimit exceeded %s %d", client_ip, - infra_ip_ratelimit); - } - return (max <= infra_ip_ratelimit); - } - - /* create */ - infra_ip_create_ratedata(infra, repinfo, timenow); - return 1; -} diff --git a/external/unbound/services/cache/infra.h b/external/unbound/services/cache/infra.h deleted file mode 100644 index 6f9471a39..000000000 --- a/external/unbound/services/cache/infra.h +++ /dev/null @@ -1,462 +0,0 @@ -/* - * services/cache/infra.h - infrastructure cache, server rtt and capabilities - * - * 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 - * - * This file contains the infrastructure cache, as well as rate limiting. - * Note that there are two sorts of rate-limiting here: - * - Pre-cache, per-query rate limiting (query ratelimits) - * - Post-cache, per-domain name rate limiting (infra-ratelimits) - */ - -#ifndef SERVICES_CACHE_INFRA_H -#define SERVICES_CACHE_INFRA_H -#include "util/storage/lruhash.h" -#include "util/storage/dnstree.h" -#include "util/rtt.h" -#include "util/netevent.h" -#include "util/data/msgreply.h" -struct slabhash; -struct config_file; - -/** - * Host information kept for every server, per zone. - */ -struct infra_key { - /** the host address. */ - struct sockaddr_storage addr; - /** length of addr. */ - socklen_t addrlen; - /** zone name in wireformat */ - uint8_t* zonename; - /** length of zonename */ - size_t namelen; - /** hash table entry, data of type infra_data. */ - struct lruhash_entry entry; -}; - -/** - * Host information encompasses host capabilities and retransmission timeouts. - * And lameness information (notAuthoritative, noEDNS, Recursive) - */ -struct infra_data { - /** TTL value for this entry. absolute time. */ - time_t ttl; - - /** time in seconds (absolute) when probing re-commences, 0 disabled */ - time_t probedelay; - /** round trip times for timeout calculation */ - struct rtt_info rtt; - - /** edns version that the host supports, -1 means no EDNS */ - int edns_version; - /** if the EDNS lameness is already known or not. - * EDNS lame is when EDNS queries or replies are dropped, - * and cause a timeout */ - uint8_t edns_lame_known; - - /** is the host lame (does not serve the zone authoritatively), - * or is the host dnssec lame (does not serve DNSSEC data) */ - uint8_t isdnsseclame; - /** is the host recursion lame (not AA, but RA) */ - uint8_t rec_lame; - /** the host is lame (not authoritative) for A records */ - uint8_t lame_type_A; - /** the host is lame (not authoritative) for other query types */ - uint8_t lame_other; - - /** timeouts counter for type A */ - uint8_t timeout_A; - /** timeouts counter for type AAAA */ - uint8_t timeout_AAAA; - /** timeouts counter for others */ - uint8_t timeout_other; -}; - -/** - * Infra cache - */ -struct infra_cache { - /** The hash table with hosts */ - struct slabhash* hosts; - /** TTL value for host information, in seconds */ - int host_ttl; - /** hash table with query rates per name: rate_key, rate_data */ - struct slabhash* domain_rates; - /** ratelimit settings for domains, struct domain_limit_data */ - rbtree_type domain_limits; - /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ - struct slabhash* client_ip_rates; -}; - -/** ratelimit, unless overridden by domain_limits, 0 is off */ -extern int infra_dp_ratelimit; - -/** - * ratelimit settings for domains - */ -struct domain_limit_data { - /** key for rbtree, must be first in struct, name of domain */ - struct name_tree_node node; - /** ratelimit for exact match with this name, -1 if not set */ - int lim; - /** ratelimit for names below this name, -1 if not set */ - int below; -}; - -/** - * key for ratelimit lookups, a domain name - */ -struct rate_key { - /** lruhash key entry */ - struct lruhash_entry entry; - /** domain name in uncompressed wireformat */ - uint8_t* name; - /** length of name */ - size_t namelen; -}; - -/** ip ratelimit, 0 is off */ -extern int infra_ip_ratelimit; - -/** - * key for ip_ratelimit lookups, a source IP. - */ -struct ip_rate_key { - /** lruhash key entry */ - struct lruhash_entry entry; - /** client ip information */ - struct sockaddr_storage addr; - /** length of address */ - socklen_t addrlen; -}; - -/** number of seconds to track qps rate */ -#define RATE_WINDOW 2 - -/** - * Data for ratelimits per domain name - * It is incremented when a non-cache-lookup happens for that domain name. - * The name is the delegation point we have for the name. - * If a new delegation point is found (a referral reply), the previous - * delegation point is decremented, and the new one is charged with the query. - */ -struct rate_data { - /** queries counted, for that second. 0 if not in use. */ - int qps[RATE_WINDOW]; - /** what the timestamp is of the qps array members, counter is - * valid for that timestamp. Usually now and now-1. */ - time_t timestamp[RATE_WINDOW]; -}; - -#define ip_rate_data rate_data - -/** infra host cache default hash lookup size */ -#define INFRA_HOST_STARTSIZE 32 -/** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ -#define INFRA_BYTES_NAME 14 - -/** - * Create infra cache. - * @param cfg: config parameters or NULL for defaults. - * @return: new infra cache, or NULL. - */ -struct infra_cache* infra_create(struct config_file* cfg); - -/** - * Delete infra cache. - * @param infra: infrastructure cache to delete. - */ -void infra_delete(struct infra_cache* infra); - -/** - * Adjust infra cache to use updated configuration settings. - * This may clean the cache. Operates a bit like realloc. - * There may be no threading or use by other threads. - * @param infra: existing cache. If NULL a new infra cache is returned. - * @param cfg: config options. - * @return the new infra cache pointer or NULL on error. - */ -struct infra_cache* infra_adjust(struct infra_cache* infra, - struct config_file* cfg); - -/** - * Plain find infra data function (used by the the other functions) - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone. - * @param namelen: length of domain name. - * @param wr: if true, writelock, else readlock. - * @return the entry, could be expired (this is not checked) or NULL. - */ -struct lruhash_entry* infra_lookup_nottl(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, - size_t namelen, int wr); - -/** - * Find host information to send a packet. Creates new entry if not found. - * Lameness is empty. EDNS is 0 (try with first), and rtt is returned for - * the first message to it. - * Use this to send a packet only, because it also locks out others when - * probing is restricted. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone. - * @param namelen: length of domain name. - * @param timenow: what time it is now. - * @param edns_vs: edns version it supports, is returned. - * @param edns_lame_known: if EDNS lame (EDNS is dropped in transit) has - * already been probed, is returned. - * @param to: timeout to use, is returned. - * @return: 0 on error. - */ -int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, - time_t timenow, int* edns_vs, uint8_t* edns_lame_known, int* to); - -/** - * Set a host to be lame for the given zone. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: domain name of zone apex. - * @param namelen: length of domain name. - * @param timenow: what time it is now. - * @param dnsseclame: if true the host is set dnssec lame. - * if false, the host is marked lame (not serving the zone). - * @param reclame: if true host is a recursor not AA server. - * if false, dnsseclame or marked lame. - * @param qtype: the query type for which it is lame. - * @return: 0 on error. - */ -int infra_set_lame(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, time_t timenow, int dnsseclame, - int reclame, uint16_t qtype); - -/** - * Update rtt information for the host. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name - * @param namelen: zone name length - * @param qtype: query type. - * @param roundtrip: estimate of roundtrip time in milliseconds or -1 for - * timeout. - * @param orig_rtt: original rtt for the query that timed out (roundtrip==-1). - * ignored if roundtrip != -1. - * @param timenow: what time it is now. - * @return: 0 on error. new rto otherwise. - */ -int infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, int qtype, - int roundtrip, int orig_rtt, time_t timenow); - -/** - * Update information for the host, store that a TCP transaction works. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name - */ -void infra_update_tcp_works(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen); - -/** - * Update edns information for the host. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: name of zone - * @param namelen: length of name - * @param edns_version: the version that it publishes. - * If it is known to support EDNS then no-EDNS is not stored over it. - * @param timenow: what time it is now. - * @return: 0 on error. - */ -int infra_edns_update(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, int edns_version, time_t timenow); - -/** - * Get Lameness information and average RTT if host is in the cache. - * This information is to be used for server selection. - * @param infra: infrastructure cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name. - * @param namelen: zone name length. - * @param qtype: the query to be made. - * @param lame: if function returns true, this returns lameness of the zone. - * @param dnsseclame: if function returns true, this returns if the zone - * is dnssec-lame. - * @param reclame: if function returns true, this is if it is recursion lame. - * @param rtt: if function returns true, this returns avg rtt of the server. - * The rtt value is unclamped and reflects recent timeouts. - * @param timenow: what time it is now. - * @return if found in cache, or false if not (or TTL bad). - */ -int infra_get_lame_rtt(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* name, size_t namelen, uint16_t qtype, - int* lame, int* dnsseclame, int* reclame, int* rtt, time_t timenow); - -/** - * Get additional (debug) info on timing. - * @param infra: infra cache. - * @param addr: host address. - * @param addrlen: length of addr. - * @param name: zone name - * @param namelen: zone name length - * @param rtt: the rtt_info is copied into here (caller alloced return struct). - * @param delay: probe delay (if any). - * @param timenow: what time it is now. - * @param tA: timeout counter on type A. - * @param tAAAA: timeout counter on type AAAA. - * @param tother: timeout counter on type other. - * @return TTL the infra host element is valid for. If -1: not found in cache. - * TTL -2: found but expired. - */ -long long infra_get_host_rto(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, - size_t namelen, struct rtt_info* rtt, int* delay, time_t timenow, - int* tA, int* tAAAA, int* tother); - -/** - * Increment the query rate counter for a delegation point. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - * @return 1 if it could be incremented. 0 if the increment overshot the - * ratelimit or if in the previous second the ratelimit was exceeded. - * Failures like alloc failures are not returned (probably as 1). - */ -int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** - * Decrement the query rate counter for a delegation point. - * Because the reply received for the delegation point was pleasant, - * we do not charge this delegation point with it (i.e. it was a referral). - * Should call it with same second as when inc() was called. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - */ -void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** - * See if the query rate counter for a delegation point is exceeded. - * So, no queries are going to be allowed. - * @param infra: infra cache. - * @param name: zone name - * @param namelen: zone name length - * @param timenow: what time it is now. - * @return true if exceeded. - */ -int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); - -/** find the maximum rate stored, not too old. 0 if no information. */ -int infra_rate_max(void* data, time_t now); - -/** find the ratelimit in qps for a domain */ -int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, - size_t namelen); - -/** Update query ratelimit hash and decide - * whether or not a query should be dropped. - * @param infra: infra cache - * @param repinfo: information about client - * @param timenow: what time it is now. - * @return 1 if it could be incremented. 0 if the increment overshot the - * ratelimit and the query should be dropped. */ -int infra_ip_ratelimit_inc(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow); - -/** - * Get memory used by the infra cache. - * @param infra: infrastructure cache. - * @return memory in use in bytes. - */ -size_t infra_get_mem(struct infra_cache* infra); - -/** calculate size for the hashtable, does not count size of lameness, - * so the hashtable is a fixed number of items */ -size_t infra_sizefunc(void* k, void* d); - -/** compare two addresses, returns -1, 0, or +1 */ -int infra_compfunc(void* key1, void* key2); - -/** delete key, and destroy the lock */ -void infra_delkeyfunc(void* k, void* arg); - -/** delete data and destroy the lameness hashtable */ -void infra_deldatafunc(void* d, void* arg); - -/** calculate size for the hashtable */ -size_t rate_sizefunc(void* k, void* d); - -/** compare two names, returns -1, 0, or +1 */ -int rate_compfunc(void* key1, void* key2); - -/** delete key, and destroy the lock */ -void rate_delkeyfunc(void* k, void* arg); - -/** delete data */ -void rate_deldatafunc(void* d, void* arg); - -/* calculate size for the client ip hashtable */ -size_t ip_rate_sizefunc(void* k, void* d); - -/* compare two addresses */ -int ip_rate_compfunc(void* key1, void* key2); - -/* delete key, and destroy the lock */ -void ip_rate_delkeyfunc(void* d, void* arg); - -/* delete data */ -#define ip_rate_deldatafunc rate_deldatafunc - -#endif /* SERVICES_CACHE_INFRA_H */ diff --git a/external/unbound/services/cache/rrset.c b/external/unbound/services/cache/rrset.c deleted file mode 100644 index 7e5732b76..000000000 --- a/external/unbound/services/cache/rrset.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * services/cache/rrset.c - Resource record set cache. - * - * 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 - * - * This file contains the rrset cache. - */ -#include "config.h" -#include "services/cache/rrset.h" -#include "sldns/rrdef.h" -#include "util/storage/slabhash.h" -#include "util/config_file.h" -#include "util/data/packed_rrset.h" -#include "util/data/msgreply.h" -#include "util/regional.h" -#include "util/alloc.h" - -void -rrset_markdel(void* key) -{ - struct ub_packed_rrset_key* r = (struct ub_packed_rrset_key*)key; - r->id = 0; -} - -struct rrset_cache* rrset_cache_create(struct config_file* cfg, - struct alloc_cache* alloc) -{ - size_t slabs = (cfg?cfg->rrset_cache_slabs:HASH_DEFAULT_SLABS); - size_t startarray = HASH_DEFAULT_STARTARRAY; - size_t maxmem = (cfg?cfg->rrset_cache_size:HASH_DEFAULT_MAXMEM); - - struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs, - startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare, - ub_rrset_key_delete, rrset_data_delete, alloc); - slabhash_setmarkdel(&r->table, &rrset_markdel); - return r; -} - -void rrset_cache_delete(struct rrset_cache* r) -{ - if(!r) - return; - slabhash_delete(&r->table); - /* slabhash delete also does free(r), since table is first in struct*/ -} - -struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r, - struct config_file* cfg, struct alloc_cache* alloc) -{ - if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size || - cfg->rrset_cache_size != slabhash_get_size(&r->table)) - { - rrset_cache_delete(r); - r = rrset_cache_create(cfg, alloc); - } - return r; -} - -void -rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_type hash, rrset_id_type id) -{ - struct lruhash* table = slabhash_gettable(&r->table, hash); - /* - * This leads to locking problems, deadlocks, if the caller is - * holding any other rrset lock. - * Because a lookup through the hashtable does: - * tablelock -> entrylock (for that entry caller holds) - * And this would do - * entrylock(already held) -> tablelock - * And if two threads do this, it results in deadlock. - * So, the caller must not hold entrylock. - */ - lock_quick_lock(&table->lock); - /* we have locked the hash table, the item can still be deleted. - * because it could already have been reclaimed, but not yet set id=0. - * This is because some lruhash routines have lazy deletion. - * so, we must acquire a lock on the item to verify the id != 0. - * also, with hash not changed, we are using the right slab. - */ - lock_rw_rdlock(&key->entry.lock); - if(key->id == id && key->entry.hash == hash) { - lru_touch(table, &key->entry); - } - lock_rw_unlock(&key->entry.lock); - lock_quick_unlock(&table->lock); -} - -/** see if rrset needs to be updated in the cache */ -static int -need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) -{ - struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; - struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; - /* o store if rrset has been validated - * everything better than bogus data - * secure is preferred */ - if( newd->security == sec_status_secure && - cached->security != sec_status_secure) - return 1; - if( cached->security == sec_status_bogus && - newd->security != sec_status_bogus && !equal) - return 1; - /* o if current RRset is more trustworthy - insert it */ - if( newd->trust > cached->trust ) { - /* if the cached rrset is bogus, and this one equal, - * do not update the TTL - let it expire. */ - if(equal && cached->ttl >= timenow && - cached->security == sec_status_bogus) - return 0; - return 1; - } - /* o item in cache has expired */ - if( cached->ttl < timenow ) - return 1; - /* o same trust, but different in data - insert it */ - if( newd->trust == cached->trust && !equal ) { - /* if this is type NS, do not 'stick' to owner that changes - * the NS RRset, but use the old TTL for the new data, and - * update to fetch the latest data. ttl is not expired, because - * that check was before this one. */ - if(ns) { - size_t i; - newd->ttl = cached->ttl; - for(i=0; i<(newd->count+newd->rrsig_count); i++) - if(newd->rr_ttl[i] > newd->ttl) - newd->rr_ttl[i] = newd->ttl; - } - return 1; - } - return 0; -} - -/** Update RRSet special key ID */ -static void -rrset_update_id(struct rrset_ref* ref, struct alloc_cache* alloc) -{ - /* this may clear the cache and invalidate lock below */ - uint64_t newid = alloc_get_id(alloc); - /* obtain writelock */ - lock_rw_wrlock(&ref->key->entry.lock); - /* check if it was deleted in the meantime, if so, skip update */ - if(ref->key->id == ref->id) { - ref->key->id = newid; - ref->id = newid; - } - lock_rw_unlock(&ref->key->entry.lock); -} - -int -rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, - struct alloc_cache* alloc, time_t timenow) -{ - struct lruhash_entry* e; - struct ub_packed_rrset_key* k = ref->key; - hashvalue_type h = k->entry.hash; - uint16_t rrset_type = ntohs(k->rk.type); - int equal = 0; - log_assert(ref->id != 0 && k->id != 0); - log_assert(k->rk.dname != NULL); - /* looks up item with a readlock - no editing! */ - if((e=slabhash_lookup(&r->table, h, k, 0)) != 0) { - /* return id and key as they will be used in the cache - * since the lruhash_insert, if item already exists, deallocs - * the passed key in favor of the already stored key. - * because of the small gap (see below) this key ptr and id - * may prove later to be already deleted, which is no problem - * as it only makes a cache miss. - */ - ref->key = (struct ub_packed_rrset_key*)e->key; - ref->id = ref->key->id; - equal = rrsetdata_equal((struct packed_rrset_data*)k->entry. - data, (struct packed_rrset_data*)e->data); - if(!need_to_update_rrset(k->entry.data, e->data, timenow, - equal, (rrset_type==LDNS_RR_TYPE_NS))) { - /* cache is superior, return that value */ - lock_rw_unlock(&e->lock); - ub_packed_rrset_parsedelete(k, alloc); - if(equal) return 2; - return 1; - } - lock_rw_unlock(&e->lock); - /* Go on and insert the passed item. - * small gap here, where entry is not locked. - * possibly entry is updated with something else. - * we then overwrite that with our data. - * this is just too bad, its cache anyway. */ - /* use insert to update entry to manage lruhash - * cache size values nicely. */ - } - log_assert(ref->key->id != 0); - slabhash_insert(&r->table, h, &k->entry, k->entry.data, alloc); - if(e) { - /* For NSEC, NSEC3, DNAME, when rdata is updated, update - * the ID number so that proofs in message cache are - * invalidated */ - if((rrset_type == LDNS_RR_TYPE_NSEC - || rrset_type == LDNS_RR_TYPE_NSEC3 - || rrset_type == LDNS_RR_TYPE_DNAME) && !equal) { - rrset_update_id(ref, alloc); - } - return 1; - } - return 0; -} - -struct ub_packed_rrset_key* -rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow, - int wr) -{ - struct lruhash_entry* e; - struct ub_packed_rrset_key key; - - key.entry.key = &key; - key.entry.data = NULL; - key.rk.dname = qname; - key.rk.dname_len = qnamelen; - key.rk.type = htons(qtype); - key.rk.rrset_class = htons(qclass); - key.rk.flags = flags; - - key.entry.hash = rrset_key_hash(&key.rk); - - if((e = slabhash_lookup(&r->table, key.entry.hash, &key, wr))) { - /* check TTL */ - struct packed_rrset_data* data = - (struct packed_rrset_data*)e->data; - if(timenow > data->ttl) { - lock_rw_unlock(&e->lock); - return NULL; - } - /* we're done */ - return (struct ub_packed_rrset_key*)e->key; - } - return NULL; -} - -int -rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow) -{ - size_t i; - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only lock items once */ - lock_rw_rdlock(&ref[i].key->entry.lock); - if(ref[i].id != ref[i].key->id || timenow > - ((struct packed_rrset_data*)(ref[i].key->entry.data)) - ->ttl) { - /* failure! rollback our readlocks */ - rrset_array_unlock(ref, i+1); - return 0; - } - } - return 1; -} - -void -rrset_array_unlock(struct rrset_ref* ref, size_t count) -{ - size_t i; - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only unlock items once */ - lock_rw_unlock(&ref[i].key->entry.lock); - } -} - -void -rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, - struct rrset_ref* ref, size_t count) -{ - hashvalue_type* h; - size_t i; - if(count > RR_COUNT_MAX || !(h = (hashvalue_type*)regional_alloc( - scratch, sizeof(hashvalue_type)*count))) { - log_warn("rrset LRU: memory allocation failed"); - h = NULL; - } else /* store hash values */ - for(i=0; i<count; i++) - h[i] = ref[i].key->entry.hash; - /* unlock */ - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only unlock items once */ - lock_rw_unlock(&ref[i].key->entry.lock); - } - if(h) { - /* LRU touch, with no rrset locks held */ - for(i=0; i<count; i++) { - if(i>0 && ref[i].key == ref[i-1].key) - continue; /* only touch items once */ - rrset_cache_touch(r, ref[i].key, h[i], ref[i].id); - } - } -} - -void -rrset_update_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now) -{ - struct packed_rrset_data* updata = - (struct packed_rrset_data*)rrset->entry.data; - struct lruhash_entry* e; - struct packed_rrset_data* cachedata; - - /* hash it again to make sure it has a hash */ - rrset->entry.hash = rrset_key_hash(&rrset->rk); - - e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 1); - if(!e) - return; /* not in the cache anymore */ - cachedata = (struct packed_rrset_data*)e->data; - if(!rrsetdata_equal(updata, cachedata)) { - lock_rw_unlock(&e->lock); - return; /* rrset has changed in the meantime */ - } - /* update the cached rrset */ - if(updata->security > cachedata->security) { - size_t i; - if(updata->trust > cachedata->trust) - cachedata->trust = updata->trust; - cachedata->security = updata->security; - /* for NS records only shorter TTLs, other types: update it */ - if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_NS || - updata->ttl+now < cachedata->ttl || - cachedata->ttl < now || - updata->security == sec_status_bogus) { - cachedata->ttl = updata->ttl + now; - for(i=0; i<cachedata->count+cachedata->rrsig_count; i++) - cachedata->rr_ttl[i] = updata->rr_ttl[i]+now; - } - } - lock_rw_unlock(&e->lock); -} - -void -rrset_check_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now) -{ - struct packed_rrset_data* updata = - (struct packed_rrset_data*)rrset->entry.data; - struct lruhash_entry* e; - struct packed_rrset_data* cachedata; - - /* hash it again to make sure it has a hash */ - rrset->entry.hash = rrset_key_hash(&rrset->rk); - - e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 0); - if(!e) - return; /* not in the cache anymore */ - cachedata = (struct packed_rrset_data*)e->data; - if(now > cachedata->ttl || !rrsetdata_equal(updata, cachedata)) { - lock_rw_unlock(&e->lock); - return; /* expired, or rrset has changed in the meantime */ - } - if(cachedata->security > updata->security) { - updata->security = cachedata->security; - if(cachedata->security == sec_status_bogus) { - size_t i; - updata->ttl = cachedata->ttl - now; - for(i=0; i<cachedata->count+cachedata->rrsig_count; i++) - if(cachedata->rr_ttl[i] < now) - updata->rr_ttl[i] = 0; - else updata->rr_ttl[i] = - cachedata->rr_ttl[i]-now; - } - if(cachedata->trust > updata->trust) - updata->trust = cachedata->trust; - } - lock_rw_unlock(&e->lock); -} - -void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, - uint16_t type, uint16_t dclass, uint32_t flags) -{ - struct ub_packed_rrset_key key; - key.entry.key = &key; - key.rk.dname = nm; - key.rk.dname_len = nmlen; - key.rk.rrset_class = htons(dclass); - key.rk.type = htons(type); - key.rk.flags = flags; - key.entry.hash = rrset_key_hash(&key.rk); - slabhash_remove(&r->table, key.entry.hash, &key); -} diff --git a/external/unbound/services/cache/rrset.h b/external/unbound/services/cache/rrset.h deleted file mode 100644 index d5439ef08..000000000 --- a/external/unbound/services/cache/rrset.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * services/cache/rrset.h - Resource record set cache. - * - * 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 - * - * This file contains the rrset cache. - */ - -#ifndef SERVICES_CACHE_RRSET_H -#define SERVICES_CACHE_RRSET_H -#include "util/storage/lruhash.h" -#include "util/storage/slabhash.h" -#include "util/data/packed_rrset.h" -struct config_file; -struct alloc_cache; -struct rrset_ref; -struct regional; - -/** - * The rrset cache - * Thin wrapper around hashtable, like a typedef. - */ -struct rrset_cache { - /** uses partitioned hash table */ - struct slabhash table; -}; - -/** - * Create rrset cache - * @param cfg: config settings or NULL for defaults. - * @param alloc: initial default rrset key allocation. - * @return: NULL on error. - */ -struct rrset_cache* rrset_cache_create(struct config_file* cfg, - struct alloc_cache* alloc); - -/** - * Delete rrset cache - * @param r: rrset cache to delete. - */ -void rrset_cache_delete(struct rrset_cache* r); - -/** - * Adjust settings of the cache to settings from the config file. - * May purge the cache. May recreate the cache. - * There may be no threading or use by other threads. - * @param r: rrset cache to adjust (like realloc). - * @param cfg: config settings or NULL for defaults. - * @param alloc: initial default rrset key allocation. - * @return 0 on error, or new rrset cache pointer on success. - */ -struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r, - struct config_file* cfg, struct alloc_cache* alloc); - -/** - * Touch rrset, with given pointer and id. - * Caller may not hold a lock on ANY rrset, this could give deadlock. - * - * This routine is faster than a hashtable lookup: - * o no bin_lock is acquired. - * o no walk through the bin-overflow-list. - * o no comparison of the entry key to find it. - * - * @param r: rrset cache. - * @param key: rrset key. Marked recently used (if it was not deleted - * before the lock is acquired, in that case nothing happens). - * @param hash: hash value of the item. Please read it from the key when - * you have it locked. Used to find slab from slabhash. - * @param id: used to check that the item is unchanged and not deleted. - */ -void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_type hash, rrset_id_type id); - -/** - * Update an rrset in the rrset cache. Stores the information for later use. - * Will lookup if the rrset is in the cache and perform an update if necessary. - * If the item was present, and superior, references are returned to that. - * The passed item is then deallocated with rrset_parsedelete. - * - * A superior rrset is: - * o rrset with better trust value. - * o same trust value, different rdata, newly passed rrset is inserted. - * If rdata is the same, TTL in the cache is updated. - * - * @param r: the rrset cache. - * @param ref: reference (ptr and id) to the rrset. Pass reference setup for - * the new rrset. The reference may be changed if the cached rrset is - * superior. - * Before calling the rrset is presumed newly allocated and changeable. - * Afer calling you do not hold a lock, and the rrset is inserted in - * the hashtable so you need a lock to change it. - * @param alloc: how to allocate (and deallocate) the special rrset key. - * @param timenow: current time (to see if ttl in cache is expired). - * @return: true if the passed reference is updated, false if it is unchanged. - * 0: reference unchanged, inserted in cache. - * 1: reference updated, item is inserted in cache. - * 2: reference updated, item in cache is considered superior. - * also the rdata is equal (but other parameters in cache are superior). - */ -int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, - struct alloc_cache* alloc, time_t timenow); - -/** - * Lookup rrset. You obtain read/write lock. You must unlock before lookup - * anything of else. - * @param r: the rrset cache. - * @param qname: name of rrset to lookup. - * @param qnamelen: length of name of rrset to lookup. - * @param qtype: type of rrset to lookup (host order). - * @param qclass: class of rrset to lookup (host order). - * @param flags: rrset flags, or 0. - * @param timenow: used to compare with TTL. - * @param wr: set true to get writelock. - * @return packed rrset key pointer. Remember to unlock the key.entry.lock. - * or NULL if could not be found or it was timed out. - */ -struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint32_t flags, time_t timenow, int wr); - -/** - * Obtain readlock on a (sorted) list of rrset references. - * Checks TTLs and IDs of the rrsets and rollbacks locking if not Ok. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - * @param timenow: used to compare with TTL. - * @return true on success, false on a failure, which can be that some - * RRsets have timed out, or that they do not exist any more, the - * RRsets have been purged from the cache. - * If true, you hold readlocks on all the ref items. - */ -int rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow); - -/** - * Unlock array (sorted) of rrset references. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - */ -void rrset_array_unlock(struct rrset_ref* ref, size_t count); - -/** - * Unlock array (sorted) of rrset references and at the same time - * touch LRU on the rrsets. It needs the scratch region for temporary - * storage as it uses the initial locks to obtain hash values. - * @param r: the rrset cache. In this cache LRU is updated. - * @param scratch: region for temporary storage of hash values. - * if memory allocation fails, the lru touch fails silently, - * but locks are released. memory errors are logged. - * @param ref: array of rrset references (key pointer and ID value). - * duplicate references are allowed and handled. - * @param count: size of array. - */ -void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, - struct rrset_ref* ref, size_t count); - -/** - * Update security status of an rrset. Looks up the rrset. - * If found, checks if rdata is equal. - * If so, it will update the security, trust and rrset-ttl values. - * The values are only updated if security is increased (towards secure). - * @param r: the rrset cache. - * @param rrset: which rrset to attempt to update. This rrset is left - * untouched. The rrset in the cache is updated in-place. - * @param now: current time. - */ -void rrset_update_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Looks up security status of an rrset. Looks up the rrset. - * If found, checks if rdata is equal, and entry did not expire. - * If so, it will update the security, trust and rrset-ttl values. - * @param r: the rrset cache. - * @param rrset: This rrset may change security status due to the cache. - * But its status will only improve, towards secure. - * @param now: current time. - */ -void rrset_check_sec_status(struct rrset_cache* r, - struct ub_packed_rrset_key* rrset, time_t now); - -/** - * Remove an rrset from the cache, by name and type and flags - * @param r: rrset cache - * @param nm: name of rrset - * @param nmlen: length of name - * @param type: type of rrset - * @param dclass: class of rrset, host order - * @param flags: flags of rrset, host order - */ -void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, - uint16_t type, uint16_t dclass, uint32_t flags); - -/** mark rrset to be deleted, set id=0 */ -void rrset_markdel(void* key); - -#endif /* SERVICES_CACHE_RRSET_H */ diff --git a/external/unbound/services/listen_dnsport.c b/external/unbound/services/listen_dnsport.c deleted file mode 100644 index 37ee9a6b9..000000000 --- a/external/unbound/services/listen_dnsport.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* - * services/listen_dnsport.c - listen on port 53 for incoming DNS queries. - * - * 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 - * - * This file has functions to get queries from clients. - */ -#include "config.h" -#ifdef HAVE_SYS_TYPES_H -# include <sys/types.h> -#endif -#include <sys/time.h> -#ifdef USE_TCP_FASTOPEN -#include <netinet/tcp.h> -#endif -#include "services/listen_dnsport.h" -#include "services/outside_network.h" -#include "util/netevent.h" -#include "util/log.h" -#include "util/config_file.h" -#include "util/net_help.h" -#include "sldns/sbuffer.h" - -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#include <fcntl.h> - -#ifdef HAVE_SYS_UN_H -#include <sys/un.h> -#endif - -#ifdef HAVE_SYSTEMD -#include <systemd/sd-daemon.h> -#endif - -/** number of queued TCP connections for listen() */ -#define TCP_BACKLOG 256 - -/** - * Debug print of the getaddrinfo returned address. - * @param addr: the address returned. - */ -static void -verbose_print_addr(struct addrinfo *addr) -{ - if(verbosity >= VERB_ALGO) { - char buf[100]; - void* sinaddr = &((struct sockaddr_in*)addr->ai_addr)->sin_addr; -#ifdef INET6 - if(addr->ai_family == AF_INET6) - sinaddr = &((struct sockaddr_in6*)addr->ai_addr)-> - sin6_addr; -#endif /* INET6 */ - if(inet_ntop(addr->ai_family, sinaddr, buf, - (socklen_t)sizeof(buf)) == 0) { - (void)strlcpy(buf, "(null)", sizeof(buf)); - } - buf[sizeof(buf)-1] = 0; - verbose(VERB_ALGO, "creating %s%s socket %s %d", - addr->ai_socktype==SOCK_DGRAM?"udp": - addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto", - addr->ai_family==AF_INET?"4": - addr->ai_family==AF_INET6?"6": - "_otherfam", buf, - ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port)); - } -} - -#ifdef HAVE_SYSTEMD -static int -systemd_get_activated(int family, int socktype, int listen, - struct sockaddr *addr, socklen_t addrlen, - const char *path) -{ - int i = 0; - int r = 0; - int s = -1; - const char* listen_pid, *listen_fds; - - /* We should use "listen" option only for stream protocols. For UDP it should be -1 */ - - if((r = sd_booted()) < 1) { - if(r == 0) - log_warn("systemd is not running"); - else - log_err("systemd sd_booted(): %s", strerror(-r)); - return -1; - } - - listen_pid = getenv("LISTEN_PID"); - listen_fds = getenv("LISTEN_FDS"); - - if (!listen_pid) { - log_warn("Systemd mandatory ENV variable is not defined: LISTEN_PID"); - return -1; - } - - if (!listen_fds) { - log_warn("Systemd mandatory ENV variable is not defined: LISTEN_FDS"); - return -1; - } - - if((r = sd_listen_fds(0)) < 1) { - if(r == 0) - log_warn("systemd: did not return socket, check unit configuration"); - else - log_err("systemd sd_listen_fds(): %s", strerror(-r)); - return -1; - } - - for(i = 0; i < r; i++) { - if(sd_is_socket(SD_LISTEN_FDS_START + i, family, socktype, listen)) { - s = SD_LISTEN_FDS_START + i; - break; - } - } - if (s == -1) { - if (addr) - log_err_addr("systemd sd_listen_fds()", - "no such socket", - (struct sockaddr_storage *)addr, addrlen); - else - log_err("systemd sd_listen_fds(): %s", path); - } - return s; -} -#endif - -int -create_udp_sock(int family, int socktype, struct sockaddr* addr, - socklen_t addrlen, int v6only, int* inuse, int* noproto, - int rcv, int snd, int listen, int* reuseport, int transparent, - int freebind, int use_systemd) -{ - int s; -#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) - int on=1; -#endif -#ifdef IPV6_MTU - int mtu = IPV6_MIN_MTU; -#endif -#if !defined(SO_RCVBUFFORCE) && !defined(SO_RCVBUF) - (void)rcv; -#endif -#if !defined(SO_SNDBUFFORCE) && !defined(SO_SNDBUF) - (void)snd; -#endif -#ifndef IPV6_V6ONLY - (void)v6only; -#endif -#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) - (void)transparent; -#endif -#if !defined(IP_FREEBIND) - (void)freebind; -#endif -#ifdef HAVE_SYSTEMD - int got_fd_from_systemd = 0; - - if (!use_systemd - || (use_systemd - && (s = systemd_get_activated(family, socktype, -1, addr, - addrlen, NULL)) == -1)) { -#else - (void)use_systemd; -#endif - if((s = socket(family, socktype, 0)) == -1) { - *inuse = 0; -#ifndef USE_WINSOCK - if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { - *noproto = 1; - return -1; - } - log_err("can't create socket: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEAFNOSUPPORT || - WSAGetLastError() == WSAEPROTONOSUPPORT) { - *noproto = 1; - return -1; - } - log_err("can't create socket: %s", - wsa_strerror(WSAGetLastError())); -#endif - *noproto = 0; - return -1; - } -#ifdef HAVE_SYSTEMD - } else { - got_fd_from_systemd = 1; - } -#endif - if(listen) { -#ifdef SO_REUSEADDR - if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, - (socklen_t)sizeof(on)) < 0) { -#ifndef USE_WINSOCK - log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", - strerror(errno)); - if(errno != ENOSYS) { - close(s); - *noproto = 0; - *inuse = 0; - return -1; - } -#else - log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); - *noproto = 0; - *inuse = 0; - return -1; -#endif - } -#endif /* SO_REUSEADDR */ -#ifdef SO_REUSEPORT - /* try to set SO_REUSEPORT so that incoming - * queries are distributed evenly among the receiving threads. - * Each thread must have its own socket bound to the same port, - * with SO_REUSEPORT set on each socket. - */ - if (reuseport && *reuseport && - setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on, - (socklen_t)sizeof(on)) < 0) { -#ifdef ENOPROTOOPT - if(errno != ENOPROTOOPT || verbosity >= 3) - log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s", - strerror(errno)); -#endif - /* this option is not essential, we can continue */ - *reuseport = 0; - } -#else - (void)reuseport; -#endif /* defined(SO_REUSEPORT) */ -#ifdef IP_TRANSPARENT - if (transparent && - setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on, - (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s", - strerror(errno)); - } -#elif defined(IP_BINDANY) - if (transparent && - setsockopt(s, (family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP), - (family == AF_INET6? IPV6_BINDANY:IP_BINDANY), - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s", - (family==AF_INET6?"V6":""), strerror(errno)); - } -#endif /* IP_TRANSPARENT || IP_BINDANY */ - } -#ifdef IP_FREEBIND - if(freebind && - setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on, - (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s", - strerror(errno)); - } -#endif /* IP_FREEBIND */ - if(rcv) { -#ifdef SO_RCVBUF - int got; - socklen_t slen = (socklen_t)sizeof(got); -# ifdef SO_RCVBUFFORCE - /* Linux specific: try to use root permission to override - * system limits on rcvbuf. The limit is stored in - * /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */ - if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv, - (socklen_t)sizeof(rcv)) < 0) { - if(errno != EPERM) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., SO_RCVBUFFORCE, " - "...) failed: %s", strerror(errno)); - close(s); -# else - log_err("setsockopt(..., SO_RCVBUFFORCE, " - "...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } -# endif /* SO_RCVBUFFORCE */ - if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv, - (socklen_t)sizeof(rcv)) < 0) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., SO_RCVBUF, " - "...) failed: %s", strerror(errno)); - close(s); -# else - log_err("setsockopt(..., SO_RCVBUF, " - "...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } - /* check if we got the right thing or if system - * reduced to some system max. Warn if so */ - if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got, - &slen) >= 0 && got < rcv/2) { - log_warn("so-rcvbuf %u was not granted. " - "Got %u. To fix: start with " - "root permissions(linux) or sysctl " - "bigger net.core.rmem_max(linux) or " - "kern.ipc.maxsockbuf(bsd) values.", - (unsigned)rcv, (unsigned)got); - } -# ifdef SO_RCVBUFFORCE - } -# endif -#endif /* SO_RCVBUF */ - } - /* first do RCVBUF as the receive buffer is more important */ - if(snd) { -#ifdef SO_SNDBUF - int got; - socklen_t slen = (socklen_t)sizeof(got); -# ifdef SO_SNDBUFFORCE - /* Linux specific: try to use root permission to override - * system limits on sndbuf. The limit is stored in - * /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */ - if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd, - (socklen_t)sizeof(snd)) < 0) { - if(errno != EPERM) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., SO_SNDBUFFORCE, " - "...) failed: %s", strerror(errno)); - close(s); -# else - log_err("setsockopt(..., SO_SNDBUFFORCE, " - "...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } -# endif /* SO_SNDBUFFORCE */ - if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd, - (socklen_t)sizeof(snd)) < 0) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., SO_SNDBUF, " - "...) failed: %s", strerror(errno)); - close(s); -# else - log_err("setsockopt(..., SO_SNDBUF, " - "...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } - /* check if we got the right thing or if system - * reduced to some system max. Warn if so */ - if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got, - &slen) >= 0 && got < snd/2) { - log_warn("so-sndbuf %u was not granted. " - "Got %u. To fix: start with " - "root permissions(linux) or sysctl " - "bigger net.core.wmem_max(linux) or " - "kern.ipc.maxsockbuf(bsd) values.", - (unsigned)snd, (unsigned)got); - } -# ifdef SO_SNDBUFFORCE - } -# endif -#endif /* SO_SNDBUF */ - } - if(family == AF_INET6) { -# if defined(IPV6_V6ONLY) - if(v6only) { - int val=(v6only==2)?0:1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - (void*)&val, (socklen_t)sizeof(val)) < 0) { -#ifndef USE_WINSOCK - log_err("setsockopt(..., IPV6_V6ONLY" - ", ...) failed: %s", strerror(errno)); - close(s); -#else - log_err("setsockopt(..., IPV6_V6ONLY" - ", ...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -#endif - *noproto = 0; - *inuse = 0; - return -1; - } - } -# endif -# if defined(IPV6_USE_MIN_MTU) - /* - * There is no fragmentation of IPv6 datagrams - * during forwarding in the network. Therefore - * we do not send UDP datagrams larger than - * the minimum IPv6 MTU of 1280 octets. The - * EDNS0 message length can be larger if the - * network stack supports IPV6_USE_MIN_MTU. - */ - if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, - (void*)&on, (socklen_t)sizeof(on)) < 0) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., IPV6_USE_MIN_MTU, " - "...) failed: %s", strerror(errno)); - close(s); -# else - log_err("setsockopt(..., IPV6_USE_MIN_MTU, " - "...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } -# elif defined(IPV6_MTU) - /* - * On Linux, to send no larger than 1280, the PMTUD is - * disabled by default for datagrams anyway, so we set - * the MTU to use. - */ - if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU, - (void*)&mtu, (socklen_t)sizeof(mtu)) < 0) { -# ifndef USE_WINSOCK - log_err("setsockopt(..., IPV6_MTU, ...) failed: %s", - strerror(errno)); - close(s); -# else - log_err("setsockopt(..., IPV6_MTU, ...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } -# endif /* IPv6 MTU */ - } else if(family == AF_INET) { -# if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) -/* linux 3.15 has IP_PMTUDISC_OMIT, Hannes Frederic Sowa made it so that - * PMTU information is not accepted, but fragmentation is allowed - * if and only if the packet size exceeds the outgoing interface MTU - * (and also uses the interface mtu to determine the size of the packets). - * So there won't be any EMSGSIZE error. Against DNS fragmentation attacks. - * FreeBSD already has same semantics without setting the option. */ - int omit_set = 0; - int action; -# if defined(IP_PMTUDISC_OMIT) - action = IP_PMTUDISC_OMIT; - if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, - &action, (socklen_t)sizeof(action)) < 0) { - - if (errno != EINVAL) { - log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s", - strerror(errno)); - -# ifndef USE_WINSOCK - close(s); -# else - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } - } - else - { - omit_set = 1; - } -# endif - if (omit_set == 0) { - action = IP_PMTUDISC_DONT; - if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, - &action, (socklen_t)sizeof(action)) < 0) { - log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s", - strerror(errno)); -# ifndef USE_WINSOCK - close(s); -# else - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } - } -# elif defined(IP_DONTFRAG) - int off = 0; - if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG, - &off, (socklen_t)sizeof(off)) < 0) { - log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s", - strerror(errno)); -# ifndef USE_WINSOCK - close(s); -# else - closesocket(s); -# endif - *noproto = 0; - *inuse = 0; - return -1; - } -# endif /* IPv4 MTU */ - } - if( -#ifdef HAVE_SYSTEMD - !got_fd_from_systemd && -#endif - bind(s, (struct sockaddr*)addr, addrlen) != 0) { - *noproto = 0; - *inuse = 0; -#ifndef USE_WINSOCK -#ifdef EADDRINUSE - *inuse = (errno == EADDRINUSE); - /* detect freebsd jail with no ipv6 permission */ - if(family==AF_INET6 && errno==EINVAL) - *noproto = 1; - else if(errno != EADDRINUSE) { - log_err_addr("can't bind socket", strerror(errno), - (struct sockaddr_storage*)addr, addrlen); - } -#endif /* EADDRINUSE */ - close(s); -#else /* USE_WINSOCK */ - if(WSAGetLastError() != WSAEADDRINUSE && - WSAGetLastError() != WSAEADDRNOTAVAIL) { - log_err_addr("can't bind socket", - wsa_strerror(WSAGetLastError()), - (struct sockaddr_storage*)addr, addrlen); - } - closesocket(s); -#endif /* USE_WINSOCK */ - return -1; - } - if(!fd_set_nonblock(s)) { - *noproto = 0; - *inuse = 0; -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return -1; - } - return s; -} - -int -create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, - int* reuseport, int transparent, int mss, int freebind, int use_systemd) -{ - int s; -#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) - int on = 1; -#endif -#ifdef HAVE_SYSTEMD - int got_fd_from_systemd = 0; -#endif -#ifdef USE_TCP_FASTOPEN - int qlen; -#endif -#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) - (void)transparent; -#endif -#if !defined(IP_FREEBIND) - (void)freebind; -#endif - verbose_print_addr(addr); - *noproto = 0; -#ifdef HAVE_SYSTEMD - if (!use_systemd || - (use_systemd - && (s = systemd_get_activated(addr->ai_family, addr->ai_socktype, 1, - addr->ai_addr, addr->ai_addrlen, - NULL)) == -1)) { -#else - (void)use_systemd; -#endif - if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) { -#ifndef USE_WINSOCK - if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { - *noproto = 1; - return -1; - } - log_err("can't create socket: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEAFNOSUPPORT || - WSAGetLastError() == WSAEPROTONOSUPPORT) { - *noproto = 1; - return -1; - } - log_err("can't create socket: %s", - wsa_strerror(WSAGetLastError())); -#endif - return -1; - } - if (mss > 0) { -#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) - if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, (void*)&mss, - (socklen_t)sizeof(mss)) < 0) { - #ifndef USE_WINSOCK - log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s", - strerror(errno)); - #else - log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s", - wsa_strerror(WSAGetLastError())); - #endif - } else { - verbose(VERB_ALGO, - " tcp socket mss set to %d", mss); - } -#else - log_warn(" setsockopt(TCP_MAXSEG) unsupported"); -#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */ - } -#ifdef HAVE_SYSTEMD - } else { - got_fd_from_systemd = 1; - } -#endif -#ifdef SO_REUSEADDR - if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, - (socklen_t)sizeof(on)) < 0) { -#ifndef USE_WINSOCK - log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", - strerror(errno)); - close(s); -#else - log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -#endif - return -1; - } -#endif /* SO_REUSEADDR */ -#ifdef IP_FREEBIND - if (freebind && setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on, - (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s", - strerror(errno)); - } -#endif /* IP_FREEBIND */ -#ifdef SO_REUSEPORT - /* try to set SO_REUSEPORT so that incoming - * connections are distributed evenly among the receiving threads. - * Each thread must have its own socket bound to the same port, - * with SO_REUSEPORT set on each socket. - */ - if (reuseport && *reuseport && - setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on, - (socklen_t)sizeof(on)) < 0) { -#ifdef ENOPROTOOPT - if(errno != ENOPROTOOPT || verbosity >= 3) - log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s", - strerror(errno)); -#endif - /* this option is not essential, we can continue */ - *reuseport = 0; - } -#else - (void)reuseport; -#endif /* defined(SO_REUSEPORT) */ -#if defined(IPV6_V6ONLY) - if(addr->ai_family == AF_INET6 && v6only) { - if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - (void*)&on, (socklen_t)sizeof(on)) < 0) { -#ifndef USE_WINSOCK - log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s", - strerror(errno)); - close(s); -#else - log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -#endif - return -1; - } - } -#else - (void)v6only; -#endif /* IPV6_V6ONLY */ -#ifdef IP_TRANSPARENT - if (transparent && - setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on, - (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s", - strerror(errno)); - } -#elif defined(IP_BINDANY) - if (transparent && - setsockopt(s, (addr->ai_family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP), - (addr->ai_family == AF_INET6? IPV6_BINDANY:IP_BINDANY), - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s", - (addr->ai_family==AF_INET6?"V6":""), strerror(errno)); - } -#endif /* IP_TRANSPARENT || IP_BINDANY */ - if( -#ifdef HAVE_SYSTEMD - !got_fd_from_systemd && -#endif - bind(s, addr->ai_addr, addr->ai_addrlen) != 0) { -#ifndef USE_WINSOCK - /* detect freebsd jail with no ipv6 permission */ - if(addr->ai_family==AF_INET6 && errno==EINVAL) - *noproto = 1; - else { - log_err_addr("can't bind socket", strerror(errno), - (struct sockaddr_storage*)addr->ai_addr, - addr->ai_addrlen); - } - close(s); -#else - log_err_addr("can't bind socket", - wsa_strerror(WSAGetLastError()), - (struct sockaddr_storage*)addr->ai_addr, - addr->ai_addrlen); - closesocket(s); -#endif - return -1; - } - if(!fd_set_nonblock(s)) { -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return -1; - } - if(listen(s, TCP_BACKLOG) == -1) { -#ifndef USE_WINSOCK - log_err("can't listen: %s", strerror(errno)); - close(s); -#else - log_err("can't listen: %s", wsa_strerror(WSAGetLastError())); - closesocket(s); -#endif - return -1; - } -#ifdef USE_TCP_FASTOPEN - /* qlen specifies how many outstanding TFO requests to allow. Limit is a defense - against IP spoofing attacks as suggested in RFC7413 */ -#ifdef __APPLE__ - /* OS X implementation only supports qlen of 1 via this call. Actual - value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */ - qlen = 1; -#else - /* 5 is recommended on linux */ - qlen = 5; -#endif - if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, - sizeof(qlen))) == -1 ) { - log_err("Setting TCP Fast Open as server failed: %s", strerror(errno)); - } -#endif - return s; -} - -int -create_local_accept_sock(const char *path, int* noproto, int use_systemd) -{ -#ifdef HAVE_SYSTEMD - int ret; - - if (use_systemd && (ret = systemd_get_activated(AF_LOCAL, SOCK_STREAM, 1, NULL, 0, path)) != -1) - return ret; - else { -#endif -#ifdef HAVE_SYS_UN_H - int s; - struct sockaddr_un usock; -#ifndef HAVE_SYSTEMD - (void)use_systemd; -#endif - - verbose(VERB_ALGO, "creating unix socket %s", path); -#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN - /* this member exists on BSDs, not Linux */ - usock.sun_len = (unsigned)sizeof(usock); -#endif - usock.sun_family = AF_LOCAL; - /* length is 92-108, 104 on FreeBSD */ - (void)strlcpy(usock.sun_path, path, sizeof(usock.sun_path)); - - if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { - log_err("Cannot create local socket %s (%s)", - path, strerror(errno)); - return -1; - } - - if (unlink(path) && errno != ENOENT) { - /* The socket already exists and cannot be removed */ - log_err("Cannot remove old local socket %s (%s)", - path, strerror(errno)); - goto err; - } - - if (bind(s, (struct sockaddr *)&usock, - (socklen_t)sizeof(struct sockaddr_un)) == -1) { - log_err("Cannot bind local socket %s (%s)", - path, strerror(errno)); - goto err; - } - - if (!fd_set_nonblock(s)) { - log_err("Cannot set non-blocking mode"); - goto err; - } - - if (listen(s, TCP_BACKLOG) == -1) { - log_err("can't listen: %s", strerror(errno)); - goto err; - } - - (void)noproto; /*unused*/ - return s; - -err: -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return -1; - -#ifdef HAVE_SYSTEMD - } -#endif -#else - (void)use_systemd; - (void)path; - log_err("Local sockets are not supported"); - *noproto = 1; - return -1; -#endif -} - - -/** - * Create socket from getaddrinfo results - */ -static int -make_sock(int stype, const char* ifname, const char* port, - struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, - int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd) -{ - struct addrinfo *res = NULL; - int r, s, inuse, noproto; - hints->ai_socktype = stype; - *noip6 = 0; - if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) { -#ifdef USE_WINSOCK - if(r == EAI_NONAME && hints->ai_family == AF_INET6){ - *noip6 = 1; /* 'Host not found' for IP6 on winXP */ - return -1; - } -#endif - log_err("node %s:%s getaddrinfo: %s %s", - ifname?ifname:"default", port, gai_strerror(r), -#ifdef EAI_SYSTEM - r==EAI_SYSTEM?(char*)strerror(errno):"" -#else - "" -#endif - ); - return -1; - } - if(stype == SOCK_DGRAM) { - verbose_print_addr(res); - s = create_udp_sock(res->ai_family, res->ai_socktype, - (struct sockaddr*)res->ai_addr, res->ai_addrlen, - v6only, &inuse, &noproto, (int)rcv, (int)snd, 1, - reuseport, transparent, freebind, use_systemd); - if(s == -1 && inuse) { - log_err("bind: address already in use"); - } else if(s == -1 && noproto && hints->ai_family == AF_INET6){ - *noip6 = 1; - } - } else { - s = create_tcp_accept_sock(res, v6only, &noproto, reuseport, - transparent, tcp_mss, freebind, use_systemd); - if(s == -1 && noproto && hints->ai_family == AF_INET6){ - *noip6 = 1; - } - } - freeaddrinfo(res); - return s; -} - -/** make socket and first see if ifname contains port override info */ -static int -make_sock_port(int stype, const char* ifname, const char* port, - struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, - int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd) -{ - char* s = strchr(ifname, '@'); - if(s) { - /* override port with ifspec@port */ - char p[16]; - char newif[128]; - if((size_t)(s-ifname) >= sizeof(newif)) { - log_err("ifname too long: %s", ifname); - *noip6 = 0; - return -1; - } - if(strlen(s+1) >= sizeof(p)) { - log_err("portnumber too long: %s", ifname); - *noip6 = 0; - return -1; - } - (void)strlcpy(newif, ifname, sizeof(newif)); - newif[s-ifname] = 0; - (void)strlcpy(p, s+1, sizeof(p)); - p[strlen(s+1)]=0; - return make_sock(stype, newif, p, hints, v6only, noip6, - rcv, snd, reuseport, transparent, tcp_mss, freebind, use_systemd); - } - return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd, - reuseport, transparent, tcp_mss, freebind, use_systemd); -} - -/** - * Add port to open ports list. - * @param list: list head. changed. - * @param s: fd. - * @param ftype: if fd is UDP. - * @return false on failure. list in unchanged then. - */ -static int -port_insert(struct listen_port** list, int s, enum listen_type ftype) -{ - struct listen_port* item = (struct listen_port*)malloc( - sizeof(struct listen_port)); - if(!item) - return 0; - item->next = *list; - item->fd = s; - item->ftype = ftype; - *list = item; - return 1; -} - -/** set fd to receive source address packet info */ -static int -set_recvpktinfo(int s, int family) -{ -#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO) - int on = 1; -#else - (void)s; -#endif - if(family == AF_INET6) { -# ifdef IPV6_RECVPKTINFO - if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s", - strerror(errno)); - return 0; - } -# elif defined(IPV6_PKTINFO) - if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s", - strerror(errno)); - return 0; - } -# else - log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please " - "disable interface-automatic or do-ip6 in config"); - return 0; -# endif /* defined IPV6_RECVPKTINFO */ - - } else if(family == AF_INET) { -# ifdef IP_PKTINFO - if(setsockopt(s, IPPROTO_IP, IP_PKTINFO, - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s", - strerror(errno)); - return 0; - } -# elif defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR) - if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, - (void*)&on, (socklen_t)sizeof(on)) < 0) { - log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s", - strerror(errno)); - return 0; - } -# else - log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable " - "interface-automatic or do-ip4 in config"); - return 0; -# endif /* IP_PKTINFO */ - - } - return 1; -} - -/** - * Helper for ports_open. Creates one interface (or NULL for default). - * @param ifname: The interface ip address. - * @param do_auto: use automatic interface detection. - * If enabled, then ifname must be the wildcard name. - * @param do_udp: if udp should be used. - * @param do_tcp: if udp should be used. - * @param hints: for getaddrinfo. family and flags have to be set by caller. - * @param port: Port number to use (as string). - * @param list: list of open ports, appended to, changed to point to list head. - * @param rcv: receive buffer size for UDP - * @param snd: send buffer size for UDP - * @param ssl_port: ssl service port number - * @param reuseport: try to set SO_REUSEPORT if nonNULL and true. - * set to false on exit if reuseport failed due to no kernel support. - * @param transparent: set IP_TRANSPARENT socket option. - * @param tcp_mss: maximum segment size of tcp socket. default if zero. - * @param freebind: set IP_FREEBIND socket option. - * @param use_systemd: if true, fetch sockets from systemd. - * @param dnscrypt_port: dnscrypt service port number - * @return: returns false on error. - */ -static int -ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, - struct addrinfo *hints, const char* port, struct listen_port** list, - size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent, - int tcp_mss, int freebind, int use_systemd, int dnscrypt_port) -{ - int s, noip6=0; -#ifdef USE_DNSCRYPT - int is_dnscrypt = ((strchr(ifname, '@') && - atoi(strchr(ifname, '@')+1) == dnscrypt_port) || - (!strchr(ifname, '@') && atoi(port) == dnscrypt_port)); -#else - int is_dnscrypt = 0; - (void)dnscrypt_port; -#endif - - if(!do_udp && !do_tcp) - return 0; - if(do_auto) { - if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, - &noip6, rcv, snd, reuseport, transparent, - tcp_mss, freebind, use_systemd)) == -1) { - if(noip6) { - log_warn("IPv6 protocol not available"); - return 1; - } - return 0; - } - /* getting source addr packet info is highly non-portable */ - if(!set_recvpktinfo(s, hints->ai_family)) { -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return 0; - } - if(!port_insert(list, s, - is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) { -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return 0; - } - } else if(do_udp) { - /* regular udp socket */ - if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, - &noip6, rcv, snd, reuseport, transparent, - tcp_mss, freebind, use_systemd)) == -1) { - if(noip6) { - log_warn("IPv6 protocol not available"); - return 1; - } - return 0; - } - if(!port_insert(list, s, - is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) { -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return 0; - } - } - if(do_tcp) { - int is_ssl = ((strchr(ifname, '@') && - atoi(strchr(ifname, '@')+1) == ssl_port) || - (!strchr(ifname, '@') && atoi(port) == ssl_port)); - if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, - &noip6, 0, 0, reuseport, transparent, tcp_mss, - freebind, use_systemd)) == -1) { - if(noip6) { - /*log_warn("IPv6 protocol not available");*/ - return 1; - } - return 0; - } - if(is_ssl) - verbose(VERB_ALGO, "setup TCP for SSL service"); - if(!port_insert(list, s, is_ssl?listen_type_ssl: - (is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) { -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return 0; - } - } - return 1; -} - -/** - * Add items to commpoint list in front. - * @param c: commpoint to add. - * @param front: listen struct. - * @return: false on failure. - */ -static int -listen_cp_insert(struct comm_point* c, struct listen_dnsport* front) -{ - struct listen_list* item = (struct listen_list*)malloc( - sizeof(struct listen_list)); - if(!item) - return 0; - item->com = c; - item->next = front->cps; - front->cps = item; - return 1; -} - -struct listen_dnsport* -listen_create(struct comm_base* base, struct listen_port* ports, - size_t bufsize, int tcp_accept_count, void* sslctx, - struct dt_env* dtenv, comm_point_callback_type* cb, void *cb_arg) -{ - struct listen_dnsport* front = (struct listen_dnsport*) - malloc(sizeof(struct listen_dnsport)); - if(!front) - return NULL; - front->cps = NULL; - front->udp_buff = sldns_buffer_new(bufsize); -#ifdef USE_DNSCRYPT - front->dnscrypt_udp_buff = NULL; -#endif - if(!front->udp_buff) { - free(front); - return NULL; - } - - /* create comm points as needed */ - while(ports) { - struct comm_point* cp = NULL; - if(ports->ftype == listen_type_udp || - ports->ftype == listen_type_udp_dnscrypt) - cp = comm_point_create_udp(base, ports->fd, - front->udp_buff, cb, cb_arg); - else if(ports->ftype == listen_type_tcp || - ports->ftype == listen_type_tcp_dnscrypt) - cp = comm_point_create_tcp(base, ports->fd, - tcp_accept_count, bufsize, cb, cb_arg); - else if(ports->ftype == listen_type_ssl) { - cp = comm_point_create_tcp(base, ports->fd, - tcp_accept_count, bufsize, cb, cb_arg); - cp->ssl = sslctx; - } else if(ports->ftype == listen_type_udpancil || - ports->ftype == listen_type_udpancil_dnscrypt) - cp = comm_point_create_udp_ancil(base, ports->fd, - front->udp_buff, cb, cb_arg); - if(!cp) { - log_err("can't create commpoint"); - listen_delete(front); - return NULL; - } - cp->dtenv = dtenv; - cp->do_not_close = 1; -#ifdef USE_DNSCRYPT - if (ports->ftype == listen_type_udp_dnscrypt || - ports->ftype == listen_type_tcp_dnscrypt || - ports->ftype == listen_type_udpancil_dnscrypt) { - cp->dnscrypt = 1; - cp->dnscrypt_buffer = sldns_buffer_new(bufsize); - if(!cp->dnscrypt_buffer) { - log_err("can't alloc dnscrypt_buffer"); - comm_point_delete(cp); - listen_delete(front); - return NULL; - } - front->dnscrypt_udp_buff = cp->dnscrypt_buffer; - } -#endif - if(!listen_cp_insert(cp, front)) { - log_err("malloc failed"); - comm_point_delete(cp); - listen_delete(front); - return NULL; - } - ports = ports->next; - } - if(!front->cps) { - log_err("Could not open sockets to accept queries."); - listen_delete(front); - return NULL; - } - - return front; -} - -void -listen_list_delete(struct listen_list* list) -{ - struct listen_list *p = list, *pn; - while(p) { - pn = p->next; - comm_point_delete(p->com); - free(p); - p = pn; - } -} - -void -listen_delete(struct listen_dnsport* front) -{ - if(!front) - return; - listen_list_delete(front->cps); -#ifdef USE_DNSCRYPT - if(front->dnscrypt_udp_buff && - front->udp_buff != front->dnscrypt_udp_buff) { - sldns_buffer_free(front->dnscrypt_udp_buff); - } -#endif - sldns_buffer_free(front->udp_buff); - free(front); -} - -struct listen_port* -listening_ports_open(struct config_file* cfg, int* reuseport) -{ - struct listen_port* list = NULL; - struct addrinfo hints; - int i, do_ip4, do_ip6; - int do_tcp, do_auto; - char portbuf[32]; - snprintf(portbuf, sizeof(portbuf), "%d", cfg->port); - do_ip4 = cfg->do_ip4; - do_ip6 = cfg->do_ip6; - do_tcp = cfg->do_tcp; - do_auto = cfg->if_automatic && cfg->do_udp; - if(cfg->incoming_num_tcp == 0) - do_tcp = 0; - - /* getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - /* no name lookups on our listening ports */ - if(cfg->num_ifs > 0) - hints.ai_flags |= AI_NUMERICHOST; - hints.ai_family = AF_UNSPEC; -#ifndef INET6 - do_ip6 = 0; -#endif - if(!do_ip4 && !do_ip6) { - return NULL; - } - /* create ip4 and ip6 ports so that return addresses are nice. */ - if(do_auto || cfg->num_ifs == 0) { - if(do_ip6) { - hints.ai_family = AF_INET6; - if(!ports_create_if(do_auto?"::0":"::1", - do_auto, cfg->do_udp, do_tcp, - &hints, portbuf, &list, - cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, - cfg->dnscrypt_port)) { - listening_ports_free(list); - return NULL; - } - } - if(do_ip4) { - hints.ai_family = AF_INET; - if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1", - do_auto, cfg->do_udp, do_tcp, - &hints, portbuf, &list, - cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, - cfg->dnscrypt_port)) { - listening_ports_free(list); - return NULL; - } - } - } else for(i = 0; i<cfg->num_ifs; i++) { - if(str_is_ip6(cfg->ifs[i])) { - if(!do_ip6) - continue; - hints.ai_family = AF_INET6; - if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, - do_tcp, &hints, portbuf, &list, - cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, - cfg->dnscrypt_port)) { - listening_ports_free(list); - return NULL; - } - } else { - if(!do_ip4) - continue; - hints.ai_family = AF_INET; - if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, - do_tcp, &hints, portbuf, &list, - cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, - cfg->dnscrypt_port)) { - listening_ports_free(list); - return NULL; - } - } - } - return list; -} - -void listening_ports_free(struct listen_port* list) -{ - struct listen_port* nx; - while(list) { - nx = list->next; - if(list->fd != -1) { -#ifndef USE_WINSOCK - close(list->fd); -#else - closesocket(list->fd); -#endif - } - free(list); - list = nx; - } -} - -size_t listen_get_mem(struct listen_dnsport* listen) -{ - struct listen_list* p; - size_t s = sizeof(*listen) + sizeof(*listen->base) + - sizeof(*listen->udp_buff) + - sldns_buffer_capacity(listen->udp_buff); -#ifdef USE_DNSCRYPT - s += sizeof(*listen->dnscrypt_udp_buff); - if(listen->udp_buff != listen->dnscrypt_udp_buff){ - s += sldns_buffer_capacity(listen->dnscrypt_udp_buff); - } -#endif - for(p = listen->cps; p; p = p->next) { - s += sizeof(*p); - s += comm_point_get_mem(p->com); - } - return s; -} - -void listen_stop_accept(struct listen_dnsport* listen) -{ - /* do not stop the ones that have no tcp_free list - * (they have already stopped listening) */ - struct listen_list* p; - for(p=listen->cps; p; p=p->next) { - if(p->com->type == comm_tcp_accept && - p->com->tcp_free != NULL) { - comm_point_stop_listening(p->com); - } - } -} - -void listen_start_accept(struct listen_dnsport* listen) -{ - /* do not start the ones that have no tcp_free list, it is no - * use to listen to them because they have no free tcp handlers */ - struct listen_list* p; - for(p=listen->cps; p; p=p->next) { - if(p->com->type == comm_tcp_accept && - p->com->tcp_free != NULL) { - comm_point_start_listening(p->com, -1, -1); - } - } -} - diff --git a/external/unbound/services/listen_dnsport.h b/external/unbound/services/listen_dnsport.h deleted file mode 100644 index fac0f7970..000000000 --- a/external/unbound/services/listen_dnsport.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * services/listen_dnsport.h - listen on port 53 for incoming DNS queries. - * - * 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 - * - * This file has functions to get queries from clients. - */ - -#ifndef LISTEN_DNSPORT_H -#define LISTEN_DNSPORT_H - -#include "util/netevent.h" -struct listen_list; -struct config_file; -struct addrinfo; -struct sldns_buffer; - -/** - * Listening for queries structure. - * Contains list of query-listen sockets. - */ -struct listen_dnsport { - /** Base for select calls */ - struct comm_base* base; - - /** buffer shared by UDP connections, since there is only one - datagram at any time. */ - struct sldns_buffer* udp_buff; -#ifdef USE_DNSCRYPT - struct sldns_buffer* dnscrypt_udp_buff; -#endif - /** list of comm points used to get incoming events */ - struct listen_list* cps; -}; - -/** - * Single linked list to store event points. - */ -struct listen_list { - /** next in list */ - struct listen_list* next; - /** event info */ - struct comm_point* com; -}; - -/** - * type of ports - */ -enum listen_type { - /** udp type */ - listen_type_udp, - /** tcp type */ - listen_type_tcp, - /** udp ipv6 (v4mapped) for use with ancillary data */ - listen_type_udpancil, - /** ssl over tcp type */ - listen_type_ssl, - /** udp type + dnscrypt*/ - listen_type_udp_dnscrypt, - /** tcp type + dnscrypt */ - listen_type_tcp_dnscrypt, - /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/ - listen_type_udpancil_dnscrypt - -}; - -/** - * Single linked list to store shared ports that have been - * opened for use by all threads. - */ -struct listen_port { - /** next in list */ - struct listen_port* next; - /** file descriptor, open and ready for use */ - int fd; - /** type of file descriptor, udp or tcp */ - enum listen_type ftype; -}; - -/** - * Create shared listening ports - * Getaddrinfo, create socket, bind and listen to zero or more - * interfaces for IP4 and/or IP6, for UDP and/or TCP. - * On the given port number. It creates the sockets. - * @param cfg: settings on what ports to open. - * @param reuseport: set to true if you want reuseport, or NULL to not have it, - * set to false on exit if reuseport failed to apply (because of no - * kernel support). - * @return: linked list of ports or NULL on error. - */ -struct listen_port* listening_ports_open(struct config_file* cfg, - int* reuseport); - -/** - * Close and delete the (list of) listening ports. - */ -void listening_ports_free(struct listen_port* list); - -/** - * Create commpoints with for this thread for the shared ports. - * @param base: the comm_base that provides event functionality. - * for default all ifs. - * @param ports: the list of shared ports. - * @param bufsize: size of datagram buffer. - * @param tcp_accept_count: max number of simultaneous TCP connections - * from clients. - * @param sslctx: nonNULL if ssl context. - * @param dtenv: nonNULL if dnstap enabled. - * @param cb: callback function when a request arrives. It is passed - * the packet and user argument. Return true to send a reply. - * @param cb_arg: user data argument for callback function. - * @return: the malloced listening structure, ready for use. NULL on error. - */ -struct listen_dnsport* listen_create(struct comm_base* base, - struct listen_port* ports, size_t bufsize, int tcp_accept_count, - void* sslctx, struct dt_env *dtenv, comm_point_callback_type* cb, - void* cb_arg); - -/** - * delete the listening structure - * @param listen: listening structure. - */ -void listen_delete(struct listen_dnsport* listen); - -/** - * delete listen_list of commpoints. Calls commpointdelete() on items. - * This may close the fds or not depending on flags. - * @param list: to delete. - */ -void listen_list_delete(struct listen_list* list); - -/** - * get memory size used by the listening structs - * @param listen: listening structure. - * @return: size in bytes. - */ -size_t listen_get_mem(struct listen_dnsport* listen); - -/** - * stop accept handlers for TCP (until enabled again) - * @param listen: listening structure. - */ -void listen_stop_accept(struct listen_dnsport* listen); - -/** - * start accept handlers for TCP (was stopped before) - * @param listen: listening structure. - */ -void listen_start_accept(struct listen_dnsport* listen); - -/** - * Create and bind nonblocking UDP socket - * @param family: for socket call. - * @param socktype: for socket call. - * @param addr: for bind call. - * @param addrlen: for bind call. - * @param v6only: if enabled, IP6 sockets get IP6ONLY option set. - * if enabled with value 2 IP6ONLY option is disabled. - * @param inuse: on error, this is set true if the port was in use. - * @param noproto: on error, this is set true if cause is that the - IPv6 proto (family) is not available. - * @param rcv: set size on rcvbuf with socket option, if 0 it is not set. - * @param snd: set size on sndbuf with socket option, if 0 it is not set. - * @param listen: if true, this is a listening UDP port, eg port 53, and - * set SO_REUSEADDR on it. - * @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on - * listening UDP port. Set to false on return if it failed to do so. - * @param transparent: set IP_TRANSPARENT socket option. - * @param freebind: set IP_FREEBIND socket option. - * @param use_systemd: if true, fetch sockets from systemd. - * @return: the socket. -1 on error. - */ -int create_udp_sock(int family, int socktype, struct sockaddr* addr, - socklen_t addrlen, int v6only, int* inuse, int* noproto, int rcv, - int snd, int listen, int* reuseport, int transparent, int freebind, int use_systemd); - -/** - * Create and bind TCP listening socket - * @param addr: address info ready to make socket. - * @param v6only: enable ip6 only flag on ip6 sockets. - * @param noproto: if error caused by lack of protocol support. - * @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on - * listening UDP port. Set to false on return if it failed to do so. - * @param transparent: set IP_TRANSPARENT socket option. - * @param mss: maximum segment size of the socket. if zero, leaves the default. - * @param freebind: set IP_FREEBIND socket option. - * @param use_systemd: if true, fetch sockets from systemd. - * @return: the socket. -1 on error. - */ -int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, - int* reuseport, int transparent, int mss, int freebind, int use_systemd); - -/** - * Create and bind local listening socket - * @param path: path to the socket. - * @param noproto: on error, this is set true if cause is that local sockets - * are not supported. - * @param use_systemd: if true, fetch sockets from systemd. - * @return: the socket. -1 on error. - */ -int create_local_accept_sock(const char* path, int* noproto, int use_systemd); - -#endif /* LISTEN_DNSPORT_H */ diff --git a/external/unbound/services/localzone.c b/external/unbound/services/localzone.c deleted file mode 100644 index dcce46e86..000000000 --- a/external/unbound/services/localzone.c +++ /dev/null @@ -1,1846 +0,0 @@ -/* - * services/localzone.c - local zones authority service. - * - * 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 - * - * This file contains functions to enable local zone authority service. - */ -#include "config.h" -#include "services/localzone.h" -#include "sldns/str2wire.h" -#include "sldns/sbuffer.h" -#include "util/regional.h" -#include "util/config_file.h" -#include "util/data/dname.h" -#include "util/data/packed_rrset.h" -#include "util/data/msgencode.h" -#include "util/net_help.h" -#include "util/netevent.h" -#include "util/data/msgreply.h" -#include "util/data/msgparse.h" -#include "util/as112.h" -#include "util/config_file.h" - -/* maximum RRs in an RRset, to cap possible 'endless' list RRs. - * with 16 bytes for an A record, a 64K packet has about 4000 max */ -#define LOCALZONE_RRSET_COUNT_MAX 4096 - -struct local_zones* -local_zones_create(void) -{ - struct local_zones* zones = (struct local_zones*)calloc(1, - sizeof(*zones)); - if(!zones) - return NULL; - rbtree_init(&zones->ztree, &local_zone_cmp); - lock_rw_init(&zones->lock); - lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree)); - /* also lock protects the rbnode's in struct local_zone */ - return zones; -} - -/** helper traverse to delete zones */ -static void -lzdel(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - struct local_zone* z = (struct local_zone*)n->key; - local_zone_delete(z); -} - -void -local_zones_delete(struct local_zones* zones) -{ - if(!zones) - return; - lock_rw_destroy(&zones->lock); - /* walk through zones and delete them all */ - traverse_postorder(&zones->ztree, lzdel, NULL); - free(zones); -} - -void -local_zone_delete(struct local_zone* z) -{ - if(!z) - return; - lock_rw_destroy(&z->lock); - regional_destroy(z->region); - free(z->name); - free(z->taglist); - free(z); -} - -int -local_zone_cmp(const void* z1, const void* z2) -{ - /* first sort on class, so that hierarchy can be maintained within - * a class */ - struct local_zone* a = (struct local_zone*)z1; - struct local_zone* b = (struct local_zone*)z2; - int m; - if(a->dclass != b->dclass) { - if(a->dclass < b->dclass) - return -1; - return 1; - } - return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m); -} - -int -local_data_cmp(const void* d1, const void* d2) -{ - struct local_data* a = (struct local_data*)d1; - struct local_data* b = (struct local_data*)d2; - int m; - return dname_canon_lab_cmp(a->name, a->namelabs, b->name, - b->namelabs, &m); -} - -/* form wireformat from text format domain name */ -int -parse_dname(const char* str, uint8_t** res, size_t* len, int* labs) -{ - *res = sldns_str2wire_dname(str, len); - *labs = 0; - if(!*res) { - log_err("cannot parse name %s", str); - return 0; - } - *labs = dname_count_size_labels(*res, len); - return 1; -} - -/** create a new localzone */ -static struct local_zone* -local_zone_create(uint8_t* nm, size_t len, int labs, - enum localzone_type t, uint16_t dclass) -{ - struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z)); - if(!z) { - return NULL; - } - z->node.key = z; - z->dclass = dclass; - z->type = t; - z->name = nm; - z->namelen = len; - z->namelabs = labs; - lock_rw_init(&z->lock); - z->region = regional_create_custom(sizeof(struct regional)); - if(!z->region) { - free(z); - return NULL; - } - rbtree_init(&z->data, &local_data_cmp); - lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_type)); - /* also the zones->lock protects node, parent, name*, class */ - return z; -} - -/** enter a new zone with allocated dname returns with WRlock */ -static struct local_zone* -lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, - int labs, enum localzone_type t, uint16_t c) -{ - struct local_zone* z = local_zone_create(nm, len, labs, t, c); - if(!z) { - free(nm); - log_err("out of memory"); - return NULL; - } - - /* add to rbtree */ - lock_rw_wrlock(&zones->lock); - lock_rw_wrlock(&z->lock); - if(!rbtree_insert(&zones->ztree, &z->node)) { - struct local_zone* oldz; - log_warn("duplicate local-zone"); - lock_rw_unlock(&z->lock); - /* save zone name locally before deallocation, - * otherwise, nm is gone if we zone_delete now. */ - oldz = z; - /* find the correct zone, so not an error for duplicate */ - z = local_zones_find(zones, nm, len, labs, c); - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - local_zone_delete(oldz); - return z; - } - lock_rw_unlock(&zones->lock); - return z; -} - -/** enter a new zone */ -static struct local_zone* -lz_enter_zone(struct local_zones* zones, const char* name, const char* type, - uint16_t dclass) -{ - struct local_zone* z; - enum localzone_type t; - uint8_t* nm; - size_t len; - int labs; - if(!parse_dname(name, &nm, &len, &labs)) { - log_err("bad zone name %s %s", name, type); - return NULL; - } - if(!local_zone_str2type(type, &t)) { - log_err("bad lz_enter_zone type %s %s", name, type); - free(nm); - return NULL; - } - if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) { - log_err("could not enter zone %s %s", name, type); - return NULL; - } - return z; -} - -int -rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, - uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, - uint8_t** rdata, size_t* rdata_len) -{ - size_t dname_len = 0; - int e = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, - NULL, 0, NULL, 0); - if(e) { - log_err("error parsing local-data at %d: '%s': %s", - LDNS_WIREPARSE_OFFSET(e), str, - sldns_get_errorstr_parse(e)); - return 0; - } - *nm = memdup(rr, dname_len); - if(!*nm) { - log_err("out of memory"); - return 0; - } - *dclass = sldns_wirerr_get_class(rr, len, dname_len); - *type = sldns_wirerr_get_type(rr, len, dname_len); - *ttl = (time_t)sldns_wirerr_get_ttl(rr, len, dname_len); - *rdata = sldns_wirerr_get_rdatawl(rr, len, dname_len); - *rdata_len = sldns_wirerr_get_rdatalen(rr, len, dname_len)+2; - return 1; -} - -/** return name and class of rr; parses string */ -static int -get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass) -{ - uint8_t rr[LDNS_RR_BUF_SIZE]; - size_t len = sizeof(rr), dname_len = 0; - int s = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, - NULL, 0, NULL, 0); - if(s != 0) { - log_err("error parsing local-data at %d '%s': %s", - LDNS_WIREPARSE_OFFSET(s), str, - sldns_get_errorstr_parse(s)); - return 0; - } - *nm = memdup(rr, dname_len); - *dclass = sldns_wirerr_get_class(rr, len, dname_len); - if(!*nm) { - log_err("out of memory"); - return 0; - } - return 1; -} - -/** - * Find an rrset in local data structure. - * @param data: local data domain name structure. - * @param type: type to look for (host order). - * @param alias_ok: 1 if matching a non-exact, alias type such as CNAME is - * allowed. otherwise 0. - * @return rrset pointer or NULL if not found. - */ -static struct local_rrset* -local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) -{ - struct local_rrset* p; - type = htons(type); - for(p = data->rrsets; p; p = p->next) { - if(p->rrset->rk.type == type) - return p; - if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; - } - return NULL; -} - -/** check for RR duplicates */ -static int -rr_is_duplicate(struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len) -{ - size_t i; - for(i=0; i<pd->count; i++) { - if(pd->rr_len[i] == rdata_len && - memcmp(pd->rr_data[i], rdata, rdata_len) == 0) - return 1; - } - return 0; -} - -/** new local_rrset */ -static struct local_rrset* -new_local_rrset(struct regional* region, struct local_data* node, - uint16_t rrtype, uint16_t rrclass) -{ - struct packed_rrset_data* pd; - struct local_rrset* rrset = (struct local_rrset*) - regional_alloc_zero(region, sizeof(*rrset)); - if(!rrset) { - log_err("out of memory"); - return NULL; - } - rrset->next = node->rrsets; - node->rrsets = rrset; - rrset->rrset = (struct ub_packed_rrset_key*) - regional_alloc_zero(region, sizeof(*rrset->rrset)); - if(!rrset->rrset) { - log_err("out of memory"); - return NULL; - } - rrset->rrset->entry.key = rrset->rrset; - pd = (struct packed_rrset_data*)regional_alloc_zero(region, - sizeof(*pd)); - if(!pd) { - log_err("out of memory"); - return NULL; - } - pd->trust = rrset_trust_prim_noglue; - pd->security = sec_status_insecure; - rrset->rrset->entry.data = pd; - rrset->rrset->rk.dname = node->name; - rrset->rrset->rk.dname_len = node->namelen; - rrset->rrset->rk.type = htons(rrtype); - rrset->rrset->rk.rrset_class = htons(rrclass); - return rrset; -} - -/** insert RR into RRset data structure; Wastes a couple of bytes */ -int -rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, - uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr) -{ - size_t* oldlen = pd->rr_len; - time_t* oldttl = pd->rr_ttl; - uint8_t** olddata = pd->rr_data; - - /* add RR to rrset */ - if(pd->count > LOCALZONE_RRSET_COUNT_MAX) { - log_warn("RRset '%s' has more than %d records, record ignored", - rrstr, LOCALZONE_RRSET_COUNT_MAX); - return 1; - } - pd->count++; - pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); - pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); - pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count); - if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { - log_err("out of memory"); - return 0; - } - if(pd->count > 1) { - memcpy(pd->rr_len+1, oldlen, - sizeof(*pd->rr_len)*(pd->count-1)); - memcpy(pd->rr_ttl+1, oldttl, - sizeof(*pd->rr_ttl)*(pd->count-1)); - memcpy(pd->rr_data+1, olddata, - sizeof(*pd->rr_data)*(pd->count-1)); - } - pd->rr_len[0] = rdata_len; - pd->rr_ttl[0] = ttl; - pd->rr_data[0] = regional_alloc_init(region, rdata, rdata_len); - if(!pd->rr_data[0]) { - log_err("out of memory"); - return 0; - } - return 1; -} - -/** find a data node by exact name */ -static struct local_data* -lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) -{ - struct local_data key; - key.node.key = &key; - key.name = nm; - key.namelen = nmlen; - key.namelabs = nmlabs; - return (struct local_data*)rbtree_search(&z->data, &key.node); -} - -/** find a node, create it if not and all its empty nonterminal parents */ -static int -lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, - int nmlabs, struct local_data** res) -{ - struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs); - if(!ld) { - /* create a domain name to store rr. */ - ld = (struct local_data*)regional_alloc_zero(z->region, - sizeof(*ld)); - if(!ld) { - log_err("out of memory adding local data"); - return 0; - } - ld->node.key = ld; - ld->name = regional_alloc_init(z->region, nm, nmlen); - if(!ld->name) { - log_err("out of memory"); - return 0; - } - ld->namelen = nmlen; - ld->namelabs = nmlabs; - if(!rbtree_insert(&z->data, &ld->node)) { - log_assert(0); /* duplicate name */ - } - /* see if empty nonterminals need to be created */ - if(nmlabs > z->namelabs) { - dname_remove_label(&nm, &nmlen); - if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res)) - return 0; - } - } - *res = ld; - return 1; -} - -/** enter data RR into auth zone */ -static int -lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) -{ - uint8_t* nm; - size_t nmlen; - int nmlabs; - struct local_data* node; - struct local_rrset* rrset; - struct packed_rrset_data* pd; - uint16_t rrtype = 0, rrclass = 0; - time_t ttl = 0; - uint8_t rr[LDNS_RR_BUF_SIZE]; - uint8_t* rdata; - size_t rdata_len; - if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, - sizeof(rr), &rdata, &rdata_len)) { - log_err("bad local-data: %s", rrstr); - return 0; - } - log_assert(z->dclass == rrclass); - if(z->type == local_zone_redirect && - query_dname_compare(z->name, nm) != 0) { - log_err("local-data in redirect zone must reside at top of zone" - ", not at %s", rrstr); - free(nm); - return 0; - } - nmlabs = dname_count_size_labels(nm, &nmlen); - if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { - free(nm); - return 0; - } - log_assert(node); - free(nm); - - /* Reject it if we would end up having CNAME and other data (including - * another CNAME) for a redirect zone. */ - if(z->type == local_zone_redirect && node->rrsets) { - const char* othertype = NULL; - if (rrtype == LDNS_RR_TYPE_CNAME) - othertype = "other"; - else if (node->rrsets->rrset->rk.type == - htons(LDNS_RR_TYPE_CNAME)) { - othertype = "CNAME"; - } - if(othertype) { - log_err("local-data '%s' in redirect zone must not " - "coexist with %s local-data", rrstr, othertype); - return 0; - } - } - rrset = local_data_find_type(node, rrtype, 0); - if(!rrset) { - rrset = new_local_rrset(z->region, node, rrtype, rrclass); - if(!rrset) - return 0; - if(query_dname_compare(node->name, z->name) == 0) { - if(rrtype == LDNS_RR_TYPE_NSEC) - rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX; - if(rrtype == LDNS_RR_TYPE_SOA) - z->soa = rrset->rrset; - } - } - pd = (struct packed_rrset_data*)rrset->rrset->entry.data; - log_assert(rrset && pd); - - /* check for duplicate RR */ - if(rr_is_duplicate(pd, rdata, rdata_len)) { - verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); - return 1; - } - return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr); -} - -/** enter a data RR into auth data; a zone for it must exist */ -static int -lz_enter_rr_str(struct local_zones* zones, const char* rr) -{ - uint8_t* rr_name; - uint16_t rr_class; - size_t len; - int labs; - struct local_zone* z; - int r; - if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { - log_err("bad rr %s", rr); - return 0; - } - labs = dname_count_size_labels(rr_name, &len); - lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class); - if(!z) { - lock_rw_unlock(&zones->lock); - fatal_exit("internal error: no zone for rr %s", rr); - } - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - free(rr_name); - r = lz_enter_rr_into_zone(z, rr); - lock_rw_unlock(&z->lock); - return r; -} - -/** enter tagstring into zone */ -static int -lz_enter_zone_tag(struct local_zones* zones, char* zname, uint8_t* list, - size_t len, uint16_t rr_class) -{ - uint8_t dname[LDNS_MAX_DOMAINLEN+1]; - size_t dname_len = sizeof(dname); - int dname_labs, r = 0; - struct local_zone* z; - - if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) { - log_err("cannot parse zone name in local-zone-tag: %s", zname); - return 0; - } - dname_labs = dname_count_labels(dname); - - lock_rw_rdlock(&zones->lock); - z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class); - if(!z) { - lock_rw_unlock(&zones->lock); - log_err("no local-zone for tag %s", zname); - return 0; - } - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - free(z->taglist); - z->taglist = memdup(list, len); - z->taglen = len; - if(z->taglist) - r = 1; - lock_rw_unlock(&z->lock); - return r; -} - -/** enter override into zone */ -static int -lz_enter_override(struct local_zones* zones, char* zname, char* netblock, - char* type, uint16_t rr_class) -{ - uint8_t dname[LDNS_MAX_DOMAINLEN+1]; - size_t dname_len = sizeof(dname); - int dname_labs; - struct sockaddr_storage addr; - int net; - socklen_t addrlen; - struct local_zone* z; - enum localzone_type t; - - /* parse zone name */ - if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) { - log_err("cannot parse zone name in local-zone-override: %s %s", - zname, netblock); - return 0; - } - dname_labs = dname_count_labels(dname); - - /* parse netblock */ - if(!netblockstrtoaddr(netblock, UNBOUND_DNS_PORT, &addr, &addrlen, - &net)) { - log_err("cannot parse netblock in local-zone-override: %s %s", - zname, netblock); - return 0; - } - - /* parse zone type */ - if(!local_zone_str2type(type, &t)) { - log_err("cannot parse type in local-zone-override: %s %s %s", - zname, netblock, type); - return 0; - } - - /* find localzone entry */ - lock_rw_rdlock(&zones->lock); - z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class); - if(!z) { - lock_rw_unlock(&zones->lock); - log_err("no local-zone for local-zone-override %s", zname); - return 0; - } - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - - /* create netblock addr_tree if not present yet */ - if(!z->override_tree) { - z->override_tree = (struct rbtree_type*)regional_alloc_zero( - z->region, sizeof(*z->override_tree)); - if(!z->override_tree) { - lock_rw_unlock(&z->lock); - log_err("out of memory"); - return 0; - } - addr_tree_init(z->override_tree); - } - /* add new elem to tree */ - if(z->override_tree) { - struct local_zone_override* n; - n = (struct local_zone_override*)regional_alloc_zero( - z->region, sizeof(*n)); - if(!n) { - lock_rw_unlock(&z->lock); - log_err("out of memory"); - return 0; - } - n->type = t; - if(!addr_tree_insert(z->override_tree, - (struct addr_tree_node*)n, &addr, addrlen, net)) { - lock_rw_unlock(&z->lock); - log_err("duplicate local-zone-override %s %s", - zname, netblock); - return 1; - } - } - - lock_rw_unlock(&z->lock); - return 1; -} - -/** parse local-zone: statements */ -static int -lz_enter_zones(struct local_zones* zones, struct config_file* cfg) -{ - struct config_str2list* p; - struct local_zone* z; - for(p = cfg->local_zones; p; p = p->next) { - if(!(z=lz_enter_zone(zones, p->str, p->str2, - LDNS_RR_CLASS_IN))) - return 0; - lock_rw_unlock(&z->lock); - } - return 1; -} - -/** lookup a zone in rbtree; exact match only; SLOW due to parse */ -static int -lz_exists(struct local_zones* zones, const char* name) -{ - struct local_zone z; - z.node.key = &z; - z.dclass = LDNS_RR_CLASS_IN; - if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) { - log_err("bad name %s", name); - return 0; - } - lock_rw_rdlock(&zones->lock); - if(rbtree_search(&zones->ztree, &z.node)) { - lock_rw_unlock(&zones->lock); - free(z.name); - return 1; - } - lock_rw_unlock(&zones->lock); - free(z.name); - return 0; -} - -/** lookup a zone in cfg->nodefault list */ -static int -lz_nodefault(struct config_file* cfg, const char* name) -{ - struct config_strlist* p; - size_t len = strlen(name); - if(len == 0) return 0; - if(name[len-1] == '.') len--; - - for(p = cfg->local_zones_nodefault; p; p = p->next) { - /* compare zone name, lowercase, compare without ending . */ - if(strncasecmp(p->str, name, len) == 0 && - (strlen(p->str) == len || (strlen(p->str)==len+1 && - p->str[len] == '.'))) - return 1; - } - return 0; -} - -/** enter AS112 default zone */ -static int -add_as112_default(struct local_zones* zones, struct config_file* cfg, - const char* name) -{ - struct local_zone* z; - char str[1024]; /* known long enough */ - if(lz_exists(zones, name) || lz_nodefault(cfg, name)) - return 1; /* do not enter default content */ - if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN))) - return 0; - snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. " - "nobody.invalid. 1 3600 1200 604800 10800", name); - if(!lz_enter_rr_into_zone(z, str)) { - lock_rw_unlock(&z->lock); - return 0; - } - snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name); - if(!lz_enter_rr_into_zone(z, str)) { - lock_rw_unlock(&z->lock); - return 0; - } - lock_rw_unlock(&z->lock); - return 1; -} - -/** enter default zones */ -static int -lz_enter_defaults(struct local_zones* zones, struct config_file* cfg) -{ - struct local_zone* z; - const char** zstr; - - /* this list of zones is from RFC 6303 and RFC 7686 */ - - /* block localhost level zones first, then onion and later the LAN zones */ - - /* localhost. zone */ - if(!lz_exists(zones, "localhost.") && - !lz_nodefault(cfg, "localhost.")) { - if(!(z=lz_enter_zone(zones, "localhost.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "localhost. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "localhost. 10800 IN SOA localhost. nobody.invalid. " - "1 3600 1200 604800 10800") || - !lz_enter_rr_into_zone(z, - "localhost. 10800 IN A 127.0.0.1") || - !lz_enter_rr_into_zone(z, - "localhost. 10800 IN AAAA ::1")) { - log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } - return 0; - } - lock_rw_unlock(&z->lock); - } - /* reverse ip4 zone */ - if(!lz_exists(zones, "127.in-addr.arpa.") && - !lz_nodefault(cfg, "127.in-addr.arpa.")) { - if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "127.in-addr.arpa. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "127.in-addr.arpa. 10800 IN SOA localhost. " - "nobody.invalid. 1 3600 1200 604800 10800") || - !lz_enter_rr_into_zone(z, - "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) { - log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } - return 0; - } - lock_rw_unlock(&z->lock); - } - /* reverse ip6 zone */ - if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") && - !lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) { - if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. " - "nobody.invalid. 1 3600 1200 604800 10800") || - !lz_enter_rr_into_zone(z, - "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) { - log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } - return 0; - } - lock_rw_unlock(&z->lock); - } - /* onion. zone (RFC 7686) */ - if(!lz_exists(zones, "onion.") && - !lz_nodefault(cfg, "onion.")) { - if(!(z=lz_enter_zone(zones, "onion.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "onion. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "onion. 10800 IN SOA localhost. nobody.invalid. " - "1 3600 1200 604800 10800")) { - log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } - return 0; - } - lock_rw_unlock(&z->lock); - } - - /* block AS112 zones, unless asked not to */ - if(!cfg->unblock_lan_zones) { - for(zstr = as112_zones; *zstr; zstr++) { - if(!add_as112_default(zones, cfg, *zstr)) { - log_err("out of memory adding default zone"); - return 0; - } - } - } - return 1; -} - -/** parse local-zone-override: statements */ -static int -lz_enter_overrides(struct local_zones* zones, struct config_file* cfg) -{ - struct config_str3list* p; - for(p = cfg->local_zone_overrides; p; p = p->next) { - if(!lz_enter_override(zones, p->str, p->str2, p->str3, - LDNS_RR_CLASS_IN)) - return 0; - } - return 1; -} - -/** setup parent pointers, so that a lookup can be done for closest match */ -static void -init_parents(struct local_zones* zones) -{ - struct local_zone* node, *prev = NULL, *p; - int m; - lock_rw_wrlock(&zones->lock); - RBTREE_FOR(node, struct local_zone*, &zones->ztree) { - lock_rw_wrlock(&node->lock); - node->parent = NULL; - if(!prev || prev->dclass != node->dclass) { - prev = node; - lock_rw_unlock(&node->lock); - continue; - } - (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, - node->namelabs, &m); /* we know prev is smaller */ - /* sort order like: . com. bla.com. zwb.com. net. */ - /* find the previous, or parent-parent-parent */ - for(p = prev; p; p = p->parent) - /* looking for name with few labels, a parent */ - if(p->namelabs <= m) { - /* ==: since prev matched m, this is closest*/ - /* <: prev matches more, but is not a parent, - * this one is a (grand)parent */ - node->parent = p; - break; - } - prev = node; - - if(node->override_tree) - addr_tree_init_parents(node->override_tree); - lock_rw_unlock(&node->lock); - } - lock_rw_unlock(&zones->lock); -} - -/** enter implicit transparent zone for local-data: without local-zone: */ -static int -lz_setup_implicit(struct local_zones* zones, struct config_file* cfg) -{ - /* walk over all items that have no parent zone and find - * the name that covers them all (could be the root) and - * add that as a transparent zone */ - struct config_strlist* p; - int have_name = 0; - int have_other_classes = 0; - uint16_t dclass = 0; - uint8_t* nm = 0; - size_t nmlen = 0; - int nmlabs = 0; - int match = 0; /* number of labels match count */ - - init_parents(zones); /* to enable local_zones_lookup() */ - for(p = cfg->local_data; p; p = p->next) { - uint8_t* rr_name; - uint16_t rr_class; - size_t len; - int labs; - if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { - log_err("Bad local-data RR %s", p->str); - return 0; - } - labs = dname_count_size_labels(rr_name, &len); - lock_rw_rdlock(&zones->lock); - if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { - if(!have_name) { - dclass = rr_class; - nm = rr_name; - nmlen = len; - nmlabs = labs; - match = labs; - have_name = 1; - } else { - int m; - if(rr_class != dclass) { - /* process other classes later */ - free(rr_name); - have_other_classes = 1; - lock_rw_unlock(&zones->lock); - continue; - } - /* find smallest shared topdomain */ - (void)dname_lab_cmp(nm, nmlabs, - rr_name, labs, &m); - free(rr_name); - if(m < match) - match = m; - } - } else free(rr_name); - lock_rw_unlock(&zones->lock); - } - if(have_name) { - uint8_t* n2; - struct local_zone* z; - /* allocate zone of smallest shared topdomain to contain em */ - n2 = nm; - dname_remove_labels(&n2, &nmlen, nmlabs - match); - n2 = memdup(n2, nmlen); - free(nm); - if(!n2) { - log_err("out of memory"); - return 0; - } - log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", - n2, 0, dclass); - if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, - local_zone_transparent, dclass))) { - return 0; - } - lock_rw_unlock(&z->lock); - } - if(have_other_classes) { - /* restart to setup other class */ - return lz_setup_implicit(zones, cfg); - } - return 1; -} - -/** enter local-zone-tag info */ -static int -lz_enter_zone_tags(struct local_zones* zones, struct config_file* cfg) -{ - struct config_strbytelist* p; - int c = 0; - for(p = cfg->local_zone_tags; p; p = p->next) { - if(!lz_enter_zone_tag(zones, p->str, p->str2, p->str2len, - LDNS_RR_CLASS_IN)) - return 0; - c++; - } - if(c) verbose(VERB_ALGO, "applied tags to %d local zones", c); - return 1; -} - -/** enter auth data */ -static int -lz_enter_data(struct local_zones* zones, struct config_file* cfg) -{ - struct config_strlist* p; - for(p = cfg->local_data; p; p = p->next) { - if(!lz_enter_rr_str(zones, p->str)) - return 0; - } - return 1; -} - -/** free memory from config */ -static void -lz_freeup_cfg(struct config_file* cfg) -{ - config_deldblstrlist(cfg->local_zones); - cfg->local_zones = NULL; - config_delstrlist(cfg->local_zones_nodefault); - cfg->local_zones_nodefault = NULL; - config_delstrlist(cfg->local_data); - cfg->local_data = NULL; -} - -int -local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg) -{ - /* create zones from zone statements. */ - if(!lz_enter_zones(zones, cfg)) { - return 0; - } - /* apply default zones+content (unless disabled, or overridden) */ - if(!lz_enter_defaults(zones, cfg)) { - return 0; - } - /* enter local zone overrides */ - if(!lz_enter_overrides(zones, cfg)) { - return 0; - } - /* create implicit transparent zone from data. */ - if(!lz_setup_implicit(zones, cfg)) { - return 0; - } - - /* setup parent ptrs for lookup during data entry */ - init_parents(zones); - /* insert local zone tags */ - if(!lz_enter_zone_tags(zones, cfg)) { - return 0; - } - /* insert local data */ - if(!lz_enter_data(zones, cfg)) { - return 0; - } - /* freeup memory from cfg struct. */ - lz_freeup_cfg(cfg); - return 1; -} - -struct local_zone* -local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass) -{ - return local_zones_tags_lookup(zones, name, len, labs, - dclass, NULL, 0, 1); -} - -struct local_zone* -local_zones_tags_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, - uint8_t* taglist, size_t taglen, int ignoretags) -{ - rbnode_type* res = NULL; - struct local_zone *result; - struct local_zone key; - int m; - key.node.key = &key; - key.dclass = dclass; - key.name = name; - key.namelen = len; - key.namelabs = labs; - rbtree_find_less_equal(&zones->ztree, &key, &res); - result = (struct local_zone*)res; - /* exact or smaller element (or no element) */ - if(!result || result->dclass != dclass) - return NULL; - /* count number of labels matched */ - (void)dname_lab_cmp(result->name, result->namelabs, key.name, - key.namelabs, &m); - while(result) { /* go up until qname is zone or subdomain of zone */ - if(result->namelabs <= m) - if(ignoretags || !result->taglist || - taglist_intersect(result->taglist, - result->taglen, taglist, taglen)) - break; - result = result->parent; - } - return result; -} - -struct local_zone* -local_zones_find(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass) -{ - struct local_zone key; - key.node.key = &key; - key.dclass = dclass; - key.name = name; - key.namelen = len; - key.namelabs = labs; - /* exact */ - return (struct local_zone*)rbtree_search(&zones->ztree, &key); -} - -/** print all RRsets in local zone */ -static void -local_zone_out(struct local_zone* z) -{ - struct local_data* d; - struct local_rrset* p; - RBTREE_FOR(d, struct local_data*, &z->data) { - for(p = d->rrsets; p; p = p->next) { - log_nametypeclass(0, "rrset", d->name, - ntohs(p->rrset->rk.type), - ntohs(p->rrset->rk.rrset_class)); - } - } -} - -void local_zones_print(struct local_zones* zones) -{ - struct local_zone* z; - lock_rw_rdlock(&zones->lock); - log_info("number of auth zones %u", (unsigned)zones->ztree.count); - RBTREE_FOR(z, struct local_zone*, &zones->ztree) { - lock_rw_rdlock(&z->lock); - switch(z->type) { - case local_zone_deny: - log_nametypeclass(0, "deny zone", - z->name, 0, z->dclass); - break; - case local_zone_refuse: - log_nametypeclass(0, "refuse zone", - z->name, 0, z->dclass); - break; - case local_zone_redirect: - log_nametypeclass(0, "redirect zone", - z->name, 0, z->dclass); - break; - case local_zone_transparent: - log_nametypeclass(0, "transparent zone", - z->name, 0, z->dclass); - break; - case local_zone_typetransparent: - log_nametypeclass(0, "typetransparent zone", - z->name, 0, z->dclass); - break; - case local_zone_static: - log_nametypeclass(0, "static zone", - z->name, 0, z->dclass); - break; - case local_zone_inform: - log_nametypeclass(0, "inform zone", - z->name, 0, z->dclass); - break; - case local_zone_inform_deny: - log_nametypeclass(0, "inform_deny zone", - z->name, 0, z->dclass); - break; - case local_zone_always_transparent: - log_nametypeclass(0, "always_transparent zone", - z->name, 0, z->dclass); - break; - case local_zone_always_refuse: - log_nametypeclass(0, "always_refuse zone", - z->name, 0, z->dclass); - break; - case local_zone_always_nxdomain: - log_nametypeclass(0, "always_nxdomain zone", - z->name, 0, z->dclass); - break; - default: - log_nametypeclass(0, "badtyped zone", - z->name, 0, z->dclass); - break; - } - local_zone_out(z); - lock_rw_unlock(&z->lock); - } - lock_rw_unlock(&zones->lock); -} - -/** encode answer consisting of 1 rrset */ -static int -local_encode(struct query_info* qinfo, struct module_env* env, - struct edns_data* edns, sldns_buffer* buf, struct regional* temp, - struct ub_packed_rrset_key* rrset, int ansec, int rcode) -{ - struct reply_info rep; - uint16_t udpsize; - /* make answer with time=0 for fixed TTL values */ - memset(&rep, 0, sizeof(rep)); - rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); - rep.qdcount = 1; - if(ansec) - rep.an_numrrsets = 1; - else rep.ns_numrrsets = 1; - rep.rrset_count = 1; - rep.rrsets = &rrset; - udpsize = edns->udp_size; - edns->edns_version = EDNS_ADVERTISED_VERSION; - edns->udp_size = EDNS_ADVERTISED_SIZE; - edns->ext_rcode = 0; - edns->bits &= EDNS_DO; - if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, temp) - || !reply_info_answer_encode(qinfo, &rep, - *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, - (int)(edns->bits&EDNS_DO), 0)) - error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, - *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), edns); - return 1; -} - -/** encode local error answer */ -static void -local_error_encode(struct query_info* qinfo, struct module_env* env, - struct edns_data* edns, sldns_buffer* buf, struct regional* temp, - int rcode, int r) -{ - edns->edns_version = EDNS_ADVERTISED_VERSION; - edns->udp_size = EDNS_ADVERTISED_SIZE; - edns->ext_rcode = 0; - edns->bits &= EDNS_DO; - - if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL, - rcode, edns, temp)) - edns->opt_list = NULL; - error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), edns); -} - -/** find local data tag string match for the given type in the list */ -int -local_data_find_tag_datas(const struct query_info* qinfo, - struct config_strlist* list, struct ub_packed_rrset_key* r, - struct regional* temp) -{ - struct config_strlist* p; - char buf[65536]; - uint8_t rr[LDNS_RR_BUF_SIZE]; - size_t len; - int res; - struct packed_rrset_data* d; - for(p=list; p; p=p->next) { - uint16_t rdr_type; - - len = sizeof(rr); - /* does this element match the type? */ - snprintf(buf, sizeof(buf), ". %s", p->str); - res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, - NULL, 0, NULL, 0); - if(res != 0) - /* parse errors are already checked before, in - * acllist check_data, skip this for robustness */ - continue; - if(len < 1 /* . */ + 8 /* typeclassttl*/ + 2 /*rdatalen*/) - continue; - rdr_type = sldns_wirerr_get_type(rr, len, 1); - if(rdr_type != qinfo->qtype && rdr_type != LDNS_RR_TYPE_CNAME) - continue; - - /* do we have entries already? if not setup key */ - if(r->rk.dname == NULL) { - r->entry.key = r; - r->rk.dname = qinfo->qname; - r->rk.dname_len = qinfo->qname_len; - r->rk.type = htons(rdr_type); - r->rk.rrset_class = htons(qinfo->qclass); - r->rk.flags = 0; - d = (struct packed_rrset_data*)regional_alloc_zero( - temp, sizeof(struct packed_rrset_data) - + sizeof(size_t) + sizeof(uint8_t*) + - sizeof(time_t)); - if(!d) return 0; /* out of memory */ - r->entry.data = d; - d->ttl = sldns_wirerr_get_ttl(rr, len, 1); - d->rr_len = (size_t*)((uint8_t*)d + - sizeof(struct packed_rrset_data)); - d->rr_data = (uint8_t**)&(d->rr_len[1]); - d->rr_ttl = (time_t*)&(d->rr_data[1]); - } - d = (struct packed_rrset_data*)r->entry.data; - /* add entry to the data */ - if(d->count != 0) { - size_t* oldlen = d->rr_len; - uint8_t** olddata = d->rr_data; - time_t* oldttl = d->rr_ttl; - /* increase arrays for lookup */ - /* this is of course slow for very many records, - * but most redirects are expected with few records */ - d->rr_len = (size_t*)regional_alloc_zero(temp, - (d->count+1)*sizeof(size_t)); - d->rr_data = (uint8_t**)regional_alloc_zero(temp, - (d->count+1)*sizeof(uint8_t*)); - d->rr_ttl = (time_t*)regional_alloc_zero(temp, - (d->count+1)*sizeof(time_t)); - if(!d->rr_len || !d->rr_data || !d->rr_ttl) - return 0; /* out of memory */ - /* first one was allocated after struct d, but new - * ones get their own array increment alloc, so - * copy old content */ - memmove(d->rr_len, oldlen, d->count*sizeof(size_t)); - memmove(d->rr_data, olddata, d->count*sizeof(uint8_t*)); - memmove(d->rr_ttl, oldttl, d->count*sizeof(time_t)); - } - - d->rr_len[d->count] = sldns_wirerr_get_rdatalen(rr, len, 1)+2; - d->rr_ttl[d->count] = sldns_wirerr_get_ttl(rr, len, 1); - d->rr_data[d->count] = regional_alloc_init(temp, - sldns_wirerr_get_rdatawl(rr, len, 1), - d->rr_len[d->count]); - if(!d->rr_data[d->count]) - return 0; /* out of memory */ - d->count++; - } - if(r->rk.dname) - return 1; - return 0; -} - -static int -find_tag_datas(struct query_info* qinfo, struct config_strlist* list, - struct ub_packed_rrset_key* r, struct regional* temp) -{ - int result = local_data_find_tag_datas(qinfo, list, r, temp); - - /* If we've found a non-exact alias type of local data, make a shallow - * copy of the RRset and remember it in qinfo to complete the alias - * chain later. */ - if(result && qinfo->qtype != LDNS_RR_TYPE_CNAME && - r->rk.type == htons(LDNS_RR_TYPE_CNAME)) { - qinfo->local_alias = - regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(!qinfo->local_alias) - return 0; /* out of memory */ - qinfo->local_alias->rrset = - regional_alloc_init(temp, r, sizeof(*r)); - if(!qinfo->local_alias->rrset) - return 0; /* out of memory */ - } - return result; -} - -/** answer local data match */ -static int -local_data_answer(struct local_zone* z, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, int labs, struct local_data** ldp, - enum localzone_type lz_type, int tag, struct config_strlist** tag_datas, - size_t tag_datas_size, char** tagname, int num_tags) -{ - struct local_data key; - struct local_data* ld; - struct local_rrset* lr; - key.node.key = &key; - key.name = qinfo->qname; - key.namelen = qinfo->qname_len; - key.namelabs = labs; - if(lz_type == local_zone_redirect) { - key.name = z->name; - key.namelen = z->namelen; - key.namelabs = z->namelabs; - if(tag != -1 && (size_t)tag<tag_datas_size && tag_datas[tag]) { - struct ub_packed_rrset_key r; - memset(&r, 0, sizeof(r)); - if(find_tag_datas(qinfo, tag_datas[tag], &r, temp)) { - verbose(VERB_ALGO, "redirect with tag data [%d] %s", - tag, (tag<num_tags?tagname[tag]:"null")); - - /* If we found a matching alias, we should - * use it as part of the answer, but we can't - * encode it until we complete the alias - * chain. */ - if(qinfo->local_alias) - return 1; - return local_encode(qinfo, env, edns, buf, temp, - &r, 1, LDNS_RCODE_NOERROR); - } - } - } - ld = (struct local_data*)rbtree_search(&z->data, &key.node); - *ldp = ld; - if(!ld) { - return 0; - } - lr = local_data_find_type(ld, qinfo->qtype, 1); - if(!lr) - return 0; - - /* Special case for alias matching. See local_data_answer(). */ - if(lz_type == local_zone_redirect && - qinfo->qtype != LDNS_RR_TYPE_CNAME && - lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) { - qinfo->local_alias = - regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(!qinfo->local_alias) - return 0; /* out of memory */ - qinfo->local_alias->rrset = - regional_alloc_init(temp, lr->rrset, sizeof(*lr->rrset)); - if(!qinfo->local_alias->rrset) - return 0; /* out of memory */ - qinfo->local_alias->rrset->rk.dname = qinfo->qname; - qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; - return 1; - } - if(lz_type == local_zone_redirect) { - /* convert rrset name to query name; like a wildcard */ - struct ub_packed_rrset_key r = *lr->rrset; - r.rk.dname = qinfo->qname; - r.rk.dname_len = qinfo->qname_len; - return local_encode(qinfo, env, edns, buf, temp, &r, 1, - LDNS_RCODE_NOERROR); - } - return local_encode(qinfo, env, edns, buf, temp, lr->rrset, 1, - LDNS_RCODE_NOERROR); -} - -/** - * answer in case where no exact match is found - * @param z: zone for query - * @param env: module environment - * @param qinfo: query - * @param edns: edns from query - * @param buf: buffer for answer. - * @param temp: temp region for encoding - * @param ld: local data, if NULL, no such name exists in localdata. - * @param lz_type: type of the local zone - * @return 1 if a reply is to be sent, 0 if not. - */ -static int -lz_zone_answer(struct local_zone* z, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, struct local_data* ld, enum localzone_type lz_type) -{ - if(lz_type == local_zone_deny || lz_type == local_zone_inform_deny) { - /** no reply at all, signal caller by clearing buffer. */ - sldns_buffer_clear(buf); - sldns_buffer_flip(buf); - return 1; - } else if(lz_type == local_zone_refuse - || lz_type == local_zone_always_refuse) { - local_error_encode(qinfo, env, edns, buf, temp, - LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA)); - return 1; - } else if(lz_type == local_zone_static || - lz_type == local_zone_redirect || - lz_type == local_zone_always_nxdomain) { - /* for static, reply nodata or nxdomain - * for redirect, reply nodata */ - /* no additional section processing, - * cname, dname or wildcard processing, - * or using closest match for NSEC. - * or using closest match for returning delegation downwards - */ - int rcode = (ld || lz_type == local_zone_redirect)? - LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; - if(z->soa) - return local_encode(qinfo, env, edns, buf, temp, - z->soa, 0, rcode); - local_error_encode(qinfo, env, edns, buf, temp, rcode, - (rcode|BIT_AA)); - return 1; - } else if(lz_type == local_zone_typetransparent - || lz_type == local_zone_always_transparent) { - /* no NODATA or NXDOMAINS for this zone type */ - return 0; - } - /* else lz_type == local_zone_transparent */ - - /* if the zone is transparent and the name exists, but the type - * does not, then we should make this noerror/nodata */ - if(ld && ld->rrsets) { - int rcode = LDNS_RCODE_NOERROR; - if(z->soa) - return local_encode(qinfo, env, edns, buf, temp, - z->soa, 0, rcode); - local_error_encode(qinfo, env, edns, buf, temp, rcode, - (rcode|BIT_AA)); - return 1; - } - - /* stop here, and resolve further on */ - return 0; -} - -/** print log information for an inform zone query */ -static void -lz_inform_print(struct local_zone* z, struct query_info* qinfo, - struct comm_reply* repinfo) -{ - char ip[128], txt[512]; - char zname[LDNS_MAX_DOMAINLEN+1]; - uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); - dname_str(z->name, zname); - addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); - snprintf(txt, sizeof(txt), "%s inform %s@%u", zname, ip, - (unsigned)port); - log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); -} - -static enum localzone_type -lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2, - uint8_t *tagactions, size_t tagactionssize, enum localzone_type lzt, - struct comm_reply* repinfo, struct rbtree_type* override_tree, - int* tag, char** tagname, int num_tags) -{ - struct local_zone_override* lzo; - if(repinfo && override_tree) { - lzo = (struct local_zone_override*)addr_tree_lookup( - override_tree, &repinfo->addr, repinfo->addrlen); - if(lzo && lzo->type) { - verbose(VERB_ALGO, "local zone override to type %s", - local_zone_type2str(lzo->type)); - return lzo->type; - } - } - if(!taglist || !taglist2) - return lzt; - return local_data_find_tag_action(taglist, taglen, taglist2, taglen2, - tagactions, tagactionssize, lzt, tag, tagname, num_tags); -} - -enum localzone_type -local_data_find_tag_action(const uint8_t* taglist, size_t taglen, - const uint8_t* taglist2, size_t taglen2, const uint8_t* tagactions, - size_t tagactionssize, enum localzone_type lzt, int* tag, - char* const* tagname, int num_tags) -{ - size_t i, j; - uint8_t tagmatch; - - for(i=0; i<taglen && i<taglen2; i++) { - tagmatch = (taglist[i] & taglist2[i]); - for(j=0; j<8 && tagmatch>0; j++) { - if((tagmatch & 0x1)) { - *tag = (int)(i*8+j); - verbose(VERB_ALGO, "matched tag [%d] %s", - *tag, (*tag<num_tags?tagname[*tag]:"null")); - /* does this tag have a tag action? */ - if(i*8+j < tagactionssize && tagactions - && tagactions[i*8+j] != 0) { - verbose(VERB_ALGO, "tag action [%d] %s to type %s", - *tag, (*tag<num_tags?tagname[*tag]:"null"), - local_zone_type2str( - (enum localzone_type) - tagactions[i*8+j])); - return (enum localzone_type)tagactions[i*8+j]; - } - return lzt; - } - tagmatch >>= 1; - } - } - return lzt; -} - -int -local_zones_answer(struct local_zones* zones, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, - size_t taglen, uint8_t* tagactions, size_t tagactionssize, - struct config_strlist** tag_datas, size_t tag_datas_size, - char** tagname, int num_tags, struct view* view) -{ - /* see if query is covered by a zone, - * if so: - try to match (exact) local data - * - look at zone type for negative response. */ - int labs = dname_count_labels(qinfo->qname); - struct local_data* ld = NULL; - struct local_zone* z = NULL; - enum localzone_type lzt = local_zone_transparent; - int r, tag = -1; - - if(view) { - lock_rw_rdlock(&view->lock); - if(view->local_zones && - (z = local_zones_lookup(view->local_zones, - qinfo->qname, qinfo->qname_len, labs, - qinfo->qclass))) { - verbose(VERB_ALGO, - "using localzone from view: %s", - view->name); - lock_rw_rdlock(&z->lock); - lzt = z->type; - } - if(!z && !view->isfirst){ - lock_rw_unlock(&view->lock); - return 0; - } - lock_rw_unlock(&view->lock); - } - if(!z) { - /* try global local_zones tree */ - lock_rw_rdlock(&zones->lock); - if(!(z = local_zones_tags_lookup(zones, qinfo->qname, - qinfo->qname_len, labs, qinfo->qclass, taglist, - taglen, 0))) { - lock_rw_unlock(&zones->lock); - return 0; - } - lock_rw_rdlock(&z->lock); - - lzt = lz_type(taglist, taglen, z->taglist, z->taglen, - tagactions, tagactionssize, z->type, repinfo, - z->override_tree, &tag, tagname, num_tags); - lock_rw_unlock(&zones->lock); - } - if((lzt == local_zone_inform || lzt == local_zone_inform_deny) - && repinfo) - lz_inform_print(z, qinfo, repinfo); - - if(lzt != local_zone_always_refuse - && lzt != local_zone_always_transparent - && lzt != local_zone_always_nxdomain - && local_data_answer(z, env, qinfo, edns, buf, temp, labs, &ld, lzt, - tag, tag_datas, tag_datas_size, tagname, num_tags)) { - lock_rw_unlock(&z->lock); - /* We should tell the caller that encode is deferred if we found - * a local alias. */ - return !qinfo->local_alias; - } - r = lz_zone_answer(z, env, qinfo, edns, buf, temp, ld, lzt); - lock_rw_unlock(&z->lock); - return r && !qinfo->local_alias; /* see above */ -} - -const char* local_zone_type2str(enum localzone_type t) -{ - switch(t) { - case local_zone_unset: return "unset"; - case local_zone_deny: return "deny"; - case local_zone_refuse: return "refuse"; - case local_zone_redirect: return "redirect"; - case local_zone_transparent: return "transparent"; - case local_zone_typetransparent: return "typetransparent"; - case local_zone_static: return "static"; - case local_zone_nodefault: return "nodefault"; - case local_zone_inform: return "inform"; - case local_zone_inform_deny: return "inform_deny"; - case local_zone_always_transparent: return "always_transparent"; - case local_zone_always_refuse: return "always_refuse"; - case local_zone_always_nxdomain: return "always_nxdomain"; - } - return "badtyped"; -} - -int local_zone_str2type(const char* type, enum localzone_type* t) -{ - if(strcmp(type, "deny") == 0) - *t = local_zone_deny; - else if(strcmp(type, "refuse") == 0) - *t = local_zone_refuse; - else if(strcmp(type, "static") == 0) - *t = local_zone_static; - else if(strcmp(type, "transparent") == 0) - *t = local_zone_transparent; - else if(strcmp(type, "typetransparent") == 0) - *t = local_zone_typetransparent; - else if(strcmp(type, "redirect") == 0) - *t = local_zone_redirect; - else if(strcmp(type, "inform") == 0) - *t = local_zone_inform; - else if(strcmp(type, "inform_deny") == 0) - *t = local_zone_inform_deny; - else if(strcmp(type, "always_transparent") == 0) - *t = local_zone_always_transparent; - else if(strcmp(type, "always_refuse") == 0) - *t = local_zone_always_refuse; - else if(strcmp(type, "always_nxdomain") == 0) - *t = local_zone_always_nxdomain; - else return 0; - return 1; -} - -/** iterate over the kiddies of the given name and set their parent ptr */ -static void -set_kiddo_parents(struct local_zone* z, struct local_zone* match, - struct local_zone* newp) -{ - /* both zones and z are locked already */ - /* in the sorted rbtree, the kiddies of z are located after z */ - /* z must be present in the tree */ - struct local_zone* p = z; - p = (struct local_zone*)rbtree_next(&p->node); - while(p!=(struct local_zone*)RBTREE_NULL && - p->dclass == z->dclass && dname_strict_subdomain(p->name, - p->namelabs, z->name, z->namelabs)) { - /* update parent ptr */ - /* only when matches with existing parent pointer, so that - * deeper child structures are not touched, i.e. - * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y - * gets to update a.x, b.x and c.x */ - lock_rw_wrlock(&p->lock); - if(p->parent == match) - p->parent = newp; - lock_rw_unlock(&p->lock); - p = (struct local_zone*)rbtree_next(&p->node); - } -} - -struct local_zone* local_zones_add_zone(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, - enum localzone_type tp) -{ - /* create */ - struct local_zone* z = local_zone_create(name, len, labs, tp, dclass); - if(!z) { - free(name); - return NULL; - } - lock_rw_wrlock(&z->lock); - - /* find the closest parent */ - z->parent = local_zones_find(zones, name, len, labs, dclass); - - /* insert into the tree */ - if(!rbtree_insert(&zones->ztree, &z->node)) { - /* duplicate entry! */ - lock_rw_unlock(&z->lock); - local_zone_delete(z); - log_err("internal: duplicate entry in local_zones_add_zone"); - return NULL; - } - - /* set parent pointers right */ - set_kiddo_parents(z, z->parent, z); - - lock_rw_unlock(&z->lock); - return z; -} - -void local_zones_del_zone(struct local_zones* zones, struct local_zone* z) -{ - /* fix up parents in tree */ - lock_rw_wrlock(&z->lock); - set_kiddo_parents(z, z, z->parent); - - /* remove from tree */ - (void)rbtree_delete(&zones->ztree, z); - - /* delete the zone */ - lock_rw_unlock(&z->lock); - local_zone_delete(z); -} - -int -local_zones_add_RR(struct local_zones* zones, const char* rr) -{ - uint8_t* rr_name; - uint16_t rr_class; - size_t len; - int labs; - struct local_zone* z; - int r; - if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { - return 0; - } - labs = dname_count_size_labels(rr_name, &len); - /* could first try readlock then get writelock if zone does not exist, - * but we do not add enough RRs (from multiple threads) to optimize */ - lock_rw_wrlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class); - if(!z) { - z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, - local_zone_transparent); - if(!z) { - lock_rw_unlock(&zones->lock); - return 0; - } - } else { - free(rr_name); - } - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - r = lz_enter_rr_into_zone(z, rr); - lock_rw_unlock(&z->lock); - return r; -} - -/** returns true if the node is terminal so no deeper domain names exist */ -static int -is_terminal(struct local_data* d) -{ - /* for empty nonterminals, the deeper domain names are sorted - * right after them, so simply check the next name in the tree - */ - struct local_data* n = (struct local_data*)rbtree_next(&d->node); - if(n == (struct local_data*)RBTREE_NULL) - return 1; /* last in tree, no deeper node */ - if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs)) - return 0; /* there is a deeper node */ - return 1; -} - -/** delete empty terminals from tree when final data is deleted */ -static void -del_empty_term(struct local_zone* z, struct local_data* d, - uint8_t* name, size_t len, int labs) -{ - while(d && d->rrsets == NULL && is_terminal(d)) { - /* is this empty nonterminal? delete */ - /* note, no memory recycling in zone region */ - (void)rbtree_delete(&z->data, d); - - /* go up and to the next label */ - if(dname_is_root(name)) - return; - dname_remove_label(&name, &len); - labs--; - d = lz_find_node(z, name, len, labs); - } -} - -void local_zones_del_data(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass) -{ - /* find zone */ - struct local_zone* z; - struct local_data* d; - lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, name, len, labs, dclass); - if(!z) { - /* no such zone, we're done */ - lock_rw_unlock(&zones->lock); - return; - } - lock_rw_wrlock(&z->lock); - lock_rw_unlock(&zones->lock); - - /* find the domain */ - d = lz_find_node(z, name, len, labs); - if(d) { - /* no memory recycling for zone deletions ... */ - d->rrsets = NULL; - /* did we delete the soa record ? */ - if(query_dname_compare(d->name, z->name) == 0) - z->soa = NULL; - - /* cleanup the empty nonterminals for this name */ - del_empty_term(z, d, name, len, labs); - } - - lock_rw_unlock(&z->lock); -} diff --git a/external/unbound/services/localzone.h b/external/unbound/services/localzone.h deleted file mode 100644 index 658f28024..000000000 --- a/external/unbound/services/localzone.h +++ /dev/null @@ -1,500 +0,0 @@ -/* - * services/localzone.h - local zones authority service. - * - * 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 - * - * This file contains functions to enable local zone authority service. - */ - -#ifndef SERVICES_LOCALZONE_H -#define SERVICES_LOCALZONE_H -#include "util/rbtree.h" -#include "util/locks.h" -#include "util/storage/dnstree.h" -#include "util/module.h" -#include "services/view.h" -struct packed_rrset_data; -struct ub_packed_rrset_key; -struct regional; -struct config_file; -struct edns_data; -struct query_info; -struct sldns_buffer; -struct comm_reply; -struct config_strlist; - -/** - * Local zone type - * This type determines processing for queries that did not match - * local-data directly. - */ -enum localzone_type { - /** unset type, used for unset tag_action elements */ - local_zone_unset = 0, - /** drop query */ - local_zone_deny, - /** answer with error */ - local_zone_refuse, - /** answer nxdomain or nodata */ - local_zone_static, - /** resolve normally */ - local_zone_transparent, - /** do not block types at localdata names */ - local_zone_typetransparent, - /** answer with data at zone apex */ - local_zone_redirect, - /** remove default AS112 blocking contents for zone - * nodefault is used in config not during service. */ - local_zone_nodefault, - /** log client address, but no block (transparent) */ - local_zone_inform, - /** log client address, and block (drop) */ - local_zone_inform_deny, - /** resolve normally, even when there is local data */ - local_zone_always_transparent, - /** answer with error, even when there is local data */ - local_zone_always_refuse, - /** answer with nxdomain, even when there is local data */ - local_zone_always_nxdomain -}; - -/** - * Authoritative local zones storage, shared. - */ -struct local_zones { - /** lock on the localzone tree */ - lock_rw_type lock; - /** rbtree of struct local_zone */ - rbtree_type ztree; -}; - -/** - * Local zone. A locally served authoritative zone. - */ -struct local_zone { - /** rbtree node, key is name and class */ - rbnode_type node; - /** parent zone, if any. */ - struct local_zone* parent; - - /** zone name, in uncompressed wireformat */ - uint8_t* name; - /** length of zone name */ - size_t namelen; - /** number of labels in zone name */ - int namelabs; - /** the class of this zone. - * uses 'dclass' to not conflict with c++ keyword class. */ - uint16_t dclass; - - /** lock on the data in the structure - * For the node, parent, name, namelen, namelabs, dclass, you - * need to also hold the zones_tree lock to change them (or to - * delete this zone) */ - lock_rw_type lock; - - /** how to process zone */ - enum localzone_type type; - /** tag bitlist */ - uint8_t* taglist; - /** length of the taglist (in bytes) */ - size_t taglen; - /** netblock addr_tree with struct local_zone_override information - * or NULL if there are no override elements */ - struct rbtree_type* override_tree; - - /** in this region the zone's data is allocated. - * the struct local_zone itself is malloced. */ - struct regional* region; - /** local data for this zone - * rbtree of struct local_data */ - rbtree_type data; - /** if data contains zone apex SOA data, this is a ptr to it. */ - struct ub_packed_rrset_key* soa; -}; - -/** - * Local data. One domain name, and the RRs to go with it. - */ -struct local_data { - /** rbtree node, key is name only */ - rbnode_type node; - /** domain name */ - uint8_t* name; - /** length of name */ - size_t namelen; - /** number of labels in name */ - int namelabs; - /** the data rrsets, with different types, linked list. - * If this list is NULL, the node is an empty non-terminal. */ - struct local_rrset* rrsets; -}; - -/** - * A local data RRset - */ -struct local_rrset { - /** next in list */ - struct local_rrset* next; - /** RRset data item */ - struct ub_packed_rrset_key* rrset; -}; - -/** - * Local zone override information - */ -struct local_zone_override { - /** node in addrtree */ - struct addr_tree_node node; - /** override for local zone type */ - enum localzone_type type; -}; - -/** - * Create local zones storage - * @return new struct or NULL on error. - */ -struct local_zones* local_zones_create(void); - -/** - * Delete local zones storage - * @param zones: to delete. - */ -void local_zones_delete(struct local_zones* zones); - -/** - * Apply config settings; setup the local authoritative data. - * Takes care of locking. - * @param zones: is set up. - * @param cfg: config data. - * @return false on error. - */ -int local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg); - -/** - * Compare two local_zone entries in rbtree. Sort hierarchical but not - * canonical - * @param z1: zone 1 - * @param z2: zone 2 - * @return: -1, 0, +1 comparison value. - */ -int local_zone_cmp(const void* z1, const void* z2); - -/** - * Compare two local_data entries in rbtree. Sort canonical. - * @param d1: data 1 - * @param d2: data 2 - * @return: -1, 0, +1 comparison value. - */ -int local_data_cmp(const void* d1, const void* d2); - -/** - * Delete one zone - * @param z: to delete. - */ -void local_zone_delete(struct local_zone* z); - -/** - * Lookup zone that contains the given name, class and taglist. - * User must lock the tree or result zone. - * @param zones: the zones tree - * @param name: dname to lookup - * @param len: length of name. - * @param labs: labelcount of name. - * @param dclass: class to lookup. - * @param taglist: taglist to lookup. - * @param taglen: lenth of taglist. - * @param ignoretags: lookup zone by name and class, regardless the - * local-zone's tags. - * @return closest local_zone or NULL if no covering zone is found. - */ -struct local_zone* local_zones_tags_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, - uint8_t* taglist, size_t taglen, int ignoretags); - -/** - * Lookup zone that contains the given name, class. - * User must lock the tree or result zone. - * @param zones: the zones tree - * @param name: dname to lookup - * @param len: length of name. - * @param labs: labelcount of name. - * @param dclass: class to lookup. - * @return closest local_zone or NULL if no covering zone is found. - */ -struct local_zone* local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass); - -/** - * Debug helper. Print all zones - * Takes care of locking. - * @param zones: the zones tree - */ -void local_zones_print(struct local_zones* zones); - -/** - * Answer authoritatively for local zones. - * Takes care of locking. - * @param zones: the stored zones (shared, read only). - * @param env: the module environment. - * @param qinfo: query info (parsed). - * @param edns: edns info (parsed). - * @param buf: buffer with query ID and flags, also for reply. - * @param temp: temporary storage region. - * @param repinfo: source address for checks. may be NULL. - * @param taglist: taglist for checks. May be NULL. - * @param taglen: length of the taglist. - * @param tagactions: local zone actions for tags. May be NULL. - * @param tagactionssize: length of the tagactions. - * @param tag_datas: array per tag of strlist with rdata strings. or NULL. - * @param tag_datas_size: size of tag_datas array. - * @param tagname: array of tag name strings (for debug output). - * @param num_tags: number of items in tagname array. - * @param view: answer using this view. May be NULL. - * @return true if answer is in buffer. false if query is not answered - * by authority data. If the reply should be dropped altogether, the return - * value is true, but the buffer is cleared (empty). - * It can also return true if a non-exact alias answer is found. In this - * case qinfo->local_alias points to the corresponding alias RRset but the - * answer is NOT encoded in buffer. It's the caller's responsibility to - * complete the alias chain (if needed) and encode the final set of answer. - * Data pointed to by qinfo->local_alias is allocated in 'temp' or refers to - * configuration data. So the caller will need to make a deep copy of it - * if it needs to keep it beyond the lifetime of 'temp' or a dynamic update - * to local zone data. - */ -int local_zones_answer(struct local_zones* zones, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf, - struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, - size_t taglen, uint8_t* tagactions, size_t tagactionssize, - struct config_strlist** tag_datas, size_t tag_datas_size, - char** tagname, int num_tags, struct view* view); - -/** - * Parse the string into localzone type. - * - * @param str: string to parse - * @param t: local zone type returned here. - * @return 0 on parse error. - */ -int local_zone_str2type(const char* str, enum localzone_type* t); - -/** - * Print localzone type to a string. Pointer to a constant string. - * - * @param t: local zone type. - * @return constant string that describes type. - */ -const char* local_zone_type2str(enum localzone_type t); - -/** - * Find zone that with exactly given name, class. - * User must lock the tree or result zone. - * @param zones: the zones tree - * @param name: dname to lookup - * @param len: length of name. - * @param labs: labelcount of name. - * @param dclass: class to lookup. - * @return the exact local_zone or NULL. - */ -struct local_zone* local_zones_find(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass); - -/** - * Add a new zone. Caller must hold the zones lock. - * Adjusts the other zones as well (parent pointers) after insertion. - * The zone must NOT exist (returns NULL and logs error). - * @param zones: the zones tree - * @param name: dname to add - * @param len: length of name. - * @param labs: labelcount of name. - * @param dclass: class to add. - * @param tp: type. - * @return local_zone or NULL on error, caller must printout memory error. - */ -struct local_zone* local_zones_add_zone(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, - enum localzone_type tp); - -/** - * Delete a zone. Caller must hold the zones lock. - * Adjusts the other zones as well (parent pointers) after insertion. - * @param zones: the zones tree - * @param zone: the zone to delete from tree. Also deletes zone from memory. - */ -void local_zones_del_zone(struct local_zones* zones, struct local_zone* zone); - -/** - * Add RR data into the localzone data. - * Looks up the zone, if no covering zone, a transparent zone with the - * name of the RR is created. - * @param zones: the zones tree. Not locked by caller. - * @param rr: string with on RR. - * @return false on failure. - */ -int local_zones_add_RR(struct local_zones* zones, const char* rr); - -/** - * Remove data from domain name in the tree. - * All types are removed. No effect if zone or name does not exist. - * @param zones: zones tree. - * @param name: dname to remove - * @param len: length of name. - * @param labs: labelcount of name. - * @param dclass: class to remove. - */ -void local_zones_del_data(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass); - - -/** - * Form wireformat from text format domain name. - * @param str: the domain name in text "www.example.com" - * @param res: resulting wireformat is stored here with malloc. - * @param len: length of resulting wireformat. - * @param labs: number of labels in resulting wireformat. - * @return false on error, syntax or memory. Also logged. - */ -int parse_dname(const char* str, uint8_t** res, size_t* len, int* labs); - -/** - * Find local data tag string match for the given type (in qinfo) in the list. - * If found, 'r' will be filled with corresponding rrset information. - * @param qinfo: contains name, type, and class for the data - * @param list: stores local tag data to be searched - * @param r: rrset key to be filled for matched data - * @param temp: region to allocate rrset in 'r' - * @return 1 if a match is found and rrset is built; otherwise 0 including - * errors. - */ -int local_data_find_tag_datas(const struct query_info* qinfo, - struct config_strlist* list, struct ub_packed_rrset_key* r, - struct regional* temp); - -/** - * See if two sets of tag lists (in the form of bitmap) have the same tag that - * has an action. If so, '*tag' will be set to the found tag index, and the - * corresponding action will be returned in the form of local zone type. - * Otherwise the passed type (lzt) will be returned as the default action. - * Pointers except tagactions must not be NULL. - * @param taglist: 1st list of tags - * @param taglen: size of taglist in bytes - * @param taglist2: 2nd list of tags - * @param taglen2: size of taglist2 in bytes - * @param tagactions: local data actions for tags. May be NULL. - * @param tagactionssize: length of the tagactions. - * @param lzt: default action (local zone type) if no tag action is found. - * @param tag: see above. - * @param tagname: array of tag name strings (for debug output). - * @param num_tags: number of items in tagname array. - * @return found tag action or the default action. - */ -enum localzone_type local_data_find_tag_action(const uint8_t* taglist, - size_t taglen, const uint8_t* taglist2, size_t taglen2, - const uint8_t* tagactions, size_t tagactionssize, - enum localzone_type lzt, int* tag, char* const* tagname, int num_tags); - -/** - * Parses resource record string into wire format, also returning its field values. - * @param str: input resource record - * @param nm: domain name field - * @param type: record type field - * @param dclass: record class field - * @param ttl: ttl field - * @param rr: buffer for the parsed rr in wire format - * @param len: buffer length - * @param rdata: rdata field - * @param rdata_len: rdata field length - * @return 1 on success; 0 otherwise. - */ -int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, - uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, - uint8_t** rdata, size_t* rdata_len); - -/** - * Insert specified rdata into the specified resource record. - * @param region: allocator - * @param pd: data portion of the destination resource record - * @param rdata: source rdata - * @param rdata_len: source rdata length - * @param ttl: time to live - * @param rrstr: resource record in text form (for logging) - * @return 1 on success; 0 otherwise. - */ -int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, - uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr); - -/** - * Valid response ip actions for the IP-response-driven-action feature; - * defined here instead of in the respip module to enable sharing of enum - * values with the localzone_type enum. - * Note that these values except 'none' are the same as localzone types of - * the 'same semantics'. It's intentional as we use these values via - * access-control-tags, which can be shared for both response ip actions and - * local zones. - */ -enum respip_action { - /** no respip action */ - respip_none = local_zone_unset, - /** don't answer */ - respip_deny = local_zone_deny, - /** redirect as per provided data */ - respip_redirect = local_zone_redirect, - /** log query source and answer query */ - respip_inform = local_zone_inform, - /** log query source and don't answer query */ - respip_inform_deny = local_zone_inform_deny, - /** resolve normally, even when there is response-ip data */ - respip_always_transparent = local_zone_always_transparent, - /** answer with 'refused' response */ - respip_always_refuse = local_zone_always_refuse, - /** answer with 'no such domain' response */ - respip_always_nxdomain = local_zone_always_nxdomain, - - /* The rest of the values are only possible as - * access-control-tag-action */ - - /** serves response data (if any), else, drops queries. */ - respip_refuse = local_zone_refuse, - /** serves response data, else, nodata answer. */ - respip_static = local_zone_static, - /** gives response data (if any), else nodata answer. */ - respip_transparent = local_zone_transparent, - /** gives response data (if any), else nodata answer. */ - respip_typetransparent = local_zone_typetransparent, -}; - -#endif /* SERVICES_LOCALZONE_H */ diff --git a/external/unbound/services/mesh.c b/external/unbound/services/mesh.c deleted file mode 100644 index 0cb134ade..000000000 --- a/external/unbound/services/mesh.c +++ /dev/null @@ -1,1502 +0,0 @@ -/* - * services/mesh.c - deal with mesh of query states and handle events for that. - * - * 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 - * - * This file contains functions to assist in dealing with a mesh of - * query states. This mesh is supposed to be thread-specific. - * It consists of query states (per qname, qtype, qclass) and connections - * between query states and the super and subquery states, and replies to - * send back to clients. - */ -#include "config.h" -#include "services/mesh.h" -#include "services/outbound_list.h" -#include "services/cache/dns.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/module.h" -#include "util/regional.h" -#include "util/data/msgencode.h" -#include "util/timehist.h" -#include "util/fptr_wlist.h" -#include "util/alloc.h" -#include "util/config_file.h" -#include "sldns/sbuffer.h" -#include "sldns/wire2str.h" -#include "services/localzone.h" -#include "util/data/dname.h" -#include "respip/respip.h" - -/** subtract timers and the values do not overflow or become negative */ -static void -timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start) -{ -#ifndef S_SPLINT_S - time_t end_usec = end->tv_usec; - d->tv_sec = end->tv_sec - start->tv_sec; - if(end_usec < start->tv_usec) { - end_usec += 1000000; - d->tv_sec--; - } - d->tv_usec = end_usec - start->tv_usec; -#endif -} - -/** add timers and the values do not overflow or become negative */ -static void -timeval_add(struct timeval* d, const struct timeval* add) -{ -#ifndef S_SPLINT_S - d->tv_sec += add->tv_sec; - d->tv_usec += add->tv_usec; - if(d->tv_usec > 1000000 ) { - d->tv_usec -= 1000000; - d->tv_sec++; - } -#endif -} - -/** divide sum of timers to get average */ -static void -timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d) -{ -#ifndef S_SPLINT_S - size_t leftover; - if(d == 0) { - avg->tv_sec = 0; - avg->tv_usec = 0; - return; - } - avg->tv_sec = sum->tv_sec / d; - avg->tv_usec = sum->tv_usec / d; - /* handle fraction from seconds divide */ - leftover = sum->tv_sec - avg->tv_sec*d; - avg->tv_usec += (leftover*1000000)/d; -#endif -} - -/** histogram compare of time values */ -static int -timeval_smaller(const struct timeval* x, const struct timeval* y) -{ -#ifndef S_SPLINT_S - if(x->tv_sec < y->tv_sec) - return 1; - else if(x->tv_sec == y->tv_sec) { - if(x->tv_usec <= y->tv_usec) - return 1; - else return 0; - } - else return 0; -#endif -} - -/* - * Compare two response-ip client info entries for the purpose of mesh state - * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise - * 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but - * in practice it should be only used to mean they are different). - * We cannot share the mesh state for two queries if different response-ip - * actions can apply in the end, even if those queries are otherwise identical. - * For this purpose we compare tag lists and tag action lists; they should be - * identical to share the same state. - * For tag data, we don't look into the data content, as it can be - * expensive; unless tag data are not defined for both or they point to the - * exact same data in memory (i.e., they come from the same ACL entry), we - * consider these data different. - * Likewise, if the client info is associated with views, we don't look into - * the views. They are considered different unless they are exactly the same - * even if the views only differ in the names. - */ -static int -client_info_compare(const struct respip_client_info* ci_a, - const struct respip_client_info* ci_b) -{ - int cmp; - - if(!ci_a && !ci_b) - return 0; - if(ci_a && !ci_b) - return -1; - if(!ci_a && ci_b) - return 1; - if(ci_a->taglen != ci_b->taglen) - return (ci_a->taglen < ci_b->taglen) ? -1 : 1; - cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen); - if(cmp != 0) - return cmp; - if(ci_a->tag_actions_size != ci_b->tag_actions_size) - return (ci_a->tag_actions_size < ci_b->tag_actions_size) ? - -1 : 1; - cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions, - ci_a->tag_actions_size); - if(cmp != 0) - return cmp; - if(ci_a->tag_datas != ci_b->tag_datas) - return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; - if(ci_a->view != ci_b->view) - return ci_a->view < ci_b->view ? -1 : 1; - /* For the unbound daemon these should be non-NULL and identical, - * but we check that just in case. */ - if(ci_a->respip_set != ci_b->respip_set) - return ci_a->respip_set < ci_b->respip_set ? -1 : 1; - return 0; -} - -int -mesh_state_compare(const void* ap, const void* bp) -{ - struct mesh_state* a = (struct mesh_state*)ap; - struct mesh_state* b = (struct mesh_state*)bp; - int cmp; - - if(a->unique < b->unique) - return -1; - if(a->unique > b->unique) - return 1; - - if(a->s.is_priming && !b->s.is_priming) - return -1; - if(!a->s.is_priming && b->s.is_priming) - return 1; - - if(a->s.is_valrec && !b->s.is_valrec) - return -1; - if(!a->s.is_valrec && b->s.is_valrec) - return 1; - - if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD)) - return -1; - if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD)) - return 1; - - if((a->s.query_flags&BIT_CD) && !(b->s.query_flags&BIT_CD)) - return -1; - if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD)) - return 1; - - cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo); - if(cmp != 0) - return cmp; - return client_info_compare(a->s.client_info, b->s.client_info); -} - -int -mesh_state_ref_compare(const void* ap, const void* bp) -{ - struct mesh_state_ref* a = (struct mesh_state_ref*)ap; - struct mesh_state_ref* b = (struct mesh_state_ref*)bp; - return mesh_state_compare(a->s, b->s); -} - -struct mesh_area* -mesh_create(struct module_stack* stack, struct module_env* env) -{ - struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area)); - if(!mesh) { - log_err("mesh area alloc: out of memory"); - return NULL; - } - mesh->histogram = timehist_setup(); - mesh->qbuf_bak = sldns_buffer_new(env->cfg->msg_buffer_size); - if(!mesh->histogram || !mesh->qbuf_bak) { - free(mesh); - log_err("mesh area alloc: out of memory"); - return NULL; - } - mesh->mods = *stack; - mesh->env = env; - rbtree_init(&mesh->run, &mesh_state_compare); - rbtree_init(&mesh->all, &mesh_state_compare); - mesh->num_reply_addrs = 0; - mesh->num_reply_states = 0; - mesh->num_detached_states = 0; - mesh->num_forever_states = 0; - mesh->stats_jostled = 0; - mesh->stats_dropped = 0; - mesh->max_reply_states = env->cfg->num_queries_per_thread; - mesh->max_forever_states = (mesh->max_reply_states+1)/2; -#ifndef S_SPLINT_S - mesh->jostle_max.tv_sec = (time_t)(env->cfg->jostle_time / 1000); - mesh->jostle_max.tv_usec = (time_t)((env->cfg->jostle_time % 1000) - *1000); -#endif - return mesh; -} - -/** help mesh delete delete mesh states */ -static void -mesh_delete_helper(rbnode_type* n) -{ - struct mesh_state* mstate = (struct mesh_state*)n->key; - /* perform a full delete, not only 'cleanup' routine, - * because other callbacks expect a clean state in the mesh. - * For 're-entrant' calls */ - mesh_state_delete(&mstate->s); - /* but because these delete the items from the tree, postorder - * traversal and rbtree rebalancing do not work together */ -} - -void -mesh_delete(struct mesh_area* mesh) -{ - if(!mesh) - return; - /* free all query states */ - while(mesh->all.count) - mesh_delete_helper(mesh->all.root); - timehist_delete(mesh->histogram); - sldns_buffer_free(mesh->qbuf_bak); - free(mesh); -} - -void -mesh_delete_all(struct mesh_area* mesh) -{ - /* free all query states */ - while(mesh->all.count) - mesh_delete_helper(mesh->all.root); - mesh->stats_dropped += mesh->num_reply_addrs; - /* clear mesh area references */ - rbtree_init(&mesh->run, &mesh_state_compare); - rbtree_init(&mesh->all, &mesh_state_compare); - mesh->num_reply_addrs = 0; - mesh->num_reply_states = 0; - mesh->num_detached_states = 0; - mesh->num_forever_states = 0; - mesh->forever_first = NULL; - mesh->forever_last = NULL; - mesh->jostle_first = NULL; - mesh->jostle_last = NULL; -} - -int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf) -{ - struct mesh_state* m = mesh->jostle_first; - /* free space is available */ - if(mesh->num_reply_states < mesh->max_reply_states) - return 1; - /* try to kick out a jostle-list item */ - if(m && m->reply_list && m->list_select == mesh_jostle_list) { - /* how old is it? */ - struct timeval age; - timeval_subtract(&age, mesh->env->now_tv, - &m->reply_list->start_time); - if(timeval_smaller(&mesh->jostle_max, &age)) { - /* its a goner */ - log_nametypeclass(VERB_ALGO, "query jostled out to " - "make space for a new one", - m->s.qinfo.qname, m->s.qinfo.qtype, - m->s.qinfo.qclass); - /* backup the query */ - if(qbuf) sldns_buffer_copy(mesh->qbuf_bak, qbuf); - /* notify supers */ - if(m->super_set.count > 0) { - verbose(VERB_ALGO, "notify supers of failure"); - m->s.return_msg = NULL; - m->s.return_rcode = LDNS_RCODE_SERVFAIL; - mesh_walk_supers(mesh, m); - } - mesh->stats_jostled ++; - mesh_state_delete(&m->s); - /* restore the query - note that the qinfo ptr to - * the querybuffer is then correct again. */ - if(qbuf) sldns_buffer_copy(qbuf, mesh->qbuf_bak); - return 1; - } - } - /* no space for new item */ - return 0; -} - -void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - struct respip_client_info* cinfo, uint16_t qflags, - struct edns_data* edns, struct comm_reply* rep, uint16_t qid) -{ - struct mesh_state* s = NULL; - int unique = unique_mesh_state(edns->opt_list, mesh->env); - int was_detached = 0; - int was_noreply = 0; - int added = 0; - if(!unique) - s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); - /* does this create a new reply state? */ - if(!s || s->list_select == mesh_no_list) { - if(!mesh_make_new_space(mesh, rep->c->buffer)) { - verbose(VERB_ALGO, "Too many queries. dropping " - "incoming query."); - comm_point_drop_reply(rep); - mesh->stats_dropped ++; - return; - } - /* for this new reply state, the reply address is free, - * so the limit of reply addresses does not stop reply states*/ - } else { - /* protect our memory usage from storing reply addresses */ - if(mesh->num_reply_addrs > mesh->max_reply_states*16) { - verbose(VERB_ALGO, "Too many requests queued. " - "dropping incoming query."); - mesh->stats_dropped++; - comm_point_drop_reply(rep); - return; - } - } - /* see if it already exists, if not, create one */ - if(!s) { -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - s = mesh_state_create(mesh->env, qinfo, cinfo, - qflags&(BIT_RD|BIT_CD), 0, 0); - if(!s) { - log_err("mesh_state_create: out of memory; SERVFAIL"); - if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, - LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch)) - edns->opt_list = NULL; - error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, - qinfo, qid, qflags, edns); - comm_point_send_reply(rep); - return; - } - if(unique) - mesh_state_make_unique(s); - /* copy the edns options we got from the front */ - if(edns->opt_list) { - s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, - s->s.region); - if(!s->s.edns_opts_front_in) { - log_err("mesh_state_create: out of memory; SERVFAIL"); - if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, - NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch)) - edns->opt_list = NULL; - error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, - qinfo, qid, qflags, edns); - comm_point_send_reply(rep); - return; - } - } - -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->all, &s->node); - log_assert(n != NULL); - /* set detached (it is now) */ - mesh->num_detached_states++; - added = 1; - } - if(!s->reply_list && !s->cb_list && s->super_set.count == 0) - was_detached = 1; - if(!s->reply_list && !s->cb_list) - was_noreply = 1; - /* add reply to s */ - if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) { - log_err("mesh_new_client: out of memory; SERVFAIL"); - if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s, - NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch)) - edns->opt_list = NULL; - error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, - qinfo, qid, qflags, edns); - comm_point_send_reply(rep); - if(added) - mesh_state_delete(&s->s); - return; - } - /* update statistics */ - if(was_detached) { - log_assert(mesh->num_detached_states > 0); - mesh->num_detached_states--; - } - if(was_noreply) { - mesh->num_reply_states ++; - } - mesh->num_reply_addrs++; - if(s->list_select == mesh_no_list) { - /* move to either the forever or the jostle_list */ - if(mesh->num_forever_states < mesh->max_forever_states) { - mesh->num_forever_states ++; - mesh_list_insert(s, &mesh->forever_first, - &mesh->forever_last); - s->list_select = mesh_forever_list; - } else { - mesh_list_insert(s, &mesh->jostle_first, - &mesh->jostle_last); - s->list_select = mesh_jostle_list; - } - } - if(added) - mesh_run(mesh, s, module_event_new, NULL); -} - -int -mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg) -{ - struct mesh_state* s = NULL; - int unique = unique_mesh_state(edns->opt_list, mesh->env); - int was_detached = 0; - int was_noreply = 0; - int added = 0; - if(!unique) - s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); - - /* there are no limits on the number of callbacks */ - - /* see if it already exists, if not, create one */ - if(!s) { -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - s = mesh_state_create(mesh->env, qinfo, NULL, - qflags&(BIT_RD|BIT_CD), 0, 0); - if(!s) { - return 0; - } - if(unique) - mesh_state_make_unique(s); - if(edns->opt_list) { - s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, - s->s.region); - if(!s->s.edns_opts_front_in) { - return 0; - } - } -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->all, &s->node); - log_assert(n != NULL); - /* set detached (it is now) */ - mesh->num_detached_states++; - added = 1; - } - if(!s->reply_list && !s->cb_list && s->super_set.count == 0) - was_detached = 1; - if(!s->reply_list && !s->cb_list) - was_noreply = 1; - /* add reply to s */ - if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) { - if(added) - mesh_state_delete(&s->s); - return 0; - } - /* update statistics */ - if(was_detached) { - log_assert(mesh->num_detached_states > 0); - mesh->num_detached_states--; - } - if(was_noreply) { - mesh->num_reply_states ++; - } - mesh->num_reply_addrs++; - if(added) - mesh_run(mesh, s, module_event_new, NULL); - return 1; -} - -void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway) -{ - struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, - qflags&(BIT_RD|BIT_CD), 0, 0); -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - /* already exists, and for a different purpose perhaps. - * if mesh_no_list, keep it that way. */ - if(s) { - /* make it ignore the cache from now on */ - if(!s->s.blacklist) - sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); - if(s->s.prefetch_leeway < leeway) - s->s.prefetch_leeway = leeway; - return; - } - if(!mesh_make_new_space(mesh, NULL)) { - verbose(VERB_ALGO, "Too many queries. dropped prefetch."); - mesh->stats_dropped ++; - return; - } - - s = mesh_state_create(mesh->env, qinfo, NULL, - qflags&(BIT_RD|BIT_CD), 0, 0); - if(!s) { - log_err("prefetch mesh_state_create: out of memory"); - return; - } -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->all, &s->node); - log_assert(n != NULL); - /* set detached (it is now) */ - mesh->num_detached_states++; - /* make it ignore the cache */ - sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); - s->s.prefetch_leeway = leeway; - - if(s->list_select == mesh_no_list) { - /* move to either the forever or the jostle_list */ - if(mesh->num_forever_states < mesh->max_forever_states) { - mesh->num_forever_states ++; - mesh_list_insert(s, &mesh->forever_first, - &mesh->forever_last); - s->list_select = mesh_forever_list; - } else { - mesh_list_insert(s, &mesh->jostle_first, - &mesh->jostle_last); - s->list_select = mesh_jostle_list; - } - } - mesh_run(mesh, s, module_event_new, NULL); -} - -void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, - struct comm_reply* reply, int what) -{ - enum module_ev event = module_event_reply; - e->qstate->reply = reply; - if(what != NETEVENT_NOERROR) { - event = module_event_noreply; - if(what == NETEVENT_CAPSFAIL) - event = module_event_capsfail; - } - mesh_run(mesh, e->qstate->mesh_info, event, e); -} - -struct mesh_state* -mesh_state_create(struct module_env* env, struct query_info* qinfo, - struct respip_client_info* cinfo, uint16_t qflags, int prime, - int valrec) -{ - struct regional* region = alloc_reg_obtain(env->alloc); - struct mesh_state* mstate; - int i; - if(!region) - return NULL; - mstate = (struct mesh_state*)regional_alloc(region, - sizeof(struct mesh_state)); - if(!mstate) { - alloc_reg_release(env->alloc, region); - return NULL; - } - memset(mstate, 0, sizeof(*mstate)); - mstate->node = *RBTREE_NULL; - mstate->run_node = *RBTREE_NULL; - mstate->node.key = mstate; - mstate->run_node.key = mstate; - mstate->reply_list = NULL; - mstate->list_select = mesh_no_list; - mstate->replies_sent = 0; - rbtree_init(&mstate->super_set, &mesh_state_ref_compare); - rbtree_init(&mstate->sub_set, &mesh_state_ref_compare); - mstate->num_activated = 0; - mstate->unique = NULL; - /* init module qstate */ - mstate->s.qinfo.qtype = qinfo->qtype; - mstate->s.qinfo.qclass = qinfo->qclass; - mstate->s.qinfo.local_alias = NULL; - mstate->s.qinfo.qname_len = qinfo->qname_len; - mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname, - qinfo->qname_len); - if(!mstate->s.qinfo.qname) { - alloc_reg_release(env->alloc, region); - return NULL; - } - if(cinfo) { - mstate->s.client_info = regional_alloc_init(region, cinfo, - sizeof(*cinfo)); - if(!mstate->s.client_info) { - alloc_reg_release(env->alloc, region); - return NULL; - } - } - /* remove all weird bits from qflags */ - mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD)); - mstate->s.is_priming = prime; - mstate->s.is_valrec = valrec; - mstate->s.reply = NULL; - mstate->s.region = region; - mstate->s.curmod = 0; - mstate->s.return_msg = 0; - mstate->s.return_rcode = LDNS_RCODE_NOERROR; - mstate->s.env = env; - mstate->s.mesh_info = mstate; - mstate->s.prefetch_leeway = 0; - mstate->s.no_cache_lookup = 0; - mstate->s.no_cache_store = 0; - /* init modules */ - for(i=0; i<env->mesh->mods.num; i++) { - mstate->s.minfo[i] = NULL; - mstate->s.ext_state[i] = module_state_initial; - } - /* init edns option lists */ - mstate->s.edns_opts_front_in = NULL; - mstate->s.edns_opts_back_out = NULL; - mstate->s.edns_opts_back_in = NULL; - mstate->s.edns_opts_front_out = NULL; - - return mstate; -} - -int -mesh_state_is_unique(struct mesh_state* mstate) -{ - return mstate->unique != NULL; -} - -void -mesh_state_make_unique(struct mesh_state* mstate) -{ - mstate->unique = mstate; -} - -void -mesh_state_cleanup(struct mesh_state* mstate) -{ - struct mesh_area* mesh; - int i; - if(!mstate) - return; - mesh = mstate->s.env->mesh; - /* drop unsent replies */ - if(!mstate->replies_sent) { - struct mesh_reply* rep; - struct mesh_cb* cb; - for(rep=mstate->reply_list; rep; rep=rep->next) { - comm_point_drop_reply(&rep->query_reply); - mesh->num_reply_addrs--; - } - for(cb=mstate->cb_list; cb; cb=cb->next) { - fptr_ok(fptr_whitelist_mesh_cb(cb->cb)); - (*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL, - sec_status_unchecked, NULL); - mesh->num_reply_addrs--; - } - } - - /* de-init modules */ - for(i=0; i<mesh->mods.num; i++) { - fptr_ok(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear)); - (*mesh->mods.mod[i]->clear)(&mstate->s, i); - mstate->s.minfo[i] = NULL; - mstate->s.ext_state[i] = module_finished; - } - alloc_reg_release(mstate->s.env->alloc, mstate->s.region); -} - -void -mesh_state_delete(struct module_qstate* qstate) -{ - struct mesh_area* mesh; - struct mesh_state_ref* super, ref; - struct mesh_state* mstate; - if(!qstate) - return; - mstate = qstate->mesh_info; - mesh = mstate->s.env->mesh; - mesh_detach_subs(&mstate->s); - if(mstate->list_select == mesh_forever_list) { - mesh->num_forever_states --; - mesh_list_remove(mstate, &mesh->forever_first, - &mesh->forever_last); - } else if(mstate->list_select == mesh_jostle_list) { - mesh_list_remove(mstate, &mesh->jostle_first, - &mesh->jostle_last); - } - if(!mstate->reply_list && !mstate->cb_list - && mstate->super_set.count == 0) { - log_assert(mesh->num_detached_states > 0); - mesh->num_detached_states--; - } - if(mstate->reply_list || mstate->cb_list) { - log_assert(mesh->num_reply_states > 0); - mesh->num_reply_states--; - } - ref.node.key = &ref; - ref.s = mstate; - RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) { - (void)rbtree_delete(&super->s->sub_set, &ref); - } - (void)rbtree_delete(&mesh->run, mstate); - (void)rbtree_delete(&mesh->all, mstate); - mesh_state_cleanup(mstate); -} - -/** helper recursive rbtree find routine */ -static int -find_in_subsub(struct mesh_state* m, struct mesh_state* tofind, size_t *c) -{ - struct mesh_state_ref* r; - if((*c)++ > MESH_MAX_SUBSUB) - return 1; - RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) { - if(r->s == tofind || find_in_subsub(r->s, tofind, c)) - return 1; - } - return 0; -} - -/** find cycle for already looked up mesh_state */ -static int -mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m) -{ - struct mesh_state* cyc_m = qstate->mesh_info; - size_t counter = 0; - if(!dep_m) - return 0; - if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m, &counter)) { - if(counter > MESH_MAX_SUBSUB) - return 2; - return 1; - } - return 0; -} - -void mesh_detach_subs(struct module_qstate* qstate) -{ - struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state_ref* ref, lookup; -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - lookup.node.key = &lookup; - lookup.s = qstate->mesh_info; - RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) { -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_delete(&ref->s->super_set, &lookup); - log_assert(n != NULL); /* must have been present */ - if(!ref->s->reply_list && !ref->s->cb_list - && ref->s->super_set.count == 0) { - mesh->num_detached_states++; - log_assert(mesh->num_detached_states + - mesh->num_reply_states <= mesh->all.count); - } - } - 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) -{ - /* find it, if not, create it */ - struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* sub = mesh_area_find(mesh, NULL, qinfo, qflags, - prime, valrec); - int was_detached; - if(mesh_detect_cycle_found(qstate, sub)) { - verbose(VERB_ALGO, "attach failed, cycle detected"); - return 0; - } - if(!sub) { -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - /* create a new one */ - sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, - valrec); - if(!sub) { - log_err("mesh_attach_sub: out of memory"); - return 0; - } -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->all, &sub->node); - log_assert(n != NULL); - /* set detached (it is now) */ - mesh->num_detached_states++; - /* set new query state to run */ -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&mesh->run, &sub->run_node); - log_assert(n != NULL); - *newq = &sub->s; - } else - *newq = NULL; - was_detached = (sub->super_set.count == 0); - if(!mesh_state_attachment(qstate->mesh_info, sub)) - return 0; - /* if it was a duplicate attachment, the count was not zero before */ - if(!sub->reply_list && !sub->cb_list && was_detached && - sub->super_set.count == 1) { - /* it used to be detached, before this one got added */ - log_assert(mesh->num_detached_states > 0); - mesh->num_detached_states--; - } - /* *newq will be run when inited after the current module stops */ - return 1; -} - -int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub) -{ -#ifdef UNBOUND_DEBUG - struct rbnode_type* n; -#endif - struct mesh_state_ref* subref; /* points to sub, inserted in super */ - struct mesh_state_ref* superref; /* points to super, inserted in sub */ - if( !(subref = regional_alloc(super->s.region, - sizeof(struct mesh_state_ref))) || - !(superref = regional_alloc(sub->s.region, - sizeof(struct mesh_state_ref))) ) { - log_err("mesh_state_attachment: out of memory"); - return 0; - } - superref->node.key = superref; - superref->s = super; - subref->node.key = subref; - subref->s = sub; - if(!rbtree_insert(&sub->super_set, &superref->node)) { - /* this should not happen, iterator and validator do not - * attach subqueries that are identical. */ - /* already attached, we are done, nothing todo. - * since superref and subref already allocated in region, - * we cannot free them */ - return 1; - } -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(&super->sub_set, &subref->node); - log_assert(n != NULL); /* we checked above if statement, the reverse - administration should not fail now, unless they are out of sync */ - return 1; -} - -/** - * callback results to mesh cb entry - * @param m: mesh state to send it for. - * @param rcode: if not 0, error code. - * @param rep: reply to send (or NULL if rcode is set). - * @param r: callback entry - */ -static void -mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, - struct mesh_cb* r) -{ - int secure; - char* reason = NULL; - /* bogus messages are not made into servfail, sec_status passed - * to the callback function */ - if(rep && rep->security == sec_status_secure) - secure = 1; - else secure = 0; - if(!rep && rcode == LDNS_RCODE_NOERROR) - rcode = LDNS_RCODE_SERVFAIL; - if(!rcode && rep->security == sec_status_bogus) { - if(!(reason = errinf_to_str(&m->s))) - rcode = LDNS_RCODE_SERVFAIL; - } - /* send the reply */ - if(rcode) { - if(rcode == LDNS_RCODE_SERVFAIL) { - if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, - rep, rcode, &r->edns, m->s.region)) - r->edns.opt_list = NULL; - } else { - if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, - &r->edns, m->s.region)) - r->edns.opt_list = NULL; - } - fptr_ok(fptr_whitelist_mesh_cb(r->cb)); - (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL); - } else { - size_t udp_size = r->edns.udp_size; - sldns_buffer_clear(r->buf); - r->edns.edns_version = EDNS_ADVERTISED_VERSION; - r->edns.udp_size = EDNS_ADVERTISED_SIZE; - r->edns.ext_rcode = 0; - r->edns.bits &= EDNS_DO; - - if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, - LDNS_RCODE_NOERROR, &r->edns, m->s.region) || - !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, - r->qflags, r->buf, 0, 1, - m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure)) - { - fptr_ok(fptr_whitelist_mesh_cb(r->cb)); - (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, - sec_status_unchecked, NULL); - } else { - fptr_ok(fptr_whitelist_mesh_cb(r->cb)); - (*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf, - rep->security, reason); - } - } - free(reason); - m->s.env->mesh->num_reply_addrs--; -} - -/** - * Send reply to mesh reply entry - * @param m: mesh state to send it for. - * @param rcode: if not 0, error code. - * @param rep: reply to send (or NULL if rcode is set). - * @param r: reply entry - * @param prev: previous reply, already has its answer encoded in buffer. - */ -static void -mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, - struct mesh_reply* r, struct mesh_reply* prev) -{ - struct timeval end_time; - struct timeval duration; - int secure; - /* Copy the client's EDNS for later restore, to make sure the edns - * compare is with the correct edns options. */ - struct edns_data edns_bak = r->edns; - /* examine security status */ - if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || - m->s.env->cfg->ignore_cd) && rep && - rep->security <= sec_status_bogus) { - rcode = LDNS_RCODE_SERVFAIL; - if(m->s.env->cfg->stat_extended) - m->s.env->mesh->ans_bogus++; - } - if(rep && rep->security == sec_status_secure) - secure = 1; - else secure = 0; - if(!rep && rcode == LDNS_RCODE_NOERROR) - rcode = LDNS_RCODE_SERVFAIL; - /* send the reply */ - /* We don't reuse the encoded answer if either the previous or current - * response has a local alias. We could compare the alias records - * and still reuse the previous answer if they are the same, but that - * would be complicated and error prone for the relatively minor case. - * So we err on the side of safety. */ - if(prev && prev->qflags == r->qflags && - !prev->local_alias && !r->local_alias && - prev->edns.edns_present == r->edns.edns_present && - prev->edns.bits == r->edns.bits && - prev->edns.udp_size == r->edns.udp_size && - edns_opt_list_compare(prev->edns.opt_list, r->edns.opt_list) - == 0) { - /* if the previous reply is identical to this one, fix ID */ - if(prev->query_reply.c->buffer != r->query_reply.c->buffer) - sldns_buffer_copy(r->query_reply.c->buffer, - prev->query_reply.c->buffer); - sldns_buffer_write_at(r->query_reply.c->buffer, 0, - &r->qid, sizeof(uint16_t)); - sldns_buffer_write_at(r->query_reply.c->buffer, 12, - r->qname, m->s.qinfo.qname_len); - comm_point_send_reply(&r->query_reply); - } else if(rcode) { - m->s.qinfo.qname = r->qname; - m->s.qinfo.local_alias = r->local_alias; - if(rcode == LDNS_RCODE_SERVFAIL) { - if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, - rep, rcode, &r->edns, m->s.region)) - r->edns.opt_list = NULL; - } else { - if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, - &r->edns, m->s.region)) - r->edns.opt_list = NULL; - } - error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, - r->qid, r->qflags, &r->edns); - comm_point_send_reply(&r->query_reply); - } else { - size_t udp_size = r->edns.udp_size; - r->edns.edns_version = EDNS_ADVERTISED_VERSION; - r->edns.udp_size = EDNS_ADVERTISED_SIZE; - r->edns.ext_rcode = 0; - r->edns.bits &= EDNS_DO; - m->s.qinfo.qname = r->qname; - m->s.qinfo.local_alias = r->local_alias; - if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, - LDNS_RCODE_NOERROR, &r->edns, m->s.region) || - !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, - r->qflags, r->query_reply.c->buffer, 0, 1, - m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure)) - { - if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, - rep, LDNS_RCODE_SERVFAIL, &r->edns, m->s.region)) - r->edns.opt_list = NULL; - error_encode(r->query_reply.c->buffer, - LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, - r->qflags, &r->edns); - } - r->edns = edns_bak; - comm_point_send_reply(&r->query_reply); - } - /* account */ - m->s.env->mesh->num_reply_addrs--; - end_time = *m->s.env->now_tv; - timeval_subtract(&duration, &end_time, &r->start_time); - verbose(VERB_ALGO, "query took " ARG_LL "d.%6.6d sec", - (long long)duration.tv_sec, (int)duration.tv_usec); - m->s.env->mesh->replies_sent++; - timeval_add(&m->s.env->mesh->replies_sum_wait, &duration); - timehist_insert(m->s.env->mesh->histogram, &duration); - if(m->s.env->cfg->stat_extended) { - uint16_t rc = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(r-> - query_reply.c->buffer, 2)); - if(secure) m->s.env->mesh->ans_secure++; - m->s.env->mesh->ans_rcode[ rc ] ++; - if(rc == 0 && LDNS_ANCOUNT(sldns_buffer_begin(r-> - query_reply.c->buffer)) == 0) - m->s.env->mesh->ans_nodata++; - } - /* Log reply sent */ - if(m->s.env->cfg->log_replies) { - log_reply_info(0, &m->s.qinfo, &r->query_reply.addr, - r->query_reply.addrlen, duration, 0, - r->query_reply.c->buffer); - } -} - -void mesh_query_done(struct mesh_state* mstate) -{ - struct mesh_reply* r; - struct mesh_reply* prev = NULL; - struct mesh_cb* c; - struct reply_info* rep = (mstate->s.return_msg? - mstate->s.return_msg->rep:NULL); - for(r = mstate->reply_list; r; r = r->next) { - /* if a response-ip address block has been stored the - * information should be logged for each client. */ - if(mstate->s.respip_action_info && - mstate->s.respip_action_info->addrinfo) { - respip_inform_print(mstate->s.respip_action_info->addrinfo, - r->qname, mstate->s.qinfo.qtype, - mstate->s.qinfo.qclass, r->local_alias, - &r->query_reply); - } - - /* if this query is determined to be dropped during the - * mesh processing, this is the point to take that action. */ - if(mstate->s.is_drop) - comm_point_drop_reply(&r->query_reply); - else { - mesh_send_reply(mstate, mstate->s.return_rcode, rep, - r, prev); - prev = r; - } - } - mstate->replies_sent = 1; - for(c = mstate->cb_list; c; c = c->next) { - mesh_do_callback(mstate, mstate->s.return_rcode, rep, c); - } -} - -void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate) -{ - struct mesh_state_ref* ref; - RBTREE_FOR(ref, struct mesh_state_ref*, &mstate->super_set) - { - /* make super runnable */ - (void)rbtree_insert(&mesh->run, &ref->s->run_node); - /* callback the function to inform super of result */ - fptr_ok(fptr_whitelist_mod_inform_super( - mesh->mods.mod[ref->s->s.curmod]->inform_super)); - (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s, - ref->s->s.curmod, &ref->s->s); - } -} - -struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct respip_client_info* cinfo, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec) -{ - struct mesh_state key; - struct mesh_state* result; - - key.node.key = &key; - key.s.is_priming = prime; - key.s.is_valrec = valrec; - key.s.qinfo = *qinfo; - key.s.query_flags = qflags; - /* We are searching for a similar mesh state when we DO want to - * aggregate the state. Thus unique is set to NULL. (default when we - * desire aggregation).*/ - key.unique = NULL; - key.s.client_info = cinfo; - - result = (struct mesh_state*)rbtree_search(&mesh->all, &key); - return result; -} - -int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, - sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, - uint16_t qid, uint16_t qflags) -{ - struct mesh_cb* r = regional_alloc(s->s.region, - sizeof(struct mesh_cb)); - if(!r) - return 0; - r->buf = buf; - log_assert(fptr_whitelist_mesh_cb(cb)); /* early failure ifmissing*/ - r->cb = cb; - r->cb_arg = cb_arg; - r->edns = *edns; - if(edns->opt_list) { - r->edns.opt_list = edns_opt_copy_region(edns->opt_list, - s->s.region); - if(!r->edns.opt_list) - return 0; - } - r->qid = qid; - r->qflags = qflags; - r->next = s->cb_list; - s->cb_list = r; - return 1; - -} - -int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, - struct comm_reply* rep, uint16_t qid, uint16_t qflags, - const struct query_info* qinfo) -{ - struct mesh_reply* r = regional_alloc(s->s.region, - sizeof(struct mesh_reply)); - if(!r) - return 0; - r->query_reply = *rep; - r->edns = *edns; - if(edns->opt_list) { - r->edns.opt_list = edns_opt_copy_region(edns->opt_list, - s->s.region); - if(!r->edns.opt_list) - return 0; - } - r->qid = qid; - r->qflags = qflags; - r->start_time = *s->s.env->now_tv; - r->next = s->reply_list; - r->qname = regional_alloc_init(s->s.region, qinfo->qname, - s->s.qinfo.qname_len); - if(!r->qname) - return 0; - - /* Data related to local alias stored in 'qinfo' (if any) is ephemeral - * and can be different for different original queries (even if the - * replaced query name is the same). So we need to make a deep copy - * and store the copy for each reply info. */ - if(qinfo->local_alias) { - struct packed_rrset_data* d; - struct packed_rrset_data* dsrc; - r->local_alias = regional_alloc_zero(s->s.region, - sizeof(*qinfo->local_alias)); - if(!r->local_alias) - return 0; - r->local_alias->rrset = regional_alloc_init(s->s.region, - qinfo->local_alias->rrset, - sizeof(*qinfo->local_alias->rrset)); - if(!r->local_alias->rrset) - return 0; - dsrc = qinfo->local_alias->rrset->entry.data; - - /* In the current implementation, a local alias must be - * a single CNAME RR (see worker_handle_request()). */ - log_assert(!qinfo->local_alias->next && dsrc->count == 1 && - qinfo->local_alias->rrset->rk.type == - htons(LDNS_RR_TYPE_CNAME)); - /* Technically, we should make a local copy for the owner - * name of the RRset, but in the case of the first (and - * currently only) local alias RRset, the owner name should - * point to the qname of the corresponding query, which should - * be valid throughout the lifetime of this mesh_reply. So - * we can skip copying. */ - log_assert(qinfo->local_alias->rrset->rk.dname == - sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE)); - - d = regional_alloc_init(s->s.region, dsrc, - sizeof(struct packed_rrset_data) - + sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t)); - if(!d) - return 0; - r->local_alias->rrset->entry.data = d; - d->rr_len = (size_t*)((uint8_t*)d + - sizeof(struct packed_rrset_data)); - d->rr_data = (uint8_t**)&(d->rr_len[1]); - d->rr_ttl = (time_t*)&(d->rr_data[1]); - d->rr_len[0] = dsrc->rr_len[0]; - d->rr_ttl[0] = dsrc->rr_ttl[0]; - d->rr_data[0] = regional_alloc_init(s->s.region, - dsrc->rr_data[0], d->rr_len[0]); - if(!d->rr_data[0]) - return 0; - } else - r->local_alias = NULL; - - s->reply_list = r; - return 1; -} - -/** - * Continue processing the mesh state at another module. - * Handles module to modules tranfer of control. - * Handles module finished. - * @param mesh: the mesh area. - * @param mstate: currently active mesh state. - * Deleted if finished, calls _done and _supers to - * send replies to clients and inform other mesh states. - * This in turn may create additional runnable mesh states. - * @param s: state at which the current module exited. - * @param ev: the event sent to the module. - * returned is the event to send to the next module. - * @return true if continue processing at the new module. - * false if not continued processing is needed. - */ -static int -mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, - enum module_ext_state s, enum module_ev* ev) -{ - mstate->num_activated++; - if(mstate->num_activated > MESH_MAX_ACTIVATION) { - /* module is looping. Stop it. */ - log_err("internal error: looping module stopped"); - log_query_info(VERB_QUERY, "pass error for qstate", - &mstate->s.qinfo); - s = module_error; - } - if(s == module_wait_module || s == module_restart_next) { - /* start next module */ - mstate->s.curmod++; - if(mesh->mods.num == mstate->s.curmod) { - log_err("Cannot pass to next module; at last module"); - log_query_info(VERB_QUERY, "pass error for qstate", - &mstate->s.qinfo); - mstate->s.curmod--; - return mesh_continue(mesh, mstate, module_error, ev); - } - if(s == module_restart_next) { - int curmod = mstate->s.curmod; - for(; mstate->s.curmod < mesh->mods.num; - mstate->s.curmod++) { - fptr_ok(fptr_whitelist_mod_clear( - mesh->mods.mod[mstate->s.curmod]->clear)); - (*mesh->mods.mod[mstate->s.curmod]->clear) - (&mstate->s, mstate->s.curmod); - mstate->s.minfo[mstate->s.curmod] = NULL; - } - mstate->s.curmod = curmod; - } - *ev = module_event_pass; - return 1; - } - if(s == module_wait_subquery && mstate->sub_set.count == 0) { - log_err("module cannot wait for subquery, subquery list empty"); - log_query_info(VERB_QUERY, "pass error for qstate", - &mstate->s.qinfo); - s = module_error; - } - if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) { - /* error is bad, handle pass back up below */ - mstate->s.return_rcode = LDNS_RCODE_SERVFAIL; - } - if(s == module_error || s == module_finished) { - if(mstate->s.curmod == 0) { - mesh_query_done(mstate); - mesh_walk_supers(mesh, mstate); - mesh_state_delete(&mstate->s); - return 0; - } - /* pass along the locus of control */ - mstate->s.curmod --; - *ev = module_event_moddone; - return 1; - } - return 0; -} - -void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate, - enum module_ev ev, struct outbound_entry* e) -{ - enum module_ext_state s; - verbose(VERB_ALGO, "mesh_run: start"); - while(mstate) { - /* run the module */ - fptr_ok(fptr_whitelist_mod_operate( - mesh->mods.mod[mstate->s.curmod]->operate)); - (*mesh->mods.mod[mstate->s.curmod]->operate) - (&mstate->s, ev, mstate->s.curmod, e); - - /* examine results */ - mstate->s.reply = NULL; - regional_free_all(mstate->s.env->scratch); - s = mstate->s.ext_state[mstate->s.curmod]; - verbose(VERB_ALGO, "mesh_run: %s module exit state is %s", - mesh->mods.mod[mstate->s.curmod]->name, strextstate(s)); - e = NULL; - if(mesh_continue(mesh, mstate, s, &ev)) - continue; - - /* run more modules */ - ev = module_event_pass; - if(mesh->run.count > 0) { - /* pop random element off the runnable tree */ - mstate = (struct mesh_state*)mesh->run.root->key; - (void)rbtree_delete(&mesh->run, mstate); - } else mstate = NULL; - } - if(verbosity >= VERB_ALGO) { - mesh_stats(mesh, "mesh_run: end"); - mesh_log_list(mesh); - } -} - -void -mesh_log_list(struct mesh_area* mesh) -{ - char buf[30]; - struct mesh_state* m; - int num = 0; - RBTREE_FOR(m, struct mesh_state*, &mesh->all) { - snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s", - num++, (m->s.is_priming)?"p":"", /* prime */ - (m->s.is_valrec)?"v":"", /* prime */ - (m->s.query_flags&BIT_RD)?"RD":"", - (m->s.query_flags&BIT_CD)?"CD":"", - (m->super_set.count==0)?"d":"", /* detached */ - (m->sub_set.count!=0)?"c":"", /* children */ - m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/ - (m->cb_list)?"cb":"" /* callbacks */ - ); - log_query_info(VERB_ALGO, buf, &m->s.qinfo); - } -} - -void -mesh_stats(struct mesh_area* mesh, const char* str) -{ - verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, " - "%u detached), %u waiting replies, %u recursion replies " - "sent, %d replies dropped, %d states jostled out", - str, (unsigned)mesh->all.count, - (unsigned)mesh->num_reply_states, - (unsigned)mesh->num_detached_states, - (unsigned)mesh->num_reply_addrs, - (unsigned)mesh->replies_sent, - (unsigned)mesh->stats_dropped, - (unsigned)mesh->stats_jostled); - if(mesh->replies_sent > 0) { - struct timeval avg; - timeval_divide(&avg, &mesh->replies_sum_wait, - mesh->replies_sent); - log_info("average recursion processing time " - ARG_LL "d.%6.6d sec", - (long long)avg.tv_sec, (int)avg.tv_usec); - log_info("histogram of recursion processing times"); - timehist_log(mesh->histogram, "recursions"); - } -} - -void -mesh_stats_clear(struct mesh_area* mesh) -{ - if(!mesh) - return; - mesh->replies_sent = 0; - mesh->replies_sum_wait.tv_sec = 0; - mesh->replies_sum_wait.tv_usec = 0; - mesh->stats_jostled = 0; - mesh->stats_dropped = 0; - timehist_clear(mesh->histogram); - mesh->ans_secure = 0; - mesh->ans_bogus = 0; - memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16); - mesh->ans_nodata = 0; -} - -size_t -mesh_get_mem(struct mesh_area* mesh) -{ - struct mesh_state* m; - size_t s = sizeof(*mesh) + sizeof(struct timehist) + - sizeof(struct th_buck)*mesh->histogram->num + - sizeof(sldns_buffer) + sldns_buffer_capacity(mesh->qbuf_bak); - RBTREE_FOR(m, struct mesh_state*, &mesh->all) { - /* all, including m itself allocated in qstate region */ - s += regional_get_mem(m->s.region); - } - return s; -} - -int -mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t flags, int prime, int valrec) -{ - struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* dep_m = NULL; - if(!mesh_state_is_unique(qstate->mesh_info)) - dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); - return mesh_detect_cycle_found(qstate, dep_m); -} - -void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp, - struct mesh_state** lp) -{ - /* insert as last element */ - m->prev = *lp; - m->next = NULL; - if(*lp) - (*lp)->next = m; - else *fp = m; - *lp = m; -} - -void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp, - struct mesh_state** lp) -{ - if(m->next) - m->next->prev = m->prev; - else *lp = m->prev; - if(m->prev) - m->prev->next = m->next; - else *fp = m->next; -} diff --git a/external/unbound/services/mesh.h b/external/unbound/services/mesh.h deleted file mode 100644 index 1c7794532..000000000 --- a/external/unbound/services/mesh.h +++ /dev/null @@ -1,607 +0,0 @@ -/* - * services/mesh.h - deal with mesh of query states and handle events for that. - * - * 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 - * - * This file contains functions to assist in dealing with a mesh of - * query states. This mesh is supposed to be thread-specific. - * It consists of query states (per qname, qtype, qclass) and connections - * between query states and the super and subquery states, and replies to - * send back to clients. - */ - -#ifndef SERVICES_MESH_H -#define SERVICES_MESH_H - -#include "util/rbtree.h" -#include "util/netevent.h" -#include "util/data/msgparse.h" -#include "util/module.h" -#include "services/modstack.h" -struct sldns_buffer; -struct mesh_state; -struct mesh_reply; -struct mesh_cb; -struct query_info; -struct reply_info; -struct outbound_entry; -struct timehist; -struct respip_client_info; - -/** - * Maximum number of mesh state activations. Any more is likely an - * infinite loop in the module. It is then terminated. - */ -#define MESH_MAX_ACTIVATION 3000 - -/** - * Max number of references-to-references-to-references.. search size. - * Any more is treated like 'too large', and the creation of a new - * dependency is failed (so that no loops can be created). - */ -#define MESH_MAX_SUBSUB 1024 - -/** - * Mesh of query states - */ -struct mesh_area { - /** active module stack */ - struct module_stack mods; - /** environment for new states */ - struct module_env* env; - - /** set of runnable queries (mesh_state.run_node) */ - rbtree_type run; - /** rbtree of all current queries (mesh_state.node)*/ - rbtree_type all; - - /** count of the total number of mesh_reply entries */ - size_t num_reply_addrs; - /** count of the number of mesh_states that have mesh_replies - * Because a state can send results to multiple reply addresses, - * this number must be equal or lower than num_reply_addrs. */ - size_t num_reply_states; - /** number of mesh_states that have no mesh_replies, and also - * an empty set of super-states, thus are 'toplevel' or detached - * internal opportunistic queries */ - size_t num_detached_states; - /** number of reply states in the forever list */ - size_t num_forever_states; - - /** max total number of reply states to have */ - size_t max_reply_states; - /** max forever number of reply states to have */ - size_t max_forever_states; - - /** stats, cumulative number of reply states jostled out */ - size_t stats_jostled; - /** stats, cumulative number of incoming client msgs dropped */ - size_t stats_dropped; - /** number of replies sent */ - size_t replies_sent; - /** sum of waiting times for the replies */ - struct timeval replies_sum_wait; - /** histogram of time values */ - struct timehist* histogram; - /** (extended stats) secure replies */ - size_t ans_secure; - /** (extended stats) bogus replies */ - size_t ans_bogus; - /** (extended stats) rcodes in replies */ - size_t ans_rcode[16]; - /** (extended stats) rcode nodata in replies */ - size_t ans_nodata; - - /** backup of query if other operations recurse and need the - * network buffers */ - struct sldns_buffer* qbuf_bak; - - /** double linked list of the run-to-completion query states. - * These are query states with a reply */ - struct mesh_state* forever_first; - /** last entry in run forever list */ - struct mesh_state* forever_last; - - /** double linked list of the query states that can be jostled out - * by new queries if too old. These are query states with a reply */ - struct mesh_state* jostle_first; - /** last entry in jostle list - this is the entry that is newest */ - struct mesh_state* jostle_last; - /** timeout for jostling. if age is lower, it does not get jostled. */ - struct timeval jostle_max; -}; - -/** - * A mesh query state - * Unique per qname, qtype, qclass (from the qstate). - * And RD / CD flag; in case a client turns it off. - * And priming queries are different from ordinary queries (because of hints). - * - * The entire structure is allocated in a region, this region is the qstate - * region. All parts (rbtree nodes etc) are also allocated in the region. - */ -struct mesh_state { - /** node in mesh_area all tree, key is this struct. Must be first. */ - rbnode_type node; - /** node in mesh_area runnable tree, key is this struct */ - rbnode_type run_node; - /** the query state. Note that the qinfo and query_flags - * may not change. */ - struct module_qstate s; - /** the list of replies to clients for the results */ - struct mesh_reply* reply_list; - /** the list of callbacks for the results */ - struct mesh_cb* cb_list; - /** set of superstates (that want this state's result) - * contains struct mesh_state_ref* */ - rbtree_type super_set; - /** set of substates (that this state needs to continue) - * contains struct mesh_state_ref* */ - rbtree_type sub_set; - /** number of activations for the mesh state */ - size_t num_activated; - - /** previous in linked list for reply states */ - struct mesh_state* prev; - /** next in linked list for reply states */ - struct mesh_state* next; - /** if this state is in the forever list, jostle list, or neither */ - enum mesh_list_select { mesh_no_list, mesh_forever_list, - mesh_jostle_list } list_select; - /** pointer to this state for uniqueness or NULL */ - struct mesh_state* unique; - - /** true if replies have been sent out (at end for alignment) */ - uint8_t replies_sent; -}; - -/** - * Rbtree reference to a mesh_state. - * Used in super_set and sub_set. - */ -struct mesh_state_ref { - /** node in rbtree for set, key is this structure */ - rbnode_type node; - /** the mesh state */ - struct mesh_state* s; -}; - -/** - * Reply to a client - */ -struct mesh_reply { - /** next in reply list */ - struct mesh_reply* next; - /** the query reply destination, packet buffer and where to send. */ - struct comm_reply query_reply; - /** edns data from query */ - struct edns_data edns; - /** the time when request was entered */ - struct timeval start_time; - /** id of query, in network byteorder. */ - uint16_t qid; - /** flags of query, for reply flags */ - uint16_t qflags; - /** qname from this query. len same as mesh qinfo. */ - uint8_t* qname; - /** same as that in query_info. */ - struct local_rrset* local_alias; -}; - -/** - * Mesh result callback func. - * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus); - */ -typedef void (*mesh_cb_func_type)(void*, int, struct sldns_buffer*, enum sec_status, - char*); - -/** - * Callback to result routine - */ -struct mesh_cb { - /** next in list */ - struct mesh_cb* next; - /** edns data from query */ - struct edns_data edns; - /** id of query, in network byteorder. */ - uint16_t qid; - /** flags of query, for reply flags */ - uint16_t qflags; - /** buffer for reply */ - struct sldns_buffer* buf; - - /** callback routine for results. if rcode != 0 buf has message. - * called as cb(cb_arg, rcode, buf, sec_state); - */ - mesh_cb_func_type cb; - /** user arg for callback */ - void* cb_arg; -}; - -/* ------------------- Functions for worker -------------------- */ - -/** - * Allocate mesh, to empty. - * @param stack: module stack to activate, copied (as readonly reference). - * @param env: environment for new queries. - * @return mesh: the new mesh or NULL on error. - */ -struct mesh_area* mesh_create(struct module_stack* stack, - struct module_env* env); - -/** - * Delete mesh, and all query states and replies in it. - * @param mesh: the mesh to delete. - */ -void mesh_delete(struct mesh_area* mesh); - -/** - * New query incoming from clients. Create new query state if needed, and - * add mesh_reply to it. Returns error to client on malloc failures. - * Will run the mesh area queries to process if a new query state is created. - * - * @param mesh: the mesh. - * @param qinfo: query from client. - * @param cinfo: additional information associated with the query client. - * 'cinfo' itself is ephemeral but data pointed to by its members - * can be assumed to be valid and unchanged until the query processing is - * completed. - * @param qflags: flags from client query. - * @param edns: edns data from client query. - * @param rep: where to reply to. - * @param qid: query id to reply with. - */ -void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - struct respip_client_info* cinfo, uint16_t qflags, - struct edns_data* edns, struct comm_reply* rep, uint16_t qid); - -/** - * New query with callback. Create new query state if needed, and - * add mesh_cb to it. - * Will run the mesh area queries to process if a new query state is created. - * - * @param mesh: the mesh. - * @param qinfo: query from client. - * @param qflags: flags from client query. - * @param edns: edns data from client query. - * @param buf: buffer for reply contents. - * @param qid: query id to reply with. - * @param cb: callback function. - * @param cb_arg: callback user arg. - * @return 0 on error. - */ -int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg); - -/** - * New prefetch message. Create new query state if needed. - * Will run the mesh area queries to process if a new query state is created. - * - * @param mesh: the mesh. - * @param qinfo: query from client. - * @param qflags: flags from client query. - * @param leeway: TTL leeway what to expire earlier for this update. - */ -void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway); - -/** - * Handle new event from the wire. A serviced query has returned. - * The query state will be made runnable, and the mesh_area will process - * query states until processing is complete. - * - * @param mesh: the query mesh. - * @param e: outbound entry, with query state to run and reply pointer. - * @param reply: the comm point reply info. - * @param what: NETEVENT_* error code (if not 0, what is wrong, TIMEOUT). - */ -void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, - struct comm_reply* reply, int what); - -/* ------------------- Functions for module environment --------------- */ - -/** - * Detach-subqueries. - * Remove all sub-query references from this query state. - * Keeps super-references of those sub-queries correct. - * Updates stat items in mesh_area structure. - * @param qstate: used to find mesh state. - */ -void mesh_detach_subs(struct module_qstate* qstate); - -/** - * Attach subquery. - * Creates it if it does not exist already. - * Keeps sub and super references correct. - * Performs a cycle detection - for double check - and fails if there is one. - * Also fails if the sub-sub-references become too large. - * 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. - * - * @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. - * @return: false on error, true if success (and init may be needed). - */ -int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq); - -/** - * Query state is done, send messages to reply entries. - * Encode messages using reply entry values and the querystate (with original - * qinfo), using given reply_info. - * Pass errcode != 0 if an error reply is needed. - * If no reply entries, nothing is done. - * Must be called before a module can module_finished or return module_error. - * The module must handle the super query states itself as well. - * - * @param mstate: mesh state that is done. return_rcode and return_msg - * are used for replies. - * return_rcode: if not 0 (NOERROR) an error is sent back (and - * return_msg is ignored). - * return_msg: reply to encode and send back to clients. - */ -void mesh_query_done(struct mesh_state* mstate); - -/** - * Call inform_super for the super query states that are interested in the - * results from this query state. These can then be changed for error - * or results. - * Called when a module is module_finished or returns module_error. - * The super query states become runnable with event module_event_pass, - * it calls the current module for the super with the inform_super event. - * - * @param mesh: mesh area to add newly runnable modules to. - * @param mstate: the state that has results, used to find mesh state. - */ -void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate); - -/** - * Delete mesh state, cleanup and also rbtrees and so on. - * Will detach from all super/subnodes. - * @param qstate: to remove. - */ -void mesh_state_delete(struct module_qstate* qstate); - -/* ------------------- Functions for mesh -------------------- */ - -/** - * Create and initialize a new mesh state and its query state - * Does not put the mesh state into rbtrees and so on. - * @param env: module environment to set. - * @param qinfo: query info that the mesh is for. - * @param cinfo: control info for the query client (can be NULL). - * @param qflags: flags for query (RD / CD flag). - * @param prime: if true, it is a priming query, set is_priming on mesh state. - * @param valrec: if true, it is a validation recursion query, and sets - * is_valrec on the mesh state. - * @return: new mesh state or NULL on allocation error. - */ -struct mesh_state* mesh_state_create(struct module_env* env, - struct query_info* qinfo, struct respip_client_info* cinfo, - uint16_t qflags, int prime, int valrec); - -/** - * Check if the mesh state is unique. - * A unique mesh state uses it's unique member to point to itself, else NULL. - * @param mstate: mesh state to check. - * @return true if the mesh state is unique, false otherwise. - */ -int mesh_state_is_unique(struct mesh_state* mstate); - -/** - * Make a mesh state unique. - * A unique mesh state uses it's unique member to point to itself. - * @param mstate: mesh state to check. - */ -void mesh_state_make_unique(struct mesh_state* mstate); - -/** - * Cleanup a mesh state and its query state. Does not do rbtree or - * reference cleanup. - * @param mstate: mesh state to cleanup. Its pointer may no longer be used - * afterwards. Cleanup rbtrees before calling this function. - */ -void mesh_state_cleanup(struct mesh_state* mstate); - -/** - * Delete all mesh states from the mesh. - * @param mesh: the mesh area to clear - */ -void mesh_delete_all(struct mesh_area* mesh); - -/** - * Find a mesh state in the mesh area. Pass relevant flags. - * - * @param mesh: the mesh area to look in. - * @param cinfo: if non-NULL client specific info that may affect IP-based - * actions that apply to the query result. - * @param qinfo: what query - * @param qflags: if RD / CD bit is set or not. - * @param prime: if it is a priming query. - * @param valrec: if it is a validation-recursion query. - * @return: mesh state or NULL if not found. - */ -struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct respip_client_info* cinfo, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec); - -/** - * Setup attachment super/sub relation between super and sub mesh state. - * The relation must not be present when calling the function. - * Does not update stat items in mesh_area. - * @param super: super state. - * @param sub: sub state. - * @return: 0 on alloc error. - */ -int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub); - -/** - * Create new reply structure and attach it to a mesh state. - * Does not update stat items in mesh area. - * @param s: the mesh state. - * @param edns: edns data for reply (bufsize). - * @param rep: comm point reply info. - * @param qid: ID of reply. - * @param qflags: original query flags. - * @param qinfo: original query info. - * @return: 0 on alloc error. - */ -int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, - struct comm_reply* rep, uint16_t qid, uint16_t qflags, - const struct query_info* qinfo); - -/** - * Create new callback structure and attach it to a mesh state. - * Does not update stat items in mesh area. - * @param s: the mesh state. - * @param edns: edns data for reply (bufsize). - * @param buf: buffer for reply - * @param cb: callback to call with results. - * @param cb_arg: callback user arg. - * @param qid: ID of reply. - * @param qflags: original query flags. - * @return: 0 on alloc error. - */ -int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, - struct sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, - uint16_t qid, uint16_t qflags); - -/** - * Run the mesh. Run all runnable mesh states. Which can create new - * runnable mesh states. Until completion. Automatically called by - * mesh_report_reply and mesh_new_client as needed. - * @param mesh: mesh area. - * @param mstate: first mesh state to run. - * @param ev: event the mstate. Others get event_pass. - * @param e: if a reply, its outbound entry. - */ -void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate, - enum module_ev ev, struct outbound_entry* e); - -/** - * Print some stats about the mesh to the log. - * @param mesh: the mesh to print it for. - * @param str: descriptive string to go with it. - */ -void mesh_stats(struct mesh_area* mesh, const char* str); - -/** - * Clear the stats that the mesh keeps (number of queries serviced) - * @param mesh: the mesh - */ -void mesh_stats_clear(struct mesh_area* mesh); - -/** - * Print all the states in the mesh to the log. - * @param mesh: the mesh to print all states of. - */ -void mesh_log_list(struct mesh_area* mesh); - -/** - * Calculate memory size in use by mesh and all queries inside it. - * @param mesh: the mesh to examine. - * @return size in bytes. - */ -size_t mesh_get_mem(struct mesh_area* mesh); - -/** - * Find cycle; see if the given mesh is in the targets sub, or sub-sub, ... - * trees. - * If the sub-sub structure is too large, it returns 'a cycle'=2. - * @param qstate: given mesh querystate. - * @param qinfo: query info for dependency. - * @param flags: query flags of dependency. - * @param prime: if dependency is a priming query or not. - * @param valrec: if it is a validation recursion query (lookup of key, DS). - * @return true if the name,type,class exists and the given qstate mesh exists - * as a dependency of that name. Thus if qstate becomes dependent on - * name,type,class then a cycle is created, this is return value 1. - * Too large to search is value 2 (also true). - */ -int mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t flags, int prime, int valrec); - -/** compare two mesh_states */ -int mesh_state_compare(const void* ap, const void* bp); - -/** compare two mesh references */ -int mesh_state_ref_compare(const void* ap, const void* bp); - -/** - * Make space for another recursion state for a reply in the mesh - * @param mesh: mesh area - * @param qbuf: query buffer to save if recursion is invoked to make space. - * This buffer is necessary, because the following sequence in calls - * can result in an overwrite of the incoming query: - * delete_other_mesh_query - iter_clean - serviced_delete - waiting - * udp query is sent - on error callback - callback sends SERVFAIL reply - * over the same network channel, and shared UDP buffer is overwritten. - * You can pass NULL if there is no buffer that must be backed up. - * @return false if no space is available. - */ -int mesh_make_new_space(struct mesh_area* mesh, struct sldns_buffer* qbuf); - -/** - * Insert mesh state into a double linked list. Inserted at end. - * @param m: mesh state. - * @param fp: pointer to the first-elem-pointer of the list. - * @param lp: pointer to the last-elem-pointer of the list. - */ -void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp, - struct mesh_state** lp); - -/** - * Remove mesh state from a double linked list. Remove from any position. - * @param m: mesh state. - * @param fp: pointer to the first-elem-pointer of the list. - * @param lp: pointer to the last-elem-pointer of the list. - */ -void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp, - struct mesh_state** lp); - -#endif /* SERVICES_MESH_H */ diff --git a/external/unbound/services/modstack.c b/external/unbound/services/modstack.c deleted file mode 100644 index 9bebd3a56..000000000 --- a/external/unbound/services/modstack.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * services/modstack.c - stack of modules - * - * 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 - * - * This file contains functions to help maintain a stack of modules. - */ -#include "config.h" -#include <ctype.h> -#include "services/modstack.h" -#include "util/module.h" -#include "util/fptr_wlist.h" -#include "dns64/dns64.h" -#include "iterator/iterator.h" -#include "validator/validator.h" -#include "respip/respip.h" - -#ifdef WITH_PYTHONMODULE -#include "pythonmod/pythonmod.h" -#endif -#ifdef USE_CACHEDB -#include "cachedb/cachedb.h" -#endif -#ifdef CLIENT_SUBNET -#include "edns-subnet/subnetmod.h" -#endif - -/** count number of modules (words) in the string */ -static int -count_modules(const char* s) -{ - int num = 0; - if(!s) - return 0; - while(*s) { - /* skip whitespace */ - while(*s && isspace((unsigned char)*s)) - s++; - if(*s && !isspace((unsigned char)*s)) { - /* skip identifier */ - num++; - while(*s && !isspace((unsigned char)*s)) - s++; - } - } - return num; -} - -void -modstack_init(struct module_stack* stack) -{ - stack->num = 0; - stack->mod = NULL; -} - -int -modstack_config(struct module_stack* stack, const char* module_conf) -{ - int i; - verbose(VERB_QUERY, "module config: \"%s\"", module_conf); - stack->num = count_modules(module_conf); - if(stack->num == 0) { - log_err("error: no modules specified"); - return 0; - } - if(stack->num > MAX_MODULE) { - log_err("error: too many modules (%d max %d)", - stack->num, MAX_MODULE); - return 0; - } - stack->mod = (struct module_func_block**)calloc((size_t) - stack->num, sizeof(struct module_func_block*)); - if(!stack->mod) { - log_err("out of memory"); - return 0; - } - for(i=0; i<stack->num; i++) { - stack->mod[i] = module_factory(&module_conf); - if(!stack->mod[i]) { - log_err("Unknown value for next module: '%s'", - module_conf); - return 0; - } - } - return 1; -} - -/** The list of module names */ -const char** -module_list_avail(void) -{ - /* these are the modules available */ - static const char* names[] = { - "dns64", -#ifdef WITH_PYTHONMODULE - "python", -#endif -#ifdef USE_CACHEDB - "cachedb", -#endif -#ifdef CLIENT_SUBNET - "subnetcache", -#endif - "respip", - "validator", - "iterator", - NULL}; - return names; -} - -/** func block get function type */ -typedef struct module_func_block* (*fbgetfunctype)(void); - -/** The list of module func blocks */ -static fbgetfunctype* -module_funcs_avail(void) -{ - static struct module_func_block* (*fb[])(void) = { - &dns64_get_funcblock, -#ifdef WITH_PYTHONMODULE - &pythonmod_get_funcblock, -#endif -#ifdef USE_CACHEDB - &cachedb_get_funcblock, -#endif -#ifdef CLIENT_SUBNET - &subnetmod_get_funcblock, -#endif - &respip_get_funcblock, - &val_get_funcblock, - &iter_get_funcblock, - NULL}; - return fb; -} - -struct -module_func_block* module_factory(const char** str) -{ - int i = 0; - const char* s = *str; - const char** names = module_list_avail(); - fbgetfunctype* fb = module_funcs_avail(); - while(*s && isspace((unsigned char)*s)) - s++; - while(names[i]) { - if(strncmp(names[i], s, strlen(names[i])) == 0) { - s += strlen(names[i]); - *str = s; - return (*fb[i])(); - } - i++; - } - return NULL; -} - -int -modstack_setup(struct module_stack* stack, const char* module_conf, - struct module_env* env) -{ - int i; - if(stack->num != 0) - modstack_desetup(stack, env); - /* fixed setup of the modules */ - if(!modstack_config(stack, module_conf)) { - return 0; - } - env->need_to_validate = 0; /* set by module init below */ - for(i=0; i<stack->num; i++) { - verbose(VERB_OPS, "init module %d: %s", - i, stack->mod[i]->name); - fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init)); - if(!(*stack->mod[i]->init)(env, i)) { - log_err("module init for module %s failed", - stack->mod[i]->name); - return 0; - } - } - return 1; -} - -void -modstack_desetup(struct module_stack* stack, struct module_env* env) -{ - int i; - for(i=0; i<stack->num; i++) { - fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit)); - (*stack->mod[i]->deinit)(env, i); - } - stack->num = 0; - free(stack->mod); - stack->mod = NULL; -} - -int -modstack_find(struct module_stack* stack, const char* name) -{ - int i; - for(i=0; i<stack->num; i++) { - if(strcmp(stack->mod[i]->name, name) == 0) - return i; - } - return -1; -} diff --git a/external/unbound/services/modstack.h b/external/unbound/services/modstack.h deleted file mode 100644 index cb8613299..000000000 --- a/external/unbound/services/modstack.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * services/modstack.h - stack of modules - * - * 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 - * - * This file contains functions to help maintain a stack of modules. - */ - -#ifndef SERVICES_MODSTACK_H -#define SERVICES_MODSTACK_H -struct module_func_block; -struct module_env; - -/** - * Stack of modules. - */ -struct module_stack { - /** the number of modules */ - int num; - /** the module callbacks, array of num_modules length (ref only) */ - struct module_func_block** mod; -}; - -/** - * Init a stack of modules - * @param stack: initialised as empty. - */ -void modstack_init(struct module_stack* stack); - -/** - * Read config file module settings and set up the modfunc block - * @param stack: the stack of modules (empty before call). - * @param module_conf: string what modules to insert. - * @return false on error - */ -int modstack_config(struct module_stack* stack, const char* module_conf); - -/** - * Get funcblock for module name - * @param str: string with module name. Advanced to next value on success. - * The string is assumed whitespace separated list of module names. - * @return funcblock or NULL on error. - */ -struct module_func_block* module_factory(const char** str); - -/** - * Get list of modules available. - * @return list of modules available. Static strings, ends with NULL. - */ -const char** module_list_avail(void); - -/** - * Setup modules. Assigns ids and calls module_init. - * @param stack: if not empty beforehand, it will be desetup()ed. - * It is then modstack_configged(). - * @param module_conf: string what modules to insert. - * @param env: module environment which is inited by the modules. - * environment should have a superalloc, cfg, - * env.need_to_validate is set by the modules. - * @return on false a module init failed. - */ -int modstack_setup(struct module_stack* stack, const char* module_conf, - struct module_env* env); - -/** - * Desetup the modules, deinit, delete. - * @param stack: made empty. - * @param env: module env for module deinit() calls. - */ -void modstack_desetup(struct module_stack* stack, struct module_env* env); - -/** - * Find index of module by name. - * @param stack: to look in - * @param name: the name to look for - * @return -1 on failure, otherwise index number. - */ -int modstack_find(struct module_stack* stack, const char* name); - -#endif /* SERVICES_MODSTACK_H */ diff --git a/external/unbound/services/outbound_list.c b/external/unbound/services/outbound_list.c deleted file mode 100644 index ad73380bc..000000000 --- a/external/unbound/services/outbound_list.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * services/outbound_list.c - keep list of outbound serviced queries. - * - * 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 - * - * This file contains functions to help a module keep track of the - * queries it has outstanding to authoritative servers. - */ -#include "config.h" -#include <sys/time.h> -#include "services/outbound_list.h" -#include "services/outside_network.h" - -void -outbound_list_init(struct outbound_list* list) -{ - list->first = NULL; -} - -void -outbound_list_clear(struct outbound_list* list) -{ - struct outbound_entry *p, *np; - p = list->first; - while(p) { - np = p->next; - outnet_serviced_query_stop(p->qsent, p); - /* in region, no free needed */ - p = np; - } - outbound_list_init(list); -} - -void -outbound_list_insert(struct outbound_list* list, struct outbound_entry* e) -{ - if(list->first) - list->first->prev = e; - e->next = list->first; - e->prev = NULL; - list->first = e; -} - -void -outbound_list_remove(struct outbound_list* list, struct outbound_entry* e) -{ - if(!e) - return; - outnet_serviced_query_stop(e->qsent, e); - if(e->next) - e->next->prev = e->prev; - if(e->prev) - e->prev->next = e->next; - else list->first = e->next; - /* in region, no free needed */ -} diff --git a/external/unbound/services/outbound_list.h b/external/unbound/services/outbound_list.h deleted file mode 100644 index ad59e42d1..000000000 --- a/external/unbound/services/outbound_list.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * services/outbound_list.h - keep list of outbound serviced queries. - * - * 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 - * - * This file contains functions to help a module keep track of the - * queries it has outstanding to authoritative servers. - */ -#ifndef SERVICES_OUTBOUND_LIST_H -#define SERVICES_OUTBOUND_LIST_H -struct outbound_entry; -struct serviced_query; -struct module_qstate; - -/** - * The outbound list. This structure is part of the module specific query - * state. - */ -struct outbound_list { - /** The linked list of outbound query entries. */ - struct outbound_entry* first; -}; - -/** - * Outbound list entry. A serviced query sent by a module processing the - * query from the qstate. Double linked list to aid removal. - */ -struct outbound_entry { - /** next in list */ - struct outbound_entry* next; - /** prev in list */ - struct outbound_entry* prev; - /** The query that was sent out */ - struct serviced_query* qsent; - /** the module query state that sent it */ - struct module_qstate* qstate; -}; - -/** - * Init the user allocated outbound list structure - * @param list: the list structure. - */ -void outbound_list_init(struct outbound_list* list); - -/** - * Clear the user owner outbound list structure. - * Deletes serviced queries. - * @param list: the list structure. It is cleared, but the list struct itself - * is callers responsability to delete. - */ -void outbound_list_clear(struct outbound_list* list); - -/** - * Insert new entry into the list. Caller must allocate the entry with malloc. - * qstate and qsent are set by caller. - * @param list: the list to add to. - * @param e: entry to add, it is only half initialised at call start, fully - * initialised at call end. - */ -void outbound_list_insert(struct outbound_list* list, - struct outbound_entry* e); - -/** - * Remove an entry from the list, and deletes it. - * Deletes serviced query in the entry. - * @param list: the list to remove from. - * @param e: the entry to remove. - */ -void outbound_list_remove(struct outbound_list* list, - struct outbound_entry* e); - -#endif /* SERVICES_OUTBOUND_LIST_H */ diff --git a/external/unbound/services/outside_network.c b/external/unbound/services/outside_network.c deleted file mode 100644 index 426e87b3e..000000000 --- a/external/unbound/services/outside_network.c +++ /dev/null @@ -1,2183 +0,0 @@ -/* - * services/outside_network.c - implement sending of queries and wait answer. - * - * 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 - * - * This file has functions to send queries to authoritative servers and - * wait for the pending answer events. - */ -#include "config.h" -#include <ctype.h> -#ifdef HAVE_SYS_TYPES_H -# include <sys/types.h> -#endif -#include <sys/time.h> -#include "services/outside_network.h" -#include "services/listen_dnsport.h" -#include "services/cache/infra.h" -#include "util/data/msgparse.h" -#include "util/data/msgreply.h" -#include "util/data/msgencode.h" -#include "util/data/dname.h" -#include "util/netevent.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/random.h" -#include "util/fptr_wlist.h" -#include "sldns/sbuffer.h" -#include "dnstap/dnstap.h" -#ifdef HAVE_OPENSSL_SSL_H -#include <openssl/ssl.h> -#endif - -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#include <fcntl.h> - -/** number of times to retry making a random ID that is unique. */ -#define MAX_ID_RETRY 1000 -/** number of times to retry finding interface, port that can be opened. */ -#define MAX_PORT_RETRY 10000 -/** number of retries on outgoing UDP queries */ -#define OUTBOUND_UDP_RETRY 1 - -/** initiate TCP transaction for serviced query */ -static void serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff); -/** with a fd available, randomize and send UDP */ -static int randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, - int timeout); - -/** remove waiting tcp from the outnet waiting list */ -static void waiting_list_remove(struct outside_network* outnet, - struct waiting_tcp* w); - -int -pending_cmp(const void* key1, const void* key2) -{ - struct pending *p1 = (struct pending*)key1; - struct pending *p2 = (struct pending*)key2; - if(p1->id < p2->id) - return -1; - if(p1->id > p2->id) - return 1; - log_assert(p1->id == p2->id); - return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen); -} - -int -serviced_cmp(const void* key1, const void* key2) -{ - struct serviced_query* q1 = (struct serviced_query*)key1; - struct serviced_query* q2 = (struct serviced_query*)key2; - int r; - if(q1->qbuflen < q2->qbuflen) - return -1; - if(q1->qbuflen > q2->qbuflen) - return 1; - log_assert(q1->qbuflen == q2->qbuflen); - log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */); - /* alternate casing of qname is still the same query */ - if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0) - return r; - if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0) - return r; - if(q1->dnssec != q2->dnssec) { - if(q1->dnssec < q2->dnssec) - return -1; - return 1; - } - if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0) - return r; - if((r = edns_opt_list_compare(q1->opt_list, q2->opt_list)) != 0) - return r; - return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen); -} - -/** delete waiting_tcp entry. Does not unlink from waiting list. - * @param w: to delete. - */ -static void -waiting_tcp_delete(struct waiting_tcp* w) -{ - if(!w) return; - if(w->timer) - comm_timer_delete(w->timer); - free(w); -} - -/** - * Pick random outgoing-interface of that family, and bind it. - * port set to 0 so OS picks a port number for us. - * if it is the ANY address, do not bind. - * @param w: tcp structure with destination address. - * @param s: socket fd. - * @return false on error, socket closed. - */ -static int -pick_outgoing_tcp(struct waiting_tcp* w, int s) -{ - struct port_if* pi = NULL; - int num; -#ifdef INET6 - if(addr_is_ip6(&w->addr, w->addrlen)) - num = w->outnet->num_ip6; - else -#endif - num = w->outnet->num_ip4; - if(num == 0) { - log_err("no TCP outgoing interfaces of family"); - log_addr(VERB_OPS, "for addr", &w->addr, w->addrlen); -#ifndef USE_WINSOCK - close(s); -#else - closesocket(s); -#endif - return 0; - } -#ifdef INET6 - if(addr_is_ip6(&w->addr, w->addrlen)) - pi = &w->outnet->ip6_ifs[ub_random_max(w->outnet->rnd, num)]; - else -#endif - pi = &w->outnet->ip4_ifs[ub_random_max(w->outnet->rnd, num)]; - log_assert(pi); - if(addr_is_any(&pi->addr, pi->addrlen)) { - /* binding to the ANY interface is for listening sockets */ - return 1; - } - /* set port to 0 */ - if(addr_is_ip6(&pi->addr, pi->addrlen)) - ((struct sockaddr_in6*)&pi->addr)->sin6_port = 0; - else ((struct sockaddr_in*)&pi->addr)->sin_port = 0; - if(bind(s, (struct sockaddr*)&pi->addr, pi->addrlen) != 0) { -#ifndef USE_WINSOCK - log_err("outgoing tcp: bind: %s", strerror(errno)); - close(s); -#else - log_err("outgoing tcp: bind: %s", - wsa_strerror(WSAGetLastError())); - closesocket(s); -#endif - return 0; - } - log_addr(VERB_ALGO, "tcp bound to src", &pi->addr, pi->addrlen); - return 1; -} - -/** use next free buffer to service a tcp query */ -static int -outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) -{ - struct pending_tcp* pend = w->outnet->tcp_free; - int s; - log_assert(pend); - log_assert(pkt); - log_assert(w->addrlen > 0); - /* open socket */ -#ifdef INET6 - if(addr_is_ip6(&w->addr, w->addrlen)) - s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); - else -#endif - s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if(s == -1) { -#ifndef USE_WINSOCK - log_err_addr("outgoing tcp: socket", strerror(errno), - &w->addr, w->addrlen); -#else - log_err_addr("outgoing tcp: socket", - wsa_strerror(WSAGetLastError()), &w->addr, w->addrlen); -#endif - return 0; - } - - if (w->outnet->tcp_mss > 0) { -#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) - if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, - (void*)&w->outnet->tcp_mss, - (socklen_t)sizeof(w->outnet->tcp_mss)) < 0) { - verbose(VERB_ALGO, "outgoing tcp:" - " setsockopt(.. SO_REUSEADDR ..) failed"); - } -#else - verbose(VERB_ALGO, "outgoing tcp:" - " setsockopt(TCP_MAXSEG) unsupported"); -#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */ - } - - if(!pick_outgoing_tcp(w, s)) - return 0; - - fd_set_nonblock(s); -#ifdef USE_OSX_MSG_FASTOPEN - /* API for fast open is different here. We use a connectx() function and - then writes can happen as normal even using SSL.*/ - /* connectx requires that the len be set in the sockaddr struct*/ - struct sockaddr_in *addr_in = (struct sockaddr_in *)&w->addr; - addr_in->sin_len = w->addrlen; - sa_endpoints_t endpoints; - endpoints.sae_srcif = 0; - endpoints.sae_srcaddr = NULL; - endpoints.sae_srcaddrlen = 0; - endpoints.sae_dstaddr = (struct sockaddr *)&w->addr; - endpoints.sae_dstaddrlen = w->addrlen; - if (connectx(s, &endpoints, SAE_ASSOCID_ANY, - CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE, - NULL, 0, NULL, NULL) == -1) { -#else /* USE_OSX_MSG_FASTOPEN*/ -#ifdef USE_MSG_FASTOPEN - pend->c->tcp_do_fastopen = 1; - /* Only do TFO for TCP in which case no connect() is required here. - Don't combine client TFO with SSL, since OpenSSL can't - currently support doing a handshake on fd that already isn't connected*/ - if (w->outnet->sslctx && w->ssl_upstream) { - if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { -#else /* USE_MSG_FASTOPEN*/ - if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { -#endif /* USE_MSG_FASTOPEN*/ -#endif /* USE_OSX_MSG_FASTOPEN*/ -#ifndef USE_WINSOCK -#ifdef EINPROGRESS - if(errno != EINPROGRESS) { -#else - if(1) { -#endif - if(tcp_connect_errno_needs_log( - (struct sockaddr*)&w->addr, w->addrlen)) - log_err_addr("outgoing tcp: connect", - strerror(errno), &w->addr, w->addrlen); - close(s); -#else /* USE_WINSOCK */ - if(WSAGetLastError() != WSAEINPROGRESS && - WSAGetLastError() != WSAEWOULDBLOCK) { - closesocket(s); -#endif - return 0; - } - } -#ifdef USE_MSG_FASTOPEN - } -#endif /* USE_MSG_FASTOPEN */ - if(w->outnet->sslctx && w->ssl_upstream) { - pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s); - if(!pend->c->ssl) { - pend->c->fd = s; - comm_point_close(pend->c); - return 0; - } -#ifdef USE_WINSOCK - comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl); -#endif - pend->c->ssl_shake_state = comm_ssl_shake_write; - } - w->pkt = NULL; - w->next_waiting = (void*)pend; - pend->id = LDNS_ID_WIRE(pkt); - w->outnet->num_tcp_outgoing++; - w->outnet->tcp_free = pend->next_free; - pend->next_free = NULL; - pend->query = w; - pend->c->repinfo.addrlen = w->addrlen; - memcpy(&pend->c->repinfo.addr, &w->addr, w->addrlen); - sldns_buffer_clear(pend->c->buffer); - sldns_buffer_write(pend->c->buffer, pkt, pkt_len); - sldns_buffer_flip(pend->c->buffer); - pend->c->tcp_is_reading = 0; - pend->c->tcp_byte_count = 0; - comm_point_start_listening(pend->c, s, -1); - return 1; -} - -/** see if buffers can be used to service TCP queries */ -static void -use_free_buffer(struct outside_network* outnet) -{ - struct waiting_tcp* w; - while(outnet->tcp_free && outnet->tcp_wait_first - && !outnet->want_to_quit) { - w = outnet->tcp_wait_first; - outnet->tcp_wait_first = w->next_waiting; - if(outnet->tcp_wait_last == w) - outnet->tcp_wait_last = NULL; - if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) { - comm_point_callback_type* cb = w->cb; - void* cb_arg = w->cb_arg; - waiting_tcp_delete(w); - fptr_ok(fptr_whitelist_pending_tcp(cb)); - (void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL); - } - } -} - -/** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */ -static void -decomission_pending_tcp(struct outside_network* outnet, - struct pending_tcp* pend) -{ - if(pend->c->ssl) { -#ifdef HAVE_SSL - SSL_shutdown(pend->c->ssl); - SSL_free(pend->c->ssl); - pend->c->ssl = NULL; -#endif - } - comm_point_close(pend->c); - pend->next_free = outnet->tcp_free; - outnet->tcp_free = pend; - waiting_tcp_delete(pend->query); - pend->query = NULL; - use_free_buffer(outnet); -} - -int -outnet_tcp_cb(struct comm_point* c, void* arg, int error, - struct comm_reply *reply_info) -{ - struct pending_tcp* pend = (struct pending_tcp*)arg; - struct outside_network* outnet = pend->query->outnet; - verbose(VERB_ALGO, "outnettcp cb"); - if(error != NETEVENT_NOERROR) { - verbose(VERB_QUERY, "outnettcp got tcp error %d", error); - /* pass error below and exit */ - } else { - /* check ID */ - if(sldns_buffer_limit(c->buffer) < sizeof(uint16_t) || - LDNS_ID_WIRE(sldns_buffer_begin(c->buffer))!=pend->id) { - log_addr(VERB_QUERY, - "outnettcp: bad ID in reply, from:", - &pend->query->addr, pend->query->addrlen); - error = NETEVENT_CLOSED; - } - } - fptr_ok(fptr_whitelist_pending_tcp(pend->query->cb)); - (void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info); - decomission_pending_tcp(outnet, pend); - return 0; -} - -/** lower use count on pc, see if it can be closed */ -static void -portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc) -{ - struct port_if* pif; - pc->num_outstanding--; - if(pc->num_outstanding > 0) { - return; - } - /* close it and replace in unused list */ - verbose(VERB_ALGO, "close of port %d", pc->number); - comm_point_close(pc->cp); - pif = pc->pif; - log_assert(pif->inuse > 0); - pif->avail_ports[pif->avail_total - pif->inuse] = pc->number; - pif->inuse--; - pif->out[pc->index] = pif->out[pif->inuse]; - pif->out[pc->index]->index = pc->index; - pc->next = outnet->unused_fds; - outnet->unused_fds = pc; -} - -/** try to send waiting UDP queries */ -static void -outnet_send_wait_udp(struct outside_network* outnet) -{ - struct pending* pend; - /* process waiting queries */ - while(outnet->udp_wait_first && outnet->unused_fds - && !outnet->want_to_quit) { - pend = outnet->udp_wait_first; - outnet->udp_wait_first = pend->next_waiting; - if(!pend->next_waiting) outnet->udp_wait_last = NULL; - sldns_buffer_clear(outnet->udp_buff); - sldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len); - sldns_buffer_flip(outnet->udp_buff); - free(pend->pkt); /* freeing now makes get_mem correct */ - pend->pkt = NULL; - pend->pkt_len = 0; - if(!randomize_and_send_udp(pend, outnet->udp_buff, - pend->timeout)) { - /* callback error on pending */ - if(pend->cb) { - fptr_ok(fptr_whitelist_pending_udp(pend->cb)); - (void)(*pend->cb)(outnet->unused_fds->cp, pend->cb_arg, - NETEVENT_CLOSED, NULL); - } - pending_delete(outnet, pend); - } - } -} - -int -outnet_udp_cb(struct comm_point* c, void* arg, int error, - struct comm_reply *reply_info) -{ - struct outside_network* outnet = (struct outside_network*)arg; - struct pending key; - struct pending* p; - verbose(VERB_ALGO, "answer cb"); - - if(error != NETEVENT_NOERROR) { - verbose(VERB_QUERY, "outnetudp got udp error %d", error); - return 0; - } - if(sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) { - verbose(VERB_QUERY, "outnetudp udp too short"); - return 0; - } - log_assert(reply_info); - - /* setup lookup key */ - key.id = (unsigned)LDNS_ID_WIRE(sldns_buffer_begin(c->buffer)); - memcpy(&key.addr, &reply_info->addr, reply_info->addrlen); - key.addrlen = reply_info->addrlen; - verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id); - log_addr(VERB_ALGO, "Incoming reply addr =", - &reply_info->addr, reply_info->addrlen); - - /* find it, see if this thing is a valid query response */ - verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count); - p = (struct pending*)rbtree_search(outnet->pending, &key); - if(!p) { - verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped."); - log_buf(VERB_ALGO, "dropped message", c->buffer); - outnet->unwanted_replies++; - if(outnet->unwanted_threshold && ++outnet->unwanted_total - >= outnet->unwanted_threshold) { - log_warn("unwanted reply total reached threshold (%u)" - " you may be under attack." - " defensive action: clearing the cache", - (unsigned)outnet->unwanted_threshold); - fptr_ok(fptr_whitelist_alloc_cleanup( - outnet->unwanted_action)); - (*outnet->unwanted_action)(outnet->unwanted_param); - outnet->unwanted_total = 0; - } - return 0; - } - - verbose(VERB_ALGO, "received udp reply."); - log_buf(VERB_ALGO, "udp message", c->buffer); - if(p->pc->cp != c) { - verbose(VERB_QUERY, "received reply id,addr on wrong port. " - "dropped."); - outnet->unwanted_replies++; - if(outnet->unwanted_threshold && ++outnet->unwanted_total - >= outnet->unwanted_threshold) { - log_warn("unwanted reply total reached threshold (%u)" - " you may be under attack." - " defensive action: clearing the cache", - (unsigned)outnet->unwanted_threshold); - fptr_ok(fptr_whitelist_alloc_cleanup( - outnet->unwanted_action)); - (*outnet->unwanted_action)(outnet->unwanted_param); - outnet->unwanted_total = 0; - } - return 0; - } - comm_timer_disable(p->timer); - verbose(VERB_ALGO, "outnet handle udp reply"); - /* delete from tree first in case callback creates a retry */ - (void)rbtree_delete(outnet->pending, p->node.key); - if(p->cb) { - fptr_ok(fptr_whitelist_pending_udp(p->cb)); - (void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_NOERROR, reply_info); - } - portcomm_loweruse(outnet, p->pc); - pending_delete(NULL, p); - outnet_send_wait_udp(outnet); - return 0; -} - -/** calculate number of ip4 and ip6 interfaces*/ -static void -calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6, - int* num_ip4, int* num_ip6) -{ - int i; - *num_ip4 = 0; - *num_ip6 = 0; - if(num_ifs <= 0) { - if(do_ip4) - *num_ip4 = 1; - if(do_ip6) - *num_ip6 = 1; - return; - } - for(i=0; i<num_ifs; i++) - { - if(str_is_ip6(ifs[i])) { - if(do_ip6) - (*num_ip6)++; - } else { - if(do_ip4) - (*num_ip4)++; - } - } - -} - -void -pending_udp_timer_delay_cb(void* arg) -{ - struct pending* p = (struct pending*)arg; - struct outside_network* outnet = p->outnet; - verbose(VERB_ALGO, "timeout udp with delay"); - portcomm_loweruse(outnet, p->pc); - pending_delete(outnet, p); - outnet_send_wait_udp(outnet); -} - -void -pending_udp_timer_cb(void *arg) -{ - struct pending* p = (struct pending*)arg; - struct outside_network* outnet = p->outnet; - /* it timed out */ - verbose(VERB_ALGO, "timeout udp"); - if(p->cb) { - fptr_ok(fptr_whitelist_pending_udp(p->cb)); - (void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_TIMEOUT, NULL); - } - /* if delayclose, keep port open for a longer time. - * But if the udpwaitlist exists, then we are struggling to - * keep up with demand for sockets, so do not wait, but service - * the customer (customer service more important than portICMPs) */ - if(outnet->delayclose && !outnet->udp_wait_first) { - p->cb = NULL; - p->timer->callback = &pending_udp_timer_delay_cb; - comm_timer_set(p->timer, &outnet->delay_tv); - return; - } - portcomm_loweruse(outnet, p->pc); - pending_delete(outnet, p); - outnet_send_wait_udp(outnet); -} - -/** create pending_tcp buffers */ -static int -create_pending_tcp(struct outside_network* outnet, size_t bufsize) -{ - size_t i; - if(outnet->num_tcp == 0) - return 1; /* no tcp needed, nothing to do */ - if(!(outnet->tcp_conns = (struct pending_tcp **)calloc( - outnet->num_tcp, sizeof(struct pending_tcp*)))) - return 0; - for(i=0; i<outnet->num_tcp; i++) { - if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1, - sizeof(struct pending_tcp)))) - return 0; - outnet->tcp_conns[i]->next_free = outnet->tcp_free; - outnet->tcp_free = outnet->tcp_conns[i]; - outnet->tcp_conns[i]->c = comm_point_create_tcp_out( - outnet->base, bufsize, outnet_tcp_cb, - outnet->tcp_conns[i]); - if(!outnet->tcp_conns[i]->c) - return 0; - } - return 1; -} - -/** setup an outgoing interface, ready address */ -static int setup_if(struct port_if* pif, const char* addrstr, - int* avail, int numavail, size_t numfd) -{ - pif->avail_total = numavail; - pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int)); - if(!pif->avail_ports) - return 0; - if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) && - !netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT, - &pif->addr, &pif->addrlen, &pif->pfxlen)) - return 0; - pif->maxout = (int)numfd; - pif->inuse = 0; - pif->out = (struct port_comm**)calloc(numfd, - sizeof(struct port_comm*)); - if(!pif->out) - return 0; - return 1; -} - -struct outside_network* -outside_network_create(struct comm_base *base, size_t bufsize, - size_t num_ports, char** ifs, int num_ifs, int do_ip4, - int do_ip6, size_t num_tcp, struct infra_cache* infra, - struct ub_randstate* rnd, int use_caps_for_id, int* availports, - int numavailports, size_t unwanted_threshold, int tcp_mss, - void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx, int delayclose, struct dt_env* dtenv) -{ - struct outside_network* outnet = (struct outside_network*) - calloc(1, sizeof(struct outside_network)); - size_t k; - if(!outnet) { - log_err("malloc failed"); - return NULL; - } - comm_base_timept(base, &outnet->now_secs, &outnet->now_tv); - outnet->base = base; - outnet->num_tcp = num_tcp; - outnet->num_tcp_outgoing = 0; - outnet->infra = infra; - outnet->rnd = rnd; - outnet->sslctx = sslctx; -#ifdef USE_DNSTAP - outnet->dtenv = dtenv; -#else - (void)dtenv; -#endif - outnet->svcd_overhead = 0; - outnet->want_to_quit = 0; - outnet->unwanted_threshold = unwanted_threshold; - outnet->unwanted_action = unwanted_action; - outnet->unwanted_param = unwanted_param; - outnet->use_caps_for_id = use_caps_for_id; - outnet->do_udp = do_udp; - outnet->tcp_mss = tcp_mss; -#ifndef S_SPLINT_S - if(delayclose) { - outnet->delayclose = 1; - outnet->delay_tv.tv_sec = delayclose/1000; - outnet->delay_tv.tv_usec = (delayclose%1000)*1000; - } -#endif - if(numavailports == 0) { - log_err("no outgoing ports available"); - outside_network_delete(outnet); - return NULL; - } -#ifndef INET6 - do_ip6 = 0; -#endif - calc_num46(ifs, num_ifs, do_ip4, do_ip6, - &outnet->num_ip4, &outnet->num_ip6); - if(outnet->num_ip4 != 0) { - if(!(outnet->ip4_ifs = (struct port_if*)calloc( - (size_t)outnet->num_ip4, sizeof(struct port_if)))) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - } - if(outnet->num_ip6 != 0) { - if(!(outnet->ip6_ifs = (struct port_if*)calloc( - (size_t)outnet->num_ip6, sizeof(struct port_if)))) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - } - if( !(outnet->udp_buff = sldns_buffer_new(bufsize)) || - !(outnet->pending = rbtree_create(pending_cmp)) || - !(outnet->serviced = rbtree_create(serviced_cmp)) || - !create_pending_tcp(outnet, bufsize)) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - - /* allocate commpoints */ - for(k=0; k<num_ports; k++) { - struct port_comm* pc; - pc = (struct port_comm*)calloc(1, sizeof(*pc)); - if(!pc) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - pc->cp = comm_point_create_udp(outnet->base, -1, - outnet->udp_buff, outnet_udp_cb, outnet); - if(!pc->cp) { - log_err("malloc failed"); - free(pc); - outside_network_delete(outnet); - return NULL; - } - pc->next = outnet->unused_fds; - outnet->unused_fds = pc; - } - - /* allocate interfaces */ - if(num_ifs == 0) { - if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0", - availports, numavailports, num_ports)) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::", - availports, numavailports, num_ports)) { - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - } else { - size_t done_4 = 0, done_6 = 0; - int i; - for(i=0; i<num_ifs; i++) { - if(str_is_ip6(ifs[i]) && do_ip6) { - if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i], - availports, numavailports, num_ports)){ - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - done_6++; - } - if(!str_is_ip6(ifs[i]) && do_ip4) { - if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i], - availports, numavailports, num_ports)){ - log_err("malloc failed"); - outside_network_delete(outnet); - return NULL; - } - done_4++; - } - } - } - return outnet; -} - -/** helper pending delete */ -static void -pending_node_del(rbnode_type* node, void* arg) -{ - struct pending* pend = (struct pending*)node; - struct outside_network* outnet = (struct outside_network*)arg; - pending_delete(outnet, pend); -} - -/** helper serviced delete */ -static void -serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg)) -{ - struct serviced_query* sq = (struct serviced_query*)node; - struct service_callback* p = sq->cblist, *np; - free(sq->qbuf); - free(sq->zone); - edns_opt_list_free(sq->opt_list); - while(p) { - np = p->next; - free(p); - p = np; - } - free(sq); -} - -void -outside_network_quit_prepare(struct outside_network* outnet) -{ - if(!outnet) - return; - /* prevent queued items from being sent */ - outnet->want_to_quit = 1; -} - -void -outside_network_delete(struct outside_network* outnet) -{ - if(!outnet) - return; - outnet->want_to_quit = 1; - /* check every element, since we can be called on malloc error */ - if(outnet->pending) { - /* free pending elements, but do no unlink from tree. */ - traverse_postorder(outnet->pending, pending_node_del, NULL); - free(outnet->pending); - } - if(outnet->serviced) { - traverse_postorder(outnet->serviced, serviced_node_del, NULL); - free(outnet->serviced); - } - if(outnet->udp_buff) - sldns_buffer_free(outnet->udp_buff); - if(outnet->unused_fds) { - struct port_comm* p = outnet->unused_fds, *np; - while(p) { - np = p->next; - comm_point_delete(p->cp); - free(p); - p = np; - } - outnet->unused_fds = NULL; - } - if(outnet->ip4_ifs) { - int i, k; - for(i=0; i<outnet->num_ip4; i++) { - for(k=0; k<outnet->ip4_ifs[i].inuse; k++) { - struct port_comm* pc = outnet->ip4_ifs[i]. - out[k]; - comm_point_delete(pc->cp); - free(pc); - } - free(outnet->ip4_ifs[i].avail_ports); - free(outnet->ip4_ifs[i].out); - } - free(outnet->ip4_ifs); - } - if(outnet->ip6_ifs) { - int i, k; - for(i=0; i<outnet->num_ip6; i++) { - for(k=0; k<outnet->ip6_ifs[i].inuse; k++) { - struct port_comm* pc = outnet->ip6_ifs[i]. - out[k]; - comm_point_delete(pc->cp); - free(pc); - } - free(outnet->ip6_ifs[i].avail_ports); - free(outnet->ip6_ifs[i].out); - } - free(outnet->ip6_ifs); - } - if(outnet->tcp_conns) { - size_t i; - for(i=0; i<outnet->num_tcp; i++) - if(outnet->tcp_conns[i]) { - comm_point_delete(outnet->tcp_conns[i]->c); - waiting_tcp_delete(outnet->tcp_conns[i]->query); - free(outnet->tcp_conns[i]); - } - free(outnet->tcp_conns); - } - if(outnet->tcp_wait_first) { - struct waiting_tcp* p = outnet->tcp_wait_first, *np; - while(p) { - np = p->next_waiting; - waiting_tcp_delete(p); - p = np; - } - } - if(outnet->udp_wait_first) { - struct pending* p = outnet->udp_wait_first, *np; - while(p) { - np = p->next_waiting; - pending_delete(NULL, p); - p = np; - } - } - free(outnet); -} - -void -pending_delete(struct outside_network* outnet, struct pending* p) -{ - if(!p) - return; - if(outnet && outnet->udp_wait_first && - (p->next_waiting || p == outnet->udp_wait_last) ) { - /* delete from waiting list, if it is in the waiting list */ - struct pending* prev = NULL, *x = outnet->udp_wait_first; - while(x && x != p) { - prev = x; - x = x->next_waiting; - } - if(x) { - log_assert(x == p); - if(prev) - prev->next_waiting = p->next_waiting; - else outnet->udp_wait_first = p->next_waiting; - if(outnet->udp_wait_last == p) - outnet->udp_wait_last = prev; - } - } - if(outnet) { - (void)rbtree_delete(outnet->pending, p->node.key); - } - if(p->timer) - comm_timer_delete(p->timer); - free(p->pkt); - free(p); -} - -static void -sai6_putrandom(struct sockaddr_in6 *sa, int pfxlen, struct ub_randstate *rnd) -{ - int i, last; - if(!(pfxlen > 0 && pfxlen < 128)) - return; - for(i = 0; i < (128 - pfxlen) / 8; i++) { - sa->sin6_addr.s6_addr[15-i] = (uint8_t)ub_random_max(rnd, 256); - } - last = pfxlen & 7; - if(last != 0) { - sa->sin6_addr.s6_addr[15-i] |= - ((0xFF >> last) & ub_random_max(rnd, 256)); - } -} - -/** - * Try to open a UDP socket for outgoing communication. - * Sets sockets options as needed. - * @param addr: socket address. - * @param addrlen: length of address. - * @param pfxlen: length of network prefix (for address randomisation). - * @param port: port override for addr. - * @param inuse: if -1 is returned, this bool means the port was in use. - * @param rnd: random state (for address randomisation). - * @return fd or -1 - */ -static int -udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen, - int port, int* inuse, struct ub_randstate* rnd) -{ - int fd, noproto; - if(addr_is_ip6(addr, addrlen)) { - int freebind = 0; - struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; - sa.sin6_port = (in_port_t)htons((uint16_t)port); - if(pfxlen != 0) { - freebind = 1; - sai6_putrandom(&sa, pfxlen, rnd); - } - fd = create_udp_sock(AF_INET6, SOCK_DGRAM, - (struct sockaddr*)&sa, addrlen, 1, inuse, &noproto, - 0, 0, 0, NULL, 0, freebind, 0); - } else { - struct sockaddr_in* sa = (struct sockaddr_in*)addr; - sa->sin_port = (in_port_t)htons((uint16_t)port); - fd = create_udp_sock(AF_INET, SOCK_DGRAM, - (struct sockaddr*)addr, addrlen, 1, inuse, &noproto, - 0, 0, 0, NULL, 0, 0, 0); - } - return fd; -} - -/** Select random ID */ -static int -select_id(struct outside_network* outnet, struct pending* pend, - sldns_buffer* packet) -{ - int id_tries = 0; - pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff; - LDNS_ID_SET(sldns_buffer_begin(packet), pend->id); - - /* insert in tree */ - pend->node.key = pend; - while(!rbtree_insert(outnet->pending, &pend->node)) { - /* change ID to avoid collision */ - pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff; - LDNS_ID_SET(sldns_buffer_begin(packet), pend->id); - id_tries++; - if(id_tries == MAX_ID_RETRY) { - pend->id=99999; /* non existant ID */ - log_err("failed to generate unique ID, drop msg"); - return 0; - } - } - verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id); - return 1; -} - -/** Select random interface and port */ -static int -select_ifport(struct outside_network* outnet, struct pending* pend, - int num_if, struct port_if* ifs) -{ - int my_if, my_port, fd, portno, inuse, tries=0; - struct port_if* pif; - /* randomly select interface and port */ - if(num_if == 0) { - verbose(VERB_QUERY, "Need to send query but have no " - "outgoing interfaces of that family"); - return 0; - } - log_assert(outnet->unused_fds); - tries = 0; - while(1) { - my_if = ub_random_max(outnet->rnd, num_if); - pif = &ifs[my_if]; - my_port = ub_random_max(outnet->rnd, pif->avail_total); - if(my_port < pif->inuse) { - /* port already open */ - pend->pc = pif->out[my_port]; - verbose(VERB_ALGO, "using UDP if=%d port=%d", - my_if, pend->pc->number); - break; - } - /* try to open new port, if fails, loop to try again */ - log_assert(pif->inuse < pif->maxout); - portno = pif->avail_ports[my_port - pif->inuse]; - fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen, - portno, &inuse, outnet->rnd); - if(fd == -1 && !inuse) { - /* nonrecoverable error making socket */ - return 0; - } - if(fd != -1) { - verbose(VERB_ALGO, "opened UDP if=%d port=%d", - my_if, portno); - /* grab fd */ - pend->pc = outnet->unused_fds; - outnet->unused_fds = pend->pc->next; - - /* setup portcomm */ - pend->pc->next = NULL; - pend->pc->number = portno; - pend->pc->pif = pif; - pend->pc->index = pif->inuse; - pend->pc->num_outstanding = 0; - comm_point_start_listening(pend->pc->cp, fd, -1); - - /* grab port in interface */ - pif->out[pif->inuse] = pend->pc; - pif->avail_ports[my_port - pif->inuse] = - pif->avail_ports[pif->avail_total-pif->inuse-1]; - pif->inuse++; - break; - } - /* failed, already in use */ - verbose(VERB_QUERY, "port %d in use, trying another", portno); - tries++; - if(tries == MAX_PORT_RETRY) { - log_err("failed to find an open port, drop msg"); - return 0; - } - } - log_assert(pend->pc); - pend->pc->num_outstanding++; - - return 1; -} - -static int -randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, int timeout) -{ - struct timeval tv; - struct outside_network* outnet = pend->sq->outnet; - - /* select id */ - if(!select_id(outnet, pend, packet)) { - return 0; - } - - /* select src_if, port */ - if(addr_is_ip6(&pend->addr, pend->addrlen)) { - if(!select_ifport(outnet, pend, - outnet->num_ip6, outnet->ip6_ifs)) - return 0; - } else { - if(!select_ifport(outnet, pend, - outnet->num_ip4, outnet->ip4_ifs)) - return 0; - } - log_assert(pend->pc && pend->pc->cp); - - /* send it over the commlink */ - if(!comm_point_send_udp_msg(pend->pc->cp, packet, - (struct sockaddr*)&pend->addr, pend->addrlen)) { - portcomm_loweruse(outnet, pend->pc); - return 0; - } - - /* system calls to set timeout after sending UDP to make roundtrip - smaller. */ -#ifndef S_SPLINT_S - tv.tv_sec = timeout/1000; - tv.tv_usec = (timeout%1000)*1000; -#endif - comm_timer_set(pend->timer, &tv); - -#ifdef USE_DNSTAP - if(outnet->dtenv && - (outnet->dtenv->log_resolver_query_messages || - outnet->dtenv->log_forwarder_query_messages)) - dt_msg_send_outside_query(outnet->dtenv, &pend->addr, comm_udp, - pend->sq->zone, pend->sq->zonelen, packet); -#endif - return 1; -} - -struct pending* -pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet, - int timeout, comm_point_callback_type* cb, void* cb_arg) -{ - struct pending* pend = (struct pending*)calloc(1, sizeof(*pend)); - if(!pend) return NULL; - pend->outnet = sq->outnet; - pend->sq = sq; - pend->addrlen = sq->addrlen; - memmove(&pend->addr, &sq->addr, sq->addrlen); - pend->cb = cb; - pend->cb_arg = cb_arg; - pend->node.key = pend; - pend->timer = comm_timer_create(sq->outnet->base, pending_udp_timer_cb, - pend); - if(!pend->timer) { - free(pend); - return NULL; - } - - if(sq->outnet->unused_fds == NULL) { - /* no unused fd, cannot create a new port (randomly) */ - verbose(VERB_ALGO, "no fds available, udp query waiting"); - pend->timeout = timeout; - pend->pkt_len = sldns_buffer_limit(packet); - pend->pkt = (uint8_t*)memdup(sldns_buffer_begin(packet), - pend->pkt_len); - if(!pend->pkt) { - comm_timer_delete(pend->timer); - free(pend); - return NULL; - } - /* put at end of waiting list */ - if(sq->outnet->udp_wait_last) - sq->outnet->udp_wait_last->next_waiting = pend; - else - sq->outnet->udp_wait_first = pend; - sq->outnet->udp_wait_last = pend; - return pend; - } - if(!randomize_and_send_udp(pend, packet, timeout)) { - pending_delete(sq->outnet, pend); - return NULL; - } - return pend; -} - -void -outnet_tcptimer(void* arg) -{ - struct waiting_tcp* w = (struct waiting_tcp*)arg; - struct outside_network* outnet = w->outnet; - comm_point_callback_type* cb; - void* cb_arg; - if(w->pkt) { - /* it is on the waiting list */ - waiting_list_remove(outnet, w); - } else { - /* it was in use */ - struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting; - comm_point_close(pend->c); - pend->query = NULL; - pend->next_free = outnet->tcp_free; - outnet->tcp_free = pend; - } - cb = w->cb; - cb_arg = w->cb_arg; - waiting_tcp_delete(w); - fptr_ok(fptr_whitelist_pending_tcp(cb)); - (void)(*cb)(NULL, cb_arg, NETEVENT_TIMEOUT, NULL); - use_free_buffer(outnet); -} - -struct waiting_tcp* -pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet, - int timeout, comm_point_callback_type* callback, void* callback_arg) -{ - struct pending_tcp* pend = sq->outnet->tcp_free; - struct waiting_tcp* w; - struct timeval tv; - uint16_t id; - /* if no buffer is free allocate space to store query */ - w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp) - + (pend?0:sldns_buffer_limit(packet))); - if(!w) { - return NULL; - } - if(!(w->timer = comm_timer_create(sq->outnet->base, outnet_tcptimer, w))) { - free(w); - return NULL; - } - w->pkt = NULL; - w->pkt_len = 0; - id = ((unsigned)ub_random(sq->outnet->rnd)>>8) & 0xffff; - LDNS_ID_SET(sldns_buffer_begin(packet), id); - memcpy(&w->addr, &sq->addr, sq->addrlen); - w->addrlen = sq->addrlen; - w->outnet = sq->outnet; - w->cb = callback; - w->cb_arg = callback_arg; - w->ssl_upstream = sq->ssl_upstream; -#ifndef S_SPLINT_S - tv.tv_sec = timeout; - tv.tv_usec = 0; -#endif - comm_timer_set(w->timer, &tv); - if(pend) { - /* we have a buffer available right now */ - if(!outnet_tcp_take_into_use(w, sldns_buffer_begin(packet), - sldns_buffer_limit(packet))) { - waiting_tcp_delete(w); - return NULL; - } -#ifdef USE_DNSTAP - if(sq->outnet->dtenv && - (sq->outnet->dtenv->log_resolver_query_messages || - sq->outnet->dtenv->log_forwarder_query_messages)) - dt_msg_send_outside_query(sq->outnet->dtenv, &sq->addr, - comm_tcp, sq->zone, sq->zonelen, packet); -#endif - } else { - /* queue up */ - w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp); - w->pkt_len = sldns_buffer_limit(packet); - memmove(w->pkt, sldns_buffer_begin(packet), w->pkt_len); - w->next_waiting = NULL; - if(sq->outnet->tcp_wait_last) - sq->outnet->tcp_wait_last->next_waiting = w; - else sq->outnet->tcp_wait_first = w; - sq->outnet->tcp_wait_last = w; - } - return w; -} - -/** create query for serviced queries */ -static void -serviced_gen_query(sldns_buffer* buff, uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags) -{ - sldns_buffer_clear(buff); - /* skip id */ - sldns_buffer_write_u16(buff, flags); - sldns_buffer_write_u16(buff, 1); /* qdcount */ - sldns_buffer_write_u16(buff, 0); /* ancount */ - sldns_buffer_write_u16(buff, 0); /* nscount */ - sldns_buffer_write_u16(buff, 0); /* arcount */ - sldns_buffer_write(buff, qname, qnamelen); - sldns_buffer_write_u16(buff, qtype); - sldns_buffer_write_u16(buff, qclass); - sldns_buffer_flip(buff); -} - -/** lookup serviced query in serviced query rbtree */ -static struct serviced_query* -lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec, - struct sockaddr_storage* addr, socklen_t addrlen, - struct edns_option* opt_list) -{ - struct serviced_query key; - key.node.key = &key; - key.qbuf = sldns_buffer_begin(buff); - key.qbuflen = sldns_buffer_limit(buff); - key.dnssec = dnssec; - memcpy(&key.addr, addr, addrlen); - key.addrlen = addrlen; - key.outnet = outnet; - key.opt_list = opt_list; - return (struct serviced_query*)rbtree_search(outnet->serviced, &key); -} - -/** Create new serviced entry */ -static struct serviced_query* -serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec, - int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, int qtype, struct edns_option* opt_list) -{ - struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq)); -#ifdef UNBOUND_DEBUG - rbnode_type* ins; -#endif - if(!sq) - return NULL; - sq->node.key = sq; - sq->qbuf = memdup(sldns_buffer_begin(buff), sldns_buffer_limit(buff)); - if(!sq->qbuf) { - free(sq); - return NULL; - } - sq->qbuflen = sldns_buffer_limit(buff); - sq->zone = memdup(zone, zonelen); - if(!sq->zone) { - free(sq->qbuf); - free(sq); - return NULL; - } - sq->zonelen = zonelen; - sq->qtype = qtype; - sq->dnssec = dnssec; - sq->want_dnssec = want_dnssec; - sq->nocaps = nocaps; - sq->tcp_upstream = tcp_upstream; - sq->ssl_upstream = ssl_upstream; - memcpy(&sq->addr, addr, addrlen); - sq->addrlen = addrlen; - sq->opt_list = NULL; - if(opt_list) { - sq->opt_list = edns_opt_copy_alloc(opt_list); - if(!sq->opt_list) { - free(sq->zone); - free(sq->qbuf); - free(sq); - return NULL; - } - } - sq->outnet = outnet; - sq->cblist = NULL; - sq->pending = NULL; - sq->status = serviced_initial; - sq->retry = 0; - sq->to_be_deleted = 0; -#ifdef UNBOUND_DEBUG - ins = -#else - (void) -#endif - rbtree_insert(outnet->serviced, &sq->node); - log_assert(ins != NULL); /* must not be already present */ - return sq; -} - -/** remove waiting tcp from the outnet waiting list */ -static void -waiting_list_remove(struct outside_network* outnet, struct waiting_tcp* w) -{ - struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL; - while(p) { - if(p == w) { - /* remove w */ - if(prev) - prev->next_waiting = w->next_waiting; - else outnet->tcp_wait_first = w->next_waiting; - if(outnet->tcp_wait_last == w) - outnet->tcp_wait_last = prev; - return; - } - prev = p; - p = p->next_waiting; - } -} - -/** cleanup serviced query entry */ -static void -serviced_delete(struct serviced_query* sq) -{ - if(sq->pending) { - /* clear up the pending query */ - if(sq->status == serviced_query_UDP_EDNS || - sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS || - sq->status == serviced_query_UDP_EDNS_FRAG || - sq->status == serviced_query_UDP_EDNS_fallback) { - struct pending* p = (struct pending*)sq->pending; - if(p->pc) - portcomm_loweruse(sq->outnet, p->pc); - pending_delete(sq->outnet, p); - /* this call can cause reentrant calls back into the - * mesh */ - outnet_send_wait_udp(sq->outnet); - } else { - struct waiting_tcp* p = (struct waiting_tcp*) - sq->pending; - if(p->pkt == NULL) { - decomission_pending_tcp(sq->outnet, - (struct pending_tcp*)p->next_waiting); - } else { - waiting_list_remove(sq->outnet, p); - waiting_tcp_delete(p); - } - } - } - /* does not delete from tree, caller has to do that */ - serviced_node_del(&sq->node, NULL); -} - -/** perturb a dname capitalization randomly */ -static void -serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len) -{ - uint8_t lablen; - uint8_t* d = qbuf + 10; - long int random = 0; - int bits = 0; - log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */); - (void)len; - lablen = *d++; - while(lablen) { - while(lablen--) { - /* only perturb A-Z, a-z */ - if(isalpha((unsigned char)*d)) { - /* get a random bit */ - if(bits == 0) { - random = ub_random(rnd); - bits = 30; - } - if(random & 0x1) { - *d = (uint8_t)toupper((unsigned char)*d); - } else { - *d = (uint8_t)tolower((unsigned char)*d); - } - random >>= 1; - bits--; - } - d++; - } - lablen = *d++; - } - if(verbosity >= VERB_ALGO) { - char buf[LDNS_MAX_DOMAINLEN+1]; - dname_str(qbuf+10, buf); - verbose(VERB_ALGO, "qname perturbed to %s", buf); - } -} - -/** put serviced query into a buffer */ -static void -serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns) -{ - /* if we are using 0x20 bits for ID randomness, perturb them */ - if(sq->outnet->use_caps_for_id && !sq->nocaps) { - serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen); - } - /* generate query */ - sldns_buffer_clear(buff); - sldns_buffer_write_u16(buff, 0); /* id placeholder */ - sldns_buffer_write(buff, sq->qbuf, sq->qbuflen); - sldns_buffer_flip(buff); - if(with_edns) { - /* add edns section */ - struct edns_data edns; - edns.edns_present = 1; - edns.ext_rcode = 0; - edns.edns_version = EDNS_ADVERTISED_VERSION; - edns.opt_list = sq->opt_list; - if(sq->status == serviced_query_UDP_EDNS_FRAG) { - if(addr_is_ip6(&sq->addr, sq->addrlen)) { - if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE) - edns.udp_size = EDNS_FRAG_SIZE_IP6; - else edns.udp_size = EDNS_ADVERTISED_SIZE; - } else { - if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE) - edns.udp_size = EDNS_FRAG_SIZE_IP4; - else edns.udp_size = EDNS_ADVERTISED_SIZE; - } - } else { - edns.udp_size = EDNS_ADVERTISED_SIZE; - } - edns.bits = 0; - if(sq->dnssec & EDNS_DO) - edns.bits = EDNS_DO; - if(sq->dnssec & BIT_CD) - LDNS_CD_SET(sldns_buffer_begin(buff)); - attach_edns_record(buff, &edns); - } -} - -/** - * Perform serviced query UDP sending operation. - * Sends UDP with EDNS, unless infra host marked non EDNS. - * @param sq: query to send. - * @param buff: buffer scratch space. - * @return 0 on error. - */ -static int -serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff) -{ - int rtt, vs; - uint8_t edns_lame_known; - time_t now = *sq->outnet->now_secs; - - if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone, - sq->zonelen, now, &vs, &edns_lame_known, &rtt)) - return 0; - sq->last_rtt = rtt; - verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs); - if(sq->status == serviced_initial) { - if(edns_lame_known == 0 && rtt > 5000 && rtt < 10001) { - /* perform EDNS lame probe - check if server is - * EDNS lame (EDNS queries to it are dropped) */ - verbose(VERB_ALGO, "serviced query: send probe to see " - " if use of EDNS causes timeouts"); - /* even 700 msec may be too small */ - rtt = 1000; - sq->status = serviced_query_PROBE_EDNS; - } else if(vs != -1) { - sq->status = serviced_query_UDP_EDNS; - } else { - sq->status = serviced_query_UDP; - } - } - serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) || - (sq->status == serviced_query_UDP_EDNS_FRAG)); - sq->last_sent_time = *sq->outnet->now_tv; - sq->edns_lame_known = (int)edns_lame_known; - verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt); - sq->pending = pending_udp_query(sq, buff, rtt, - serviced_udp_callback, sq); - if(!sq->pending) - return 0; - return 1; -} - -/** check that perturbed qname is identical */ -static int -serviced_check_qname(sldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen) -{ - uint8_t* d1 = sldns_buffer_at(pkt, 12); - uint8_t* d2 = qbuf+10; - uint8_t len1, len2; - int count = 0; - log_assert(qbuflen >= 15 /* 10 header, root, type, class */); - len1 = *d1++; - len2 = *d2++; - if(sldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */ - return 0; - while(len1 != 0 || len2 != 0) { - if(LABEL_IS_PTR(len1)) { - d1 = sldns_buffer_begin(pkt)+PTR_OFFSET(len1, *d1); - if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt))) - return 0; - len1 = *d1++; - if(count++ > MAX_COMPRESS_PTRS) - return 0; - continue; - } - if(d2 > qbuf+qbuflen) - return 0; - if(len1 != len2) - return 0; - if(len1 > LDNS_MAX_LABELLEN) - return 0; - log_assert(len1 <= LDNS_MAX_LABELLEN); - log_assert(len2 <= LDNS_MAX_LABELLEN); - log_assert(len1 == len2 && len1 != 0); - /* compare the labels - bitwise identical */ - if(memcmp(d1, d2, len1) != 0) - return 0; - d1 += len1; - d2 += len2; - len1 = *d1++; - len2 = *d2++; - } - return 1; -} - -/** call the callbacks for a serviced query */ -static void -serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c, - struct comm_reply* rep) -{ - struct service_callback* p; - int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/ - uint8_t *backup_p = NULL; - size_t backlen = 0; -#ifdef UNBOUND_DEBUG - rbnode_type* rem = -#else - (void) -#endif - /* remove from tree, and schedule for deletion, so that callbacks - * can safely deregister themselves and even create new serviced - * queries that are identical to this one. */ - rbtree_delete(sq->outnet->serviced, sq); - log_assert(rem); /* should have been present */ - sq->to_be_deleted = 1; - verbose(VERB_ALGO, "svcd callbacks start"); - if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c && - !sq->nocaps && sq->qtype != LDNS_RR_TYPE_PTR) { - /* for type PTR do not check perturbed name in answer, - * compatibility with cisco dns guard boxes that mess up - * reverse queries 0x20 contents */ - /* noerror and nxdomain must have a qname in reply */ - if(sldns_buffer_read_u16_at(c->buffer, 4) == 0 && - (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) - == LDNS_RCODE_NOERROR || - LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) - == LDNS_RCODE_NXDOMAIN)) { - verbose(VERB_DETAIL, "no qname in reply to check 0x20ID"); - log_addr(VERB_DETAIL, "from server", - &sq->addr, sq->addrlen); - log_buf(VERB_DETAIL, "for packet", c->buffer); - error = NETEVENT_CLOSED; - c = NULL; - } else if(sldns_buffer_read_u16_at(c->buffer, 4) > 0 && - !serviced_check_qname(c->buffer, sq->qbuf, - sq->qbuflen)) { - verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname"); - log_addr(VERB_DETAIL, "from server", - &sq->addr, sq->addrlen); - log_buf(VERB_DETAIL, "for packet", c->buffer); - error = NETEVENT_CAPSFAIL; - /* and cleanup too */ - pkt_dname_tolower(c->buffer, - sldns_buffer_at(c->buffer, 12)); - } else { - verbose(VERB_ALGO, "good 0x20-ID in reply qname"); - /* cleanup caps, prettier cache contents. */ - pkt_dname_tolower(c->buffer, - sldns_buffer_at(c->buffer, 12)); - } - } - if(dobackup && c) { - /* make a backup of the query, since the querystate processing - * may send outgoing queries that overwrite the buffer. - * use secondary buffer to store the query. - * This is a data copy, but faster than packet to server */ - backlen = sldns_buffer_limit(c->buffer); - backup_p = memdup(sldns_buffer_begin(c->buffer), backlen); - if(!backup_p) { - log_err("malloc failure in serviced query callbacks"); - error = NETEVENT_CLOSED; - c = NULL; - } - sq->outnet->svcd_overhead = backlen; - } - /* test the actual sq->cblist, because the next elem could be deleted*/ - while((p=sq->cblist) != NULL) { - sq->cblist = p->next; /* remove this element */ - if(dobackup && c) { - sldns_buffer_clear(c->buffer); - sldns_buffer_write(c->buffer, backup_p, backlen); - sldns_buffer_flip(c->buffer); - } - fptr_ok(fptr_whitelist_serviced_query(p->cb)); - (void)(*p->cb)(c, p->cb_arg, error, rep); - free(p); - } - if(backup_p) { - free(backup_p); - sq->outnet->svcd_overhead = 0; - } - verbose(VERB_ALGO, "svcd callbacks end"); - log_assert(sq->cblist == NULL); - serviced_delete(sq); -} - -int -serviced_tcp_callback(struct comm_point* c, void* arg, int error, - struct comm_reply* rep) -{ - struct serviced_query* sq = (struct serviced_query*)arg; - struct comm_reply r2; - sq->pending = NULL; /* removed after this callback */ - if(error != NETEVENT_NOERROR) - log_addr(VERB_QUERY, "tcp error for address", - &sq->addr, sq->addrlen); - if(error==NETEVENT_NOERROR) - infra_update_tcp_works(sq->outnet->infra, &sq->addr, - sq->addrlen, sq->zone, sq->zonelen); -#ifdef USE_DNSTAP - if(error==NETEVENT_NOERROR && sq->outnet->dtenv && - (sq->outnet->dtenv->log_resolver_response_messages || - sq->outnet->dtenv->log_forwarder_response_messages)) - dt_msg_send_outside_response(sq->outnet->dtenv, &sq->addr, - c->type, sq->zone, sq->zonelen, sq->qbuf, sq->qbuflen, - &sq->last_sent_time, sq->outnet->now_tv, c->buffer); -#endif - if(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS && - (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == - LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(sldns_buffer_begin( - c->buffer)) == LDNS_RCODE_NOTIMPL) ) { - /* attempt to fallback to nonEDNS */ - sq->status = serviced_query_TCP_EDNS_fallback; - serviced_tcp_initiate(sq, c->buffer); - return 0; - } else if(error==NETEVENT_NOERROR && - sq->status == serviced_query_TCP_EDNS_fallback && - (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == - LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE( - sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN - || LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) - == LDNS_RCODE_YXDOMAIN)) { - /* the fallback produced a result that looks promising, note - * that this server should be approached without EDNS */ - /* only store noEDNS in cache if domain is noDNSSEC */ - if(!sq->want_dnssec) - if(!infra_edns_update(sq->outnet->infra, &sq->addr, - sq->addrlen, sq->zone, sq->zonelen, -1, - *sq->outnet->now_secs)) - log_err("Out of memory caching no edns for host"); - sq->status = serviced_query_TCP; - } - if(sq->tcp_upstream || sq->ssl_upstream) { - struct timeval now = *sq->outnet->now_tv; - if(now.tv_sec > sq->last_sent_time.tv_sec || - (now.tv_sec == sq->last_sent_time.tv_sec && - now.tv_usec > sq->last_sent_time.tv_usec)) { - /* convert from microseconds to milliseconds */ - int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000 - + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000; - verbose(VERB_ALGO, "measured TCP-time at %d msec", roundtime); - log_assert(roundtime >= 0); - /* only store if less then AUTH_TIMEOUT seconds, it could be - * huge due to system-hibernated and we woke up */ - if(roundtime < TCP_AUTH_QUERY_TIMEOUT*1000) { - if(!infra_rtt_update(sq->outnet->infra, &sq->addr, - sq->addrlen, sq->zone, sq->zonelen, sq->qtype, - roundtime, sq->last_rtt, (time_t)now.tv_sec)) - log_err("out of memory noting rtt."); - } - } - } - /* insert address into reply info */ - if(!rep) { - /* create one if there isn't (on errors) */ - rep = &r2; - r2.c = c; - } - memcpy(&rep->addr, &sq->addr, sq->addrlen); - rep->addrlen = sq->addrlen; - serviced_callbacks(sq, error, c, rep); - return 0; -} - -static void -serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff) -{ - verbose(VERB_ALGO, "initiate TCP query %s", - sq->status==serviced_query_TCP_EDNS?"EDNS":""); - serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS); - sq->last_sent_time = *sq->outnet->now_tv; - sq->pending = pending_tcp_query(sq, buff, TCP_AUTH_QUERY_TIMEOUT, - serviced_tcp_callback, sq); - if(!sq->pending) { - /* delete from tree so that a retry by above layer does not - * clash with this entry */ - log_err("serviced_tcp_initiate: failed to send tcp query"); - serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL); - } -} - -/** Send serviced query over TCP return false on initial failure */ -static int -serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff) -{ - int vs, rtt; - uint8_t edns_lame_known; - if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone, - sq->zonelen, *sq->outnet->now_secs, &vs, &edns_lame_known, - &rtt)) - return 0; - if(vs != -1) - sq->status = serviced_query_TCP_EDNS; - else sq->status = serviced_query_TCP; - serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS); - sq->last_sent_time = *sq->outnet->now_tv; - sq->pending = pending_tcp_query(sq, buff, TCP_AUTH_QUERY_TIMEOUT, - serviced_tcp_callback, sq); - return sq->pending != NULL; -} - -/* see if packet is edns malformed; got zeroes at start. - * This is from servers that return malformed packets to EDNS0 queries, - * but they return good packets for nonEDNS0 queries. - * We try to detect their output; without resorting to a full parse or - * check for too many bytes after the end of the packet. */ -static int -packet_edns_malformed(struct sldns_buffer* buf, int qtype) -{ - size_t len; - if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE) - return 1; /* malformed */ - /* they have NOERROR rcode, 1 answer. */ - if(LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) != LDNS_RCODE_NOERROR) - return 0; - /* one query (to skip) and answer records */ - if(LDNS_QDCOUNT(sldns_buffer_begin(buf)) != 1 || - LDNS_ANCOUNT(sldns_buffer_begin(buf)) == 0) - return 0; - /* skip qname */ - len = dname_valid(sldns_buffer_at(buf, LDNS_HEADER_SIZE), - sldns_buffer_limit(buf)-LDNS_HEADER_SIZE); - if(len == 0) - return 0; - if(len == 1 && qtype == 0) - return 0; /* we asked for '.' and type 0 */ - /* and then 4 bytes (type and class of query) */ - if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE + len + 4 + 3) - return 0; - - /* and start with 11 zeroes as the answer RR */ - /* so check the qtype of the answer record, qname=0, type=0 */ - if(sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[0] == 0 && - sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[1] == 0 && - sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[2] == 0) - return 1; - return 0; -} - -int -serviced_udp_callback(struct comm_point* c, void* arg, int error, - struct comm_reply* rep) -{ - struct serviced_query* sq = (struct serviced_query*)arg; - struct outside_network* outnet = sq->outnet; - struct timeval now = *sq->outnet->now_tv; - int fallback_tcp = 0; - - sq->pending = NULL; /* removed after callback */ - if(error == NETEVENT_TIMEOUT) { - int rto = 0; - if(sq->status == serviced_query_PROBE_EDNS) { - /* non-EDNS probe failed; we do not know its status, - * keep trying with EDNS, timeout may not be caused - * by EDNS. */ - sq->status = serviced_query_UDP_EDNS; - } - if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000) { - /* fallback to 1480/1280 */ - sq->status = serviced_query_UDP_EDNS_FRAG; - log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10, - &sq->addr, sq->addrlen); - if(!serviced_udp_send(sq, c->buffer)) { - serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); - } - return 0; - } - if(sq->status == serviced_query_UDP_EDNS_FRAG) { - /* fragmentation size did not fix it */ - sq->status = serviced_query_UDP_EDNS; - } - sq->retry++; - if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, - sq->zone, sq->zonelen, sq->qtype, -1, sq->last_rtt, - (time_t)now.tv_sec))) - log_err("out of memory in UDP exponential backoff"); - if(sq->retry < OUTBOUND_UDP_RETRY) { - log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10, - &sq->addr, sq->addrlen); - if(!serviced_udp_send(sq, c->buffer)) { - serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); - } - return 0; - } - if(rto >= RTT_MAX_TIMEOUT) { - fallback_tcp = 1; - /* UDP does not work, fallback to TCP below */ - } else { - serviced_callbacks(sq, NETEVENT_TIMEOUT, c, rep); - return 0; - } - } else if(error != NETEVENT_NOERROR) { - /* udp returns error (due to no ID or interface available) */ - serviced_callbacks(sq, error, c, rep); - return 0; - } -#ifdef USE_DNSTAP - if(error == NETEVENT_NOERROR && outnet->dtenv && - (outnet->dtenv->log_resolver_response_messages || - outnet->dtenv->log_forwarder_response_messages)) - dt_msg_send_outside_response(outnet->dtenv, &sq->addr, c->type, - sq->zone, sq->zonelen, sq->qbuf, sq->qbuflen, - &sq->last_sent_time, sq->outnet->now_tv, c->buffer); -#endif - if(!fallback_tcp) { - if( (sq->status == serviced_query_UDP_EDNS - ||sq->status == serviced_query_UDP_EDNS_FRAG) - && (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) - == LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE( - sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL - || packet_edns_malformed(c->buffer, sq->qtype) - )) { - /* try to get an answer by falling back without EDNS */ - verbose(VERB_ALGO, "serviced query: attempt without EDNS"); - sq->status = serviced_query_UDP_EDNS_fallback; - sq->retry = 0; - if(!serviced_udp_send(sq, c->buffer)) { - serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); - } - return 0; - } else if(sq->status == serviced_query_PROBE_EDNS) { - /* probe without EDNS succeeds, so we conclude that this - * host likely has EDNS packets dropped */ - log_addr(VERB_DETAIL, "timeouts, concluded that connection to " - "host drops EDNS packets", &sq->addr, sq->addrlen); - /* only store noEDNS in cache if domain is noDNSSEC */ - if(!sq->want_dnssec) - if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, - sq->zone, sq->zonelen, -1, (time_t)now.tv_sec)) { - log_err("Out of memory caching no edns for host"); - } - sq->status = serviced_query_UDP; - } else if(sq->status == serviced_query_UDP_EDNS && - !sq->edns_lame_known) { - /* now we know that edns queries received answers store that */ - log_addr(VERB_ALGO, "serviced query: EDNS works for", - &sq->addr, sq->addrlen); - if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, - sq->zone, sq->zonelen, 0, (time_t)now.tv_sec)) { - log_err("Out of memory caching edns works"); - } - sq->edns_lame_known = 1; - } else if(sq->status == serviced_query_UDP_EDNS_fallback && - !sq->edns_lame_known && (LDNS_RCODE_WIRE( - sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR || - LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == - LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(sldns_buffer_begin( - c->buffer)) == LDNS_RCODE_YXDOMAIN)) { - /* the fallback produced a result that looks promising, note - * that this server should be approached without EDNS */ - /* only store noEDNS in cache if domain is noDNSSEC */ - if(!sq->want_dnssec) { - log_addr(VERB_ALGO, "serviced query: EDNS fails for", - &sq->addr, sq->addrlen); - if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, - sq->zone, sq->zonelen, -1, (time_t)now.tv_sec)) { - log_err("Out of memory caching no edns for host"); - } - } else { - log_addr(VERB_ALGO, "serviced query: EDNS fails, but " - "not stored because need DNSSEC for", &sq->addr, - sq->addrlen); - } - sq->status = serviced_query_UDP; - } - if(now.tv_sec > sq->last_sent_time.tv_sec || - (now.tv_sec == sq->last_sent_time.tv_sec && - now.tv_usec > sq->last_sent_time.tv_usec)) { - /* convert from microseconds to milliseconds */ - int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000 - + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000; - verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime); - log_assert(roundtime >= 0); - /* in case the system hibernated, do not enter a huge value, - * above this value gives trouble with server selection */ - if(roundtime < 60000) { - if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, - sq->zone, sq->zonelen, sq->qtype, roundtime, - sq->last_rtt, (time_t)now.tv_sec)) - log_err("out of memory noting rtt."); - } - } - } /* end of if_!fallback_tcp */ - /* perform TC flag check and TCP fallback after updating our - * cache entries for EDNS status and RTT times */ - if(LDNS_TC_WIRE(sldns_buffer_begin(c->buffer)) || fallback_tcp) { - /* fallback to TCP */ - /* this discards partial UDP contents */ - if(sq->status == serviced_query_UDP_EDNS || - sq->status == serviced_query_UDP_EDNS_FRAG || - sq->status == serviced_query_UDP_EDNS_fallback) - /* if we have unfinished EDNS_fallback, start again */ - sq->status = serviced_query_TCP_EDNS; - else sq->status = serviced_query_TCP; - serviced_tcp_initiate(sq, c->buffer); - return 0; - } - /* yay! an answer */ - serviced_callbacks(sq, error, c, rep); - return 0; -} - -struct serviced_query* -outnet_serviced_query(struct outside_network* outnet, - struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, - int nocaps, int tcp_upstream, int ssl_upstream, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* qstate, - comm_point_callback_type* callback, void* callback_arg, sldns_buffer* buff, - struct module_env* env) -{ - struct serviced_query* sq; - struct service_callback* cb; - if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, zonelen, - qstate, qstate->region)) - return NULL; - serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype, - qinfo->qclass, flags); - sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, - qstate->edns_opts_back_out); - /* duplicate entries are included in the callback list, because - * there is a counterpart registration by our caller that needs to - * be doubly-removed (with callbacks perhaps). */ - if(!(cb = (struct service_callback*)malloc(sizeof(*cb)))) - return NULL; - if(!sq) { - /* make new serviced query entry */ - sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps, - tcp_upstream, ssl_upstream, addr, addrlen, zone, - zonelen, (int)qinfo->qtype, qstate->edns_opts_back_out); - if(!sq) { - free(cb); - return NULL; - } - /* perform first network action */ - if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) { - if(!serviced_udp_send(sq, buff)) { - (void)rbtree_delete(outnet->serviced, sq); - free(sq->qbuf); - free(sq->zone); - free(sq); - free(cb); - return NULL; - } - } else { - if(!serviced_tcp_send(sq, buff)) { - (void)rbtree_delete(outnet->serviced, sq); - free(sq->qbuf); - free(sq->zone); - free(sq); - free(cb); - return NULL; - } - } - } - /* add callback to list of callbacks */ - cb->cb = callback; - cb->cb_arg = callback_arg; - cb->next = sq->cblist; - sq->cblist = cb; - return sq; -} - -/** remove callback from list */ -static void -callback_list_remove(struct serviced_query* sq, void* cb_arg) -{ - struct service_callback** pp = &sq->cblist; - while(*pp) { - if((*pp)->cb_arg == cb_arg) { - struct service_callback* del = *pp; - *pp = del->next; - free(del); - return; - } - pp = &(*pp)->next; - } -} - -void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg) -{ - if(!sq) - return; - callback_list_remove(sq, cb_arg); - /* if callbacks() routine scheduled deletion, let it do that */ - if(!sq->cblist && !sq->to_be_deleted) { - (void)rbtree_delete(sq->outnet->serviced, sq); - serviced_delete(sq); - } -} - -/** get memory used by waiting tcp entry (in use or not) */ -static size_t -waiting_tcp_get_mem(struct waiting_tcp* w) -{ - size_t s; - if(!w) return 0; - s = sizeof(*w) + w->pkt_len; - if(w->timer) - s += comm_timer_get_mem(w->timer); - return s; -} - -/** get memory used by port if */ -static size_t -if_get_mem(struct port_if* pif) -{ - size_t s; - int i; - s = sizeof(*pif) + sizeof(int)*pif->avail_total + - sizeof(struct port_comm*)*pif->maxout; - for(i=0; i<pif->inuse; i++) - s += sizeof(*pif->out[i]) + - comm_point_get_mem(pif->out[i]->cp); - return s; -} - -/** get memory used by waiting udp */ -static size_t -waiting_udp_get_mem(struct pending* w) -{ - size_t s; - s = sizeof(*w) + comm_timer_get_mem(w->timer) + w->pkt_len; - return s; -} - -size_t outnet_get_mem(struct outside_network* outnet) -{ - size_t i; - int k; - struct waiting_tcp* w; - struct pending* u; - struct serviced_query* sq; - struct service_callback* sb; - struct port_comm* pc; - size_t s = sizeof(*outnet) + sizeof(*outnet->base) + - sizeof(*outnet->udp_buff) + - sldns_buffer_capacity(outnet->udp_buff); - /* second buffer is not ours */ - for(pc = outnet->unused_fds; pc; pc = pc->next) { - s += sizeof(*pc) + comm_point_get_mem(pc->cp); - } - for(k=0; k<outnet->num_ip4; k++) - s += if_get_mem(&outnet->ip4_ifs[k]); - for(k=0; k<outnet->num_ip6; k++) - s += if_get_mem(&outnet->ip6_ifs[k]); - for(u=outnet->udp_wait_first; u; u=u->next_waiting) - s += waiting_udp_get_mem(u); - - s += sizeof(struct pending_tcp*)*outnet->num_tcp; - for(i=0; i<outnet->num_tcp; i++) { - s += sizeof(struct pending_tcp); - s += comm_point_get_mem(outnet->tcp_conns[i]->c); - if(outnet->tcp_conns[i]->query) - s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query); - } - for(w=outnet->tcp_wait_first; w; w = w->next_waiting) - s += waiting_tcp_get_mem(w); - s += sizeof(*outnet->pending); - s += (sizeof(struct pending) + comm_timer_get_mem(NULL)) * - outnet->pending->count; - s += sizeof(*outnet->serviced); - s += outnet->svcd_overhead; - RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) { - s += sizeof(*sq) + sq->qbuflen; - for(sb = sq->cblist; sb; sb = sb->next) - s += sizeof(*sb); - } - return s; -} - -size_t -serviced_get_mem(struct serviced_query* sq) -{ - struct service_callback* sb; - size_t s; - s = sizeof(*sq) + sq->qbuflen; - for(sb = sq->cblist; sb; sb = sb->next) - s += sizeof(*sb); - if(sq->status == serviced_query_UDP_EDNS || - sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS || - sq->status == serviced_query_UDP_EDNS_FRAG || - sq->status == serviced_query_UDP_EDNS_fallback) { - s += sizeof(struct pending); - s += comm_timer_get_mem(NULL); - } else { - /* does not have size of the pkt pointer */ - /* always has a timer except on malloc failures */ - - /* these sizes are part of the main outside network mem */ - /* - s += sizeof(struct waiting_tcp); - s += comm_timer_get_mem(NULL); - */ - } - return s; -} - diff --git a/external/unbound/services/outside_network.h b/external/unbound/services/outside_network.h deleted file mode 100644 index befd512f0..000000000 --- a/external/unbound/services/outside_network.h +++ /dev/null @@ -1,567 +0,0 @@ -/* - * services/outside_network.h - listen to answers from the network - * - * 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 - * - * This file has functions to send queries to authoritative servers, - * and wait for the pending answer, with timeouts. - */ - -#ifndef OUTSIDE_NETWORK_H -#define OUTSIDE_NETWORK_H - -#include "util/rbtree.h" -#include "util/netevent.h" -#include "dnstap/dnstap_config.h" -struct pending; -struct pending_timeout; -struct ub_randstate; -struct pending_tcp; -struct waiting_tcp; -struct waiting_udp; -struct infra_cache; -struct port_comm; -struct port_if; -struct sldns_buffer; -struct serviced_query; -struct dt_env; -struct edns_option; -struct module_env; -struct module_qstate; -struct query_info; - -/** - * Send queries to outside servers and wait for answers from servers. - * Contains answer-listen sockets. - */ -struct outside_network { - /** Base for select calls */ - struct comm_base* base; - /** pointer to time in seconds */ - time_t* now_secs; - /** pointer to time in microseconds */ - struct timeval* now_tv; - - /** buffer shared by UDP connections, since there is only one - datagram at any time. */ - struct sldns_buffer* udp_buff; - /** serviced_callbacks malloc overhead when processing multiple - * identical serviced queries to the same server. */ - size_t svcd_overhead; - /** use x20 bits to encode additional ID random bits */ - int use_caps_for_id; - /** outside network wants to quit. Stop queued msgs from sent. */ - int want_to_quit; - - /** number of unwanted replies received (for statistics) */ - size_t unwanted_replies; - /** cumulative total of unwanted replies (for defense) */ - size_t unwanted_total; - /** threshold when to take defensive action. If 0 then never. */ - size_t unwanted_threshold; - /** what action to take, called when defensive action is needed */ - void (*unwanted_action)(void*); - /** user param for action */ - void* unwanted_param; - - /** linked list of available commpoints, unused file descriptors, - * for use as outgoing UDP ports. cp.fd=-1 in them. */ - struct port_comm* unused_fds; - /** if udp is done */ - int do_udp; - /** if udp is delay-closed (delayed answers do not meet closed port)*/ - int delayclose; - /** timeout for delayclose */ - struct timeval delay_tv; - - /** array of outgoing IP4 interfaces */ - struct port_if* ip4_ifs; - /** number of outgoing IP4 interfaces */ - int num_ip4; - - /** array of outgoing IP6 interfaces */ - struct port_if* ip6_ifs; - /** number of outgoing IP6 interfaces */ - int num_ip6; - - /** pending udp queries waiting to be sent out, waiting for fd */ - struct pending* udp_wait_first; - /** last pending udp query in list */ - struct pending* udp_wait_last; - - /** pending udp answers. sorted by id, addr */ - rbtree_type* pending; - /** serviced queries, sorted by qbuf, addr, dnssec */ - rbtree_type* serviced; - /** host cache, pointer but not owned by outnet. */ - struct infra_cache* infra; - /** where to get random numbers */ - struct ub_randstate* rnd; - /** ssl context to create ssl wrapped TCP with DNS connections */ - void* sslctx; -#ifdef USE_DNSTAP - /** dnstap environment */ - struct dt_env* dtenv; -#endif - /** maximum segment size of tcp socket */ - int tcp_mss; - - /** - * Array of tcp pending used for outgoing TCP connections. - * Each can be used to establish a TCP connection with a server. - * The file descriptors are -1 if they are free, and need to be - * opened for the tcp connection. Can be used for ip4 and ip6. - */ - struct pending_tcp **tcp_conns; - /** number of tcp communication points. */ - size_t num_tcp; - /** number of tcp communication points in use. */ - size_t num_tcp_outgoing; - /** list of tcp comm points that are free for use */ - struct pending_tcp* tcp_free; - /** list of tcp queries waiting for a buffer */ - struct waiting_tcp* tcp_wait_first; - /** last of waiting query list */ - struct waiting_tcp* tcp_wait_last; -}; - -/** - * Outgoing interface. Ports available and currently used are tracked - * per interface - */ -struct port_if { - /** address ready to allocate new socket (except port no). */ - struct sockaddr_storage addr; - /** length of addr field */ - socklen_t addrlen; - - /** prefix length of network address (in bits), for randomisation. - * if 0, no randomisation. */ - int pfxlen; - - /** the available ports array. These are unused. - * Only the first total-inuse part is filled. */ - int* avail_ports; - /** the total number of available ports (size of the array) */ - int avail_total; - - /** array of the commpoints currently in use. - * allocated for max number of fds, first part in use. */ - struct port_comm** out; - /** max number of fds, size of out array */ - int maxout; - /** number of commpoints (and thus also ports) in use */ - int inuse; -}; - -/** - * Outgoing commpoint for UDP port. - */ -struct port_comm { - /** next in free list */ - struct port_comm* next; - /** which port number (when in use) */ - int number; - /** interface it is used in */ - struct port_if* pif; - /** index in the out array of the interface */ - int index; - /** number of outstanding queries on this port */ - int num_outstanding; - /** UDP commpoint, fd=-1 if not in use */ - struct comm_point* cp; -}; - -/** - * A query that has an answer pending for it. - */ -struct pending { - /** redblacktree entry, key is the pending struct(id, addr). */ - rbnode_type node; - /** the ID for the query. int so that a value out of range can - * be used to signify a pending that is for certain not present in - * the rbtree. (and for which deletion is safe). */ - unsigned int id; - /** remote address. */ - struct sockaddr_storage addr; - /** length of addr field in use. */ - socklen_t addrlen; - /** comm point it was sent on (and reply must come back on). */ - struct port_comm* pc; - /** timeout event */ - struct comm_timer* timer; - /** callback for the timeout, error or reply to the message */ - comm_point_callback_type* cb; - /** callback user argument */ - void* cb_arg; - /** the outside network it is part of */ - struct outside_network* outnet; - /** the corresponding serviced_query */ - struct serviced_query* sq; - - /*---- filled if udp pending is waiting -----*/ - /** next in waiting list. */ - struct pending* next_waiting; - /** timeout in msec */ - int timeout; - /** The query itself, the query packet to send. */ - uint8_t* pkt; - /** length of query packet. */ - size_t pkt_len; -}; - -/** - * Pending TCP query to server. - */ -struct pending_tcp { - /** next in list of free tcp comm points, or NULL. */ - struct pending_tcp* next_free; - /** the ID for the query; checked in reply */ - uint16_t id; - /** tcp comm point it was sent on (and reply must come back on). */ - struct comm_point* c; - /** the query being serviced, NULL if the pending_tcp is unused. */ - struct waiting_tcp* query; -}; - -/** - * Query waiting for TCP buffer. - */ -struct waiting_tcp { - /** - * next in waiting list. - * if pkt==0, this points to the pending_tcp structure. - */ - struct waiting_tcp* next_waiting; - /** timeout event; timer keeps running whether the query is - * waiting for a buffer or the tcp reply is pending */ - struct comm_timer* timer; - /** the outside network it is part of */ - struct outside_network* outnet; - /** remote address. */ - struct sockaddr_storage addr; - /** length of addr field in use. */ - socklen_t addrlen; - /** - * The query itself, the query packet to send. - * allocated after the waiting_tcp structure. - * set to NULL when the query is serviced and it part of pending_tcp. - * if this is NULL, the next_waiting points to the pending_tcp. - */ - uint8_t* pkt; - /** length of query packet. */ - size_t pkt_len; - /** callback for the timeout, error or reply to the message */ - comm_point_callback_type* cb; - /** callback user argument */ - void* cb_arg; - /** if it uses ssl upstream */ - int ssl_upstream; -}; - -/** - * Callback to party interested in serviced query results. - */ -struct service_callback { - /** next in callback list */ - struct service_callback* next; - /** callback function */ - comm_point_callback_type* cb; - /** user argument for callback function */ - void* cb_arg; -}; - -/** fallback size for fragmentation for EDNS in IPv4 */ -#define EDNS_FRAG_SIZE_IP4 1472 -/** fallback size for EDNS in IPv6, fits one fragment with ip6-tunnel-ids */ -#define EDNS_FRAG_SIZE_IP6 1232 - -/** - * Query service record. - * Contains query and destination. UDP, TCP, EDNS are all tried. - * complete with retries and timeouts. A number of interested parties can - * receive a callback. - */ -struct serviced_query { - /** The rbtree node, key is this record */ - rbnode_type node; - /** The query that needs to be answered. Starts with flags u16, - * then qdcount, ..., including qname, qtype, qclass. Does not include - * EDNS record. */ - uint8_t* qbuf; - /** length of qbuf. */ - size_t qbuflen; - /** If an EDNS section is included, the DO/CD bit will be turned on. */ - int dnssec; - /** We want signatures, or else the answer is likely useless */ - int want_dnssec; - /** ignore capsforid */ - int nocaps; - /** tcp upstream used, use tcp, or ssl_upstream for SSL */ - int tcp_upstream, ssl_upstream; - /** where to send it */ - struct sockaddr_storage addr; - /** length of addr field in use. */ - socklen_t addrlen; - /** zone name, uncompressed domain name in wireformat */ - uint8_t* zone; - /** length of zone name */ - size_t zonelen; - /** qtype */ - int qtype; - /** current status */ - enum serviced_query_status { - /** initial status */ - serviced_initial, - /** UDP with EDNS sent */ - serviced_query_UDP_EDNS, - /** UDP without EDNS sent */ - serviced_query_UDP, - /** TCP with EDNS sent */ - serviced_query_TCP_EDNS, - /** TCP without EDNS sent */ - serviced_query_TCP, - /** probe to test EDNS lameness (EDNS is dropped) */ - serviced_query_PROBE_EDNS, - /** probe to test noEDNS0 (EDNS gives FORMERRorNOTIMP) */ - serviced_query_UDP_EDNS_fallback, - /** probe to test TCP noEDNS0 (EDNS gives FORMERRorNOTIMP) */ - serviced_query_TCP_EDNS_fallback, - /** send UDP query with EDNS1480 (or 1280) */ - serviced_query_UDP_EDNS_FRAG - } - /** variable with current status */ - status; - /** true if serviced_query is scheduled for deletion already */ - int to_be_deleted; - /** number of UDP retries */ - int retry; - /** time last UDP was sent */ - struct timeval last_sent_time; - /** rtt of last (UDP) message */ - int last_rtt; - /** do we know edns probe status already, for UDP_EDNS queries */ - int edns_lame_known; - /** edns options to use for sending upstream packet */ - struct edns_option* opt_list; - /** outside network this is part of */ - struct outside_network* outnet; - /** list of interested parties that need callback on results. */ - struct service_callback* cblist; - /** the UDP or TCP query that is pending, see status which */ - void* pending; -}; - -/** - * Create outside_network structure with N udp ports. - * @param base: the communication base to use for event handling. - * @param bufsize: size for network buffers. - * @param num_ports: number of udp ports to open per interface. - * @param ifs: interface names (or NULL for default interface). - * These interfaces must be able to access all authoritative servers. - * @param num_ifs: number of names in array ifs. - * @param do_ip4: service IP4. - * @param do_ip6: service IP6. - * @param num_tcp: number of outgoing tcp buffers to preallocate. - * @param infra: pointer to infra cached used for serviced queries. - * @param rnd: stored to create random numbers for serviced queries. - * @param use_caps_for_id: enable to use 0x20 bits to encode id randomness. - * @param availports: array of available ports. - * @param numavailports: number of available ports in array. - * @param unwanted_threshold: when to take defensive action. - * @param unwanted_action: the action to take. - * @param unwanted_param: user parameter to action. - * @param tcp_mss: maximum segment size of tcp socket. - * @param do_udp: if udp is done. - * @param sslctx: context to create outgoing connections with (if enabled). - * @param delayclose: if not 0, udp sockets are delayed before timeout closure. - * msec to wait on timeouted udp sockets. - * @param dtenv: environment to send dnstap events with (if enabled). - * @return: the new structure (with no pending answers) or NULL on error. - */ -struct outside_network* outside_network_create(struct comm_base* base, - size_t bufsize, size_t num_ports, char** ifs, int num_ifs, - int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra, - struct ub_randstate* rnd, int use_caps_for_id, int* availports, - int numavailports, size_t unwanted_threshold, int tcp_mss, - void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx, int delayclose, struct dt_env *dtenv); - -/** - * Delete outside_network structure. - * @param outnet: object to delete. - */ -void outside_network_delete(struct outside_network* outnet); - -/** - * Prepare for quit. Sends no more queries, even if queued up. - * @param outnet: object to prepare for removal - */ -void outside_network_quit_prepare(struct outside_network* outnet); - -/** - * Send UDP query, create pending answer. - * Changes the ID for the query to be random and unique for that destination. - * @param sq: serviced query. - * @param packet: wireformat query to send to destination. - * @param timeout: in milliseconds from now. - * @param callback: function to call on error, timeout or reply. - * @param callback_arg: user argument for callback function. - * @return: NULL on error for malloc or socket. Else the pending query object. - */ -struct pending* pending_udp_query(struct serviced_query* sq, - struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback, - void* callback_arg); - -/** - * Send TCP query. May wait for TCP buffer. Selects ID to be random, and - * checks id. - * @param sq: serviced query. - * @param packet: wireformat query to send to destination. copied from. - * @param timeout: in seconds from now. - * Timer starts running now. Timer may expire if all buffers are used, - * without any query been sent to the server yet. - * @param callback: function to call on error, timeout or reply. - * @param callback_arg: user argument for callback function. - * @return: false on error for malloc or socket. Else the pending TCP object. - */ -struct waiting_tcp* pending_tcp_query(struct serviced_query* sq, - struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback, - void* callback_arg); - -/** - * Delete pending answer. - * @param outnet: outside network the pending query is part of. - * Internal feature: if outnet is NULL, p is not unlinked from rbtree. - * @param p: deleted - */ -void pending_delete(struct outside_network* outnet, struct pending* p); - -/** - * Perform a serviced query to the authoritative servers. - * Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed. - * @param outnet: outside network, with rbtree of serviced queries. - * @param qinfo: query info. - * @param flags: flags u16 (host format), includes opcode, CD bit. - * @param dnssec: if set, DO bit is set in EDNS queries. - * If the value includes BIT_CD, CD bit is set when in EDNS queries. - * If the value includes BIT_DO, DO bit is set when in EDNS queries. - * @param want_dnssec: signatures are needed, without EDNS the answer is - * likely to be useless. - * @param nocaps: ignore use_caps_for_id and use unperturbed qname. - * @param tcp_upstream: use TCP for upstream queries. - * @param ssl_upstream: use SSL for upstream queries. - * @param addr: to which server to send the query. - * @param addrlen: length of addr. - * @param zone: name of the zone of the delegation point. wireformat dname. - This is the delegation point name for which the server is deemed - authoritative. - * @param zonelen: length of zone. - * @param qstate: module qstate. Mainly for inspecting the available - * edns_opts_lists. - * @param callback: callback function. - * @param callback_arg: user argument to callback function. - * @param buff: scratch buffer to create query contents in. Empty on exit. - * @param env: the module environment. - * @return 0 on error, or pointer to serviced query that is used to answer - * this serviced query may be shared with other callbacks as well. - */ -struct serviced_query* outnet_serviced_query(struct outside_network* outnet, - struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, - int nocaps, int tcp_upstream, int ssl_upstream, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* qstate, - comm_point_callback_type* callback, void* callback_arg, - struct sldns_buffer* buff, struct module_env* env); - -/** - * Remove service query callback. - * If that leads to zero callbacks, the query is completely cancelled. - * @param sq: serviced query to adjust. - * @param cb_arg: callback argument of callback that needs removal. - * same as the callback_arg to outnet_serviced_query(). - */ -void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg); - -/** - * Get memory size in use by outside network. - * Counts buffers and outstanding query (serviced queries) malloced data. - * @param outnet: outside network structure. - * @return size in bytes. - */ -size_t outnet_get_mem(struct outside_network* outnet); - -/** - * Get memory size in use by serviced query while it is servicing callbacks. - * This takes into account the pre-deleted status of it; it will be deleted - * when the callbacks are done. - * @param sq: serviced query. - * @return size in bytes. - */ -size_t serviced_get_mem(struct serviced_query* sq); - -/** callback for incoming udp answers from the network */ -int outnet_udp_cb(struct comm_point* c, void* arg, int error, - struct comm_reply *reply_info); - -/** callback for pending tcp connections */ -int outnet_tcp_cb(struct comm_point* c, void* arg, int error, - struct comm_reply *reply_info); - -/** callback for udp timeout */ -void pending_udp_timer_cb(void *arg); - -/** callback for udp delay for timeout */ -void pending_udp_timer_delay_cb(void *arg); - -/** callback for outgoing TCP timer event */ -void outnet_tcptimer(void* arg); - -/** callback for serviced query UDP answers */ -int serviced_udp_callback(struct comm_point* c, void* arg, int error, - struct comm_reply* rep); - -/** TCP reply or error callback for serviced queries */ -int serviced_tcp_callback(struct comm_point* c, void* arg, int error, - struct comm_reply* rep); - -/** compare function of pending rbtree */ -int pending_cmp(const void* key1, const void* key2); - -/** compare function of serviced query rbtree */ -int serviced_cmp(const void* key1, const void* key2); - -#endif /* OUTSIDE_NETWORK_H */ diff --git a/external/unbound/services/view.c b/external/unbound/services/view.c deleted file mode 100644 index 33f4f4986..000000000 --- a/external/unbound/services/view.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * services/view.c - named views containing local zones authority service. - * - * Copyright (c) 2016, 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 - * - * This file contains functions to enable named views that can hold local zone - * authority service. - */ -#include "config.h" -#include "services/view.h" -#include "services/localzone.h" -#include "util/config_file.h" - -int -view_cmp(const void* v1, const void* v2) -{ - struct view* a = (struct view*)v1; - struct view* b = (struct view*)v2; - - return strcmp(a->name, b->name); -} - -struct views* -views_create(void) -{ - struct views* v = (struct views*)calloc(1, - sizeof(*v)); - if(!v) - return NULL; - rbtree_init(&v->vtree, &view_cmp); - lock_rw_init(&v->lock); - lock_protect(&v->lock, &v->vtree, sizeof(v->vtree)); - return v; -} - -/** This prototype is defined in in respip.h, but we want to avoid - * unnecessary dependencies */ -void respip_set_delete(struct respip_set *set); - -void -view_delete(struct view* v) -{ - if(!v) - return; - lock_rw_destroy(&v->lock); - local_zones_delete(v->local_zones); - respip_set_delete(v->respip_set); - free(v->name); - free(v); -} - -static void -delviewnode(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - struct view* v = (struct view*)n; - view_delete(v); -} - -void -views_delete(struct views* v) -{ - if(!v) - return; - lock_rw_destroy(&v->lock); - traverse_postorder(&v->vtree, delviewnode, NULL); - free(v); -} - -/** create a new view */ -static struct view* -view_create(char* name) -{ - struct view* v = (struct view*)calloc(1, sizeof(*v)); - if(!v) - return NULL; - v->node.key = v; - if(!(v->name = strdup(name))) { - free(v); - return NULL; - } - lock_rw_init(&v->lock); - lock_protect(&v->lock, &v->name, sizeof(*v)-sizeof(rbnode_type)); - return v; -} - -/** enter a new view returns with WRlock */ -static struct view* -views_enter_view_name(struct views* vs, char* name) -{ - struct view* v = view_create(name); - if(!v) { - log_err("out of memory"); - return NULL; - } - - /* add to rbtree */ - lock_rw_wrlock(&vs->lock); - lock_rw_wrlock(&v->lock); - if(!rbtree_insert(&vs->vtree, &v->node)) { - log_warn("duplicate view: %s", name); - lock_rw_unlock(&v->lock); - view_delete(v); - lock_rw_unlock(&vs->lock); - return NULL; - } - lock_rw_unlock(&vs->lock); - return v; -} - -int -views_apply_cfg(struct views* vs, struct config_file* cfg) -{ - struct config_view* cv; - struct view* v; - struct config_file lz_cfg; - /* Check existence of name in first view (last in config). Rest of - * views are already checked when parsing config. */ - if(cfg->views && !cfg->views->name) { - log_err("view without a name"); - return 0; - } - for(cv = cfg->views; cv; cv = cv->next) { - /* create and enter view */ - if(!(v = views_enter_view_name(vs, cv->name))) - return 0; - v->isfirst = cv->isfirst; - if(cv->local_zones || cv->local_data) { - if(!(v->local_zones = local_zones_create())){ - lock_rw_unlock(&v->lock); - return 0; - } - memset(&lz_cfg, 0, sizeof(lz_cfg)); - lz_cfg.local_zones = cv->local_zones; - lz_cfg.local_data = cv->local_data; - lz_cfg.local_zones_nodefault = - cv->local_zones_nodefault; - if(!local_zones_apply_cfg(v->local_zones, &lz_cfg)){ - lock_rw_unlock(&v->lock); - return 0; - } - /* local_zones, local_zones_nodefault and local_data - * are free'd from config_view by local_zones_apply_cfg. - * Set pointers to NULL. */ - cv->local_zones = NULL; - cv->local_data = NULL; - cv->local_zones_nodefault = NULL; - } - lock_rw_unlock(&v->lock); - } - return 1; -} - -/** find a view by name */ -struct view* -views_find_view(struct views* vs, const char* name, int write) -{ - struct view* v; - struct view key; - key.node.key = &v; - key.name = (char *)name; - lock_rw_rdlock(&vs->lock); - if(!(v = (struct view*)rbtree_search(&vs->vtree, &key.node))) { - lock_rw_unlock(&vs->lock); - return 0; - } - if(write) { - lock_rw_wrlock(&v->lock); - } else { - lock_rw_rdlock(&v->lock); - } - lock_rw_unlock(&vs->lock); - return v; -} - -void views_print(struct views* v) -{ - /* TODO implement print */ - (void)v; -} diff --git a/external/unbound/services/view.h b/external/unbound/services/view.h deleted file mode 100644 index e0b346419..000000000 --- a/external/unbound/services/view.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * services/view.h - named views containing local zones authority service. - * - * Copyright (c) 2016, 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 - * - * This file contains functions to enable named views that can hold local zone - * authority service. - */ - -#ifndef SERVICES_VIEW_H -#define SERVICES_VIEW_H -#include "util/rbtree.h" -#include "util/locks.h" -struct regional; -struct config_file; -struct config_view; -struct respip_set; - - -/** - * Views storage, shared. - */ -struct views { - /** lock on the view tree */ - lock_rw_type lock; - /** rbtree of struct view */ - rbtree_type vtree; -}; - -/** - * View. Named structure holding local authority zones. - */ -struct view { - /** rbtree node, key is name */ - rbnode_type node; - /** view name. - * Has to be right after rbnode_t due to pointer arithmatic in - * view_create's lock protect */ - char* name; - /** view specific local authority zones */ - struct local_zones* local_zones; - /** response-ip configuration data for this view */ - struct respip_set* respip_set; - /** Fallback to global local_zones when there is no match in the view - * specific tree. 1 for yes, 0 for no */ - int isfirst; - /** lock on the data in the structure - * For the node and name you need to also hold the views_tree lock to - * change them. */ - lock_rw_type lock; -}; - - -/** - * Create views storage - * @return new struct or NULL on error. - */ -struct views* views_create(void); - -/** - * Delete views storage - * @param v: views to delete. - */ -void views_delete(struct views* v); - -/** - * Apply config settings; - * Takes care of locking. - * @param v: view is set up. - * @param cfg: config data. - * @return false on error. - */ -int views_apply_cfg(struct views* v, struct config_file* cfg); - -/** - * Compare two view entries in rbtree. Sort canonical. - * @param v1: view 1 - * @param v2: view 2 - * @return: negative, positive or 0 comparison value. - */ -int view_cmp(const void* v1, const void* v2); - -/** - * Delete one view - * @param v: view to delete. - */ -void view_delete(struct view* v); - -/** - * Debug helper. Print all views - * Takes care of locking. - * @param v: the views tree - */ -void views_print(struct views* v); - -/* Find a view by name. - * @param vs: views - * @param name: name of the view we are looking for - * @param write: 1 for obtaining write lock on found view, 0 for read lock - * @return: locked view or NULL. - */ -struct view* views_find_view(struct views* vs, const char* name, int write); - -#endif /* SERVICES_VIEW_H */ |