diff --git a/CHANGES b/CHANGES index 12e3727b7b..f459cda0a0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ + 551. [func] Implemented the 'sortlist' option. + 550. [func] Support unknown rdata types and classes. 549. [bug] "make" did not immediately abort the build when a diff --git a/bin/named/query.c b/bin/named/query.c index b373e86287..af6981a64c 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.143 2000/11/09 19:55:16 mws Exp $ */ +/* $Id: query.c,v 1.144 2000/11/10 03:16:14 gson Exp $ */ #include @@ -2097,6 +2097,147 @@ do { \ want_restart = ISC_FALSE; \ } while (0) +/* + * Extract a network address from the RDATA of an A or AAAA + * record. + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOTIMPLEMENTED The rdata is not a known address type. + */ + +static isc_result_t +rdata_tonetaddr(dns_rdata_t *rdata, isc_netaddr_t *netaddr) { + struct in_addr ina; + struct in6_addr in6a; + + switch (rdata->type) { + case dns_rdatatype_a: + INSIST(rdata->length == 4); + memcpy(&ina.s_addr, rdata->data, 4); + isc_netaddr_fromin(netaddr, &ina); + return (ISC_R_SUCCESS); + case dns_rdatatype_aaaa: + INSIST(rdata->length == 16); + memcpy(in6a.s6_addr, rdata->data, 16); + isc_netaddr_fromin6(netaddr, &in6a); + return (ISC_R_SUCCESS); + default: + return (ISC_R_NOTIMPLEMENTED); + } +} + +/* + * Find the sort order of 'rdata' in the topology-like + * ACL forming the second element in a 2-element top-level + * sortlist statement. + */ +static int +sortlist_order_2element(dns_rdata_t *rdata, void *arg) { + dns_acl_t *sortacl = (dns_acl_t *) arg; + isc_netaddr_t netaddr; + int match; + + if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) + return (INT_MAX); + + (void)dns_acl_match(&netaddr, NULL, sortacl, + &ns_g_server->aclenv, + &match, NULL); + if (match > 0) + return (match); + else + return (INT_MAX - (-match)); +} + +/* + * Find the sort order of 'rdata' in the matching element + * of a 1-element top-level sortlist statement. + */ +static int +sortlist_order_1element(dns_rdata_t *rdata, void *arg) { + dns_aclelement_t *matchelt = (dns_aclelement_t *) arg; + isc_netaddr_t netaddr; + + if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) + return (INT_MAX); + + if (dns_aclelement_match(&netaddr, NULL, matchelt, + &ns_g_server->aclenv, + NULL)) { + return (0); + } else { + return (INT_MAX); + } +} + +/* + * Find the sortlist element that applies to 'client', + * store data identifying it in '*data', and set up + * the sortlist info in in client->message appropriately. + * + * '*data' must persist until the message has been rendered. + */ +static void +setup_sortlist(ns_client_t *client) { + isc_netaddr_t netaddr; + dns_acl_t *acl = client->view->sortlist; + unsigned int i; + + if (acl == NULL) + goto dont_sort; + + isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); + + for (i = 0; i < acl->length; i++) { + /* + * 'e' refers to the current 'top level statement' + * in the sortlist (see ARM). + */ + dns_aclelement_t *e = &acl->elements[i]; + dns_aclelement_t *matchelt = NULL; + dns_acl_t *inner; + + if (e->type != dns_aclelementtype_nestedacl) + goto dont_sort; + + inner = e->u.nestedacl; + + if (inner->length < 1 || inner->length > 2) + goto dont_sort; + + if (inner->elements[0].negative) + goto dont_sort; + + if (dns_aclelement_match(&netaddr, client->signer, + &inner->elements[0], + &ns_g_server->aclenv, + &matchelt)) { + if (inner->length == 2) { + dns_aclelement_t *elt1 = &inner->elements[1]; + if (elt1->type != dns_aclelementtype_nestedacl) + goto dont_sort; + dns_message_setsortorder(client->message, + sortlist_order_2element, + elt1->u.nestedacl); + return; + } else { + INSIST(matchelt != NULL); + dns_message_setsortorder(client->message, + sortlist_order_1element, + matchelt); + return; + } + } + + } + + /* No match; don't sort. */ + dont_sort: + dns_message_setsortorder(client->message, NULL, NULL); + return; +} + static void query_find(ns_client_t *client, dns_fetchevent_t *event) { dns_db_t *db, *zdb; @@ -3009,10 +3150,13 @@ query_find(ns_client_t *client, dns_fetchevent_t *event) { ns_client_detach(&client); } else if (!RECURSING(client)) { /* - * We are done. Make a final tweak to the AA bit if the - * auth-nxdomain config option says so, then send the - * response. + * We are done. Set up sortlist data for the message + * rendering code, make a final tweak to the AA bit if the + * auth-nxdomain config option says so, then render and + * send the response. */ + setup_sortlist(client); + if (client->message->rcode == dns_rcode_nxdomain && client->view->auth_nxdomain == ISC_TRUE) client->message->flags |= DNS_MESSAGEFLAG_AA; diff --git a/bin/tests/system/sortlist/clean.sh b/bin/tests/system/sortlist/clean.sh new file mode 100644 index 0000000000..5a05e6f781 --- /dev/null +++ b/bin/tests/system/sortlist/clean.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (C) 2000 Internet Software Consortium. +# +# Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS +# ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +# CONSORTIUM 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. + +# $Id: clean.sh,v 1.1 2000/11/10 03:16:16 gson Exp $ + +rm -f *.dig *.good + + + diff --git a/bin/tests/system/sortlist/ns1/.cvsignore b/bin/tests/system/sortlist/ns1/.cvsignore new file mode 100644 index 0000000000..6af68aad15 --- /dev/null +++ b/bin/tests/system/sortlist/ns1/.cvsignore @@ -0,0 +1 @@ +named.run diff --git a/bin/tests/system/sortlist/ns1/example.db b/bin/tests/system/sortlist/ns1/example.db new file mode 100644 index 0000000000..05fde2b861 --- /dev/null +++ b/bin/tests/system/sortlist/ns1/example.db @@ -0,0 +1,42 @@ +; Copyright (C) 2000 Internet Software Consortium. +; +; Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM +; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +; INTERNET SOFTWARE CONSORTIUM 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. + +; $Id: example.db,v 1.1 2000/11/10 03:16:16 gson Exp $ + +$TTL 300 ; 5 minutes +@ IN SOA ns2.example. hostmaster.example. ( + 2000042795 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +example. NS ns1.example. +ns2.example. A 10.53.0.1 + +; Let's see what the sortlist picks out of this... +a A 1.1.1.1 +a A 1.1.1.2 +a A 192.168.3.1 +a A 1.1.1.3 +a A 192.168.1.1 +a A 1.1.1.4 + +b A 10.53.0.1 +b A 10.53.0.2 +b A 10.53.0.3 +b A 10.53.0.4 +b A 10.53.0.5 + diff --git a/bin/tests/system/sortlist/ns1/named.conf b/bin/tests/system/sortlist/ns1/named.conf new file mode 100644 index 0000000000..995bb5cf04 --- /dev/null +++ b/bin/tests/system/sortlist/ns1/named.conf @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 Internet Software Consortium. + * + * Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +/* $Id: named.conf,v 1.1 2000/11/10 03:16:16 gson Exp $ */ + +options { + query-source address 10.53.0.1; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + + sortlist { + { 10.53.0.1; // IF 10.53.0.1 + { // THEN first fit on the + 192.168.3/24; // following nets + { 192.168.2/24; 192.168.1/24; }; }; }; + { { 10.53.0.2; 10.53.0.3; }; }; // Prefer self + }; +}; + +zone "." { + type master; + file "root.db"; +}; + +zone "example" { + type master; + file "example.db"; +}; diff --git a/bin/tests/system/sortlist/ns1/root.db b/bin/tests/system/sortlist/ns1/root.db new file mode 100644 index 0000000000..8b59632ab8 --- /dev/null +++ b/bin/tests/system/sortlist/ns1/root.db @@ -0,0 +1,30 @@ +; Copyright (C) 2000 Internet Software Consortium. +; +; Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM +; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +; INTERNET SOFTWARE CONSORTIUM 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. + +; $Id: root.db,v 1.1 2000/11/10 03:16:16 gson Exp $ + +$TTL 300 +. IN SOA gson.nominum.com. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +example. NS ns2.example. +ns2.example. A 10.53.0.2 diff --git a/bin/tests/system/sortlist/tests.sh b/bin/tests/system/sortlist/tests.sh new file mode 100644 index 0000000000..4361a763b8 --- /dev/null +++ b/bin/tests/system/sortlist/tests.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# +# Copyright (C) 2000 Internet Software Consortium. +# +# Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SOFTWARE CONSORTIUM 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. + +# $Id: tests.sh,v 1.1 2000/11/10 03:16:16 gson Exp $ + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +status=0 + +echo "I:test 2-element sortlist statement" +cat <test1.good +a.example. 300 IN A 192.168.3.1 +a.example. 300 IN A 192.168.1.1 +EOF +$DIG +tcp +noadd +nosea +nostat +noquest +noauth +nocomm +nocmd a.example. \ + @10.53.0.1 -b 10.53.0.1 -p 5300 | head -2 >test1.dig +# Note that this can't use digcomp.pl because here, the ordering of the +# result RRs is significant. +diff test1.dig test1.good || status=1 + +echo "I:test 1-element sortlist statement" +for n in 2 3 +do + cat <test2.good +b.example. 300 IN A 10.53.0.$n +EOF + $DIG +tcp +noadd +nosea +nostat +noquest +noauth +nocomm +nocmd \ + b.example. \ + @10.53.0.1 -b 10.53.0.$n -p 5300 | head -1 >test2.dig + diff test2.dig test2.good || status=1 +done + +echo "I:exit status: $status" +exit $status diff --git a/lib/dns/acl.c b/lib/dns/acl.c index c1b7afe6fa..0cff1ee17c 100644 --- a/lib/dns/acl.c +++ b/lib/dns/acl.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.c,v 1.14 2000/08/11 01:53:46 gson Exp $ */ +/* $Id: acl.c,v 1.15 2000/11/10 03:16:16 gson Exp $ */ #include @@ -130,84 +130,17 @@ dns_acl_match(isc_netaddr_t *reqaddr, int *match, dns_aclelement_t **matchelt) { - isc_result_t result; unsigned int i; - int indirectmatch; REQUIRE(reqaddr != NULL); REQUIRE(matchelt == NULL || *matchelt == NULL); - + for (i = 0; i < acl->length; i++) { dns_aclelement_t *e = &acl->elements[i]; - dns_acl_t *inner = NULL; - switch (e->type) { - case dns_aclelementtype_ipprefix: - if (isc_netaddr_eqprefix(reqaddr, - &e->u.ip_prefix.address, - e->u.ip_prefix.prefixlen)) - goto matched; - break; - - case dns_aclelementtype_keyname: - if (reqsigner != NULL && - dns_name_equal(reqsigner, &e->u.keyname)) - goto matched; - break; - - case dns_aclelementtype_nestedacl: - inner = e->u.nestedacl; - nested: - result = dns_acl_match(reqaddr, reqsigner, - inner, - env, - &indirectmatch, matchelt); - if (result != ISC_R_SUCCESS) - return (result); - /* - * Treat negative matches in indirect ACLs as - * "no match". - * That way, a negated indirect ACL will never become - * a surprise positive match through double negation. - * XXXDCL this should be documented. - */ - if (indirectmatch > 0) - goto matched; - - /* - * A negative indirect match may have set *matchelt, - * but we don't want it set when we return. - */ - if (matchelt != NULL) - *matchelt = NULL; - break; - - case dns_aclelementtype_any: - matched: + if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt)) { *match = e->negative ? -(i+1) : (i+1); - if (matchelt != NULL) - *matchelt = e; return (ISC_R_SUCCESS); - - case dns_aclelementtype_localhost: - if (env != NULL && env->localhost != NULL) { - inner = env->localhost; - goto nested; - } else { - break; - } - - case dns_aclelementtype_localnets: - if (env != NULL && env->localnets != NULL) { - inner = env->localnets; - goto nested; - } else { - break; - } - - default: - INSIST(0); - break; } } /* No match. */ @@ -215,6 +148,89 @@ dns_acl_match(isc_netaddr_t *reqaddr, return (ISC_R_SUCCESS); } +isc_boolean_t +dns_aclelement_match(isc_netaddr_t *reqaddr, + dns_name_t *reqsigner, + dns_aclelement_t *e, + dns_aclenv_t *env, + dns_aclelement_t **matchelt) +{ + dns_acl_t *inner = NULL; + int indirectmatch; + isc_result_t result; + + switch (e->type) { + case dns_aclelementtype_ipprefix: + if (isc_netaddr_eqprefix(reqaddr, + &e->u.ip_prefix.address, + e->u.ip_prefix.prefixlen)) + goto matched; + break; + + case dns_aclelementtype_keyname: + if (reqsigner != NULL && + dns_name_equal(reqsigner, &e->u.keyname)) + goto matched; + break; + + case dns_aclelementtype_nestedacl: + inner = e->u.nestedacl; + nested: + result = dns_acl_match(reqaddr, reqsigner, + inner, + env, + &indirectmatch, matchelt); + if (result != ISC_R_SUCCESS) + return (result); + /* + * Treat negative matches in indirect ACLs as + * "no match". + * That way, a negated indirect ACL will never become + * a surprise positive match through double negation. + * XXXDCL this should be documented. + */ + if (indirectmatch > 0) + goto matchelt_set; + + /* + * A negative indirect match may have set *matchelt, + * but we don't want it set when we return. + */ + if (matchelt != NULL) + *matchelt = NULL; + break; + + case dns_aclelementtype_any: + matched: + if (matchelt != NULL) + *matchelt = e; + matchelt_set: + return (ISC_TRUE); + + case dns_aclelementtype_localhost: + if (env != NULL && env->localhost != NULL) { + inner = env->localhost; + goto nested; + } else { + break; + } + + case dns_aclelementtype_localnets: + if (env != NULL && env->localnets != NULL) { + inner = env->localnets; + goto nested; + } else { + break; + } + + default: + INSIST(0); + break; + } + + return (ISC_FALSE); +} + void dns_acl_attach(dns_acl_t *source, dns_acl_t **target) { REQUIRE(DNS_ACL_VALID(source)); diff --git a/lib/dns/config/confctx.c b/lib/dns/config/confctx.c index 2e56dc748b..aacc0c6d16 100644 --- a/lib/dns/config/confctx.c +++ b/lib/dns/config/confctx.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: confctx.c,v 1.99 2000/11/08 03:53:13 marka Exp $ */ +/* $Id: confctx.c,v 1.100 2000/11/10 03:16:21 gson Exp $ */ #include @@ -515,13 +515,6 @@ dns_c_checkconfig(dns_c_ctx_t *cfg) "option 'topology' is deprecated"); } - if (dns_c_ctx_getsortlist(cfg, &ipml) != ISC_R_NOTFOUND) { - dns_c_ipmatchlist_detach(&ipml); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, - DNS_LOGMODULE_CONFIG, ISC_LOG_WARNING, - "option 'sortlist' is not yet implemented"); - } - if (dns_c_ctx_getrrsetorderlist(cfg, &olist) != ISC_R_NOTFOUND) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, ISC_LOG_WARNING, diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h index e6f506b23e..6650f0b367 100644 --- a/lib/dns/include/dns/acl.h +++ b/lib/dns/include/dns/acl.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: acl.h,v 1.14 2000/11/07 23:43:23 bwelling Exp $ */ +/* $Id: acl.h,v 1.15 2000/11/10 03:16:23 gson Exp $ */ #ifndef DNS_ACL_H #define DNS_ACL_H 1 @@ -167,6 +167,21 @@ dns_acl_match(isc_netaddr_t *reqaddr, * ISC_R_SUCCESS Always succeeds. */ +isc_boolean_t +dns_aclelement_match(isc_netaddr_t *reqaddr, + dns_name_t *reqsigner, + dns_aclelement_t *e, + dns_aclenv_t *env, + dns_aclelement_t **matchelt); +/* + * Like dns_acl_match, but matches against the single ACL element 'e' + * rather than a complete list and returns ISC_TRUE iff it matched. + * To determine whether the match was prositive or negative, the + * caller should examine e->negative. Since the element 'e' may be + * a reference to a named ACL or a nested ACL, the matching element + * returned through 'matchelt' is not necessarily 'e' itself. + */ + ISC_LANG_ENDDECLS #endif /* DNS_ACL_H */ diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index b2f037d7c4..48ac780887 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: message.h,v 1.80 2000/11/10 03:13:03 gson Exp $ */ +/* $Id: message.h,v 1.81 2000/11/10 03:16:24 gson Exp $ */ #ifndef DNS_MESSAGE_H #define DNS_MESSAGE_H 1 @@ -1188,6 +1188,20 @@ dns_message_getrawmessage(dns_message_t *msg); * a pointer to a region which refers the dns message. */ +void +dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order, + void *order_arg); +/* + * Define the order in which RR sets get rendered by + * dns_message_rendersection() to be the ascending order + * defined by the integer value returned by 'order' when + * given each RR and 'arg' as arguments. If 'order' and + * 'order_arg' are NULL, a default order is used. + * + * Requires: + * order_arg is NULL if and only if order is NULL. + */ + ISC_LANG_ENDDECLS #endif /* DNS_MESSAGE_H */ diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 81dfe83227..59c4d4b5c8 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: types.h,v 1.97 2000/11/03 07:16:07 marka Exp $ */ +/* $Id: types.h,v 1.98 2000/11/10 03:16:25 gson Exp $ */ #ifndef DNS_TYPES_H #define DNS_TYPES_H 1 @@ -280,4 +280,7 @@ typedef void typedef void (*dns_updatecallback_t)(void *, isc_result_t, dns_message_t *); +typedef int +(*dns_rdatasetorderfunc_t)(dns_rdata_t *rdata, void *arg); + #endif /* DNS_TYPES_H */ diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 20cf1078d7..affe2b780b 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.h,v 1.55 2000/11/03 18:35:31 gson Exp $ */ +/* $Id: view.h,v 1.56 2000/11/10 03:16:26 gson Exp $ */ #ifndef DNS_VIEW_H #define DNS_VIEW_H 1 @@ -106,6 +106,7 @@ struct dns_view { dns_transfer_format_t transfer_format; dns_acl_t * queryacl; dns_acl_t * recursionacl; + dns_acl_t * sortlist; isc_boolean_t requestixfr; isc_boolean_t provideixfr; dns_ttl_t maxcachettl; diff --git a/lib/dns/message.c b/lib/dns/message.c index 89c547a006..f442f4e164 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: message.c,v 1.156 2000/10/27 21:56:56 bwelling Exp $ */ +/* $Id: message.c,v 1.157 2000/11/10 03:16:18 gson Exp $ */ /*** *** Imports @@ -372,6 +372,8 @@ msginit(dns_message_t *m) { m->tcp_continuation = 0; m->verified_sig = 0; m->verify_attempted = 0; + m->order = NULL; + m->order_arg = NULL; } static inline void @@ -468,7 +470,7 @@ msgresetsigs(dns_message_t *msg, isc_boolean_t replying) { /* * Free all but one (or everything) for this message. This is used by - * both dns_message_reset() and dns_message_parse(). + * both dns_message_reset() and dns_message_destroy(). */ static void msgreset(dns_message_t *msg, isc_boolean_t everything) { @@ -1744,10 +1746,13 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid, st = *(msg->buffer); count = 0; - result = dns_rdataset_towire(rdataset, name, - &msg->cctx, - msg->buffer, - &count); + result = dns_rdataset_towiresorted(rdataset, + name, + &msg->cctx, + msg->buffer, + msg->order, + msg->order_arg, + &count); total += count; @@ -2948,3 +2953,12 @@ dns_message_getrawmessage(dns_message_t *msg) { REQUIRE(DNS_MESSAGE_VALID(msg)); return (msg->saved); } + +void +dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order, + void *order_arg) +{ + msg->order = order; + msg->order_arg = order_arg; +} + diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 66c8bc25d9..623cd1c4a2 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataset.c,v 1.51 2000/10/25 04:26:47 marka Exp $ */ +/* $Id: rdataset.c,v 1.52 2000/11/10 03:16:19 gson Exp $ */ #include @@ -256,25 +256,38 @@ dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { #define MAX_SHUFFLE 32 #define WANT_FIXED(r) (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0) -#define WANT_RANDOM(r) (((r)->attributes & DNS_RDATASETATTR_RANDOMIZE) != 0) +struct towire_sort { + int key; + dns_rdata_t *rdata; +}; + +static int +towire_compare(const void *av, const void *bv) { + const struct towire_sort *a = (const struct towire_sort *) av; + const struct towire_sort *b = (const struct towire_sort *) bv; + return (a->key - b->key); +} isc_result_t -dns_rdataset_towire(dns_rdataset_t *rdataset, - dns_name_t *owner_name, - dns_compress_t *cctx, - isc_buffer_t *target, - unsigned int *countp) +dns_rdataset_towiresorted(dns_rdataset_t *rdataset, + dns_name_t *owner_name, + dns_compress_t *cctx, + isc_buffer_t *target, + dns_rdatasetorderfunc_t order, + void *order_arg, + unsigned int *countp) { dns_rdata_t rdata; isc_region_t r; isc_result_t result; - unsigned int i, count, tcount, choice; + unsigned int i, count; isc_buffer_t savedbuffer, rdlen; unsigned int headlen; isc_boolean_t question = ISC_FALSE; isc_boolean_t shuffle = ISC_FALSE; dns_rdata_t shuffled[MAX_SHUFFLE]; + struct towire_sort sorted[MAX_SHUFFLE]; /* * Convert 'rdataset' to wire format, compressing names as specified @@ -283,6 +296,7 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, REQUIRE(DNS_RDATASET_VALID(rdataset)); REQUIRE(countp != NULL); + REQUIRE((order == NULL) == (order_arg == NULL)); count = 0; if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) { @@ -304,60 +318,64 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, return (result); } - choice = 0; - if (!question && count > 1 && !WANT_FIXED(rdataset)) { + /* + * We'll only shuffle if we've got enough slots in our + * deck. + * + * There's no point to shuffling SIGs. + */ + if (!question && + count > 1 && + !WANT_FIXED(rdataset) && + count <= MAX_SHUFFLE && + rdataset->type != dns_rdatatype_sig) + { + shuffle = ISC_TRUE; /* - * We'll only shuffle if we've got enough slots in our - * deck. - * - * There's no point to shuffling SIGs. + * First we get handles to all of the rdata. */ - if (count <= MAX_SHUFFLE && - rdataset->type != dns_rdatatype_sig) { - shuffle = ISC_TRUE; + i = 0; + do { + INSIST(i < count); + dns_rdata_init(&shuffled[i]); + dns_rdataset_current(rdataset, &shuffled[i]); + i++; + result = dns_rdataset_next(rdataset); + } while (result == ISC_R_SUCCESS); + if (result != ISC_R_NOMORE) + return (result); + INSIST(i == count); + /* + * Now we shuffle. + */ + if (order != NULL) { /* - * First we get handles to all of the rdata. + * Sorted order. */ - i = 0; - do { - INSIST(i < count); - dns_rdata_init(&shuffled[i]); - dns_rdataset_current(rdataset, &shuffled[i]); - i++; - result = dns_rdataset_next(rdataset); - } while (result == ISC_R_SUCCESS); - if (result != ISC_R_NOMORE) - return (result); - INSIST(i == count); + for (i = 0; i < count; i++) { + sorted[i].key = (*order)(&shuffled[i], + order_arg); + sorted[i].rdata = &shuffled[i]; + } + qsort(sorted, count, sizeof(sorted[0]), + towire_compare); + } else { /* - * Now we shuffle. + * "Cyclic" order. */ - if (WANT_RANDOM(rdataset)) { - /* - * "Random" order. - */ - tcount = count; - for (i = 0; i < count; i++) { - choice = (((unsigned int)rand()) >> 3) - % tcount; - rdata = shuffled[i]; - shuffled[i] = shuffled[i + choice]; - shuffled[i + choice] = rdata; - tcount--; - } - choice = 0; - } else { - /* - * "Cyclic" order. - */ - choice = (((unsigned int)rand()) >> 3) % count; + unsigned int j = (((unsigned int)rand()) >> 3) % count; + for (i = 0; i < count; i++) { + sorted[j].key = 0; /* Unused */ + sorted[j].rdata = &shuffled[i]; + j++; + if (j == count) + j = 0; /* Wrap around. */ } } } savedbuffer = *target; - i = choice; - tcount = 0; + i = 0; do { /* @@ -391,7 +409,7 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, * Copy out the rdata */ if (shuffle) - rdata = shuffled[i]; + rdata = *(sorted[i].rdata); else { dns_rdata_init(&rdata); dns_rdataset_current(rdataset, &rdata); @@ -408,18 +426,13 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, if (shuffle) { i++; - /* - * Wrap around in case we're doing cyclic ordering. - */ if (i == count) - i = 0; - tcount++; - if (tcount == count) result = ISC_R_NOMORE; else result = ISC_R_SUCCESS; - } else + } else { result = dns_rdataset_next(rdataset); + } } while (result == ISC_R_SUCCESS); if (result != ISC_R_NOMORE) @@ -438,6 +451,17 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, return (result); } +isc_result_t +dns_rdataset_towire(dns_rdataset_t *rdataset, + dns_name_t *owner_name, + dns_compress_t *cctx, + isc_buffer_t *target, + unsigned int *countp) +{ + return (dns_rdataset_towiresorted(rdataset, owner_name, cctx, target, + NULL, NULL, countp)); +} + isc_result_t dns_rdataset_additionaldata(dns_rdataset_t *rdataset, dns_additionaldatafunc_t add, void *arg) diff --git a/lib/dns/view.c b/lib/dns/view.c index 8c5f164ee4..bf673bd7a6 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.c,v 1.84 2000/11/03 18:27:31 gson Exp $ */ +/* $Id: view.c,v 1.85 2000/11/10 03:16:20 gson Exp $ */ #include @@ -156,6 +156,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->transfer_format = dns_one_answer; view->queryacl = NULL; view->recursionacl = NULL; + view->sortlist = NULL; view->requestixfr = ISC_TRUE; view->provideixfr = ISC_TRUE; view->maxcachettl = 7 * 24 * 3600; @@ -248,6 +249,8 @@ destroy(dns_view_t *view) { dns_acl_detach(&view->queryacl); if (view->recursionacl != NULL) dns_acl_detach(&view->recursionacl); + if (view->sortlist != NULL) + dns_acl_detach(&view->sortlist); dns_keytable_detach(&view->trustedkeys); dns_keytable_detach(&view->secroots); dns_fwdtable_destroy(&view->fwdtable);