-
-
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 |
Could you please tell us which version of bind is this patch for?
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/
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
+1 would be nice to have the patch for 9.10.1 as well. thanks!