Last active
March 11, 2020 14:36
-
-
Save vincentbernat/6524506 to your computer and use it in GitHub Desktop.
EDNS0 client subnet support for BIND
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 83f0062d385fd4f111b31c1f26b571cabd7e0e4c Mon Sep 17 00:00:00 2001 | |
From: Vincent Bernat <[email protected]> | |
Date: Thu, 5 Sep 2013 16:52:45 +0200 | |
Subject: [PATCH] EDNS0 client subnet support. | |
--- | |
bin/named/client.c | 227 +++++++++++++++++++++++++++++++-------- | |
bin/named/include/named/client.h | 4 + | |
bin/named/include/named/server.h | 81 +++++++------- | |
bin/named/sortlist.c | 4 +- | |
bin/named/statschannel.c | 2 + | |
lib/dns/acl.c | 86 ++++++++++----- | |
lib/dns/include/dns/acl.h | 18 ++++ | |
7 files changed, 312 insertions(+), 110 deletions(-) | |
diff --git a/bin/named/client.c b/bin/named/client.c | |
index 2115ac1..f5db80e 100644 | |
--- a/bin/named/client.c | |
+++ b/bin/named/client.c | |
@@ -1188,6 +1188,8 @@ client_addopt(ns_client_t *client) { | |
dns_view_t *view; | |
dns_resolver_t *resolver; | |
isc_uint16_t udpsize; | |
+ isc_buffer_t *buffer = NULL; | |
+ char nsid[BUFSIZ], *nsidp = NULL; | |
REQUIRE(client->opt == NULL); /* XXXRTH free old. */ | |
@@ -1225,6 +1227,20 @@ client_addopt(ns_client_t *client) { | |
rdatalist->ttl = (client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE); | |
/* Set EDNS options if applicable */ | |
+ rdata->data = NULL; | |
+ rdata->length = 0; | |
+ if (client->clientaddrlen) { | |
+ /* Client subnet */ | |
+ /* What to do when scope is 0? If we keep 0, we will | |
+ * provide an answer valid for any IP address. If we | |
+ * don't provide a client subnet, all the same. So we | |
+ * have to do something sensible. We assume that we | |
+ * did use client subnet. TODO: add a flag to check | |
+ * that we have a geoenabled view. */ | |
+ if (client->clientscopelen == 0) | |
+ client->clientscopelen = client->clientaddrlen; | |
+ rdata->length += 8 + (client->clientaddrlen + 7) / 8; | |
+ } | |
if (client->attributes & NS_CLIENTATTR_WANTNSID && | |
(ns_g_server->server_id != NULL || | |
ns_g_server->server_usehostname)) { | |
@@ -1234,36 +1250,49 @@ client_addopt(ns_client_t *client) { | |
* + 2 bytes for NSID length | |
* + NSID itself | |
*/ | |
- char nsid[BUFSIZ], *nsidp; | |
- isc_buffer_t *buffer = NULL; | |
- | |
if (ns_g_server->server_usehostname) { | |
isc_result_t result; | |
result = ns_os_gethostname(nsid, sizeof(nsid)); | |
- if (result != ISC_R_SUCCESS) { | |
- goto no_nsid; | |
+ if (result == ISC_R_SUCCESS) { | |
+ nsidp = nsid; | |
} | |
- nsidp = nsid; | |
} else | |
nsidp = ns_g_server->server_id; | |
- rdata->length = strlen(nsidp) + 4; | |
- result = isc_buffer_allocate(client->mctx, &buffer, | |
- rdata->length); | |
- if (result != ISC_R_SUCCESS) | |
- goto no_nsid; | |
- | |
+ if (nsidp) | |
+ rdata->length += strlen(nsidp) + 4; | |
+ } | |
+ | |
+ result = isc_buffer_allocate(client->mctx, &buffer, | |
+ rdata->length); | |
+ if (result != ISC_R_SUCCESS) goto no_opt; | |
+ if (client->clientaddrlen) { | |
+ int addrbytes = (client->clientaddrlen + 7) / 8; | |
+ isc_uint8_t *paddr; | |
+ int i; | |
+ isc_buffer_putuint16(buffer, DNS_OPT_CLIENT_SUBNET); | |
+ isc_buffer_putuint16(buffer, 4 + addrbytes); | |
+ isc_buffer_putuint16(buffer, | |
+ (client->clientaddr.family == AF_INET)?1:2); | |
+ isc_buffer_putuint8(buffer, client->clientaddrlen); | |
+ isc_buffer_putuint8(buffer, client->clientscopelen); | |
+ paddr = (isc_uint8_t*)&client->clientaddr.type; | |
+ for (i = 0; i < addrbytes; i++) { | |
+ isc_buffer_putuint8(buffer, | |
+ paddr[i] & ((i == addrbytes - 1 && | |
+ (client->clientaddrlen % 8))? | |
+ (1 << (8 - (client->clientaddrlen % 8))): | |
+ 0xff)); | |
+ } | |
+ } | |
+ if (nsidp) { | |
isc_buffer_putuint16(buffer, DNS_OPT_NSID); | |
isc_buffer_putuint16(buffer, strlen(nsidp)); | |
isc_buffer_putstr(buffer, nsidp); | |
- rdata->data = buffer->base; | |
- dns_message_takebuffer(client->message, &buffer); | |
- } else { | |
-no_nsid: | |
- rdata->data = NULL; | |
- rdata->length = 0; | |
} | |
- | |
+ rdata->data = buffer->base; | |
+ dns_message_takebuffer(client->message, &buffer); | |
+no_opt: | |
rdata->rdclass = rdatalist->rdclass; | |
rdata->type = rdatalist->type; | |
rdata->flags = 0; | |
@@ -1279,14 +1308,17 @@ no_nsid: | |
} | |
static inline isc_boolean_t | |
-allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) { | |
+allowed(isc_netaddr_t *addr, isc_netaddr_t *clientaddr, | |
+ isc_uint8_t *scope, | |
+ dns_name_t *signer, 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_match_with_client(addr, clientaddr, scope, | |
+ signer, acl, &ns_g_server->aclenv, | |
+ &match, NULL); | |
if (result == ISC_R_SUCCESS && match > 0) | |
return (ISC_TRUE); | |
return (ISC_FALSE); | |
@@ -1349,14 +1381,91 @@ 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, NULL, NULL, tsig, view->matchclients) && | |
+ allowed(&netdst, NULL, NULL, tsig, view->matchdestinations)) | |
break; | |
} | |
return (ISC_TF(view == myview)); | |
} | |
/* | |
+ * Parse EDNS0 client subnet option. `optlen` is the length of the | |
+ * option after the code and the length. It will be updated to reflect | |
+ * with what has been read. | |
+ */ | |
+static isc_result_t | |
+client_parse_clientsubnet(ns_client_t *client, isc_buffer_t *rdbuf, isc_uint16_t *optlen) { | |
+ isc_uint16_t family; | |
+ isc_uint8_t addrlen; /* Source netmask */ | |
+ isc_uint8_t addrbytes; | |
+ isc_uint8_t scopelen; /* Scope netmask */ | |
+ isc_netaddr_t clientaddr; | |
+ isc_uint8_t *paddr; | |
+ int i; | |
+ | |
+ REQUIRE(NS_CLIENT_VALID(client)); | |
+ REQUIRE(rdbuf != NULL); | |
+ REQUIRE(optlen != NULL); | |
+ INSIST(isc_buffer_remaininglength(rdbuf) >= *optlen); | |
+ | |
+ if (*optlen < 4) { | |
+ /* 2 octets for family, 1 octet for source netmask and | |
+ * 1 octet for scope netmask */ | |
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT, | |
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2), | |
+ "EDNS0 client subnet option is too short"); | |
+ return DNS_R_FORMERR; | |
+ } | |
+ | |
+ family = isc_buffer_getuint16(rdbuf); | |
+ addrlen = isc_buffer_getuint8(rdbuf); | |
+ scopelen = isc_buffer_getuint8(rdbuf); | |
+ *optlen -= 4; | |
+ | |
+ if (scopelen != 0) { | |
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT, | |
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2), | |
+ "EDNS0 client subnet scope is not 0"); | |
+ return DNS_R_FORMERR; | |
+ } | |
+ | |
+ memset(&clientaddr, 0, sizeof(clientaddr)); | |
+ switch (family) { | |
+ case 1: | |
+ /* IPv4 */ | |
+ if (addrlen > 32) addrlen = 32; | |
+ clientaddr.family = AF_INET; | |
+ break; | |
+ case 2: | |
+ /* IPv6 */ | |
+ if (addrlen > 128) addrlen = 128; | |
+ clientaddr.family = AF_INET6; | |
+ break; | |
+ default: | |
+ /* Dunno what to do, let's skip all. */ | |
+ return DNS_R_FORMERR; | |
+ } | |
+ | |
+ addrbytes = (addrlen + 7) / 8; | |
+ if (*optlen < addrbytes) { | |
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT, | |
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2), | |
+ "EDNS0 client subnet address is too short"); | |
+ return DNS_R_FORMERR; | |
+ } | |
+ paddr = (isc_uint8_t*)&clientaddr.type; | |
+ for (i = 0; i < addrbytes; i++) { | |
+ paddr[i] = isc_buffer_getuint8(rdbuf); | |
+ } | |
+ *optlen -= i; | |
+ | |
+ memcpy(&client->clientaddr, &clientaddr, sizeof(clientaddr)); | |
+ client->clientaddrlen = addrlen; | |
+ client->clientscopelen = 0; | |
+ return ISC_R_SUCCESS; | |
+} | |
+ | |
+/* | |
* Handle an incoming request event from the socket (UDP case) | |
* or tcpmsg (TCP case). | |
*/ | |
@@ -1379,6 +1488,7 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
isc_boolean_t notimp; | |
dns_rdata_t rdata; | |
isc_uint16_t optcode; | |
+ isc_uint16_t optlen; | |
REQUIRE(event != NULL); | |
client = event->ev_arg; | |
@@ -1577,6 +1687,7 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
/* | |
* Deal with EDNS. | |
*/ | |
+ client->clientaddrlen = client->clientscopelen = 0; | |
opt = dns_message_getopt(client->message); | |
if (opt != NULL) { | |
/* | |
@@ -1612,20 +1723,43 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
goto cleanup; | |
} | |
- /* Check for NSID request */ | |
- result = dns_rdataset_first(opt); | |
- if (result == ISC_R_SUCCESS) { | |
+ /* Check for EDNS0 options */ | |
+ for (result = dns_rdataset_first(opt); result == ISC_R_SUCCESS; | |
+ result = dns_rdataset_next(opt)) { | |
+ isc_buffer_t rdbuf; | |
dns_rdata_init(&rdata); | |
dns_rdataset_current(opt, &rdata); | |
- if (rdata.length >= 2) { | |
- isc_buffer_t nsidbuf; | |
- isc_buffer_init(&nsidbuf, | |
- rdata.data, rdata.length); | |
- isc_buffer_add(&nsidbuf, rdata.length); | |
- optcode = isc_buffer_getuint16(&nsidbuf); | |
- if (optcode == DNS_OPT_NSID) | |
+ isc_buffer_init(&rdbuf, | |
+ rdata.data, rdata.length); | |
+ isc_buffer_add(&rdbuf, rdata.length); | |
+ while (isc_buffer_remaininglength(&rdbuf) >= 4) { | |
+ optcode = isc_buffer_getuint16(&rdbuf); | |
+ optlen = isc_buffer_getuint16(&rdbuf); | |
+ if (isc_buffer_remaininglength(&rdbuf) < optlen) { | |
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT, | |
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2), | |
+ "EDNS0 option is shorter than expected"); | |
+ ns_client_error(client, DNS_R_FORMERR); | |
+ goto cleanup; | |
+ } | |
+ switch (optcode) { | |
+ case DNS_OPT_NSID: | |
client->attributes |= | |
NS_CLIENTATTR_WANTNSID; | |
+ break; | |
+ case DNS_OPT_CLIENT_SUBNET: | |
+ result = client_parse_clientsubnet(client, | |
+ &rdbuf, | |
+ &optlen); | |
+ if (result != ISC_R_SUCCESS) { | |
+ ns_client_error(client, result); | |
+ goto cleanup; | |
+ } | |
+ isc_stats_increment(ns_g_server->nsstats, | |
+ dns_nsstatscounter_subnetclientin); | |
+ break; | |
+ } | |
+ isc__buffer_forward(&rdbuf, optlen); | |
} | |
} | |
@@ -1633,13 +1767,9 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
dns_nsstatscounter_edns0in); | |
/* | |
- * Create an OPT for our reply. | |
+ * Request an OPT for our reply. | |
*/ | |
- result = client_addopt(client); | |
- if (result != ISC_R_SUCCESS) { | |
- ns_client_error(client, result); | |
- goto cleanup; | |
- } | |
+ client->attributes |= NS_CLIENTATTR_EDNS0; | |
} | |
if (client->message->rdclass == 0) { | |
@@ -1719,8 +1849,11 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
if (sigresult == ISC_R_SUCCESS) | |
tsig = dns_tsigkey_identity(client->message->tsigkey); | |
- if (allowed(&netaddr, tsig, view->matchclients) && | |
- allowed(&client->destaddr, tsig, | |
+ if (allowed(&netaddr, | |
+ client->clientaddrlen?&client->clientaddr:NULL, | |
+ client->clientaddrlen?&client->clientscopelen:NULL, | |
+ tsig, view->matchclients) && | |
+ allowed(&client->destaddr, NULL, NULL, tsig, | |
view->matchdestinations) && | |
!((client->message->flags & DNS_MESSAGEFLAG_RD) | |
== 0 && view->matchrecursiveonly)) | |
@@ -1763,6 +1896,14 @@ client_request(isc_task_t *task, isc_event_t *event) { | |
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5), | |
"using view '%s'", view->name); | |
+ if (client->attributes | NS_CLIENTATTR_EDNS0) { | |
+ result = client_addopt(client); | |
+ if (result != ISC_R_SUCCESS) { | |
+ ns_client_error(client, result); | |
+ goto cleanup; | |
+ } | |
+ } | |
+ | |
/* | |
* Check for a signature. We log bad signatures regardless of | |
* whether they ultimately cause the request to be rejected or | |
@@ -2109,6 +2250,8 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { | |
client->recursionquota = NULL; | |
client->interface = NULL; | |
client->peeraddr_valid = ISC_FALSE; | |
+ client->clientaddrlen = 0; | |
+ client->clientscopelen = 0; | |
ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL, | |
NS_EVENT_CLIENTCONTROL, client_start, client, client, | |
NULL, NULL); | |
diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h | |
index 33f124d..5eceb2f 100644 | |
--- a/bin/named/include/named/client.h | |
+++ b/bin/named/include/named/client.h | |
@@ -139,6 +139,9 @@ struct ns_client { | |
isc_sockaddr_t peeraddr; | |
isc_boolean_t peeraddr_valid; | |
isc_netaddr_t destaddr; | |
+ isc_netaddr_t clientaddr; | |
+ isc_uint8_t clientaddrlen; | |
+ isc_uint8_t clientscopelen; | |
struct in6_pktinfo pktinfo; | |
isc_event_t ctlevent; | |
/*% | |
@@ -172,6 +175,7 @@ struct ns_client { | |
#define NS_CLIENTATTR_FILTER_AAAA 0x40 /*%< suppress AAAAs */ | |
#define NS_CLIENTATTR_FILTER_AAAA_RC 0x80 /*%< recursing for A against AAAA */ | |
#endif | |
+#define NS_CLIENTATTR_EDNS0 0x100 /*%< add an EDNS0 option block in response */ | |
extern unsigned int ns_client_requests; | |
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h | |
index 3c6426e..9491e37 100644 | |
--- a/bin/named/include/named/server.h | |
+++ b/bin/named/include/named/server.h | |
@@ -126,46 +126,47 @@ enum { | |
dns_nsstatscounter_requestv4 = 0, | |
dns_nsstatscounter_requestv6 = 1, | |
dns_nsstatscounter_edns0in = 2, | |
- dns_nsstatscounter_badednsver = 3, | |
- dns_nsstatscounter_tsigin = 4, | |
- dns_nsstatscounter_sig0in = 5, | |
- dns_nsstatscounter_invalidsig = 6, | |
- dns_nsstatscounter_tcp = 7, | |
- | |
- dns_nsstatscounter_authrej = 8, | |
- dns_nsstatscounter_recurserej = 9, | |
- dns_nsstatscounter_xfrrej = 10, | |
- dns_nsstatscounter_updaterej = 11, | |
- | |
- dns_nsstatscounter_response = 12, | |
- dns_nsstatscounter_truncatedresp = 13, | |
- dns_nsstatscounter_edns0out = 14, | |
- dns_nsstatscounter_tsigout = 15, | |
- dns_nsstatscounter_sig0out = 16, | |
- | |
- dns_nsstatscounter_success = 17, | |
- dns_nsstatscounter_authans = 18, | |
- dns_nsstatscounter_nonauthans = 19, | |
- dns_nsstatscounter_referral = 20, | |
- dns_nsstatscounter_nxrrset = 21, | |
- dns_nsstatscounter_servfail = 22, | |
- dns_nsstatscounter_formerr = 23, | |
- dns_nsstatscounter_nxdomain = 24, | |
- dns_nsstatscounter_recursion = 25, | |
- dns_nsstatscounter_duplicate = 26, | |
- dns_nsstatscounter_dropped = 27, | |
- dns_nsstatscounter_failure = 28, | |
- | |
- dns_nsstatscounter_xfrdone = 29, | |
- | |
- dns_nsstatscounter_updatereqfwd = 30, | |
- dns_nsstatscounter_updaterespfwd = 31, | |
- dns_nsstatscounter_updatefwdfail = 32, | |
- dns_nsstatscounter_updatedone = 33, | |
- dns_nsstatscounter_updatefail = 34, | |
- dns_nsstatscounter_updatebadprereq = 35, | |
- | |
- dns_nsstatscounter_max = 36 | |
+ dns_nsstatscounter_subnetclientin = 3, | |
+ dns_nsstatscounter_badednsver = 4, | |
+ dns_nsstatscounter_tsigin = 5, | |
+ dns_nsstatscounter_sig0in = 6, | |
+ dns_nsstatscounter_invalidsig = 7, | |
+ dns_nsstatscounter_tcp = 8, | |
+ | |
+ dns_nsstatscounter_authrej = 9, | |
+ dns_nsstatscounter_recurserej = 10, | |
+ dns_nsstatscounter_xfrrej = 11, | |
+ dns_nsstatscounter_updaterej = 12, | |
+ | |
+ dns_nsstatscounter_response = 13, | |
+ dns_nsstatscounter_truncatedresp = 14, | |
+ dns_nsstatscounter_edns0out = 15, | |
+ dns_nsstatscounter_tsigout = 16, | |
+ dns_nsstatscounter_sig0out = 17, | |
+ | |
+ dns_nsstatscounter_success = 18, | |
+ dns_nsstatscounter_authans = 19, | |
+ dns_nsstatscounter_nonauthans = 20, | |
+ dns_nsstatscounter_referral = 21, | |
+ dns_nsstatscounter_nxrrset = 22, | |
+ dns_nsstatscounter_servfail = 23, | |
+ dns_nsstatscounter_formerr = 24, | |
+ dns_nsstatscounter_nxdomain = 25, | |
+ dns_nsstatscounter_recursion = 26, | |
+ dns_nsstatscounter_duplicate = 27, | |
+ dns_nsstatscounter_dropped = 28, | |
+ dns_nsstatscounter_failure = 29, | |
+ | |
+ dns_nsstatscounter_xfrdone = 30, | |
+ | |
+ dns_nsstatscounter_updatereqfwd = 31, | |
+ dns_nsstatscounter_updaterespfwd = 32, | |
+ dns_nsstatscounter_updatefwdfail = 33, | |
+ dns_nsstatscounter_updatedone = 34, | |
+ dns_nsstatscounter_updatefail = 35, | |
+ dns_nsstatscounter_updatebadprereq = 36, | |
+ | |
+ dns_nsstatscounter_max = 37 | |
}; | |
void | |
diff --git a/bin/named/sortlist.c b/bin/named/sortlist.c | |
index 0710fb1..37af55c 100644 | |
--- a/bin/named/sortlist.c | |
+++ b/bin/named/sortlist.c | |
@@ -72,7 +72,7 @@ ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr, | |
try_elt = e; | |
} | |
- if (dns_aclelement_match(clientaddr, NULL, try_elt, | |
+ if (dns_aclelement_match(clientaddr, NULL, NULL, NULL, try_elt, | |
&ns_g_server->aclenv, | |
&matched_elt)) { | |
if (order_elt != NULL) { | |
@@ -132,7 +132,7 @@ ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg) { | |
int | |
ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg) { | |
const dns_aclelement_t *matchelt = (const dns_aclelement_t *) arg; | |
- if (dns_aclelement_match(addr, NULL, matchelt, | |
+ if (dns_aclelement_match(addr, NULL, NULL, NULL, matchelt, | |
&ns_g_server->aclenv, | |
NULL)) { | |
return (0); | |
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c | |
index 1f72694..110dff4 100644 | |
--- a/bin/named/statschannel.c | |
+++ b/bin/named/statschannel.c | |
@@ -152,6 +152,8 @@ init_desc(void) { | |
SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4"); | |
SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6"); | |
SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0"); | |
+ SET_NSSTATDESC(subnetclientin, "requests with EDNS(0) subnet client received", | |
+ "ReqEdns0SubnetClient"); | |
SET_NSSTATDESC(badednsver, | |
"requests with unsupported EDNS version received", | |
"ReqBadEDNSVer"); | |
diff --git a/lib/dns/acl.c b/lib/dns/acl.c | |
index 2f272e2..44ac41e 100644 | |
--- a/lib/dns/acl.c | |
+++ b/lib/dns/acl.c | |
@@ -249,12 +249,14 @@ dns_acl_isnone(dns_acl_t *acl) | |
* element or radix entry, return with a negative value in match. | |
*/ | |
isc_result_t | |
-dns_acl_match(const isc_netaddr_t *reqaddr, | |
- const dns_name_t *reqsigner, | |
- const dns_acl_t *acl, | |
- const dns_aclenv_t *env, | |
- int *match, | |
- const dns_aclelement_t **matchelt) | |
+dns_acl_match_with_client(const isc_netaddr_t *reqaddr, | |
+ const isc_netaddr_t *clientaddr, | |
+ isc_uint8_t *scope, | |
+ const dns_name_t *reqsigner, | |
+ const dns_acl_t *acl, | |
+ const dns_aclenv_t *env, | |
+ int *match, | |
+ const dns_aclelement_t **matchelt) | |
{ | |
isc_uint16_t bitlen, family; | |
isc_prefix_t pfx; | |
@@ -307,7 +309,7 @@ dns_acl_match(const isc_netaddr_t *reqaddr, | |
return (ISC_R_SUCCESS); | |
} | |
- if (dns_aclelement_match(reqaddr, reqsigner, | |
+ if (dns_aclelement_match(reqaddr, clientaddr, scope, reqsigner, | |
e, env, matchelt)) { | |
if (match_num == -1 || e->node_num < match_num) { | |
if (e->negative == ISC_TRUE) | |
@@ -324,6 +326,18 @@ dns_acl_match(const isc_netaddr_t *reqaddr, | |
return (ISC_R_SUCCESS); | |
} | |
+isc_result_t | |
+dns_acl_match(const isc_netaddr_t *reqaddr, | |
+ const dns_name_t *reqsigner, | |
+ const dns_acl_t *acl, | |
+ const dns_aclenv_t *env, | |
+ int *match, | |
+ const dns_aclelement_t **matchelt) | |
+{ | |
+ return dns_acl_match_with_client(reqaddr, NULL, NULL, reqsigner, acl, | |
+ env, match, matchelt); | |
+} | |
+ | |
/* | |
* Merge the contents of one ACL into another. Call dns_iptable_merge() | |
* for the IP tables, then concatenate the element arrays. | |
@@ -433,6 +447,8 @@ dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos) | |
*/ | |
isc_boolean_t | |
dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
+ const isc_netaddr_t *clientaddr, | |
+ isc_uint8_t *scope, | |
const dns_name_t *reqsigner, | |
const dns_aclelement_t *e, | |
const dns_aclenv_t *env, | |
@@ -442,6 +458,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
int indirectmatch; | |
isc_result_t result; | |
#ifdef HAVE_GEOIP | |
+ const isc_netaddr_t *geoaddr = clientaddr?clientaddr:reqaddr; | |
+ GeoIPLookup gl; | |
uint32_t ipnum = 0; | |
#ifdef HAVE_GEOIP_V6 | |
const geoipv6_t *ipnum6 = NULL; | |
@@ -455,18 +473,18 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
#endif | |
#endif /* HAVE_GEOIP_V6 */ | |
- switch ( reqaddr->family ) { | |
+ switch ( geoaddr->family ) { | |
case AF_INET: | |
- ipnum = ntohl(reqaddr->type.in.s_addr); | |
+ ipnum = ntohl(geoaddr->type.in.s_addr); | |
#ifdef DEBUG_GEOIP | |
- inet_ntop(AF_INET, &reqaddr->type.in, ipstr, INET_ADDRSTRLEN); | |
+ inet_ntop(AF_INET, &geoaddr->type.in, ipstr, INET_ADDRSTRLEN); | |
#endif | |
break; | |
#ifdef HAVE_GEOIP_V6 | |
case AF_INET6: | |
- ipnum6 = &reqaddr->type.in6; | |
+ ipnum6 = &geoaddr->type.in6; | |
#ifdef DEBUG_GEOIP | |
- inet_ntop(AF_INET6, &reqaddr->type.in6, ipstr, INET6_ADDRSTRLEN); | |
+ inet_ntop(AF_INET6, &geoaddr->type.in6, ipstr, INET6_ADDRSTRLEN); | |
#endif | |
break; | |
#endif | |
@@ -521,10 +539,10 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_countryDB.subtype ) { | |
case geoip_countryDB_country_code: | |
if ( ipnum ) | |
- result = GeoIP_country_code_by_ipnum( ns_g_geoip_countryDB, ipnum ); | |
+ result = GeoIP_country_code_by_ipnum_gl( ns_g_geoip_countryDB, ipnum, &gl ); | |
#ifdef HAVE_GEOIP_V6 | |
else if ( ipnum6 ) | |
- result = GeoIP_country_code_by_ipnum_v6( ns_g_geoip_countryDB_v6, *ipnum6 ); | |
+ result = GeoIP_country_code_by_ipnum_v6_gl( ns_g_geoip_countryDB_v6, *ipnum6, &gl ); | |
#endif | |
if ( result ) | |
georesult = ( strncasecmp( e->geoip_countryDB.country_code, result, 2 ) == 0 ); | |
@@ -539,10 +557,10 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
break; | |
case geoip_countryDB_country_code3: | |
if ( ipnum ) | |
- result = GeoIP_country_code3_by_ipnum( ns_g_geoip_countryDB, ipnum ); | |
+ result = GeoIP_country_code3_by_ipnum_gl( ns_g_geoip_countryDB, ipnum, &gl ); | |
#ifdef HAVE_GEOIP_V6 | |
else if ( ipnum6 ) | |
- result = GeoIP_country_code3_by_ipnum_v6( ns_g_geoip_countryDB_v6, *ipnum6 ); | |
+ result = GeoIP_country_code3_by_ipnum_v6_gl( ns_g_geoip_countryDB_v6, *ipnum6, &gl ); | |
#endif | |
if ( result ) | |
georesult = ( strncasecmp( e->geoip_countryDB.country_code3, result, 3 ) == 0 ); | |
@@ -557,10 +575,10 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
break; | |
case geoip_countryDB_country_name: | |
if ( ipnum ) | |
- result = GeoIP_country_name_by_ipnum( ns_g_geoip_countryDB, ipnum ); | |
+ result = GeoIP_country_name_by_ipnum_gl( ns_g_geoip_countryDB, ipnum, &gl ); | |
#ifdef HAVE_GEOIP_V6 | |
else if ( ipnum6 ) | |
- result = GeoIP_country_name_by_ipnum_v6( ns_g_geoip_countryDB_v6, *ipnum6 ); | |
+ result = GeoIP_country_name_by_ipnum_v6_gl( ns_g_geoip_countryDB_v6, *ipnum6, &gl ); | |
#endif | |
if ( result ) | |
georesult = ( strcasecmp( e->geoip_countryDB.country_name, result ) == 0 ); | |
@@ -576,6 +594,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_countryDB */ | |
@@ -783,6 +803,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
"client %s: geoip_cityDB found no record", | |
ipstr); | |
#endif | |
+ if (georesult && record && scope && *scope == 0) | |
+ *scope = record->netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_cityDB */ | |
@@ -793,7 +815,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
if ( !ipnum || !ns_g_geoip_regionDB ) | |
return(ISC_FALSE); | |
- if (( record = GeoIP_region_by_ipnum( ns_g_geoip_regionDB, ipnum) )) { | |
+ if (( record = GeoIP_region_by_ipnum_gl( ns_g_geoip_regionDB, ipnum, &gl ) )) { | |
switch ( e->geoip_regionDB.subtype ) { | |
case geoip_regionDB_country_code: | |
if ( record->country_code ) | |
@@ -830,6 +852,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
"client %s: geoip_regionDB found no record", | |
ipstr); | |
#endif | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_regionDB */ | |
@@ -842,7 +866,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_ispDB.subtype ) { | |
case geoip_ispDB_name: | |
- if (( result = GeoIP_name_by_ipnum( ns_g_geoip_ispDB, ipnum ) )) | |
+ if (( result = GeoIP_name_by_ipnum_gl( ns_g_geoip_ispDB, ipnum, &gl ) )) | |
georesult = ( strcasecmp( e->geoip_ispDB.name, result ) == 0 ); | |
#ifdef DEBUG_GEOIP | |
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, | |
@@ -856,6 +880,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_ispDB */ | |
@@ -868,7 +894,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_orgDB.subtype ) { | |
case geoip_orgDB_name: | |
- if (( result = GeoIP_name_by_ipnum( ns_g_geoip_orgDB, ipnum ) )) | |
+ if (( result = GeoIP_name_by_ipnum_gl( ns_g_geoip_orgDB, ipnum, &gl ) )) | |
georesult = ( strcasecmp( e->geoip_orgDB.name, result ) == 0 ); | |
#ifdef DEBUG_GEOIP | |
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, | |
@@ -882,6 +908,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_orgDB */ | |
@@ -894,7 +922,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_asDB.subtype ) { | |
case geoip_asDB_org: | |
- if (( result = GeoIP_org_by_ipnum( ns_g_geoip_asDB, ipnum ) )) | |
+ if (( result = GeoIP_name_by_ipnum_gl( ns_g_geoip_asDB, ipnum, &gl ) )) | |
georesult = ( strcasecmp( e->geoip_asDB.org, result ) == 0 ); | |
#ifdef DEBUG_GEOIP | |
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, | |
@@ -908,6 +936,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_asDB */ | |
@@ -920,7 +950,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_netspeedDB.subtype ) { | |
case geoip_netspeedDB_id: | |
- result = GeoIP_id_by_ipnum( ns_g_geoip_netspeedDB, ipnum ); | |
+ result = GeoIP_id_by_ipnum_gl( ns_g_geoip_netspeedDB, ipnum, &gl ); | |
georesult = ( e->geoip_netspeedDB.id == result ); | |
#ifdef DEBUG_GEOIP | |
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, | |
@@ -934,6 +964,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_netspeedDB */ | |
@@ -946,7 +978,7 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
switch ( e->geoip_domainDB.subtype ) { | |
case geoip_domainDB_name: | |
- if (( result = GeoIP_name_by_ipnum( ns_g_geoip_domainDB, ipnum ) )) | |
+ if (( result = GeoIP_name_by_ipnum_gl( ns_g_geoip_domainDB, ipnum, &gl ) )) | |
georesult = ( strcasecmp( e->geoip_domainDB.name, result ) == 0 ); | |
#ifdef DEBUG_GEOIP | |
isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, | |
@@ -960,6 +992,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
default: | |
break; | |
} /* switch */ | |
+ if (georesult && scope && *scope == 0) | |
+ *scope = gl.netmask; | |
return( georesult ? ISC_TRUE : ISC_FALSE ); | |
} /* case geoip_domainDB */ | |
@@ -982,8 +1016,8 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
INSIST(0); | |
} | |
- result = dns_acl_match(reqaddr, reqsigner, inner, env, | |
- &indirectmatch, matchelt); | |
+ result = dns_acl_match_with_client(reqaddr, clientaddr, scope, reqsigner, inner, env, | |
+ &indirectmatch, matchelt); | |
INSIST(result == ISC_R_SUCCESS); | |
/* | |
diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h | |
index 26c6912..0d6423e 100644 | |
--- a/lib/dns/include/dns/acl.h | |
+++ b/lib/dns/include/dns/acl.h | |
@@ -359,8 +359,26 @@ dns_acl_match(const isc_netaddr_t *reqaddr, | |
*\li #ISC_R_SUCCESS Always succeeds. | |
*/ | |
+isc_result_t | |
+dns_acl_match_with_client(const isc_netaddr_t *reqaddr, | |
+ const isc_netaddr_t *clientaddr, | |
+ isc_uint8_t *scope, | |
+ const dns_name_t *reqsigner, | |
+ const dns_acl_t *acl, | |
+ const dns_aclenv_t *env, | |
+ int *match, | |
+ const dns_aclelement_t **matchelt); | |
+/*%< | |
+ * Similar to dns_acl_match but should be provided with the IP | |
+ * address of the client (through EDNS0 client subnet extension) in | |
+ * addition to the IP address of the requester. This IP address will be | |
+ * used to match GeoIP. | |
+ */ | |
+ | |
isc_boolean_t | |
dns_aclelement_match(const isc_netaddr_t *reqaddr, | |
+ const isc_netaddr_t *clientaddr, | |
+ isc_uint8_t *scope, | |
const dns_name_t *reqsigner, | |
const dns_aclelement_t *e, | |
const dns_aclenv_t *env, | |
-- | |
1.8.4.rc3 |
Hi Vincent,
Please help me here as i am struggling with this from long time and didn't find the way to solve it.
Thanks,
Punit
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks Vincent,
I successfully installed the bind,and tried with the dig but it seems it doesn't provide the expected results.
[root@ns3 bin]# ./dig @dns-02.dailymotion.com www.dailymotion.com +client=170.149.100.0/24
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; CLIENT-SUBNET: 170.149.100.0/24/22
;; QUESTION SECTION:
[root@ns3 bin]# ./dig @127.0.0.1 www.dailymotion.com +client=170.149.100.0/24
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.dailymotion.com. IN A