/** 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 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)) { log_warn("duplicate local-zone"); lock_rw_unlock(&z->lock); local_zone_delete(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); return z; } lock_rw_unlock(&zones->lock); return 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; }
/** 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 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); }
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; }
/* Add a new zone */ int ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name, const char *zone_type) { enum localzone_type t; struct local_zone* z; uint8_t* nm; int nmlabs; size_t nmlen; int res = ub_ctx_finalize(ctx); if (res) return res; if(!local_zone_str2type(zone_type, &t)) { return UB_SYNTAX; } if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) { return UB_SYNTAX; } lock_rw_wrlock(&ctx->local_zones->lock); if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN))) { /* already present in tree */ lock_rw_wrlock(&z->lock); z->type = t; /* update type anyway */ lock_rw_unlock(&z->lock); lock_rw_unlock(&ctx->local_zones->lock); free(nm); return UB_NOERROR; } if(!local_zones_add_zone(ctx->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN, t)) { lock_rw_unlock(&ctx->local_zones->lock); return UB_NOMEM; } lock_rw_unlock(&ctx->local_zones->lock); return UB_NOERROR; }
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); }
/** Add a new zone */ static void do_zone_add(SSL* ssl, struct worker* worker, char* arg) { uint8_t* nm; int nmlabs; size_t nmlen; char* arg2; enum localzone_type t; struct local_zone* z; if(!find_arg2(ssl, arg, &arg2)) return; if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) return; if(!local_zone_str2type(arg2, &t)) { ssl_printf(ssl, "error not a zone type. %s\n", arg2); free(nm); return; } lock_quick_lock(&worker->daemon->local_zones->lock); if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN))) { /* already present in tree */ lock_rw_wrlock(&z->lock); z->type = t; /* update type anyway */ lock_rw_unlock(&z->lock); free(nm); lock_quick_unlock(&worker->daemon->local_zones->lock); send_ok(ssl); return; } if(!local_zones_add_zone(worker->daemon->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN, t)) { lock_quick_unlock(&worker->daemon->local_zones->lock); ssl_printf(ssl, "error out of memory\n"); return; } lock_quick_unlock(&worker->daemon->local_zones->lock); send_ok(ssl); }
/* Remove zone */ int ub_ctx_zone_remove(struct ub_ctx* ctx, const char *zone_name) { struct local_zone* z; uint8_t* nm; int nmlabs; size_t nmlen; int res = ub_ctx_finalize(ctx); if (res) return res; if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) { return UB_SYNTAX; } lock_rw_wrlock(&ctx->local_zones->lock); if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN))) { /* present in tree */ local_zones_del_zone(ctx->local_zones, z); } lock_rw_unlock(&ctx->local_zones->lock); free(nm); return UB_NOERROR; }
/** 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); } }
/** 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; }