diff --git a/respip/respip.c b/respip/respip.c index 6fa4f1885..82ca37d50 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -807,7 +807,6 @@ respip_nodata_answer(uint16_t qtype, enum respip_action action, * is explicitly specified. */ int rcode = (action == respip_always_nxdomain)? LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; - /* We should empty the answer section except for any preceding * CNAMEs (in that case rrset_id > 0). Type-ANY case is * special as noted in respip_data_answer(). */ diff --git a/services/localzone.c b/services/localzone.c index cad460663..b5d8472bc 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1514,6 +1514,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, @@ -1536,7 +1545,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, @@ -1546,8 +1557,10 @@ 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; + rcode = lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode; if(z->soa) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); @@ -1763,6 +1776,7 @@ const char* local_zone_type2str(enum localzone_type t) case local_zone_always_nodata: return "always_nodata"; case local_zone_always_deny: return "always_deny"; case local_zone_noview: return "noview"; + case local_zone_truncate: return "truncate"; case local_zone_invalid: return "invalid"; } return "badtyped"; @@ -1800,6 +1814,8 @@ int local_zone_str2type(const char* type, enum localzone_type* t) *t = local_zone_always_deny; 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 bb3593936..39517c36a 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -98,6 +98,8 @@ enum localzone_type { local_zone_always_deny, /** 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 }; @@ -556,6 +558,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/rpz.c b/services/rpz.c index 13304652c..25f8c8892 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -213,8 +213,8 @@ rpz_action_to_localzone_type(enum rpz_action a) 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_TCP_ONLY_ACTION: return local_zone_truncate; case RPZ_INVALID_ACTION: /* fallthrough */ - case RPZ_TCP_ONLY_ACTION: /* fallthrough */ default: return local_zone_invalid; } } @@ -223,15 +223,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; } } @@ -244,6 +244,7 @@ localzone_type_to_rpz_action(enum localzone_type lzt) 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: default: return RPZ_INVALID_ACTION; @@ -259,6 +260,7 @@ respip_action_to_rpz_action(enum respip_action a) 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: default: return RPZ_INVALID_ACTION; @@ -478,13 +480,17 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, char* rrstr; int newzone = 0; - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) { + if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); free(dname); return; } + if(a == RPZ_TCP_ONLY_ACTION) { + verbose(VERB_ALGO, "RPZ: insert qname trigger: tcp-only"); + } + lock_rw_wrlock(&r->local_zones->lock); /* exact match */ z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs, @@ -550,13 +556,16 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, 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) { + if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); return 0; } + if(a == RPZ_TCP_ONLY_ACTION) { + verbose(VERB_ALGO, "RPZ: insert respip trigger: tcp-only"); + } + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) return 0; @@ -984,6 +993,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lock_rw_unlock(&a->lock); /* not found in this auth_zone */ } lock_rw_unlock(&az->rpz_lock); + if(!z) return 0; /* not holding auth_zone.lock anymore */ @@ -1032,7 +1042,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lock_rw_unlock(&a->lock); return !qinfo->local_alias; } - +verbose(VERB_ALGO, "xxxxxx repinfo=%p is_udp=%d", repinfo, repinfo->c->type == comm_udp); ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, lzt); if(r->log) diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index 7940e9392..4f17f8577 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -38,6 +38,7 @@ d TXT "local data 2nd zone" e CNAME *.a.example. *.e CNAME *.b.example. drop CNAME rpz-drop. +tcp CNAME rpz-tcp-only. TEMPFILE_END stub-zone: @@ -295,10 +296,45 @@ something.e.b.example. IN TXT "*.b.example. answer from upstream ns" ENTRY_END ; deny zone -STEP 90 QUERY +;STEP 90 QUERY +;ENTRY_BEGIN +;SECTION QUESTION +;drop. IN TXT +;ENTRY_END + +; tcp-only action + +STEP 95 QUERY ENTRY_BEGIN SECTION QUESTION -drop. IN TXT +tcp. IN TXT ENTRY_END + +STEP 96 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA AA TC NOERROR +SECTION QUESTION +tcp. IN TXT +SECTION ANSWER +ENTRY_END + +STEP 97 QUERY +ENTRY_BEGIN +MATCH TCP +SECTION QUESTION +tcp. IN TXT +ENTRY_END + +STEP 98 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA AA NOERROR +SECTION QUESTION +tcp. IN TXT +SECTION ANSWER +tcp. IN TXT "local tcp data 2nd zone" +ENTRY_END + ; no answer is checked at exit of testbound. SCENARIO_END diff --git a/testdata/rpz_respip.rpl b/testdata/rpz_respip.rpl index 94f998be6..894a7cc5f 100644 --- a/testdata/rpz_respip.rpl +++ b/testdata/rpz_respip.rpl @@ -20,6 +20,7 @@ $ORIGIN rpz.example.com. 16.0.0.10.10.rpz-ip CNAME . 24.0.10.10.10.rpz-ip CNAME rpz-drop. 32.10.10.10.10.rpz-ip CNAME rpz-passthru. +32.1.1.1.10.rpz-ip CNAME rpz-tcp-only. 32.zz.db8.2001.rpz-ip CNAME *. 48.zz.aa.db8.2001.rpz-ip CNAME . 64.zz.bb.aa.db8.2001.rpz-ip CNAME rpz-drop. @@ -217,6 +218,16 @@ SECTION ANSWER h. IN AAAA 2001:db8:aa:bb:cc::124 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +y. IN A 10.1.1.1 +ENTRY_END + RANGE_END STEP 1 QUERY @@ -446,4 +457,21 @@ SECTION QUESTION e. IN AAAA ENTRY_END STEP 29 TIME_PASSES ELAPSE 12 + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +y. IN A +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR TC RD RA NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END