diff --git a/CHANGES b/CHANGES
index aad5f8be88..c596182538 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,13 @@
+3731. [func] Added a "no-case-compress" ACL, which causes
+ named to use case-insensitive compression
+ (disabling change #3645) for specified
+ clients. (This is useful when dealing
+ with broken client implementations that
+ use case-sensitive name comparisons,
+ rejecting responses that fail to match the
+ capitalization of the query that was sent.)
+ [RT #35300]
+
3730. [cleanup] Added "never" as a synonym for "none" when
configuring key event dates in the dnssec tools.
[RT #35277]
diff --git a/bin/named/client.c b/bin/named/client.c
index 77d8943574..61082e76c8 100644
--- a/bin/named/client.c
+++ b/bin/named/client.c
@@ -233,6 +233,8 @@ static void client_request(isc_task_t *task, isc_event_t *event);
static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
dns_dispatch_t *disp, isc_boolean_t tcp);
+static inline isc_boolean_t
+allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
void
ns_client_recursing(ns_client_t *client) {
@@ -994,7 +996,19 @@ client_send(ns_client_t *client) {
result = dns_compress_init(&cctx, -1, client->mctx);
if (result != ISC_R_SUCCESS)
goto done;
- dns_compress_setsensitive(&cctx, ISC_TRUE);
+ if (client->peeraddr_valid && client->view != NULL) {
+ isc_netaddr_t netaddr;
+ dns_name_t *name = NULL;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (client->message->tsigkey != NULL)
+ name = &client->message->tsigkey->name;
+ if (client->view->nocasecompress == NULL ||
+ !allowed(&netaddr, name, client->view->nocasecompress))
+ {
+ dns_compress_setsensitive(&cctx, ISC_TRUE);
+ }
+ }
cleanup_cctx = ISC_TRUE;
result = dns_message_renderbegin(client->message, &cctx, &buffer);
diff --git a/bin/named/server.c b/bin/named/server.c
index 15b822e016..f86aa6733b 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -3306,6 +3306,14 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
CHECK(dns_acl_none(mctx, &view->cacheacl));
}
+ /*
+ * Ignore case when compressing responses to the specified
+ * clients. This causes case not always to be presrerved,
+ * and is needed by some broken clients.
+ */
+ CHECK(configure_view_acl(vconfig, config, "no-case-compress", NULL,
+ actx, ns_g_mctx, &view->nocasecompress));
+
/*
* Filter setting on addresses in the answer section.
*/
diff --git a/bin/tests/system/case/ns2/named.conf b/bin/tests/system/case/ns2/named.conf
new file mode 100644
index 0000000000..7d98911162
--- /dev/null
+++ b/bin/tests/system/case/ns2/named.conf
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.2;
+ notify-source 10.53.0.2;
+ transfer-source 10.53.0.2;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.2; };
+ listen-on-v6 { none; };
+ recursion no;
+ notify yes;
+ ixfr-from-differences yes;
+ check-integrity no;
+ no-case-compress { 10.53.0.2; };
+};
+
+zone "example" {
+ type slave;
+ file "example.bk";
+ masters { 10.53.0.1; };
+};
diff --git a/bin/tests/system/case/tests.sh b/bin/tests/system/case/tests.sh
index d9c159bffc..2c235c3c88 100644
--- a/bin/tests/system/case/tests.sh
+++ b/bin/tests/system/case/tests.sh
@@ -23,11 +23,28 @@ status=0
n=0
n=`expr $n + 1`
-echo "I:testing case sensitive responses ($n)"
+echo "I:testing case preserving responses - no acl ($n)"
ret=0
-$DIG $DIGOPTS mx example. @10.53.0.1 -p 5300 > dig.n1.test$n
-grep "0.mail.eXaMpLe" dig.n1.test$n > /dev/null || ret=1
-grep "mAiL.example" dig.n1.test$n > /dev/null || ret=1
+$DIG $DIGOPTS mx example. @10.53.0.1 -p 5300 > dig.ns1.test$n
+grep "0.mail.eXaMpLe" dig.ns1.test$n > /dev/null || ret=1
+grep "mAiL.example" dig.ns1.test$n > /dev/null || ret=1
+test $ret -eq 0 || echo "I:failed"
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:testing no-case-compress acl '{ 10.53.0.2; }' ($n)"
+ret=0
+
+# check that we preserve zone case for non-matching query (10.53.0.1)
+$DIG $DIGOPTS mx example. -b 10.53.0.1 @10.53.0.1 -p 5300 > dig.ns1.test$n
+grep "0.mail.eXaMpLe" dig.ns1.test$n > /dev/null || ret=1
+grep "mAiL.example" dig.ns1.test$n > /dev/null || ret=1
+
+# check that we don't preserve zone case for match (10.53.0.2)
+$DIG $DIGOPTS mx example. -b 10.53.0.2 @10.53.0.2 -p 5300 > dig.ns2.test$n
+grep "0.mail.example" dig.ns2.test$n > /dev/null || ret=1
+grep "mail.example" dig.ns2.test$n > /dev/null || ret=1
+
test $ret -eq 0 || echo "I:failed"
status=`expr $status + $ret`
diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
index 16490079ae..8cc2689229 100644
--- a/doc/arm/Bv9ARM-book.xml
+++ b/doc/arm/Bv9ARM-book.xml
@@ -4813,6 +4813,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
try-tcp-refresh yes_or_no;
allow-v6-synthesis { address_match_list };
blackhole { address_match_list };
+ no-case-compress { address_match_list };
use-v4-udp-ports { port_list };
avoid-v4-udp-ports { port_list };
use-v6-udp-ports { port_list };
@@ -7181,6 +7182,59 @@ options {
+
+ no-case-compress
+
+ Specifies a list of addresses which require responses
+ to use case-insensitive compression. This ACL can be
+ used when named needs to work with
+ clients that do not comply with the requirement in RFC
+ 1034 to use case-insensitive name comparisons when
+ checking for matching domain names.
+
+
+ If left undefined, the ACL defaults to
+ none: case-insensitive compression
+ will be used for all clients. If the ACL is defined and
+ matches a client, then case will be ignored when
+ compressing domain names in DNS responses sent to that
+ client.
+
+
+ This can result in slightly smaller responses: if
+ a response contains the names "example.com" and
+ "example.COM", case-insensitive compression would treat
+ the second one as a duplicate. It also ensures
+ that the case of the query name exactly matches the
+ case of the owner names of returned records, rather
+ than matching the case of the records entered in
+ the zone file. This allows responses to exactly
+ match the query, which is required by some clients
+ due to incorrect use of case-sensitive comparisions.
+
+
+ Case-insensitive compression is always
+ used in AXFR and IXFR responses, regardless of whether
+ the client matches this ACL.
+
+
+ There are circusmstances in which named
+ will not preserve the case of owner names of records:
+ if a zone file defines records of different types with
+ the same name, but the capitalization of the name is
+ different (e.g., "www.example.com/A" and
+ "WWW.EXAMPLE.COM/AAAA"), then all resposnes for that
+ name will use the first version
+ of the name that was used in the zone file. This
+ limitation may be addressed in a future release. However,
+ domain names specified in the rdata of resource records
+ (i.e., records of type NS, MX, CNAME, etc) will always
+ have their case preserved unless the client matches this
+ ACL.
+
+
+
+
resolver-query-timeout
diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
index cff940b734..676eef336d 100644
--- a/lib/dns/include/dns/view.h
+++ b/lib/dns/include/dns/view.h
@@ -141,6 +141,7 @@ struct dns_view {
dns_acl_t * updateacl;
dns_acl_t * upfwdacl;
dns_acl_t * denyansweracl;
+ dns_acl_t * nocasecompress;
dns_rbt_t * answeracl_exclude;
dns_rbt_t * denyanswernames;
dns_rbt_t * answernames_exclude;
diff --git a/lib/dns/view.c b/lib/dns/view.c
index 35969134b1..e5af1be76d 100644
--- a/lib/dns/view.c
+++ b/lib/dns/view.c
@@ -182,6 +182,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->updateacl = NULL;
view->upfwdacl = NULL;
view->denyansweracl = NULL;
+ view->nocasecompress = NULL;
view->answeracl_exclude = NULL;
view->denyanswernames = NULL;
view->answernames_exclude = NULL;
@@ -357,6 +358,8 @@ destroy(dns_view_t *view) {
dns_db_detach(&view->cachedb);
if (view->cache != NULL)
dns_cache_detach(&view->cache);
+ if (view->nocasecompress != NULL)
+ dns_acl_detach(&view->nocasecompress);
if (view->matchclients != NULL)
dns_acl_detach(&view->matchclients);
if (view->matchdestinations != NULL)
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index dc979c6935..3a95962bb0 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -1522,6 +1522,7 @@ view_clauses[] = {
{ "minimal-responses", &cfg_type_boolean, 0 },
{ "prefetch", &cfg_type_prefetch, 0 },
{ "preferred-glue", &cfg_type_astring, 0 },
+ { "no-case-compress", &cfg_type_bracketed_aml, 0 },
{ "provide-ixfr", &cfg_type_boolean, 0 },
/*
* Note that the query-source option syntax is different