mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 17:15:31 +00:00
Don't attempt to resolve DNS responses for intermediate results. This
may create multiple refreshes and can cause a crash.
One scenario is where for the query there is a CNAME and canonical
answer in cache that are both stale. This will trigger a refresh of
the RRsets because we encountered stale data and we prioritized it over
the lookup. It will trigger a refresh of both RRsets. When we start
recursing, it will detect a recursion loop because the recursion
parameters will eventually be the same. In 'dns_resolver_destroyfetch'
the sanity check fails, one of the callers did not get its event back
before trying to destroy the fetch.
Move the call to 'query_refresh_rrset' to 'ns_query_done', so that it
is only called once per client request.
Another scenario is where for the query there is a stale CNAME in the
cache that points to a record that is also in cache but not stale. This
will trigger a refresh of the RRset (because we encountered stale data
and we prioritized it over the lookup).
We mark RRsets that we add to the message with
DNS_RDATASETATTR_STALE_ADDED to prevent adding a duplicate RRset when
a stale lookup and a normal lookup conflict with each other. However,
the other non-stale RRset when following a CNAME chain will be added to
the message without setting that attribute, because it is not stale.
This is a variant of the bug in #2594. The fix covered the same crash
but for stale-answer-client-timeout > 0.
Fix this by clearing all RRsets from the message before refreshing.
This requires the refresh to happen after the query is send back to
the client.
(cherry picked from commit d939d2ecde
)
272 lines
8.0 KiB
C
272 lines
8.0 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
/*! \file */
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <isc/buffer.h>
|
|
#include <isc/netaddr.h>
|
|
#include <isc/task.h>
|
|
#include <isc/types.h>
|
|
|
|
#include <dns/rdataset.h>
|
|
#include <dns/resolver.h>
|
|
#include <dns/rpz.h>
|
|
#include <dns/types.h>
|
|
|
|
#include <ns/types.h>
|
|
|
|
/*% nameserver database version structure */
|
|
typedef struct ns_dbversion {
|
|
dns_db_t *db;
|
|
dns_dbversion_t *version;
|
|
bool acl_checked;
|
|
bool queryok;
|
|
ISC_LINK(struct ns_dbversion) link;
|
|
} ns_dbversion_t;
|
|
|
|
/*%
|
|
* nameserver recursion parameters, to uniquely identify a recursion
|
|
* query; this is used to detect a recursion loop
|
|
*/
|
|
typedef struct ns_query_recparam {
|
|
dns_rdatatype_t qtype;
|
|
dns_name_t *qname;
|
|
dns_fixedname_t fqname;
|
|
dns_name_t *qdomain;
|
|
dns_fixedname_t fqdomain;
|
|
} ns_query_recparam_t;
|
|
|
|
/*% nameserver query structure */
|
|
struct ns_query {
|
|
unsigned int attributes;
|
|
unsigned int restarts;
|
|
bool timerset;
|
|
dns_name_t *qname;
|
|
dns_name_t *origqname;
|
|
dns_rdatatype_t qtype;
|
|
unsigned int dboptions;
|
|
unsigned int fetchoptions;
|
|
dns_db_t *gluedb;
|
|
dns_db_t *authdb;
|
|
dns_zone_t *authzone;
|
|
bool authdbset;
|
|
bool isreferral;
|
|
isc_mutex_t fetchlock;
|
|
dns_fetch_t *fetch;
|
|
dns_fetch_t *prefetch;
|
|
ns_hookasync_t *hookactx;
|
|
dns_rpz_st_t *rpz_st;
|
|
isc_bufferlist_t namebufs;
|
|
ISC_LIST(ns_dbversion_t) activeversions;
|
|
ISC_LIST(ns_dbversion_t) freeversions;
|
|
dns_rdataset_t *dns64_aaaa;
|
|
dns_rdataset_t *dns64_sigaaaa;
|
|
bool *dns64_aaaaok;
|
|
unsigned int dns64_aaaaoklen;
|
|
unsigned int dns64_options;
|
|
unsigned int dns64_ttl;
|
|
|
|
struct {
|
|
dns_db_t *db;
|
|
dns_zone_t *zone;
|
|
dns_dbnode_t *node;
|
|
dns_rdatatype_t qtype;
|
|
dns_name_t *fname;
|
|
dns_fixedname_t fixed;
|
|
isc_result_t result;
|
|
dns_rdataset_t *rdataset;
|
|
dns_rdataset_t *sigrdataset;
|
|
bool authoritative;
|
|
bool is_zone;
|
|
} redirect;
|
|
|
|
ns_query_recparam_t recparam;
|
|
|
|
dns_keytag_t root_key_sentinel_keyid;
|
|
bool root_key_sentinel_is_ta;
|
|
bool root_key_sentinel_not_ta;
|
|
};
|
|
|
|
#define NS_QUERYATTR_RECURSIONOK 0x000001
|
|
#define NS_QUERYATTR_CACHEOK 0x000002
|
|
#define NS_QUERYATTR_PARTIALANSWER 0x000004
|
|
#define NS_QUERYATTR_NAMEBUFUSED 0x000008
|
|
#define NS_QUERYATTR_RECURSING 0x000010
|
|
#define NS_QUERYATTR_QUERYOKVALID 0x000040
|
|
#define NS_QUERYATTR_QUERYOK 0x000080
|
|
#define NS_QUERYATTR_WANTRECURSION 0x000100
|
|
#define NS_QUERYATTR_SECURE 0x000200
|
|
#define NS_QUERYATTR_NOAUTHORITY 0x000400
|
|
#define NS_QUERYATTR_NOADDITIONAL 0x000800
|
|
#define NS_QUERYATTR_CACHEACLOKVALID 0x001000
|
|
#define NS_QUERYATTR_CACHEACLOK 0x002000
|
|
#define NS_QUERYATTR_DNS64 0x004000
|
|
#define NS_QUERYATTR_DNS64EXCLUDE 0x008000
|
|
#define NS_QUERYATTR_RRL_CHECKED 0x010000
|
|
#define NS_QUERYATTR_REDIRECT 0x020000
|
|
#define NS_QUERYATTR_ANSWERED 0x040000
|
|
#define NS_QUERYATTR_STALEOK 0x080000
|
|
#define NS_QUERYATTR_STALEPENDING 0x100000
|
|
|
|
typedef struct query_ctx query_ctx_t;
|
|
|
|
/* query context structure */
|
|
struct query_ctx {
|
|
isc_buffer_t *dbuf; /* name buffer */
|
|
dns_name_t *fname; /* found name from DB lookup */
|
|
dns_name_t *tname; /* temporary name, used
|
|
* when processing ANY
|
|
* queries */
|
|
dns_rdataset_t *rdataset; /* found rdataset */
|
|
dns_rdataset_t *sigrdataset; /* found sigrdataset */
|
|
dns_rdataset_t *noqname; /* rdataset needing
|
|
* NOQNAME proof */
|
|
dns_rdatatype_t qtype;
|
|
dns_rdatatype_t type;
|
|
|
|
unsigned int options; /* DB lookup options */
|
|
|
|
bool redirected; /* nxdomain redirected? */
|
|
bool is_zone; /* is DB a zone DB? */
|
|
bool is_staticstub_zone;
|
|
bool resuming; /* resumed from recursion? */
|
|
bool dns64, dns64_exclude, rpz;
|
|
bool authoritative; /* authoritative query? */
|
|
bool want_restart; /* CNAME chain or other
|
|
* restart needed */
|
|
bool refresh_rrset; /* stale RRset refresh needed */
|
|
bool need_wildcardproof; /* wildcard proof needed */
|
|
bool nxrewrite; /* negative answer from RPZ */
|
|
bool findcoveringnsec; /* lookup covering NSEC */
|
|
bool answer_has_ns; /* NS is in answer */
|
|
dns_fixedname_t wildcardname; /* name needing wcard proof */
|
|
dns_fixedname_t dsname; /* name needing DS */
|
|
|
|
ns_client_t *client; /* client object */
|
|
bool detach_client; /* client needs detaching */
|
|
|
|
dns_fetchevent_t *event; /* recursion event */
|
|
|
|
dns_db_t *db; /* zone or cache database */
|
|
dns_dbversion_t *version; /* DB version */
|
|
dns_dbnode_t *node; /* DB node */
|
|
|
|
dns_db_t *zdb; /* zone DB values, saved */
|
|
dns_dbnode_t *znode; /* while searching cache */
|
|
dns_name_t *zfname; /* for a better answer */
|
|
dns_dbversion_t *zversion;
|
|
dns_rdataset_t *zrdataset;
|
|
dns_rdataset_t *zsigrdataset;
|
|
|
|
dns_rpz_st_t *rpz_st; /* RPZ state */
|
|
dns_zone_t *zone; /* zone to search */
|
|
|
|
dns_view_t *view; /* client view */
|
|
|
|
isc_result_t result; /* query result */
|
|
int line; /* line to report error */
|
|
};
|
|
|
|
typedef isc_result_t (*ns_query_starthookasync_t)(
|
|
query_ctx_t *qctx, isc_mem_t *mctx, void *arg, isc_task_t *task,
|
|
isc_taskaction_t action, void *evarg, ns_hookasync_t **ctxp);
|
|
|
|
/*
|
|
* The following functions are expected to be used only within query.c
|
|
* and query modules.
|
|
*/
|
|
|
|
isc_result_t
|
|
ns_query_done(query_ctx_t *qctx);
|
|
/*%<
|
|
* Finalize this phase of the query process:
|
|
*
|
|
* - Clean up.
|
|
* - If we have an answer ready (positive or negative), send it.
|
|
* - If we need to restart for a chaining query, call ns__query_start() again.
|
|
* - If we've started recursion, then just clean up; things will be
|
|
* restarted via fetch_callback()/query_resume().
|
|
*/
|
|
|
|
isc_result_t
|
|
ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
|
|
dns_name_t *qdomain, dns_rdataset_t *nameservers,
|
|
bool resuming);
|
|
/*%<
|
|
* Prepare client for recursion, then create a resolver fetch, with
|
|
* the event callback set to fetch_callback(). Afterward we terminate
|
|
* this phase of the query, and resume with a new query context when
|
|
* recursion completes.
|
|
*/
|
|
|
|
isc_result_t
|
|
ns_query_hookasync(query_ctx_t *qctx, ns_query_starthookasync_t runasync,
|
|
void *arg);
|
|
/*%<
|
|
* Prepare the client for an asynchronous hook action, then call the
|
|
* specified 'runasync' function to start an asynchronous process running
|
|
* in the background. This function works similarly to ns_query_recurse(),
|
|
* but is expected to be called from a query hook action to support
|
|
* asynchronous event handling in a hook. A typical use case would be for
|
|
* a plugin to initiate recursion, but it may also be used to carry out
|
|
* other time-consuming tasks without blocking the caller or the worker
|
|
* thread.
|
|
*
|
|
* The calling plugin action must pass 'qctx' as passed from the query
|
|
* module.
|
|
*
|
|
* Once a plugin action calls this function, the ownership of 'qctx' is
|
|
* essentially transferred to the query module. Regardless of the return
|
|
* value of this function, the hook must not use 'qctx' anymore.
|
|
*
|
|
* This function must not be called after ns_query_recurse() is called,
|
|
* until the fetch is completed, as it needs resources that
|
|
* ns_query_recurse() would also use.
|
|
*
|
|
* See hooks.h for details about how 'runasync' is supposed to work, and
|
|
* other aspects of hook-triggered asynchronous event handling.
|
|
*/
|
|
|
|
isc_result_t
|
|
ns_query_init(ns_client_t *client);
|
|
|
|
void
|
|
ns_query_free(ns_client_t *client);
|
|
|
|
void
|
|
ns_query_start(ns_client_t *client, isc_nmhandle_t *handle);
|
|
|
|
void
|
|
ns_query_cancel(ns_client_t *client);
|
|
|
|
/*
|
|
* The following functions are expected to be used only within query.c
|
|
* and query modules.
|
|
*/
|
|
|
|
isc_result_t
|
|
ns__query_sfcache(query_ctx_t *qctx);
|
|
/*%<
|
|
* (Must not be used outside this module and its associated unit tests.)
|
|
*/
|
|
|
|
isc_result_t
|
|
ns__query_start(query_ctx_t *qctx);
|
|
/*%<
|
|
* (Must not be used outside this module and its associated unit tests.)
|
|
*/
|