aboutsummaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c215
-rw-r--r--services/authzone.h5
-rw-r--r--services/cache/rrset.h4
-rw-r--r--services/listen_dnsport.c71
-rw-r--r--services/listen_dnsport.h5
-rw-r--r--services/localzone.c35
-rw-r--r--services/localzone.h6
-rw-r--r--services/mesh.c95
-rw-r--r--services/outbound_list.h2
-rw-r--r--services/outside_network.c11
-rw-r--r--services/rpz.c1743
-rw-r--r--services/rpz.h54
12 files changed, 1895 insertions, 351 deletions
diff --git a/services/authzone.c b/services/authzone.c
index e6e3a8cff9b4..e83af533dbc0 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -84,7 +84,7 @@
#define AUTH_PROBE_TIMEOUT_STOP 1000 /* msec */
/* auth transfer timeout for TCP connections, in msec */
#define AUTH_TRANSFER_TIMEOUT 10000 /* msec */
-/* auth transfer max backoff for failed tranfers and probes */
+/* auth transfer max backoff for failed transfers and probes */
#define AUTH_TRANSFER_MAX_BACKOFF 86400 /* sec */
/* auth http port number */
#define AUTH_HTTP_PORT 80
@@ -243,7 +243,7 @@ msg_add_rrset_an(struct auth_zone* z, struct regional* region,
return 1;
}
-/** add rrset to authority section (no additonal section rrsets yet) */
+/** add rrset to authority section (no additional section rrsets yet) */
static int
msg_add_rrset_ns(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node, struct auth_rrset* rrset)
@@ -1950,6 +1950,17 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
return 0;
}
+/** find the apex SOA RRset, if it exists */
+struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z)
+{
+ struct auth_data* apex;
+ struct auth_rrset* soa;
+ apex = az_find_name(z, z->name, z->namelen);
+ if(!apex) return NULL;
+ soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
+ return soa;
+}
+
/** find serial number of zone or false if none */
int
auth_zone_get_serial(struct auth_zone* z, uint32_t* serial)
@@ -3507,7 +3518,7 @@ auth_error_encode(struct query_info* qinfo, struct module_env* env,
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
rcode, edns, repinfo, temp, env->now_tv))
- edns->opt_list = NULL;
+ edns->opt_list_inplace_cb_out = NULL;
error_encode(buf, rcode|BIT_AA, qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
@@ -5347,7 +5358,9 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
edns.ext_rcode = 0;
edns.edns_version = 0;
edns.bits = EDNS_DO;
- edns.opt_list = NULL;
+ edns.opt_list_in = NULL;
+ edns.opt_list_out = NULL;
+ edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
@@ -6480,7 +6493,7 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
- /* if the result was not a successfull probe, we need
+ /* if the result was not a successful probe, we need
* to send the next one */
xfr_probe_nextmaster(xfr);
xfr_probe_send_or_end(xfr, env);
@@ -6536,7 +6549,9 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
edns.ext_rcode = 0;
edns.edns_version = 0;
edns.bits = EDNS_DO;
- edns.opt_list = NULL;
+ edns.opt_list_in = NULL;
+ edns.opt_list_out = NULL;
+ edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
@@ -7149,7 +7164,7 @@ parse_url(char* url, char** host, char** file, int* port, int* ssl)
while(p && *p == '/')
p++;
if(!p || p[0] == 0)
- *file = strdup("index.html");
+ *file = strdup("/");
else *file = strdup(p);
if(!*file) {
log_err("malloc failure");
@@ -7683,7 +7698,7 @@ static void auth_zone_log(uint8_t* name, enum verbosity_value level,
static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* node,
- struct auth_rrset* rrset, char** why_bogus)
+ struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg)
{
struct ub_packed_rrset_key pk;
enum sec_status sec;
@@ -7711,7 +7726,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
auth_zone_log(z->name, VERB_ALGO,
"zonemd: verify %s RRset with DNSKEY", typestr);
}
- sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, NULL, why_bogus,
+ sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus,
LDNS_SECTION_ANSWER, NULL);
if(sec == sec_status_secure) {
return 1;
@@ -7755,7 +7770,7 @@ static int nsec3_of_param_has_type(struct auth_rrset* nsec3, int algo,
static int zonemd_check_dnssec_absence(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
- char** reason, char** why_bogus)
+ char** reason, char** why_bogus, uint8_t* sigalg)
{
struct auth_rrset* nsec = NULL;
if(!apex) {
@@ -7767,7 +7782,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z,
struct ub_packed_rrset_key pk;
/* dnssec verify the NSEC */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
- nsec, why_bogus)) {
+ nsec, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for NSEC RRset";
return 0;
}
@@ -7810,7 +7825,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z,
}
/* dnssec verify the NSEC3 */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, match,
- nsec3, why_bogus)) {
+ nsec3, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for NSEC3 RRset";
return 0;
}
@@ -7831,7 +7846,8 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z,
static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
- struct auth_rrset* zonemd_rrset, char** reason, char** why_bogus)
+ struct auth_rrset* zonemd_rrset, char** reason, char** why_bogus,
+ uint8_t* sigalg)
{
struct auth_rrset* soa;
if(!apex) {
@@ -7844,12 +7860,12 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, soa,
- why_bogus)) {
+ why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for SOA RRset";
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
- zonemd_rrset, why_bogus)) {
+ zonemd_rrset, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for ZONEMD RRset";
return 0;
}
@@ -7908,12 +7924,14 @@ static void auth_zone_zonemd_fail(struct auth_zone* z, struct module_env* env,
* @param is_insecure: if true, the dnskey is not used, the zone is insecure.
* And dnssec is not used. It is DNSSEC secure insecure or not under
* a trust anchor.
+ * @param sigalg: if nonNULL provide algorithm downgrade protection.
+ * Otherwise one algorithm is enough. Must have space of ALGO_NEEDS_MAX+1.
* @param result: if not NULL result reason copied here.
*/
static void
auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, struct ub_packed_rrset_key* dnskey,
- int is_insecure, char** result)
+ int is_insecure, char** result, uint8_t* sigalg)
{
char* reason = NULL, *why_bogus = NULL;
struct auth_data* apex = NULL;
@@ -7943,7 +7961,7 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
} else if(!zonemd_rrset && dnskey && !is_insecure) {
/* fetch, DNSSEC verify, and check NSEC/NSEC3 */
if(!zonemd_check_dnssec_absence(z, env, mods, dnskey, apex,
- &reason, &why_bogus)) {
+ &reason, &why_bogus, sigalg)) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
@@ -7951,7 +7969,7 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
} else if(zonemd_rrset && dnskey && !is_insecure) {
/* check DNSSEC verify of SOA and ZONEMD */
if(!zonemd_check_dnssec_soazonemd(z, env, mods, dnskey, apex,
- zonemd_rrset, &reason, &why_bogus)) {
+ zonemd_rrset, &reason, &why_bogus, sigalg)) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
@@ -8065,15 +8083,78 @@ zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env,
return NULL;
}
+/** verify the DNSKEY from the zone with looked up DS record */
+static struct ub_packed_rrset_key*
+auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
+ struct module_env* env, struct module_stack* mods,
+ struct ub_packed_rrset_key* ds, int* is_insecure, char** why_bogus,
+ struct ub_packed_rrset_key* keystorage, uint8_t* sigalg)
+{
+ struct auth_data* apex;
+ struct auth_rrset* dnskey_rrset;
+ enum sec_status sec;
+ struct val_env* ve;
+ int m;
+
+ /* fetch DNSKEY from zone data */
+ apex = az_find_name(z, z->name, z->namelen);
+ if(!apex) {
+ *why_bogus = "in verifywithDS, zone has no apex";
+ return NULL;
+ }
+ dnskey_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_DNSKEY);
+ if(!dnskey_rrset || dnskey_rrset->data->count==0) {
+ *why_bogus = "in verifywithDS, zone has no DNSKEY";
+ return NULL;
+ }
+
+ m = modstack_find(mods, "validator");
+ if(m == -1) {
+ *why_bogus = "in verifywithDS, have no validator module";
+ return NULL;
+ }
+ ve = (struct val_env*)env->modinfo[m];
+
+ memset(keystorage, 0, sizeof(*keystorage));
+ keystorage->entry.key = keystorage;
+ keystorage->entry.data = dnskey_rrset->data;
+ keystorage->rk.dname = apex->name;
+ keystorage->rk.dname_len = apex->namelen;
+ keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY);
+ keystorage->rk.rrset_class = htons(z->dclass);
+ auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
+ sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
+ why_bogus, NULL);
+ regional_free_all(env->scratch);
+ if(sec == sec_status_secure) {
+ /* success */
+ return keystorage;
+ } else if(sec == sec_status_insecure) {
+ /* insecure */
+ *is_insecure = 1;
+ } else {
+ /* bogus */
+ *is_insecure = 0;
+ if(*why_bogus == NULL)
+ *why_bogus = "verify failed";
+ auth_zone_log(z->name, VERB_ALGO,
+ "zonemd: verify DNSKEY RRset with DS failed: %s",
+ *why_bogus);
+ }
+ return NULL;
+}
+
/** callback for ZONEMD lookup of DNSKEY */
void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
enum sec_status sec, char* why_bogus, int ATTR_UNUSED(was_ratelimited))
{
struct auth_zone* z = (struct auth_zone*)arg;
struct module_env* env;
- char* reason = NULL;
- struct ub_packed_rrset_key* dnskey = NULL;
- int is_insecure = 0;
+ char* reason = NULL, *ds_bogus = NULL, *typestr="DNSKEY";
+ struct ub_packed_rrset_key* dnskey = NULL, *ds = NULL;
+ int is_insecure = 0, downprot;
+ struct ub_packed_rrset_key keystorage;
+ uint8_t sigalg[ALGO_NEEDS_MAX+1];
lock_rw_wrlock(&z->lock);
env = z->zonemd_callback_env;
@@ -8084,16 +8165,22 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
lock_rw_unlock(&z->lock);
return; /* stop on quit */
}
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DS)
+ typestr = "DS";
+ downprot = env->cfg->harden_algo_downgrade;
/* process result */
if(sec == sec_status_bogus) {
reason = why_bogus;
- if(!reason)
- reason = "lookup of DNSKEY was bogus";
+ if(!reason) {
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
+ reason = "lookup of DNSKEY was bogus";
+ else reason = "lookup of DS was bogus";
+ }
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was bogus: %s", reason);
+ "zonemd lookup of %s was bogus: %s", typestr, reason);
} else if(rcode == LDNS_RCODE_NOERROR) {
- uint16_t wanted_qtype = LDNS_RR_TYPE_DNSKEY;
+ uint16_t wanted_qtype = z->zonemd_callback_qtype;
struct regional* temp = env->scratch;
struct query_info rq;
struct reply_info* rep;
@@ -8106,25 +8193,29 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
struct ub_packed_rrset_key* answer =
reply_find_answer_rrset(&rq, rep);
if(answer && sec == sec_status_secure) {
- dnskey = answer;
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
+ dnskey = answer;
+ else ds = answer;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was secure");
+ "zonemd lookup of %s was secure", typestr);
} else if(sec == sec_status_secure && !answer) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY has no content, but is secure, treat as insecure");
+ "zonemd lookup of %s has no content, but is secure, treat as insecure", typestr);
} else if(sec == sec_status_insecure) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was insecure");
+ "zonemd lookup of %s was insecure", typestr);
} else if(sec == sec_status_indeterminate) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was indeterminate, treat as insecure");
+ "zonemd lookup of %s was indeterminate, treat as insecure", typestr);
} else {
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY has nodata");
- reason = "lookup of DNSKEY has nodata";
+ "zonemd lookup of %s has nodata", typestr);
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
+ reason = "lookup of DNSKEY has nodata";
+ else reason = "lookup of DS has nodata";
}
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
@@ -8137,40 +8228,52 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
* trust, as insecure. */
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was secure NXDOMAIN, treat as insecure");
+ "zonemd lookup of %s was secure NXDOMAIN, treat as insecure", typestr);
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN &&
sec == sec_status_insecure) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was insecure NXDOMAIN, treat as insecure");
+ "zonemd lookup of %s was insecure NXDOMAIN, treat as insecure", typestr);
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN &&
sec == sec_status_indeterminate) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY was indeterminate NXDOMAIN, treat as insecure");
+ "zonemd lookup of %s was indeterminate NXDOMAIN, treat as insecure", typestr);
} else {
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY has no answer");
- reason = "lookup of DNSKEY has no answer";
+ "zonemd lookup of %s has no answer", typestr);
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
+ reason = "lookup of DNSKEY has no answer";
+ else reason = "lookup of DS has no answer";
}
} else {
auth_zone_log(z->name, VERB_ALGO,
- "zonemd lookup of DNSKEY failed");
- reason = "lookup of DNSKEY failed";
+ "zonemd lookup of %s failed", typestr);
+ if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
+ reason = "lookup of DNSKEY failed";
+ else reason = "lookup of DS failed";
+ }
+
+ if(!reason && !is_insecure && !dnskey && ds) {
+ dnskey = auth_zone_verify_zonemd_key_with_ds(z, env,
+ &env->mesh->mods, ds, &is_insecure, &ds_bogus,
+ &keystorage, downprot?sigalg:NULL);
+ if(!dnskey && !is_insecure && !reason)
+ reason = "DNSKEY verify with DS failed";
}
if(reason) {
- auth_zone_zonemd_fail(z, env, reason, NULL, NULL);
+ auth_zone_zonemd_fail(z, env, reason, ds_bogus, NULL);
lock_rw_unlock(&z->lock);
return;
}
auth_zone_verify_zonemd_with_key(z, env, &env->mesh->mods, dnskey,
- is_insecure, NULL);
+ is_insecure, NULL, downprot?sigalg:NULL);
regional_free_all(env->scratch);
lock_rw_unlock(&z->lock);
}
@@ -8183,14 +8286,21 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
uint16_t qflags = BIT_RD;
struct edns_data edns;
sldns_buffer* buf = env->scratch_buffer;
+ int fetch_ds = 0;
+ if(!z->fallback_enabled) {
+ /* we cannot actually get the DNSKEY, because it is in the
+ * zone we have ourselves, and it is not served yet
+ * (possibly), so fetch type DS */
+ fetch_ds = 1;
+ }
if(z->zonemd_callback_env) {
/* another worker is already working on the callback
* for the DNSKEY lookup for ZONEMD verification.
* We do not also have to do ZONEMD verification, let that
* worker do it */
auth_zone_log(z->name, VERB_ALGO,
- "zonemd needs lookup of DNSKEY and that already worked on by another worker");
+ "zonemd needs lookup of %s and that already is worked on by another worker", (fetch_ds?"DS":"DNSKEY"));
return 1;
}
@@ -8199,21 +8309,26 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
qinfo.qname_len = z->namelen;
qinfo.qname = z->name;
qinfo.qclass = z->dclass;
- qinfo.qtype = LDNS_RR_TYPE_DNSKEY;
+ if(fetch_ds)
+ qinfo.qtype = LDNS_RR_TYPE_DS;
+ else qinfo.qtype = LDNS_RR_TYPE_DNSKEY;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(z->name, buf2);
- snprintf(buf1, sizeof(buf1), "auth zone %s: lookup DNSKEY "
- "for zonemd verification", buf2);
+ snprintf(buf1, sizeof(buf1), "auth zone %s: lookup %s "
+ "for zonemd verification", buf2,
+ (fetch_ds?"DS":"DNSKEY"));
log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = 0;
edns.bits = EDNS_DO;
- edns.opt_list = NULL;
+ edns.opt_list_in = NULL;
+ edns.opt_list_out = NULL;
+ edns.opt_list_inplace_cb_out = NULL;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
@@ -8221,12 +8336,14 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
/* store the worker-specific module env for the callback.
* We can then reference this when the callback executes */
z->zonemd_callback_env = env;
+ z->zonemd_callback_qtype = qinfo.qtype;
/* the callback can be called straight away */
lock_rw_unlock(&z->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
&auth_zonemd_dnskey_lookup_callback, z)) {
lock_rw_wrlock(&z->lock);
- log_err("out of memory lookup up dnskey for zonemd");
+ log_err("out of memory lookup of %s for zonemd",
+ (fetch_ds?"DS":"DNSKEY"));
return 0;
}
lock_rw_wrlock(&z->lock);
@@ -8245,6 +8362,8 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
* If not present check if absence is allowed by DNSSEC */
if(!z->zonemd_check)
return;
+ if(z->data.count == 0)
+ return; /* no data */
/* if zone is under a trustanchor */
/* is it equal to trustanchor - get dnskey's verified */
@@ -8298,7 +8417,7 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
}
auth_zone_verify_zonemd_with_key(z, env, mods, dnskey, is_insecure,
- result);
+ result, NULL);
regional_free_all(env->scratch);
}
diff --git a/services/authzone.h b/services/authzone.h
index ffe234d59b53..d24e569d3b85 100644
--- a/services/authzone.h
+++ b/services/authzone.h
@@ -143,6 +143,8 @@ struct auth_zone {
* worker has already picked up the zonemd verification task and
* this worker does not have to do it as well. */
struct module_env* zonemd_callback_env;
+ /** for the zonemd callback, the type of data looked up */
+ uint16_t zonemd_callback_qtype;
/** zone has been deleted */
int zone_deleted;
/** deletelist pointer, unused normally except during delete */
@@ -634,6 +636,9 @@ int auth_zones_startprobesequence(struct auth_zones* az,
/** read auth zone from zonefile. caller must lock zone. false on failure */
int auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg);
+/** find the apex SOA RRset, if it exists. NULL if no SOA RRset. */
+struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z);
+
/** find serial number of zone or false if none (no SOA record) */
int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial);
diff --git a/services/cache/rrset.h b/services/cache/rrset.h
index 35a0d732b048..7c36d4032ebc 100644
--- a/services/cache/rrset.h
+++ b/services/cache/rrset.h
@@ -120,7 +120,7 @@ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
* 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
+ * After 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).
@@ -143,7 +143,7 @@ int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
* @param rrset: which rrset to cache as wildcard. This rrset is left
* untouched.
* @param ce: the closest encloser, will be uses to generate the wildcard dname.
- * @param ce_len: the closest encloser lenght.
+ * @param ce_len: the closest encloser length.
* @param alloc: how to allocate (and deallocate) the special rrset key.
* @param timenow: current time (to see if ttl in cache is expired).
*/
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index b43def567501..6a33fbcdaf7e 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -869,9 +869,14 @@ set_ip_dscp(int socket, int addrfamily, int dscp)
ds = dscp << 2;
switch(addrfamily) {
case AF_INET6:
- if(setsockopt(socket, IPPROTO_IPV6, IPV6_TCLASS, (void*)&ds, sizeof(ds)) < 0)
+ #ifdef IPV6_TCLASS
+ if(setsockopt(socket, IPPROTO_IPV6, IPV6_TCLASS, (void*)&ds,
+ sizeof(ds)) < 0)
return sock_strerror(errno);
break;
+ #else
+ return "IPV6_TCLASS not defined on this system";
+ #endif
default:
if(setsockopt(socket, IPPROTO_IP, IP_TOS, (void*)&ds, sizeof(ds)) < 0)
return sock_strerror(errno);
@@ -1306,6 +1311,38 @@ listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
return 1;
}
+void listen_setup_locks(void)
+{
+ if(!stream_wait_lock_inited) {
+ lock_basic_init(&stream_wait_count_lock);
+ stream_wait_lock_inited = 1;
+ }
+ if(!http2_query_buffer_lock_inited) {
+ lock_basic_init(&http2_query_buffer_count_lock);
+ http2_query_buffer_lock_inited = 1;
+ }
+ if(!http2_response_buffer_lock_inited) {
+ lock_basic_init(&http2_response_buffer_count_lock);
+ http2_response_buffer_lock_inited = 1;
+ }
+}
+
+void listen_desetup_locks(void)
+{
+ if(stream_wait_lock_inited) {
+ stream_wait_lock_inited = 0;
+ lock_basic_destroy(&stream_wait_count_lock);
+ }
+ if(http2_query_buffer_lock_inited) {
+ http2_query_buffer_lock_inited = 0;
+ lock_basic_destroy(&http2_query_buffer_count_lock);
+ }
+ if(http2_response_buffer_lock_inited) {
+ http2_response_buffer_lock_inited = 0;
+ lock_basic_destroy(&http2_response_buffer_count_lock);
+ }
+}
+
struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
@@ -1327,18 +1364,6 @@ listen_create(struct comm_base* base, struct listen_port* ports,
free(front);
return NULL;
}
- if(!stream_wait_lock_inited) {
- lock_basic_init(&stream_wait_count_lock);
- stream_wait_lock_inited = 1;
- }
- if(!http2_query_buffer_lock_inited) {
- lock_basic_init(&http2_query_buffer_count_lock);
- http2_query_buffer_lock_inited = 1;
- }
- if(!http2_response_buffer_lock_inited) {
- lock_basic_init(&http2_response_buffer_count_lock);
- http2_response_buffer_lock_inited = 1;
- }
/* create comm points as needed */
while(ports) {
@@ -1454,18 +1479,6 @@ listen_delete(struct listen_dnsport* front)
#endif
sldns_buffer_free(front->udp_buff);
free(front);
- if(stream_wait_lock_inited) {
- stream_wait_lock_inited = 0;
- lock_basic_destroy(&stream_wait_count_lock);
- }
- if(http2_query_buffer_lock_inited) {
- http2_query_buffer_lock_inited = 0;
- lock_basic_destroy(&http2_query_buffer_count_lock);
- }
- if(http2_response_buffer_lock_inited) {
- http2_response_buffer_lock_inited = 0;
- lock_basic_destroy(&http2_response_buffer_count_lock);
- }
}
#ifdef HAVE_GETIFADDRS
@@ -2610,7 +2623,7 @@ static int http2_req_begin_headers_cb(nghttp2_session* session,
int ret;
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
- /* only interrested in request headers */
+ /* only interested in request headers */
return 0;
}
if(!(h2_stream = http2_stream_create(frame->hd.stream_id))) {
@@ -2738,7 +2751,7 @@ static int http2_req_header_cb(nghttp2_session* session,
* the HEADER */
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
- /* only interrested in request headers */
+ /* only interested in request headers */
return 0;
}
if(!(h2_stream = nghttp2_session_get_stream_user_data(session,
@@ -2834,7 +2847,7 @@ static int http2_req_header_cb(nghttp2_session* session,
h2_stream->query_too_large = 1;
return 0;
}
- /* guaranteed to only contian digits and be null terminated */
+ /* guaranteed to only contain digits and be null terminated */
h2_stream->content_length = atoi((const char*)value);
if(h2_stream->content_length >
h2_session->c->http2_stream_max_qbuffer_size) {
@@ -2874,7 +2887,7 @@ static int http2_req_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session),
/* setting this to msg-buffer-size can result in a lot
* of memory consuption. Most queries should fit in a
* single DATA frame, and most POST queries will
- * containt content-length which does not impose this
+ * contain content-length which does not impose this
* limit. */
qlen = len;
}
diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h
index 1e51be9bfcab..0e63236bcbce 100644
--- a/services/listen_dnsport.h
+++ b/services/listen_dnsport.h
@@ -199,6 +199,11 @@ listen_create(struct comm_base* base, struct listen_port* ports,
*/
void listen_delete(struct listen_dnsport* listen);
+/** setup the locks for the listen ports */
+void listen_setup_locks(void);
+/** desetup the locks for the listen ports */
+void listen_desetup_locks(void);
+
/**
* delete listen_list of commpoints. Calls commpointdelete() on items.
* This may close the fds or not depending on flags.
diff --git a/services/localzone.c b/services/localzone.c
index 54f55ab810e4..77d0107f9f6f 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -465,7 +465,7 @@ lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
/* 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
+ * records. An artificial 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
@@ -898,6 +898,11 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg
}
lock_rw_unlock(&z->lock);
}
+ /* home.arpa. zone (RFC 8375) */
+ if(!add_empty_default(zones, cfg, "home.arpa.")) {
+ log_err("out of memory adding default zone");
+ return 0;
+ }
/* onion. zone (RFC 7686) */
if(!add_empty_default(zones, cfg, "onion.")) {
log_err("out of memory adding default zone");
@@ -1294,7 +1299,7 @@ local_error_encode(struct query_info* qinfo, struct module_env* env,
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
rcode, edns, repinfo, temp, env->now_tv))
- edns->opt_list = NULL;
+ edns->opt_list_inplace_cb_out = NULL;
error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
@@ -1521,7 +1526,7 @@ local_data_answer(struct local_zone* z, struct module_env* env,
/* write qname */
memmove(d->rr_data[0] + sizeof(uint16_t), qinfo->qname,
qinfo->qname_len - 1);
- /* write cname target wilcard wildcard label */
+ /* write cname target wildcard label */
memmove(d->rr_data[0] + sizeof(uint16_t) +
qinfo->qname_len - 1, ctarget + 2,
ctargetlen - 2);
@@ -1570,6 +1575,15 @@ local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo,
return (lr == NULL);
}
+static inline int
+local_zone_is_udp_query(struct comm_reply* repinfo) {
+ return repinfo != NULL
+ ? (repinfo->c != NULL
+ ? repinfo->c->type == comm_udp
+ : 0)
+ : 0;
+}
+
int
local_zones_zone_answer(struct local_zone* z, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
@@ -1592,7 +1606,9 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect ||
lz_type == local_zone_always_nxdomain ||
- lz_type == local_zone_always_nodata) {
+ lz_type == local_zone_always_nodata ||
+ (lz_type == local_zone_truncate
+ && local_zone_is_udp_query(repinfo))) {
/* for static, reply nodata or nxdomain
* for redirect, reply nodata */
/* no additional section processing,
@@ -1602,9 +1618,11 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env,
*/
int rcode = (ld || lz_type == local_zone_redirect ||
lz_type == local_zone_inform_redirect ||
- lz_type == local_zone_always_nodata)?
+ lz_type == local_zone_always_nodata ||
+ lz_type == local_zone_truncate)?
LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
- if(z->soa && z->soa_negative)
+ rcode = (lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode);
+ if(z != NULL && z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
z->soa_negative, 0, rcode);
local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
@@ -1661,7 +1679,7 @@ 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 && z->soa_negative)
+ if(z != NULL && z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
z->soa_negative, 0, rcode);
local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
@@ -1860,6 +1878,7 @@ const char* local_zone_type2str(enum localzone_type t)
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_truncate: return "truncate";
case local_zone_invalid: return "invalid";
}
return "badtyped";
@@ -1899,6 +1918,8 @@ int local_zone_str2type(const char* type, enum localzone_type* t)
*t = local_zone_always_null;
else if(strcmp(type, "noview") == 0)
*t = local_zone_noview;
+ else if(strcmp(type, "truncate") == 0)
+ *t = local_zone_truncate;
else if(strcmp(type, "nodefault") == 0)
*t = local_zone_nodefault;
else return 0;
diff --git a/services/localzone.h b/services/localzone.h
index b52d81dc72cb..19534f7509ed 100644
--- a/services/localzone.h
+++ b/services/localzone.h
@@ -101,6 +101,8 @@ enum localzone_type {
local_zone_always_null,
/** answer not from the view, but global or no-answer */
local_zone_noview,
+ /** truncate the response; client should retry via tcp */
+ local_zone_truncate,
/** Invalid type, cannot be used to generate answer */
local_zone_invalid
};
@@ -255,7 +257,7 @@ void local_zone_delete(struct local_zone* z);
* @param dclass: class to lookup.
* @param dtype: type to lookup, if type DS a zone higher is used for zonecuts.
* @param taglist: taglist to lookup.
- * @param taglen: lenth of taglist.
+ * @param taglen: length 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.
@@ -563,6 +565,8 @@ enum respip_action {
respip_always_nodata = local_zone_always_nodata,
/** answer with nodata response */
respip_always_deny = local_zone_always_deny,
+ /** RPZ: truncate answer in order to force switch to tcp */
+ respip_truncate = local_zone_truncate,
/* The rest of the values are only possible as
* access-control-tag-action */
diff --git a/services/mesh.c b/services/mesh.c
index 5679a8b64e58..cdcfedda270c 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -461,7 +461,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
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 unique = unique_mesh_state(edns->opt_list_in, mesh->env);
int was_detached = 0;
int was_noreply = 0;
int added = 0;
@@ -505,7 +505,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
log_err("mesh_state_create: out of memory; SERVFAIL");
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL,
LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv))
- edns->opt_list = NULL;
+ edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
@@ -514,14 +514,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
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,
+ if(edns->opt_list_in) {
+ s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
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, rep, mesh->env->scratch, mesh->env->now_tv))
- edns->opt_list = NULL;
+ edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
@@ -594,7 +594,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
servfail_mem:
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv))
- edns->opt_list = NULL;
+ edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
@@ -609,7 +609,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
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 unique = unique_mesh_state(edns->opt_list_in, mesh->env);
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
int was_detached = 0;
@@ -632,8 +632,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
}
if(unique)
mesh_state_make_unique(s);
- if(edns->opt_list) {
- s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list,
+ if(edns->opt_list_in) {
+ s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
return 0;
@@ -1145,11 +1145,11 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
if(rcode == LDNS_RCODE_SERVFAIL) {
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, rcode, &r->edns, NULL, m->s.region, start_time))
- r->edns.opt_list = NULL;
+ r->edns.opt_list_inplace_cb_out = NULL;
} else {
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
&r->edns, NULL, m->s.region, start_time))
- r->edns.opt_list = NULL;
+ r->edns.opt_list_inplace_cb_out = NULL;
}
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL,
@@ -1183,6 +1183,22 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
m->s.env->mesh->num_reply_addrs--;
}
+static inline int
+mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m)
+{
+ struct respip_action_info const* respip_info = m->s.respip_action_info;
+ return respip_info == NULL
+ ? 0
+ : (respip_info->rpz_used
+ && !respip_info->rpz_disabled
+ && respip_info->action == respip_truncate);
+}
+
+static inline int
+mesh_is_udp(struct mesh_reply const* r) {
+ return r->query_reply.c->type == comm_udp;
+}
+
/**
* Send reply to mesh reply entry
* @param m: mesh state to send it for.
@@ -1201,15 +1217,17 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
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;
/* briefly set the replylist to null in case the
* meshsendreply calls tcpreqinfo sendreply that
* comm_point_drops because of size, and then the
* null stops the mesh state remove and thus
* reply_list modification and accounting */
struct mesh_reply* rlist = m->reply_list;
+
+ /* rpz: apply actions */
+ rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m)
+ ? (rcode|BIT_TC) : rcode;
+
/* examine security status */
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
m->s.env->cfg->ignore_cd) && rep &&
@@ -1248,8 +1266,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
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) {
+ edns_opt_list_compare(prev->edns.opt_list_out, r->edns.opt_list_out) == 0 &&
+ edns_opt_list_compare(prev->edns.opt_list_inplace_cb_out, r->edns.opt_list_inplace_cb_out) == 0
+ ) {
/* if the previous reply is identical to this one, fix ID */
if(prev_buffer != r_buffer)
sldns_buffer_copy(r_buffer, prev_buffer);
@@ -1265,11 +1284,11 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
if(rcode == LDNS_RCODE_SERVFAIL) {
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, rcode, &r->edns, &r->query_reply, m->s.region, &r->start_time))
- r->edns.opt_list = NULL;
+ r->edns.opt_list_inplace_cb_out = NULL;
} else {
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
&r->edns, &r->query_reply, m->s.region, &r->start_time))
- r->edns.opt_list = NULL;
+ r->edns.opt_list_inplace_cb_out = NULL;
}
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
@@ -1286,9 +1305,6 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
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, &r->query_reply, m->s.region, &r->start_time) ||
- !apply_edns_options(&r->edns, &edns_bak,
- m->s.env->cfg, r->query_reply.c,
- m->s.region) ||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
@@ -1296,11 +1312,10 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
{
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time))
- r->edns.opt_list = NULL;
+ r->edns.opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
- r->edns = edns_bak;
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
@@ -1346,7 +1361,7 @@ void mesh_query_done(struct mesh_state* mstate)
}
if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) {
- /* we are SERVFAILing; check for expired asnwer here */
+ /* we are SERVFAILing; check for expired answer here */
mesh_serve_expired_callback(mstate);
if((mstate->reply_list || mstate->cb_list)
&& mstate->s.env->cfg->log_servfail
@@ -1488,12 +1503,15 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
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;
- }
+ if(edns->opt_list_in && !(r->edns.opt_list_in =
+ edns_opt_copy_region(edns->opt_list_in, s->s.region)))
+ return 0;
+ if(edns->opt_list_out && !(r->edns.opt_list_out =
+ edns_opt_copy_region(edns->opt_list_out, s->s.region)))
+ return 0;
+ if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out =
+ edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region)))
+ return 0;
r->qid = qid;
r->qflags = qflags;
r->next = s->cb_list;
@@ -1512,12 +1530,15 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
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;
- }
+ if(edns->opt_list_in && !(r->edns.opt_list_in =
+ edns_opt_copy_region(edns->opt_list_in, s->s.region)))
+ return 0;
+ if(edns->opt_list_out && !(r->edns.opt_list_out =
+ edns_opt_copy_region(edns->opt_list_out, s->s.region)))
+ return 0;
+ if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out =
+ edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region)))
+ return 0;
r->qid = qid;
r->qflags = qflags;
r->start_time = *s->s.env->now_tv;
@@ -1563,7 +1584,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
return 0;
/* the rrset is not packed, like in the cache, but it is
- * individualy allocated with an allocator from localzone. */
+ * individually allocated with an allocator from localzone. */
d = regional_alloc_zero(s->s.region, sizeof(*d));
if(!d)
return 0;
diff --git a/services/outbound_list.h b/services/outbound_list.h
index ad59e42d1929..73c137d50993 100644
--- a/services/outbound_list.h
+++ b/services/outbound_list.h
@@ -79,7 +79,7 @@ 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.
+ * is callers responsibility to delete.
*/
void outbound_list_clear(struct outbound_list* list);
diff --git a/services/outside_network.c b/services/outside_network.c
index a3f982e72185..f4a5d0707845 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -1935,7 +1935,7 @@ select_id(struct outside_network* outnet, struct pending* pend,
LDNS_ID_SET(sldns_buffer_begin(packet), pend->id);
id_tries++;
if(id_tries == MAX_ID_RETRY) {
- pend->id=99999; /* non existant ID */
+ pend->id=99999; /* non existent ID */
log_err("failed to generate unique ID, drop msg");
return 0;
}
@@ -1962,6 +1962,7 @@ static int udp_connect_needs_log(int err)
case ENETDOWN:
# endif
case EPERM:
+ case EACCES:
if(verbosity >= VERB_ALGO)
return 1;
return 0;
@@ -2708,7 +2709,9 @@ serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns)
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
- edns.opt_list = sq->opt_list;
+ edns.opt_list_in = NULL;
+ edns.opt_list_out = sq->opt_list;
+ edns.opt_list_inplace_cb_out = NULL;
if(sq->status == serviced_query_UDP_EDNS_FRAG) {
if(addr_is_ip6(&sq->addr, sq->addrlen)) {
if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
@@ -2731,8 +2734,8 @@ serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns)
padding_option.opt_code = LDNS_EDNS_PADDING;
padding_option.opt_len = 0;
padding_option.opt_data = NULL;
- padding_option.next = edns.opt_list;
- edns.opt_list = &padding_option;
+ padding_option.next = edns.opt_list_out;
+ edns.opt_list_out = &padding_option;
edns.padding_block_size = sq->padding_block_size;
}
attach_edns_record(buff, &edns);
diff --git a/services/rpz.c b/services/rpz.c
index 3a1ec00d7d38..d408f9383d24 100644
--- a/services/rpz.c
+++ b/services/rpz.c
@@ -50,45 +50,50 @@
#include "util/data/dname.h"
#include "util/locks.h"
#include "util/regional.h"
+#include "util/data/msgencode.h"
+#include "services/cache/dns.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_delegpt.h"
+#include "daemon/worker.h"
+
+typedef struct resp_addr rpz_aclnode_type;
+
+struct matched_delegation_point {
+ uint8_t* dname;
+ size_t dname_len;
+};
/** string for RPZ action enum */
const char*
rpz_action_to_string(enum rpz_action a)
{
switch(a) {
- case RPZ_NXDOMAIN_ACTION: return "nxdomain";
- case RPZ_NODATA_ACTION: return "nodata";
- case RPZ_PASSTHRU_ACTION: return "passthru";
- case RPZ_DROP_ACTION: return "drop";
- case RPZ_TCP_ONLY_ACTION: return "tcp_only";
- case RPZ_INVALID_ACTION: return "invalid";
- case RPZ_LOCAL_DATA_ACTION: return "local_data";
- case RPZ_DISABLED_ACTION: return "disabled";
- case RPZ_CNAME_OVERRIDE_ACTION: return "cname_override";
- case RPZ_NO_OVERRIDE_ACTION: return "no_override";
+ case RPZ_NXDOMAIN_ACTION: return "rpz-nxdomain";
+ case RPZ_NODATA_ACTION: return "rpz-nodata";
+ case RPZ_PASSTHRU_ACTION: return "rpz-passthru";
+ case RPZ_DROP_ACTION: return "rpz-drop";
+ case RPZ_TCP_ONLY_ACTION: return "rpz-tcp-only";
+ case RPZ_INVALID_ACTION: return "rpz-invalid";
+ case RPZ_LOCAL_DATA_ACTION: return "rpz-local-data";
+ case RPZ_DISABLED_ACTION: return "rpz-disabled";
+ case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override";
+ case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override";
+ default: return "rpz-unknown-action";
}
- return "unknown";
}
/** RPZ action enum for config string */
static enum rpz_action
rpz_config_to_action(char* a)
{
- if(strcmp(a, "nxdomain") == 0)
- return RPZ_NXDOMAIN_ACTION;
- else if(strcmp(a, "nodata") == 0)
- return RPZ_NODATA_ACTION;
- else if(strcmp(a, "passthru") == 0)
- return RPZ_PASSTHRU_ACTION;
- else if(strcmp(a, "drop") == 0)
- return RPZ_DROP_ACTION;
- else if(strcmp(a, "tcp_only") == 0)
- return RPZ_TCP_ONLY_ACTION;
- else if(strcmp(a, "cname") == 0)
- return RPZ_CNAME_OVERRIDE_ACTION;
- else if(strcmp(a, "disabled") == 0)
- return RPZ_DISABLED_ACTION;
- return RPZ_INVALID_ACTION;
+ if(strcmp(a, "nxdomain") == 0) return RPZ_NXDOMAIN_ACTION;
+ else if(strcmp(a, "nodata") == 0) return RPZ_NODATA_ACTION;
+ else if(strcmp(a, "passthru") == 0) return RPZ_PASSTHRU_ACTION;
+ else if(strcmp(a, "drop") == 0) return RPZ_DROP_ACTION;
+ else if(strcmp(a, "tcp_only") == 0) return RPZ_TCP_ONLY_ACTION;
+ else if(strcmp(a, "cname") == 0) return RPZ_CNAME_OVERRIDE_ACTION;
+ else if(strcmp(a, "disabled") == 0) return RPZ_DISABLED_ACTION;
+ else return RPZ_INVALID_ACTION;
}
/** string for RPZ trigger enum */
@@ -96,14 +101,14 @@ static const char*
rpz_trigger_to_string(enum rpz_trigger r)
{
switch(r) {
- case RPZ_QNAME_TRIGGER: return "qname";
- case RPZ_CLIENT_IP_TRIGGER: return "client_ip";
- case RPZ_RESPONSE_IP_TRIGGER: return "response_ip";
- case RPZ_NSDNAME_TRIGGER: return "nsdname";
- case RPZ_NSIP_TRIGGER: return "nsip";
- case RPZ_INVALID_TRIGGER: return "invalid";
+ case RPZ_QNAME_TRIGGER: return "rpz-qname";
+ case RPZ_CLIENT_IP_TRIGGER: return "rpz-client-ip";
+ case RPZ_RESPONSE_IP_TRIGGER: return "rpz-response-ip";
+ case RPZ_NSDNAME_TRIGGER: return "rpz-nsdname";
+ case RPZ_NSIP_TRIGGER: return "rpz-nsip";
+ case RPZ_INVALID_TRIGGER: return "rpz-invalid";
+ default: return "rpz-unknown-trigger";
}
- return "unknown";
}
/**
@@ -138,6 +143,31 @@ get_tld_label(uint8_t* dname, size_t maxdnamelen)
}
/**
+ * The RR types that are to be ignored.
+ * DNSSEC RRs at the apex, and SOA and NS are ignored.
+ */
+static int
+rpz_type_ignored(uint16_t rr_type)
+{
+ switch(rr_type) {
+ case LDNS_RR_TYPE_SOA:
+ case LDNS_RR_TYPE_NS:
+ case LDNS_RR_TYPE_DNAME:
+ /* all DNSSEC-related RRs must be ignored */
+ case LDNS_RR_TYPE_DNSKEY:
+ case LDNS_RR_TYPE_DS:
+ case LDNS_RR_TYPE_RRSIG:
+ case LDNS_RR_TYPE_NSEC:
+ case LDNS_RR_TYPE_NSEC3:
+ case LDNS_RR_TYPE_NSEC3PARAM:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/**
* Classify RPZ action for RR type/rdata
* @param rr_type: the RR type
* @param rdatawl: RDATA with 2 bytes length
@@ -208,15 +238,15 @@ static enum localzone_type
rpz_action_to_localzone_type(enum rpz_action a)
{
switch(a) {
- case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
- case RPZ_NODATA_ACTION: return local_zone_always_nodata;
- case RPZ_DROP_ACTION: return local_zone_always_deny;
- case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
+ case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
+ case RPZ_NODATA_ACTION: return local_zone_always_nodata;
+ case RPZ_DROP_ACTION: return local_zone_always_deny;
+ case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
- case RPZ_INVALID_ACTION: /* fallthrough */
- case RPZ_TCP_ONLY_ACTION: /* fallthrough */
- default: return local_zone_invalid;
+ case RPZ_TCP_ONLY_ACTION: return local_zone_truncate;
+ case RPZ_INVALID_ACTION: /* fallthrough */
+ default: return local_zone_invalid;
}
}
@@ -224,15 +254,15 @@ enum respip_action
rpz_action_to_respip_action(enum rpz_action a)
{
switch(a) {
- case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
- case RPZ_NODATA_ACTION: return respip_always_nodata;
- case RPZ_DROP_ACTION: return respip_always_deny;
- case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
- case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
+ case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
+ case RPZ_NODATA_ACTION: return respip_always_nodata;
+ case RPZ_DROP_ACTION: return respip_always_deny;
+ case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
+ case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
- case RPZ_INVALID_ACTION: /* fallthrough */
- case RPZ_TCP_ONLY_ACTION: /* fallthrough */
- default: return respip_invalid;
+ case RPZ_TCP_ONLY_ACTION: return respip_truncate;
+ case RPZ_INVALID_ACTION: /* fallthrough */
+ default: return respip_invalid;
}
}
@@ -240,14 +270,14 @@ static enum rpz_action
localzone_type_to_rpz_action(enum localzone_type lzt)
{
switch(lzt) {
- case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
- case local_zone_always_nodata: return RPZ_NODATA_ACTION;
- case local_zone_always_deny: return RPZ_DROP_ACTION;
- case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
- case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
- case local_zone_invalid:
- default:
- return RPZ_INVALID_ACTION;
+ case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
+ case local_zone_always_nodata: return RPZ_NODATA_ACTION;
+ case local_zone_always_deny: return RPZ_DROP_ACTION;
+ case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
+ case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
+ case local_zone_truncate: return RPZ_TCP_ONLY_ACTION;
+ case local_zone_invalid: /* fallthrough */
+ default: return RPZ_INVALID_ACTION;
}
}
@@ -255,14 +285,14 @@ enum rpz_action
respip_action_to_rpz_action(enum respip_action a)
{
switch(a) {
- case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
- case respip_always_nodata: return RPZ_NODATA_ACTION;
- case respip_always_deny: return RPZ_DROP_ACTION;
- case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
- case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
- case respip_invalid:
- default:
- return RPZ_INVALID_ACTION;
+ case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
+ case respip_always_nodata: return RPZ_NODATA_ACTION;
+ case respip_always_deny: return RPZ_DROP_ACTION;
+ case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
+ case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
+ case respip_truncate: return RPZ_TCP_ONLY_ACTION;
+ case respip_invalid: /* fallthrough */
+ default: return RPZ_INVALID_ACTION;
}
}
@@ -298,12 +328,55 @@ rpz_dname_to_trigger(uint8_t* dname, size_t dname_len)
return RPZ_QNAME_TRIGGER;
}
-void rpz_delete(struct rpz* r)
+static inline struct clientip_synthesized_rrset*
+rpz_clientip_synthesized_set_create(void)
+{
+ struct clientip_synthesized_rrset* set = calloc(1, sizeof(*set));
+ if(set == NULL) {
+ return NULL;
+ }
+ set->region = regional_create();
+ if(set->region == NULL) {
+ free(set);
+ return NULL;
+ }
+ addr_tree_init(&set->entries);
+ lock_rw_init(&set->lock);
+ return set;
+}
+
+static void
+rpz_clientip_synthesized_rr_delete(rbnode_type* n, void* ATTR_UNUSED(arg))
+{
+ struct clientip_synthesized_rr* r = (struct clientip_synthesized_rr*)n->key;
+ lock_rw_destroy(&r->lock);
+#ifdef THREADS_DISABLED
+ (void)r;
+#endif
+}
+
+static inline void
+rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset* set)
+{
+ if(set == NULL) {
+ return;
+ }
+ lock_rw_destroy(&set->lock);
+ traverse_postorder(&set->entries, rpz_clientip_synthesized_rr_delete, NULL);
+ regional_destroy(set->region);
+ free(set);
+}
+
+void
+rpz_delete(struct rpz* r)
{
if(!r)
return;
local_zones_delete(r->local_zones);
+ local_zones_delete(r->nsdname_zones);
respip_set_delete(r->respip_set);
+ rpz_clientip_synthesized_set_delete(r->client_set);
+ rpz_clientip_synthesized_set_delete(r->ns_set);
regional_destroy(r->region);
free(r->taglist);
free(r->log_name);
@@ -315,13 +388,31 @@ rpz_clear(struct rpz* r)
{
/* must hold write lock on auth_zone */
local_zones_delete(r->local_zones);
+ r->local_zones = NULL;
+ local_zones_delete(r->nsdname_zones);
+ r->nsdname_zones = NULL;
respip_set_delete(r->respip_set);
+ r->respip_set = NULL;
+ rpz_clientip_synthesized_set_delete(r->client_set);
+ r->client_set = NULL;
+ rpz_clientip_synthesized_set_delete(r->ns_set);
+ r->ns_set = NULL;
if(!(r->local_zones = local_zones_create())){
return 0;
}
+ r->nsdname_zones = local_zones_create();
+ if(r->nsdname_zones == NULL) {
+ return 0;
+ }
if(!(r->respip_set = respip_set_create())) {
return 0;
}
+ if(!(r->client_set = rpz_clientip_synthesized_set_create())) {
+ return 0;
+ }
+ if(!(r->ns_set = rpz_clientip_synthesized_set_create())) {
+ return 0;
+ }
return 1;
}
@@ -331,6 +422,14 @@ rpz_finish_config(struct rpz* r)
lock_rw_wrlock(&r->respip_set->lock);
addr_tree_init_parents(&r->respip_set->ip_tree);
lock_rw_unlock(&r->respip_set->lock);
+
+ lock_rw_wrlock(&r->client_set->lock);
+ addr_tree_init_parents(&r->client_set->entries);
+ lock_rw_unlock(&r->client_set->lock);
+
+ lock_rw_wrlock(&r->ns_set->lock);
+ addr_tree_init_parents(&r->ns_set->entries);
+ lock_rw_unlock(&r->ns_set->lock);
}
/** new rrset containing CNAME override, does not yet contain a dname */
@@ -394,9 +493,26 @@ rpz_create(struct config_auth* p)
if(!(r->local_zones = local_zones_create())){
goto err;
}
+
+ r->nsdname_zones = local_zones_create();
+ if(r->local_zones == NULL){
+ goto err;
+ }
+
if(!(r->respip_set = respip_set_create())) {
goto err;
}
+
+ r->client_set = rpz_clientip_synthesized_set_create();
+ if(r->client_set == NULL) {
+ goto err;
+ }
+
+ r->ns_set = rpz_clientip_synthesized_set_create();
+ if(r->ns_set == NULL) {
+ goto err;
+ }
+
r->taglistlen = p->rpz_taglistlen;
r->taglist = memdup(p->rpz_taglist, r->taglistlen);
if(p->rpz_action_override) {
@@ -437,8 +553,14 @@ err:
if(r) {
if(r->local_zones)
local_zones_delete(r->local_zones);
+ if(r->nsdname_zones)
+ local_zones_delete(r->nsdname_zones);
if(r->respip_set)
respip_set_delete(r->respip_set);
+ if(r->client_set != NULL)
+ rpz_clientip_synthesized_set_delete(r->client_set);
+ if(r->ns_set != NULL)
+ rpz_clientip_synthesized_set_delete(r->ns_set);
if(r->taglist)
free(r->taglist);
if(r->region)
@@ -467,19 +589,17 @@ strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
return newdnamelen + 1; /* + 1 for root label */
}
-/** Insert RR into RPZ's local-zone */
static void
-rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
- enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
- uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
+ size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass,
+ uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
struct local_zone* z;
enum localzone_type tp = local_zone_always_transparent;
int dnamelabs = dname_count_labels(dname);
- char* rrstr;
int newzone = 0;
- if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) {
+ if(a == RPZ_INVALID_ACTION) {
char str[255+1];
if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
rrtype == LDNS_RR_TYPE_DNAME ||
@@ -499,110 +619,394 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
return;
}
- lock_rw_wrlock(&r->local_zones->lock);
+ lock_rw_wrlock(&lz->lock);
/* exact match */
- z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs,
- LDNS_RR_CLASS_IN);
- if(z && a != RPZ_LOCAL_DATA_ACTION) {
- rrstr = sldns_wire2str_rr(rr, rr_len);
- if(!rrstr) {
- log_err("malloc error while inserting RPZ qname "
- "trigger");
+ z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN);
+ if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) {
+ char* rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(rrstr == NULL) {
+ log_err("malloc error while inserting rpz nsdname trigger");
free(dname);
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&lz->lock);
return;
}
- verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'",
- rrstr);
+ if(rrstr[0])
+ rrstr[strlen(rrstr)-1]=0; /* remove newline */
+ verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr);
free(rrstr);
free(dname);
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&lz->lock);
return;
}
- if(!z) {
+ if(z == NULL) {
tp = rpz_action_to_localzone_type(a);
- if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen,
- dnamelabs, rrclass, tp))) {
- log_warn("RPZ create failed");
- lock_rw_unlock(&r->local_zones->lock);
+ z = local_zones_add_zone(lz, dname, dnamelen,
+ dnamelabs, rrclass, tp);
+ if(z == NULL) {
+ log_warn("rpz: create failed");
+ lock_rw_unlock(&lz->lock);
/* dname will be free'd in failed local_zone_create() */
return;
}
newzone = 1;
}
if(a == RPZ_LOCAL_DATA_ACTION) {
- rrstr = sldns_wire2str_rr(rr, rr_len);
- if(!rrstr) {
- log_err("malloc error while inserting RPZ qname "
- "trigger");
+ char* rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(rrstr == NULL) {
+ log_err("malloc error while inserting rpz nsdname trigger");
free(dname);
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&lz->lock);
return;
}
lock_rw_wrlock(&z->lock);
- local_zone_enter_rr(z, dname, dnamelen, dnamelabs,
- rrtype, rrclass, ttl, rdata, rdata_len, rrstr);
+ local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype,
+ rrclass, ttl, rdata, rdata_len, rrstr);
lock_rw_unlock(&z->lock);
free(rrstr);
}
- if(!newzone)
+ if(!newzone) {
free(dname);
- lock_rw_unlock(&r->local_zones->lock);
- return;
+ }
+ lock_rw_unlock(&lz->lock);
+}
+
+static void
+rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len)
+{
+ char buf[LDNS_MAX_DOMAINLEN+1];
+ (void)dname_len;
+ dname_str(dname, buf);
+ verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf);
+}
+
+static void
+rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ if(a == RPZ_INVALID_ACTION) {
+ verbose(VERB_ALGO, "rpz: skipping invalid action");
+ free(dname);
+ return;
+ }
+
+ rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype,
+ rrclass, ttl, rdata, rdata_len, rr, rr_len);
}
-/** Insert RR into RPZ's respip_set */
static int
-rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen,
+ uint8_t** stripdname, size_t* stripdnamelen)
+{
+ uint8_t* tldstart = get_tld_label(dname, maxdnamelen);
+ uint8_t swap;
+ if(tldstart == NULL) {
+ if(dname == NULL) {
+ *stripdname = NULL;
+ *stripdnamelen = 0;
+ return 0;
+ }
+ *stripdname = memdup(dname, maxdnamelen);
+ if(!*stripdname) {
+ *stripdnamelen = 0;
+ log_err("malloc failure for rpz strip suffix");
+ return 0;
+ }
+ *stripdnamelen = maxdnamelen;
+ return 1;
+ }
+ /* shorten the domain name briefly,
+ * then we allocate a new name with the correct length */
+ swap = *tldstart;
+ *tldstart = 0;
+ (void)dname_count_size_labels(dname, stripdnamelen);
+ *stripdname = memdup(dname, *stripdnamelen);
+ *tldstart = swap;
+ if(!*stripdname) {
+ *stripdnamelen = 0;
+ log_err("malloc failure for rpz strip suffix");
+ return 0;
+ }
+ return 1;
+}
+
+static void
+rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
+ uint8_t* dname_stripped = NULL;
+ size_t dnamelen_stripped = 0;
+
+ rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
+ &dnamelen_stripped);
+ if(a == RPZ_INVALID_ACTION) {
+ verbose(VERB_ALGO, "rpz: skipping invalid action");
+ free(dname_stripped);
+ return;
+ }
+
+ /* dname_stripped is consumed or freed by the insert routine */
+ rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped,
+ dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len,
+ rr, rr_len);
+}
+
+static int
+rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr,
+ socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
+ uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
+ uint8_t* rr, size_t rr_len)
+{
struct resp_addr* node;
- struct sockaddr_storage addr;
- socklen_t addrlen;
- int net, af;
char* rrstr;
enum respip_action respa = rpz_action_to_respip_action(a);
- if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION ||
- respa == respip_invalid) {
- char str[255+1];
- dname_str(dname, str);
- verbose(VERB_ALGO, "RPZ: respip trigger, %s skipping unsupported action: %s",
- str, rpz_action_to_string(a));
+ lock_rw_wrlock(&set->lock);
+ rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(rrstr == NULL) {
+ log_err("malloc error while inserting rpz ipaddr based trigger");
+ lock_rw_unlock(&set->lock);
return 0;
}
- if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
+ node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr);
+ if(node == NULL) {
+ lock_rw_unlock(&set->lock);
+ free(rrstr);
return 0;
+ }
- lock_rw_wrlock(&r->respip_set->lock);
- rrstr = sldns_wire2str_rr(rr, rr_len);
- if(!rrstr) {
- log_err("malloc error while inserting RPZ respip trigger");
- lock_rw_unlock(&r->respip_set->lock);
+ lock_rw_wrlock(&node->lock);
+ lock_rw_unlock(&set->lock);
+
+ node->action = respa;
+
+ if(a == RPZ_LOCAL_DATA_ACTION) {
+ respip_enter_rr(set->region, node, rrtype,
+ rrclass, ttl, rdata, rdata_len, rrstr, "");
+ }
+
+ lock_rw_unlock(&node->lock);
+ free(rrstr);
+ return 1;
+}
+
+static inline struct clientip_synthesized_rr*
+rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set,
+ struct sockaddr_storage* addr, socklen_t addrlen, int net)
+{
+ int insert_ok;
+ struct clientip_synthesized_rr* node =
+ (struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
+ addr, addrlen, net);
+
+ if(node != NULL) { return node; }
+
+ /* node does not yet exist => allocate one */
+ node = regional_alloc_zero(set->region, sizeof(*node));
+ if(node == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+
+ lock_rw_init(&node->lock);
+ node->action = RPZ_INVALID_ACTION;
+ insert_ok = addr_tree_insert(&set->entries, &node->node,
+ addr, addrlen, net);
+ if (!insert_ok) {
+ log_warn("rpz: unexpected: unable to insert clientip address node");
+ /* we can not free the just allocated node.
+ * theoretically a memleak */
+ return NULL;
+ }
+
+ return node;
+}
+
+static void
+rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) {
+ char* rrstr = sldns_wire2str_rr(rr, rr_len);
+ if(rrstr == NULL) {
+ log_err("malloc error while inserting rpz clientip based record");
+ return;
+ }
+ log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr);
+ free(rrstr);
+}
+
+/* from localzone.c; difference is we don't have a dname */
+struct local_rrset*
+rpz_clientip_new_rrset(struct regional* region,
+ struct clientip_synthesized_rr* raddr, 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 == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ rrset->next = raddr->data;
+ raddr->data = rrset;
+ rrset->rrset = (struct ub_packed_rrset_key*)
+ regional_alloc_zero(region, sizeof(*rrset->rrset));
+ if(rrset->rrset == NULL) {
+ 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 == NULL) {
+ 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.type = htons(rrtype);
+ rrset->rrset->rk.rrset_class = htons(rrclass);
+ rrset->rrset->rk.dname = regional_alloc_zero(region, 1);
+ if(rrset->rrset->rk.dname == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ rrset->rrset->rk.dname_len = 1;
+ return rrset;
+}
+
+static int
+rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* raddr,
+ uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
+ size_t rdata_len)
+{
+ struct local_rrset* rrset;
+ if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) {
+ log_err("CNAME response-ip data can not co-exist with other "
+ "client-ip data");
return 0;
}
- if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen,
- net, 1, rrstr))) {
- lock_rw_unlock(&r->respip_set->lock);
- free(rrstr);
+
+ rrset = rpz_clientip_new_rrset(region, raddr, rrtype, rrclass);
+ if(raddr->data == NULL) {
+ return 0;
+ }
+
+ return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, "");
+}
+
+static int
+rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr,
+ socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
+ uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
+ uint8_t* rr, size_t rr_len)
+{
+ struct clientip_synthesized_rr* node;
+
+ lock_rw_wrlock(&set->lock);
+
+ node = rpz_clientip_ensure_entry(set, addr, addrlen, net);
+ if(node == NULL) {
+ lock_rw_unlock(&set->lock);
+ rpz_report_rrset_error("client ip address", rr, rr_len);
return 0;
}
lock_rw_wrlock(&node->lock);
- lock_rw_unlock(&r->respip_set->lock);
- node->action = respa;
+ lock_rw_unlock(&set->lock);
+ node->action = a;
if(a == RPZ_LOCAL_DATA_ACTION) {
- respip_enter_rr(r->respip_set->region, node, rrtype,
- rrclass, ttl, rdata, rdata_len, rrstr, "");
+ if(!rpz_clientip_enter_rr(set->region, node, rrtype,
+ rrclass, ttl, rdata, rdata_len)) {
+ verbose(VERB_ALGO, "rpz: unable to insert clientip rr");
+ lock_rw_unlock(&node->lock);
+ return 0;
+ }
+
}
+
lock_rw_unlock(&node->lock);
- free(rrstr);
+
return 1;
}
+static int
+rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+
+ if(a == RPZ_INVALID_ACTION) {
+ return 0;
+ }
+
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
+ verbose(VERB_ALGO, "rpz: unable to parse client ip");
+ return 0;
+ }
+
+ return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net,
+ a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
+}
+
+static int
+rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+
+ if(a == RPZ_INVALID_ACTION) {
+ return 0;
+ }
+
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
+ verbose(VERB_ALGO, "rpz: unable to parse ns ip");
+ return 0;
+ }
+
+ return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net,
+ a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
+}
+
+/** Insert RR into RPZ's respip_set */
+static int
+rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
+ enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
+ uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int net, af;
+
+ if(a == RPZ_INVALID_ACTION) {
+ return 0;
+ }
+
+ if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
+ verbose(VERB_ALGO, "rpz: unable to parse response ip");
+ return 0;
+ }
+
+ if(a == RPZ_INVALID_ACTION ||
+ rpz_action_to_respip_action(a) == respip_invalid) {
+ char str[255+1];
+ dname_str(dname, str);
+ verbose(VERB_ALGO, "RPZ: respip trigger, %s skipping unsupported action: %s",
+ str, rpz_action_to_string(a));
+ return 0;
+ }
+
+ return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net,
+ a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
+}
+
int
rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
@@ -614,15 +1018,19 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
enum rpz_action a;
uint8_t* policydname;
+ if(rpz_type_ignored(rr_type)) {
+ /* this rpz action is not valid, eg. this is the SOA or NS RR */
+ return 1;
+ }
if(!dname_subdomain_c(dname, azname)) {
char* dname_str = sldns_wire2str_dname(dname, dnamelen);
char* azname_str = sldns_wire2str_dname(azname, aznamelen);
if(dname_str && azname_str) {
- log_err("RPZ: name of record (%s) to insert into RPZ is not a "
+ log_err("rpz: name of record (%s) to insert into RPZ is not a "
"subdomain of the configured name of the RPZ zone (%s)",
dname_str, azname_str);
} else {
- log_err("RPZ: name of record to insert into RPZ is not a "
+ log_err("rpz: name of record to insert into RPZ is not a "
"subdomain of the configured name of the RPZ zone");
}
free(dname_str);
@@ -645,23 +1053,37 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
t = rpz_dname_to_trigger(policydname, policydnamelen);
if(t == RPZ_INVALID_TRIGGER) {
free(policydname);
- verbose(VERB_ALGO, "RPZ: skipping invalid trigger");
+ verbose(VERB_ALGO, "rpz: skipping invalid trigger");
return 1;
}
if(t == RPZ_QNAME_TRIGGER) {
+ /* policydname will be consumed, no free */
rpz_insert_qname_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
- }
- else if(t == RPZ_RESPONSE_IP_TRIGGER) {
+ } else if(t == RPZ_RESPONSE_IP_TRIGGER) {
rpz_insert_response_ip_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
free(policydname);
- }
- else {
+ } else if(t == RPZ_CLIENT_IP_TRIGGER) {
+ rpz_insert_clientip_trigger(r, policydname, policydnamelen,
+ a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
+ rr_len);
+ free(policydname);
+ } else if(t == RPZ_NSIP_TRIGGER) {
+ rpz_insert_nsip_trigger(r, policydname, policydnamelen,
+ a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
+ rr_len);
+ free(policydname);
+ } else if(t == RPZ_NSDNAME_TRIGGER) {
+ rpz_insert_nsdname_trigger(r, policydname, policydnamelen,
+ a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
+ rr_len);
free(policydname);
- verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s",
+ } else {
+ free(policydname);
+ verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s",
rpz_trigger_to_string(t));
}
return 1;
@@ -669,18 +1091,18 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
/**
* Find RPZ local-zone by qname.
- * @param r: rpz containing local-zone tree
+ * @param zones: local-zone tree
* @param qname: qname
* @param qname_len: length of qname
* @param qclass: qclass
- * @param only_exact: if 1 only excact (non wildcard) matches are returned
+ * @param only_exact: if 1 only exact (non wildcard) matches are returned
* @param wr: get write lock for local-zone if 1, read lock if 0
* @param zones_keep_lock: if set do not release the r->local_zones lock, this
* makes the caller of this function responsible for releasing the lock.
* @return: NULL or local-zone holding rd or wr lock
*/
static struct local_zone*
-rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
+rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint16_t qclass,
int only_exact, int wr, int zones_keep_lock)
{
uint8_t* ce;
@@ -689,16 +1111,19 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
uint8_t wc[LDNS_MAX_DOMAINLEN+1];
int exact;
struct local_zone* z = NULL;
+
if(wr) {
- lock_rw_wrlock(&r->local_zones->lock);
+ lock_rw_wrlock(&zones->lock);
} else {
- lock_rw_rdlock(&r->local_zones->lock);
+ lock_rw_rdlock(&zones->lock);
}
- z = local_zones_find_le(r->local_zones, qname, qname_len,
+ z = local_zones_find_le(zones, qname, qname_len,
dname_count_labels(qname),
LDNS_RR_CLASS_IN, &exact);
if(!z || (only_exact && !exact)) {
- lock_rw_unlock(&r->local_zones->lock);
+ if(!zones_keep_lock) {
+ lock_rw_unlock(&zones->lock);
+ }
return NULL;
}
if(wr) {
@@ -707,7 +1132,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
lock_rw_rdlock(&z->lock);
}
if(!zones_keep_lock) {
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
}
if(exact)
@@ -721,7 +1146,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
if(!ce /* should not happen */) {
lock_rw_unlock(&z->lock);
if(zones_keep_lock) {
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
}
return NULL;
}
@@ -729,7 +1154,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
if(ce_len+2 > sizeof(wc)) {
lock_rw_unlock(&z->lock);
if(zones_keep_lock) {
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
}
return NULL;
}
@@ -740,15 +1165,15 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
if(!zones_keep_lock) {
if(wr) {
- lock_rw_wrlock(&r->local_zones->lock);
+ lock_rw_wrlock(&zones->lock);
} else {
- lock_rw_rdlock(&r->local_zones->lock);
+ lock_rw_rdlock(&zones->lock);
}
}
- z = local_zones_find_le(r->local_zones, wc,
+ z = local_zones_find_le(zones, wc,
ce_len+2, ce_labs+1, qclass, &exact);
if(!z || !exact) {
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
return NULL;
}
if(wr) {
@@ -757,7 +1182,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
lock_rw_rdlock(&z->lock);
}
if(!zones_keep_lock) {
- lock_rw_unlock(&r->local_zones->lock);
+ lock_rw_unlock(&zones->lock);
}
return z;
}
@@ -766,7 +1191,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
* Remove RR from RPZ's local-data
* @param z: local-zone for RPZ, holding write lock
* @param policydname: dname of RR to remove
- * @param policydnamelen: lenth of policydname
+ * @param policydnamelen: length of policydname
* @param rr_type: RR type of RR to remove
* @param rdata: rdata of RR to remove
* @param rdatalen: length of rdata
@@ -852,10 +1277,10 @@ rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
{
struct local_zone* z;
int delete_zone = 1;
- z = rpz_find_zone(r, dname, dnamelen, rr_class,
+ z = rpz_find_zone(r->local_zones, dname, dnamelen, rr_class,
1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
if(!z) {
- verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
+ verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
"RPZ domain not found");
return;
}
@@ -891,7 +1316,7 @@ rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
lock_rw_wrlock(&r->respip_set->lock);
if(!(node = (struct resp_addr*)addr_tree_find(
&r->respip_set->ip_tree, &addr, addrlen, net))) {
- verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
+ verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
"RPZ domain not found");
lock_rw_unlock(&r->respip_set->lock);
return;
@@ -944,118 +1369,998 @@ rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen,
/** print log information for an applied RPZ policy. Based on local-zone's
* lz_inform_print().
+ * The repinfo contains the reply address. If it is NULL, the module
+ * state is used to report the first IP address (if any).
+ * The dname is used, for the applied rpz, if NULL, addrnode is used.
*/
static void
-log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo,
- struct comm_reply* repinfo, char* log_name)
+log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
+ enum rpz_action a, struct query_info* qinfo,
+ struct comm_reply* repinfo, struct module_qstate* ms, char* log_name)
{
- char ip[128], txt[512];
+ char ip[128], txt[512], portstr[32];
char dnamestr[LDNS_MAX_DOMAINLEN+1];
- uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
- dname_str(dname, dnamestr);
- addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
- if(log_name)
- snprintf(txt, sizeof(txt), "RPZ applied [%s] %s %s %s@%u",
- log_name, dnamestr, rpz_action_to_string(a), ip,
- (unsigned)port);
- else
- snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u",
- dnamestr, rpz_action_to_string(a), ip, (unsigned)port);
+ uint16_t port = 0;
+ if(dname) {
+ dname_str(dname, dnamestr);
+ } else if(addrnode) {
+ char a[128];
+ addr_to_str(&addrnode->addr, addrnode->addrlen, a, sizeof(a));
+ snprintf(dnamestr, sizeof(dnamestr), "%s/%d", a, addrnode->net);
+ } else {
+ dnamestr[0]=0;
+ }
+ if(repinfo) {
+ addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
+ port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
+ } else if(ms && ms->mesh_info && ms->mesh_info->reply_list) {
+ addr_to_str(&ms->mesh_info->reply_list->query_reply.addr, ms->mesh_info->reply_list->query_reply.addrlen, ip, sizeof(ip));
+ port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.addr)->sin_port);
+ } else {
+ ip[0]=0;
+ port = 0;
+ }
+ snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port);
+ snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s%s%s %s %s%s",
+ (log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""),
+ (strcmp(trigger,"qname")==0?"":trigger),
+ (strcmp(trigger,"qname")==0?"":" "),
+ dnamestr, rpz_action_to_string(a),
+ (ip[0]?ip:""), (ip[0]?portstr:""));
log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
}
-int
-rpz_apply_qname_trigger(struct auth_zones* az, 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, struct ub_server_stats* stats)
+static struct clientip_synthesized_rr*
+rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set,
+ struct sockaddr_storage* addr, socklen_t addrlen, char* triggername)
{
+ struct clientip_synthesized_rr* raddr = NULL;
+ enum rpz_action action = RPZ_INVALID_ACTION;
+
+ lock_rw_rdlock(&set->lock);
+
+ raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&set->entries,
+ addr, addrlen);
+ if(raddr != NULL) {
+ lock_rw_rdlock(&raddr->lock);
+ action = raddr->action;
+ if(verbosity >= VERB_ALGO) {
+ char ip[256], net[256];
+ addr_to_str(addr, addrlen, ip, sizeof(ip));
+ addr_to_str(&raddr->node.addr, raddr->node.addrlen,
+ net, sizeof(net));
+ verbose(VERB_ALGO, "rpz: trigger %s %s/%d on %s action=%s",
+ triggername, net, raddr->node.net, ip, rpz_action_to_string(action));
+ }
+ }
+ lock_rw_unlock(&set->lock);
+
+ return raddr;
+}
+
+static inline
+struct clientip_synthesized_rr*
+rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo,
+ struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
+ struct ub_server_stats* stats,
+ /* output parameters */
+ struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out)
+{
+ struct clientip_synthesized_rr* node = NULL;
+ struct auth_zone* a = NULL;
struct rpz* r = NULL;
- struct auth_zone* a;
- int ret;
- enum localzone_type lzt;
struct local_zone* z = NULL;
- struct local_data* ld = NULL;
+
lock_rw_rdlock(&az->rpz_lock);
+
for(a = az->rpz_first; a; a = a->rpz_az_next) {
lock_rw_rdlock(&a->lock);
r = a->rpz;
- if(!r->disabled && (!r->taglist || taglist_intersect(r->taglist,
- r->taglistlen, taglist, taglen))) {
- z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len,
- qinfo->qclass, 0, 0, 0);
- if(z && r->action_override == RPZ_DISABLED_ACTION) {
- if(r->log)
- log_rpz_apply(z->name,
- r->action_override,
- qinfo, repinfo, r->log_name);
- /* TODO only register stats when stats_extended?
- * */
- stats->rpz_action[r->action_override]++;
+ if(r->disabled) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
+ if(r->taglist && !taglist_intersect(r->taglist,
+ r->taglistlen, taglist, taglen)) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
+ z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len,
+ qinfo->qclass, 0, 0, 0);
+ node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen, "clientip");
+ if((z || node) && r->action_override == RPZ_DISABLED_ACTION) {
+ if(r->log)
+ log_rpz_apply((node?"clientip":"qname"),
+ (z?z->name:NULL),
+ (node?&node->node:NULL),
+ r->action_override,
+ qinfo, repinfo, NULL, r->log_name);
+ stats->rpz_action[r->action_override]++;
+ if(z != NULL) {
lock_rw_unlock(&z->lock);
z = NULL;
}
- if(z)
- break;
+ if(node != NULL) {
+ lock_rw_unlock(&node->lock);
+ node = NULL;
+ }
}
- lock_rw_unlock(&a->lock); /* not found in this auth_zone */
+ if(z || node) {
+ break;
+ }
+ /* not found in this auth_zone */
+ lock_rw_unlock(&a->lock);
}
+
lock_rw_unlock(&az->rpz_lock);
- if(!z)
- return 0; /* not holding auth_zone.lock anymore */
- log_assert(r);
- if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
- lzt = z->type;
- else
- lzt = rpz_action_to_localzone_type(r->action_override);
+ *r_out = r;
+ *a_out = a;
+ *z_out = z;
+
+ return node;
+}
+
+static inline int
+rpz_is_udp_query(struct comm_reply* repinfo) {
+ return repinfo != NULL
+ ? (repinfo->c != NULL
+ ? repinfo->c->type == comm_udp
+ : 0)
+ : 0;
+}
+
+/** encode answer consisting of 1 rrset */
+static int
+rpz_local_encode(struct module_env* env, struct query_info* qinfo,
+ struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
+ struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec,
+ int rcode, struct ub_packed_rrset_key* soa_rrset)
+{
+ struct reply_info rep;
+ uint16_t udpsize;
+ struct ub_packed_rrset_key* rrsetlist[3];
+
+ memset(&rep, 0, sizeof(rep));
+ rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
+ rep.qdcount = 1;
+ rep.rrset_count = ansec;
+ rep.rrsets = rrsetlist;
+ if(ansec > 0) {
+ rep.an_numrrsets = 1;
+ rep.rrsets[0] = rrset;
+ rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0];
+ }
+ if(soa_rrset != NULL) {
+ rep.ar_numrrsets = 1;
+ rep.rrsets[rep.rrset_count] = soa_rrset;
+ rep.rrset_count ++;
+ if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) {
+ rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0];
+ }
+ }
+
+ 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,
+ 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,
+ *(uint16_t*)sldns_buffer_begin(buf),
+ sldns_buffer_read_u16_at(buf, 2), edns);
+ }
+
+ return 1;
+}
+
+static struct local_rrset*
+rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) {
+ struct local_rrset* cursor = data->data;
+ while( cursor != NULL) {
+ struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
+ if(htons(qtype) == packed_rrset->type) {
+ return cursor;
+ }
+ cursor = cursor->next;
+ }
+ return NULL;
+}
+
+/** allocate SOA record ubrrsetkey in region */
+static struct ub_packed_rrset_key*
+make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa,
+ struct regional* temp)
+{
+ struct ub_packed_rrset_key csoa;
+ if(!soa)
+ return NULL;
+ memset(&csoa, 0, sizeof(csoa));
+ csoa.entry.key = &csoa;
+ csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
+ csoa.rk.type = htons(LDNS_RR_TYPE_SOA);
+ csoa.rk.flags |= PACKED_RRSET_FIXEDTTL
+ | PACKED_RRSET_RPZ;
+ csoa.rk.dname = auth_zone->name;
+ csoa.rk.dname_len = auth_zone->namelen;
+ csoa.entry.hash = rrset_key_hash(&csoa.rk);
+ csoa.entry.data = soa->data;
+ return respip_copy_rrset(&csoa, temp);
+}
+
+static void
+rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr,
+ struct module_env* env, struct query_info* qinfo,
+ struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
+ struct regional* temp, struct auth_zone* auth_zone)
+{
+ struct local_rrset* rrset;
+ enum rpz_action action = RPZ_INVALID_ACTION;
+ struct ub_packed_rrset_key* rp = NULL;
+ struct ub_packed_rrset_key* rsoa = NULL;
+ int rcode = LDNS_RCODE_NOERROR|BIT_AA;
+ int rrset_count = 1;
+
+ /* prepare synthesized answer for client */
+ action = raddr->action;
+ if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) {
+ verbose(VERB_ALGO, "rpz: bug: local-data action but no local data");
+ return;
+ }
+
+ /* check query type / rr type */
+ rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr);
+ if(rrset == NULL) {
+ verbose(VERB_ALGO, "rpz: unable to find local-data for query");
+ rrset_count = 0;
+ goto nodata;
+ }
+
+ rp = respip_copy_rrset(rrset->rrset, temp);
+ if(!rp) {
+ verbose(VERB_ALGO, "rpz: local data action: out of memory");
+ return;
+ }
+
+ rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ;
+ rp->rk.dname = qinfo->qname;
+ rp->rk.dname_len = qinfo->qname_len;
+ rp->entry.hash = rrset_key_hash(&rp->rk);
+nodata:
+ if(auth_zone) {
+ struct auth_rrset* soa = NULL;
+ soa = auth_zone_get_soa_rrset(auth_zone);
+ if(soa) {
+ rsoa = make_soa_ubrrset(auth_zone, soa, temp);
+ if(!rsoa) {
+ verbose(VERB_ALGO, "rpz: local data action soa: out of memory");
+ return;
+ }
+ }
+ }
+
+ rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp,
+ rrset_count, rcode, rsoa);
+}
+
+/** add additional section SOA record to the reply.
+ * Since this gets fed into the normal iterator answer creation, it
+ * gets minimal-responses applied to it, that can remove the additional SOA
+ * again. */
+static int
+rpz_add_soa(struct reply_info* rep, struct module_qstate* ms,
+ struct auth_zone* az)
+{
+ struct auth_rrset* soa = NULL;
+ struct ub_packed_rrset_key* rsoa = NULL;
+ struct ub_packed_rrset_key** prevrrsets;
+ if(!az) return 1;
+ soa = auth_zone_get_soa_rrset(az);
+ if(!soa) return 1;
+ if(!rep) return 0;
+ rsoa = make_soa_ubrrset(az, soa, ms->region);
+ if(!rsoa) return 0;
+ prevrrsets = rep->rrsets;
+ rep->rrsets = regional_alloc_zero(ms->region,
+ sizeof(*rep->rrsets)*(rep->rrset_count+1));
+ if(!rep->rrsets)
+ return 0;
+ if(prevrrsets && rep->rrset_count > 0)
+ memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets));
+ rep->rrset_count++;
+ rep->ar_numrrsets++;
+ rep->rrsets[rep->rrset_count-1] = rsoa;
+ return 1;
+}
+
+static inline struct dns_msg*
+rpz_dns_msg_new(struct regional* region)
+{
+ struct dns_msg* msg =
+ (struct dns_msg*)regional_alloc(region,
+ sizeof(struct dns_msg));
+ if(msg == NULL) { return NULL; }
+ memset(msg, 0, sizeof(struct dns_msg));
+
+ return msg;
+}
+static inline struct dns_msg*
+rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
+ struct query_info* qinfo, struct auth_zone* az)
+{
+ struct dns_msg* msg = rpz_dns_msg_new(ms->region);
+ if(msg == NULL) { return msg; }
+ msg->qinfo = *qinfo;
+ msg->rep = construct_reply_info_base(ms->region,
+ LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ 1, /* qd */
+ 0, /* ttl */
+ 0, /* prettl */
+ 0, /* expttl */
+ 0, /* an */
+ 0, /* ns */
+ 0, /* ar */
+ 0, /* total */
+ sec_status_insecure);
+ if(msg->rep)
+ msg->rep->authoritative = 1;
+ if(!rpz_add_soa(msg->rep, ms, az))
+ return NULL;
+ return msg;
+}
+
+static inline struct dns_msg*
+rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
+ struct query_info* qinfo, struct auth_zone* az)
+{
+ struct dns_msg* msg = rpz_dns_msg_new(ms->region);
+ if(msg == NULL) { return msg; }
+ msg->qinfo = *qinfo;
+ msg->rep = construct_reply_info_base(ms->region,
+ LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ 1, /* qd */
+ 0, /* ttl */
+ 0, /* prettl */
+ 0, /* expttl */
+ 0, /* an */
+ 0, /* ns */
+ 0, /* ar */
+ 0, /* total */
+ sec_status_insecure);
+ if(msg->rep)
+ msg->rep->authoritative = 1;
+ if(!rpz_add_soa(msg->rep, ms, az))
+ return NULL;
+ return msg;
+}
+
+static inline struct dns_msg*
+rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
+ struct query_info* qi, struct local_rrset* rrset, struct auth_zone* az)
+{
+ struct dns_msg* msg = NULL;
+ struct reply_info* new_reply_info;
+ struct ub_packed_rrset_key* rp;
+
+
+ msg = rpz_dns_msg_new(ms->region);
+ if(msg == NULL) { return NULL; }
+
+ new_reply_info = construct_reply_info_base(ms->region,
+ LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA,
+ 1, /* qd */
+ 0, /* ttl */
+ 0, /* prettl */
+ 0, /* expttl */
+ 1, /* an */
+ 0, /* ns */
+ 0, /* ar */
+ 1, /* total */
+ sec_status_insecure);
+ if(new_reply_info == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ new_reply_info->authoritative = 1;
+ rp = respip_copy_rrset(rrset->rrset, ms->region);
+ if(rp == NULL) {
+ log_err("out of memory");
+ return NULL;
+ }
+ rp->rk.dname = qi->qname;
+ rp->rk.dname_len = qi->qname_len;
+ /* this rrset is from the rpz data, or synthesized.
+ * It is not actually from the network, so we flag it with this
+ * flags as a fake RRset. If later the cache is used to look up
+ * rrsets, then the fake ones are not returned (if you look without
+ * the flag). For like CNAME lookups from the iterator or A, AAAA
+ * lookups for nameserver targets, it would use the without flag
+ * actual data. So that the actual network data and fake data
+ * are kept track of separately. */
+ rp->rk.flags |= PACKED_RRSET_RPZ;
+ new_reply_info->rrsets[0] = rp;
+ msg->rep = new_reply_info;
+ if(!rpz_add_soa(msg->rep, ms, az))
+ return NULL;
+ return msg;
+}
+
+static inline struct dns_msg*
+rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms,
+ struct clientip_synthesized_rr* data, struct auth_zone* az)
+{
+ struct query_info* qi = &ms->qinfo;
+ struct local_rrset* rrset;
+
+ rrset = rpz_find_synthesized_rrset(qi->qtype, data);
+ if(rrset == NULL) {
+ verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
+ return NULL;
+ }
+
+ return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
+}
+
+/* copy'n'paste from localzone.c */
+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;
+}
+
+/* based on localzone.c:local_data_answer() */
+static inline struct dns_msg*
+rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
+ struct local_zone* z, struct matched_delegation_point const* match,
+ struct auth_zone* az)
+{
+ struct local_data key;
+ struct local_data* ld;
+ struct local_rrset* rrset;
+
+ if(match->dname == NULL) { return NULL; }
+
+ key.node.key = &key;
+ key.name = match->dname;
+ key.namelen = match->dname_len;
+ key.namelabs = dname_count_labels(match->dname);
+
+ rpz_log_dname("nsdname local data", key.name, key.namelen);
+
+ ld = (struct local_data*)rbtree_search(&z->data, &key.node);
+ if(ld == NULL) {
+ verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found");
+ return NULL;
+ }
+
+ rrset = local_data_find_type(ld, ms->qinfo.qtype, 1);
+ if(rrset == NULL) {
+ verbose(VERB_ALGO, "rpz: nsdname: no matching local data found");
+ return NULL;
+ }
+
+ return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
+}
+
+/* like local_data_answer for qname triggers after a cname */
+static struct dns_msg*
+rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms,
+ struct query_info* qinfo, struct local_zone* z, struct auth_zone* az)
+{
+ struct local_data key;
+ struct local_data* ld;
+ struct local_rrset* rrset;
+ key.node.key = &key;
+ key.name = qinfo->qname;
+ key.namelen = qinfo->qname_len;
+ key.namelabs = dname_count_labels(qinfo->qname);
+ ld = (struct local_data*)rbtree_search(&z->data, &key.node);
+ if(ld == NULL) {
+ verbose(VERB_ALGO, "rpz: qname after cname: name not found");
+ return NULL;
+ }
+ rrset = local_data_find_type(ld, qinfo->qtype, 1);
+ if(rrset == NULL) {
+ verbose(VERB_ALGO, "rpz: qname after cname: type not found");
+ return NULL;
+ }
+ return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az);
+}
+
+static int
+rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
+ struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo,
+ struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
+ struct comm_reply* repinfo, struct ub_server_stats* stats)
+{
+ struct local_data* ld = NULL;
+ int ret = 0;
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
- qinfo->local_alias =
- regional_alloc_zero(temp, sizeof(struct local_rrset));
- if(!qinfo->local_alias) {
- lock_rw_unlock(&z->lock);
- lock_rw_unlock(&a->lock);
+ qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset));
+ if(qinfo->local_alias == NULL) {
return 0; /* out of memory */
}
- qinfo->local_alias->rrset =
- regional_alloc_init(temp, r->cname_override,
- sizeof(*r->cname_override));
- if(!qinfo->local_alias->rrset) {
- lock_rw_unlock(&z->lock);
- lock_rw_unlock(&a->lock);
+ qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override,
+ sizeof(*r->cname_override));
+ if(qinfo->local_alias->rrset == NULL) {
return 0; /* out of memory */
}
qinfo->local_alias->rrset->rk.dname = qinfo->qname;
qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
- if(r->log)
- log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION,
- qinfo, repinfo, r->log_name);
+ if(r->log) {
+ log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION,
+ qinfo, repinfo, NULL, r->log_name);
+ }
stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
- lock_rw_unlock(&z->lock);
- lock_rw_unlock(&a->lock);
return 0;
}
if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
&ld, lzt, -1, NULL, 0, NULL, 0)) {
- if(r->log)
- log_rpz_apply(z->name,
+ if(r->log) {
+ log_rpz_apply("qname", z->name, NULL,
localzone_type_to_rpz_action(lzt), qinfo,
- repinfo, r->log_name);
+ repinfo, NULL, r->log_name);
+ }
stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
- lock_rw_unlock(&z->lock);
- lock_rw_unlock(&a->lock);
return !qinfo->local_alias;
}
ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
0 /* no local data used */, lzt);
- if(r->log)
- log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt),
- qinfo, repinfo, r->log_name);
+ if(r->log) {
+ log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt),
+ qinfo, repinfo, NULL, r->log_name);
+ }
stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
+ return ret;
+}
+
+struct clientip_synthesized_rr*
+rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is)
+{
+ struct delegpt_addr* cursor;
+ struct clientip_synthesized_rr* action = NULL;
+ if(is->dp == NULL) { return NULL; }
+ for(cursor = is->dp->target_list;
+ cursor != NULL;
+ cursor = cursor->next_target) {
+ if(cursor->bogus) { continue; }
+ action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr,
+ cursor->addrlen, "nsip");
+ if(action != NULL) { return action; }
+ }
+ return NULL;
+}
+
+struct dns_msg*
+rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
+ struct clientip_synthesized_rr* raddr, struct auth_zone* az)
+{
+ enum rpz_action action = raddr->action;
+ struct dns_msg* ret = NULL;
+
+ if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
+ verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
+ rpz_action_to_string(r->action_override), rpz_action_to_string(action));
+ action = r->action_override;
+ }
+
+ if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) {
+ verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data");
+ ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ goto done;
+ }
+
+ switch(action) {
+ case RPZ_NXDOMAIN_ACTION:
+ ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
+ break;
+ case RPZ_NODATA_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ break;
+ case RPZ_TCP_ONLY_ACTION:
+ /* basically a passthru here but the tcp-only will be
+ * honored before the query gets sent. */
+ ms->respip_action_info->action = respip_truncate;
+ ret = NULL;
+ break;
+ case RPZ_DROP_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ms->is_drop = 1;
+ break;
+ case RPZ_LOCAL_DATA_ACTION:
+ ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az);
+ if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
+ break;
+ case RPZ_PASSTHRU_ACTION:
+ ret = NULL;
+ break;
+ default:
+ verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
+ rpz_action_to_string(action));
+ ret = NULL;
+ }
+
+done:
+ if(r->log)
+ log_rpz_apply("nsip", NULL, &raddr->node,
+ action, &ms->qinfo, NULL, ms, r->log_name);
+ if(ms->env->worker)
+ ms->env->worker->stats.rpz_action[action]++;
+ lock_rw_unlock(&raddr->lock);
+ return ret;
+}
+
+struct dns_msg*
+rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
+ struct local_zone* z, struct matched_delegation_point const* match,
+ struct auth_zone* az)
+{
+ struct dns_msg* ret = NULL;
+ enum rpz_action action = localzone_type_to_rpz_action(z->type);
+
+ if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
+ verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
+ rpz_action_to_string(r->action_override), rpz_action_to_string(action));
+ action = r->action_override;
+ }
+
+ switch(action) {
+ case RPZ_NXDOMAIN_ACTION:
+ ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
+ break;
+ case RPZ_NODATA_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ break;
+ case RPZ_TCP_ONLY_ACTION:
+ /* basically a passthru here but the tcp-only will be
+ * honored before the query gets sent. */
+ ms->respip_action_info->action = respip_truncate;
+ ret = NULL;
+ break;
+ case RPZ_DROP_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
+ ms->is_drop = 1;
+ break;
+ case RPZ_LOCAL_DATA_ACTION:
+ ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az);
+ if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
+ break;
+ case RPZ_PASSTHRU_ACTION:
+ ret = NULL;
+ break;
+ default:
+ verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
+ rpz_action_to_string(action));
+ ret = NULL;
+ }
+
+ if(r->log)
+ log_rpz_apply("nsdname", match->dname, NULL,
+ action, &ms->qinfo, NULL, ms, r->log_name);
+ if(ms->env->worker)
+ ms->env->worker->stats.rpz_action[action]++;
+ lock_rw_unlock(&z->lock);
+ return ret;
+}
+
+static struct local_zone*
+rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones,
+ uint16_t qclass,
+ /* output parameter */
+ struct matched_delegation_point* match)
+{
+ struct delegpt_ns* nameserver;
+ struct local_zone* z = NULL;
+
+ /* the rpz specs match the nameserver names (NS records), not the
+ * name of the delegation point itself, to the nsdname triggers */
+ for(nameserver = dp->nslist;
+ nameserver != NULL;
+ nameserver = nameserver->next) {
+ z = rpz_find_zone(zones, nameserver->name, nameserver->namelen,
+ qclass, 0, 0, 0);
+ if(z != NULL) {
+ match->dname = nameserver->name;
+ match->dname_len = nameserver->namelen;
+ if(verbosity >= VERB_ALGO) {
+ char nm[255+1], zn[255+1];
+ dname_str(match->dname, nm);
+ dname_str(z->name, zn);
+ if(strcmp(nm, zn) != 0)
+ verbose(VERB_ALGO, "rpz: trigger nsdname %s on %s action=%s",
+ zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
+ else
+ verbose(VERB_ALGO, "rpz: trigger nsdname %s action=%s",
+ nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
+ }
+ break;
+ }
+ }
+
+ return z;
+}
+
+struct dns_msg*
+rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* is)
+{
+ struct auth_zones* az;
+ struct auth_zone* a;
+ struct clientip_synthesized_rr* raddr = NULL;
+ struct rpz* r = NULL;
+ struct local_zone* z = NULL;
+ struct matched_delegation_point match = {0};
+
+ if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
+
+ az = ms->env->auth_zones;
+
+ verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL);
+
+ lock_rw_rdlock(&az->rpz_lock);
+
+ /* precedence of RPZ works, loosely, like this:
+ * CNAMEs in order of the CNAME chain. rpzs in the order they are
+ * configured. In an RPZ: first client-IP addr, then QNAME, then
+ * response IP, then NSDNAME, then NSIP. Longest match first. Smallest
+ * one from a set. */
+ /* we use the precedence rules for the topics and triggers that
+ * are pertinent at this stage of the resolve processing */
+ for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) {
+ lock_rw_rdlock(&a->lock);
+ r = a->rpz;
+ if(r->disabled) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
+
+ /* the nsdname has precedence over the nsip triggers */
+ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
+ ms->qinfo.qclass, &match);
+ if(z != NULL) {
+ lock_rw_unlock(&a->lock);
+ break;
+ }
+
+ raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is);
+ if(raddr != NULL) {
+ lock_rw_unlock(&a->lock);
+ break;
+ }
+ lock_rw_unlock(&a->lock);
+ }
+
+ lock_rw_unlock(&az->rpz_lock);
+
+ if(raddr == NULL && z == NULL) { return NULL; }
+ else if(raddr != NULL) {
+ if(z) {
+ lock_rw_unlock(&z->lock);
+ }
+ return rpz_apply_nsip_trigger(ms, r, raddr, a);
+ } else if(z != NULL) {
+ if(raddr) {
+ lock_rw_unlock(&raddr->lock);
+ }
+ return rpz_apply_nsdname_trigger(ms, r, z, &match, a);
+ } else { return NULL; }
+}
+
+struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
+ struct iter_qstate* is)
+{
+ struct auth_zones* az;
+ struct auth_zone* a = NULL;
+ struct rpz* r = NULL;
+ struct local_zone* z = NULL;
+ enum localzone_type lzt;
+ struct dns_msg* ret = NULL;
+
+ if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
+ az = ms->env->auth_zones;
+
+ lock_rw_rdlock(&az->rpz_lock);
+
+ for(a = az->rpz_first; a; a = a->rpz_az_next) {
+ lock_rw_rdlock(&a->lock);
+ r = a->rpz;
+ if(r->disabled) {
+ lock_rw_unlock(&a->lock);
+ continue;
+ }
+ z = rpz_find_zone(r->local_zones, is->qchase.qname,
+ is->qchase.qname_len, is->qchase.qclass, 0, 0, 0);
+ if(z && r->action_override == RPZ_DISABLED_ACTION) {
+ if(r->log)
+ log_rpz_apply("qname", z->name, NULL,
+ r->action_override,
+ &ms->qinfo, NULL, ms, r->log_name);
+ if(ms->env->worker)
+ ms->env->worker->stats.rpz_action[r->action_override]++;
+ lock_rw_unlock(&z->lock);
+ z = NULL;
+ }
+ if(z) {
+ break;
+ }
+ /* not found in this auth_zone */
+ lock_rw_unlock(&a->lock);
+ }
+ lock_rw_unlock(&az->rpz_lock);
+
+ if(z == NULL)
+ return NULL;
+ if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
+ lzt = z->type;
+ } else {
+ lzt = rpz_action_to_localzone_type(r->action_override);
+ }
+
+ if(verbosity >= VERB_ALGO) {
+ char nm[255+1], zn[255+1];
+ dname_str(is->qchase.qname, nm);
+ dname_str(z->name, zn);
+ if(strcmp(zn, nm) != 0)
+ verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s",
+ zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
+ else
+ verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s",
+ nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
+ }
+ switch(localzone_type_to_rpz_action(lzt)) {
+ case RPZ_NXDOMAIN_ACTION:
+ ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a);
+ break;
+ case RPZ_NODATA_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
+ break;
+ case RPZ_TCP_ONLY_ACTION:
+ /* basically a passthru here but the tcp-only will be
+ * honored before the query gets sent. */
+ ms->respip_action_info->action = respip_truncate;
+ ret = NULL;
+ break;
+ case RPZ_DROP_ACTION:
+ ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
+ ms->is_drop = 1;
+ break;
+ case RPZ_LOCAL_DATA_ACTION:
+ ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a);
+ if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); }
+ break;
+ case RPZ_PASSTHRU_ACTION:
+ ret = NULL;
+ break;
+ default:
+ verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'",
+ rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
+ ret = NULL;
+ }
+ lock_rw_unlock(&z->lock);
+ lock_rw_unlock(&a->lock);
+ return ret;
+}
+
+static int
+rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
+ struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo,
+ uint8_t* taglist, size_t taglen, struct ub_server_stats* stats,
+ sldns_buffer* buf, struct regional* temp,
+ /* output parameters */
+ struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out)
+{
+ int ret = 0;
+ enum rpz_action client_action;
+ struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone(
+ az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out);
+
+ client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action);
+
+ if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION &&
+ client_action != RPZ_PASSTHRU_ACTION)) {
+ if(client_action == RPZ_PASSTHRU_ACTION
+ || client_action == RPZ_INVALID_ACTION
+ || (client_action == RPZ_TCP_ONLY_ACTION
+ && !rpz_is_udp_query(repinfo))) {
+ ret = 0;
+ goto done;
+ }
+ stats->rpz_action[client_action]++;
+ if(client_action == RPZ_LOCAL_DATA_ACTION) {
+ rpz_apply_clientip_localdata_action(node, env, qinfo,
+ edns, repinfo, buf, temp, *a_out);
+ } else {
+ if(*r_out && (*r_out)->log)
+ log_rpz_apply(
+ (node?"clientip":"qname"),
+ ((*z_out)?(*z_out)->name:NULL),
+ (node?&node->node:NULL),
+ client_action, qinfo, repinfo, NULL,
+ (*r_out)->log_name);
+ local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns,
+ repinfo, buf, temp, 0 /* no local data used */,
+ rpz_action_to_localzone_type(client_action));
+ }
+ ret = 1;
+ goto done;
+ }
+ ret = -1;
+done:
+ if(node != NULL) {
+ lock_rw_unlock(&node->lock);
+ }
+ return ret;
+}
+
+int
+rpz_callback_from_worker_request(struct auth_zones* az, 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, struct ub_server_stats* stats)
+{
+ struct rpz* r = NULL;
+ struct auth_zone* a = NULL;
+ struct local_zone* z = NULL;
+ int ret;
+ enum localzone_type lzt;
+
+ int clientip_trigger = rpz_apply_maybe_clientip_trigger(az, env, qinfo,
+ edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r);
+ if(clientip_trigger >= 0) {
+ if(a) {
+ lock_rw_unlock(&a->lock);
+ }
+ if(z) {
+ lock_rw_unlock(&z->lock);
+ }
+ return clientip_trigger;
+ }
+
+ if(z == NULL) {
+ if(a) {
+ lock_rw_unlock(&a->lock);
+ }
+ return 0;
+ }
+
+ log_assert(r);
+
+ if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
+ lzt = z->type;
+ } else {
+ lzt = rpz_action_to_localzone_type(r->action_override);
+ }
+
+ if(verbosity >= VERB_ALGO) {
+ char nm[255+1], zn[255+1];
+ dname_str(qinfo->qname, nm);
+ dname_str(z->name, zn);
+ if(strcmp(zn, nm) != 0)
+ verbose(VERB_ALGO, "rpz: qname trigger %s on %s with action=%s",
+ zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
+ else
+ verbose(VERB_ALGO, "rpz: qname trigger %s with action=%s",
+ nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
+ }
+
+ ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp,
+ repinfo, stats);
+
lock_rw_unlock(&z->lock);
lock_rw_unlock(&a->lock);
diff --git a/services/rpz.h b/services/rpz.h
index d5996a6cfa26..691475743606 100644
--- a/services/rpz.h
+++ b/services/rpz.h
@@ -50,6 +50,7 @@
#include "sldns/sbuffer.h"
#include "daemon/stats.h"
#include "respip/respip.h"
+struct iter_qstate;
/**
* RPZ triggers, only the QNAME trigger is currently supported in Unbound.
@@ -83,6 +84,27 @@ enum rpz_action {
RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/
};
+struct clientip_synthesized_rrset{
+ struct regional* region;
+ struct rbtree_type entries;
+ lock_rw_type lock; /* lock on the respip tree */
+};
+
+struct clientip_synthesized_rr {
+ /** node in address tree */
+ struct addr_tree_node node;
+ /** lock on the node item */
+ lock_rw_type lock;
+ /** tag bitlist */
+ uint8_t* taglist;
+ /** length of the taglist (in bytes) */
+ size_t taglen;
+ /** action for this address span */
+ enum rpz_action action;
+ /** "local data" for this node */
+ struct local_rrset* data;
+};
+
/**
* RPZ containing policies. Pointed to from corresponding auth-zone. Part of a
* linked list to keep configuration order. Iterating or changing the linked
@@ -92,6 +114,9 @@ enum rpz_action {
struct rpz {
struct local_zones* local_zones;
struct respip_set* respip_set;
+ struct clientip_synthesized_rrset* client_set;
+ struct clientip_synthesized_rrset* ns_set;
+ struct local_zones* nsdname_zones;
uint8_t* taglist;
size_t taglistlen;
enum rpz_action action_override;
@@ -147,16 +172,39 @@ void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
* @param temp: scratchpad
* @param repinfo: reply info
* @param taglist: taglist to lookup.
- * @param taglen: lenth of taglist.
+ * @param taglen: length of taglist.
* @param stats: worker stats struct
* @return: 1 if client answer is ready, 0 to continue resolving
*/
-int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
+int rpz_callback_from_worker_request(struct auth_zones* az, 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, struct ub_server_stats* stats);
/**
+ * Callback to process when the iterator module is about to send queries.
+ * Checks for nsip and nsdname triggers.
+ * @param qstate: the query state.
+ * @param iq: iterator module query state.
+ * @return NULL if nothing is done. Or a new message with the contents from
+ * the rpz, based on the delegation point. It is allocated in the
+ * qstate region.
+ */
+struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate* qstate,
+ struct iter_qstate* iq);
+
+/**
+ * Callback to process when the iterator module has followed a cname.
+ * There can be a qname trigger for the new query name.
+ * @param qstate: the query state.
+ * @param iq: iterator module query state.
+ * @return NULL if nothing is done. Or a new message with the contents from
+ * the rpz, based on the iq.qchase. It is allocated in the qstate region.
+ */
+struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* qstate,
+ struct iter_qstate* iq);
+
+/**
* Delete RPZ
* @param r: RPZ struct to delete
*/
@@ -186,7 +234,7 @@ enum rpz_action
respip_action_to_rpz_action(enum respip_action a);
/**
- * Prepare RPZ after procesing feed content.
+ * Prepare RPZ after processing feed content.
* @param r: RPZ to use
*/
void rpz_finish_config(struct rpz* r);