/* * 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 /***** ***** Module Info *****/ /*! \file * \brief * This module defines two objects, ns_client_t and ns_clientmgr_t. * * An ns_client_t object handles incoming DNS requests from clients * on a given network interface. * * Each ns_client_t object can handle only one TCP connection or UDP * request at a time. Therefore, several ns_client_t objects are * typically created to serve each network interface, e.g., one * for handling TCP requests and a few (one per CPU) for handling * UDP requests. * * Incoming requests are classified as queries, zone transfer * requests, update requests, notify requests, etc, and handed off * to the appropriate request handler. When the request has been * fully handled (which can be much later), the ns_client_t must be * notified of this by calling one of the following functions * exactly once in the context of its task: * \code * ns_client_send() (sending a non-error response) * ns_client_sendraw() (sending a raw response) * ns_client_error() (sending an error response) * ns_client_drop() (sending no response, logging the reason) *\endcode * This will release any resources used by the request and * and allow the ns_client_t to listen for the next request. * * A ns_clientmgr_t manages a number of ns_client_t objects. * New ns_client_t objects are created by calling * ns_clientmgr_createclients(). They are destroyed by * destroying their manager. */ /*** *** Imports ***/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*** *** Types ***/ #define NS_CLIENT_TCP_BUFFER_SIZE 65535 #define NS_CLIENT_SEND_BUFFER_SIZE 4096 /*! * Client object states. Ordering is significant: higher-numbered * states are generally "more active", meaning that the client can * have more dynamically allocated data, outstanding events, etc. * In the list below, any such properties listed for state N * also apply to any state > N. */ typedef enum { NS_CLIENTSTATE_FREED = 0, /*%< * The client object no longer exists. */ NS_CLIENTSTATE_INACTIVE = 1, /*%< * The client object exists and has a task and timer. * Its "query" struct and sendbuf are initialized. * It has a message and OPT, both in the reset state. */ NS_CLIENTSTATE_READY = 2, /*%< * The client object is either a TCP or a UDP one, and * it is associated with a network interface. It is on the * client manager's list of active clients. * * If it is a TCP client object, it has a TCP listener socket * and an outstanding TCP listen request. * * If it is a UDP client object, it has a UDP listener socket * and an outstanding UDP receive request. */ NS_CLIENTSTATE_WORKING = 3, /*%< * The client object has received a request and is working * on it. It has a view, and it may have any of a non-reset OPT, * recursion quota, and an outstanding write request. */ NS_CLIENTSTATE_RECURSING = 4, /*%< * The client object is recursing. It will be on the * 'recursing' list. */ NS_CLIENTSTATE_MAX = 5 /*%< * Sentinel value used to indicate "no state". */ } ns_clientstate_t; typedef ISC_LIST(ns_client_t) client_list_t; /*% nameserver client manager structure */ struct ns_clientmgr { /* Unlocked. */ unsigned int magic; isc_mem_t *mctx; isc_mem_t *send_mctx; ns_server_t *sctx; isc_taskmgr_t *taskmgr; isc_timermgr_t *timermgr; isc_refcount_t references; int tid; /* Attached by clients, needed for e.g. recursion */ isc_task_t *task; dns_aclenv_t *aclenv; /* Lock covers the recursing list */ isc_mutex_t reclock; client_list_t recursing; /*%< Recursing clients */ }; /*% nameserver client structure */ struct ns_client { unsigned int magic; isc_mem_t *mctx; int tid; bool allocated; /* Do we need to free it? */ ns_server_t *sctx; ns_clientmgr_t *manager; ns_clientstate_t state; int nupdates; bool nodetach; bool shuttingdown; unsigned int attributes; isc_task_t *task; dns_view_t *view; dns_dispatch_t *dispatch; isc_nmhandle_t *handle; /* Permanent pointer to handle */ isc_nmhandle_t *sendhandle; /* Waiting for send callback */ isc_nmhandle_t *reqhandle; /* Waiting for request callback (query, update, notify) */ isc_nmhandle_t *fetchhandle; /* Waiting for recursive fetch */ isc_nmhandle_t *prefetchhandle; /* Waiting for prefetch / rpzfetch */ isc_nmhandle_t *updatehandle; /* Waiting for update callback */ unsigned char *tcpbuf; size_t tcpbuf_size; dns_message_t *message; unsigned char *sendbuf; dns_rdataset_t *opt; dns_ednsopt_t *ede; uint16_t udpsize; uint16_t extflags; int16_t ednsversion; /* -1 noedns */ uint16_t additionaldepth; void (*cleanup)(ns_client_t *); ns_query_t query; isc_time_t requesttime; isc_stdtime_t now; isc_time_t tnow; dns_name_t signername; /*%< [T]SIG key name */ dns_name_t *signer; /*%< NULL if not valid sig */ bool mortal; /*%< Die after handling request */ isc_quota_t *recursionquota; isc_sockaddr_t peeraddr; bool peeraddr_valid; isc_netaddr_t destaddr; isc_sockaddr_t destsockaddr; dns_ecs_t ecs; /*%< EDNS client subnet sent by client */ struct in6_pktinfo pktinfo; /*% * Information about recent FORMERR response(s), for * FORMERR loop avoidance. This is separate for each * client object rather than global only to avoid * the need for locking. */ struct { isc_sockaddr_t addr; isc_stdtime_t time; dns_messageid_t id; } formerrcache; /*% Callback function to send a response when unit testing */ void (*sendcb)(isc_buffer_t *buf); ISC_LINK(ns_client_t) rlink; unsigned char cookie[8]; uint32_t expire; unsigned char *keytag; uint16_t keytag_len; /*% * Used to override the DNS response code in ns_client_error(). * If set to -1, the rcode is determined from the result code, * but if set to any other value, the least significant 12 * bits will be used as the rcode in the response message. */ int32_t rcode_override; }; #define NS_CLIENT_MAGIC ISC_MAGIC('N', 'S', 'C', 'c') #define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC) #define NS_CLIENTATTR_TCP 0x00001 #define NS_CLIENTATTR_RA 0x00002 /*%< Client gets recursive service */ #define NS_CLIENTATTR_PKTINFO 0x00004 /*%< pktinfo is valid */ #define NS_CLIENTATTR_MULTICAST 0x00008 /*%< recv'd from multicast */ #define NS_CLIENTATTR_WANTDNSSEC 0x00010 /*%< include dnssec records */ #define NS_CLIENTATTR_WANTNSID 0x00020 /*%< include nameserver ID */ /* Obsolete: NS_CLIENTATTR_FILTER_AAAA 0x00040 */ /* Obsolete: NS_CLIENTATTR_FILTER_AAAA_RC 0x00080 */ #define NS_CLIENTATTR_WANTAD 0x00100 /*%< want AD in response if possible */ #define NS_CLIENTATTR_WANTCOOKIE 0x00200 /*%< return a COOKIE */ #define NS_CLIENTATTR_HAVECOOKIE 0x00400 /*%< has a valid COOKIE */ #define NS_CLIENTATTR_WANTEXPIRE 0x00800 /*%< return seconds to expire */ #define NS_CLIENTATTR_HAVEEXPIRE 0x01000 /*%< return seconds to expire */ #define NS_CLIENTATTR_WANTOPT 0x02000 /*%< add opt to reply */ #define NS_CLIENTATTR_HAVEECS 0x04000 /*%< received an ECS option */ #define NS_CLIENTATTR_WANTPAD 0x08000 /*%< pad reply */ #define NS_CLIENTATTR_USEKEEPALIVE 0x10000 /*%< use TCP keepalive */ #define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */ /* * Flag to use with the SERVFAIL cache to indicate * that a query had the CD bit set. */ #define NS_FAILCACHE_CD 0x01 extern atomic_uint_fast64_t ns_client_requests; /*** *** Functions ***/ /* * Note! These ns_client_ routines MUST be called ONLY from the client's * task in order to ensure synchronization. */ void ns_client_send(ns_client_t *client); /*%< * Finish processing the current client request and * send client->message as a response. * \brief * Note! These ns_client_ routines MUST be called ONLY from the client's * task in order to ensure synchronization. */ void ns_client_sendraw(ns_client_t *client, dns_message_t *msg); /*%< * Finish processing the current client request and * send msg as a response using client->message->id for the id. */ void ns_client_error(ns_client_t *client, isc_result_t result); /*%< * Finish processing the current client request and return * an error response to the client. The error response * will have an RCODE determined by 'result'. */ void ns_client_extendederror(ns_client_t *client, uint16_t code, const char *text); /*%< * Set extended error with INFO-CODE and EXTRA-TEXT . */ void ns_client_drop(ns_client_t *client, isc_result_t result); /*%< * Log the reason the current client request has failed; no response * will be sent. */ bool ns_client_shuttingdown(ns_client_t *client); /*%< * Return true iff the client is currently shutting down. */ isc_result_t ns_client_replace(ns_client_t *client); /*%< * Try to replace the current client with a new one, so that the * current one can go off and do some lengthy work without * leaving the dispatch/socket without service. */ void ns_client_settimeout(ns_client_t *client, unsigned int seconds); /*%< * Set a timer in the client to go off in the specified amount of time. */ isc_result_t ns_clientmgr_create(ns_server_t *sctx, isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr, dns_aclenv_t *aclenv, int tid, ns_clientmgr_t **managerp); /*%< * Create a client manager. */ void ns_clientmgr_shutdown(ns_clientmgr_t *manager); /*%< * Shutdown a client manager and all ns_client_t objects * managed by it */ void ns_clientmgr_detach(ns_clientmgr_t **managerp); /*%< * Detach from a client manager. */ isc_sockaddr_t * ns_client_getsockaddr(ns_client_t *client); /*%< * Get the socket address of the client whose request is * currently being processed. */ isc_sockaddr_t * ns_client_getdestaddr(ns_client_t *client); /*%< * Get the destination address (server) for the request that is * currently being processed. */ isc_result_t ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr, dns_acl_t *acl, bool default_allow); /*%< * Convenience function for client request ACL checking. * * Check the current client request against 'acl'. If 'acl' * is NULL, allow the request iff 'default_allow' is true. * If netaddr is NULL, check the ACL against client->peeraddr; * otherwise check it against netaddr. * * Notes: *\li This is appropriate for checking allow-update, * allow-query, allow-transfer, etc. It is not appropriate * for checking the blackhole list because we treat positive * matches as "allow" and negative matches as "deny"; in * the case of the blackhole list this would be backwards. * * Requires: *\li 'client' points to a valid client. *\li 'netaddr' points to a valid address, or is NULL. *\li 'acl' points to a valid ACL, or is NULL. * * Returns: *\li ISC_R_SUCCESS if the request should be allowed * \li DNS_R_REFUSED if the request should be denied *\li No other return values are possible. */ isc_result_t ns_client_checkacl(ns_client_t *client, isc_sockaddr_t *sockaddr, const char *opname, dns_acl_t *acl, bool default_allow, int log_level); /*%< * Like ns_client_checkaclsilent, except the outcome of the check is * logged at log level 'log_level' if denied, and at debug 3 if approved. * Log messages will refer to the request as an 'opname' request. * * Requires: *\li 'client' points to a valid client. *\li 'sockaddr' points to a valid address, or is NULL. *\li 'acl' points to a valid ACL, or is NULL. *\li 'opname' points to a null-terminated string. */ void ns_client_log(ns_client_t *client, isc_logcategory_t *category, isc_logmodule_t *module, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6); void ns_client_logv(ns_client_t *client, isc_logcategory_t *category, isc_logmodule_t *module, int level, const char *fmt, va_list ap) ISC_FORMAT_PRINTF(5, 0); void ns_client_aclmsg(const char *msg, const dns_name_t *name, dns_rdatatype_t type, dns_rdataclass_t rdclass, char *buf, size_t len); #define NS_CLIENT_ACLMSGSIZE(x) \ (DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + \ DNS_RDATACLASS_FORMATSIZE + sizeof(x) + sizeof("'/'")) void ns_client_recursing(ns_client_t *client); /*%< * Add client to end of th recursing list. */ void ns_client_killoldestquery(ns_client_t *client); /*%< * Kill the oldest recursive query (recursing list head). */ void ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager); /*%< * Dump the outstanding recursive queries to 'f'. */ void ns_client_qnamereplace(ns_client_t *client, dns_name_t *name); /*%< * Replace the qname. */ isc_result_t ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp); isc_result_t ns_client_addopt(ns_client_t *client, dns_message_t *message, dns_rdataset_t **opt); /*%< * Get a client object from the inactive queue, or create one, as needed. * (Not intended for use outside this module and associated tests.) */ void ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, void *arg); /*%< * Handle client requests. * (Not intended for use outside this module and associated tests.) */ isc_result_t ns__client_tcpconn(isc_nmhandle_t *handle, isc_result_t result, void *arg); /*%< * Called every time a TCP connection is establish. This is used for * updating TCP statistics. */ dns_rdataset_t * ns_client_newrdataset(ns_client_t *client); void ns_client_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp); /*%< * Get and release temporary rdatasets in the client message; * used in query.c and in plugins. */ isc_result_t ns_client_newnamebuf(ns_client_t *client); /*%< * Allocate a name buffer for the client message. */ dns_name_t * ns_client_newname(ns_client_t *client, isc_buffer_t *dbuf, isc_buffer_t *nbuf); /*%< * Get a temporary name for the client message. */ isc_buffer_t * ns_client_getnamebuf(ns_client_t *client); /*%< * Get a name buffer from the pool, or allocate a new one if needed. */ void ns_client_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf); /*%< * Adjust buffer 'dbuf' to reflect that 'name' is using space in it, * and set client attributes appropriately. */ void ns_client_releasename(ns_client_t *client, dns_name_t **namep); /*%< * Release 'name' back to the pool of temporary names for the client * message. If it is using a name buffer, relinquish its exclusive * rights on the buffer. */ isc_result_t ns_client_newdbversion(ns_client_t *client, unsigned int n); /*%< * Allocate 'n' new database versions for use by client queries. */ ns_dbversion_t * ns_client_getdbversion(ns_client_t *client); /*%< * Get a free database version for use by a client query, allocating * a new one if necessary. */ ns_dbversion_t * ns_client_findversion(ns_client_t *client, dns_db_t *db); /*%< * Find the correct database version to use with a client query. * If we have already done a query related to the database 'db', * make sure subsequent queries are from the same version; * otherwise, take a database version from the list of dbversions * allocated by ns_client_newdbversion(). */ isc_result_t ns__client_setup(ns_client_t *client, ns_clientmgr_t *manager, bool new); /*%< * Perform initial setup of an allocated client. */ void ns__client_reset_cb(void *client0); /*%< * Reset the client object so that it can be reused. */ void ns__client_put_cb(void *client0); /*%< * Free all resources allocated to this client object, so that * it can be freed. */