/** 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; }
/** dump lruhash msg cache */ static int dump_msg_lruhash(SSL* ssl, struct worker* worker, struct lruhash* h) { struct lruhash_entry* e; struct query_info* k; struct reply_info* d; /* lruhash already locked by caller */ /* walk in order of lru; best first */ for(e=h->lru_start; e; e = e->lru_next) { regional_free_all(worker->scratchpad); lock_rw_rdlock(&e->lock); /* make copy of rrset in worker buffer */ if(!copy_msg(worker->scratchpad, e, &k, &d)) { lock_rw_unlock(&e->lock); return 0; } lock_rw_unlock(&e->lock); /* release lock so we can lookup the rrset references * in the rrset cache */ if(!dump_msg(ssl, k, d, *worker->env.now)) { return 0; } } return 1; }
/** 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; }
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); }
int local_zones_answer(struct local_zones* zones, 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) { /* 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; enum localzone_type lzt; int r, tag = -1; lock_rw_rdlock(&zones->lock); z = local_zones_tags_lookup(zones, qinfo->qname, qinfo->qname_len, labs, qinfo->qclass, taglist, taglen, 0); if(!z) { lock_rw_unlock(&zones->lock); return 0; } lock_rw_rdlock(&z->lock); lock_rw_unlock(&zones->lock); lzt = lz_type(taglist, taglen, z->taglist, z->taglen, tagactions, tagactionssize, z->type, repinfo, z->override_tree, &tag, tagname, num_tags); 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, qinfo, edns, buf, temp, labs, &ld, lzt, tag, tag_datas, tag_datas_size, tagname, num_tags)) { lock_rw_unlock(&z->lock); return 1; } r = lz_zone_answer(z, qinfo, edns, buf, temp, ld, lzt); lock_rw_unlock(&z->lock); return r; }
/** dump lruhash rrset cache */ static int dump_rrset_lruhash(SSL* ssl, struct lruhash* h, time_t now) { struct lruhash_entry* e; /* lruhash already locked by caller */ /* walk in order of lru; best first */ for(e=h->lru_start; e; e = e->lru_next) { lock_rw_rdlock(&e->lock); if(!dump_rrset(ssl, (struct ub_packed_rrset_key*)e->key, (struct packed_rrset_data*)e->data, now)) { lock_rw_unlock(&e->lock); return 0; } lock_rw_unlock(&e->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; }
/** 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 */ /* the line below is matched by gcc regex and silences * the fallthrough warning */ /* fallthrough */ case 1: /* ref updated, item inserted */ rep->rrsets[i] = rep->ref[i].key; } } }
int respip_rewrite_reply(const struct query_info* qinfo, const struct respip_client_info* cinfo, const struct reply_info* rep, struct reply_info** new_repp, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, struct regional* region) { const uint8_t* ctaglist; size_t ctaglen; const uint8_t* tag_actions; size_t tag_actions_size; struct config_strlist** tag_datas; size_t tag_datas_size; struct view* view = NULL; struct respip_set* ipset = NULL; size_t rrset_id = 0; enum respip_action action = respip_none; int tag = -1; const struct resp_addr* raddr = NULL; int ret = 1; struct ub_packed_rrset_key* redirect_rrset = NULL; if(!cinfo) goto done; ctaglist = cinfo->taglist; ctaglen = cinfo->taglen; tag_actions = cinfo->tag_actions; tag_actions_size = cinfo->tag_actions_size; tag_datas = cinfo->tag_datas; tag_datas_size = cinfo->tag_datas_size; view = cinfo->view; ipset = cinfo->respip_set; /** Try to use response-ip config from the view first; use * global response-ip config if we don't have the view or we don't * have the matching per-view config (and the view allows the use * of global data in this case). * Note that we lock the view even if we only use view members that * currently don't change after creation. This is for safety for * future possible changes as the view documentation seems to expect * any of its member can change in the view's lifetime. * Note also that we assume 'view' is valid in this function, which * should be safe (see unbound bug #1191) */ if(view) { lock_rw_rdlock(&view->lock); if(view->respip_set) { if((raddr = respip_addr_lookup(rep, &view->respip_set->ip_tree, &rrset_id))) { /** for per-view respip directives the action * can only be direct (i.e. not tag-based) */ action = raddr->action; } } if(!raddr && !view->isfirst) goto done; } if(!raddr && ipset && (raddr = respip_addr_lookup(rep, &ipset->ip_tree, &rrset_id))) { action = (enum respip_action)local_data_find_tag_action( raddr->taglist, raddr->taglen, ctaglist, ctaglen, tag_actions, tag_actions_size, (enum localzone_type)raddr->action, &tag, ipset->tagname, ipset->num_tags); } if(raddr && !search_only) { int result = 0; /* first, see if we have response-ip or tag action for the * action except for 'always' variants. */ if(action != respip_always_refuse && action != respip_always_transparent && action != respip_always_nxdomain && (result = respip_data_answer(raddr, action, qinfo->qtype, rep, rrset_id, new_repp, tag, tag_datas, tag_datas_size, ipset->tagname, ipset->num_tags, &redirect_rrset, region)) < 0) { ret = 0; goto done; } /* if no action data applied, take action specific to the * action without data. */ if(!result && !respip_nodata_answer(qinfo->qtype, action, rep, rrset_id, new_repp, region)) { ret = 0; goto done; } } done: if(view) { lock_rw_unlock(&view->lock); } if(ret) { /* If we're redirecting the original answer to a * CNAME, record the CNAME rrset so the caller can take * the appropriate action. Note that we don't check the * action type; it should normally be 'redirect', but it * can be of other type when a data-dependent tag action * uses redirect response-ip data. */ if(redirect_rrset && redirect_rrset->rk.type == ntohs(LDNS_RR_TYPE_CNAME) && qinfo->qtype != LDNS_RR_TYPE_ANY) *alias_rrset = redirect_rrset; /* on success, populate respip result structure */ ret = populate_action_info(actinfo, action, raddr, redirect_rrset, tag, ipset, search_only, region); } return ret; }
/** 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 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_t*)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; }
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); }