aboutsummaryrefslogtreecommitdiff
path: root/services/localzone.c
diff options
context:
space:
mode:
Diffstat (limited to 'services/localzone.c')
-rw-r--r--services/localzone.c107
1 files changed, 98 insertions, 9 deletions
diff --git a/services/localzone.c b/services/localzone.c
index cad46066334c..fd2ff2bb67f7 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -463,6 +463,48 @@ lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
return 1;
}
+/* Mark the SOA record for the zone. This only marks the SOA rrset; the data
+ * for the RR is entered later on local_zone_enter_rr() as with the other
+ * records. An artifical soa_negative record with a modified TTL (minimum of
+ * the TTL and the SOA.MINIMUM) is also created and marked for usage with
+ * negative answers and to avoid allocations during those answers. */
+static int
+lz_mark_soa_for_zone(struct local_zone* z, struct ub_packed_rrset_key* soa_rrset,
+ uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr)
+{
+ struct packed_rrset_data* pd = (struct packed_rrset_data*)
+ regional_alloc_zero(z->region, sizeof(*pd));
+ struct ub_packed_rrset_key* rrset_negative = (struct ub_packed_rrset_key*)
+ regional_alloc_zero(z->region, sizeof(*rrset_negative));
+ time_t minimum;
+ if(!rrset_negative||!pd) {
+ log_err("out of memory");
+ return 0;
+ }
+ /* Mark the original SOA record and then continue with the negative one. */
+ z->soa = soa_rrset;
+ rrset_negative->entry.key = rrset_negative;
+ pd->trust = rrset_trust_prim_noglue;
+ pd->security = sec_status_insecure;
+ rrset_negative->entry.data = pd;
+ rrset_negative->rk.dname = soa_rrset->rk.dname;
+ rrset_negative->rk.dname_len = soa_rrset->rk.dname_len;
+ rrset_negative->rk.type = soa_rrset->rk.type;
+ rrset_negative->rk.rrset_class = soa_rrset->rk.rrset_class;
+ if(!rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr))
+ return 0;
+ /* last 4 bytes are minimum ttl in network format */
+ if(pd->count == 0 || pd->rr_len[0] < 2+4)
+ return 0;
+ minimum = (time_t)sldns_read_uint32(pd->rr_data[0]+(pd->rr_len[0]-4));
+ minimum = ttl<minimum?ttl:minimum;
+ pd->ttl = minimum;
+ pd->rr_ttl[0] = minimum;
+
+ z->soa_negative = rrset_negative;
+ return 1;
+}
+
int
local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl,
@@ -502,8 +544,10 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
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;
+ if(rrtype == LDNS_RR_TYPE_SOA &&
+ !lz_mark_soa_for_zone(z, rrset->rrset, rdata, rdata_len, ttl,
+ rrstr))
+ return 0;
}
}
pd = (struct packed_rrset_data*)rrset->rrset->entry.data;
@@ -1215,7 +1259,7 @@ local_encode(struct query_info* qinfo, struct module_env* env,
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,
- repinfo, temp) || !reply_info_answer_encode(qinfo, &rep,
+ repinfo, temp, env->now_tv) || !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,
@@ -1237,7 +1281,7 @@ local_error_encode(struct query_info* qinfo, struct module_env* env,
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
- rcode, edns, repinfo, temp))
+ rcode, edns, repinfo, temp, env->now_tv))
edns->opt_list = NULL;
error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
@@ -1548,9 +1592,9 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
lz_type == local_zone_inform_redirect ||
lz_type == local_zone_always_nodata)?
LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
- if(z->soa)
+ if(z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
- z->soa, 0, rcode);
+ z->soa_negative, 0, rcode);
local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
(rcode|BIT_AA));
return 1;
@@ -1558,6 +1602,46 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
|| lz_type == local_zone_always_transparent) {
/* no NODATA or NXDOMAINS for this zone type */
return 0;
+ } else if(lz_type == local_zone_always_null) {
+ /* 0.0.0.0 or ::0 or noerror/nodata for this zone type,
+ * used for blocklists. */
+ if(qinfo->qtype == LDNS_RR_TYPE_A ||
+ qinfo->qtype == LDNS_RR_TYPE_AAAA) {
+ struct ub_packed_rrset_key lrr;
+ struct packed_rrset_data d;
+ time_t rr_ttl = 3600;
+ size_t rr_len = 0;
+ uint8_t rr_data[2+16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ uint8_t* rr_datas = rr_data;
+ memset(&lrr, 0, sizeof(lrr));
+ memset(&d, 0, sizeof(d));
+ lrr.entry.data = &d;
+ lrr.rk.dname = qinfo->qname;
+ lrr.rk.dname_len = qinfo->qname_len;
+ lrr.rk.type = htons(qinfo->qtype);
+ lrr.rk.rrset_class = htons(qinfo->qclass);
+ if(qinfo->qtype == LDNS_RR_TYPE_A) {
+ rr_len = 4;
+ sldns_write_uint16(rr_data, rr_len);
+ rr_len += 2;
+ } else {
+ rr_len = 16;
+ sldns_write_uint16(rr_data, rr_len);
+ rr_len += 2;
+ }
+ d.ttl = rr_ttl;
+ d.count = 1;
+ d.rr_len = &rr_len;
+ d.rr_data = &rr_datas;
+ d.rr_ttl = &rr_ttl;
+ return local_encode(qinfo, env, edns, repinfo, buf, temp,
+ &lrr, 1, LDNS_RCODE_NOERROR);
+ } else {
+ local_error_encode(qinfo, env, edns, repinfo, buf,
+ temp, LDNS_RCODE_NOERROR,
+ (LDNS_RCODE_NOERROR|BIT_AA));
+ }
+ return 1;
}
/* else lz_type == local_zone_transparent */
@@ -1565,9 +1649,9 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
* does not, then we should make this noerror/nodata */
if(ld && ld->rrsets) {
int rcode = LDNS_RCODE_NOERROR;
- if(z->soa)
+ if(z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
- z->soa, 0, rcode);
+ z->soa_negative, 0, rcode);
local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
(rcode|BIT_AA));
return 1;
@@ -1762,6 +1846,7 @@ const char* local_zone_type2str(enum localzone_type t)
case local_zone_always_nxdomain: return "always_nxdomain";
case local_zone_always_nodata: return "always_nodata";
case local_zone_always_deny: return "always_deny";
+ case local_zone_always_null: return "always_null";
case local_zone_noview: return "noview";
case local_zone_invalid: return "invalid";
}
@@ -1798,6 +1883,8 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*t = local_zone_always_nodata;
else if(strcmp(type, "always_deny") == 0)
*t = local_zone_always_deny;
+ else if(strcmp(type, "always_null") == 0)
+ *t = local_zone_always_null;
else if(strcmp(type, "noview") == 0)
*t = local_zone_noview;
else if(strcmp(type, "nodefault") == 0)
@@ -2000,8 +2087,10 @@ void local_zones_del_data(struct local_zones* zones,
/* no memory recycling for zone deletions ... */
d->rrsets = NULL;
/* did we delete the soa record ? */
- if(query_dname_compare(d->name, z->name) == 0)
+ if(query_dname_compare(d->name, z->name) == 0) {
z->soa = NULL;
+ z->soa_negative = NULL;
+ }
/* cleanup the empty nonterminals for this name */
del_empty_term(z, d, name, len, labs);