[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:
Evan Hunt
2014-08-28 22:05:57 -07:00
parent 180319f572
commit d46855caed
35 changed files with 1155 additions and 357 deletions

21
CHANGES
View File

@@ -1,3 +1,24 @@
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]
3935. [bug] "geoip asnum" ACL elements would not match unless
the full organization name was specified. They
can now match against the AS number alone (e.g.,

6
README
View File

@@ -56,6 +56,12 @@ BIND 9.11.0
BIND 9.11.0 includes a number of changes from BIND 9.10 and earlier
releases. New features include:
- The EDNS Client Subnet (ECS) option is now supported for
authoritative servers; if a query contains an ECS option
then ACLs containing "geoip" or "ecs" elements can match
against the the address encoded in the option. This can be
used to select a view for a query, so that different answers
can be provided depending on the client network.
- The EDNS EXPIRE option has been implemented on the client
side, allowing a slave server to set the expiration timer
correctly when transferring zone data from another slave

View File

@@ -122,6 +122,7 @@
#endif
#define SIT_SIZE 24U /* 8 + 4 + 4 + 8 */
#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
/*% nameserver client manager structure */
struct ns_clientmgr {
@@ -244,7 +245,8 @@ static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
dns_dispatch_t *disp, isc_boolean_t tcp);
static inline isc_boolean_t
allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr,
isc_uint8_t ecs_addrlen, isc_uint8_t *ecs_scope, dns_acl_t *acl);
#ifdef ISC_PLATFORM_USESIT
static void compute_sit(ns_client_t *client, isc_uint32_t when,
isc_uint32_t nonce, isc_buffer_t *buf);
@@ -1042,7 +1044,8 @@ client_send(ns_client_t *client) {
if (client->message->tsigkey != NULL)
name = &client->message->tsigkey->name;
if (client->view->nocasecompress == NULL ||
!allowed(&netaddr, name, client->view->nocasecompress))
!allowed(&netaddr, name, NULL, 0, NULL,
client->view->nocasecompress))
{
dns_compress_setsensitive(&cctx, ISC_TRUE);
}
@@ -1381,6 +1384,7 @@ isc_result_t
ns_client_addopt(ns_client_t *client, dns_message_t *message,
dns_rdataset_t **opt)
{
unsigned char ecs[ECS_SIZE];
char nsid[BUFSIZ], *nsidp;
#ifdef ISC_PLATFORM_USESIT
unsigned char sit[SIT_SIZE];
@@ -1459,6 +1463,38 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
ednsopts[count].value = expire;
count++;
}
if (((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) &&
(client->ecs_addr.family == AF_INET ||
client->ecs_addr.family == AF_INET6))
{
int i, addrbytes = (client->ecs_addrlen + 7) / 8;
isc_uint8_t *paddr;
isc_buffer_t buf;
/* Add client subnet option. */
isc_buffer_init(&buf, ecs, sizeof(ecs));
if (client->ecs_addr.family == AF_INET)
isc_buffer_putuint16(&buf, 1);
else
isc_buffer_putuint16(&buf, 2);
isc_buffer_putuint8(&buf, client->ecs_addrlen);
isc_buffer_putuint8(&buf, client->ecs_scope);
paddr = (isc_uint8_t *) &client->ecs_addr.type;
for (i = 0; i < addrbytes; i++) {
unsigned char uc;
uc = paddr[i];
if (i == addrbytes - 1 &&
((client->ecs_addrlen % 8) != 0))
uc &= (1U << (8 - (client->ecs_addrlen % 8)));
isc_buffer_putuint8(&buf, uc);
}
ednsopts[count].code = DNS_OPT_CLIENT_SUBNET;
ednsopts[count].length = addrbytes + 4;
ednsopts[count].value = ecs;
count++;
}
result = dns_message_buildopt(message, opt, 0, udpsize, flags,
ednsopts, count);
@@ -1466,14 +1502,17 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
}
static inline isc_boolean_t
allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
allowed(isc_netaddr_t *addr, dns_name_t *signer,
isc_netaddr_t *ecs_addr, isc_uint8_t ecs_addrlen,
isc_uint8_t *ecs_scope, dns_acl_t *acl)
{
int match;
isc_result_t result;
if (acl == NULL)
return (ISC_TRUE);
result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
&match, NULL);
result = dns_acl_match2(addr, signer, ecs_addr, ecs_addrlen, ecs_scope,
acl, &ns_g_server->aclenv, &match, NULL);
if (result == ISC_R_SUCCESS && match > 0)
return (ISC_TRUE);
return (ISC_FALSE);
@@ -1536,8 +1575,10 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
tsig = dns_tsigkey_identity(mykey);
}
if (allowed(&netsrc, tsig, view->matchclients) &&
allowed(&netdst, tsig, view->matchdestinations))
if (allowed(&netsrc, tsig, NULL, 0, NULL,
view->matchclients) &&
allowed(&netdst, tsig, NULL, 0, NULL,
view->matchdestinations))
break;
}
return (ISC_TF(view == myview));
@@ -1718,6 +1759,81 @@ process_sit(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
}
#endif
static isc_result_t
process_ecs(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
isc_uint16_t family;
isc_uint8_t addrlen, addrbytes, scope, *paddr;
isc_netaddr_t caddr;
int i;
if (optlen < 4) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
"EDNS client subnet option too short");
return (DNS_R_FORMERR);
}
family = isc_buffer_getuint16(buf);
addrlen = isc_buffer_getuint8(buf);
scope = isc_buffer_getuint8(buf);
optlen -= 4;
if (scope != 0U) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
"EDNS client subnet option: invalid scope");
return (DNS_R_FORMERR);
}
memset(&caddr, 0, sizeof(caddr));
switch (family) {
case 1:
if (addrlen > 32U)
goto invalid_length;
caddr.family = AF_INET;
break;
case 2:
if (addrlen > 128U) {
invalid_length:
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
"EDNS client subnet option: invalid "
"address length (%u) for %s",
addrlen, family == 1 ? "IPv4" : "IPv6");
return (DNS_R_FORMERR);
}
caddr.family = AF_INET6;
break;
default:
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
"EDNS client subnet option: invalid family");
return (DNS_R_FORMERR);
}
addrbytes = (addrlen + 7) / 8;
if (isc_buffer_remaininglength(buf) < addrbytes) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
"EDNS client subnet option: address too short");
return (DNS_R_FORMERR);
}
paddr = (isc_uint8_t *) &caddr.type;
for (i = 0; i < addrbytes; i++) {
paddr[i] = isc_buffer_getuint8(buf);
optlen--;
}
memmove(&client->ecs_addr, &caddr, sizeof(caddr));
client->ecs_addrlen = addrlen;
client->ecs_scope = 0;
client->attributes |= NS_CLIENTATTR_HAVEECS;
isc_buffer_forward(buf, optlen);
return (ISC_R_SUCCESS);
}
static isc_result_t
process_opt(ns_client_t *client, dns_rdataset_t *opt) {
dns_rdata_t rdata;
@@ -1788,6 +1904,15 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
client->attributes |= NS_CLIENTATTR_WANTEXPIRE;
isc_buffer_forward(&optbuf, optlen);
break;
case DNS_OPT_CLIENT_SUBNET:
result = process_ecs(client, &optbuf, optlen);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
goto cleanup;
}
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_ecsopt);
break;
default:
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_otheropt);
@@ -1925,7 +2050,6 @@ client_request(isc_task_t *task, isc_event_t *event) {
* client_newconn.
*/
if (!TCP_CLIENT(client)) {
if (ns_g_server->blackholeacl != NULL &&
dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
&ns_g_server->aclenv,
@@ -2033,6 +2157,10 @@ client_request(isc_task_t *task, isc_event_t *event) {
opt = NULL;
else
opt = dns_message_getopt(client->message);
client->ecs_addrlen = 0;
client->ecs_scope = 0;
if (opt != NULL) {
/*
* Are we dropping all EDNS queries?
@@ -2117,17 +2245,29 @@ client_request(isc_task_t *task, isc_event_t *event) {
client->message->rdclass == dns_rdataclass_any)
{
dns_name_t *tsig = NULL;
isc_netaddr_t *addr = NULL;
isc_uint8_t *scope = NULL;
sigresult = dns_message_rechecksig(client->message,
view);
if (sigresult == ISC_R_SUCCESS)
tsig = dns_tsigkey_identity(client->message->tsigkey);
if (sigresult == ISC_R_SUCCESS) {
dns_tsigkey_t *tsigkey;
if (allowed(&netaddr, tsig, view->matchclients) &&
allowed(&client->destaddr, tsig,
view->matchdestinations) &&
!((client->message->flags & DNS_MESSAGEFLAG_RD)
== 0 && view->matchrecursiveonly))
tsigkey = client->message->tsigkey;
tsig = dns_tsigkey_identity(tsigkey);
}
if ((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) {
addr = &client->ecs_addr;
scope = &client->ecs_scope;
}
if (allowed(&netaddr, tsig, addr, client->ecs_addrlen,
scope, view->matchclients) &&
allowed(&client->destaddr, tsig, NULL,
0, NULL, view->matchdestinations) &&
!(view->matchrecursiveonly &&
(client->message->flags & DNS_MESSAGEFLAG_RD) == 0))
{
dns_view_attach(view, &client->view);
break;
@@ -2519,6 +2659,8 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
client->recursionquota = NULL;
client->interface = NULL;
client->peeraddr_valid = ISC_FALSE;
client->ecs_addrlen = 0;
client->ecs_scope = 0;
#ifdef ALLOW_FILTER_AAAA
client->filter_aaaa = dns_aaaa_ok;
#endif
@@ -3055,6 +3197,8 @@ ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
{
isc_result_t result;
isc_netaddr_t tmpnetaddr;
isc_netaddr_t *ecs_addr = NULL;
isc_uint8_t ecs_addrlen = 0;
int match;
if (acl == NULL) {
@@ -3069,11 +3213,18 @@ ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
netaddr = &tmpnetaddr;
}
result = dns_acl_match(netaddr, client->signer, acl,
&ns_g_server->aclenv, &match, NULL);
if ((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) {
ecs_addr = &client->ecs_addr;
ecs_addrlen = client->ecs_addrlen;
}
result = dns_acl_match2(netaddr, client->signer,
ecs_addr, ecs_addrlen, NULL, acl,
&ns_g_server->aclenv, &match, NULL);
if (result != ISC_R_SUCCESS)
goto deny; /* Internal error, already logged. */
if (match > 0)
goto allow;
goto deny; /* Negative match or no match. */

View File

@@ -177,6 +177,11 @@ options {\n\
nsec3-test-zone no;\n\
allow-new-zones no;\n\
"
#ifdef HAVE_GEOIP
"\
geoip-use-ecs yes;\n\
"
#endif
#ifdef ALLOW_FILTER_AAAA
" filter-aaaa-on-v4 no;\n\
filter-aaaa-on-v6 no;\n\

View File

@@ -137,9 +137,15 @@ struct ns_client {
isc_quota_t *tcpquota;
isc_quota_t *recursionquota;
ns_interface_t *interface;
isc_sockaddr_t peeraddr;
isc_boolean_t peeraddr_valid;
isc_netaddr_t destaddr;
isc_netaddr_t ecs_addr; /*%< EDNS client subnet */
isc_uint8_t ecs_addrlen;
isc_uint8_t ecs_scope;
struct in6_pktinfo pktinfo;
isc_dscp_t dscp;
isc_event_t ctlevent;
@@ -187,6 +193,7 @@ typedef ISC_LIST(ns_client_t) client_list_t;
#define NS_CLIENTATTR_WANTEXPIRE 0x0800 /*%< return seconds to expire */
#define NS_CLIENTATTR_HAVEEXPIRE 0x1000 /*%< return seconds to expire */
#define NS_CLIENTATTR_WANTOPT 0x2000 /*%< add opt to reply */
#define NS_CLIENTATTR_HAVEECS 0x4000 /*%< sent an ECS option */
extern unsigned int ns_client_requests;

View File

@@ -182,18 +182,19 @@ enum {
dns_nsstatscounter_nsidopt = 43,
dns_nsstatscounter_expireopt = 44,
dns_nsstatscounter_otheropt = 45,
dns_nsstatscounter_ecsopt = 46,
#ifdef ISC_PLATFORM_USESIT
dns_nsstatscounter_sitopt = 46,
dns_nsstatscounter_sitbadsize = 47,
dns_nsstatscounter_sitbadtime = 48,
dns_nsstatscounter_sitnomatch = 49,
dns_nsstatscounter_sitmatch = 50,
dns_nsstatscounter_sitnew = 51,
dns_nsstatscounter_sitopt = 47,
dns_nsstatscounter_sitbadsize = 48,
dns_nsstatscounter_sitbadtime = 49,
dns_nsstatscounter_sitnomatch = 50,
dns_nsstatscounter_sitmatch = 51,
dns_nsstatscounter_sitnew = 52,
dns_nsstatscounter_max = 52
dns_nsstatscounter_max = 53
#else
dns_nsstatscounter_max = 46
dns_nsstatscounter_max = 47
#endif
};

View File

@@ -4684,6 +4684,9 @@ directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
static void
scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
isc_boolean_t match_mapped = server->aclenv.match_mapped;
#ifdef HAVE_GEOIP
isc_boolean_t use_ecs = server->aclenv.geoip_use_ecs;
#endif
ns_interfacemgr_scan(server->interfacemgr, verbose);
/*
@@ -4694,6 +4697,9 @@ scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
ns_interfacemgr_getaclenv(server->interfacemgr));
server->aclenv.match_mapped = match_mapped;
#ifdef HAVE_GEOIP
server->aclenv.geoip_use_ecs = use_ecs;
#endif
}
static isc_result_t
@@ -5554,6 +5560,11 @@ load_configuration(const char *filename, ns_server_t *server,
} else
ns_geoip_load(NULL);
ns_g_aclconfctx->geoip = ns_g_geoip;
obj = NULL;
result = ns_config_get(maps, "geoip-use-ecs", &obj);
INSIST(result == ISC_R_SUCCESS);
ns_g_server->aclenv.geoip_use_ecs = cfg_obj_asboolean(obj);
#endif /* HAVE_GEOIP */
/*

View File

@@ -242,6 +242,7 @@ init_desc(void) {
"SitNoMatch");
SET_NSSTATDESC(sitmatch, "source identity token - match", "SitMatch");
#endif
SET_NSSTATDESC(ecsopt, "EDNS client subnet option recieved", "ECSOpt");
INSIST(i == dns_nsstatscounter_max);
/* Initialize resolver statistics */

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion no;
notify yes;
ixfr-from-differences yes;
check-integrity no;
allow-query-on { 10.53.0.2; };
};
include "../../common/controls.conf";
zone "." {
type hint;
file "../../common/root.hint";
};
zone "example" {
type master;
file "example.db";
};
zone "tsigzone" {
type master;
file "tsigzone.db";
allow-transfer { ecs 10.53/16; !10/8; };
};

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion no;
notify yes;
ixfr-from-differences yes;
check-integrity no;
allow-query-on { 10.53.0.2; };
};
include "../../common/controls.conf";
view one {
match-clients { ecs 192.0.2/24; };
zone "." {
type hint;
file "../../common/root.hint";
};
zone "example" {
type master;
file "example.db";
};
};
view two {
zone "." {
type hint;
file "../../common/root.hint";
};
zone "example" {
type master;
file "example.db";
};
};

View File

@@ -150,5 +150,35 @@ $DIG +tcp soa example. \
@10.53.0.2 -b 10.53.0.3 -p 5300 > dig.out.${t}
grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || { echo "I:test $t failed" ; status=1; }
echo "I:testing EDNS client-subnet ACL processing"
cp -f ns2/named6.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
sleep 5
# should fail
t=`expr $t + 1`
$DIG $DIGOPTS tsigzone. \
@10.53.0.2 -b 10.53.0.2 axfr -p 5300 > dig.out.${t}
grep "^;" dig.out.${t} > /dev/null 2>&1 || { echo "I:test $t failed" ; status=1; }
# should succeed
t=`expr $t + 1`
$DIG $DIGOPTS tsigzone. \
@10.53.0.2 -b 10.53.0.2 +subnet="10.53.0/24" axfr -p 5300 > dig.out.${t}
grep "^;" dig.out.${t} > /dev/null 2>&1 && { echo "I:test $t failed" ; status=1; }
echo "I:testing EDNS client-subnet response scope"
cp -f ns2/named7.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
sleep 5
t=`expr $t + 1`
$DIG example. soa @10.53.0.2 +subnet="10.53.0.1/32" -p 5300 > dig.out.${t}
grep "CLIENT-SUBNET.*10.53.0.1/32/0" dig.out.${t} > /dev/null || { echo "I:test $t failed" ; status=1; }
t=`expr $t + 1`
$DIG example. soa @10.53.0.2 +subnet="192.0.2.128/32" -p 5300 > dig.out.${t}
grep "CLIENT-SUBNET.*192.0.2.128/32/24" dig.out.${t} > /dev/null || { echo "I:test $t failed" ; status=1; }
echo "I:exit status: $status"
exit $status

View File

@@ -15,5 +15,5 @@
# PERFORMANCE OF THIS SOFTWARE.
rm -f ns2/named.conf
rm -f ns2/example[1234567].db
rm -f ns2/example*.db
rm -f dig.out.* rndc.out.*

View File

@@ -5,3 +5,4 @@
10.53.0.5/32 CL
10.53.0.6/32 DE
10.53.0.7/32 EH
192.0.2/24 O1
1 10.53.0.1/32 AU
5 10.53.0.5/32 CL
6 10.53.0.6/32 DE
7 10.53.0.7/32 EH
8 192.0.2/24 O1

View File

@@ -18,8 +18,8 @@ GeoIPDoain.dat: Domain Name
GeoIPASNum.dat: AS Number
GeoIPNetSpeed.dat: Net Speed
GeoIP.dat can also be generated using the open source 'geoip-csv-to-dat'
utility:
GeoIP.dat can also be egenerated using the open source 'geoip-csv-to-dat'
utility (also known in some packages as "geoip-generator"):
$ geoip-csv-to-dat -i "BIND9 geoip test data v1" -o GeoIP.dat << EOF
"10.53.0.1","10.53.0.1","171245569","171245569","AU","Australia"
@@ -29,4 +29,5 @@ $ geoip-csv-to-dat -i "BIND9 geoip test data v1" -o GeoIP.dat << EOF
"10.53.0.5","10.53.0.5","171245573","171245573","CL","Chile"
"10.53.0.6","10.53.0.6","171245574","171245574","DE","Germany"
"10.53.0.7","10.53.0.7","171245575","171245575","EH","Western Sahara"
"192.0.2.0","192.0.2.255","3221225984","3221226239","O1","Other"
EOF

View File

@@ -95,6 +95,14 @@ view seven {
};
};
view other {
match-clients { geoip db country country O1; };
zone "example" {
type master;
file "exampleother.db";
};
};
view none {
match-clients { any; };
zone "example" {

View File

@@ -24,10 +24,11 @@ options {
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on { 127.0.0.1; 10.53.0.2; };
listen-on-v6 { none; };
recursion no;
geoip-directory "../data";
geoip-use-ecs no;
};
key rndc_key {
@@ -107,6 +108,6 @@ view none {
match-clients { any; };
zone "example" {
type master;
file "example.db.in";
file "examplebogus.db";
};
};

View File

@@ -21,7 +21,7 @@ $SHELL clean.sh
cp ns2/named1.conf ns2/named.conf
for i in 1 2 3 4 5 6 7; do
for i in 1 2 3 4 5 6 7 other bogus; do
cp ns2/example.db.in ns2/example${i}.db
echo "@ IN TXT \"$i\"" >> ns2/example$i.db
done

View File

@@ -38,6 +38,30 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP country database by code (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/0" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking response scope using client subnet ($n)"
ret=0
$DIG +tcp -p5300 @10.53.0.2 txt example -b 127.0.0.1 +subnet="10.53.0.1/32" > dig.out.ns2.test$n.1 || ret=1
grep 'CLIENT-SUBNET.*10.53.0.1/32/32' dig.out.ns2.test$n.1 > /dev/null || ret=1
$DIG +tcp -p5300 @10.53.0.2 txt example -b 127.0.0.1 +subnet="192.0.2.64/32" > dig.out.ns2.test$n.2 || ret=1
grep 'CLIENT-SUBNET.*192.0.2.64/32/24' dig.out.ns2.test$n.2 > /dev/null || ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named2.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -115,6 +139,21 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP region database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named6.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -134,6 +173,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP city database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named7.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -153,6 +206,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP isp database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named8.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -172,6 +239,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP org database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named9.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -191,6 +272,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP asnum database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named10.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -210,6 +305,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP domain database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named11.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -248,6 +357,20 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking GeoIP netspeed database (using client subnet) ($n)"
ret=0
lret=0
for i in 1 2 3 4; do
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named13.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
@@ -280,5 +403,29 @@ done
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:reloading server"
cp -f ns2/named14.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 2>&1 | sed 's/^/I:ns2 /'
sleep 3
n=`expr $n + 1`
echo "I:checking geoip-use-ecs ($n)"
ret=0
lret=0
for i in 1 2 3 4 5 6 7; do
$DIG $DIGOPTS txt example -b 10.53.0.$i > dig.out.ns2.test$n.$i || lret=1
j=`cat dig.out.ns2.test$n.$i | tr -d '"'`
[ "$i" = "$j" ] || lret=1
[ $lret -eq 1 ] && break
$DIG $DIGOPTS txt example -b 127.0.0.1 +subnet="10.53.0.$i/32" > dig.out.ns2.test$n.ecs.$i || lret=1
j=`cat dig.out.ns2.test$n.ecs.$i | tr -d '"'`
[ "$j" = "bogus" ] || lret=1
[ $lret -eq 1 ] && break
done
[ $lret -eq 1 ] && ret=1
[ $ret -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status

View File

@@ -3444,66 +3444,6 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
</tbody>
</tgroup>
</informaltable>
<para>
When <acronym>BIND</acronym> 9 is built with GeoIP support,
ACLs can also be used for geographic access restrictions.
This is done by specifying an ACL element of the form:
<command>geoip <optional>db <replaceable>database</replaceable></optional> <replaceable>field</replaceable> <replaceable>value</replaceable></command>
</para>
<para>
The <replaceable>field</replaceable> indicates which field
to search for a match. Available fields are "country",
"region", "city", "continent", "postal" (postal code),
"metro" (metro code), "area" (area code), "tz" (timezone),
"isp", "org", "asnum", "domain" and "netspeed".
</para>
<para>
<replaceable>value</replaceable> is the value to search
for within the database. A string may be quoted if it
contains spaces or other special characters. If this is
an "asnum" search, then the leading "ASNNNN" string can be
used, otherwise the full description must be used (e.g.
"ASNNNN Example Company Name"). If this is a "country"
search and the string is two characters long, then it must
be a standard ISO-3166-1 two-letter country code, and if it
is three characters long then it must be an ISO-3166-1
three-letter country code; otherwise it is the full name
of the country. Similarly, if this is a "region" search
and the string is two characters long, then it must be a
standard two-letter state or province abbreviation;
otherwise it is the full name of the state or province.
</para>
<para>
The <replaceable>database</replaceable> field indicates which
GeoIP database to search for a match. In most cases this is
unnecessary, because most search fields can only be found in
a single database. However, searches for country can be
answered from the "city", "region", or "country" databases,
and searches for region (i.e., state or province) can be
answered from the "city" or "region" databases. For these
search types, specifying a <replaceable>database</replaceable>
will force the query to be answered from that database and no
other. If <replaceable>database</replaceable> is not
specified, then these queries will be answered from the "city",
database if it is installed, or the "region" database if it is
installed, or the "country" database, in that order.
</para>
<para>
Some example GeoIP ACLs:
</para>
<programlisting>geoip country US;
geoip country JAP;
geoip db country country Canada;
geoip db region region WA;
geoip city "San Francisco";
geoip region Oklahoma;
geoip postal 95062;
geoip tz "America/Los_Angeles";
geoip org "Internet Systems Consortium";
</programlisting>
</sect2>
<sect2>
<title><command>controls</command> Statement Grammar</title>
@@ -4858,6 +4798,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> allow-update { <replaceable>address_match_list</replaceable> }; </optional>
<optional> allow-update-forwarding { <replaceable>address_match_list</replaceable> }; </optional>
<optional> automatic-interface-scan { <replaceable>yes_or_no</replaceable> }; </optional>
<optional> geoip-use-ecs <replaceable>yes_or_no</replaceable>;</optional>
<optional> update-check-ksk <replaceable>yes_or_no</replaceable>; </optional>
<optional> dnssec-update-mode ( <replaceable>maintain</replaceable> | <replaceable>no-resign</replaceable> ); </optional>
<optional> dnssec-dnskey-kskonly <replaceable>yes_or_no</replaceable>; </optional>
@@ -6240,6 +6181,20 @@ options {
</listitem>
</varlistentry>
<varlistentry>
<term><command>geoip-use-ecs</command></term>
<listitem>
<para>
When BIND is compiled with GeoIP support and configured
with "geoip" ACL elements, this option indicates whether
the EDNS Client Subnet option, if present in a request,
should be used for matching against the GeoIP database.
The default is
<command>geoip-use-ecs</command> <userinput>yes</userinput>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>has-old-clients</command></term>
<listitem>
@@ -16188,11 +16143,11 @@ HOST-127.EXAMPLE. MX 0 .
<title>Access Control Lists</title>
<para>
Access Control Lists (ACLs) are address match lists that
you can set up and nickname for future use in <command>allow-notify</command>,
<command>allow-query</command>, <command>allow-query-on</command>,
<command>allow-recursion</command>, <command>allow-recursion-on</command>,
you can set up and nickname for future use in
<command>allow-notify</command>, <command>allow-query</command>,
<command>allow-query-on</command>, <command>allow-recursion</command>,
<command>blackhole</command>, <command>allow-transfer</command>,
etc.
<command>match-clients</command>, etc.
</para>
<para>
Using ACLs allows you to have finer control over who can access
@@ -16202,11 +16157,19 @@ HOST-127.EXAMPLE. MX 0 .
<para>
It is a <emphasis>good idea</emphasis> to use ACLs, and to
control access to your server. Limiting access to your server by
outside parties can help prevent spoofing and denial of service (DoS) attacks against
your server.
outside parties can help prevent spoofing and denial of service
(DoS) attacks against your server.
</para>
<para>
Here is an example of how to properly apply ACLs:
ACLs match clients on the basis of up to three characteristics:
1) The client's IP address; 2) the TSIG or SIG(0) key that was
used to sign the request, if any; and 3) an address prefix
encoded in an EDNS Client Subnet option, if any.
</para>
<para>
ACLs
<para>
Here is an example of ACLs based on client addresses:
</para>
<programlisting>
@@ -16239,10 +16202,137 @@ zone "example.com" {
</programlisting>
<para>
This allows recursive queries of the server from the outside
unless recursion has been previously disabled.
This allows authoritative queries for "example.com" from any
address, but recursive queries only from the networks specified
in "our-nets", and no queries at all from the networks
specified in "bogusnets".
</para>
<para>
In addition to network addresses and prefixes, which are
matched against the source address of the DNS request, ACLs
may include <option>key</option> elements, which specify the
name of a TSIG or SIG(0) key, or <option>ecs</option>
elements, which specify a network prefix but are only matched
if that prefix matches an EDNS client subnet option included
in the request.
</para>
<para>
The EDNS Client Subnet (ECS) option is used by a recursive
resolver to inform an authoritative name server of the network
address block from which the original query was received, enabling
authoritative servers to give different answers to the same
resolver for different resolver clients. An ACL containing
an element of the form
<command>ecs <replaceable>prefix</replaceable></command>
will match if a request arrives in containing an ECS option
encoding an address within that prefix. If the request has no
ECS option, then "ecs" elements are simply ignored. Addresses
in ACLs that are not prefixed with "ecs" are matched only
against the source address.
</para>
<para>
When <acronym>BIND</acronym> 9 is built with GeoIP support,
ACLs can also be used for geographic access restrictions.
This is done by specifying an ACL element of the form:
<command>geoip <optional>db <replaceable>database</replaceable></optional> <replaceable>field</replaceable> <replaceable>value</replaceable></command>
</para>
<para>
The <replaceable>field</replaceable> indicates which field
to search for a match. Available fields are "country",
"region", "city", "continent", "postal" (postal code),
"metro" (metro code), "area" (area code), "tz" (timezone),
"isp", "org", "asnum", "domain" and "netspeed".
</para>
<para>
<replaceable>value</replaceable> is the value to search
for within the database. A string may be quoted if it
contains spaces or other special characters. If this is
an "asnum" search, then the leading "ASNNNN" string can be
used, otherwise the full description must be used (e.g.
"ASNNNN Example Company Name"). If this is a "country"
search and the string is two characters long, then it must
be a standard ISO-3166-1 two-letter country code, and if it
is three characters long then it must be an ISO-3166-1
three-letter country code; otherwise it is the full name
of the country. Similarly, if this is a "region" search
and the string is two characters long, then it must be a
standard two-letter state or province abbreviation;
otherwise it is the full name of the state or province.
</para>
<para>
The <replaceable>database</replaceable> field indicates which
GeoIP database to search for a match. In most cases this is
unnecessary, because most search fields can only be found in
a single database. However, searches for country can be
answered from the "city", "region", or "country" databases,
and searches for region (i.e., state or province) can be
answered from the "city" or "region" databases. For these
search types, specifying a <replaceable>database</replaceable>
will force the query to be answered from that database and no
other. If <replaceable>database</replaceable> is not
specified, then these queries will be answered from the "city",
database if it is installed, or the "region" database if it is
installed, or the "country" database, in that order.
</para>
<para>
By default, if a DNS query includes an EDNS Client Subnet (ECS)
option which encodes a non-zero address prefix, then GeoIP ACLs
will be matched against that address prefix. Otherwise, they
are matched against the source address of the query. To
prevent GeoIP ACLs from matching against ECS options, set
the <command>geoip-use-ecs</option> to <literal>no</literal>.
</para>
<para>
Some example GeoIP ACLs:
</para>
<programlisting>geoip country US;
geoip country JAP;
geoip db country country Canada;
geoip db region region WA;
geoip city "San Francisco";
geoip region Oklahoma;
geoip postal 95062;
geoip tz "America/Los_Angeles";
geoip org "Internet Systems Consortium";
</programlisting>
<para>
ACLs use a "first-match" logic rather than "best-match":
if an address prefix matches an ACL element, then that ACL
is considered to have matched even if a later element would
have matched more specifically. For example, the ACL
<command> { 10/8; !10.0.0.1; }</command> would actually
match a query from 10.0.0.1, because the first element
indicated that the query should be accepted, and the second
element is ignored.
</para>
<para>
When using "nested" ACLs (that is, ACLs included or referenced
within other ACLs), a negative match of a nested ACL will
the containing ACL to continue looking for matches. This
enables complex ACLs to be constructed, in which multiple
client characteristics can be checked at the same time. For
example, to construct an ACL which allows queries only when
it originates from a particular network <emphasis>and</emphasis>
only when it is signed with a particular key, use:
</para>
<programlisting>
allow-query { !{ !10/8; any; }; key example; };
</programlisting>
<para>
Within the nested ACL, any address that is
<emphasis>not</emphasis> in the 10/8 network prefix will
be rejected, and this will terminate processing of the
ACL. Any address that <emphasis>is</emphasis> in the 10/8
network prefix will be accepted, but this causes a negative
match of the nested ACL, so the containing ACL continues
processing. The query will then be accepted if it is signed
by the key "example", and rejected otherwise. The ACL, then,
will only matches when <emphasis>both</emphasis> conditions
are true.
</para>
</sect1>
<sect1>
<title><command>Chroot</command> and <command>Setuid</command></title>
<para>

View File

@@ -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

View File

@@ -72,6 +72,7 @@ typedef struct geoip_state {
unsigned int family;
isc_uint32_t ipnum;
geoipv6_t ipnum6;
isc_uint8_t scope;
GeoIPRecord *record;
GeoIPRegion *region;
const char *text;
@@ -157,7 +158,7 @@ clean_state(geoip_state_t *state) {
static isc_result_t
set_state(unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6,
dns_geoip_subtype_t subtype, GeoIPRecord *record,
isc_uint8_t scope, dns_geoip_subtype_t subtype, GeoIPRecord *record,
GeoIPRegion *region, char *name, const char *text, int id)
{
isc_result_t result;
@@ -198,6 +199,7 @@ set_state(unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6,
state->family = family;
state->subtype = subtype;
state->scope = scope;
state->record = record;
state->region = region;
state->name = name;
@@ -232,10 +234,12 @@ get_state(void) {
static const char *
country_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
unsigned int family,
isc_uint32_t ipnum, const geoipv6_t *ipnum6)
isc_uint32_t ipnum, const geoipv6_t *ipnum6,
isc_uint8_t *scope)
{
geoip_state_t *prev_state = NULL;
const char *text = NULL;
GeoIPLookup gl;
REQUIRE(db != NULL);
@@ -253,42 +257,55 @@ country_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
((prev_state->family == AF_INET && prev_state->ipnum == ipnum) ||
(prev_state->family == AF_INET6 && ipnum6 != NULL &&
memcmp(prev_state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
{
text = prev_state->text;
if (scope != NULL)
*scope = prev_state->scope;
}
if (text == NULL) {
switch (subtype) {
case dns_geoip_country_code:
if (family == AF_INET)
text = GeoIP_country_code_by_ipnum(db, ipnum);
text = GeoIP_country_code_by_ipnum_gl(db,
ipnum, &gl);
#ifdef HAVE_GEOIP_V6
else
text = GeoIP_country_code_by_ipnum_v6(db,
*ipnum6);
text = GeoIP_country_code_by_ipnum_v6_gl(db,
*ipnum6, &gl);
#endif
break;
case dns_geoip_country_code3:
if (family == AF_INET)
text = GeoIP_country_code3_by_ipnum(db, ipnum);
text = GeoIP_country_code3_by_ipnum_gl(db,
ipnum, &gl);
#ifdef HAVE_GEOIP_V6
else
text = GeoIP_country_code3_by_ipnum_v6(db,
*ipnum6);
text = GeoIP_country_code3_by_ipnum_v6_gl(db,
*ipnum6, &gl);
#endif
break;
case dns_geoip_country_name:
if (family == AF_INET)
text = GeoIP_country_name_by_ipnum(db, ipnum);
text = GeoIP_country_name_by_ipnum_gl(db,
ipnum, &gl);
#ifdef HAVE_GEOIP_V6
else
text = GeoIP_country_name_by_ipnum_v6(db,
*ipnum6);
text = GeoIP_country_name_by_ipnum_v6_gl(db,
*ipnum6, &gl);
#endif
break;
default:
INSIST(0);
}
set_state(family, ipnum, ipnum6, subtype,
if (text == NULL)
return (NULL);
if (scope != NULL)
*scope = gl.netmask;
set_state(family, ipnum, ipnum6, gl.netmask, subtype,
NULL, NULL, NULL, text, 0);
}
@@ -377,7 +394,9 @@ is_city(dns_geoip_subtype_t subtype) {
*/
static GeoIPRecord *
city_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6)
unsigned int family, isc_uint32_t ipnum,
const geoipv6_t *ipnum6,
isc_uint8_t *scope)
{
GeoIPRecord *record = NULL;
geoip_state_t *prev_state = NULL;
@@ -397,7 +416,11 @@ city_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
((prev_state->family == AF_INET && prev_state->ipnum == ipnum) ||
(prev_state->family == AF_INET6 &&
memcmp(prev_state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
{
record = prev_state->record;
if (scope != NULL)
*scope = record->netmask;
}
if (record == NULL) {
if (family == AF_INET)
@@ -409,15 +432,17 @@ city_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
if (record == NULL)
return (NULL);
set_state(family, ipnum, ipnum6, subtype,
if (scope != NULL)
*scope = record->netmask;
set_state(family, ipnum, ipnum6, record->netmask, subtype,
record, NULL, NULL, NULL, 0);
}
return (record);
}
static char *
region_string(GeoIPRegion *region, dns_geoip_subtype_t subtype, int *maxlen) {
static char * region_string(GeoIPRegion *region, dns_geoip_subtype_t subtype, int *maxlen) {
const char *s;
char *deconst;
@@ -459,9 +484,12 @@ is_region(dns_geoip_subtype_t subtype) {
* outside the Region database.
*/
static GeoIPRegion *
region_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
region_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
isc_uint32_t ipnum, isc_uint8_t *scope)
{
GeoIPRegion *region = NULL;
geoip_state_t *prev_state = NULL;
GeoIPLookup gl;
REQUIRE(db != NULL);
@@ -469,14 +497,21 @@ region_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
if (prev_state != NULL && prev_state->ipnum == ipnum &&
is_region(prev_state->subtype))
{
region = prev_state->region;
if (scope != NULL)
*scope = prev_state->scope;
}
if (region == NULL) {
region = GeoIP_region_by_ipnum(db, ipnum);
region = GeoIP_region_by_ipnum_gl(db, ipnum, &gl);
if (region == NULL)
return (NULL);
set_state(AF_INET, ipnum, NULL,
if (scope != NULL)
*scope = gl.netmask;
set_state(AF_INET, ipnum, NULL, gl.netmask,
subtype, NULL, region, NULL, NULL, 0);
}
@@ -489,9 +524,12 @@ region_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
* or was for a search of a different subtype.
*/
static char *
name_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
name_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
isc_uint32_t ipnum, isc_uint8_t *scope)
{
char *name = NULL;
geoip_state_t *prev_state = NULL;
GeoIPLookup gl;
REQUIRE(db != NULL);
@@ -499,14 +537,21 @@ name_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
if (prev_state != NULL && prev_state->ipnum == ipnum &&
prev_state->subtype == subtype)
{
name = prev_state->name;
if (scope != NULL)
*scope = prev_state->scope;
}
if (name == NULL) {
name = GeoIP_name_by_ipnum(db, ipnum);
name = GeoIP_name_by_ipnum_gl(db, ipnum, &gl);
if (name == NULL)
return (NULL);
set_state(AF_INET, ipnum, NULL,
if (scope != NULL)
*scope = gl.netmask;
set_state(AF_INET, ipnum, NULL, gl.netmask,
subtype, NULL, NULL, name, NULL, 0);
}
@@ -519,9 +564,12 @@ name_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
* different subtype.
*/
static int
netspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
netspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
isc_uint32_t ipnum, isc_uint8_t *scope)
{
geoip_state_t *prev_state = NULL;
isc_boolean_t found = ISC_FALSE;
GeoIPLookup gl;
int id = -1;
REQUIRE(db != NULL);
@@ -531,12 +579,20 @@ netspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
if (prev_state != NULL && prev_state->ipnum == ipnum &&
prev_state->subtype == subtype) {
id = prev_state->id;
if (scope != NULL)
*scope = prev_state->scope;
found = ISC_TRUE;
}
if (!found) {
id = GeoIP_id_by_ipnum(db, ipnum);
set_state(AF_INET, ipnum, NULL,
id = GeoIP_id_by_ipnum_gl(db, ipnum, &gl);
if (id == 0)
return (0);
if (scope != NULL)
*scope = gl.netmask;
set_state(AF_INET, ipnum, NULL, gl.netmask,
subtype, NULL, NULL, NULL, NULL, id);
}
@@ -599,7 +655,7 @@ fix_subtype(const isc_netaddr_t *reqaddr, const dns_geoip_databases_t *geoip,
#endif /* HAVE_GEOIP */
isc_boolean_t
dns_geoip_match(const isc_netaddr_t *reqaddr,
dns_geoip_match(const isc_netaddr_t *reqaddr, isc_uint8_t *scope,
const dns_geoip_databases_t *geoip,
const dns_geoip_elem_t *elt)
{
@@ -662,7 +718,7 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
INSIST(elt->as_string != NULL);
cs = country_lookup(db, subtype, family, ipnum, ipnum6);
cs = country_lookup(db, subtype, family, ipnum, ipnum6, scope);
if (cs != NULL && strncasecmp(elt->as_string, cs, maxlen) == 0)
return (ISC_TRUE);
break;
@@ -682,7 +738,8 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (db == NULL)
return (ISC_FALSE);
record = city_lookup(db, subtype, family, ipnum, ipnum6);
record = city_lookup(db, subtype, family,
ipnum, ipnum6, scope);
if (record == NULL)
break;
@@ -697,7 +754,8 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (db == NULL)
return (ISC_FALSE);
record = city_lookup(db, subtype, family, ipnum, ipnum6);
record = city_lookup(db, subtype, family,
ipnum, ipnum6, scope);
if (record == NULL)
break;
@@ -710,7 +768,8 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (db == NULL)
return (ISC_FALSE);
record = city_lookup(db, subtype, family, ipnum, ipnum6);
record = city_lookup(db, subtype, family,
ipnum, ipnum6, scope);
if (record == NULL)
break;
@@ -731,7 +790,7 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (family == AF_INET6)
return (ISC_FALSE);
region = region_lookup(geoip->region, subtype, ipnum);
region = region_lookup(geoip->region, subtype, ipnum, scope);
if (region == NULL)
break;
@@ -765,7 +824,7 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (family == AF_INET6)
return (ISC_FALSE);
s = name_lookup(db, subtype, ipnum);
s = name_lookup(db, subtype, ipnum, scope);
if (s != NULL) {
size_t l;
if (strcasecmp(elt->as_string, s) == 0)
@@ -790,7 +849,7 @@ dns_geoip_match(const isc_netaddr_t *reqaddr,
if (family == AF_INET6)
return (ISC_FALSE);
id = netspeed_lookup(geoip->netspeed, subtype, ipnum);
id = netspeed_lookup(geoip->netspeed, subtype, ipnum, scope);
if (id == elt->as_int)
return (ISC_TRUE);
break;

View File

@@ -103,6 +103,7 @@ struct dns_aclenv {
isc_boolean_t match_mapped;
#ifdef HAVE_GEOIP
dns_geoip_databases_t *geoip;
isc_boolean_t geoip_use_ecs;
#endif
};
@@ -212,12 +213,28 @@ dns_acl_match(const isc_netaddr_t *reqaddr,
const dns_aclenv_t *env,
int *match,
const dns_aclelement_t **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);
/*%<
* General, low-level ACL matching. This is expected to
* be useful even for weird stuff like the topology and sortlist statements.
*
* Match the address 'reqaddr', and optionally the key name 'reqsigner',
* against 'acl'. 'reqsigner' may be NULL.
* and optionally the client prefix 'ecs' of length 'ecslen'
* (reported via EDNS client subnet option) against 'acl'.
*
* 'reqsigner' and 'ecs' may be NULL. If an ACL matches against 'ecs'
* and 'ecslen', then 'scope' will be set to indicate the netmask that
* matched.
*
* If there is a match, '*match' will be set to an integer whose absolute
* value corresponds to the order in which the matching value was inserted
@@ -244,6 +261,16 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
const dns_aclelement_t *e,
const dns_aclenv_t *env,
const dns_aclelement_t **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);
/*%<
* Like dns_acl_match, but matches against the single ACL element 'e'
* rather than a complete ACL, and returns ISC_TRUE iff it matched.

View File

@@ -108,7 +108,7 @@ typedef struct dns_geoip_databases {
ISC_LANG_BEGINDECLS
isc_boolean_t
dns_geoip_match(const isc_netaddr_t *reqaddr,
dns_geoip_match(const isc_netaddr_t *reqaddr, isc_uint8_t *scope,
const dns_geoip_databases_t *geoip,
const dns_geoip_elem_t *elt);

View File

@@ -51,6 +51,10 @@ dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target);
isc_result_t
dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
isc_uint16_t bitlen, isc_boolean_t pos);
isc_result_t
dns_iptable_addprefix2(dns_iptable_t *tab, isc_netaddr_t *addr,
isc_uint16_t bitlen, isc_boolean_t pos,
isc_boolean_t is_ecs);
/*
* Add an IP prefix to an existing IP table
*/

View File

@@ -112,7 +112,7 @@
#define DNS_OPT_SIT 65001 /*%< SIT opt code */
/*%< The number of EDNS options we know about. */
#define DNS_EDNSOPTIONS 4
#define DNS_EDNSOPTIONS 5
#define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD|DNS_MESSAGEFLAG_CD)
#define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO)

View File

@@ -14,8 +14,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: iptable.c,v 1.15 2009/02/18 23:47:48 tbox Exp $ */
#include <config.h>
#include <isc/mem.h>
@@ -63,16 +61,24 @@ isc_boolean_t dns_iptable_pos = ISC_TRUE;
isc_result_t
dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
isc_uint16_t bitlen, isc_boolean_t pos)
{
return(dns_iptable_addprefix2(tab, addr, bitlen, pos, ISC_FALSE));
}
isc_result_t
dns_iptable_addprefix2(dns_iptable_t *tab, isc_netaddr_t *addr,
isc_uint16_t bitlen, isc_boolean_t pos,
isc_boolean_t is_ecs)
{
isc_result_t result;
isc_prefix_t pfx;
isc_radix_node_t *node = NULL;
int family;
int i;
INSIST(DNS_IPTABLE_VALID(tab));
INSIST(tab->radix);
NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
NETADDR_TO_PREFIX_T(addr, pfx, bitlen, is_ecs);
result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
if (result != ISC_R_SUCCESS) {
@@ -81,28 +87,20 @@ dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
}
/* If a node already contains data, don't overwrite it */
family = pfx.family;
if (family == AF_UNSPEC) {
if (pfx.family == AF_UNSPEC) {
/* "any" or "none" */
INSIST(pfx.bitlen == 0);
if (pos) {
if (node->data[0] == NULL)
node->data[0] = &dns_iptable_pos;
if (node->data[1] == NULL)
node->data[1] = &dns_iptable_pos;
} else {
if (node->data[0] == NULL)
node->data[0] = &dns_iptable_neg;
if (node->data[1] == NULL)
node->data[1] = &dns_iptable_neg;
for (i = 0; i < 4; i++) {
if (node->data[i] == NULL)
node->data[i] = pos ? &dns_iptable_pos
: &dns_iptable_neg;
}
} else {
/* any other prefix */
if (node->data[ISC_IS6(family)] == NULL) {
if (pos)
node->data[ISC_IS6(family)] = &dns_iptable_pos;
else
node->data[ISC_IS6(family)] = &dns_iptable_neg;
int offset = ISC_RADIX_OFF(&pfx);
if (node->data[offset] == NULL) {
node->data[offset] = pos ? &dns_iptable_pos
: &dns_iptable_neg;
}
}
@@ -118,7 +116,7 @@ dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos)
{
isc_result_t result;
isc_radix_node_t *node, *new_node;
int max_node = 0;
int i, max_node = 0;
RADIX_WALK (source->radix->head, node) {
new_node = NULL;
@@ -135,20 +133,15 @@ dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos)
* could be a security risk. To prevent this, we
* just leave the negative nodes negative.
*/
if (!pos) {
if (node->data[0] &&
*(isc_boolean_t *) node->data[0] == ISC_TRUE)
new_node->data[0] = &dns_iptable_neg;
if (node->data[1] &&
*(isc_boolean_t *) node->data[1] == ISC_TRUE)
new_node->data[1] = &dns_iptable_neg;
for (i = 0; i < 4; i++) {
if (!pos) {
if (node->data[i] &&
*(isc_boolean_t *) node->data[i])
new_node->data[i] = &dns_iptable_neg;
}
if (node->node_num[i] > max_node)
max_node = node->node_num[i];
}
if (node->node_num[0] > max_node)
max_node = node->node_num[0];
if (node->node_num[1] > max_node)
max_node = node->node_num[1];
} RADIX_WALK_END;
tab->radix->num_added_node += max_node;

View File

@@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id$ */
/*! \file */
/***

View File

@@ -136,8 +136,8 @@ load_geoip(const char *dir) {
}
static isc_boolean_t
do_lookup_string(const char *addr, dns_geoip_subtype_t subtype,
const char *string)
do_lookup_string(const char *addr, isc_uint8_t *scope,
dns_geoip_subtype_t subtype, const char *string)
{
dns_geoip_elem_t elt;
struct in_addr in4;
@@ -149,12 +149,12 @@ do_lookup_string(const char *addr, dns_geoip_subtype_t subtype,
elt.subtype = subtype;
strcpy(elt.as_string, string);
return (dns_geoip_match(&na, &geoip, &elt));
return (dns_geoip_match(&na, scope, &geoip, &elt));
}
static isc_boolean_t
do_lookup_string_v6(const char *addr, dns_geoip_subtype_t subtype,
const char *string)
do_lookup_string_v6(const char *addr, isc_uint8_t *scope,
dns_geoip_subtype_t subtype, const char *string)
{
dns_geoip_elem_t elt;
struct in6_addr in6;
@@ -166,11 +166,13 @@ do_lookup_string_v6(const char *addr, dns_geoip_subtype_t subtype,
elt.subtype = subtype;
strcpy(elt.as_string, string);
return (dns_geoip_match(&na, &geoip, &elt));
return (dns_geoip_match(&na, scope, &geoip, &elt));
}
static isc_boolean_t
do_lookup_int(const char *addr, dns_geoip_subtype_t subtype, int id) {
do_lookup_int(const char *addr, isc_uint8_t *scope,
dns_geoip_subtype_t subtype, int id)
{
dns_geoip_elem_t elt;
struct in_addr in4;
isc_netaddr_t na;
@@ -181,7 +183,7 @@ do_lookup_int(const char *addr, dns_geoip_subtype_t subtype, int id) {
elt.subtype = subtype;
elt.as_int = id;
return (dns_geoip_match(&na, &geoip, &elt));
return (dns_geoip_match(&na, scope, &geoip, &elt));
}
/*
@@ -196,6 +198,7 @@ ATF_TC_HEAD(country, tc) {
ATF_TC_BODY(country, tc) {
isc_result_t result;
isc_boolean_t match;
isc_uint8_t scope;
UNUSED(tc);
@@ -210,16 +213,30 @@ ATF_TC_BODY(country, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.1", dns_geoip_country_code, "AU");
match = do_lookup_string("10.53.0.1", &scope,
dns_geoip_country_code, "AU");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 32);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", &scope,
dns_geoip_country_code3, "AUS");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 32);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", &scope,
dns_geoip_country_name, "Australia");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 32);
match = do_lookup_string("192.0.2.128", &scope,
dns_geoip_country_code, "O1");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 24);
match = do_lookup_string("192.0.2.128", &scope,
dns_geoip_country_name, "Other");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 24);
dns_test_end();
}
@@ -232,6 +249,7 @@ ATF_TC_HEAD(country_v6, tc) {
ATF_TC_BODY(country_v6, tc) {
isc_result_t result;
isc_boolean_t match;
isc_uint8_t scope;
UNUSED(tc);
@@ -246,17 +264,20 @@ ATF_TC_BODY(country_v6, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
dns_geoip_country_code, "AU");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 128);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
dns_geoip_country_code3, "AUS");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 128);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
dns_geoip_country_name, "Australia");
ATF_CHECK(match);
ATF_CHECK_EQ(scope, 128);
dns_test_end();
}
@@ -283,42 +304,42 @@ ATF_TC_BODY(city, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_continentcode, "NA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_countrycode, "US");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_countrycode3, "USA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_countryname, "United States");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_region, "CA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_regionname, "California");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_name, "Redwood City");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_city_postalcode, "94063");
ATF_CHECK(match);
match = do_lookup_int("10.53.0.1", dns_geoip_city_areacode, 650);
match = do_lookup_int("10.53.0.1", NULL, dns_geoip_city_areacode, 650);
ATF_CHECK(match);
match = do_lookup_int("10.53.0.1", dns_geoip_city_metrocode, 807);
match = do_lookup_int("10.53.0.1", NULL, dns_geoip_city_metrocode, 807);
ATF_CHECK(match);
dns_test_end();
@@ -346,36 +367,36 @@ ATF_TC_BODY(city_v6, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_continentcode, "NA");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_countrycode, "US");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_countrycode3, "USA");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_countryname,
"United States");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_region, "CA");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_regionname, "California");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_name, "Redwood City");
ATF_CHECK(match);
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
dns_geoip_city_postalcode, "94063");
ATF_CHECK(match);
@@ -405,15 +426,15 @@ ATF_TC_BODY(region, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_region_code, "CA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_region_name, "California");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.1",
match = do_lookup_string("10.53.0.1", NULL,
dns_geoip_region_countrycode, "US");
ATF_CHECK(match);
@@ -447,30 +468,30 @@ ATF_TC_BODY(best, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode, "US");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode3, "USA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countryname, "United States");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_regionname, "Virginia");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_region, "VA");
ATF_CHECK(match);
GeoIP_delete(geoip.city_v4);
geoip.city_v4 = NULL;
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode, "AU");
ATF_CHECK(match);
@@ -478,26 +499,26 @@ ATF_TC_BODY(best, tc) {
* Note, region doesn't support code3 or countryname, so
* the next two would be answered from the country database instead
*/
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode3, "CAN");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countryname, "Canada");
ATF_CHECK(match);
GeoIP_delete(geoip.region);
geoip.region = NULL;
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode, "CA");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countrycode3, "CAN");
ATF_CHECK(match);
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_countryname, "Canada");
ATF_CHECK(match);
@@ -528,7 +549,7 @@ ATF_TC_BODY(asnum, tc) {
}
match = do_lookup_string("10.53.0.3", dns_geoip_as_asnum,
match = do_lookup_string("10.53.0.3", NULL, dns_geoip_as_asnum,
"AS100003 Three Network Labs");
ATF_CHECK(match);
@@ -557,7 +578,7 @@ ATF_TC_BODY(isp, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.1", dns_geoip_isp_name,
match = do_lookup_string("10.53.0.1", NULL, dns_geoip_isp_name,
"One Systems, Inc.");
ATF_CHECK(match);
@@ -586,7 +607,7 @@ ATF_TC_BODY(org, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.2", dns_geoip_org_name,
match = do_lookup_string("10.53.0.2", NULL, dns_geoip_org_name,
"Two Technology Ltd.");
ATF_CHECK(match);
@@ -615,7 +636,7 @@ ATF_TC_BODY(domain, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_string("10.53.0.4",
match = do_lookup_string("10.53.0.4", NULL,
dns_geoip_domain_name, "four.com");
ATF_CHECK(match);
@@ -644,16 +665,16 @@ ATF_TC_BODY(netspeed, tc) {
atf_tc_skip("Database not available");
}
match = do_lookup_int("10.53.0.1", dns_geoip_netspeed_id, 0);
match = do_lookup_int("10.53.0.1", NULL, dns_geoip_netspeed_id, 0);
ATF_CHECK(match);
match = do_lookup_int("10.53.0.2", dns_geoip_netspeed_id, 1);
match = do_lookup_int("10.53.0.2", NULL, dns_geoip_netspeed_id, 1);
ATF_CHECK(match);
match = do_lookup_int("10.53.0.3", dns_geoip_netspeed_id, 2);
match = do_lookup_int("10.53.0.3", NULL, dns_geoip_netspeed_id, 2);
ATF_CHECK(match);
match = do_lookup_int("10.53.0.4", dns_geoip_netspeed_id, 3);
match = do_lookup_int("10.53.0.4", NULL, dns_geoip_netspeed_id, 3);
ATF_CHECK(match);
dns_test_end();

View File

@@ -25,9 +25,11 @@ dns_acl_isany
dns_acl_isinsecure
dns_acl_isnone
dns_acl_match
dns_acl_match2
dns_acl_merge
dns_acl_none
dns_aclelement_match
dns_aclelement_match2
dns_aclenv_copy
dns_aclenv_destroy
dns_aclenv_init

View File

@@ -14,8 +14,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: radix.h,v 1.13 2008/12/01 23:47:45 tbox Exp $ */
/*
* This source was adapted from MRT's RCS Ids:
* Id: radix.h,v 1.6 1999/08/03 03:32:53 masaki Exp
@@ -34,7 +32,7 @@
#ifndef _RADIX_H
#define _RADIX_H
#define NETADDR_TO_PREFIX_T(na,pt,bits) \
#define NETADDR_TO_PREFIX_T(na,pt,bits,isecs) \
do { \
memset(&(pt), 0, sizeof(pt)); \
if((na) != NULL) { \
@@ -50,6 +48,7 @@
(pt).family = AF_UNSPEC; \
(pt).bitlen = 0; \
} \
(pt).ecs = isecs; \
isc_refcount_init(&(pt).refcount, 0); \
} while(0)
@@ -57,6 +56,7 @@ typedef struct isc_prefix {
isc_mem_t *mctx;
unsigned int family; /* AF_INET | AF_INET6, or AF_UNSPEC for "any" */
unsigned int bitlen; /* 0 for "any" */
isc_boolean_t ecs; /* ISC_TRUE for an EDNS client subnet address */
isc_refcount_t refcount;
union {
struct in_addr sin;
@@ -81,23 +81,32 @@ typedef void (*isc_radix_processfunc_t)(isc_prefix_t *, void **);
* return the one that was added first.
*
* An IPv4 prefix and an IPv6 prefix may share a radix tree node if they
* have the same length and bit pattern (e.g., 127/8 and 7f::/8). To
* disambiguate between them, node_num and data are two-element arrays;
* node_num[0] and data[0] are used for IPv4 addresses, node_num[1]
* and data[1] for IPv6 addresses. The only exception is a prefix of
* 0/0 (aka "any" or "none"), which is always stored as IPv4 but matches
* IPv6 addresses too.
* have the same length and bit pattern (e.g., 127/8 and 7f::/8). Also,
* a node that matches a client address may also match an EDNS client
* subnet address. To disambiguate between these, node_num and data
* are four-element arrays;
*
* - node_num[0] and data[0] are used for IPv4 client addresses
* - node_num[1] and data[1] for IPv4 client subnet addresses
* - node_num[2] and data[2] are used for IPv6 client addresses
* - node_num[3] and data[3] for IPv6 client subnet addresses
*
* A prefix of 0/0 (aka "any" or "none"), is always stored as IPv4,
* but matches IPv6 addresses too, as well as all client subnet
* addresses.
*/
#define ISC_IS6(family) ((family) == AF_INET6 ? 1 : 0)
#define ISC_RADIX_OFF(p) \
((((p)->family == AF_INET6) ? 1 : 0) + ((p)->ecs ? 2 : 0))
typedef struct isc_radix_node {
isc_mem_t *mctx;
isc_uint32_t bit; /* bit length of the prefix */
isc_prefix_t *prefix; /* who we are in radix tree */
struct isc_radix_node *l, *r; /* left and right children */
struct isc_radix_node *parent; /* may be used */
void *data[2]; /* pointers to IPv4 and IPV6 data */
int node_num[2]; /* which node this was in the tree,
void *data[4]; /* pointers to IPv4 and IPV6 data */
int node_num[4]; /* which node this was in the tree,
or -1 for glue nodes */
} isc_radix_node_t;

View File

@@ -70,6 +70,7 @@ _new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest,
}
prefix->family = family;
prefix->ecs = ISC_FALSE;
prefix->mctx = NULL;
isc_mem_attach(mctx, &prefix->mctx);
@@ -182,12 +183,13 @@ _clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) {
if (Xrn->prefix != NULL) {
_deref_prefix(Xrn->prefix);
if (func != NULL && (Xrn->data[0] != NULL ||
Xrn->data[1] != NULL))
if (func != NULL)
func(Xrn->data);
} else {
INSIST(Xrn->data[0] == NULL &&
Xrn->data[1] == NULL);
Xrn->data[1] == NULL &&
Xrn->data[2] == NULL &&
Xrn->data[3] == NULL);
}
isc_mem_put(radix->mctx, Xrn, sizeof(*Xrn));
@@ -242,8 +244,7 @@ isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target,
isc_radix_node_t *stack[RADIX_MAXBITS + 1];
u_char *addr;
isc_uint32_t bitlen;
int tfamily = -1;
int cnt = 0;
int toff = -1, cnt = 0;
REQUIRE(radix != NULL);
REQUIRE(prefix != NULL);
@@ -281,13 +282,15 @@ isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target,
if (_comp_with_mask(isc_prefix_tochar(node->prefix),
isc_prefix_tochar(prefix),
node->prefix->bitlen)) {
if (node->node_num[ISC_IS6(prefix->family)] != -1 &&
((*target == NULL) ||
(*target)->node_num[ISC_IS6(tfamily)] >
node->node_num[ISC_IS6(prefix->family)])) {
node->prefix->bitlen))
{
int off = ISC_RADIX_OFF(prefix);
if (node->node_num[off] != -1 &&
((*target == NULL) ||
(*target)->node_num[toff] > node->node_num[off]))
{
*target = node;
tfamily = prefix->family;
toff = off;
}
}
}
@@ -327,7 +330,8 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
if (node == NULL)
return (ISC_R_NOMEMORY);
node->bit = bitlen;
node->node_num[0] = node->node_num[1] = -1;
for (i = 0; i < 4; i++)
node->node_num[i] = -1;
node->prefix = NULL;
result = _ref_prefix(radix->mctx, &node->prefix, prefix);
if (result != ISC_R_SUCCESS) {
@@ -346,25 +350,24 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
* added to num_added_node at the end of
* the merge operation--we don't do it here.
*/
if (source->node_num[0] != -1)
node->node_num[0] = radix->num_added_node +
source->node_num[0];
if (source->node_num[1] != -1)
node->node_num[1] = radix->num_added_node +
source->node_num[1];
node->data[0] = source->data[0];
node->data[1] = source->data[1];
for (i = 0; i < 4; i++) {
if (source->node_num[i] != -1)
node->node_num[i] =
radix->num_added_node +
source->node_num[i];
node->data[i] = source->data[i];
}
} else {
int next = ++radix->num_added_node;
if (fam == AF_UNSPEC) {
/* "any" or "none" */
node->node_num[0] = node->node_num[1] =
++radix->num_added_node;
for (i = 0; i < 4; i++)
node->node_num[i] = next;
} else {
node->node_num[ISC_IS6(fam)] =
++radix->num_added_node;
node->node_num[ISC_RADIX_OFF(prefix)] = next;
}
node->data[0] = NULL;
node->data[1] = NULL;
memset(node->data, 0, sizeof(node->data));
}
radix->head = node;
radix->num_active_node++;
@@ -426,37 +429,33 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
if (node->prefix != NULL) {
/* Set node_num only if it hasn't been set before */
if (source != NULL) {
/* Merging node */
if (node->node_num[0] == -1 &&
source->node_num[0] != -1) {
node->node_num[0] =
radix->num_added_node +
source->node_num[0];
node->data[0] = source->data[0];
}
if (node->node_num[1] == -1 &&
source->node_num[0] != -1) {
node->node_num[1] =
radix->num_added_node +
source->node_num[1];
node->data[1] = source->data[1];
/* Merging nodes */
for (i = 0; i < 4; i++) {
if (node->node_num[i] == -1 &&
source->node_num[i] != -1) {
node->node_num[i] =
radix->num_added_node +
source->node_num[i];
node->data[i] = source->data[i];
}
}
} else {
if (fam == AF_UNSPEC) {
/* "any" or "none" */
int next = radix->num_added_node + 1;
if (node->node_num[0] == -1) {
node->node_num[0] = next;
radix->num_added_node = next;
}
if (node->node_num[1] == -1) {
node->node_num[1] = next;
radix->num_added_node = next;
for (i = 0; i < 4; i++) {
if (node->node_num[i] == -1) {
node->node_num[i] =
next;
radix->num_added_node =
next;
}
}
} else {
if (node->node_num[ISC_IS6(fam)] == -1)
node->node_num[ISC_IS6(fam)]
= ++radix->num_added_node;
int off = ISC_RADIX_OFF(prefix);
if (node->node_num[off] == -1)
node->node_num[off] =
++radix->num_added_node;
}
}
*target = node;
@@ -468,27 +467,27 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
return (result);
}
INSIST(node->data[0] == NULL && node->node_num[0] == -1 &&
node->data[1] == NULL && node->node_num[1] == -1);
node->data[1] == NULL && node->node_num[1] == -1 &&
node->data[2] == NULL && node->node_num[2] == -1 &&
node->data[3] == NULL && node->node_num[3] == -1);
if (source != NULL) {
/* Merging node */
if (source->node_num[0] != -1) {
node->node_num[0] = radix->num_added_node +
source->node_num[0];
node->data[0] = source->data[0];
}
if (source->node_num[1] != -1) {
node->node_num[1] = radix->num_added_node +
source->node_num[1];
node->data[1] = source->data[1];
for (i = 0; i < 4; i++) {
int cur = radix->num_added_node;
if (source->node_num[i] != -1) {
node->node_num[i] =
source->node_num[i] + cur;
node->data[i] = source->data[i];
}
}
} else {
int next = ++radix->num_added_node;
if (fam == AF_UNSPEC) {
/* "any" or "none" */
node->node_num[0] = node->node_num[1] =
++radix->num_added_node;
for (i = 0; i < 4; i++)
node->node_num[i] = next;
} else {
node->node_num[ISC_IS6(fam)] =
++radix->num_added_node;
node->node_num[ISC_RADIX_OFF(prefix)] = next;
}
}
*target = node;
@@ -518,30 +517,30 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
}
new_node->parent = NULL;
new_node->l = new_node->r = NULL;
new_node->node_num[0] = new_node->node_num[1] = -1;
for (i = 0; i < 4; i++)
new_node->node_num[i] = -1;
radix->num_active_node++;
if (source != NULL) {
/* Merging node */
if (source->node_num[0] != -1)
new_node->node_num[0] = radix->num_added_node +
source->node_num[0];
if (source->node_num[1] != -1)
new_node->node_num[1] = radix->num_added_node +
source->node_num[1];
new_node->data[0] = source->data[0];
new_node->data[1] = source->data[1];
for (i = 0; i < 4; i++) {
int cur = radix->num_added_node;
if (source->node_num[i] != -1) {
new_node->node_num[i] =
source->node_num[i] + cur;
new_node->data[i] = source->data[i];
}
}
} else {
int next = ++radix->num_added_node;
if (fam == AF_UNSPEC) {
/* "any" or "none" */
new_node->node_num[0] = new_node->node_num[1] =
++radix->num_added_node;
for (i = 0; i < 4; i++)
new_node->node_num[i] = next;
} else {
new_node->node_num[ISC_IS6(fam)] =
++radix->num_added_node;
new_node->node_num[ISC_RADIX_OFF(prefix)] = next;
}
new_node->data[0] = NULL;
new_node->data[1] = NULL;
memset(new_node->data, 0, sizeof(new_node->data));
}
if (node->bit == differ_bit) {
@@ -583,8 +582,10 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
glue->bit = differ_bit;
glue->prefix = NULL;
glue->parent = node->parent;
glue->data[0] = glue->data[1] = NULL;
glue->node_num[0] = glue->node_num[1] = -1;
for (i = 0; i < 4; i++) {
glue->data[i] = NULL;
glue->node_num[i] = -1;
}
radix->num_active_node++;
if (differ_bit < radix->maxbits &&
BIT_TEST(addr[differ_bit>>3], 0x80 >> (differ_bit & 07))) {
@@ -627,7 +628,7 @@ isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) {
_deref_prefix(node->prefix);
node->prefix = NULL;
node->data[0] = node->data[1] = NULL;
memset(node->data, 0, sizeof(node->data));
return;
}

View File

@@ -696,6 +696,7 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx,
/* Network prefix */
isc_netaddr_t addr;
unsigned int bitlen;
isc_boolean_t setpos, setecs;
cfg_obj_asnetprefix(ce, &addr, &bitlen);
if (family != 0 && family != addr.family) {
@@ -713,8 +714,10 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx,
* If nesting ACLs (nest_level != 0), we negate
* the nestedacl element, not the iptable entry.
*/
result = dns_iptable_addprefix(iptab, &addr, bitlen,
ISC_TF(nest_level != 0 || !neg));
setpos = ISC_TF(nest_level != 0 || !neg);
setecs = cfg_obj_istype(ce, &cfg_type_ecsprefix);
result = dns_iptable_addprefix2(iptab, &addr, bitlen,
setpos, setecs);
if (result != ISC_R_SUCCESS)
goto cleanup;

View File

@@ -54,4 +54,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_sessionkey;
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_keyref;
/*%< A key reference, used as an ACL element */
/*%< An EDNS client subnet address, used as an ACL element */
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_ecsprefix;
#endif /* ISCCFG_NAMEDCONF_H */

View File

@@ -950,9 +950,12 @@ options_clauses[] = {
{ "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
#ifdef HAVE_GEOIP
{ "geoip-directory", &cfg_type_qstringornone, 0 },
{ "geoip-use-ecs", &cfg_type_boolean, 0 },
#else
{ "geoip-directory", &cfg_type_qstringornone,
CFG_CLAUSEFLAG_NOTCONFIGURED },
{ "geoip-use-ecs", &cfg_type_qstringornone,
CFG_CLAUSEFLAG_NOTCONFIGURED },
#endif /* HAVE_GEOIP */
{ "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
{ "heartbeat-interval", &cfg_type_uint32, 0 },
@@ -2281,6 +2284,16 @@ doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
}
#endif /* HAVE_GEOIP */
/*%
* An EDNS client subnet address
*/
static keyword_type_t ecs_kw = { "ecs", &cfg_type_netprefix };
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_ecsprefix = {
"edns_client_subnet", parse_keyvalue, print_keyvalue, doc_keyvalue,
&cfg_rep_netprefix, &ecs_kw
};
/*%
* A "controls" statement is represented as a map with the multivalued
* "inet" and "unix" clauses.
@@ -2570,6 +2583,9 @@ parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
if (pctx->token.type == isc_tokentype_string &&
(strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
} else if (pctx->token.type == isc_tokentype_string &&
(strcasecmp(TOKEN_STRING(pctx), "ecs") == 0)) {
CHECK(cfg_parse_obj(pctx, &cfg_type_ecsprefix, ret));
} else if (pctx->token.type == isc_tokentype_string &&
(strcasecmp(TOKEN_STRING(pctx), "geoip") == 0)) {
#ifdef HAVE_GEOIP