-
-
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 |
And I have currently no plan to update to 9.10 (from what I understand, the geoip patch shouldn't be needed anymore).
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
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
Sorry for the late answers (I don't get notified for gist comments?). It is for BIND 9.8.1-p1 with the following patch: https://code.google.com/p/bind-geoip/