mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 09:05:40 +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.
327 lines
9.8 KiB
C
327 lines
9.8 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;
|
|
|
|
/*%
|
|
* recursion type; various features can initiate recursion and this enum value
|
|
* allows common code paths to differentiate between them
|
|
*/
|
|
typedef enum {
|
|
RECTYPE_NORMAL,
|
|
RECTYPE_PREFETCH,
|
|
RECTYPE_RPZ,
|
|
RECTYPE_HOOK,
|
|
RECTYPE_COUNT,
|
|
} ns_query_rectype_t;
|
|
|
|
/*%
|
|
* Helper macros for accessing isc_nmhandle_t pointers for a specific recursion
|
|
* a given client is associated with.
|
|
*/
|
|
#define HANDLE_RECTYPE_NORMAL(client) \
|
|
((client)->query.recursions[RECTYPE_NORMAL].handle)
|
|
#define HANDLE_RECTYPE_PREFETCH(client) \
|
|
((client)->query.recursions[RECTYPE_PREFETCH].handle)
|
|
#define HANDLE_RECTYPE_RPZ(client) \
|
|
((client)->query.recursions[RECTYPE_RPZ].handle)
|
|
#define HANDLE_RECTYPE_HOOK(client) \
|
|
((client)->query.recursions[RECTYPE_HOOK].handle)
|
|
|
|
/*%
|
|
* Helper macros for accessing dns_fetch_t pointers for a specific recursion a
|
|
* given client is associated with.
|
|
*/
|
|
#define FETCH_RECTYPE_NORMAL(client) \
|
|
((client)->query.recursions[RECTYPE_NORMAL].fetch)
|
|
#define FETCH_RECTYPE_PREFETCH(client) \
|
|
((client)->query.recursions[RECTYPE_PREFETCH].fetch)
|
|
#define FETCH_RECTYPE_RPZ(client) \
|
|
((client)->query.recursions[RECTYPE_RPZ].fetch)
|
|
#define FETCH_RECTYPE_HOOK(client) \
|
|
((client)->query.recursions[RECTYPE_HOOK].fetch)
|
|
|
|
/*%
|
|
* Helper macros for accessing isc_quota_t pointers for a specific recursion a
|
|
* given client is associated with.
|
|
*/
|
|
#define QUOTA_RECTYPE_NORMAL(client) \
|
|
((client)->query.recursions[RECTYPE_NORMAL].quota)
|
|
#define QUOTA_RECTYPE_PREFETCH(client) \
|
|
((client)->query.recursions[RECTYPE_PREFETCH].quota)
|
|
#define QUOTA_RECTYPE_RPZ(client) \
|
|
((client)->query.recursions[RECTYPE_RPZ].quota)
|
|
#define QUOTA_RECTYPE_HOOK(client) \
|
|
((client)->query.recursions[RECTYPE_HOOK].quota)
|
|
|
|
/*%
|
|
* 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;
|
|
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;
|
|
|
|
struct {
|
|
isc_nmhandle_t *handle;
|
|
dns_fetch_t *fetch;
|
|
isc_quota_t *quota;
|
|
} recursions[RECTYPE_COUNT];
|
|
|
|
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.)
|
|
*/
|