mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-01 15:05:23 +00:00
refactor filter-aaaa implementation
- the goal of this change is for AAAA filtering to be fully contained in the query logic, and implemented at discrete points that can be replaced with hook callouts later on. - the new code may be slightly less efficient than the old filter-aaaa implementation, but maximum efficiency was never a priority for AAAA filtering anyway. - we now use the rdataset RENDERED attribute to indicate that an AAAA rdataset should not be included when rendering the message. (this flag was originally meant to indicate that an rdataset has already been rendered and should not be repeated, but it can also be used to prevent rendering in the first place.) - the DNS_MESSAGERENDER_FILTER_AAAA, NS_CLIENTATTR_FILTER_AAAA, and DNS_RDATASETGLUE_FILTERAAAA flags are all now unnecessary and have been removed.
This commit is contained in:
694
lib/ns/query.c
694
lib/ns/query.c
@@ -191,7 +191,6 @@ client_trace(ns_client_t *client, int level, const char *message) {
|
||||
#define CCTRACE(l,m) ((void)m)
|
||||
#endif /* WANT_QUERYTRACE */
|
||||
|
||||
|
||||
#define DNS_GETDB_NOEXACT 0x01U
|
||||
#define DNS_GETDB_NOLOG 0x02U
|
||||
#define DNS_GETDB_PARTIAL 0x04U
|
||||
@@ -429,6 +428,64 @@ query_addauth(query_ctx_t *qctx);
|
||||
static isc_result_t
|
||||
query_done(query_ctx_t *qctx);
|
||||
|
||||
/*
|
||||
* XXX:
|
||||
* Functions implementing filter-aaaa. Later, these will be moved
|
||||
* out to a loadable module.
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa_check(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_filter_aaaa(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_filter_aaaa_any(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_filter_aaaa_additional(query_ctx_t *qctx);
|
||||
|
||||
/*
|
||||
* XXX:
|
||||
* This is a temporary hooks table, pre-populated with pointers to
|
||||
* the functions implementing filter-aaaa. Later, this will be
|
||||
* redesigned to be set up at initialization time when the
|
||||
* filter-aaaa module is loaded. To activate this hooks table
|
||||
* at runtime, call ns__query_inithooks().
|
||||
*/
|
||||
static bool
|
||||
filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp);
|
||||
|
||||
static bool
|
||||
filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp);
|
||||
|
||||
static bool
|
||||
filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp);
|
||||
|
||||
static bool
|
||||
filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp);
|
||||
|
||||
ns_hook_t filter_respbegin = {
|
||||
.callback = filter_respond_begin,
|
||||
.callback_data = NULL,
|
||||
.link = { (void *) -1, (void *) -1 },
|
||||
};
|
||||
ns_hook_t filter_respanyfound = {
|
||||
.callback = filter_respond_any_found,
|
||||
.callback_data = NULL,
|
||||
.link = { (void *) -1, (void *) -1 },
|
||||
};
|
||||
ns_hook_t filter_prepresp = {
|
||||
.callback = filter_prep_response_begin,
|
||||
.callback_data = NULL,
|
||||
.link = { (void *) -1, (void *) -1 },
|
||||
};
|
||||
ns_hook_t filter_donesend = {
|
||||
.callback = filter_query_done_send,
|
||||
.callback_data = NULL,
|
||||
.link = { (void *) -1, (void *) -1 },
|
||||
};
|
||||
|
||||
/*%
|
||||
* Increment query statistics counters.
|
||||
*/
|
||||
@@ -1876,8 +1933,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
|
||||
}
|
||||
|
||||
if (qtype == dns_rdatatype_a) {
|
||||
bool have_a = false;
|
||||
|
||||
/*
|
||||
* We now go looking for A and AAAA records, along with
|
||||
* their signatures.
|
||||
@@ -1923,8 +1978,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
|
||||
} else if (result == ISC_R_SUCCESS) {
|
||||
bool invalid = false;
|
||||
mname = NULL;
|
||||
|
||||
have_a = true;
|
||||
if (additionaltype ==
|
||||
dns_rdatasetadditional_fromcache &&
|
||||
(DNS_TRUST_PENDING(rdataset->trust) ||
|
||||
@@ -1999,17 +2052,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
|
||||
} else if (result == ISC_R_SUCCESS) {
|
||||
bool invalid = false;
|
||||
mname = NULL;
|
||||
/*
|
||||
* There's an A; check whether we're filtering AAAA
|
||||
*/
|
||||
if (have_a &&
|
||||
(qctx->filter_aaaa == dns_aaaa_break_dnssec ||
|
||||
(qctx->filter_aaaa == dns_aaaa_filter &&
|
||||
(!WANTDNSSEC(client) || sigrdataset == NULL ||
|
||||
!dns_rdataset_isassociated(sigrdataset)))))
|
||||
{
|
||||
goto addname;
|
||||
}
|
||||
|
||||
if (additionaltype ==
|
||||
dns_rdatasetadditional_fromcache &&
|
||||
@@ -2165,21 +2207,14 @@ query_additional(query_ctx_t *qctx, dns_rdataset_t *rdataset) {
|
||||
{
|
||||
isc_result_t result;
|
||||
ns_dbversion_t *dbversion;
|
||||
unsigned int options = 0;
|
||||
|
||||
dbversion = query_findversion(client, client->query.gluedb);
|
||||
if (dbversion == NULL) {
|
||||
goto regular;
|
||||
}
|
||||
|
||||
if (qctx->filter_aaaa == dns_aaaa_filter ||
|
||||
qctx->filter_aaaa == dns_aaaa_break_dnssec)
|
||||
{
|
||||
options |= DNS_RDATASETADDGLUE_FILTERAAAA;
|
||||
}
|
||||
|
||||
result = dns_rdataset_addglue(rdataset, dbversion->version,
|
||||
options, client->message);
|
||||
client->message);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
@@ -4518,7 +4553,6 @@ static dns_name_t rfc1918names[] = {
|
||||
DNS_NAME_INITABSOLUTE(inaddr168192, inaddr192_offsets)
|
||||
};
|
||||
|
||||
|
||||
static unsigned char prisoner_data[] = "\010prisoner\004iana\003org";
|
||||
static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org";
|
||||
|
||||
@@ -6946,21 +6980,10 @@ query_addnoqnameproof(query_ctx_t *qctx) {
|
||||
*/
|
||||
static isc_result_t
|
||||
query_respond_any(query_ctx_t *qctx) {
|
||||
dns_name_t *tname;
|
||||
int rdatasets_found = 0;
|
||||
bool found = false;
|
||||
dns_rdatasetiter_t *rdsiter = NULL;
|
||||
isc_result_t result;
|
||||
dns_rdatatype_t onetype = 0; /* type to use for minimal-any */
|
||||
bool have_aaaa, have_a, have_sig;
|
||||
|
||||
/*
|
||||
* If we are not authoritative, assume there is an A record
|
||||
* even in if it is not in our cache. This assumption could
|
||||
* be wrong but it is a good bet.
|
||||
*/
|
||||
have_aaaa = false;
|
||||
have_a = !qctx->authoritative;
|
||||
have_sig = false;
|
||||
|
||||
PROCESS_HOOK(NS_QUERY_RESPOND_ANY_BEGIN, qctx);
|
||||
|
||||
@@ -6985,21 +7008,11 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
* cleanup qctx->fname even though we're using it!
|
||||
*/
|
||||
query_keepname(qctx->client, qctx->fname, qctx->dbuf);
|
||||
tname = qctx->fname;
|
||||
qctx->tname = qctx->fname;
|
||||
|
||||
result = dns_rdatasetiter_first(rdsiter);
|
||||
while (result == ISC_R_SUCCESS) {
|
||||
dns_rdatasetiter_current(rdsiter, qctx->rdataset);
|
||||
/*
|
||||
* Notice the presence of A and AAAAs so
|
||||
* that AAAAs can be hidden from IPv4 clients.
|
||||
*/
|
||||
if (qctx->filter_aaaa != dns_aaaa_ok) {
|
||||
if (qctx->rdataset->type == dns_rdatatype_aaaa)
|
||||
have_aaaa = true;
|
||||
else if (qctx->rdataset->type == dns_rdatatype_a)
|
||||
have_a = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We found an NS RRset; no need to add one later.
|
||||
@@ -7047,9 +7060,6 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
qctx->rdataset->type == qctx->qtype) &&
|
||||
qctx->rdataset->type != 0)
|
||||
{
|
||||
if (dns_rdatatype_isdnssec(qctx->rdataset->type))
|
||||
have_sig = true;
|
||||
|
||||
if (NOQNAME(qctx->rdataset) && WANTDNSSEC(qctx->client))
|
||||
{
|
||||
qctx->noqname = qctx->rdataset;
|
||||
@@ -7067,7 +7077,7 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
dns_name_t *name;
|
||||
name = (qctx->fname != NULL)
|
||||
? qctx->fname
|
||||
: tname;
|
||||
: qctx->tname;
|
||||
query_prefetch(qctx->client, name,
|
||||
qctx->rdataset);
|
||||
}
|
||||
@@ -7078,29 +7088,32 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
*/
|
||||
if (qctx->rdataset->type == dns_rdatatype_sig ||
|
||||
qctx->rdataset->type == dns_rdatatype_rrsig)
|
||||
{
|
||||
onetype = qctx->rdataset->covers;
|
||||
else
|
||||
} else {
|
||||
onetype = qctx->rdataset->type;
|
||||
}
|
||||
|
||||
query_addrrset(qctx,
|
||||
(qctx->fname != NULL)
|
||||
? &qctx->fname
|
||||
: &tname,
|
||||
: &qctx->tname,
|
||||
&qctx->rdataset, NULL,
|
||||
NULL, DNS_SECTION_ANSWER);
|
||||
|
||||
query_addnoqnameproof(qctx);
|
||||
|
||||
rdatasets_found++;
|
||||
INSIST(tname != NULL);
|
||||
found = true;
|
||||
INSIST(qctx->tname != NULL);
|
||||
|
||||
/*
|
||||
* rdataset is non-NULL only in certain
|
||||
* pathological cases involving DNAMEs.
|
||||
*/
|
||||
if (qctx->rdataset != NULL)
|
||||
if (qctx->rdataset != NULL) {
|
||||
query_putrdataset(qctx->client,
|
||||
&qctx->rdataset);
|
||||
}
|
||||
|
||||
qctx->rdataset = query_newrdataset(qctx->client);
|
||||
if (qctx->rdataset == NULL)
|
||||
@@ -7115,36 +7128,40 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
result = dns_rdatasetiter_next(rdsiter);
|
||||
}
|
||||
|
||||
PROCESS_HOOK(NS_QUERY_RESPOND_ANY_POST_LOOKUP, qctx);
|
||||
dns_rdatasetiter_destroy(&rdsiter);
|
||||
|
||||
/*
|
||||
* Filter AAAAs if there is an A and there is no signature
|
||||
* or we are supposed to break DNSSEC.
|
||||
*/
|
||||
if (qctx->filter_aaaa == dns_aaaa_break_dnssec)
|
||||
qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
|
||||
else if (qctx->filter_aaaa != dns_aaaa_ok &&
|
||||
have_aaaa && have_a &&
|
||||
(!have_sig || !WANTDNSSEC(qctx->client)))
|
||||
qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
|
||||
if (result != ISC_R_NOMORE) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"query_respond_any: rdataset iterator failed");
|
||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||
}
|
||||
|
||||
if (qctx->fname != NULL)
|
||||
dns_message_puttempname(qctx->client->message, &qctx->fname);
|
||||
if (found) {
|
||||
PROCESS_HOOK(NS_QUERY_RESPOND_ANY_FOUND, qctx);
|
||||
|
||||
if (qctx->fname != NULL) {
|
||||
dns_message_puttempname(qctx->client->message,
|
||||
&qctx->fname);
|
||||
}
|
||||
} else {
|
||||
PROCESS_HOOK(NS_QUERY_RESPOND_ANY_NOT_FOUND, qctx);
|
||||
|
||||
if (qctx->fname != NULL) {
|
||||
dns_message_puttempname(qctx->client->message,
|
||||
&qctx->fname);
|
||||
}
|
||||
|
||||
if (rdatasets_found == 0) {
|
||||
/*
|
||||
* No matching rdatasets found in cache. If we were
|
||||
* searching for RRSIG/SIG, that's probably okay;
|
||||
* otherwise this is an error condition.
|
||||
*/
|
||||
if ((qctx->qtype == dns_rdatatype_rrsig ||
|
||||
qctx->qtype == dns_rdatatype_sig) &&
|
||||
result == ISC_R_NOMORE)
|
||||
if (qctx->qtype == dns_rdatatype_rrsig ||
|
||||
qctx->qtype == dns_rdatatype_sig)
|
||||
{
|
||||
isc_buffer_t b;
|
||||
if (!qctx->is_zone) {
|
||||
qctx->authoritative = false;
|
||||
dns_rdatasetiter_destroy(&rdsiter);
|
||||
qctx->client->attributes &= ~NS_CLIENTATTR_RA;
|
||||
query_addauth(qctx);
|
||||
return (query_done(qctx));
|
||||
@@ -7164,7 +7181,6 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
namebuf);
|
||||
}
|
||||
|
||||
dns_rdatasetiter_destroy(&rdsiter);
|
||||
qctx->fname = query_newname(qctx->client,
|
||||
qctx->dbuf, &b);
|
||||
return (query_sign_nodata(qctx));
|
||||
@@ -7176,14 +7192,7 @@ query_respond_any(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
|
||||
dns_rdatasetiter_destroy(&rdsiter);
|
||||
if (result != ISC_R_NOMORE) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"query_respond_any: dns_rdatasetiter_destroy failed");
|
||||
QUERY_ERROR(qctx, result);
|
||||
} else {
|
||||
query_addauth(qctx);
|
||||
}
|
||||
query_addauth(qctx);
|
||||
|
||||
return (query_done(qctx));
|
||||
}
|
||||
@@ -7242,101 +7251,6 @@ query_getexpire(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionally hide AAAAs from IPv4 clients if there is an A.
|
||||
*
|
||||
* We add the AAAAs now, but might refuse to render them later
|
||||
* after DNSSEC is figured out.
|
||||
*
|
||||
* This could be more efficient, but the whole idea is
|
||||
* so fundamentally wrong, unavoidably inaccurate, and
|
||||
* unneeded that it is best to keep it as short as possible.
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa(query_ctx_t *qctx) {
|
||||
isc_result_t result;
|
||||
|
||||
if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
|
||||
(qctx->filter_aaaa != dns_aaaa_filter ||
|
||||
(WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
|
||||
dns_rdataset_isassociated(qctx->sigrdataset))))
|
||||
{
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
if (qctx->qtype == dns_rdatatype_aaaa) {
|
||||
dns_rdataset_t *trdataset;
|
||||
trdataset = query_newrdataset(qctx->client);
|
||||
result = dns_db_findrdataset(qctx->db, qctx->node,
|
||||
qctx->version,
|
||||
dns_rdatatype_a, 0,
|
||||
qctx->client->now,
|
||||
trdataset, NULL);
|
||||
if (dns_rdataset_isassociated(trdataset)) {
|
||||
dns_rdataset_disassociate(trdataset);
|
||||
}
|
||||
query_putrdataset(qctx->client, &trdataset);
|
||||
|
||||
/*
|
||||
* We have an AAAA but the A is not in our cache.
|
||||
* Assume any result other than DNS_R_DELEGATION
|
||||
* or ISC_R_NOTFOUND means there is no A and
|
||||
* so AAAAs are ok.
|
||||
*
|
||||
* Assume there is no A if we can't recurse
|
||||
* for this client, although that could be
|
||||
* the wrong answer. What else can we do?
|
||||
* Besides, that we have the AAAA and are using
|
||||
* this mechanism suggests that we care more
|
||||
* about As than AAAAs and would have cached
|
||||
* the A if it existed.
|
||||
*/
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
qctx->client->attributes |=
|
||||
NS_CLIENTATTR_FILTER_AAAA;
|
||||
|
||||
} else if (qctx->authoritative ||
|
||||
!RECURSIONOK(qctx->client) ||
|
||||
(result != DNS_R_DELEGATION &&
|
||||
result != ISC_R_NOTFOUND))
|
||||
{
|
||||
qctx->client->attributes &=
|
||||
~NS_CLIENTATTR_FILTER_AAAA;
|
||||
} else {
|
||||
/*
|
||||
* This is an ugly kludge to recurse
|
||||
* for the A and discard the result.
|
||||
*
|
||||
* Continue to add the AAAA now.
|
||||
* We'll make a note to not render it
|
||||
* if the recursion for the A succeeds.
|
||||
*/
|
||||
INSIST(!REDIRECT(qctx->client));
|
||||
result = query_recurse(qctx->client,
|
||||
dns_rdatatype_a,
|
||||
qctx->client->query.qname,
|
||||
NULL, NULL, qctx->resuming);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
qctx->client->attributes |=
|
||||
NS_CLIENTATTR_FILTER_AAAA_RC;
|
||||
qctx->client->query.attributes |=
|
||||
NS_QUERYATTR_RECURSING;
|
||||
}
|
||||
}
|
||||
} else if (qctx->qtype == dns_rdatatype_a &&
|
||||
(qctx->client->attributes &
|
||||
NS_CLIENTATTR_FILTER_AAAA_RC) != 0)
|
||||
{
|
||||
qctx->client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA_RC;
|
||||
qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
|
||||
qctx_clean(qctx);
|
||||
|
||||
return (query_done(qctx));
|
||||
}
|
||||
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Build a repsonse for a "normal" query, for a type other than ANY,
|
||||
* for which we have an answer (either positive or negative).
|
||||
@@ -7379,12 +7293,6 @@ query_respond(query_ctx_t *qctx) {
|
||||
/*
|
||||
* Check to see if the AAAA RRset has non-excluded addresses
|
||||
* in it. If not look for a A RRset.
|
||||
*
|
||||
* Note: the order of dns64_aaaaok() and query_filter_aaaa() is
|
||||
* important. query_filter_aaaa() calls query_recurse() but
|
||||
* continues so that the AAAA records are added. If the
|
||||
* order is reversed client->query.fetch will be non-NULL
|
||||
* when query_lookup() is called leading to a assertion.
|
||||
*/
|
||||
INSIST(qctx->client->query.dns64_aaaaok == NULL);
|
||||
|
||||
@@ -7407,10 +7315,6 @@ query_respond(query_ctx_t *qctx) {
|
||||
return (query_lookup(qctx));
|
||||
}
|
||||
|
||||
result = query_filter_aaaa(qctx);
|
||||
if (result != ISC_R_COMPLETE)
|
||||
return (result);
|
||||
|
||||
if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL) {
|
||||
sigrdatasetp = &qctx->sigrdataset;
|
||||
}
|
||||
@@ -9268,12 +9172,6 @@ query_coveringnsec(query_ctx_t *qctx) {
|
||||
if (qctx->type == dns_rdatatype_any) { /* XXX not yet */
|
||||
goto cleanup;
|
||||
}
|
||||
if (qctx->filter_aaaa != dns_aaaa_ok &&
|
||||
(qctx->type == dns_rdatatype_a ||
|
||||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
|
||||
(qctx->type == dns_rdatatype_a ||
|
||||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
|
||||
@@ -9338,12 +9236,6 @@ query_coveringnsec(query_ctx_t *qctx) {
|
||||
if (qctx->type == dns_rdatatype_any) { /* XXX not yet */
|
||||
goto cleanup;
|
||||
}
|
||||
if (qctx->filter_aaaa != dns_aaaa_ok &&
|
||||
(qctx->type == dns_rdatatype_a ||
|
||||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
|
||||
(qctx->type == dns_rdatatype_a ||
|
||||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
|
||||
@@ -9865,8 +9757,8 @@ query_addcname(query_ctx_t *qctx, dns_trust_t trust, dns_ttl_t ttl) {
|
||||
|
||||
/*%
|
||||
* Prepare to respond: determine whether a wildcard proof is needed,
|
||||
* check whether to filter AAAA answers, then hand off to query_respond()
|
||||
* or (for type ANY queries) query_respond_any().
|
||||
* then hand off to query_respond() or (for type ANY queries)
|
||||
* query_respond_any().
|
||||
*/
|
||||
static isc_result_t
|
||||
query_prepresponse(query_ctx_t *qctx) {
|
||||
@@ -9881,33 +9773,6 @@ query_prepresponse(query_ctx_t *qctx) {
|
||||
qctx->need_wildcardproof = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The filter-aaaa-on-v4 option should suppress AAAAs for IPv4
|
||||
* clients if there is an A; filter-aaaa-on-v6 option does the same
|
||||
* for IPv6 clients.
|
||||
*/
|
||||
qctx->filter_aaaa = dns_aaaa_ok;
|
||||
if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
|
||||
qctx->client->view->v6_aaaa != dns_aaaa_ok)
|
||||
{
|
||||
isc_result_t result;
|
||||
result = ns_client_checkaclsilent(qctx->client, NULL,
|
||||
qctx->client->view->aaaa_acl,
|
||||
true);
|
||||
if (result == ISC_R_SUCCESS &&
|
||||
qctx->client->view->v4_aaaa != dns_aaaa_ok &&
|
||||
is_v4_client(qctx->client))
|
||||
{
|
||||
qctx->filter_aaaa = qctx->client->view->v4_aaaa;
|
||||
} else if (result == ISC_R_SUCCESS &&
|
||||
qctx->client->view->v6_aaaa != dns_aaaa_ok &&
|
||||
is_v6_client(qctx->client))
|
||||
{
|
||||
qctx->filter_aaaa = qctx->client->view->v6_aaaa;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (qctx->type == dns_rdatatype_any) {
|
||||
return (query_respond_any(qctx));
|
||||
} else {
|
||||
@@ -10783,6 +10648,7 @@ query_glueanswer(query_ctx_t *qctx) {
|
||||
static isc_result_t
|
||||
query_done(query_ctx_t *qctx) {
|
||||
const dns_namelist_t *secs = qctx->client->message->sections;
|
||||
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "query_done");
|
||||
|
||||
PROCESS_HOOK(NS_QUERY_DONE_BEGIN, qctx);
|
||||
@@ -11284,3 +11150,371 @@ ns_query_start(ns_client_t *client) {
|
||||
ns_client_attach(client, &qclient);
|
||||
(void)query_setup(qclient, qtype);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per-client flags set by this module
|
||||
*/
|
||||
#define FILTER_AAAA_RECURSING 0x0001 /* Recursing for A */
|
||||
#define FILTER_AAAA_FILTERED 0x0002 /* AAAA was removed from answer */
|
||||
|
||||
/*
|
||||
* The filter-aaaa-on-v4 option suppresses AAAAs for IPv4
|
||||
* clients if there is an A; filter-aaaa-on-v6 option does
|
||||
* the same for IPv6 clients.
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa_check(query_ctx_t *qctx) {
|
||||
qctx->filter_aaaa = dns_aaaa_ok;
|
||||
if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
|
||||
qctx->client->view->v6_aaaa != dns_aaaa_ok)
|
||||
{
|
||||
isc_result_t result;
|
||||
result = ns_client_checkaclsilent(qctx->client, NULL,
|
||||
qctx->client->view->aaaa_acl,
|
||||
true);
|
||||
if (result == ISC_R_SUCCESS &&
|
||||
qctx->client->view->v4_aaaa != dns_aaaa_ok &&
|
||||
is_v4_client(qctx->client))
|
||||
{
|
||||
qctx->filter_aaaa = qctx->client->view->v4_aaaa;
|
||||
} else if (result == ISC_R_SUCCESS &&
|
||||
qctx->client->view->v6_aaaa != dns_aaaa_ok &&
|
||||
is_v6_client(qctx->client))
|
||||
{
|
||||
qctx->filter_aaaa = qctx->client->view->v6_aaaa;
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionally hide AAAA rrsets if there is a matching A.
|
||||
* (This version is for processing answers to explicit AAAA
|
||||
* queries; ANY queries are handled in query_filter_aaaa_any().)
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa(query_ctx_t *qctx) {
|
||||
isc_result_t result;
|
||||
|
||||
if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
|
||||
(qctx->filter_aaaa != dns_aaaa_filter ||
|
||||
(WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
|
||||
dns_rdataset_isassociated(qctx->sigrdataset))))
|
||||
{
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
if (qctx->qtype == dns_rdatatype_aaaa) {
|
||||
dns_rdataset_t *trdataset;
|
||||
trdataset = query_newrdataset(qctx->client);
|
||||
result = dns_db_findrdataset(qctx->db, qctx->node,
|
||||
qctx->version,
|
||||
dns_rdatatype_a, 0,
|
||||
qctx->client->now,
|
||||
trdataset, NULL);
|
||||
if (dns_rdataset_isassociated(trdataset)) {
|
||||
dns_rdataset_disassociate(trdataset);
|
||||
}
|
||||
query_putrdataset(qctx->client, &trdataset);
|
||||
|
||||
/*
|
||||
* We found an AAAA. If we also found an A, then the AAAA
|
||||
* must not be rendered.
|
||||
*
|
||||
* If the A is not in our cache, then any result other than
|
||||
* DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A,
|
||||
* and so AAAAs are okay.
|
||||
*
|
||||
* We assume there is no A if we can't recurse for this
|
||||
* client. That might be the wrong answer, but what else
|
||||
* can we do? Besides, the fact that we have the AAAA and
|
||||
* are using this mechanism in the first place suggests
|
||||
* that we care more about As than AAAAs, and would have
|
||||
* cached an A if it existed.
|
||||
*/
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
qctx->rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
if (qctx->sigrdataset != NULL &&
|
||||
dns_rdataset_isassociated(qctx->sigrdataset))
|
||||
{
|
||||
qctx->sigrdataset->attributes |=
|
||||
DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
qctx->client->hookflags |= FILTER_AAAA_FILTERED;
|
||||
} else if (!qctx->authoritative &&
|
||||
RECURSIONOK(qctx->client) &&
|
||||
(result == DNS_R_DELEGATION ||
|
||||
result == ISC_R_NOTFOUND))
|
||||
{
|
||||
/*
|
||||
* This is an ugly kludge to recurse
|
||||
* for the A and discard the result.
|
||||
*
|
||||
* Continue to add the AAAA now.
|
||||
* We'll make a note to not render it
|
||||
* if the recursion for the A succeeds.
|
||||
*/
|
||||
INSIST(!REDIRECT(qctx->client));
|
||||
result = query_recurse(qctx->client,
|
||||
dns_rdatatype_a,
|
||||
qctx->client->query.qname,
|
||||
NULL, NULL, qctx->resuming);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
qctx->client->hookflags |=
|
||||
FILTER_AAAA_RECURSING;
|
||||
qctx->client->query.attributes |=
|
||||
NS_QUERYATTR_RECURSING;
|
||||
}
|
||||
}
|
||||
} else if (qctx->qtype == dns_rdatatype_a &&
|
||||
((qctx->client->hookflags & FILTER_AAAA_RECURSING) != 0))
|
||||
{
|
||||
|
||||
dns_rdataset_t *mrdataset = NULL;
|
||||
dns_rdataset_t *sigrdataset = NULL;
|
||||
|
||||
result = dns_message_findname(qctx->client->message,
|
||||
DNS_SECTION_ANSWER, qctx->fname,
|
||||
dns_rdatatype_aaaa, 0,
|
||||
NULL, &mrdataset);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
mrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
|
||||
result = dns_message_findname(qctx->client->message,
|
||||
DNS_SECTION_ANSWER, qctx->fname,
|
||||
dns_rdatatype_rrsig,
|
||||
dns_rdatatype_aaaa,
|
||||
NULL, &sigrdataset);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
|
||||
qctx->client->hookflags &= ~FILTER_AAAA_RECURSING;
|
||||
|
||||
return (query_done(qctx));
|
||||
}
|
||||
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionally hide AAAA rrsets if there is a matching A.
|
||||
* (This version is for processing answers to ANY queries;
|
||||
* explicit AAAA queries are handled in query_filter_aaaa().)
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa_any(query_ctx_t *qctx) {
|
||||
dns_name_t *name = NULL;
|
||||
dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
|
||||
dns_rdataset_t *a = NULL;
|
||||
bool have_a = true;
|
||||
|
||||
if (qctx->filter_aaaa == dns_aaaa_ok) {
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
dns_message_findname(qctx->client->message, DNS_SECTION_ANSWER,
|
||||
(qctx->fname != NULL)
|
||||
? qctx->fname
|
||||
: qctx->tname,
|
||||
dns_rdatatype_any, 0, &name, NULL);
|
||||
|
||||
/*
|
||||
* If we're not authoritative, just assume there's an
|
||||
* A even if it wasn't in the cache and therefore isn't
|
||||
* in the message. But if we're authoritative, then
|
||||
* if there was an A, it should be here.
|
||||
*/
|
||||
if (qctx->authoritative && name != NULL) {
|
||||
dns_message_findtype(name, dns_rdatatype_a, 0, &a);
|
||||
if (a == NULL) {
|
||||
have_a = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (name != NULL) {
|
||||
dns_message_findtype(name, dns_rdatatype_aaaa, 0, &aaaa);
|
||||
dns_message_findtype(name, dns_rdatatype_rrsig,
|
||||
dns_rdatatype_aaaa, &aaaa_sig);
|
||||
}
|
||||
|
||||
if (have_a && aaaa != NULL &&
|
||||
(aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
|
||||
qctx->filter_aaaa == dns_aaaa_break_dnssec))
|
||||
{
|
||||
aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
if (aaaa_sig != NULL) {
|
||||
aaaa_sig->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide AAAA rrsets in the additional section if there is a matching A,
|
||||
* and hide NS in the additional section if AAAA was filtered in the answer
|
||||
* section.
|
||||
*/
|
||||
static isc_result_t
|
||||
query_filter_aaaa_additional(query_ctx_t *qctx) {
|
||||
isc_result_t result;
|
||||
|
||||
if (qctx->filter_aaaa == dns_aaaa_ok) {
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
result = dns_message_firstname(qctx->client->message,
|
||||
DNS_SECTION_ADDITIONAL);
|
||||
while (result == ISC_R_SUCCESS) {
|
||||
dns_name_t *name = NULL;
|
||||
dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
|
||||
dns_rdataset_t *a = NULL;
|
||||
|
||||
dns_message_currentname(qctx->client->message,
|
||||
DNS_SECTION_ADDITIONAL,
|
||||
&name);
|
||||
|
||||
result = dns_message_nextname(qctx->client->message,
|
||||
DNS_SECTION_ADDITIONAL);
|
||||
|
||||
dns_message_findtype(name, dns_rdatatype_a, 0, &a);
|
||||
if (a == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dns_message_findtype(name, dns_rdatatype_aaaa, 0,
|
||||
&aaaa);
|
||||
if (aaaa == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dns_message_findtype(name, dns_rdatatype_rrsig,
|
||||
dns_rdatatype_aaaa, &aaaa_sig);
|
||||
|
||||
if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
|
||||
qctx->filter_aaaa == dns_aaaa_break_dnssec)
|
||||
{
|
||||
aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
if (aaaa_sig != NULL) {
|
||||
aaaa_sig->attributes |=
|
||||
DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((qctx->client->hookflags & FILTER_AAAA_FILTERED) != 0) {
|
||||
result = dns_message_firstname(qctx->client->message,
|
||||
DNS_SECTION_AUTHORITY);
|
||||
while (result == ISC_R_SUCCESS) {
|
||||
dns_name_t *name = NULL;
|
||||
dns_rdataset_t *ns = NULL, *ns_sig = NULL;
|
||||
|
||||
dns_message_currentname(qctx->client->message,
|
||||
DNS_SECTION_AUTHORITY,
|
||||
&name);
|
||||
|
||||
result = dns_message_findtype(name, dns_rdatatype_ns,
|
||||
0, &ns);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
ns->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
|
||||
result = dns_message_findtype(name, dns_rdatatype_rrsig,
|
||||
dns_rdatatype_ns,
|
||||
&ns_sig);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
ns_sig->attributes |= DNS_RDATASETATTR_RENDERED;
|
||||
}
|
||||
|
||||
result = dns_message_nextname(qctx->client->message,
|
||||
DNS_SECTION_AUTHORITY);
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
static bool
|
||||
filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(cbdata);
|
||||
|
||||
result = query_filter_aaaa((query_ctx_t *) hookdata);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
*resp = result;
|
||||
return (true);
|
||||
}
|
||||
|
||||
*resp = ISC_R_SUCCESS;
|
||||
return (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) {
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(cbdata);
|
||||
|
||||
result = query_filter_aaaa_any((query_ctx_t *) hookdata);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
*resp = result;
|
||||
return (true);
|
||||
}
|
||||
|
||||
*resp = ISC_R_SUCCESS;
|
||||
return (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(cbdata);
|
||||
|
||||
result = query_filter_aaaa_check((query_ctx_t *) hookdata);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
*resp = result;
|
||||
return (true);
|
||||
}
|
||||
|
||||
*resp = ISC_R_SUCCESS;
|
||||
return (false);
|
||||
}
|
||||
|
||||
static bool
|
||||
filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) {
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(cbdata);
|
||||
|
||||
result = query_filter_aaaa_additional((query_ctx_t *) hookdata);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
*resp = result;
|
||||
return (true);
|
||||
}
|
||||
|
||||
*resp = ISC_R_SUCCESS;
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
ns__query_inithooks() {
|
||||
/*
|
||||
* XXX: This function is temporary. Later, the hook table
|
||||
* will be set up when initializing hook modules after
|
||||
* configuring the server.
|
||||
*
|
||||
* For now, however, we just call this once when initializing named
|
||||
* and it will set up all the filter-aaaa hooks.
|
||||
*/
|
||||
|
||||
ns_hooktable_init(NULL);
|
||||
ns_hook_add(NULL, NS_QUERY_RESPOND_BEGIN, &filter_respbegin);
|
||||
ns_hook_add(NULL, NS_QUERY_RESPOND_ANY_FOUND, &filter_respanyfound);
|
||||
ns_hook_add(NULL, NS_QUERY_PREP_RESPONSE_BEGIN, &filter_prepresp);
|
||||
ns_hook_add(NULL, NS_QUERY_DONE_SEND, &filter_donesend);
|
||||
}
|
||||
|
Reference in New Issue
Block a user