[master] ECS authoritative support
3936. [func] Added authoritative support for the EDNS Client Subnet (ECS) option. ACLs can now include "ecs" elements which specify an address or network prefix; if an ECS option is included in a DNS query, then the address encoded in the option will be matched against "ecs" ACL elements. Also, if an ECS address is included in a query, then it will be used instead of the client source address when matching "geoip" ACL elements. This behavior can be overridden with "geoip-use-ecs no;". When "ecs" or "geoip" ACL elements are used to select a view for a query, the response will include an ECS option to indicate which client network the answer is valid for. (Thanks to Vincent Bernat.) [RT #36781]
This commit is contained in:
139
lib/dns/acl.c
139
lib/dns/acl.c
@@ -15,8 +15,6 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: acl.c,v 1.55 2011/06/17 23:47:49 tbox Exp $ */
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <config.h>
|
||||
@@ -194,10 +192,25 @@ dns_acl_match(const isc_netaddr_t *reqaddr,
|
||||
int *match,
|
||||
const dns_aclelement_t **matchelt)
|
||||
{
|
||||
isc_uint16_t bitlen, family;
|
||||
return (dns_acl_match2(reqaddr, reqsigner, NULL, 0, NULL, acl, env,
|
||||
match, matchelt));
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_acl_match2(const isc_netaddr_t *reqaddr,
|
||||
const dns_name_t *reqsigner,
|
||||
const isc_netaddr_t *ecs,
|
||||
isc_uint8_t ecslen,
|
||||
isc_uint8_t *scope,
|
||||
const dns_acl_t *acl,
|
||||
const dns_aclenv_t *env,
|
||||
int *match,
|
||||
const dns_aclelement_t **matchelt)
|
||||
{
|
||||
isc_uint16_t bitlen;
|
||||
isc_prefix_t pfx;
|
||||
isc_radix_node_t *node = NULL;
|
||||
const isc_netaddr_t *addr;
|
||||
const isc_netaddr_t *addr = reqaddr;
|
||||
isc_netaddr_t v4addr;
|
||||
isc_result_t result;
|
||||
int match_num = -1;
|
||||
@@ -205,20 +218,19 @@ dns_acl_match(const isc_netaddr_t *reqaddr,
|
||||
|
||||
REQUIRE(reqaddr != NULL);
|
||||
REQUIRE(matchelt == NULL || *matchelt == NULL);
|
||||
REQUIRE(ecs != NULL || scope == NULL);
|
||||
|
||||
if (env == NULL || env->match_mapped == ISC_FALSE ||
|
||||
reqaddr->family != AF_INET6 ||
|
||||
!IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
|
||||
addr = reqaddr;
|
||||
else {
|
||||
isc_netaddr_fromv4mapped(&v4addr, reqaddr);
|
||||
if (env != NULL && env->match_mapped &&
|
||||
addr->family == AF_INET6 &&
|
||||
IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
|
||||
{
|
||||
isc_netaddr_fromv4mapped(&v4addr, addr);
|
||||
addr = &v4addr;
|
||||
}
|
||||
|
||||
/* Always match with host addresses. */
|
||||
family = addr->family;
|
||||
bitlen = family == AF_INET6 ? 128 : 32;
|
||||
NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
|
||||
bitlen = (addr->family == AF_INET6) ? 128 : 32;
|
||||
NETADDR_TO_PREFIX_T(addr, pfx, bitlen, ISC_FALSE);
|
||||
|
||||
/* Assume no match. */
|
||||
*match = 0;
|
||||
@@ -228,37 +240,75 @@ dns_acl_match(const isc_netaddr_t *reqaddr,
|
||||
|
||||
/* Found a match. */
|
||||
if (result == ISC_R_SUCCESS && node != NULL) {
|
||||
match_num = node->node_num[ISC_IS6(family)];
|
||||
if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
|
||||
int off = ISC_RADIX_OFF(&pfx);
|
||||
match_num = node->node_num[off];
|
||||
if (*(isc_boolean_t *) node->data[off])
|
||||
*match = match_num;
|
||||
else
|
||||
*match = -match_num;
|
||||
}
|
||||
|
||||
isc_refcount_destroy(&pfx.refcount);
|
||||
|
||||
/*
|
||||
* If ecs is not NULL, we search the radix tree again to
|
||||
* see if we find a better match on an ECS node
|
||||
*/
|
||||
if (ecs != NULL) {
|
||||
node = NULL;
|
||||
addr = ecs;
|
||||
|
||||
if (env != NULL && env->match_mapped &&
|
||||
addr->family == AF_INET6 &&
|
||||
IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
|
||||
{
|
||||
isc_netaddr_fromv4mapped(&v4addr, addr);
|
||||
addr = &v4addr;
|
||||
}
|
||||
|
||||
NETADDR_TO_PREFIX_T(addr, pfx, ecslen, ISC_TRUE);
|
||||
|
||||
result = isc_radix_search(acl->iptable->radix, &node, &pfx);
|
||||
if (result == ISC_R_SUCCESS && node != NULL) {
|
||||
int off = ISC_RADIX_OFF(&pfx);
|
||||
if (match_num == -1 ||
|
||||
node->node_num[off] < match_num)
|
||||
{
|
||||
match_num = node->node_num[off];
|
||||
if (scope != NULL)
|
||||
*scope = node->bit;
|
||||
if (*(isc_boolean_t *) node->data[off])
|
||||
*match = match_num;
|
||||
else
|
||||
*match = -match_num;
|
||||
}
|
||||
}
|
||||
|
||||
isc_refcount_destroy(&pfx.refcount);
|
||||
}
|
||||
|
||||
/* Now search non-radix elements for a match with a lower node_num. */
|
||||
for (i = 0; i < acl->length; i++) {
|
||||
dns_aclelement_t *e = &acl->elements[i];
|
||||
|
||||
/* Already found a better match? */
|
||||
if (match_num != -1 && match_num < e->node_num) {
|
||||
isc_refcount_destroy(&pfx.refcount);
|
||||
return (ISC_R_SUCCESS);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dns_aclelement_match(reqaddr, reqsigner,
|
||||
e, env, matchelt)) {
|
||||
if (dns_aclelement_match2(reqaddr, reqsigner, ecs, ecslen,
|
||||
scope, e, env, matchelt))
|
||||
{
|
||||
if (match_num == -1 || e->node_num < match_num) {
|
||||
if (e->negative == ISC_TRUE)
|
||||
if (e->negative)
|
||||
*match = -e->node_num;
|
||||
else
|
||||
*match = e->node_num;
|
||||
}
|
||||
isc_refcount_destroy(&pfx.refcount);
|
||||
return (ISC_R_SUCCESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isc_refcount_destroy(&pfx.refcount);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -349,7 +399,7 @@ dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
|
||||
#endif
|
||||
|
||||
/* reverse sense of positives if this is a negative acl */
|
||||
if (!pos && source->elements[i].negative == ISC_FALSE) {
|
||||
if (!pos && !source->elements[i].negative) {
|
||||
dest->elements[nelem + i].negative = ISC_TRUE;
|
||||
} else {
|
||||
dest->elements[nelem + i].negative =
|
||||
@@ -386,10 +436,29 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
|
||||
const dns_aclelement_t *e,
|
||||
const dns_aclenv_t *env,
|
||||
const dns_aclelement_t **matchelt)
|
||||
{
|
||||
return (dns_aclelement_match2(reqaddr, reqsigner, NULL, 0, NULL,
|
||||
e, env, matchelt));
|
||||
}
|
||||
|
||||
isc_boolean_t
|
||||
dns_aclelement_match2(const isc_netaddr_t *reqaddr,
|
||||
const dns_name_t *reqsigner,
|
||||
const isc_netaddr_t *ecs,
|
||||
isc_uint8_t ecslen,
|
||||
isc_uint8_t *scope,
|
||||
const dns_aclelement_t *e,
|
||||
const dns_aclenv_t *env,
|
||||
const dns_aclelement_t **matchelt)
|
||||
{
|
||||
dns_acl_t *inner = NULL;
|
||||
int indirectmatch;
|
||||
isc_result_t result;
|
||||
#ifdef HAVE_GEOIP
|
||||
const isc_netaddr_t *addr = NULL;
|
||||
#endif
|
||||
|
||||
REQUIRE(ecs != NULL || scope == NULL);
|
||||
|
||||
switch (e->type) {
|
||||
case dns_aclelementtype_keyname:
|
||||
@@ -421,15 +490,17 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
|
||||
case dns_aclelementtype_geoip:
|
||||
if (env == NULL || env->geoip == NULL)
|
||||
return (ISC_FALSE);
|
||||
return (dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem));
|
||||
addr = (env->geoip_use_ecs && ecs != NULL) ? ecs : reqaddr;
|
||||
return (dns_geoip_match(addr, scope, env->geoip,
|
||||
&e->geoip_elem));
|
||||
#endif
|
||||
default:
|
||||
/* Should be impossible. */
|
||||
INSIST(0);
|
||||
}
|
||||
|
||||
result = dns_acl_match(reqaddr, reqsigner, inner, env,
|
||||
&indirectmatch, matchelt);
|
||||
result = dns_acl_match2(reqaddr, reqsigner, ecs, ecslen, scope,
|
||||
inner, env, &indirectmatch, matchelt);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
|
||||
/*
|
||||
@@ -438,7 +509,6 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
|
||||
* surprise positive match through double negation.
|
||||
* XXXDCL this should be documented.
|
||||
*/
|
||||
|
||||
if (indirectmatch > 0) {
|
||||
if (matchelt != NULL)
|
||||
*matchelt = e;
|
||||
@@ -449,7 +519,6 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
|
||||
* A negative indirect match may have set *matchelt, but we don't
|
||||
* want it set when we return.
|
||||
*/
|
||||
|
||||
if (matchelt != NULL)
|
||||
*matchelt = NULL;
|
||||
|
||||
@@ -519,17 +588,15 @@ initialize_action(void) {
|
||||
*/
|
||||
static void
|
||||
is_insecure(isc_prefix_t *prefix, void **data) {
|
||||
isc_boolean_t secure;
|
||||
int bitlen, family;
|
||||
int bitlen, family, off;
|
||||
|
||||
bitlen = prefix->bitlen;
|
||||
family = prefix->family;
|
||||
|
||||
/* Negated entries are always secure. */
|
||||
secure = * (isc_boolean_t *)data[ISC_IS6(family)];
|
||||
if (!secure) {
|
||||
off = ISC_RADIX_OFF(prefix);
|
||||
if (data[off] != NULL && * (isc_boolean_t *) data[off])
|
||||
return;
|
||||
}
|
||||
|
||||
/* If loopback prefix found, return */
|
||||
switch (family) {
|
||||
@@ -628,6 +695,7 @@ dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
|
||||
env->match_mapped = ISC_FALSE;
|
||||
#ifdef HAVE_GEOIP
|
||||
env->geoip = NULL;
|
||||
env->geoip_use_ecs = ISC_FALSE;
|
||||
#endif
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
@@ -644,6 +712,9 @@ dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
|
||||
dns_acl_detach(&t->localnets);
|
||||
dns_acl_attach(s->localnets, &t->localnets);
|
||||
t->match_mapped = s->match_mapped;
|
||||
#ifdef HAVE_GEOIP
|
||||
t->geoip_use_ecs = s->geoip_use_ecs;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user