-
-
Save vincentbernat/6524506 to your computer and use it in GitHub Desktop.
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,
I tried it but failed to compile the bind after patch it up with "0001-EDNS0-client-subnet-support.patch" and "bind-9.8.7-P1-geoip-1.4.patch",i am using "bind-9.8.1-P1",please help to put some light on it to make it happen.
client.c: In function ‘client_addopt’:
client.c:1273: error: ‘DNS_OPT_CLIENT_SUBNET’ undeclared (first use in this function)
client.c:1273: error: (Each undeclared identifier is reported only once
client.c:1273: error: for each function it appears in.)
client.c: In function ‘client_request’:
client.c:1750: error: ‘DNS_OPT_CLIENT_SUBNET’ undeclared (first use in this function)
make[2]: *** [client.o] Error 1
make[2]: Leaving directory /usr/local/src/bind-9.8.1-P1/bin/named' make[1]: *** [subdirs] Error 1 make[1]: Leaving directory
/usr/local/src/bind-9.8.1-P1/bin'
make: *** [subdirs] Error 1
Thanks,
Punit
@supporthk In fact, you also need to add this patch: http://wilmer.gaa.st/edns-client-subnet/bind-9.8.4-dig-edns-client-subnet-iana.diff ; it will add the missing symbol.
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
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
hello. This version is BIND9.8.1-P1? lib/dns/acl.c and lib/dns/include/dns/acl.h There will be mistakes on the patch BIND9.8.1-P1。
patching file a/bin/named/client.c
patching file a/bin/named/include/named/client.h
patching file a/bin/named/include/named/server.h
patching file a/bin/named/sortlist.c
patching file a/bin/named/statschannel.c
patching file a/lib/dns/acl.c
Hunk #1 succeeded at 183 (offset -66 lines).
Hunk #2 succeeded at 243 (offset -66 lines).
Hunk #3 succeeded at 260 (offset -66 lines).
Hunk #4 succeeded at 381 (offset -66 lines).
Hunk #5 FAILED at 458.
Hunk #6 FAILED at 471.
Hunk #7 FAILED at 537.
Hunk #8 FAILED at 555.
Hunk #9 FAILED at 573.
Hunk #10 FAILED at 592.
Hunk #11 FAILED at 799.
Hunk #12 FAILED at 809.
Hunk #13 FAILED at 846.
Hunk #14 FAILED at 858.
Hunk #15 FAILED at 872.
Hunk #16 FAILED at 884.
Hunk #17 FAILED at 898.
Hunk #18 FAILED at 910.
Hunk #19 FAILED at 924.
Hunk #20 FAILED at 936.
Hunk #21 FAILED at 950.
Hunk #22 FAILED at 962.
Hunk #23 FAILED at 976.
Hunk #24 succeeded at 424 (offset -574 lines).
19 out of 24 hunks FAILED -- saving rejects to file a/lib/dns/acl.c.rej
patching file a/lib/dns/include/dns/acl.h