aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/services/localzone.c
diff options
context:
space:
mode:
authorErik de Castro Lopo <erikd@mega-nerd.com>2017-06-16 20:16:05 +1000
committerErik de Castro Lopo <erikd@mega-nerd.com>2017-06-17 23:04:00 +1000
commita85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch)
tree518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/services/localzone.c
parentMerge pull request #2059 (diff)
downloadmonero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
Diffstat (limited to 'external/unbound/services/localzone.c')
-rw-r--r--external/unbound/services/localzone.c797
1 files changed, 604 insertions, 193 deletions
diff --git a/external/unbound/services/localzone.c b/external/unbound/services/localzone.c
index c50ad0f15..dcce46e86 100644
--- a/external/unbound/services/localzone.c
+++ b/external/unbound/services/localzone.c
@@ -51,6 +51,12 @@
#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)
@@ -68,7 +74,7 @@ local_zones_create(void)
/** helper traverse to delete zones */
static void
-lzdel(rbnode_t* n, void* ATTR_UNUSED(arg))
+lzdel(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct local_zone* z = (struct local_zone*)n->key;
local_zone_delete(z);
@@ -93,6 +99,7 @@ local_zone_delete(struct local_zone* z)
lock_rw_destroy(&z->lock);
regional_destroy(z->region);
free(z->name);
+ free(z->taglist);
free(z);
}
@@ -152,13 +159,13 @@ local_zone_create(uint8_t* nm, size_t len, int labs,
z->namelen = len;
z->namelabs = labs;
lock_rw_init(&z->lock);
- z->region = regional_create();
+ 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_t));
+ lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_type));
/* also the zones->lock protects node, parent, name*, class */
return z;
}
@@ -170,6 +177,7 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
{
struct local_zone* z = local_zone_create(nm, len, labs, t, c);
if(!z) {
+ free(nm);
log_err("out of memory");
return NULL;
}
@@ -178,11 +186,18 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
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);
- local_zone_delete(z);
+ /* 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);
- return NULL;
+ local_zone_delete(oldz);
+ return z;
}
lock_rw_unlock(&zones->lock);
return z;
@@ -214,9 +229,8 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
return z;
}
-/** return name and class and rdata of rr; parses string */
-static int
-get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
+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)
{
@@ -269,16 +283,20 @@ get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
* 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)
+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;
}
@@ -334,15 +352,20 @@ new_local_rrset(struct regional* region, struct local_data* node,
}
/** insert RR into RRset data structure; Wastes a couple of bytes */
-static int
-insert_rr(struct regional* region, struct packed_rrset_data* pd,
- uint8_t* rdata, size_t rdata_len, time_t ttl)
+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);
@@ -432,8 +455,8 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
uint8_t rr[LDNS_RR_BUF_SIZE];
uint8_t* rdata;
size_t rdata_len;
- if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr),
- &rdata, &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;
}
@@ -453,7 +476,23 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
log_assert(node);
free(nm);
- rrset = local_data_find_type(node, rrtype);
+ /* 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)
@@ -473,7 +512,7 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
return 1;
}
- return insert_rr(z->region, pd, rdata, rdata_len, ttl);
+ 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 */
@@ -505,6 +544,123 @@ lz_enter_rr_str(struct local_zones* zones, const char* rr)
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)
@@ -592,10 +748,11 @@ 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 */
+ /* this list of zones is from RFC 6303 and RFC 7686 */
- /* block localhost level zones, first, later the LAN zones */
+ /* block localhost level zones first, then onion and later the LAN zones */
/* localhost. zone */
if(!lz_exists(zones, "localhost.") &&
@@ -653,111 +810,44 @@ lz_enter_defaults(struct local_zones* zones, struct config_file* cfg)
}
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);
+ }
- /* if unblock lan-zones, then do not add the zones below.
- * we do add the zones above, about 127.0.0.1, because localhost is
- * not on the lan. */
- if(cfg->unblock_lan_zones)
- return 1;
+ /* 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;
+}
- /* block LAN level zones */
- if ( !add_as112_default(zones, cfg, "10.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "16.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "17.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "18.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "19.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "20.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "21.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "22.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "23.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "24.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "25.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "26.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "27.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "28.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "29.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "30.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "31.172.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "168.192.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "0.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "64.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "65.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "66.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "67.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "68.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "69.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "70.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "71.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "72.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "73.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "74.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "75.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "76.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "77.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "78.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "79.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "80.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "81.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "82.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "83.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "84.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "85.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "86.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "87.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "88.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "89.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "90.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "91.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "92.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "93.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "94.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "95.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "96.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "97.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "98.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "99.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "100.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "101.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "102.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "103.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "104.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "105.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "106.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "107.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "108.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "109.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "110.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "111.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "112.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "113.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "114.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "115.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "116.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "117.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "118.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "119.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "120.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "121.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "122.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "123.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "124.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "125.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "126.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "127.100.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "254.169.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "2.0.192.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "100.51.198.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "113.0.203.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "255.255.255.255.in-addr.arpa.") ||
- !add_as112_default(zones, cfg, "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.0.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "d.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "8.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "9.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "a.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "b.e.f.ip6.arpa.") ||
- !add_as112_default(zones, cfg, "8.b.d.0.1.0.0.2.ip6.arpa.")) {
- log_err("out of memory adding default zone");
- return 0;
+/** 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;
}
@@ -791,6 +881,9 @@ init_parents(struct local_zones* zones)
break;
}
prev = node;
+
+ if(node->override_tree)
+ addr_tree_init_parents(node->override_tree);
lock_rw_unlock(&node->lock);
}
lock_rw_unlock(&zones->lock);
@@ -878,6 +971,22 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* 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)
@@ -913,6 +1022,10 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
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;
@@ -920,6 +1033,10 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
/* 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;
@@ -933,33 +1050,41 @@ struct local_zone*
local_zones_lookup(struct local_zones* zones,
uint8_t* name, size_t len, int labs, uint16_t dclass)
{
- rbnode_t* res = NULL;
+ 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;
- if(rbtree_find_less_equal(&zones->ztree, &key, &res)) {
- /* exact */
- return (struct local_zone*)res;
- } else {
- /* smaller element (or no element) */
- int m;
- result = (struct local_zone*)res;
- 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 subdomain of zone */
- if(result->namelabs <= m)
- break;
- result = result->parent;
- }
- return result;
+ 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*
@@ -1031,6 +1156,18 @@ void local_zones_print(struct local_zones* zones)
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);
@@ -1044,8 +1181,8 @@ void local_zones_print(struct local_zones* zones)
/** encode answer consisting of 1 rrset */
static int
-local_encode(struct query_info* qinfo, struct edns_data* edns,
- sldns_buffer* buf, struct regional* temp,
+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;
@@ -1064,22 +1201,155 @@ local_encode(struct query_info* qinfo, struct edns_data* edns,
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
- if(!reply_info_answer_encode(qinfo, &rep,
+ 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,
+ 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);
+ 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 query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- int labs, struct local_data** ldp)
+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;
@@ -1088,58 +1358,95 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = labs;
- if(z->type == local_zone_redirect) {
+ 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);
+ lr = local_data_find_type(ld, qinfo->qtype, 1);
if(!lr)
return 0;
- if(z->type == local_zone_redirect) {
+
+ /* 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, edns, buf, temp, &r, 1,
+ return local_encode(qinfo, env, edns, buf, temp, &r, 1,
LDNS_RCODE_NOERROR);
}
- return local_encode(qinfo, edns, buf, temp, lr->rrset, 1,
+ 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 query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- struct local_data* ld)
+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(z->type == local_zone_deny || z->type == local_zone_inform_deny) {
+ 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(z->type == local_zone_refuse) {
- error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ } 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(z->type == local_zone_static ||
- z->type == local_zone_redirect) {
+ } 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,
@@ -1147,30 +1454,30 @@ lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
* or using closest match for NSEC.
* or using closest match for returning delegation downwards
*/
- int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
+ int rcode = (ld || lz_type == local_zone_redirect)?
+ LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
if(z->soa)
- return local_encode(qinfo, edns, buf, temp,
+ return local_encode(qinfo, env, edns, buf, temp,
z->soa, 0, rcode);
- error_encode(buf, (rcode|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ local_error_encode(qinfo, env, edns, buf, temp, rcode,
+ (rcode|BIT_AA));
return 1;
- } else if(z->type == local_zone_typetransparent) {
+ } else if(lz_type == local_zone_typetransparent
+ || lz_type == local_zone_always_transparent) {
/* no NODATA or NXDOMAINS for this zone type */
return 0;
}
- /* else z->type == local_zone_transparent */
+ /* 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, edns, buf, temp,
+ return local_encode(qinfo, env, edns, buf, temp,
z->soa, 0, rcode);
- error_encode(buf, (rcode|BIT_AA), qinfo,
- *(uint16_t*)sldns_buffer_begin(buf),
- sldns_buffer_read_u16_at(buf, 2), edns);
+ local_error_encode(qinfo, env, edns, buf, temp, rcode,
+ (rcode|BIT_AA));
return 1;
}
@@ -1193,44 +1500,136 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
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 query_info* qinfo,
- struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
- struct comm_reply* repinfo)
+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;
- struct local_zone* z;
- int r;
- lock_rw_rdlock(&zones->lock);
- z = local_zones_lookup(zones, qinfo->qname,
- qinfo->qname_len, labs, qinfo->qclass);
+ 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);
- return 0;
}
- lock_rw_rdlock(&z->lock);
- lock_rw_unlock(&zones->lock);
-
- if((z->type == local_zone_inform || z->type == local_zone_inform_deny)
+ if((lzt == local_zone_inform || lzt == local_zone_inform_deny)
&& repinfo)
lz_inform_print(z, qinfo, repinfo);
- if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) {
+ 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);
- return 1;
+ /* We should tell the caller that encode is deferred if we found
+ * a local alias. */
+ return !qinfo->local_alias;
}
- r = lz_zone_answer(z, qinfo, edns, buf, temp, ld);
+ r = lz_zone_answer(z, env, qinfo, edns, buf, temp, ld, lzt);
lock_rw_unlock(&z->lock);
- return r;
+ 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";
@@ -1240,6 +1639,9 @@ const char* local_zone_type2str(enum localzone_type t)
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";
}
@@ -1262,6 +1664,12 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*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;
}
@@ -1298,7 +1706,10 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones,
{
/* create */
struct local_zone* z = local_zone_create(name, len, labs, tp, dclass);
- if(!z) return NULL;
+ if(!z) {
+ free(name);
+ return NULL;
+ }
lock_rw_wrlock(&z->lock);
/* find the closest parent */