xref: /minix3/external/bsd/bind/dist/bin/named/client.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: client.c,v 1.13 2015/07/08 17:28:55 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  * Copyright (C) 1999-2003  Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek  *
7*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek  *
11*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek  */
19*00b67f09SDavid van Moolenbroek 
20*00b67f09SDavid van Moolenbroek /* Id: client.c,v 1.286 2012/01/31 23:47:30 tbox Exp  */
21*00b67f09SDavid van Moolenbroek 
22*00b67f09SDavid van Moolenbroek #include <config.h>
23*00b67f09SDavid van Moolenbroek 
24*00b67f09SDavid van Moolenbroek #include <isc/formatcheck.h>
25*00b67f09SDavid van Moolenbroek #include <isc/mutex.h>
26*00b67f09SDavid van Moolenbroek #include <isc/once.h>
27*00b67f09SDavid van Moolenbroek #include <isc/platform.h>
28*00b67f09SDavid van Moolenbroek #include <isc/print.h>
29*00b67f09SDavid van Moolenbroek #include <isc/queue.h>
30*00b67f09SDavid van Moolenbroek #include <isc/random.h>
31*00b67f09SDavid van Moolenbroek #include <isc/serial.h>
32*00b67f09SDavid van Moolenbroek #include <isc/stats.h>
33*00b67f09SDavid van Moolenbroek #include <isc/stdio.h>
34*00b67f09SDavid van Moolenbroek #include <isc/string.h>
35*00b67f09SDavid van Moolenbroek #include <isc/task.h>
36*00b67f09SDavid van Moolenbroek #include <isc/timer.h>
37*00b67f09SDavid van Moolenbroek #include <isc/util.h>
38*00b67f09SDavid van Moolenbroek 
39*00b67f09SDavid van Moolenbroek #ifdef AES_SIT
40*00b67f09SDavid van Moolenbroek #include <isc/aes.h>
41*00b67f09SDavid van Moolenbroek #else
42*00b67f09SDavid van Moolenbroek #include <isc/hmacsha.h>
43*00b67f09SDavid van Moolenbroek #endif
44*00b67f09SDavid van Moolenbroek 
45*00b67f09SDavid van Moolenbroek #include <dns/db.h>
46*00b67f09SDavid van Moolenbroek #include <dns/dispatch.h>
47*00b67f09SDavid van Moolenbroek #include <dns/events.h>
48*00b67f09SDavid van Moolenbroek #include <dns/message.h>
49*00b67f09SDavid van Moolenbroek #include <dns/peer.h>
50*00b67f09SDavid van Moolenbroek #include <dns/rcode.h>
51*00b67f09SDavid van Moolenbroek #include <dns/rdata.h>
52*00b67f09SDavid van Moolenbroek #include <dns/rdataclass.h>
53*00b67f09SDavid van Moolenbroek #include <dns/rdatalist.h>
54*00b67f09SDavid van Moolenbroek #include <dns/rdataset.h>
55*00b67f09SDavid van Moolenbroek #include <dns/resolver.h>
56*00b67f09SDavid van Moolenbroek #include <dns/stats.h>
57*00b67f09SDavid van Moolenbroek #include <dns/tsig.h>
58*00b67f09SDavid van Moolenbroek #include <dns/view.h>
59*00b67f09SDavid van Moolenbroek #include <dns/zone.h>
60*00b67f09SDavid van Moolenbroek 
61*00b67f09SDavid van Moolenbroek #include <named/interfacemgr.h>
62*00b67f09SDavid van Moolenbroek #include <named/log.h>
63*00b67f09SDavid van Moolenbroek #include <named/notify.h>
64*00b67f09SDavid van Moolenbroek #include <named/os.h>
65*00b67f09SDavid van Moolenbroek #include <named/server.h>
66*00b67f09SDavid van Moolenbroek #include <named/update.h>
67*00b67f09SDavid van Moolenbroek 
68*00b67f09SDavid van Moolenbroek #include "pfilter.h"
69*00b67f09SDavid van Moolenbroek 
70*00b67f09SDavid van Moolenbroek /***
71*00b67f09SDavid van Moolenbroek  *** Client
72*00b67f09SDavid van Moolenbroek  ***/
73*00b67f09SDavid van Moolenbroek 
74*00b67f09SDavid van Moolenbroek /*! \file
75*00b67f09SDavid van Moolenbroek  * Client Routines
76*00b67f09SDavid van Moolenbroek  *
77*00b67f09SDavid van Moolenbroek  * Important note!
78*00b67f09SDavid van Moolenbroek  *
79*00b67f09SDavid van Moolenbroek  * All client state changes, other than that from idle to listening, occur
80*00b67f09SDavid van Moolenbroek  * as a result of events.  This guarantees serialization and avoids the
81*00b67f09SDavid van Moolenbroek  * need for locking.
82*00b67f09SDavid van Moolenbroek  *
83*00b67f09SDavid van Moolenbroek  * If a routine is ever created that allows someone other than the client's
84*00b67f09SDavid van Moolenbroek  * task to change the client, then the client will have to be locked.
85*00b67f09SDavid van Moolenbroek  */
86*00b67f09SDavid van Moolenbroek 
87*00b67f09SDavid van Moolenbroek #define NS_CLIENT_TRACE
88*00b67f09SDavid van Moolenbroek #ifdef NS_CLIENT_TRACE
89*00b67f09SDavid van Moolenbroek #define CTRACE(m)	ns_client_log(client, \
90*00b67f09SDavid van Moolenbroek 				      NS_LOGCATEGORY_CLIENT, \
91*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, \
92*00b67f09SDavid van Moolenbroek 				      ISC_LOG_DEBUG(3), \
93*00b67f09SDavid van Moolenbroek 				      "%s", (m))
94*00b67f09SDavid van Moolenbroek #define MTRACE(m)	isc_log_write(ns_g_lctx, \
95*00b67f09SDavid van Moolenbroek 				      NS_LOGCATEGORY_GENERAL, \
96*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, \
97*00b67f09SDavid van Moolenbroek 				      ISC_LOG_DEBUG(3), \
98*00b67f09SDavid van Moolenbroek 				      "clientmgr @%p: %s", manager, (m))
99*00b67f09SDavid van Moolenbroek #else
100*00b67f09SDavid van Moolenbroek #define CTRACE(m)	((void)(m))
101*00b67f09SDavid van Moolenbroek #define MTRACE(m)	((void)(m))
102*00b67f09SDavid van Moolenbroek #endif
103*00b67f09SDavid van Moolenbroek 
104*00b67f09SDavid van Moolenbroek #define TCP_CLIENT(c)	(((c)->attributes & NS_CLIENTATTR_TCP) != 0)
105*00b67f09SDavid van Moolenbroek 
106*00b67f09SDavid van Moolenbroek #define TCP_BUFFER_SIZE			(65535 + 2)
107*00b67f09SDavid van Moolenbroek #define SEND_BUFFER_SIZE		4096
108*00b67f09SDavid van Moolenbroek #define RECV_BUFFER_SIZE		4096
109*00b67f09SDavid van Moolenbroek 
110*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
111*00b67f09SDavid van Moolenbroek #define NMCTXS				100
112*00b67f09SDavid van Moolenbroek /*%<
113*00b67f09SDavid van Moolenbroek  * Number of 'mctx pools' for clients. (Should this be configurable?)
114*00b67f09SDavid van Moolenbroek  * When enabling threads, we use a pool of memory contexts shared by
115*00b67f09SDavid van Moolenbroek  * client objects, since concurrent access to a shared context would cause
116*00b67f09SDavid van Moolenbroek  * heavy contentions.  The above constant is expected to be enough for
117*00b67f09SDavid van Moolenbroek  * completely avoiding contentions among threads for an authoritative-only
118*00b67f09SDavid van Moolenbroek  * server.
119*00b67f09SDavid van Moolenbroek  */
120*00b67f09SDavid van Moolenbroek #else
121*00b67f09SDavid van Moolenbroek #define NMCTXS				0
122*00b67f09SDavid van Moolenbroek /*%<
123*00b67f09SDavid van Moolenbroek  * If named with built without thread, simply share manager's context.  Using
124*00b67f09SDavid van Moolenbroek  * a separate context in this case would simply waste memory.
125*00b67f09SDavid van Moolenbroek  */
126*00b67f09SDavid van Moolenbroek #endif
127*00b67f09SDavid van Moolenbroek 
128*00b67f09SDavid van Moolenbroek #define SIT_SIZE 24U /* 8 + 4 + 4 + 8 */
129*00b67f09SDavid van Moolenbroek 
130*00b67f09SDavid van Moolenbroek /*% nameserver client manager structure */
131*00b67f09SDavid van Moolenbroek struct ns_clientmgr {
132*00b67f09SDavid van Moolenbroek 	/* Unlocked. */
133*00b67f09SDavid van Moolenbroek 	unsigned int			magic;
134*00b67f09SDavid van Moolenbroek 
135*00b67f09SDavid van Moolenbroek 	/* The queue object has its own locks */
136*00b67f09SDavid van Moolenbroek 	client_queue_t			inactive;     /*%< To be recycled */
137*00b67f09SDavid van Moolenbroek 
138*00b67f09SDavid van Moolenbroek 	isc_mem_t *			mctx;
139*00b67f09SDavid van Moolenbroek 	isc_taskmgr_t *			taskmgr;
140*00b67f09SDavid van Moolenbroek 	isc_timermgr_t *		timermgr;
141*00b67f09SDavid van Moolenbroek 
142*00b67f09SDavid van Moolenbroek 	/* Lock covers manager state. */
143*00b67f09SDavid van Moolenbroek 	isc_mutex_t			lock;
144*00b67f09SDavid van Moolenbroek 	isc_boolean_t			exiting;
145*00b67f09SDavid van Moolenbroek 
146*00b67f09SDavid van Moolenbroek 	/* Lock covers the clients list */
147*00b67f09SDavid van Moolenbroek 	isc_mutex_t			listlock;
148*00b67f09SDavid van Moolenbroek 	client_list_t			clients;      /*%< All active clients */
149*00b67f09SDavid van Moolenbroek 
150*00b67f09SDavid van Moolenbroek 	/* Lock covers the recursing list */
151*00b67f09SDavid van Moolenbroek 	isc_mutex_t			reclock;
152*00b67f09SDavid van Moolenbroek 	client_list_t			recursing;    /*%< Recursing clients */
153*00b67f09SDavid van Moolenbroek 
154*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
155*00b67f09SDavid van Moolenbroek 	/*%< mctx pool for clients. */
156*00b67f09SDavid van Moolenbroek 	unsigned int			nextmctx;
157*00b67f09SDavid van Moolenbroek 	isc_mem_t *			mctxpool[NMCTXS];
158*00b67f09SDavid van Moolenbroek #endif
159*00b67f09SDavid van Moolenbroek };
160*00b67f09SDavid van Moolenbroek 
161*00b67f09SDavid van Moolenbroek #define MANAGER_MAGIC			ISC_MAGIC('N', 'S', 'C', 'm')
162*00b67f09SDavid van Moolenbroek #define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, MANAGER_MAGIC)
163*00b67f09SDavid van Moolenbroek 
164*00b67f09SDavid van Moolenbroek /*!
165*00b67f09SDavid van Moolenbroek  * Client object states.  Ordering is significant: higher-numbered
166*00b67f09SDavid van Moolenbroek  * states are generally "more active", meaning that the client can
167*00b67f09SDavid van Moolenbroek  * have more dynamically allocated data, outstanding events, etc.
168*00b67f09SDavid van Moolenbroek  * In the list below, any such properties listed for state N
169*00b67f09SDavid van Moolenbroek  * also apply to any state > N.
170*00b67f09SDavid van Moolenbroek  *
171*00b67f09SDavid van Moolenbroek  * To force the client into a less active state, set client->newstate
172*00b67f09SDavid van Moolenbroek  * to that state and call exit_check().  This will cause any
173*00b67f09SDavid van Moolenbroek  * activities defined for higher-numbered states to be aborted.
174*00b67f09SDavid van Moolenbroek  */
175*00b67f09SDavid van Moolenbroek 
176*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_FREED    0
177*00b67f09SDavid van Moolenbroek /*%<
178*00b67f09SDavid van Moolenbroek  * The client object no longer exists.
179*00b67f09SDavid van Moolenbroek  */
180*00b67f09SDavid van Moolenbroek 
181*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_INACTIVE 1
182*00b67f09SDavid van Moolenbroek /*%<
183*00b67f09SDavid van Moolenbroek  * The client object exists and has a task and timer.
184*00b67f09SDavid van Moolenbroek  * Its "query" struct and sendbuf are initialized.
185*00b67f09SDavid van Moolenbroek  * It is on the client manager's list of inactive clients.
186*00b67f09SDavid van Moolenbroek  * It has a message and OPT, both in the reset state.
187*00b67f09SDavid van Moolenbroek  */
188*00b67f09SDavid van Moolenbroek 
189*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_READY    2
190*00b67f09SDavid van Moolenbroek /*%<
191*00b67f09SDavid van Moolenbroek  * The client object is either a TCP or a UDP one, and
192*00b67f09SDavid van Moolenbroek  * it is associated with a network interface.  It is on the
193*00b67f09SDavid van Moolenbroek  * client manager's list of active clients.
194*00b67f09SDavid van Moolenbroek  *
195*00b67f09SDavid van Moolenbroek  * If it is a TCP client object, it has a TCP listener socket
196*00b67f09SDavid van Moolenbroek  * and an outstanding TCP listen request.
197*00b67f09SDavid van Moolenbroek  *
198*00b67f09SDavid van Moolenbroek  * If it is a UDP client object, it has a UDP listener socket
199*00b67f09SDavid van Moolenbroek  * and an outstanding UDP receive request.
200*00b67f09SDavid van Moolenbroek  */
201*00b67f09SDavid van Moolenbroek 
202*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_READING  3
203*00b67f09SDavid van Moolenbroek /*%<
204*00b67f09SDavid van Moolenbroek  * The client object is a TCP client object that has received
205*00b67f09SDavid van Moolenbroek  * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
206*00b67f09SDavid van Moolenbroek  * outstanding TCP read request.  This state is not used for
207*00b67f09SDavid van Moolenbroek  * UDP client objects.
208*00b67f09SDavid van Moolenbroek  */
209*00b67f09SDavid van Moolenbroek 
210*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_WORKING  4
211*00b67f09SDavid van Moolenbroek /*%<
212*00b67f09SDavid van Moolenbroek  * The client object has received a request and is working
213*00b67f09SDavid van Moolenbroek  * on it.  It has a view, and it may have any of a non-reset OPT,
214*00b67f09SDavid van Moolenbroek  * recursion quota, and an outstanding write request.
215*00b67f09SDavid van Moolenbroek  */
216*00b67f09SDavid van Moolenbroek 
217*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_RECURSING  5
218*00b67f09SDavid van Moolenbroek /*%<
219*00b67f09SDavid van Moolenbroek  * The client object is recursing.  It will be on the 'recursing'
220*00b67f09SDavid van Moolenbroek  * list.
221*00b67f09SDavid van Moolenbroek  */
222*00b67f09SDavid van Moolenbroek 
223*00b67f09SDavid van Moolenbroek #define NS_CLIENTSTATE_MAX      9
224*00b67f09SDavid van Moolenbroek /*%<
225*00b67f09SDavid van Moolenbroek  * Sentinel value used to indicate "no state".  When client->newstate
226*00b67f09SDavid van Moolenbroek  * has this value, we are not attempting to exit the current state.
227*00b67f09SDavid van Moolenbroek  * Must be greater than any valid state.
228*00b67f09SDavid van Moolenbroek  */
229*00b67f09SDavid van Moolenbroek 
230*00b67f09SDavid van Moolenbroek /*
231*00b67f09SDavid van Moolenbroek  * Enable ns_client_dropport() by default.
232*00b67f09SDavid van Moolenbroek  */
233*00b67f09SDavid van Moolenbroek #ifndef NS_CLIENT_DROPPORT
234*00b67f09SDavid van Moolenbroek #define NS_CLIENT_DROPPORT 1
235*00b67f09SDavid van Moolenbroek #endif
236*00b67f09SDavid van Moolenbroek 
237*00b67f09SDavid van Moolenbroek unsigned int ns_client_requests;
238*00b67f09SDavid van Moolenbroek 
239*00b67f09SDavid van Moolenbroek static void client_read(ns_client_t *client);
240*00b67f09SDavid van Moolenbroek static void client_accept(ns_client_t *client);
241*00b67f09SDavid van Moolenbroek static void client_udprecv(ns_client_t *client);
242*00b67f09SDavid van Moolenbroek static void clientmgr_destroy(ns_clientmgr_t *manager);
243*00b67f09SDavid van Moolenbroek static isc_boolean_t exit_check(ns_client_t *client);
244*00b67f09SDavid van Moolenbroek static void ns_client_endrequest(ns_client_t *client);
245*00b67f09SDavid van Moolenbroek static void client_start(isc_task_t *task, isc_event_t *event);
246*00b67f09SDavid van Moolenbroek static void client_request(isc_task_t *task, isc_event_t *event);
247*00b67f09SDavid van Moolenbroek static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
248*00b67f09SDavid van Moolenbroek static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
249*00b67f09SDavid van Moolenbroek 			       dns_dispatch_t *disp, isc_boolean_t tcp);
250*00b67f09SDavid van Moolenbroek static inline isc_boolean_t
251*00b67f09SDavid van Moolenbroek allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
252*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
253*00b67f09SDavid van Moolenbroek static void compute_sit(ns_client_t *client, isc_uint32_t when,
254*00b67f09SDavid van Moolenbroek 			isc_uint32_t nonce, isc_buffer_t *buf);
255*00b67f09SDavid van Moolenbroek #endif
256*00b67f09SDavid van Moolenbroek 
257*00b67f09SDavid van Moolenbroek void
ns_client_recursing(ns_client_t * client)258*00b67f09SDavid van Moolenbroek ns_client_recursing(ns_client_t *client) {
259*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
260*00b67f09SDavid van Moolenbroek 	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
261*00b67f09SDavid van Moolenbroek 
262*00b67f09SDavid van Moolenbroek 	LOCK(&client->manager->reclock);
263*00b67f09SDavid van Moolenbroek 	client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
264*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(client->manager->recursing, client, rlink);
265*00b67f09SDavid van Moolenbroek 	UNLOCK(&client->manager->reclock);
266*00b67f09SDavid van Moolenbroek }
267*00b67f09SDavid van Moolenbroek 
268*00b67f09SDavid van Moolenbroek void
ns_client_killoldestquery(ns_client_t * client)269*00b67f09SDavid van Moolenbroek ns_client_killoldestquery(ns_client_t *client) {
270*00b67f09SDavid van Moolenbroek 	ns_client_t *oldest;
271*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
272*00b67f09SDavid van Moolenbroek 
273*00b67f09SDavid van Moolenbroek 	LOCK(&client->manager->reclock);
274*00b67f09SDavid van Moolenbroek 	oldest = ISC_LIST_HEAD(client->manager->recursing);
275*00b67f09SDavid van Moolenbroek 	if (oldest != NULL) {
276*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
277*00b67f09SDavid van Moolenbroek 		UNLOCK(&client->manager->reclock);
278*00b67f09SDavid van Moolenbroek 		ns_query_cancel(oldest);
279*00b67f09SDavid van Moolenbroek 	} else
280*00b67f09SDavid van Moolenbroek 		UNLOCK(&client->manager->reclock);
281*00b67f09SDavid van Moolenbroek }
282*00b67f09SDavid van Moolenbroek 
283*00b67f09SDavid van Moolenbroek void
ns_client_settimeout(ns_client_t * client,unsigned int seconds)284*00b67f09SDavid van Moolenbroek ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
285*00b67f09SDavid van Moolenbroek 	isc_result_t result;
286*00b67f09SDavid van Moolenbroek 	isc_interval_t interval;
287*00b67f09SDavid van Moolenbroek 
288*00b67f09SDavid van Moolenbroek 	isc_interval_set(&interval, seconds, 0);
289*00b67f09SDavid van Moolenbroek 	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
290*00b67f09SDavid van Moolenbroek 				 &interval, ISC_FALSE);
291*00b67f09SDavid van Moolenbroek 	client->timerset = ISC_TRUE;
292*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
293*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
294*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
295*00b67f09SDavid van Moolenbroek 			      "setting timeout: %s",
296*00b67f09SDavid van Moolenbroek 			      isc_result_totext(result));
297*00b67f09SDavid van Moolenbroek 		/* Continue anyway. */
298*00b67f09SDavid van Moolenbroek 	}
299*00b67f09SDavid van Moolenbroek }
300*00b67f09SDavid van Moolenbroek 
301*00b67f09SDavid van Moolenbroek /*%
302*00b67f09SDavid van Moolenbroek  * Check for a deactivation or shutdown request and take appropriate
303*00b67f09SDavid van Moolenbroek  * action.  Returns ISC_TRUE if either is in progress; in this case
304*00b67f09SDavid van Moolenbroek  * the caller must no longer use the client object as it may have been
305*00b67f09SDavid van Moolenbroek  * freed.
306*00b67f09SDavid van Moolenbroek  */
307*00b67f09SDavid van Moolenbroek static isc_boolean_t
exit_check(ns_client_t * client)308*00b67f09SDavid van Moolenbroek exit_check(ns_client_t *client) {
309*00b67f09SDavid van Moolenbroek 	isc_boolean_t destroy_manager = ISC_FALSE;
310*00b67f09SDavid van Moolenbroek 	ns_clientmgr_t *manager = NULL;
311*00b67f09SDavid van Moolenbroek 
312*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
313*00b67f09SDavid van Moolenbroek 	manager = client->manager;
314*00b67f09SDavid van Moolenbroek 
315*00b67f09SDavid van Moolenbroek 	if (client->state <= client->newstate)
316*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE); /* Business as usual. */
317*00b67f09SDavid van Moolenbroek 
318*00b67f09SDavid van Moolenbroek 	INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
319*00b67f09SDavid van Moolenbroek 
320*00b67f09SDavid van Moolenbroek 	/*
321*00b67f09SDavid van Moolenbroek 	 * We need to detach from the view early when shutting down
322*00b67f09SDavid van Moolenbroek 	 * the server to break the following vicious circle:
323*00b67f09SDavid van Moolenbroek 	 *
324*00b67f09SDavid van Moolenbroek 	 *  - The resolver will not shut down until the view refcount is zero
325*00b67f09SDavid van Moolenbroek 	 *  - The view refcount does not go to zero until all clients detach
326*00b67f09SDavid van Moolenbroek 	 *  - The client does not detach from the view until references is zero
327*00b67f09SDavid van Moolenbroek 	 *  - references does not go to zero until the resolver has shut down
328*00b67f09SDavid van Moolenbroek 	 *
329*00b67f09SDavid van Moolenbroek 	 * Keep the view attached until any outstanding updates complete.
330*00b67f09SDavid van Moolenbroek 	 */
331*00b67f09SDavid van Moolenbroek 	if (client->nupdates == 0 &&
332*00b67f09SDavid van Moolenbroek 	    client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
333*00b67f09SDavid van Moolenbroek 		dns_view_detach(&client->view);
334*00b67f09SDavid van Moolenbroek 
335*00b67f09SDavid van Moolenbroek 	if (client->state == NS_CLIENTSTATE_WORKING ||
336*00b67f09SDavid van Moolenbroek 	    client->state == NS_CLIENTSTATE_RECURSING)
337*00b67f09SDavid van Moolenbroek 	{
338*00b67f09SDavid van Moolenbroek 		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
339*00b67f09SDavid van Moolenbroek 		/*
340*00b67f09SDavid van Moolenbroek 		 * Let the update processing complete.
341*00b67f09SDavid van Moolenbroek 		 */
342*00b67f09SDavid van Moolenbroek 		if (client->nupdates > 0)
343*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
344*00b67f09SDavid van Moolenbroek 
345*00b67f09SDavid van Moolenbroek 		/*
346*00b67f09SDavid van Moolenbroek 		 * We are trying to abort request processing.
347*00b67f09SDavid van Moolenbroek 		 */
348*00b67f09SDavid van Moolenbroek 		if (client->nsends > 0) {
349*00b67f09SDavid van Moolenbroek 			isc_socket_t *socket;
350*00b67f09SDavid van Moolenbroek 			if (TCP_CLIENT(client))
351*00b67f09SDavid van Moolenbroek 				socket = client->tcpsocket;
352*00b67f09SDavid van Moolenbroek 			else
353*00b67f09SDavid van Moolenbroek 				socket = client->udpsocket;
354*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(socket, client->task,
355*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_SEND);
356*00b67f09SDavid van Moolenbroek 		}
357*00b67f09SDavid van Moolenbroek 
358*00b67f09SDavid van Moolenbroek 		if (! (client->nsends == 0 && client->nrecvs == 0 &&
359*00b67f09SDavid van Moolenbroek 		       client->references == 0))
360*00b67f09SDavid van Moolenbroek 		{
361*00b67f09SDavid van Moolenbroek 			/*
362*00b67f09SDavid van Moolenbroek 			 * Still waiting for I/O cancel completion.
363*00b67f09SDavid van Moolenbroek 			 * or lingering references.
364*00b67f09SDavid van Moolenbroek 			 */
365*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
366*00b67f09SDavid van Moolenbroek 		}
367*00b67f09SDavid van Moolenbroek 
368*00b67f09SDavid van Moolenbroek 		/*
369*00b67f09SDavid van Moolenbroek 		 * I/O cancel is complete.  Burn down all state
370*00b67f09SDavid van Moolenbroek 		 * related to the current request.  Ensure that
371*00b67f09SDavid van Moolenbroek 		 * the client is no longer on the recursing list.
372*00b67f09SDavid van Moolenbroek 		 *
373*00b67f09SDavid van Moolenbroek 		 * We need to check whether the client is still linked,
374*00b67f09SDavid van Moolenbroek 		 * because it may already have been removed from the
375*00b67f09SDavid van Moolenbroek 		 * recursing list by ns_client_killoldestquery()
376*00b67f09SDavid van Moolenbroek 		 */
377*00b67f09SDavid van Moolenbroek 		if (client->state == NS_CLIENTSTATE_RECURSING) {
378*00b67f09SDavid van Moolenbroek 			LOCK(&manager->reclock);
379*00b67f09SDavid van Moolenbroek 			if (ISC_LINK_LINKED(client, rlink))
380*00b67f09SDavid van Moolenbroek 				ISC_LIST_UNLINK(manager->recursing,
381*00b67f09SDavid van Moolenbroek 						client, rlink);
382*00b67f09SDavid van Moolenbroek 			UNLOCK(&manager->reclock);
383*00b67f09SDavid van Moolenbroek 		}
384*00b67f09SDavid van Moolenbroek 		ns_client_endrequest(client);
385*00b67f09SDavid van Moolenbroek 
386*00b67f09SDavid van Moolenbroek 		client->state = NS_CLIENTSTATE_READING;
387*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
388*00b67f09SDavid van Moolenbroek 
389*00b67f09SDavid van Moolenbroek 		if (NS_CLIENTSTATE_READING == client->newstate) {
390*00b67f09SDavid van Moolenbroek 			client_read(client);
391*00b67f09SDavid van Moolenbroek 			client->newstate = NS_CLIENTSTATE_MAX;
392*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE); /* We're done. */
393*00b67f09SDavid van Moolenbroek 		}
394*00b67f09SDavid van Moolenbroek 	}
395*00b67f09SDavid van Moolenbroek 
396*00b67f09SDavid van Moolenbroek 	if (client->state == NS_CLIENTSTATE_READING) {
397*00b67f09SDavid van Moolenbroek 		/*
398*00b67f09SDavid van Moolenbroek 		 * We are trying to abort the current TCP connection,
399*00b67f09SDavid van Moolenbroek 		 * if any.
400*00b67f09SDavid van Moolenbroek 		 */
401*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
402*00b67f09SDavid van Moolenbroek 		INSIST(client->newstate <= NS_CLIENTSTATE_READY);
403*00b67f09SDavid van Moolenbroek 		if (client->nreads > 0)
404*00b67f09SDavid van Moolenbroek 			dns_tcpmsg_cancelread(&client->tcpmsg);
405*00b67f09SDavid van Moolenbroek 		if (! client->nreads == 0) {
406*00b67f09SDavid van Moolenbroek 			/* Still waiting for read cancel completion. */
407*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
408*00b67f09SDavid van Moolenbroek 		}
409*00b67f09SDavid van Moolenbroek 
410*00b67f09SDavid van Moolenbroek 		if (client->tcpmsg_valid) {
411*00b67f09SDavid van Moolenbroek 			dns_tcpmsg_invalidate(&client->tcpmsg);
412*00b67f09SDavid van Moolenbroek 			client->tcpmsg_valid = ISC_FALSE;
413*00b67f09SDavid van Moolenbroek 		}
414*00b67f09SDavid van Moolenbroek 		if (client->tcpsocket != NULL) {
415*00b67f09SDavid van Moolenbroek 			CTRACE("closetcp");
416*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&client->tcpsocket);
417*00b67f09SDavid van Moolenbroek 		}
418*00b67f09SDavid van Moolenbroek 
419*00b67f09SDavid van Moolenbroek 		if (client->tcpquota != NULL)
420*00b67f09SDavid van Moolenbroek 			isc_quota_detach(&client->tcpquota);
421*00b67f09SDavid van Moolenbroek 
422*00b67f09SDavid van Moolenbroek 		if (client->timerset) {
423*00b67f09SDavid van Moolenbroek 			(void)isc_timer_reset(client->timer,
424*00b67f09SDavid van Moolenbroek 					      isc_timertype_inactive,
425*00b67f09SDavid van Moolenbroek 					      NULL, NULL, ISC_TRUE);
426*00b67f09SDavid van Moolenbroek 			client->timerset = ISC_FALSE;
427*00b67f09SDavid van Moolenbroek 		}
428*00b67f09SDavid van Moolenbroek 
429*00b67f09SDavid van Moolenbroek 		client->peeraddr_valid = ISC_FALSE;
430*00b67f09SDavid van Moolenbroek 
431*00b67f09SDavid van Moolenbroek 		client->state = NS_CLIENTSTATE_READY;
432*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
433*00b67f09SDavid van Moolenbroek 
434*00b67f09SDavid van Moolenbroek 		/*
435*00b67f09SDavid van Moolenbroek 		 * Now the client is ready to accept a new TCP connection
436*00b67f09SDavid van Moolenbroek 		 * or UDP request, but we may have enough clients doing
437*00b67f09SDavid van Moolenbroek 		 * that already.  Check whether this client needs to remain
438*00b67f09SDavid van Moolenbroek 		 * active and force it to go inactive if not.
439*00b67f09SDavid van Moolenbroek 		 *
440*00b67f09SDavid van Moolenbroek 		 * UDP clients go inactive at this point, but TCP clients
441*00b67f09SDavid van Moolenbroek 		 * may remain active if we have fewer active TCP client
442*00b67f09SDavid van Moolenbroek 		 * objects than desired due to an earlier quota exhaustion.
443*00b67f09SDavid van Moolenbroek 		 */
444*00b67f09SDavid van Moolenbroek 		if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
445*00b67f09SDavid van Moolenbroek 			LOCK(&client->interface->lock);
446*00b67f09SDavid van Moolenbroek 			if (client->interface->ntcpcurrent <
447*00b67f09SDavid van Moolenbroek 				    client->interface->ntcptarget)
448*00b67f09SDavid van Moolenbroek 				client->mortal = ISC_FALSE;
449*00b67f09SDavid van Moolenbroek 			UNLOCK(&client->interface->lock);
450*00b67f09SDavid van Moolenbroek 		}
451*00b67f09SDavid van Moolenbroek 
452*00b67f09SDavid van Moolenbroek 		/*
453*00b67f09SDavid van Moolenbroek 		 * We don't need the client; send it to the inactive
454*00b67f09SDavid van Moolenbroek 		 * queue for recycling.
455*00b67f09SDavid van Moolenbroek 		 */
456*00b67f09SDavid van Moolenbroek 		if (client->mortal) {
457*00b67f09SDavid van Moolenbroek 			if (client->newstate > NS_CLIENTSTATE_INACTIVE)
458*00b67f09SDavid van Moolenbroek 				client->newstate = NS_CLIENTSTATE_INACTIVE;
459*00b67f09SDavid van Moolenbroek 		}
460*00b67f09SDavid van Moolenbroek 
461*00b67f09SDavid van Moolenbroek 		if (NS_CLIENTSTATE_READY == client->newstate) {
462*00b67f09SDavid van Moolenbroek 			if (TCP_CLIENT(client)) {
463*00b67f09SDavid van Moolenbroek 				client_accept(client);
464*00b67f09SDavid van Moolenbroek 			} else
465*00b67f09SDavid van Moolenbroek 				client_udprecv(client);
466*00b67f09SDavid van Moolenbroek 			client->newstate = NS_CLIENTSTATE_MAX;
467*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
468*00b67f09SDavid van Moolenbroek 		}
469*00b67f09SDavid van Moolenbroek 	}
470*00b67f09SDavid van Moolenbroek 
471*00b67f09SDavid van Moolenbroek 	if (client->state == NS_CLIENTSTATE_READY) {
472*00b67f09SDavid van Moolenbroek 		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
473*00b67f09SDavid van Moolenbroek 
474*00b67f09SDavid van Moolenbroek 		/*
475*00b67f09SDavid van Moolenbroek 		 * We are trying to enter the inactive state.
476*00b67f09SDavid van Moolenbroek 		 */
477*00b67f09SDavid van Moolenbroek 		if (client->naccepts > 0)
478*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(client->tcplistener, client->task,
479*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_ACCEPT);
480*00b67f09SDavid van Moolenbroek 
481*00b67f09SDavid van Moolenbroek 		/* Still waiting for accept cancel completion. */
482*00b67f09SDavid van Moolenbroek 		if (! (client->naccepts == 0))
483*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
484*00b67f09SDavid van Moolenbroek 
485*00b67f09SDavid van Moolenbroek 		/* Accept cancel is complete. */
486*00b67f09SDavid van Moolenbroek 		if (client->nrecvs > 0)
487*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(client->udpsocket, client->task,
488*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_RECV);
489*00b67f09SDavid van Moolenbroek 
490*00b67f09SDavid van Moolenbroek 		/* Still waiting for recv cancel completion. */
491*00b67f09SDavid van Moolenbroek 		if (! (client->nrecvs == 0))
492*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
493*00b67f09SDavid van Moolenbroek 
494*00b67f09SDavid van Moolenbroek 		/* Still waiting for control event to be delivered */
495*00b67f09SDavid van Moolenbroek 		if (client->nctls > 0)
496*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
497*00b67f09SDavid van Moolenbroek 
498*00b67f09SDavid van Moolenbroek 		/* Deactivate the client. */
499*00b67f09SDavid van Moolenbroek 		if (client->interface)
500*00b67f09SDavid van Moolenbroek 			ns_interface_detach(&client->interface);
501*00b67f09SDavid van Moolenbroek 
502*00b67f09SDavid van Moolenbroek 		INSIST(client->naccepts == 0);
503*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
504*00b67f09SDavid van Moolenbroek 		if (client->tcplistener != NULL)
505*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&client->tcplistener);
506*00b67f09SDavid van Moolenbroek 
507*00b67f09SDavid van Moolenbroek 		if (client->udpsocket != NULL)
508*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&client->udpsocket);
509*00b67f09SDavid van Moolenbroek 
510*00b67f09SDavid van Moolenbroek 		if (client->dispatch != NULL)
511*00b67f09SDavid van Moolenbroek 			dns_dispatch_detach(&client->dispatch);
512*00b67f09SDavid van Moolenbroek 
513*00b67f09SDavid van Moolenbroek 		client->attributes = 0;
514*00b67f09SDavid van Moolenbroek 		client->mortal = ISC_FALSE;
515*00b67f09SDavid van Moolenbroek 
516*00b67f09SDavid van Moolenbroek 		/*
517*00b67f09SDavid van Moolenbroek 		 * Put the client on the inactive list.  If we are aiming for
518*00b67f09SDavid van Moolenbroek 		 * the "freed" state, it will be removed from the inactive
519*00b67f09SDavid van Moolenbroek 		 * list shortly, and we need to keep the manager locked until
520*00b67f09SDavid van Moolenbroek 		 * that has been done, lest the manager decide to reactivate
521*00b67f09SDavid van Moolenbroek 		 * the dying client inbetween.
522*00b67f09SDavid van Moolenbroek 		 */
523*00b67f09SDavid van Moolenbroek 		client->state = NS_CLIENTSTATE_INACTIVE;
524*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
525*00b67f09SDavid van Moolenbroek 
526*00b67f09SDavid van Moolenbroek 		if (client->state == client->newstate) {
527*00b67f09SDavid van Moolenbroek 			client->newstate = NS_CLIENTSTATE_MAX;
528*00b67f09SDavid van Moolenbroek 			if (!ns_g_clienttest && manager != NULL &&
529*00b67f09SDavid van Moolenbroek 			    !manager->exiting)
530*00b67f09SDavid van Moolenbroek 				ISC_QUEUE_PUSH(manager->inactive, client,
531*00b67f09SDavid van Moolenbroek 					       ilink);
532*00b67f09SDavid van Moolenbroek 			if (client->needshutdown)
533*00b67f09SDavid van Moolenbroek 				isc_task_shutdown(client->task);
534*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
535*00b67f09SDavid van Moolenbroek 		}
536*00b67f09SDavid van Moolenbroek 	}
537*00b67f09SDavid van Moolenbroek 
538*00b67f09SDavid van Moolenbroek 	if (client->state == NS_CLIENTSTATE_INACTIVE) {
539*00b67f09SDavid van Moolenbroek 		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
540*00b67f09SDavid van Moolenbroek 		/*
541*00b67f09SDavid van Moolenbroek 		 * We are trying to free the client.
542*00b67f09SDavid van Moolenbroek 		 *
543*00b67f09SDavid van Moolenbroek 		 * When "shuttingdown" is true, either the task has received
544*00b67f09SDavid van Moolenbroek 		 * its shutdown event or no shutdown event has ever been
545*00b67f09SDavid van Moolenbroek 		 * set up.  Thus, we have no outstanding shutdown
546*00b67f09SDavid van Moolenbroek 		 * event at this point.
547*00b67f09SDavid van Moolenbroek 		 */
548*00b67f09SDavid van Moolenbroek 		REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
549*00b67f09SDavid van Moolenbroek 
550*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
551*00b67f09SDavid van Moolenbroek 		INSIST(!ISC_QLINK_LINKED(client, ilink));
552*00b67f09SDavid van Moolenbroek 
553*00b67f09SDavid van Moolenbroek 		if (manager != NULL) {
554*00b67f09SDavid van Moolenbroek 			LOCK(&manager->listlock);
555*00b67f09SDavid van Moolenbroek 			ISC_LIST_UNLINK(manager->clients, client, link);
556*00b67f09SDavid van Moolenbroek 			LOCK(&manager->lock);
557*00b67f09SDavid van Moolenbroek 			if (manager->exiting &&
558*00b67f09SDavid van Moolenbroek 			    ISC_LIST_EMPTY(manager->clients))
559*00b67f09SDavid van Moolenbroek 				destroy_manager = ISC_TRUE;
560*00b67f09SDavid van Moolenbroek 			UNLOCK(&manager->lock);
561*00b67f09SDavid van Moolenbroek 			UNLOCK(&manager->listlock);
562*00b67f09SDavid van Moolenbroek 		}
563*00b67f09SDavid van Moolenbroek 
564*00b67f09SDavid van Moolenbroek 		ns_query_free(client);
565*00b67f09SDavid van Moolenbroek 		isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
566*00b67f09SDavid van Moolenbroek 		isc_event_free((isc_event_t **)&client->sendevent);
567*00b67f09SDavid van Moolenbroek 		isc_event_free((isc_event_t **)&client->recvevent);
568*00b67f09SDavid van Moolenbroek 		isc_timer_detach(&client->timer);
569*00b67f09SDavid van Moolenbroek 		if (client->delaytimer != NULL)
570*00b67f09SDavid van Moolenbroek 			isc_timer_detach(&client->delaytimer);
571*00b67f09SDavid van Moolenbroek 
572*00b67f09SDavid van Moolenbroek 		if (client->tcpbuf != NULL)
573*00b67f09SDavid van Moolenbroek 			isc_mem_put(client->mctx, client->tcpbuf,
574*00b67f09SDavid van Moolenbroek 				    TCP_BUFFER_SIZE);
575*00b67f09SDavid van Moolenbroek 		if (client->opt != NULL) {
576*00b67f09SDavid van Moolenbroek 			INSIST(dns_rdataset_isassociated(client->opt));
577*00b67f09SDavid van Moolenbroek 			dns_rdataset_disassociate(client->opt);
578*00b67f09SDavid van Moolenbroek 			dns_message_puttemprdataset(client->message,
579*00b67f09SDavid van Moolenbroek 						    &client->opt);
580*00b67f09SDavid van Moolenbroek 		}
581*00b67f09SDavid van Moolenbroek 
582*00b67f09SDavid van Moolenbroek 		dns_message_destroy(&client->message);
583*00b67f09SDavid van Moolenbroek 
584*00b67f09SDavid van Moolenbroek 		/*
585*00b67f09SDavid van Moolenbroek 		 * Detaching the task must be done after unlinking from
586*00b67f09SDavid van Moolenbroek 		 * the manager's lists because the manager accesses
587*00b67f09SDavid van Moolenbroek 		 * client->task.
588*00b67f09SDavid van Moolenbroek 		 */
589*00b67f09SDavid van Moolenbroek 		if (client->task != NULL)
590*00b67f09SDavid van Moolenbroek 			isc_task_detach(&client->task);
591*00b67f09SDavid van Moolenbroek 
592*00b67f09SDavid van Moolenbroek 		CTRACE("free");
593*00b67f09SDavid van Moolenbroek 		client->magic = 0;
594*00b67f09SDavid van Moolenbroek 
595*00b67f09SDavid van Moolenbroek 		/*
596*00b67f09SDavid van Moolenbroek 		 * Check that there are no other external references to
597*00b67f09SDavid van Moolenbroek 		 * the memory context.
598*00b67f09SDavid van Moolenbroek 		 */
599*00b67f09SDavid van Moolenbroek 		if (ns_g_clienttest && isc_mem_references(client->mctx) != 1) {
600*00b67f09SDavid van Moolenbroek 			isc_mem_stats(client->mctx, stderr);
601*00b67f09SDavid van Moolenbroek 			INSIST(0);
602*00b67f09SDavid van Moolenbroek 		}
603*00b67f09SDavid van Moolenbroek 
604*00b67f09SDavid van Moolenbroek 		/*
605*00b67f09SDavid van Moolenbroek 		 * Destroy the fetchlock mutex that was created in
606*00b67f09SDavid van Moolenbroek 		 * ns_query_init().
607*00b67f09SDavid van Moolenbroek 		 */
608*00b67f09SDavid van Moolenbroek 		DESTROYLOCK(&client->query.fetchlock);
609*00b67f09SDavid van Moolenbroek 
610*00b67f09SDavid van Moolenbroek 		isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
611*00b67f09SDavid van Moolenbroek 	}
612*00b67f09SDavid van Moolenbroek 
613*00b67f09SDavid van Moolenbroek 	if (destroy_manager && manager != NULL)
614*00b67f09SDavid van Moolenbroek 		clientmgr_destroy(manager);
615*00b67f09SDavid van Moolenbroek 
616*00b67f09SDavid van Moolenbroek 	return (ISC_TRUE);
617*00b67f09SDavid van Moolenbroek }
618*00b67f09SDavid van Moolenbroek 
619*00b67f09SDavid van Moolenbroek /*%
620*00b67f09SDavid van Moolenbroek  * The client's task has received the client's control event
621*00b67f09SDavid van Moolenbroek  * as part of the startup process.
622*00b67f09SDavid van Moolenbroek  */
623*00b67f09SDavid van Moolenbroek static void
client_start(isc_task_t * task,isc_event_t * event)624*00b67f09SDavid van Moolenbroek client_start(isc_task_t *task, isc_event_t *event) {
625*00b67f09SDavid van Moolenbroek 	ns_client_t *client = (ns_client_t *) event->ev_arg;
626*00b67f09SDavid van Moolenbroek 
627*00b67f09SDavid van Moolenbroek 	INSIST(task == client->task);
628*00b67f09SDavid van Moolenbroek 
629*00b67f09SDavid van Moolenbroek 	UNUSED(task);
630*00b67f09SDavid van Moolenbroek 
631*00b67f09SDavid van Moolenbroek 	INSIST(client->nctls == 1);
632*00b67f09SDavid van Moolenbroek 	client->nctls--;
633*00b67f09SDavid van Moolenbroek 
634*00b67f09SDavid van Moolenbroek 	if (exit_check(client))
635*00b67f09SDavid van Moolenbroek 		return;
636*00b67f09SDavid van Moolenbroek 
637*00b67f09SDavid van Moolenbroek 	if (TCP_CLIENT(client)) {
638*00b67f09SDavid van Moolenbroek 		client_accept(client);
639*00b67f09SDavid van Moolenbroek 	} else {
640*00b67f09SDavid van Moolenbroek 		client_udprecv(client);
641*00b67f09SDavid van Moolenbroek 	}
642*00b67f09SDavid van Moolenbroek }
643*00b67f09SDavid van Moolenbroek 
644*00b67f09SDavid van Moolenbroek 
645*00b67f09SDavid van Moolenbroek /*%
646*00b67f09SDavid van Moolenbroek  * The client's task has received a shutdown event.
647*00b67f09SDavid van Moolenbroek  */
648*00b67f09SDavid van Moolenbroek static void
client_shutdown(isc_task_t * task,isc_event_t * event)649*00b67f09SDavid van Moolenbroek client_shutdown(isc_task_t *task, isc_event_t *event) {
650*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
651*00b67f09SDavid van Moolenbroek 
652*00b67f09SDavid van Moolenbroek 	REQUIRE(event != NULL);
653*00b67f09SDavid van Moolenbroek 	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
654*00b67f09SDavid van Moolenbroek 	client = event->ev_arg;
655*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
656*00b67f09SDavid van Moolenbroek 	REQUIRE(task == client->task);
657*00b67f09SDavid van Moolenbroek 
658*00b67f09SDavid van Moolenbroek 	UNUSED(task);
659*00b67f09SDavid van Moolenbroek 
660*00b67f09SDavid van Moolenbroek 	CTRACE("shutdown");
661*00b67f09SDavid van Moolenbroek 
662*00b67f09SDavid van Moolenbroek 	isc_event_free(&event);
663*00b67f09SDavid van Moolenbroek 
664*00b67f09SDavid van Moolenbroek 	if (client->shutdown != NULL) {
665*00b67f09SDavid van Moolenbroek 		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
666*00b67f09SDavid van Moolenbroek 		client->shutdown = NULL;
667*00b67f09SDavid van Moolenbroek 		client->shutdown_arg = NULL;
668*00b67f09SDavid van Moolenbroek 	}
669*00b67f09SDavid van Moolenbroek 
670*00b67f09SDavid van Moolenbroek 	if (ISC_QLINK_LINKED(client, ilink))
671*00b67f09SDavid van Moolenbroek 		ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
672*00b67f09SDavid van Moolenbroek 
673*00b67f09SDavid van Moolenbroek 	client->newstate = NS_CLIENTSTATE_FREED;
674*00b67f09SDavid van Moolenbroek 	client->needshutdown = ISC_FALSE;
675*00b67f09SDavid van Moolenbroek 	(void)exit_check(client);
676*00b67f09SDavid van Moolenbroek }
677*00b67f09SDavid van Moolenbroek 
678*00b67f09SDavid van Moolenbroek static void
ns_client_endrequest(ns_client_t * client)679*00b67f09SDavid van Moolenbroek ns_client_endrequest(ns_client_t *client) {
680*00b67f09SDavid van Moolenbroek 	INSIST(client->naccepts == 0);
681*00b67f09SDavid van Moolenbroek 	INSIST(client->nreads == 0);
682*00b67f09SDavid van Moolenbroek 	INSIST(client->nsends == 0);
683*00b67f09SDavid van Moolenbroek 	INSIST(client->nrecvs == 0);
684*00b67f09SDavid van Moolenbroek 	INSIST(client->nupdates == 0);
685*00b67f09SDavid van Moolenbroek 	INSIST(client->state == NS_CLIENTSTATE_WORKING ||
686*00b67f09SDavid van Moolenbroek 	       client->state == NS_CLIENTSTATE_RECURSING);
687*00b67f09SDavid van Moolenbroek 
688*00b67f09SDavid van Moolenbroek 	CTRACE("endrequest");
689*00b67f09SDavid van Moolenbroek 
690*00b67f09SDavid van Moolenbroek 	if (client->next != NULL) {
691*00b67f09SDavid van Moolenbroek 		(client->next)(client);
692*00b67f09SDavid van Moolenbroek 		client->next = NULL;
693*00b67f09SDavid van Moolenbroek 	}
694*00b67f09SDavid van Moolenbroek 
695*00b67f09SDavid van Moolenbroek 	if (client->view != NULL)
696*00b67f09SDavid van Moolenbroek 		dns_view_detach(&client->view);
697*00b67f09SDavid van Moolenbroek 	if (client->opt != NULL) {
698*00b67f09SDavid van Moolenbroek 		INSIST(dns_rdataset_isassociated(client->opt));
699*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(client->opt);
700*00b67f09SDavid van Moolenbroek 		dns_message_puttemprdataset(client->message, &client->opt);
701*00b67f09SDavid van Moolenbroek 	}
702*00b67f09SDavid van Moolenbroek 
703*00b67f09SDavid van Moolenbroek 	client->signer = NULL;
704*00b67f09SDavid van Moolenbroek 	client->udpsize = 512;
705*00b67f09SDavid van Moolenbroek 	client->extflags = 0;
706*00b67f09SDavid van Moolenbroek 	client->ednsversion = -1;
707*00b67f09SDavid van Moolenbroek 	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
708*00b67f09SDavid van Moolenbroek 
709*00b67f09SDavid van Moolenbroek 	if (client->recursionquota != NULL) {
710*00b67f09SDavid van Moolenbroek 		isc_quota_detach(&client->recursionquota);
711*00b67f09SDavid van Moolenbroek 		isc_stats_decrement(ns_g_server->nsstats,
712*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_recursclients);
713*00b67f09SDavid van Moolenbroek 	}
714*00b67f09SDavid van Moolenbroek 
715*00b67f09SDavid van Moolenbroek 	/*
716*00b67f09SDavid van Moolenbroek 	 * Clear all client attributes that are specific to
717*00b67f09SDavid van Moolenbroek 	 * the request; that's all except the TCP flag.
718*00b67f09SDavid van Moolenbroek 	 */
719*00b67f09SDavid van Moolenbroek 	client->attributes &= NS_CLIENTATTR_TCP;
720*00b67f09SDavid van Moolenbroek }
721*00b67f09SDavid van Moolenbroek 
722*00b67f09SDavid van Moolenbroek void
ns_client_next(ns_client_t * client,isc_result_t result)723*00b67f09SDavid van Moolenbroek ns_client_next(ns_client_t *client, isc_result_t result) {
724*00b67f09SDavid van Moolenbroek 	int newstate;
725*00b67f09SDavid van Moolenbroek 
726*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
727*00b67f09SDavid van Moolenbroek 	REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
728*00b67f09SDavid van Moolenbroek 		client->state == NS_CLIENTSTATE_RECURSING ||
729*00b67f09SDavid van Moolenbroek 		client->state == NS_CLIENTSTATE_READING);
730*00b67f09SDavid van Moolenbroek 
731*00b67f09SDavid van Moolenbroek 	CTRACE("next");
732*00b67f09SDavid van Moolenbroek 
733*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
734*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
735*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
736*00b67f09SDavid van Moolenbroek 			      "request failed: %s", isc_result_totext(result));
737*00b67f09SDavid van Moolenbroek 
738*00b67f09SDavid van Moolenbroek 	/*
739*00b67f09SDavid van Moolenbroek 	 * An error processing a TCP request may have left
740*00b67f09SDavid van Moolenbroek 	 * the connection out of sync.  To be safe, we always
741*00b67f09SDavid van Moolenbroek 	 * sever the connection when result != ISC_R_SUCCESS.
742*00b67f09SDavid van Moolenbroek 	 */
743*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
744*00b67f09SDavid van Moolenbroek 		newstate = NS_CLIENTSTATE_READING;
745*00b67f09SDavid van Moolenbroek 	else
746*00b67f09SDavid van Moolenbroek 		newstate = NS_CLIENTSTATE_READY;
747*00b67f09SDavid van Moolenbroek 
748*00b67f09SDavid van Moolenbroek 	if (client->newstate > newstate)
749*00b67f09SDavid van Moolenbroek 		client->newstate = newstate;
750*00b67f09SDavid van Moolenbroek 	(void)exit_check(client);
751*00b67f09SDavid van Moolenbroek }
752*00b67f09SDavid van Moolenbroek 
753*00b67f09SDavid van Moolenbroek 
754*00b67f09SDavid van Moolenbroek static void
client_senddone(isc_task_t * task,isc_event_t * event)755*00b67f09SDavid van Moolenbroek client_senddone(isc_task_t *task, isc_event_t *event) {
756*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
757*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
758*00b67f09SDavid van Moolenbroek 
759*00b67f09SDavid van Moolenbroek 	REQUIRE(sevent != NULL);
760*00b67f09SDavid van Moolenbroek 	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
761*00b67f09SDavid van Moolenbroek 	client = sevent->ev_arg;
762*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
763*00b67f09SDavid van Moolenbroek 	REQUIRE(task == client->task);
764*00b67f09SDavid van Moolenbroek 	REQUIRE(sevent == client->sendevent);
765*00b67f09SDavid van Moolenbroek 
766*00b67f09SDavid van Moolenbroek 	UNUSED(task);
767*00b67f09SDavid van Moolenbroek 
768*00b67f09SDavid van Moolenbroek 	CTRACE("senddone");
769*00b67f09SDavid van Moolenbroek 
770*00b67f09SDavid van Moolenbroek 	if (sevent->result != ISC_R_SUCCESS)
771*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
772*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
773*00b67f09SDavid van Moolenbroek 			      "error sending response: %s",
774*00b67f09SDavid van Moolenbroek 			      isc_result_totext(sevent->result));
775*00b67f09SDavid van Moolenbroek 
776*00b67f09SDavid van Moolenbroek 	INSIST(client->nsends > 0);
777*00b67f09SDavid van Moolenbroek 	client->nsends--;
778*00b67f09SDavid van Moolenbroek 
779*00b67f09SDavid van Moolenbroek 	if (client->tcpbuf != NULL) {
780*00b67f09SDavid van Moolenbroek 		INSIST(TCP_CLIENT(client));
781*00b67f09SDavid van Moolenbroek 		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
782*00b67f09SDavid van Moolenbroek 		client->tcpbuf = NULL;
783*00b67f09SDavid van Moolenbroek 	}
784*00b67f09SDavid van Moolenbroek 
785*00b67f09SDavid van Moolenbroek 	ns_client_next(client, ISC_R_SUCCESS);
786*00b67f09SDavid van Moolenbroek }
787*00b67f09SDavid van Moolenbroek 
788*00b67f09SDavid van Moolenbroek /*%
789*00b67f09SDavid van Moolenbroek  * We only want to fail with ISC_R_NOSPACE when called from
790*00b67f09SDavid van Moolenbroek  * ns_client_sendraw() and not when called from ns_client_send(),
791*00b67f09SDavid van Moolenbroek  * tcpbuffer is NULL when called from ns_client_sendraw() and
792*00b67f09SDavid van Moolenbroek  * length != 0.  tcpbuffer != NULL when called from ns_client_send()
793*00b67f09SDavid van Moolenbroek  * and length == 0.
794*00b67f09SDavid van Moolenbroek  */
795*00b67f09SDavid van Moolenbroek 
796*00b67f09SDavid van Moolenbroek static isc_result_t
client_allocsendbuf(ns_client_t * client,isc_buffer_t * buffer,isc_buffer_t * tcpbuffer,isc_uint32_t length,unsigned char * sendbuf,unsigned char ** datap)797*00b67f09SDavid van Moolenbroek client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
798*00b67f09SDavid van Moolenbroek 		    isc_buffer_t *tcpbuffer, isc_uint32_t length,
799*00b67f09SDavid van Moolenbroek 		    unsigned char *sendbuf, unsigned char **datap)
800*00b67f09SDavid van Moolenbroek {
801*00b67f09SDavid van Moolenbroek 	unsigned char *data;
802*00b67f09SDavid van Moolenbroek 	isc_uint32_t bufsize;
803*00b67f09SDavid van Moolenbroek 	isc_result_t result;
804*00b67f09SDavid van Moolenbroek 
805*00b67f09SDavid van Moolenbroek 	INSIST(datap != NULL);
806*00b67f09SDavid van Moolenbroek 	INSIST((tcpbuffer == NULL && length != 0) ||
807*00b67f09SDavid van Moolenbroek 	       (tcpbuffer != NULL && length == 0));
808*00b67f09SDavid van Moolenbroek 
809*00b67f09SDavid van Moolenbroek 	if (TCP_CLIENT(client)) {
810*00b67f09SDavid van Moolenbroek 		INSIST(client->tcpbuf == NULL);
811*00b67f09SDavid van Moolenbroek 		if (length + 2 > TCP_BUFFER_SIZE) {
812*00b67f09SDavid van Moolenbroek 			result = ISC_R_NOSPACE;
813*00b67f09SDavid van Moolenbroek 			goto done;
814*00b67f09SDavid van Moolenbroek 		}
815*00b67f09SDavid van Moolenbroek 		client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
816*00b67f09SDavid van Moolenbroek 		if (client->tcpbuf == NULL) {
817*00b67f09SDavid van Moolenbroek 			result = ISC_R_NOMEMORY;
818*00b67f09SDavid van Moolenbroek 			goto done;
819*00b67f09SDavid van Moolenbroek 		}
820*00b67f09SDavid van Moolenbroek 		data = client->tcpbuf;
821*00b67f09SDavid van Moolenbroek 		if (tcpbuffer != NULL) {
822*00b67f09SDavid van Moolenbroek 			isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
823*00b67f09SDavid van Moolenbroek 			isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
824*00b67f09SDavid van Moolenbroek 		} else {
825*00b67f09SDavid van Moolenbroek 			isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
826*00b67f09SDavid van Moolenbroek 			INSIST(length <= 0xffff);
827*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(buffer, (isc_uint16_t)length);
828*00b67f09SDavid van Moolenbroek 		}
829*00b67f09SDavid van Moolenbroek 	} else {
830*00b67f09SDavid van Moolenbroek 		data = sendbuf;
831*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
832*00b67f09SDavid van Moolenbroek 		if ((client->attributes & NS_CLIENTATTR_HAVESIT) == 0) {
833*00b67f09SDavid van Moolenbroek 			if (client->view != NULL)
834*00b67f09SDavid van Moolenbroek 				bufsize = client->view->situdp;
835*00b67f09SDavid van Moolenbroek 			else
836*00b67f09SDavid van Moolenbroek 				bufsize = 512;
837*00b67f09SDavid van Moolenbroek 		} else
838*00b67f09SDavid van Moolenbroek 			bufsize = client->udpsize;
839*00b67f09SDavid van Moolenbroek 		if (bufsize > client->udpsize)
840*00b67f09SDavid van Moolenbroek 			bufsize = client->udpsize;
841*00b67f09SDavid van Moolenbroek 		if (bufsize > SEND_BUFFER_SIZE)
842*00b67f09SDavid van Moolenbroek 			bufsize = SEND_BUFFER_SIZE;
843*00b67f09SDavid van Moolenbroek #else
844*00b67f09SDavid van Moolenbroek 		if (client->udpsize < SEND_BUFFER_SIZE)
845*00b67f09SDavid van Moolenbroek 			bufsize = client->udpsize;
846*00b67f09SDavid van Moolenbroek 		else
847*00b67f09SDavid van Moolenbroek 			bufsize = SEND_BUFFER_SIZE;
848*00b67f09SDavid van Moolenbroek #endif
849*00b67f09SDavid van Moolenbroek 		if (length > bufsize) {
850*00b67f09SDavid van Moolenbroek 			result = ISC_R_NOSPACE;
851*00b67f09SDavid van Moolenbroek 			goto done;
852*00b67f09SDavid van Moolenbroek 		}
853*00b67f09SDavid van Moolenbroek 		isc_buffer_init(buffer, data, bufsize);
854*00b67f09SDavid van Moolenbroek 	}
855*00b67f09SDavid van Moolenbroek 	*datap = data;
856*00b67f09SDavid van Moolenbroek 	result = ISC_R_SUCCESS;
857*00b67f09SDavid van Moolenbroek 
858*00b67f09SDavid van Moolenbroek  done:
859*00b67f09SDavid van Moolenbroek 	return (result);
860*00b67f09SDavid van Moolenbroek }
861*00b67f09SDavid van Moolenbroek 
862*00b67f09SDavid van Moolenbroek static isc_result_t
client_sendpkg(ns_client_t * client,isc_buffer_t * buffer)863*00b67f09SDavid van Moolenbroek client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
864*00b67f09SDavid van Moolenbroek 	struct in6_pktinfo *pktinfo;
865*00b67f09SDavid van Moolenbroek 	isc_result_t result;
866*00b67f09SDavid van Moolenbroek 	isc_region_t r;
867*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t *address;
868*00b67f09SDavid van Moolenbroek 	isc_socket_t *socket;
869*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
870*00b67f09SDavid van Moolenbroek 	int match;
871*00b67f09SDavid van Moolenbroek 	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
872*00b67f09SDavid van Moolenbroek 	isc_dscp_t dispdscp = -1;
873*00b67f09SDavid van Moolenbroek 
874*00b67f09SDavid van Moolenbroek 	if (TCP_CLIENT(client)) {
875*00b67f09SDavid van Moolenbroek 		socket = client->tcpsocket;
876*00b67f09SDavid van Moolenbroek 		address = NULL;
877*00b67f09SDavid van Moolenbroek 	} else {
878*00b67f09SDavid van Moolenbroek 		socket = client->udpsocket;
879*00b67f09SDavid van Moolenbroek 		address = &client->peeraddr;
880*00b67f09SDavid van Moolenbroek 
881*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
882*00b67f09SDavid van Moolenbroek 		if (ns_g_server->blackholeacl != NULL &&
883*00b67f09SDavid van Moolenbroek 		    dns_acl_match(&netaddr, NULL,
884*00b67f09SDavid van Moolenbroek 				  ns_g_server->blackholeacl,
885*00b67f09SDavid van Moolenbroek 				  &ns_g_server->aclenv,
886*00b67f09SDavid van Moolenbroek 				  &match, NULL) == ISC_R_SUCCESS &&
887*00b67f09SDavid van Moolenbroek 		    match > 0)
888*00b67f09SDavid van Moolenbroek 			return (DNS_R_BLACKHOLED);
889*00b67f09SDavid van Moolenbroek 		sockflags |= ISC_SOCKFLAG_NORETRY;
890*00b67f09SDavid van Moolenbroek 	}
891*00b67f09SDavid van Moolenbroek 
892*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
893*00b67f09SDavid van Moolenbroek 	    (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
894*00b67f09SDavid van Moolenbroek 		pktinfo = &client->pktinfo;
895*00b67f09SDavid van Moolenbroek 	else
896*00b67f09SDavid van Moolenbroek 		pktinfo = NULL;
897*00b67f09SDavid van Moolenbroek 
898*00b67f09SDavid van Moolenbroek 	if (client->dispatch != NULL) {
899*00b67f09SDavid van Moolenbroek 		dispdscp = dns_dispatch_getdscp(client->dispatch);
900*00b67f09SDavid van Moolenbroek 		if (dispdscp != -1)
901*00b67f09SDavid van Moolenbroek 			client->dscp = dispdscp;
902*00b67f09SDavid van Moolenbroek 	}
903*00b67f09SDavid van Moolenbroek 
904*00b67f09SDavid van Moolenbroek 	if (client->dscp == -1) {
905*00b67f09SDavid van Moolenbroek 		client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
906*00b67f09SDavid van Moolenbroek 		client->sendevent->dscp = 0;
907*00b67f09SDavid van Moolenbroek 	} else {
908*00b67f09SDavid van Moolenbroek 		client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
909*00b67f09SDavid van Moolenbroek 		client->sendevent->dscp = client->dscp;
910*00b67f09SDavid van Moolenbroek 	}
911*00b67f09SDavid van Moolenbroek 
912*00b67f09SDavid van Moolenbroek 	isc_buffer_usedregion(buffer, &r);
913*00b67f09SDavid van Moolenbroek 
914*00b67f09SDavid van Moolenbroek 	CTRACE("sendto");
915*00b67f09SDavid van Moolenbroek 
916*00b67f09SDavid van Moolenbroek 	result = isc_socket_sendto2(socket, &r, client->task,
917*00b67f09SDavid van Moolenbroek 				    address, pktinfo,
918*00b67f09SDavid van Moolenbroek 				    client->sendevent, sockflags);
919*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
920*00b67f09SDavid van Moolenbroek 		client->nsends++;
921*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
922*00b67f09SDavid van Moolenbroek 			client_senddone(client->task,
923*00b67f09SDavid van Moolenbroek 					(isc_event_t *)client->sendevent);
924*00b67f09SDavid van Moolenbroek 		result = ISC_R_SUCCESS;
925*00b67f09SDavid van Moolenbroek 	}
926*00b67f09SDavid van Moolenbroek 	return (result);
927*00b67f09SDavid van Moolenbroek }
928*00b67f09SDavid van Moolenbroek 
929*00b67f09SDavid van Moolenbroek void
ns_client_sendraw(ns_client_t * client,dns_message_t * message)930*00b67f09SDavid van Moolenbroek ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
931*00b67f09SDavid van Moolenbroek 	isc_result_t result;
932*00b67f09SDavid van Moolenbroek 	unsigned char *data;
933*00b67f09SDavid van Moolenbroek 	isc_buffer_t buffer;
934*00b67f09SDavid van Moolenbroek 	isc_region_t r;
935*00b67f09SDavid van Moolenbroek 	isc_region_t *mr;
936*00b67f09SDavid van Moolenbroek 	unsigned char sendbuf[SEND_BUFFER_SIZE];
937*00b67f09SDavid van Moolenbroek 
938*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
939*00b67f09SDavid van Moolenbroek 
940*00b67f09SDavid van Moolenbroek 	CTRACE("sendraw");
941*00b67f09SDavid van Moolenbroek 
942*00b67f09SDavid van Moolenbroek 	mr = dns_message_getrawmessage(message);
943*00b67f09SDavid van Moolenbroek 	if (mr == NULL) {
944*00b67f09SDavid van Moolenbroek 		result = ISC_R_UNEXPECTEDEND;
945*00b67f09SDavid van Moolenbroek 		goto done;
946*00b67f09SDavid van Moolenbroek 	}
947*00b67f09SDavid van Moolenbroek 
948*00b67f09SDavid van Moolenbroek 	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
949*00b67f09SDavid van Moolenbroek 				     sendbuf, &data);
950*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
951*00b67f09SDavid van Moolenbroek 		goto done;
952*00b67f09SDavid van Moolenbroek 
953*00b67f09SDavid van Moolenbroek 	/*
954*00b67f09SDavid van Moolenbroek 	 * Copy message to buffer and fixup id.
955*00b67f09SDavid van Moolenbroek 	 */
956*00b67f09SDavid van Moolenbroek 	isc_buffer_availableregion(&buffer, &r);
957*00b67f09SDavid van Moolenbroek 	result = isc_buffer_copyregion(&buffer, mr);
958*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
959*00b67f09SDavid van Moolenbroek 		goto done;
960*00b67f09SDavid van Moolenbroek 	r.base[0] = (client->message->id >> 8) & 0xff;
961*00b67f09SDavid van Moolenbroek 	r.base[1] = client->message->id & 0xff;
962*00b67f09SDavid van Moolenbroek 
963*00b67f09SDavid van Moolenbroek 	result = client_sendpkg(client, &buffer);
964*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
965*00b67f09SDavid van Moolenbroek 		return;
966*00b67f09SDavid van Moolenbroek 
967*00b67f09SDavid van Moolenbroek  done:
968*00b67f09SDavid van Moolenbroek 	if (client->tcpbuf != NULL) {
969*00b67f09SDavid van Moolenbroek 		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
970*00b67f09SDavid van Moolenbroek 		client->tcpbuf = NULL;
971*00b67f09SDavid van Moolenbroek 	}
972*00b67f09SDavid van Moolenbroek 	ns_client_next(client, result);
973*00b67f09SDavid van Moolenbroek }
974*00b67f09SDavid van Moolenbroek 
975*00b67f09SDavid van Moolenbroek static void
client_send(ns_client_t * client)976*00b67f09SDavid van Moolenbroek client_send(ns_client_t *client) {
977*00b67f09SDavid van Moolenbroek 	isc_result_t result;
978*00b67f09SDavid van Moolenbroek 	unsigned char *data;
979*00b67f09SDavid van Moolenbroek 	isc_buffer_t buffer;
980*00b67f09SDavid van Moolenbroek 	isc_buffer_t tcpbuffer;
981*00b67f09SDavid van Moolenbroek 	isc_region_t r;
982*00b67f09SDavid van Moolenbroek 	dns_compress_t cctx;
983*00b67f09SDavid van Moolenbroek 	isc_boolean_t cleanup_cctx = ISC_FALSE;
984*00b67f09SDavid van Moolenbroek 	unsigned char sendbuf[SEND_BUFFER_SIZE];
985*00b67f09SDavid van Moolenbroek 	unsigned int render_opts;
986*00b67f09SDavid van Moolenbroek 	unsigned int preferred_glue;
987*00b67f09SDavid van Moolenbroek 	isc_boolean_t opt_included = ISC_FALSE;
988*00b67f09SDavid van Moolenbroek 
989*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
990*00b67f09SDavid van Moolenbroek 
991*00b67f09SDavid van Moolenbroek 	CTRACE("send");
992*00b67f09SDavid van Moolenbroek 
993*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_RA) != 0)
994*00b67f09SDavid van Moolenbroek 		client->message->flags |= DNS_MESSAGEFLAG_RA;
995*00b67f09SDavid van Moolenbroek 
996*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
997*00b67f09SDavid van Moolenbroek 		render_opts = 0;
998*00b67f09SDavid van Moolenbroek 	else
999*00b67f09SDavid van Moolenbroek 		render_opts = DNS_MESSAGERENDER_OMITDNSSEC;
1000*00b67f09SDavid van Moolenbroek 
1001*00b67f09SDavid van Moolenbroek 	preferred_glue = 0;
1002*00b67f09SDavid van Moolenbroek 	if (client->view != NULL) {
1003*00b67f09SDavid van Moolenbroek 		if (client->view->preferred_glue == dns_rdatatype_a)
1004*00b67f09SDavid van Moolenbroek 			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
1005*00b67f09SDavid van Moolenbroek 		else if (client->view->preferred_glue == dns_rdatatype_aaaa)
1006*00b67f09SDavid van Moolenbroek 			preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
1007*00b67f09SDavid van Moolenbroek 	}
1008*00b67f09SDavid van Moolenbroek 
1009*00b67f09SDavid van Moolenbroek #ifdef ALLOW_FILTER_AAAA
1010*00b67f09SDavid van Moolenbroek 	/*
1011*00b67f09SDavid van Moolenbroek 	 * filter-aaaa-on-v4 yes or break-dnssec option to suppress
1012*00b67f09SDavid van Moolenbroek 	 * AAAA records.
1013*00b67f09SDavid van Moolenbroek 	 *
1014*00b67f09SDavid van Moolenbroek 	 * We already know that request came via IPv4,
1015*00b67f09SDavid van Moolenbroek 	 * that we have both AAAA and A records,
1016*00b67f09SDavid van Moolenbroek 	 * and that we either have no signatures that the client wants
1017*00b67f09SDavid van Moolenbroek 	 * or we are supposed to break DNSSEC.
1018*00b67f09SDavid van Moolenbroek 	 *
1019*00b67f09SDavid van Moolenbroek 	 * Override preferred glue if necessary.
1020*00b67f09SDavid van Moolenbroek 	 */
1021*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
1022*00b67f09SDavid van Moolenbroek 		render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
1023*00b67f09SDavid van Moolenbroek 		if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
1024*00b67f09SDavid van Moolenbroek 			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
1025*00b67f09SDavid van Moolenbroek 	}
1026*00b67f09SDavid van Moolenbroek #endif
1027*00b67f09SDavid van Moolenbroek 
1028*00b67f09SDavid van Moolenbroek 	/*
1029*00b67f09SDavid van Moolenbroek 	 * Create an OPT for our reply.
1030*00b67f09SDavid van Moolenbroek 	 */
1031*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
1032*00b67f09SDavid van Moolenbroek 		result = ns_client_addopt(client, client->message,
1033*00b67f09SDavid van Moolenbroek 					  &client->opt);
1034*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1035*00b67f09SDavid van Moolenbroek 			goto done;
1036*00b67f09SDavid van Moolenbroek 	}
1037*00b67f09SDavid van Moolenbroek 
1038*00b67f09SDavid van Moolenbroek 	/*
1039*00b67f09SDavid van Moolenbroek 	 * XXXRTH  The following doesn't deal with TCP buffer resizing.
1040*00b67f09SDavid van Moolenbroek 	 */
1041*00b67f09SDavid van Moolenbroek 	result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
1042*00b67f09SDavid van Moolenbroek 				     sendbuf, &data);
1043*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1044*00b67f09SDavid van Moolenbroek 		goto done;
1045*00b67f09SDavid van Moolenbroek 
1046*00b67f09SDavid van Moolenbroek 	result = dns_compress_init(&cctx, -1, client->mctx);
1047*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1048*00b67f09SDavid van Moolenbroek 		goto done;
1049*00b67f09SDavid van Moolenbroek 	if (client->peeraddr_valid && client->view != NULL) {
1050*00b67f09SDavid van Moolenbroek 		isc_netaddr_t netaddr;
1051*00b67f09SDavid van Moolenbroek 		dns_name_t *name = NULL;
1052*00b67f09SDavid van Moolenbroek 
1053*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1054*00b67f09SDavid van Moolenbroek 		if (client->message->tsigkey != NULL)
1055*00b67f09SDavid van Moolenbroek 			name = &client->message->tsigkey->name;
1056*00b67f09SDavid van Moolenbroek 		if (client->view->nocasecompress == NULL ||
1057*00b67f09SDavid van Moolenbroek 		    !allowed(&netaddr, name, client->view->nocasecompress))
1058*00b67f09SDavid van Moolenbroek 		{
1059*00b67f09SDavid van Moolenbroek 			dns_compress_setsensitive(&cctx, ISC_TRUE);
1060*00b67f09SDavid van Moolenbroek 		}
1061*00b67f09SDavid van Moolenbroek 	}
1062*00b67f09SDavid van Moolenbroek 	cleanup_cctx = ISC_TRUE;
1063*00b67f09SDavid van Moolenbroek 
1064*00b67f09SDavid van Moolenbroek 	result = dns_message_renderbegin(client->message, &cctx, &buffer);
1065*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1066*00b67f09SDavid van Moolenbroek 		goto done;
1067*00b67f09SDavid van Moolenbroek 
1068*00b67f09SDavid van Moolenbroek 	if (client->opt != NULL) {
1069*00b67f09SDavid van Moolenbroek 		result = dns_message_setopt(client->message, client->opt);
1070*00b67f09SDavid van Moolenbroek 		opt_included = ISC_TRUE;
1071*00b67f09SDavid van Moolenbroek 		client->opt = NULL;
1072*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1073*00b67f09SDavid van Moolenbroek 			goto done;
1074*00b67f09SDavid van Moolenbroek 	}
1075*00b67f09SDavid van Moolenbroek 	result = dns_message_rendersection(client->message,
1076*00b67f09SDavid van Moolenbroek 					   DNS_SECTION_QUESTION, 0);
1077*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOSPACE) {
1078*00b67f09SDavid van Moolenbroek 		client->message->flags |= DNS_MESSAGEFLAG_TC;
1079*00b67f09SDavid van Moolenbroek 		goto renderend;
1080*00b67f09SDavid van Moolenbroek 	}
1081*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1082*00b67f09SDavid van Moolenbroek 		goto done;
1083*00b67f09SDavid van Moolenbroek 	/*
1084*00b67f09SDavid van Moolenbroek 	 * Stop after the question if TC was set for rate limiting.
1085*00b67f09SDavid van Moolenbroek 	 */
1086*00b67f09SDavid van Moolenbroek 	if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1087*00b67f09SDavid van Moolenbroek 		goto renderend;
1088*00b67f09SDavid van Moolenbroek 	result = dns_message_rendersection(client->message,
1089*00b67f09SDavid van Moolenbroek 					   DNS_SECTION_ANSWER,
1090*00b67f09SDavid van Moolenbroek 					   DNS_MESSAGERENDER_PARTIAL |
1091*00b67f09SDavid van Moolenbroek 					   render_opts);
1092*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOSPACE) {
1093*00b67f09SDavid van Moolenbroek 		client->message->flags |= DNS_MESSAGEFLAG_TC;
1094*00b67f09SDavid van Moolenbroek 		goto renderend;
1095*00b67f09SDavid van Moolenbroek 	}
1096*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1097*00b67f09SDavid van Moolenbroek 		goto done;
1098*00b67f09SDavid van Moolenbroek 	result = dns_message_rendersection(client->message,
1099*00b67f09SDavid van Moolenbroek 					   DNS_SECTION_AUTHORITY,
1100*00b67f09SDavid van Moolenbroek 					   DNS_MESSAGERENDER_PARTIAL |
1101*00b67f09SDavid van Moolenbroek 					   render_opts);
1102*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOSPACE) {
1103*00b67f09SDavid van Moolenbroek 		client->message->flags |= DNS_MESSAGEFLAG_TC;
1104*00b67f09SDavid van Moolenbroek 		goto renderend;
1105*00b67f09SDavid van Moolenbroek 	}
1106*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1107*00b67f09SDavid van Moolenbroek 		goto done;
1108*00b67f09SDavid van Moolenbroek 	result = dns_message_rendersection(client->message,
1109*00b67f09SDavid van Moolenbroek 					   DNS_SECTION_ADDITIONAL,
1110*00b67f09SDavid van Moolenbroek 					   preferred_glue | render_opts);
1111*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
1112*00b67f09SDavid van Moolenbroek 		goto done;
1113*00b67f09SDavid van Moolenbroek  renderend:
1114*00b67f09SDavid van Moolenbroek 	result = dns_message_renderend(client->message);
1115*00b67f09SDavid van Moolenbroek 
1116*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1117*00b67f09SDavid van Moolenbroek 		goto done;
1118*00b67f09SDavid van Moolenbroek 
1119*00b67f09SDavid van Moolenbroek 	if (cleanup_cctx) {
1120*00b67f09SDavid van Moolenbroek 		dns_compress_invalidate(&cctx);
1121*00b67f09SDavid van Moolenbroek 		cleanup_cctx = ISC_FALSE;
1122*00b67f09SDavid van Moolenbroek 	}
1123*00b67f09SDavid van Moolenbroek 
1124*00b67f09SDavid van Moolenbroek 	if (TCP_CLIENT(client)) {
1125*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&buffer, &r);
1126*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
1127*00b67f09SDavid van Moolenbroek 		isc_buffer_add(&tcpbuffer, r.length);
1128*00b67f09SDavid van Moolenbroek 		result = client_sendpkg(client, &tcpbuffer);
1129*00b67f09SDavid van Moolenbroek 	} else
1130*00b67f09SDavid van Moolenbroek 		result = client_sendpkg(client, &buffer);
1131*00b67f09SDavid van Moolenbroek 
1132*00b67f09SDavid van Moolenbroek 	/* update statistics (XXXJT: is it okay to access message->xxxkey?) */
1133*00b67f09SDavid van Moolenbroek 	isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_response);
1134*00b67f09SDavid van Moolenbroek 	if (opt_included) {
1135*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1136*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_edns0out);
1137*00b67f09SDavid van Moolenbroek 	}
1138*00b67f09SDavid van Moolenbroek 	if (client->message->tsigkey != NULL) {
1139*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1140*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_tsigout);
1141*00b67f09SDavid van Moolenbroek 	}
1142*00b67f09SDavid van Moolenbroek 	if (client->message->sig0key != NULL) {
1143*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1144*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_sig0out);
1145*00b67f09SDavid van Moolenbroek 	}
1146*00b67f09SDavid van Moolenbroek 	if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1147*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1148*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_truncatedresp);
1149*00b67f09SDavid van Moolenbroek 
1150*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
1151*00b67f09SDavid van Moolenbroek 		return;
1152*00b67f09SDavid van Moolenbroek 
1153*00b67f09SDavid van Moolenbroek  done:
1154*00b67f09SDavid van Moolenbroek 	if (client->tcpbuf != NULL) {
1155*00b67f09SDavid van Moolenbroek 		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
1156*00b67f09SDavid van Moolenbroek 		client->tcpbuf = NULL;
1157*00b67f09SDavid van Moolenbroek 	}
1158*00b67f09SDavid van Moolenbroek 
1159*00b67f09SDavid van Moolenbroek 	if (cleanup_cctx)
1160*00b67f09SDavid van Moolenbroek 		dns_compress_invalidate(&cctx);
1161*00b67f09SDavid van Moolenbroek 
1162*00b67f09SDavid van Moolenbroek 	ns_client_next(client, result);
1163*00b67f09SDavid van Moolenbroek }
1164*00b67f09SDavid van Moolenbroek 
1165*00b67f09SDavid van Moolenbroek /*
1166*00b67f09SDavid van Moolenbroek  * Completes the sending of a delayed client response.
1167*00b67f09SDavid van Moolenbroek  */
1168*00b67f09SDavid van Moolenbroek static void
client_delay(isc_task_t * task,isc_event_t * event)1169*00b67f09SDavid van Moolenbroek client_delay(isc_task_t *task, isc_event_t *event) {
1170*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
1171*00b67f09SDavid van Moolenbroek 
1172*00b67f09SDavid van Moolenbroek 	REQUIRE(event != NULL);
1173*00b67f09SDavid van Moolenbroek 	REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
1174*00b67f09SDavid van Moolenbroek 		event->ev_type == ISC_TIMEREVENT_IDLE);
1175*00b67f09SDavid van Moolenbroek 	client = event->ev_arg;
1176*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
1177*00b67f09SDavid van Moolenbroek 	REQUIRE(task == client->task);
1178*00b67f09SDavid van Moolenbroek 	REQUIRE(client->delaytimer != NULL);
1179*00b67f09SDavid van Moolenbroek 
1180*00b67f09SDavid van Moolenbroek 	UNUSED(task);
1181*00b67f09SDavid van Moolenbroek 
1182*00b67f09SDavid van Moolenbroek 	CTRACE("client_delay");
1183*00b67f09SDavid van Moolenbroek 
1184*00b67f09SDavid van Moolenbroek 	isc_event_free(&event);
1185*00b67f09SDavid van Moolenbroek 	isc_timer_detach(&client->delaytimer);
1186*00b67f09SDavid van Moolenbroek 
1187*00b67f09SDavid van Moolenbroek 	client_send(client);
1188*00b67f09SDavid van Moolenbroek 	ns_client_detach(&client);
1189*00b67f09SDavid van Moolenbroek }
1190*00b67f09SDavid van Moolenbroek 
1191*00b67f09SDavid van Moolenbroek void
ns_client_send(ns_client_t * client)1192*00b67f09SDavid van Moolenbroek ns_client_send(ns_client_t *client) {
1193*00b67f09SDavid van Moolenbroek 
1194*00b67f09SDavid van Moolenbroek 	/*
1195*00b67f09SDavid van Moolenbroek 	 * Delay the response by ns_g_delay ms.
1196*00b67f09SDavid van Moolenbroek 	 */
1197*00b67f09SDavid van Moolenbroek 	if (ns_g_delay != 0) {
1198*00b67f09SDavid van Moolenbroek 		ns_client_t *dummy = NULL;
1199*00b67f09SDavid van Moolenbroek 		isc_result_t result;
1200*00b67f09SDavid van Moolenbroek 		isc_interval_t interval;
1201*00b67f09SDavid van Moolenbroek 
1202*00b67f09SDavid van Moolenbroek 		/*
1203*00b67f09SDavid van Moolenbroek 		 * Replace ourselves if we have not already been replaced.
1204*00b67f09SDavid van Moolenbroek 		 */
1205*00b67f09SDavid van Moolenbroek 		if (!client->mortal) {
1206*00b67f09SDavid van Moolenbroek 			result = ns_client_replace(client);
1207*00b67f09SDavid van Moolenbroek 			if (result != ISC_R_SUCCESS)
1208*00b67f09SDavid van Moolenbroek 				goto nodelay;
1209*00b67f09SDavid van Moolenbroek 		}
1210*00b67f09SDavid van Moolenbroek 
1211*00b67f09SDavid van Moolenbroek 		ns_client_attach(client, &dummy);
1212*00b67f09SDavid van Moolenbroek 		if (ns_g_delay >= 1000)
1213*00b67f09SDavid van Moolenbroek 			isc_interval_set(&interval, ns_g_delay / 1000,
1214*00b67f09SDavid van Moolenbroek 					 (ns_g_delay % 1000) * 1000000);
1215*00b67f09SDavid van Moolenbroek 		else
1216*00b67f09SDavid van Moolenbroek 			isc_interval_set(&interval, 0, ns_g_delay * 1000000);
1217*00b67f09SDavid van Moolenbroek 		result = isc_timer_create(client->manager->timermgr,
1218*00b67f09SDavid van Moolenbroek 					  isc_timertype_once, NULL, &interval,
1219*00b67f09SDavid van Moolenbroek 					  client->task, client_delay,
1220*00b67f09SDavid van Moolenbroek 					  client, &client->delaytimer);
1221*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
1222*00b67f09SDavid van Moolenbroek 			return;
1223*00b67f09SDavid van Moolenbroek 
1224*00b67f09SDavid van Moolenbroek 		ns_client_detach(&dummy);
1225*00b67f09SDavid van Moolenbroek 	}
1226*00b67f09SDavid van Moolenbroek 
1227*00b67f09SDavid van Moolenbroek  nodelay:
1228*00b67f09SDavid van Moolenbroek 	client_send(client);
1229*00b67f09SDavid van Moolenbroek }
1230*00b67f09SDavid van Moolenbroek 
1231*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1232*00b67f09SDavid van Moolenbroek #define DROPPORT_NO		0
1233*00b67f09SDavid van Moolenbroek #define DROPPORT_REQUEST	1
1234*00b67f09SDavid van Moolenbroek #define DROPPORT_RESPONSE	2
1235*00b67f09SDavid van Moolenbroek /*%
1236*00b67f09SDavid van Moolenbroek  * ns_client_dropport determines if certain requests / responses
1237*00b67f09SDavid van Moolenbroek  * should be dropped based on the port number.
1238*00b67f09SDavid van Moolenbroek  *
1239*00b67f09SDavid van Moolenbroek  * Returns:
1240*00b67f09SDavid van Moolenbroek  * \li	0:	Don't drop.
1241*00b67f09SDavid van Moolenbroek  * \li	1:	Drop request.
1242*00b67f09SDavid van Moolenbroek  * \li	2:	Drop (error) response.
1243*00b67f09SDavid van Moolenbroek  */
1244*00b67f09SDavid van Moolenbroek static int
ns_client_dropport(in_port_t port)1245*00b67f09SDavid van Moolenbroek ns_client_dropport(in_port_t port) {
1246*00b67f09SDavid van Moolenbroek 	switch (port) {
1247*00b67f09SDavid van Moolenbroek 	case 7: /* echo */
1248*00b67f09SDavid van Moolenbroek 	case 13: /* daytime */
1249*00b67f09SDavid van Moolenbroek 	case 19: /* chargen */
1250*00b67f09SDavid van Moolenbroek 	case 37: /* time */
1251*00b67f09SDavid van Moolenbroek 		return (DROPPORT_REQUEST);
1252*00b67f09SDavid van Moolenbroek 	case 464: /* kpasswd */
1253*00b67f09SDavid van Moolenbroek 		return (DROPPORT_RESPONSE);
1254*00b67f09SDavid van Moolenbroek 	}
1255*00b67f09SDavid van Moolenbroek 	return (DROPPORT_NO);
1256*00b67f09SDavid van Moolenbroek }
1257*00b67f09SDavid van Moolenbroek #endif
1258*00b67f09SDavid van Moolenbroek 
1259*00b67f09SDavid van Moolenbroek void
ns_client_error(ns_client_t * client,isc_result_t result)1260*00b67f09SDavid van Moolenbroek ns_client_error(ns_client_t *client, isc_result_t result) {
1261*00b67f09SDavid van Moolenbroek 	dns_rcode_t rcode;
1262*00b67f09SDavid van Moolenbroek 	dns_message_t *message;
1263*00b67f09SDavid van Moolenbroek 
1264*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
1265*00b67f09SDavid van Moolenbroek 
1266*00b67f09SDavid van Moolenbroek 	CTRACE("error");
1267*00b67f09SDavid van Moolenbroek 
1268*00b67f09SDavid van Moolenbroek 	message = client->message;
1269*00b67f09SDavid van Moolenbroek 	rcode = dns_result_torcode(result);
1270*00b67f09SDavid van Moolenbroek 
1271*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1272*00b67f09SDavid van Moolenbroek 	/*
1273*00b67f09SDavid van Moolenbroek 	 * Don't send FORMERR to ports on the drop port list.
1274*00b67f09SDavid van Moolenbroek 	 */
1275*00b67f09SDavid van Moolenbroek 	if (rcode == dns_rcode_formerr &&
1276*00b67f09SDavid van Moolenbroek 	    ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) !=
1277*00b67f09SDavid van Moolenbroek 	    DROPPORT_NO) {
1278*00b67f09SDavid van Moolenbroek 		char buf[64];
1279*00b67f09SDavid van Moolenbroek 		isc_buffer_t b;
1280*00b67f09SDavid van Moolenbroek 
1281*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&b, buf, sizeof(buf) - 1);
1282*00b67f09SDavid van Moolenbroek 		if (dns_rcode_totext(rcode, &b) != ISC_R_SUCCESS)
1283*00b67f09SDavid van Moolenbroek 			isc_buffer_putstr(&b, "UNKNOWN RCODE");
1284*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1285*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1286*00b67f09SDavid van Moolenbroek 			      "dropped error (%.*s) response: suspicious port",
1287*00b67f09SDavid van Moolenbroek 			      (int)isc_buffer_usedlength(&b), buf);
1288*00b67f09SDavid van Moolenbroek 		ns_client_next(client, ISC_R_SUCCESS);
1289*00b67f09SDavid van Moolenbroek 		return;
1290*00b67f09SDavid van Moolenbroek 	}
1291*00b67f09SDavid van Moolenbroek #endif
1292*00b67f09SDavid van Moolenbroek 
1293*00b67f09SDavid van Moolenbroek 	/*
1294*00b67f09SDavid van Moolenbroek 	 * Try to rate limit error responses.
1295*00b67f09SDavid van Moolenbroek 	 */
1296*00b67f09SDavid van Moolenbroek 	if (client->view != NULL && client->view->rrl != NULL) {
1297*00b67f09SDavid van Moolenbroek 		isc_boolean_t wouldlog;
1298*00b67f09SDavid van Moolenbroek 		char log_buf[DNS_RRL_LOG_BUF_LEN];
1299*00b67f09SDavid van Moolenbroek 		dns_rrl_result_t rrl_result;
1300*00b67f09SDavid van Moolenbroek 
1301*00b67f09SDavid van Moolenbroek 		INSIST(rcode != dns_rcode_noerror &&
1302*00b67f09SDavid van Moolenbroek 		       rcode != dns_rcode_nxdomain);
1303*00b67f09SDavid van Moolenbroek 		wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
1304*00b67f09SDavid van Moolenbroek 		rrl_result = dns_rrl(client->view, &client->peeraddr,
1305*00b67f09SDavid van Moolenbroek 				     TCP_CLIENT(client),
1306*00b67f09SDavid van Moolenbroek 				     dns_rdataclass_in, dns_rdatatype_none,
1307*00b67f09SDavid van Moolenbroek 				     NULL, result, client->now,
1308*00b67f09SDavid van Moolenbroek 				     wouldlog, log_buf, sizeof(log_buf));
1309*00b67f09SDavid van Moolenbroek 		if (rrl_result != DNS_RRL_RESULT_OK) {
1310*00b67f09SDavid van Moolenbroek 			/*
1311*00b67f09SDavid van Moolenbroek 			 * Log dropped errors in the query category
1312*00b67f09SDavid van Moolenbroek 			 * so that they are not lost in silence.
1313*00b67f09SDavid van Moolenbroek 			 * Starts of rate-limited bursts are logged in
1314*00b67f09SDavid van Moolenbroek 			 * NS_LOGCATEGORY_RRL.
1315*00b67f09SDavid van Moolenbroek 			 */
1316*00b67f09SDavid van Moolenbroek 			if (wouldlog) {
1317*00b67f09SDavid van Moolenbroek 				ns_client_log(client,
1318*00b67f09SDavid van Moolenbroek 					      NS_LOGCATEGORY_QUERY_EERRORS,
1319*00b67f09SDavid van Moolenbroek 					      NS_LOGMODULE_CLIENT,
1320*00b67f09SDavid van Moolenbroek 					      DNS_RRL_LOG_DROP,
1321*00b67f09SDavid van Moolenbroek 					      "%s", log_buf);
1322*00b67f09SDavid van Moolenbroek 			}
1323*00b67f09SDavid van Moolenbroek 			/*
1324*00b67f09SDavid van Moolenbroek 			 * Some error responses cannot be 'slipped',
1325*00b67f09SDavid van Moolenbroek 			 * so don't try to slip any error responses.
1326*00b67f09SDavid van Moolenbroek 			 */
1327*00b67f09SDavid van Moolenbroek 			if (!client->view->rrl->log_only) {
1328*00b67f09SDavid van Moolenbroek 				isc_stats_increment(ns_g_server->nsstats,
1329*00b67f09SDavid van Moolenbroek 						dns_nsstatscounter_ratedropped);
1330*00b67f09SDavid van Moolenbroek 				isc_stats_increment(ns_g_server->nsstats,
1331*00b67f09SDavid van Moolenbroek 						dns_nsstatscounter_dropped);
1332*00b67f09SDavid van Moolenbroek 				ns_client_next(client, DNS_R_DROP);
1333*00b67f09SDavid van Moolenbroek 				return;
1334*00b67f09SDavid van Moolenbroek 			}
1335*00b67f09SDavid van Moolenbroek 		}
1336*00b67f09SDavid van Moolenbroek 	}
1337*00b67f09SDavid van Moolenbroek 
1338*00b67f09SDavid van Moolenbroek 	/*
1339*00b67f09SDavid van Moolenbroek 	 * Message may be an in-progress reply that we had trouble
1340*00b67f09SDavid van Moolenbroek 	 * with, in which case QR will be set.  We need to clear QR before
1341*00b67f09SDavid van Moolenbroek 	 * calling dns_message_reply() to avoid triggering an assertion.
1342*00b67f09SDavid van Moolenbroek 	 */
1343*00b67f09SDavid van Moolenbroek 	message->flags &= ~DNS_MESSAGEFLAG_QR;
1344*00b67f09SDavid van Moolenbroek 	/*
1345*00b67f09SDavid van Moolenbroek 	 * AA and AD shouldn't be set.
1346*00b67f09SDavid van Moolenbroek 	 */
1347*00b67f09SDavid van Moolenbroek 	message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
1348*00b67f09SDavid van Moolenbroek 	result = dns_message_reply(message, ISC_TRUE);
1349*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1350*00b67f09SDavid van Moolenbroek 		/*
1351*00b67f09SDavid van Moolenbroek 		 * It could be that we've got a query with a good header,
1352*00b67f09SDavid van Moolenbroek 		 * but a bad question section, so we try again with
1353*00b67f09SDavid van Moolenbroek 		 * want_question_section set to ISC_FALSE.
1354*00b67f09SDavid van Moolenbroek 		 */
1355*00b67f09SDavid van Moolenbroek 		result = dns_message_reply(message, ISC_FALSE);
1356*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
1357*00b67f09SDavid van Moolenbroek 			ns_client_next(client, result);
1358*00b67f09SDavid van Moolenbroek 			return;
1359*00b67f09SDavid van Moolenbroek 		}
1360*00b67f09SDavid van Moolenbroek 	}
1361*00b67f09SDavid van Moolenbroek 	message->rcode = rcode;
1362*00b67f09SDavid van Moolenbroek 
1363*00b67f09SDavid van Moolenbroek 	/*
1364*00b67f09SDavid van Moolenbroek 	 * FORMERR loop avoidance:  If we sent a FORMERR message
1365*00b67f09SDavid van Moolenbroek 	 * with the same ID to the same client less than two
1366*00b67f09SDavid van Moolenbroek 	 * seconds ago, assume that we are in an infinite error
1367*00b67f09SDavid van Moolenbroek 	 * packet dialog with a server for some protocol whose
1368*00b67f09SDavid van Moolenbroek 	 * error responses look enough like DNS queries to
1369*00b67f09SDavid van Moolenbroek 	 * elicit a FORMERR response.  Drop a packet to break
1370*00b67f09SDavid van Moolenbroek 	 * the loop.
1371*00b67f09SDavid van Moolenbroek 	 */
1372*00b67f09SDavid van Moolenbroek 	if (rcode == dns_rcode_formerr) {
1373*00b67f09SDavid van Moolenbroek 		if (isc_sockaddr_equal(&client->peeraddr,
1374*00b67f09SDavid van Moolenbroek 				       &client->formerrcache.addr) &&
1375*00b67f09SDavid van Moolenbroek 		    message->id == client->formerrcache.id &&
1376*00b67f09SDavid van Moolenbroek 		    client->requesttime - client->formerrcache.time < 2) {
1377*00b67f09SDavid van Moolenbroek 			/* Drop packet. */
1378*00b67f09SDavid van Moolenbroek 			ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1379*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
1380*00b67f09SDavid van Moolenbroek 				      "possible error packet loop, "
1381*00b67f09SDavid van Moolenbroek 				      "FORMERR dropped");
1382*00b67f09SDavid van Moolenbroek 			ns_client_next(client, result);
1383*00b67f09SDavid van Moolenbroek 			return;
1384*00b67f09SDavid van Moolenbroek 		}
1385*00b67f09SDavid van Moolenbroek 		client->formerrcache.addr = client->peeraddr;
1386*00b67f09SDavid van Moolenbroek 		client->formerrcache.time = client->requesttime;
1387*00b67f09SDavid van Moolenbroek 		client->formerrcache.id = message->id;
1388*00b67f09SDavid van Moolenbroek 	}
1389*00b67f09SDavid van Moolenbroek 	ns_client_send(client);
1390*00b67f09SDavid van Moolenbroek }
1391*00b67f09SDavid van Moolenbroek 
1392*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_addopt(ns_client_t * client,dns_message_t * message,dns_rdataset_t ** opt)1393*00b67f09SDavid van Moolenbroek ns_client_addopt(ns_client_t *client, dns_message_t *message,
1394*00b67f09SDavid van Moolenbroek 		 dns_rdataset_t **opt)
1395*00b67f09SDavid van Moolenbroek {
1396*00b67f09SDavid van Moolenbroek 	char nsid[BUFSIZ], *nsidp;
1397*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1398*00b67f09SDavid van Moolenbroek 	unsigned char sit[SIT_SIZE];
1399*00b67f09SDavid van Moolenbroek #endif
1400*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1401*00b67f09SDavid van Moolenbroek 	dns_view_t *view;
1402*00b67f09SDavid van Moolenbroek 	dns_resolver_t *resolver;
1403*00b67f09SDavid van Moolenbroek 	isc_uint16_t udpsize;
1404*00b67f09SDavid van Moolenbroek 	dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
1405*00b67f09SDavid van Moolenbroek 	int count = 0;
1406*00b67f09SDavid van Moolenbroek 	unsigned int flags;
1407*00b67f09SDavid van Moolenbroek 	unsigned char expire[4];
1408*00b67f09SDavid van Moolenbroek 
1409*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
1410*00b67f09SDavid van Moolenbroek 	REQUIRE(opt != NULL && *opt == NULL);
1411*00b67f09SDavid van Moolenbroek 	REQUIRE(message != NULL);
1412*00b67f09SDavid van Moolenbroek 
1413*00b67f09SDavid van Moolenbroek 	view = client->view;
1414*00b67f09SDavid van Moolenbroek 	resolver = (view != NULL) ? view->resolver : NULL;
1415*00b67f09SDavid van Moolenbroek 	if (resolver != NULL)
1416*00b67f09SDavid van Moolenbroek 		udpsize = dns_resolver_getudpsize(resolver);
1417*00b67f09SDavid van Moolenbroek 	else
1418*00b67f09SDavid van Moolenbroek 		udpsize = ns_g_udpsize;
1419*00b67f09SDavid van Moolenbroek 
1420*00b67f09SDavid van Moolenbroek 	flags = client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE;
1421*00b67f09SDavid van Moolenbroek 
1422*00b67f09SDavid van Moolenbroek 	/* Set EDNS options if applicable */
1423*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_WANTNSID) != 0 &&
1424*00b67f09SDavid van Moolenbroek 	    (ns_g_server->server_id != NULL ||
1425*00b67f09SDavid van Moolenbroek 	     ns_g_server->server_usehostname)) {
1426*00b67f09SDavid van Moolenbroek 		if (ns_g_server->server_usehostname) {
1427*00b67f09SDavid van Moolenbroek 			result = ns_os_gethostname(nsid, sizeof(nsid));
1428*00b67f09SDavid van Moolenbroek 			if (result != ISC_R_SUCCESS) {
1429*00b67f09SDavid van Moolenbroek 				goto no_nsid;
1430*00b67f09SDavid van Moolenbroek 			}
1431*00b67f09SDavid van Moolenbroek 			nsidp = nsid;
1432*00b67f09SDavid van Moolenbroek 		} else
1433*00b67f09SDavid van Moolenbroek 			nsidp = ns_g_server->server_id;
1434*00b67f09SDavid van Moolenbroek 
1435*00b67f09SDavid van Moolenbroek 		INSIST(count < DNS_EDNSOPTIONS);
1436*00b67f09SDavid van Moolenbroek 		ednsopts[count].code = DNS_OPT_NSID;
1437*00b67f09SDavid van Moolenbroek 		ednsopts[count].length = strlen(nsidp);
1438*00b67f09SDavid van Moolenbroek 		ednsopts[count].value = (unsigned char *)nsidp;
1439*00b67f09SDavid van Moolenbroek 		count++;
1440*00b67f09SDavid van Moolenbroek 	}
1441*00b67f09SDavid van Moolenbroek  no_nsid:
1442*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1443*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_WANTSIT) != 0) {
1444*00b67f09SDavid van Moolenbroek 		isc_buffer_t buf;
1445*00b67f09SDavid van Moolenbroek 		isc_stdtime_t now;
1446*00b67f09SDavid van Moolenbroek 		isc_uint32_t nonce;
1447*00b67f09SDavid van Moolenbroek 
1448*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&buf, sit, sizeof(sit));
1449*00b67f09SDavid van Moolenbroek 		isc_stdtime_get(&now);
1450*00b67f09SDavid van Moolenbroek 		isc_random_get(&nonce);
1451*00b67f09SDavid van Moolenbroek 
1452*00b67f09SDavid van Moolenbroek 		compute_sit(client, now, nonce, &buf);
1453*00b67f09SDavid van Moolenbroek 
1454*00b67f09SDavid van Moolenbroek 		INSIST(count < DNS_EDNSOPTIONS);
1455*00b67f09SDavid van Moolenbroek 		ednsopts[count].code = DNS_OPT_SIT;
1456*00b67f09SDavid van Moolenbroek 		ednsopts[count].length = SIT_SIZE;
1457*00b67f09SDavid van Moolenbroek 		ednsopts[count].value = sit;
1458*00b67f09SDavid van Moolenbroek 		count++;
1459*00b67f09SDavid van Moolenbroek 	}
1460*00b67f09SDavid van Moolenbroek #endif
1461*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_HAVEEXPIRE) != 0) {
1462*00b67f09SDavid van Moolenbroek 		isc_buffer_t buf;
1463*00b67f09SDavid van Moolenbroek 
1464*00b67f09SDavid van Moolenbroek 		INSIST(count < DNS_EDNSOPTIONS);
1465*00b67f09SDavid van Moolenbroek 
1466*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&buf, expire, sizeof(expire));
1467*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint32(&buf, client->expire);
1468*00b67f09SDavid van Moolenbroek 		ednsopts[count].code = DNS_OPT_EXPIRE;
1469*00b67f09SDavid van Moolenbroek 		ednsopts[count].length = 4;
1470*00b67f09SDavid van Moolenbroek 		ednsopts[count].value = expire;
1471*00b67f09SDavid van Moolenbroek 		count++;
1472*00b67f09SDavid van Moolenbroek 	}
1473*00b67f09SDavid van Moolenbroek 
1474*00b67f09SDavid van Moolenbroek 	result = dns_message_buildopt(message, opt, 0, udpsize, flags,
1475*00b67f09SDavid van Moolenbroek 				      ednsopts, count);
1476*00b67f09SDavid van Moolenbroek 	return (result);
1477*00b67f09SDavid van Moolenbroek }
1478*00b67f09SDavid van Moolenbroek 
1479*00b67f09SDavid van Moolenbroek static inline isc_boolean_t
allowed(isc_netaddr_t * addr,dns_name_t * signer,dns_acl_t * acl)1480*00b67f09SDavid van Moolenbroek allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
1481*00b67f09SDavid van Moolenbroek 	int match;
1482*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1483*00b67f09SDavid van Moolenbroek 
1484*00b67f09SDavid van Moolenbroek 	if (acl == NULL)
1485*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1486*00b67f09SDavid van Moolenbroek 	result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
1487*00b67f09SDavid van Moolenbroek 			       &match, NULL);
1488*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS && match > 0)
1489*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1490*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
1491*00b67f09SDavid van Moolenbroek }
1492*00b67f09SDavid van Moolenbroek 
1493*00b67f09SDavid van Moolenbroek /*
1494*00b67f09SDavid van Moolenbroek  * Callback to see if a non-recursive query coming from 'srcaddr' to
1495*00b67f09SDavid van Moolenbroek  * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
1496*00b67f09SDavid van Moolenbroek  * delivered to 'myview'.
1497*00b67f09SDavid van Moolenbroek  *
1498*00b67f09SDavid van Moolenbroek  * We run this unlocked as both the view list and the interface list
1499*00b67f09SDavid van Moolenbroek  * are updated when the appropriate task has exclusivity.
1500*00b67f09SDavid van Moolenbroek  */
1501*00b67f09SDavid van Moolenbroek isc_boolean_t
ns_client_isself(dns_view_t * myview,dns_tsigkey_t * mykey,isc_sockaddr_t * srcaddr,isc_sockaddr_t * dstaddr,dns_rdataclass_t rdclass,void * arg)1502*00b67f09SDavid van Moolenbroek ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
1503*00b67f09SDavid van Moolenbroek 		 isc_sockaddr_t *srcaddr, isc_sockaddr_t *dstaddr,
1504*00b67f09SDavid van Moolenbroek 		 dns_rdataclass_t rdclass, void *arg)
1505*00b67f09SDavid van Moolenbroek {
1506*00b67f09SDavid van Moolenbroek 	dns_view_t *view;
1507*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *key = NULL;
1508*00b67f09SDavid van Moolenbroek 	dns_name_t *tsig = NULL;
1509*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netsrc;
1510*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netdst;
1511*00b67f09SDavid van Moolenbroek 
1512*00b67f09SDavid van Moolenbroek 	UNUSED(arg);
1513*00b67f09SDavid van Moolenbroek 
1514*00b67f09SDavid van Moolenbroek 	/*
1515*00b67f09SDavid van Moolenbroek 	 * ns_g_server->interfacemgr is task exclusive locked.
1516*00b67f09SDavid van Moolenbroek 	 */
1517*00b67f09SDavid van Moolenbroek 	if (ns_g_server->interfacemgr == NULL)
1518*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1519*00b67f09SDavid van Moolenbroek 
1520*00b67f09SDavid van Moolenbroek 	if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
1521*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1522*00b67f09SDavid van Moolenbroek 
1523*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netsrc, srcaddr);
1524*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netdst, dstaddr);
1525*00b67f09SDavid van Moolenbroek 
1526*00b67f09SDavid van Moolenbroek 	for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
1527*00b67f09SDavid van Moolenbroek 	     view != NULL;
1528*00b67f09SDavid van Moolenbroek 	     view = ISC_LIST_NEXT(view, link)) {
1529*00b67f09SDavid van Moolenbroek 
1530*00b67f09SDavid van Moolenbroek 		if (view->matchrecursiveonly)
1531*00b67f09SDavid van Moolenbroek 			continue;
1532*00b67f09SDavid van Moolenbroek 
1533*00b67f09SDavid van Moolenbroek 		if (rdclass != view->rdclass)
1534*00b67f09SDavid van Moolenbroek 			continue;
1535*00b67f09SDavid van Moolenbroek 
1536*00b67f09SDavid van Moolenbroek 		if (mykey != NULL) {
1537*00b67f09SDavid van Moolenbroek 			isc_boolean_t match;
1538*00b67f09SDavid van Moolenbroek 			isc_result_t result;
1539*00b67f09SDavid van Moolenbroek 
1540*00b67f09SDavid van Moolenbroek 			result = dns_view_gettsig(view, &mykey->name, &key);
1541*00b67f09SDavid van Moolenbroek 			if (result != ISC_R_SUCCESS)
1542*00b67f09SDavid van Moolenbroek 				continue;
1543*00b67f09SDavid van Moolenbroek 			match = dst_key_compare(mykey->key, key->key);
1544*00b67f09SDavid van Moolenbroek 			dns_tsigkey_detach(&key);
1545*00b67f09SDavid van Moolenbroek 			if (!match)
1546*00b67f09SDavid van Moolenbroek 				continue;
1547*00b67f09SDavid van Moolenbroek 			tsig = dns_tsigkey_identity(mykey);
1548*00b67f09SDavid van Moolenbroek 		}
1549*00b67f09SDavid van Moolenbroek 
1550*00b67f09SDavid van Moolenbroek 		if (allowed(&netsrc, tsig, view->matchclients) &&
1551*00b67f09SDavid van Moolenbroek 		    allowed(&netdst, tsig, view->matchdestinations))
1552*00b67f09SDavid van Moolenbroek 			break;
1553*00b67f09SDavid van Moolenbroek 	}
1554*00b67f09SDavid van Moolenbroek 	return (ISC_TF(view == myview));
1555*00b67f09SDavid van Moolenbroek }
1556*00b67f09SDavid van Moolenbroek 
1557*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1558*00b67f09SDavid van Moolenbroek static void
compute_sit(ns_client_t * client,isc_uint32_t when,isc_uint32_t nonce,isc_buffer_t * buf)1559*00b67f09SDavid van Moolenbroek compute_sit(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce,
1560*00b67f09SDavid van Moolenbroek 	    isc_buffer_t *buf)
1561*00b67f09SDavid van Moolenbroek {
1562*00b67f09SDavid van Moolenbroek #ifdef AES_SIT
1563*00b67f09SDavid van Moolenbroek 	unsigned char digest[ISC_AES_BLOCK_LENGTH];
1564*00b67f09SDavid van Moolenbroek 	unsigned char input[4 + 4 + 16];
1565*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
1566*00b67f09SDavid van Moolenbroek 	unsigned char *cp;
1567*00b67f09SDavid van Moolenbroek 	unsigned int i;
1568*00b67f09SDavid van Moolenbroek 
1569*00b67f09SDavid van Moolenbroek 	memset(input, 0, sizeof(input));
1570*00b67f09SDavid van Moolenbroek 	cp = isc_buffer_used(buf);
1571*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, client->cookie, 8);
1572*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, nonce);
1573*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, when);
1574*00b67f09SDavid van Moolenbroek 	memmove(input, cp, 16);
1575*00b67f09SDavid van Moolenbroek 	isc_aes128_crypt(ns_g_server->secret, input, digest);
1576*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 8; i++)
1577*00b67f09SDavid van Moolenbroek 		input[i] = digest[i] ^ digest[i + 8];
1578*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1579*00b67f09SDavid van Moolenbroek 	switch (netaddr.family) {
1580*00b67f09SDavid van Moolenbroek 	case AF_INET:
1581*00b67f09SDavid van Moolenbroek 		memmove(input + 8, (unsigned char *)&netaddr.type.in, 4);
1582*00b67f09SDavid van Moolenbroek 		memset(input + 12, 0, 4);
1583*00b67f09SDavid van Moolenbroek 		isc_aes128_crypt(ns_g_server->secret, input, digest);
1584*00b67f09SDavid van Moolenbroek 		break;
1585*00b67f09SDavid van Moolenbroek 	case AF_INET6:
1586*00b67f09SDavid van Moolenbroek 		memmove(input + 8, (unsigned char *)&netaddr.type.in6, 16);
1587*00b67f09SDavid van Moolenbroek 		isc_aes128_crypt(ns_g_server->secret, input, digest);
1588*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 8; i++)
1589*00b67f09SDavid van Moolenbroek 			input[i + 8] = digest[i] ^ digest[i + 8];
1590*00b67f09SDavid van Moolenbroek 		isc_aes128_crypt(ns_g_server->secret, input + 8, digest);
1591*00b67f09SDavid van Moolenbroek 		break;
1592*00b67f09SDavid van Moolenbroek 	}
1593*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 8; i++)
1594*00b67f09SDavid van Moolenbroek 		digest[i] ^= digest[i + 8];
1595*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, digest, 8);
1596*00b67f09SDavid van Moolenbroek #endif
1597*00b67f09SDavid van Moolenbroek #ifdef HMAC_SHA1_SIT
1598*00b67f09SDavid van Moolenbroek 	unsigned char digest[ISC_SHA1_DIGESTLENGTH];
1599*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
1600*00b67f09SDavid van Moolenbroek 	unsigned char *cp;
1601*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_t hmacsha1;
1602*00b67f09SDavid van Moolenbroek 
1603*00b67f09SDavid van Moolenbroek 	cp = isc_buffer_used(buf);
1604*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, client->cookie, 8);
1605*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, nonce);
1606*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, when);
1607*00b67f09SDavid van Moolenbroek 
1608*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_init(&hmacsha1,
1609*00b67f09SDavid van Moolenbroek 			  ns_g_server->secret,
1610*00b67f09SDavid van Moolenbroek 			  ISC_SHA1_DIGESTLENGTH);
1611*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_update(&hmacsha1, cp, 16);
1612*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1613*00b67f09SDavid van Moolenbroek 	switch (netaddr.family) {
1614*00b67f09SDavid van Moolenbroek 	case AF_INET:
1615*00b67f09SDavid van Moolenbroek 		isc_hmacsha1_update(&hmacsha1,
1616*00b67f09SDavid van Moolenbroek 				    (unsigned char *)&netaddr.type.in, 4);
1617*00b67f09SDavid van Moolenbroek 		break;
1618*00b67f09SDavid van Moolenbroek 	case AF_INET6:
1619*00b67f09SDavid van Moolenbroek 		isc_hmacsha1_update(&hmacsha1,
1620*00b67f09SDavid van Moolenbroek 				    (unsigned char *)&netaddr.type.in6, 16);
1621*00b67f09SDavid van Moolenbroek 		break;
1622*00b67f09SDavid van Moolenbroek 	}
1623*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_update(&hmacsha1, client->cookie, sizeof(client->cookie));
1624*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_sign(&hmacsha1, digest, sizeof(digest));
1625*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, digest, 8);
1626*00b67f09SDavid van Moolenbroek 	isc_hmacsha1_invalidate(&hmacsha1);
1627*00b67f09SDavid van Moolenbroek #endif
1628*00b67f09SDavid van Moolenbroek #ifdef HMAC_SHA256_SIT
1629*00b67f09SDavid van Moolenbroek 	unsigned char digest[ISC_SHA256_DIGESTLENGTH];
1630*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
1631*00b67f09SDavid van Moolenbroek 	unsigned char *cp;
1632*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_t hmacsha256;
1633*00b67f09SDavid van Moolenbroek 
1634*00b67f09SDavid van Moolenbroek 	cp = isc_buffer_used(buf);
1635*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, client->cookie, 8);
1636*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, nonce);
1637*00b67f09SDavid van Moolenbroek 	isc_buffer_putuint32(buf, when);
1638*00b67f09SDavid van Moolenbroek 
1639*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_init(&hmacsha256,
1640*00b67f09SDavid van Moolenbroek 			    ns_g_server->secret,
1641*00b67f09SDavid van Moolenbroek 			    ISC_SHA256_DIGESTLENGTH);
1642*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_update(&hmacsha256, cp, 16);
1643*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1644*00b67f09SDavid van Moolenbroek 	switch (netaddr.family) {
1645*00b67f09SDavid van Moolenbroek 	case AF_INET:
1646*00b67f09SDavid van Moolenbroek 		isc_hmacsha256_update(&hmacsha256,
1647*00b67f09SDavid van Moolenbroek 				      (unsigned char *)&netaddr.type.in, 4);
1648*00b67f09SDavid van Moolenbroek 		break;
1649*00b67f09SDavid van Moolenbroek 	case AF_INET6:
1650*00b67f09SDavid van Moolenbroek 		isc_hmacsha256_update(&hmacsha256,
1651*00b67f09SDavid van Moolenbroek 				      (unsigned char *)&netaddr.type.in6, 16);
1652*00b67f09SDavid van Moolenbroek 		break;
1653*00b67f09SDavid van Moolenbroek 	}
1654*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_update(&hmacsha256, client->cookie,
1655*00b67f09SDavid van Moolenbroek 			      sizeof(client->cookie));
1656*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_sign(&hmacsha256, digest, sizeof(digest));
1657*00b67f09SDavid van Moolenbroek 	isc_buffer_putmem(buf, digest, 8);
1658*00b67f09SDavid van Moolenbroek 	isc_hmacsha256_invalidate(&hmacsha256);
1659*00b67f09SDavid van Moolenbroek #endif
1660*00b67f09SDavid van Moolenbroek }
1661*00b67f09SDavid van Moolenbroek 
1662*00b67f09SDavid van Moolenbroek static void
process_sit(ns_client_t * client,isc_buffer_t * buf,size_t optlen)1663*00b67f09SDavid van Moolenbroek process_sit(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
1664*00b67f09SDavid van Moolenbroek 	unsigned char dbuf[SIT_SIZE];
1665*00b67f09SDavid van Moolenbroek 	unsigned char *old;
1666*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
1667*00b67f09SDavid van Moolenbroek 	isc_uint32_t when;
1668*00b67f09SDavid van Moolenbroek 	isc_uint32_t nonce;
1669*00b67f09SDavid van Moolenbroek 	isc_buffer_t db;
1670*00b67f09SDavid van Moolenbroek 
1671*00b67f09SDavid van Moolenbroek 	client->attributes |= NS_CLIENTATTR_WANTSIT;
1672*00b67f09SDavid van Moolenbroek 
1673*00b67f09SDavid van Moolenbroek 	isc_stats_increment(ns_g_server->nsstats,
1674*00b67f09SDavid van Moolenbroek 			    dns_nsstatscounter_sitopt);
1675*00b67f09SDavid van Moolenbroek 
1676*00b67f09SDavid van Moolenbroek 	if (optlen != SIT_SIZE) {
1677*00b67f09SDavid van Moolenbroek 		/*
1678*00b67f09SDavid van Moolenbroek 		 * Not our token.
1679*00b67f09SDavid van Moolenbroek 		 */
1680*00b67f09SDavid van Moolenbroek 		if (optlen >= 8U)
1681*00b67f09SDavid van Moolenbroek 			memmove(client->cookie, isc_buffer_current(buf), 8);
1682*00b67f09SDavid van Moolenbroek 		else
1683*00b67f09SDavid van Moolenbroek 			memset(client->cookie, 0, 8);
1684*00b67f09SDavid van Moolenbroek 		isc_buffer_forward(buf, (unsigned int)optlen);
1685*00b67f09SDavid van Moolenbroek 
1686*00b67f09SDavid van Moolenbroek 		if (optlen == 8U)
1687*00b67f09SDavid van Moolenbroek 			isc_stats_increment(ns_g_server->nsstats,
1688*00b67f09SDavid van Moolenbroek 					    dns_nsstatscounter_sitnew);
1689*00b67f09SDavid van Moolenbroek 		else
1690*00b67f09SDavid van Moolenbroek 			isc_stats_increment(ns_g_server->nsstats,
1691*00b67f09SDavid van Moolenbroek 					    dns_nsstatscounter_sitbadsize);
1692*00b67f09SDavid van Moolenbroek 		return;
1693*00b67f09SDavid van Moolenbroek 	}
1694*00b67f09SDavid van Moolenbroek 
1695*00b67f09SDavid van Moolenbroek 	/*
1696*00b67f09SDavid van Moolenbroek 	 * Process all of the incoming buffer.
1697*00b67f09SDavid van Moolenbroek 	 */
1698*00b67f09SDavid van Moolenbroek 	old = isc_buffer_current(buf);
1699*00b67f09SDavid van Moolenbroek 	memmove(client->cookie, old, 8);
1700*00b67f09SDavid van Moolenbroek 	isc_buffer_forward(buf, 8);
1701*00b67f09SDavid van Moolenbroek 	nonce = isc_buffer_getuint32(buf);
1702*00b67f09SDavid van Moolenbroek 	when = isc_buffer_getuint32(buf);
1703*00b67f09SDavid van Moolenbroek 	isc_buffer_forward(buf, 8);
1704*00b67f09SDavid van Moolenbroek 
1705*00b67f09SDavid van Moolenbroek 	/*
1706*00b67f09SDavid van Moolenbroek 	 * Allow for a 5 minute clock skew between servers sharing a secret.
1707*00b67f09SDavid van Moolenbroek 	 * Only accept SIT if we have talked to the client in the last hour.
1708*00b67f09SDavid van Moolenbroek 	 */
1709*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
1710*00b67f09SDavid van Moolenbroek 	if (isc_serial_gt(when, (now + 300)) ||		/* In the future. */
1711*00b67f09SDavid van Moolenbroek 	    isc_serial_lt(when, (now - 3600))) {	/* In the past. */
1712*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1713*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_sitbadtime);
1714*00b67f09SDavid van Moolenbroek 		return;
1715*00b67f09SDavid van Moolenbroek 	}
1716*00b67f09SDavid van Moolenbroek 
1717*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&db, dbuf, sizeof(dbuf));
1718*00b67f09SDavid van Moolenbroek 	compute_sit(client, when, nonce, &db);
1719*00b67f09SDavid van Moolenbroek 
1720*00b67f09SDavid van Moolenbroek 	if (memcmp(old, dbuf, SIT_SIZE) != 0) {
1721*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1722*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_sitnomatch);
1723*00b67f09SDavid van Moolenbroek 		return;
1724*00b67f09SDavid van Moolenbroek 	}
1725*00b67f09SDavid van Moolenbroek 	isc_stats_increment(ns_g_server->nsstats,
1726*00b67f09SDavid van Moolenbroek 			    dns_nsstatscounter_sitmatch);
1727*00b67f09SDavid van Moolenbroek 
1728*00b67f09SDavid van Moolenbroek 	client->attributes |= NS_CLIENTATTR_HAVESIT;
1729*00b67f09SDavid van Moolenbroek }
1730*00b67f09SDavid van Moolenbroek #endif
1731*00b67f09SDavid van Moolenbroek 
1732*00b67f09SDavid van Moolenbroek static isc_result_t
process_opt(ns_client_t * client,dns_rdataset_t * opt)1733*00b67f09SDavid van Moolenbroek process_opt(ns_client_t *client, dns_rdataset_t *opt) {
1734*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata;
1735*00b67f09SDavid van Moolenbroek 	isc_buffer_t optbuf;
1736*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1737*00b67f09SDavid van Moolenbroek 	isc_uint16_t optcode;
1738*00b67f09SDavid van Moolenbroek 	isc_uint16_t optlen;
1739*00b67f09SDavid van Moolenbroek 
1740*00b67f09SDavid van Moolenbroek 	/*
1741*00b67f09SDavid van Moolenbroek 	 * Set the client's UDP buffer size.
1742*00b67f09SDavid van Moolenbroek 	 */
1743*00b67f09SDavid van Moolenbroek 	client->udpsize = opt->rdclass;
1744*00b67f09SDavid van Moolenbroek 
1745*00b67f09SDavid van Moolenbroek 	/*
1746*00b67f09SDavid van Moolenbroek 	 * If the requested UDP buffer size is less than 512,
1747*00b67f09SDavid van Moolenbroek 	 * ignore it and use 512.
1748*00b67f09SDavid van Moolenbroek 	 */
1749*00b67f09SDavid van Moolenbroek 	if (client->udpsize < 512)
1750*00b67f09SDavid van Moolenbroek 		client->udpsize = 512;
1751*00b67f09SDavid van Moolenbroek 
1752*00b67f09SDavid van Moolenbroek 	/*
1753*00b67f09SDavid van Moolenbroek 	 * Get the flags out of the OPT record.
1754*00b67f09SDavid van Moolenbroek 	 */
1755*00b67f09SDavid van Moolenbroek 	client->extflags = (isc_uint16_t)(opt->ttl & 0xFFFF);
1756*00b67f09SDavid van Moolenbroek 
1757*00b67f09SDavid van Moolenbroek 	/*
1758*00b67f09SDavid van Moolenbroek 	 * Do we understand this version of EDNS?
1759*00b67f09SDavid van Moolenbroek 	 *
1760*00b67f09SDavid van Moolenbroek 	 * XXXRTH need library support for this!
1761*00b67f09SDavid van Moolenbroek 	 */
1762*00b67f09SDavid van Moolenbroek 	client->ednsversion = (opt->ttl & 0x00FF0000) >> 16;
1763*00b67f09SDavid van Moolenbroek 	if (client->ednsversion > 0) {
1764*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1765*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_badednsver);
1766*00b67f09SDavid van Moolenbroek 		result = ns_client_addopt(client, client->message,
1767*00b67f09SDavid van Moolenbroek 					  &client->opt);
1768*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
1769*00b67f09SDavid van Moolenbroek 			result = DNS_R_BADVERS;
1770*00b67f09SDavid van Moolenbroek 		ns_client_error(client, result);
1771*00b67f09SDavid van Moolenbroek 		goto cleanup;
1772*00b67f09SDavid van Moolenbroek 	}
1773*00b67f09SDavid van Moolenbroek 
1774*00b67f09SDavid van Moolenbroek 	/* Check for NSID request */
1775*00b67f09SDavid van Moolenbroek 	result = dns_rdataset_first(opt);
1776*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
1777*00b67f09SDavid van Moolenbroek 		dns_rdata_init(&rdata);
1778*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(opt, &rdata);
1779*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
1780*00b67f09SDavid van Moolenbroek 		isc_buffer_add(&optbuf, rdata.length);
1781*00b67f09SDavid van Moolenbroek 		while (isc_buffer_remaininglength(&optbuf) >= 4) {
1782*00b67f09SDavid van Moolenbroek 			optcode = isc_buffer_getuint16(&optbuf);
1783*00b67f09SDavid van Moolenbroek 			optlen = isc_buffer_getuint16(&optbuf);
1784*00b67f09SDavid van Moolenbroek 			switch (optcode) {
1785*00b67f09SDavid van Moolenbroek 			case DNS_OPT_NSID:
1786*00b67f09SDavid van Moolenbroek 				isc_stats_increment(ns_g_server->nsstats,
1787*00b67f09SDavid van Moolenbroek 						    dns_nsstatscounter_nsidopt);
1788*00b67f09SDavid van Moolenbroek 				client->attributes |= NS_CLIENTATTR_WANTNSID;
1789*00b67f09SDavid van Moolenbroek 				isc_buffer_forward(&optbuf, optlen);
1790*00b67f09SDavid van Moolenbroek 				break;
1791*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USESIT
1792*00b67f09SDavid van Moolenbroek 			case DNS_OPT_SIT:
1793*00b67f09SDavid van Moolenbroek 				process_sit(client, &optbuf, optlen);
1794*00b67f09SDavid van Moolenbroek 				break;
1795*00b67f09SDavid van Moolenbroek #endif
1796*00b67f09SDavid van Moolenbroek 			case DNS_OPT_EXPIRE:
1797*00b67f09SDavid van Moolenbroek 				isc_stats_increment(ns_g_server->nsstats,
1798*00b67f09SDavid van Moolenbroek 						  dns_nsstatscounter_expireopt);
1799*00b67f09SDavid van Moolenbroek 				client->attributes |= NS_CLIENTATTR_WANTEXPIRE;
1800*00b67f09SDavid van Moolenbroek 				isc_buffer_forward(&optbuf, optlen);
1801*00b67f09SDavid van Moolenbroek 				break;
1802*00b67f09SDavid van Moolenbroek 			default:
1803*00b67f09SDavid van Moolenbroek 				isc_stats_increment(ns_g_server->nsstats,
1804*00b67f09SDavid van Moolenbroek 						  dns_nsstatscounter_otheropt);
1805*00b67f09SDavid van Moolenbroek 				isc_buffer_forward(&optbuf, optlen);
1806*00b67f09SDavid van Moolenbroek 				break;
1807*00b67f09SDavid van Moolenbroek 			}
1808*00b67f09SDavid van Moolenbroek 		}
1809*00b67f09SDavid van Moolenbroek 	}
1810*00b67f09SDavid van Moolenbroek 
1811*00b67f09SDavid van Moolenbroek 	isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_edns0in);
1812*00b67f09SDavid van Moolenbroek 	client->attributes |= NS_CLIENTATTR_WANTOPT;
1813*00b67f09SDavid van Moolenbroek 
1814*00b67f09SDavid van Moolenbroek  cleanup:
1815*00b67f09SDavid van Moolenbroek 	return (result);
1816*00b67f09SDavid van Moolenbroek }
1817*00b67f09SDavid van Moolenbroek 
1818*00b67f09SDavid van Moolenbroek /*
1819*00b67f09SDavid van Moolenbroek  * Handle an incoming request event from the socket (UDP case)
1820*00b67f09SDavid van Moolenbroek  * or tcpmsg (TCP case).
1821*00b67f09SDavid van Moolenbroek  */
1822*00b67f09SDavid van Moolenbroek static void
client_request(isc_task_t * task,isc_event_t * event)1823*00b67f09SDavid van Moolenbroek client_request(isc_task_t *task, isc_event_t *event) {
1824*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
1825*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *sevent;
1826*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1827*00b67f09SDavid van Moolenbroek 	isc_result_t sigresult = ISC_R_SUCCESS;
1828*00b67f09SDavid van Moolenbroek 	isc_buffer_t *buffer;
1829*00b67f09SDavid van Moolenbroek 	isc_buffer_t tbuffer;
1830*00b67f09SDavid van Moolenbroek 	dns_view_t *view;
1831*00b67f09SDavid van Moolenbroek 	dns_rdataset_t *opt;
1832*00b67f09SDavid van Moolenbroek 	dns_name_t *signame;
1833*00b67f09SDavid van Moolenbroek 	isc_boolean_t ra;	/* Recursion available. */
1834*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
1835*00b67f09SDavid van Moolenbroek 	int match;
1836*00b67f09SDavid van Moolenbroek 	dns_messageid_t id;
1837*00b67f09SDavid van Moolenbroek 	unsigned int flags;
1838*00b67f09SDavid van Moolenbroek 	isc_boolean_t notimp;
1839*00b67f09SDavid van Moolenbroek 
1840*00b67f09SDavid van Moolenbroek 	REQUIRE(event != NULL);
1841*00b67f09SDavid van Moolenbroek 	client = event->ev_arg;
1842*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
1843*00b67f09SDavid van Moolenbroek 	REQUIRE(task == client->task);
1844*00b67f09SDavid van Moolenbroek 
1845*00b67f09SDavid van Moolenbroek 	INSIST(client->recursionquota == NULL);
1846*00b67f09SDavid van Moolenbroek 
1847*00b67f09SDavid van Moolenbroek 	INSIST(client->state == (TCP_CLIENT(client) ?
1848*00b67f09SDavid van Moolenbroek 				       NS_CLIENTSTATE_READING :
1849*00b67f09SDavid van Moolenbroek 				       NS_CLIENTSTATE_READY));
1850*00b67f09SDavid van Moolenbroek 
1851*00b67f09SDavid van Moolenbroek 	ns_client_requests++;
1852*00b67f09SDavid van Moolenbroek 
1853*00b67f09SDavid van Moolenbroek 	if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
1854*00b67f09SDavid van Moolenbroek 		INSIST(!TCP_CLIENT(client));
1855*00b67f09SDavid van Moolenbroek 		sevent = (isc_socketevent_t *)event;
1856*00b67f09SDavid van Moolenbroek 		REQUIRE(sevent == client->recvevent);
1857*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
1858*00b67f09SDavid van Moolenbroek 		isc_buffer_add(&tbuffer, sevent->n);
1859*00b67f09SDavid van Moolenbroek 		buffer = &tbuffer;
1860*00b67f09SDavid van Moolenbroek 		result = sevent->result;
1861*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS) {
1862*00b67f09SDavid van Moolenbroek 			client->peeraddr = sevent->address;
1863*00b67f09SDavid van Moolenbroek 			client->peeraddr_valid = ISC_TRUE;
1864*00b67f09SDavid van Moolenbroek 		}
1865*00b67f09SDavid van Moolenbroek 		if ((sevent->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) {
1866*00b67f09SDavid van Moolenbroek 			ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1867*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(90),
1868*00b67f09SDavid van Moolenbroek 			      "received DSCP %d", sevent->dscp);
1869*00b67f09SDavid van Moolenbroek 			if (client->dscp == -1)
1870*00b67f09SDavid van Moolenbroek 				client->dscp = sevent->dscp;
1871*00b67f09SDavid van Moolenbroek 		}
1872*00b67f09SDavid van Moolenbroek 		if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
1873*00b67f09SDavid van Moolenbroek 			client->attributes |= NS_CLIENTATTR_PKTINFO;
1874*00b67f09SDavid van Moolenbroek 			client->pktinfo = sevent->pktinfo;
1875*00b67f09SDavid van Moolenbroek 		}
1876*00b67f09SDavid van Moolenbroek 		if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
1877*00b67f09SDavid van Moolenbroek 			client->attributes |= NS_CLIENTATTR_MULTICAST;
1878*00b67f09SDavid van Moolenbroek 		client->nrecvs--;
1879*00b67f09SDavid van Moolenbroek 	} else {
1880*00b67f09SDavid van Moolenbroek 		INSIST(TCP_CLIENT(client));
1881*00b67f09SDavid van Moolenbroek 		REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
1882*00b67f09SDavid van Moolenbroek 		REQUIRE(event->ev_sender == &client->tcpmsg);
1883*00b67f09SDavid van Moolenbroek 		buffer = &client->tcpmsg.buffer;
1884*00b67f09SDavid van Moolenbroek 		result = client->tcpmsg.result;
1885*00b67f09SDavid van Moolenbroek 		INSIST(client->nreads == 1);
1886*00b67f09SDavid van Moolenbroek 		/*
1887*00b67f09SDavid van Moolenbroek 		 * client->peeraddr was set when the connection was accepted.
1888*00b67f09SDavid van Moolenbroek 		 */
1889*00b67f09SDavid van Moolenbroek 		client->nreads--;
1890*00b67f09SDavid van Moolenbroek 	}
1891*00b67f09SDavid van Moolenbroek 
1892*00b67f09SDavid van Moolenbroek 	if (exit_check(client))
1893*00b67f09SDavid van Moolenbroek 		goto cleanup;
1894*00b67f09SDavid van Moolenbroek 	client->state = client->newstate = NS_CLIENTSTATE_WORKING;
1895*00b67f09SDavid van Moolenbroek 
1896*00b67f09SDavid van Moolenbroek 	isc_task_getcurrenttime(task, &client->requesttime);
1897*00b67f09SDavid van Moolenbroek 	client->now = client->requesttime;
1898*00b67f09SDavid van Moolenbroek 
1899*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1900*00b67f09SDavid van Moolenbroek 		if (TCP_CLIENT(client)) {
1901*00b67f09SDavid van Moolenbroek 			ns_client_next(client, result);
1902*00b67f09SDavid van Moolenbroek 		} else {
1903*00b67f09SDavid van Moolenbroek 			if  (result != ISC_R_CANCELED)
1904*00b67f09SDavid van Moolenbroek 				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_CLIENT,
1905*00b67f09SDavid van Moolenbroek 					      NS_LOGMODULE_CLIENT,
1906*00b67f09SDavid van Moolenbroek 					      ISC_LOG_ERROR,
1907*00b67f09SDavid van Moolenbroek 					      "UDP client handler shutting "
1908*00b67f09SDavid van Moolenbroek 					      "down due to fatal receive "
1909*00b67f09SDavid van Moolenbroek 					      "error: %s",
1910*00b67f09SDavid van Moolenbroek 					      isc_result_totext(result));
1911*00b67f09SDavid van Moolenbroek 			isc_task_shutdown(client->task);
1912*00b67f09SDavid van Moolenbroek 		}
1913*00b67f09SDavid van Moolenbroek 		goto cleanup;
1914*00b67f09SDavid van Moolenbroek 	}
1915*00b67f09SDavid van Moolenbroek 
1916*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1917*00b67f09SDavid van Moolenbroek 
1918*00b67f09SDavid van Moolenbroek #if NS_CLIENT_DROPPORT
1919*00b67f09SDavid van Moolenbroek 	if (ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) ==
1920*00b67f09SDavid van Moolenbroek 	    DROPPORT_REQUEST) {
1921*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1922*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1923*00b67f09SDavid van Moolenbroek 			      "dropped request: suspicious port");
1924*00b67f09SDavid van Moolenbroek 		ns_client_next(client, ISC_R_SUCCESS);
1925*00b67f09SDavid van Moolenbroek 		goto cleanup;
1926*00b67f09SDavid van Moolenbroek 	}
1927*00b67f09SDavid van Moolenbroek #endif
1928*00b67f09SDavid van Moolenbroek 
1929*00b67f09SDavid van Moolenbroek 	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1930*00b67f09SDavid van Moolenbroek 		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1931*00b67f09SDavid van Moolenbroek 		      "%s request",
1932*00b67f09SDavid van Moolenbroek 		      TCP_CLIENT(client) ? "TCP" : "UDP");
1933*00b67f09SDavid van Moolenbroek 
1934*00b67f09SDavid van Moolenbroek 	/*
1935*00b67f09SDavid van Moolenbroek 	 * Check the blackhole ACL for UDP only, since TCP is done in
1936*00b67f09SDavid van Moolenbroek 	 * client_newconn.
1937*00b67f09SDavid van Moolenbroek 	 */
1938*00b67f09SDavid van Moolenbroek 	if (!TCP_CLIENT(client)) {
1939*00b67f09SDavid van Moolenbroek 
1940*00b67f09SDavid van Moolenbroek 		if (ns_g_server->blackholeacl != NULL &&
1941*00b67f09SDavid van Moolenbroek 		    dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
1942*00b67f09SDavid van Moolenbroek 				  &ns_g_server->aclenv,
1943*00b67f09SDavid van Moolenbroek 				  &match, NULL) == ISC_R_SUCCESS &&
1944*00b67f09SDavid van Moolenbroek 		    match > 0)
1945*00b67f09SDavid van Moolenbroek 		{
1946*00b67f09SDavid van Moolenbroek 			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1947*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1948*00b67f09SDavid van Moolenbroek 				      "blackholed UDP datagram");
1949*00b67f09SDavid van Moolenbroek 			ns_client_next(client, ISC_R_SUCCESS);
1950*00b67f09SDavid van Moolenbroek 			goto cleanup;
1951*00b67f09SDavid van Moolenbroek 		}
1952*00b67f09SDavid van Moolenbroek 	}
1953*00b67f09SDavid van Moolenbroek 
1954*00b67f09SDavid van Moolenbroek 	/*
1955*00b67f09SDavid van Moolenbroek 	 * Silently drop multicast requests for the present.
1956*00b67f09SDavid van Moolenbroek 	 * XXXMPA revisit this as mDNS spec was published.
1957*00b67f09SDavid van Moolenbroek 	 */
1958*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
1959*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1960*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
1961*00b67f09SDavid van Moolenbroek 			      "dropping multicast request");
1962*00b67f09SDavid van Moolenbroek 		ns_client_next(client, DNS_R_REFUSED);
1963*00b67f09SDavid van Moolenbroek 		goto cleanup;
1964*00b67f09SDavid van Moolenbroek 	}
1965*00b67f09SDavid van Moolenbroek 
1966*00b67f09SDavid van Moolenbroek 	result = dns_message_peekheader(buffer, &id, &flags);
1967*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1968*00b67f09SDavid van Moolenbroek 		/*
1969*00b67f09SDavid van Moolenbroek 		 * There isn't enough header to determine whether
1970*00b67f09SDavid van Moolenbroek 		 * this was a request or a response.  Drop it.
1971*00b67f09SDavid van Moolenbroek 		 */
1972*00b67f09SDavid van Moolenbroek 		ns_client_next(client, result);
1973*00b67f09SDavid van Moolenbroek 		goto cleanup;
1974*00b67f09SDavid van Moolenbroek 	}
1975*00b67f09SDavid van Moolenbroek 
1976*00b67f09SDavid van Moolenbroek 	/*
1977*00b67f09SDavid van Moolenbroek 	 * The client object handles requests, not responses.
1978*00b67f09SDavid van Moolenbroek 	 * If this is a UDP response, forward it to the dispatcher.
1979*00b67f09SDavid van Moolenbroek 	 * If it's a TCP response, discard it here.
1980*00b67f09SDavid van Moolenbroek 	 */
1981*00b67f09SDavid van Moolenbroek 	if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
1982*00b67f09SDavid van Moolenbroek 		if (TCP_CLIENT(client)) {
1983*00b67f09SDavid van Moolenbroek 			CTRACE("unexpected response");
1984*00b67f09SDavid van Moolenbroek 			ns_client_next(client, DNS_R_FORMERR);
1985*00b67f09SDavid van Moolenbroek 			goto cleanup;
1986*00b67f09SDavid van Moolenbroek 		} else {
1987*00b67f09SDavid van Moolenbroek 			dns_dispatch_importrecv(client->dispatch, event);
1988*00b67f09SDavid van Moolenbroek 			ns_client_next(client, ISC_R_SUCCESS);
1989*00b67f09SDavid van Moolenbroek 			goto cleanup;
1990*00b67f09SDavid van Moolenbroek 		}
1991*00b67f09SDavid van Moolenbroek 	}
1992*00b67f09SDavid van Moolenbroek 
1993*00b67f09SDavid van Moolenbroek 	/*
1994*00b67f09SDavid van Moolenbroek 	 * Update some statistics counters.  Don't count responses.
1995*00b67f09SDavid van Moolenbroek 	 */
1996*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_pf(&client->peeraddr) == PF_INET) {
1997*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
1998*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_requestv4);
1999*00b67f09SDavid van Moolenbroek 	} else {
2000*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
2001*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_requestv6);
2002*00b67f09SDavid van Moolenbroek 	}
2003*00b67f09SDavid van Moolenbroek 	if (TCP_CLIENT(client))
2004*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
2005*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_requesttcp);
2006*00b67f09SDavid van Moolenbroek 
2007*00b67f09SDavid van Moolenbroek 	/*
2008*00b67f09SDavid van Moolenbroek 	 * It's a request.  Parse it.
2009*00b67f09SDavid van Moolenbroek 	 */
2010*00b67f09SDavid van Moolenbroek 	result = dns_message_parse(client->message, buffer, 0);
2011*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2012*00b67f09SDavid van Moolenbroek 		/*
2013*00b67f09SDavid van Moolenbroek 		 * Parsing the request failed.  Send a response
2014*00b67f09SDavid van Moolenbroek 		 * (typically FORMERR or SERVFAIL).
2015*00b67f09SDavid van Moolenbroek 		 */
2016*00b67f09SDavid van Moolenbroek 		ns_client_error(client, result);
2017*00b67f09SDavid van Moolenbroek 		goto cleanup;
2018*00b67f09SDavid van Moolenbroek 	}
2019*00b67f09SDavid van Moolenbroek 
2020*00b67f09SDavid van Moolenbroek 	dns_opcodestats_increment(ns_g_server->opcodestats,
2021*00b67f09SDavid van Moolenbroek 				  client->message->opcode);
2022*00b67f09SDavid van Moolenbroek 	switch (client->message->opcode) {
2023*00b67f09SDavid van Moolenbroek 	case dns_opcode_query:
2024*00b67f09SDavid van Moolenbroek 	case dns_opcode_update:
2025*00b67f09SDavid van Moolenbroek 	case dns_opcode_notify:
2026*00b67f09SDavid van Moolenbroek 		notimp = ISC_FALSE;
2027*00b67f09SDavid van Moolenbroek 		break;
2028*00b67f09SDavid van Moolenbroek 	case dns_opcode_iquery:
2029*00b67f09SDavid van Moolenbroek 	default:
2030*00b67f09SDavid van Moolenbroek 		notimp = ISC_TRUE;
2031*00b67f09SDavid van Moolenbroek 		break;
2032*00b67f09SDavid van Moolenbroek 	}
2033*00b67f09SDavid van Moolenbroek 
2034*00b67f09SDavid van Moolenbroek 	client->message->rcode = dns_rcode_noerror;
2035*00b67f09SDavid van Moolenbroek 
2036*00b67f09SDavid van Moolenbroek 	/* RFC1123 section 6.1.3.2 */
2037*00b67f09SDavid van Moolenbroek 	if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
2038*00b67f09SDavid van Moolenbroek 		client->message->flags &= ~DNS_MESSAGEFLAG_RD;
2039*00b67f09SDavid van Moolenbroek 
2040*00b67f09SDavid van Moolenbroek 	/*
2041*00b67f09SDavid van Moolenbroek 	 * Deal with EDNS.
2042*00b67f09SDavid van Moolenbroek 	 */
2043*00b67f09SDavid van Moolenbroek 	if (ns_g_noedns)
2044*00b67f09SDavid van Moolenbroek 		opt = NULL;
2045*00b67f09SDavid van Moolenbroek 	else
2046*00b67f09SDavid van Moolenbroek 		opt = dns_message_getopt(client->message);
2047*00b67f09SDavid van Moolenbroek 	if (opt != NULL) {
2048*00b67f09SDavid van Moolenbroek 		/*
2049*00b67f09SDavid van Moolenbroek 		 * Are we dropping all EDNS queries?
2050*00b67f09SDavid van Moolenbroek 		 */
2051*00b67f09SDavid van Moolenbroek 		if (ns_g_dropedns) {
2052*00b67f09SDavid van Moolenbroek 			ns_client_next(client, ISC_R_SUCCESS);
2053*00b67f09SDavid van Moolenbroek 			goto cleanup;
2054*00b67f09SDavid van Moolenbroek 		}
2055*00b67f09SDavid van Moolenbroek 		result = process_opt(client, opt);
2056*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
2057*00b67f09SDavid van Moolenbroek 			goto cleanup;
2058*00b67f09SDavid van Moolenbroek 	}
2059*00b67f09SDavid van Moolenbroek 
2060*00b67f09SDavid van Moolenbroek 	if (client->message->rdclass == 0) {
2061*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2062*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
2063*00b67f09SDavid van Moolenbroek 			      "message class could not be determined");
2064*00b67f09SDavid van Moolenbroek 		ns_client_dumpmessage(client,
2065*00b67f09SDavid van Moolenbroek 				      "message class could not be determined");
2066*00b67f09SDavid van Moolenbroek 		ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
2067*00b67f09SDavid van Moolenbroek 		goto cleanup;
2068*00b67f09SDavid van Moolenbroek 	}
2069*00b67f09SDavid van Moolenbroek 
2070*00b67f09SDavid van Moolenbroek 	/*
2071*00b67f09SDavid van Moolenbroek 	 * Determine the destination address.  If the receiving interface is
2072*00b67f09SDavid van Moolenbroek 	 * bound to a specific address, we simply use it regardless of the
2073*00b67f09SDavid van Moolenbroek 	 * address family.  All IPv4 queries should fall into this case.
2074*00b67f09SDavid van Moolenbroek 	 * Otherwise, if this is a TCP query, get the address from the
2075*00b67f09SDavid van Moolenbroek 	 * receiving socket (this needs a system call and can be heavy).
2076*00b67f09SDavid van Moolenbroek 	 * For IPv6 UDP queries, we get this from the pktinfo structure (if
2077*00b67f09SDavid van Moolenbroek 	 * supported).
2078*00b67f09SDavid van Moolenbroek 	 * If all the attempts fail (this can happen due to memory shortage,
2079*00b67f09SDavid van Moolenbroek 	 * etc), we regard this as an error for safety.
2080*00b67f09SDavid van Moolenbroek 	 */
2081*00b67f09SDavid van Moolenbroek 	if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
2082*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&client->destaddr,
2083*00b67f09SDavid van Moolenbroek 					 &client->interface->addr);
2084*00b67f09SDavid van Moolenbroek 	else {
2085*00b67f09SDavid van Moolenbroek 		isc_sockaddr_t sockaddr;
2086*00b67f09SDavid van Moolenbroek 		result = ISC_R_FAILURE;
2087*00b67f09SDavid van Moolenbroek 
2088*00b67f09SDavid van Moolenbroek 		if (TCP_CLIENT(client))
2089*00b67f09SDavid van Moolenbroek 			result = isc_socket_getsockname(client->tcpsocket,
2090*00b67f09SDavid van Moolenbroek 							&sockaddr);
2091*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
2092*00b67f09SDavid van Moolenbroek 			isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
2093*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS &&
2094*00b67f09SDavid van Moolenbroek 		    client->interface->addr.type.sa.sa_family == AF_INET6 &&
2095*00b67f09SDavid van Moolenbroek 		    (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
2096*00b67f09SDavid van Moolenbroek 			/*
2097*00b67f09SDavid van Moolenbroek 			 * XXXJT technically, we should convert the receiving
2098*00b67f09SDavid van Moolenbroek 			 * interface ID to a proper scope zone ID.  However,
2099*00b67f09SDavid van Moolenbroek 			 * due to the fact there is no standard API for this,
2100*00b67f09SDavid van Moolenbroek 			 * we only handle link-local addresses and use the
2101*00b67f09SDavid van Moolenbroek 			 * interface index as link ID.  Despite the assumption,
2102*00b67f09SDavid van Moolenbroek 			 * it should cover most typical cases.
2103*00b67f09SDavid van Moolenbroek 			 */
2104*00b67f09SDavid van Moolenbroek 			isc_netaddr_fromin6(&client->destaddr,
2105*00b67f09SDavid van Moolenbroek 					    &client->pktinfo.ipi6_addr);
2106*00b67f09SDavid van Moolenbroek 			if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
2107*00b67f09SDavid van Moolenbroek 				isc_netaddr_setzone(&client->destaddr,
2108*00b67f09SDavid van Moolenbroek 						client->pktinfo.ipi6_ifindex);
2109*00b67f09SDavid van Moolenbroek 			result = ISC_R_SUCCESS;
2110*00b67f09SDavid van Moolenbroek 		}
2111*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
2112*00b67f09SDavid van Moolenbroek 			UNEXPECTED_ERROR(__FILE__, __LINE__,
2113*00b67f09SDavid van Moolenbroek 					 "failed to get request's "
2114*00b67f09SDavid van Moolenbroek 					 "destination: %s",
2115*00b67f09SDavid van Moolenbroek 					 isc_result_totext(result));
2116*00b67f09SDavid van Moolenbroek 			ns_client_next(client, ISC_R_SUCCESS);
2117*00b67f09SDavid van Moolenbroek 			goto cleanup;
2118*00b67f09SDavid van Moolenbroek 		}
2119*00b67f09SDavid van Moolenbroek 	}
2120*00b67f09SDavid van Moolenbroek 
2121*00b67f09SDavid van Moolenbroek 	/*
2122*00b67f09SDavid van Moolenbroek 	 * Find a view that matches the client's source address.
2123*00b67f09SDavid van Moolenbroek 	 */
2124*00b67f09SDavid van Moolenbroek 	for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
2125*00b67f09SDavid van Moolenbroek 	     view != NULL;
2126*00b67f09SDavid van Moolenbroek 	     view = ISC_LIST_NEXT(view, link)) {
2127*00b67f09SDavid van Moolenbroek 		if (client->message->rdclass == view->rdclass ||
2128*00b67f09SDavid van Moolenbroek 		    client->message->rdclass == dns_rdataclass_any)
2129*00b67f09SDavid van Moolenbroek 		{
2130*00b67f09SDavid van Moolenbroek 			dns_name_t *tsig = NULL;
2131*00b67f09SDavid van Moolenbroek 
2132*00b67f09SDavid van Moolenbroek 			sigresult = dns_message_rechecksig(client->message,
2133*00b67f09SDavid van Moolenbroek 							   view);
2134*00b67f09SDavid van Moolenbroek 			if (sigresult == ISC_R_SUCCESS)
2135*00b67f09SDavid van Moolenbroek 				tsig = dns_tsigkey_identity(client->message->tsigkey);
2136*00b67f09SDavid van Moolenbroek 
2137*00b67f09SDavid van Moolenbroek 			if (allowed(&netaddr, tsig, view->matchclients) &&
2138*00b67f09SDavid van Moolenbroek 			    allowed(&client->destaddr, tsig,
2139*00b67f09SDavid van Moolenbroek 				    view->matchdestinations) &&
2140*00b67f09SDavid van Moolenbroek 			    !((client->message->flags & DNS_MESSAGEFLAG_RD)
2141*00b67f09SDavid van Moolenbroek 			      == 0 && view->matchrecursiveonly))
2142*00b67f09SDavid van Moolenbroek 			{
2143*00b67f09SDavid van Moolenbroek 				dns_view_attach(view, &client->view);
2144*00b67f09SDavid van Moolenbroek 				break;
2145*00b67f09SDavid van Moolenbroek 			}
2146*00b67f09SDavid van Moolenbroek 		}
2147*00b67f09SDavid van Moolenbroek 	}
2148*00b67f09SDavid van Moolenbroek 
2149*00b67f09SDavid van Moolenbroek 	if (view == NULL) {
2150*00b67f09SDavid van Moolenbroek 		char classname[DNS_RDATACLASS_FORMATSIZE];
2151*00b67f09SDavid van Moolenbroek 
2152*00b67f09SDavid van Moolenbroek 		/*
2153*00b67f09SDavid van Moolenbroek 		 * Do a dummy TSIG verification attempt so that the
2154*00b67f09SDavid van Moolenbroek 		 * response will have a TSIG if the query did, as
2155*00b67f09SDavid van Moolenbroek 		 * required by RFC2845.
2156*00b67f09SDavid van Moolenbroek 		 */
2157*00b67f09SDavid van Moolenbroek 		isc_buffer_t b;
2158*00b67f09SDavid van Moolenbroek 		isc_region_t *r;
2159*00b67f09SDavid van Moolenbroek 
2160*00b67f09SDavid van Moolenbroek 		dns_message_resetsig(client->message);
2161*00b67f09SDavid van Moolenbroek 
2162*00b67f09SDavid van Moolenbroek 		r = dns_message_getrawmessage(client->message);
2163*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&b, r->base, r->length);
2164*00b67f09SDavid van Moolenbroek 		isc_buffer_add(&b, r->length);
2165*00b67f09SDavid van Moolenbroek 		(void)dns_tsig_verify(&b, client->message, NULL, NULL);
2166*00b67f09SDavid van Moolenbroek 
2167*00b67f09SDavid van Moolenbroek 		dns_rdataclass_format(client->message->rdclass, classname,
2168*00b67f09SDavid van Moolenbroek 				      sizeof(classname));
2169*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2170*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
2171*00b67f09SDavid van Moolenbroek 			      "no matching view in class '%s'", classname);
2172*00b67f09SDavid van Moolenbroek 		ns_client_dumpmessage(client, "no matching view in class");
2173*00b67f09SDavid van Moolenbroek 		ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
2174*00b67f09SDavid van Moolenbroek 		goto cleanup;
2175*00b67f09SDavid van Moolenbroek 	}
2176*00b67f09SDavid van Moolenbroek 
2177*00b67f09SDavid van Moolenbroek 	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2178*00b67f09SDavid van Moolenbroek 		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
2179*00b67f09SDavid van Moolenbroek 		      "using view '%s'", view->name);
2180*00b67f09SDavid van Moolenbroek 
2181*00b67f09SDavid van Moolenbroek 	/*
2182*00b67f09SDavid van Moolenbroek 	 * Check for a signature.  We log bad signatures regardless of
2183*00b67f09SDavid van Moolenbroek 	 * whether they ultimately cause the request to be rejected or
2184*00b67f09SDavid van Moolenbroek 	 * not.  We do not log the lack of a signature unless we are
2185*00b67f09SDavid van Moolenbroek 	 * debugging.
2186*00b67f09SDavid van Moolenbroek 	 */
2187*00b67f09SDavid van Moolenbroek 	client->signer = NULL;
2188*00b67f09SDavid van Moolenbroek 	dns_name_init(&client->signername, NULL);
2189*00b67f09SDavid van Moolenbroek 	result = dns_message_signer(client->message, &client->signername);
2190*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_NOTFOUND) {
2191*00b67f09SDavid van Moolenbroek 		signame = NULL;
2192*00b67f09SDavid van Moolenbroek 		if (dns_message_gettsig(client->message, &signame) != NULL) {
2193*00b67f09SDavid van Moolenbroek 			isc_stats_increment(ns_g_server->nsstats,
2194*00b67f09SDavid van Moolenbroek 					    dns_nsstatscounter_tsigin);
2195*00b67f09SDavid van Moolenbroek 		} else {
2196*00b67f09SDavid van Moolenbroek 			isc_stats_increment(ns_g_server->nsstats,
2197*00b67f09SDavid van Moolenbroek 					    dns_nsstatscounter_sig0in);
2198*00b67f09SDavid van Moolenbroek 		}
2199*00b67f09SDavid van Moolenbroek 
2200*00b67f09SDavid van Moolenbroek 	}
2201*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
2202*00b67f09SDavid van Moolenbroek 		char namebuf[DNS_NAME_FORMATSIZE];
2203*00b67f09SDavid van Moolenbroek 		dns_name_format(&client->signername, namebuf, sizeof(namebuf));
2204*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2205*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2206*00b67f09SDavid van Moolenbroek 			      "request has valid signature: %s", namebuf);
2207*00b67f09SDavid van Moolenbroek 		client->signer = &client->signername;
2208*00b67f09SDavid van Moolenbroek 	} else if (result == ISC_R_NOTFOUND) {
2209*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2210*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2211*00b67f09SDavid van Moolenbroek 			      "request is not signed");
2212*00b67f09SDavid van Moolenbroek 	} else if (result == DNS_R_NOIDENTITY) {
2213*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2214*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2215*00b67f09SDavid van Moolenbroek 			      "request is signed by a nonauthoritative key");
2216*00b67f09SDavid van Moolenbroek 	} else {
2217*00b67f09SDavid van Moolenbroek 		char tsigrcode[64];
2218*00b67f09SDavid van Moolenbroek 		isc_buffer_t b;
2219*00b67f09SDavid van Moolenbroek 		dns_rcode_t status;
2220*00b67f09SDavid van Moolenbroek 		isc_result_t tresult;
2221*00b67f09SDavid van Moolenbroek 
2222*00b67f09SDavid van Moolenbroek 		/* There is a signature, but it is bad. */
2223*00b67f09SDavid van Moolenbroek 		isc_stats_increment(ns_g_server->nsstats,
2224*00b67f09SDavid van Moolenbroek 				    dns_nsstatscounter_invalidsig);
2225*00b67f09SDavid van Moolenbroek 		signame = NULL;
2226*00b67f09SDavid van Moolenbroek 		if (dns_message_gettsig(client->message, &signame) != NULL) {
2227*00b67f09SDavid van Moolenbroek 			char namebuf[DNS_NAME_FORMATSIZE];
2228*00b67f09SDavid van Moolenbroek 			char cnamebuf[DNS_NAME_FORMATSIZE];
2229*00b67f09SDavid van Moolenbroek 			dns_name_format(signame, namebuf, sizeof(namebuf));
2230*00b67f09SDavid van Moolenbroek 			status = client->message->tsigstatus;
2231*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
2232*00b67f09SDavid van Moolenbroek 			tresult = dns_tsigrcode_totext(status, &b);
2233*00b67f09SDavid van Moolenbroek 			INSIST(tresult == ISC_R_SUCCESS);
2234*00b67f09SDavid van Moolenbroek 			tsigrcode[isc_buffer_usedlength(&b)] = '\0';
2235*00b67f09SDavid van Moolenbroek 			if (client->message->tsigkey->generated) {
2236*00b67f09SDavid van Moolenbroek 				dns_name_format(client->message->tsigkey->creator,
2237*00b67f09SDavid van Moolenbroek 						cnamebuf, sizeof(cnamebuf));
2238*00b67f09SDavid van Moolenbroek 				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2239*00b67f09SDavid van Moolenbroek 					      NS_LOGMODULE_CLIENT,
2240*00b67f09SDavid van Moolenbroek 					      ISC_LOG_ERROR,
2241*00b67f09SDavid van Moolenbroek 					      "request has invalid signature: "
2242*00b67f09SDavid van Moolenbroek 					      "TSIG %s (%s): %s (%s)", namebuf,
2243*00b67f09SDavid van Moolenbroek 					      cnamebuf,
2244*00b67f09SDavid van Moolenbroek 					      isc_result_totext(result),
2245*00b67f09SDavid van Moolenbroek 					      tsigrcode);
2246*00b67f09SDavid van Moolenbroek 			} else {
2247*00b67f09SDavid van Moolenbroek 				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2248*00b67f09SDavid van Moolenbroek 					      NS_LOGMODULE_CLIENT,
2249*00b67f09SDavid van Moolenbroek 					      ISC_LOG_ERROR,
2250*00b67f09SDavid van Moolenbroek 					      "request has invalid signature: "
2251*00b67f09SDavid van Moolenbroek 					      "TSIG %s: %s (%s)", namebuf,
2252*00b67f09SDavid van Moolenbroek 					      isc_result_totext(result),
2253*00b67f09SDavid van Moolenbroek 					      tsigrcode);
2254*00b67f09SDavid van Moolenbroek 			}
2255*00b67f09SDavid van Moolenbroek 		} else {
2256*00b67f09SDavid van Moolenbroek 			status = client->message->sig0status;
2257*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
2258*00b67f09SDavid van Moolenbroek 			tresult = dns_tsigrcode_totext(status, &b);
2259*00b67f09SDavid van Moolenbroek 			INSIST(tresult == ISC_R_SUCCESS);
2260*00b67f09SDavid van Moolenbroek 			tsigrcode[isc_buffer_usedlength(&b)] = '\0';
2261*00b67f09SDavid van Moolenbroek 			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2262*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
2263*00b67f09SDavid van Moolenbroek 				      "request has invalid signature: %s (%s)",
2264*00b67f09SDavid van Moolenbroek 				      isc_result_totext(result), tsigrcode);
2265*00b67f09SDavid van Moolenbroek 		}
2266*00b67f09SDavid van Moolenbroek 		/*
2267*00b67f09SDavid van Moolenbroek 		 * Accept update messages signed by unknown keys so that
2268*00b67f09SDavid van Moolenbroek 		 * update forwarding works transparently through slaves
2269*00b67f09SDavid van Moolenbroek 		 * that don't have all the same keys as the master.
2270*00b67f09SDavid van Moolenbroek 		 */
2271*00b67f09SDavid van Moolenbroek 		if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
2272*00b67f09SDavid van Moolenbroek 		      client->message->opcode == dns_opcode_update)) {
2273*00b67f09SDavid van Moolenbroek 			ns_client_error(client, sigresult);
2274*00b67f09SDavid van Moolenbroek 			goto cleanup;
2275*00b67f09SDavid van Moolenbroek 		}
2276*00b67f09SDavid van Moolenbroek 	}
2277*00b67f09SDavid van Moolenbroek 
2278*00b67f09SDavid van Moolenbroek 	/*
2279*00b67f09SDavid van Moolenbroek 	 * Decide whether recursive service is available to this client.
2280*00b67f09SDavid van Moolenbroek 	 * We do this here rather than in the query code so that we can
2281*00b67f09SDavid van Moolenbroek 	 * set the RA bit correctly on all kinds of responses, not just
2282*00b67f09SDavid van Moolenbroek 	 * responses to ordinary queries.  Note if you can't query the
2283*00b67f09SDavid van Moolenbroek 	 * cache there is no point in setting RA.
2284*00b67f09SDavid van Moolenbroek 	 */
2285*00b67f09SDavid van Moolenbroek 	ra = ISC_FALSE;
2286*00b67f09SDavid van Moolenbroek 	if (client->view->resolver != NULL &&
2287*00b67f09SDavid van Moolenbroek 	    client->view->recursion == ISC_TRUE &&
2288*00b67f09SDavid van Moolenbroek 	    ns_client_checkaclsilent(client, NULL,
2289*00b67f09SDavid van Moolenbroek 				     client->view->recursionacl,
2290*00b67f09SDavid van Moolenbroek 				     ISC_TRUE) == ISC_R_SUCCESS &&
2291*00b67f09SDavid van Moolenbroek 	    ns_client_checkaclsilent(client, NULL,
2292*00b67f09SDavid van Moolenbroek 				     client->view->cacheacl,
2293*00b67f09SDavid van Moolenbroek 				     ISC_TRUE) == ISC_R_SUCCESS &&
2294*00b67f09SDavid van Moolenbroek 	    ns_client_checkaclsilent(client, &client->destaddr,
2295*00b67f09SDavid van Moolenbroek 				     client->view->recursiononacl,
2296*00b67f09SDavid van Moolenbroek 				     ISC_TRUE) == ISC_R_SUCCESS &&
2297*00b67f09SDavid van Moolenbroek 	    ns_client_checkaclsilent(client, &client->destaddr,
2298*00b67f09SDavid van Moolenbroek 				     client->view->cacheonacl,
2299*00b67f09SDavid van Moolenbroek 				     ISC_TRUE) == ISC_R_SUCCESS)
2300*00b67f09SDavid van Moolenbroek 		ra = ISC_TRUE;
2301*00b67f09SDavid van Moolenbroek 
2302*00b67f09SDavid van Moolenbroek 	if (ra == ISC_TRUE)
2303*00b67f09SDavid van Moolenbroek 		client->attributes |= NS_CLIENTATTR_RA;
2304*00b67f09SDavid van Moolenbroek 
2305*00b67f09SDavid van Moolenbroek 	ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT,
2306*00b67f09SDavid van Moolenbroek 		      ISC_LOG_DEBUG(3), ra ? "recursion available" :
2307*00b67f09SDavid van Moolenbroek 					     "recursion not available");
2308*00b67f09SDavid van Moolenbroek 
2309*00b67f09SDavid van Moolenbroek 	/*
2310*00b67f09SDavid van Moolenbroek 	 * Adjust maximum UDP response size for this client.
2311*00b67f09SDavid van Moolenbroek 	 */
2312*00b67f09SDavid van Moolenbroek 	if (client->udpsize > 512) {
2313*00b67f09SDavid van Moolenbroek 		dns_peer_t *peer = NULL;
2314*00b67f09SDavid van Moolenbroek 		isc_uint16_t udpsize = view->maxudp;
2315*00b67f09SDavid van Moolenbroek 		(void) dns_peerlist_peerbyaddr(view->peers, &netaddr, &peer);
2316*00b67f09SDavid van Moolenbroek 		if (peer != NULL)
2317*00b67f09SDavid van Moolenbroek 			dns_peer_getmaxudp(peer, &udpsize);
2318*00b67f09SDavid van Moolenbroek 		if (client->udpsize > udpsize)
2319*00b67f09SDavid van Moolenbroek 			client->udpsize = udpsize;
2320*00b67f09SDavid van Moolenbroek 	}
2321*00b67f09SDavid van Moolenbroek 
2322*00b67f09SDavid van Moolenbroek 	/*
2323*00b67f09SDavid van Moolenbroek 	 * Dispatch the request.
2324*00b67f09SDavid van Moolenbroek 	 */
2325*00b67f09SDavid van Moolenbroek 	switch (client->message->opcode) {
2326*00b67f09SDavid van Moolenbroek 	case dns_opcode_query:
2327*00b67f09SDavid van Moolenbroek 		CTRACE("query");
2328*00b67f09SDavid van Moolenbroek 		ns_query_start(client);
2329*00b67f09SDavid van Moolenbroek 		break;
2330*00b67f09SDavid van Moolenbroek 	case dns_opcode_update:
2331*00b67f09SDavid van Moolenbroek 		CTRACE("update");
2332*00b67f09SDavid van Moolenbroek 		ns_client_settimeout(client, 60);
2333*00b67f09SDavid van Moolenbroek 		ns_update_start(client, sigresult);
2334*00b67f09SDavid van Moolenbroek 		break;
2335*00b67f09SDavid van Moolenbroek 	case dns_opcode_notify:
2336*00b67f09SDavid van Moolenbroek 		CTRACE("notify");
2337*00b67f09SDavid van Moolenbroek 		ns_client_settimeout(client, 60);
2338*00b67f09SDavid van Moolenbroek 		ns_notify_start(client);
2339*00b67f09SDavid van Moolenbroek 		break;
2340*00b67f09SDavid van Moolenbroek 	case dns_opcode_iquery:
2341*00b67f09SDavid van Moolenbroek 		CTRACE("iquery");
2342*00b67f09SDavid van Moolenbroek 		ns_client_error(client, DNS_R_NOTIMP);
2343*00b67f09SDavid van Moolenbroek 		break;
2344*00b67f09SDavid van Moolenbroek 	default:
2345*00b67f09SDavid van Moolenbroek 		CTRACE("unknown opcode");
2346*00b67f09SDavid van Moolenbroek 		ns_client_error(client, DNS_R_NOTIMP);
2347*00b67f09SDavid van Moolenbroek 	}
2348*00b67f09SDavid van Moolenbroek 
2349*00b67f09SDavid van Moolenbroek  cleanup:
2350*00b67f09SDavid van Moolenbroek 	return;
2351*00b67f09SDavid van Moolenbroek }
2352*00b67f09SDavid van Moolenbroek 
2353*00b67f09SDavid van Moolenbroek static void
client_timeout(isc_task_t * task,isc_event_t * event)2354*00b67f09SDavid van Moolenbroek client_timeout(isc_task_t *task, isc_event_t *event) {
2355*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
2356*00b67f09SDavid van Moolenbroek 
2357*00b67f09SDavid van Moolenbroek 	REQUIRE(event != NULL);
2358*00b67f09SDavid van Moolenbroek 	REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
2359*00b67f09SDavid van Moolenbroek 		event->ev_type == ISC_TIMEREVENT_IDLE);
2360*00b67f09SDavid van Moolenbroek 	client = event->ev_arg;
2361*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
2362*00b67f09SDavid van Moolenbroek 	REQUIRE(task == client->task);
2363*00b67f09SDavid van Moolenbroek 	REQUIRE(client->timer != NULL);
2364*00b67f09SDavid van Moolenbroek 
2365*00b67f09SDavid van Moolenbroek 	UNUSED(task);
2366*00b67f09SDavid van Moolenbroek 
2367*00b67f09SDavid van Moolenbroek 	CTRACE("timeout");
2368*00b67f09SDavid van Moolenbroek 
2369*00b67f09SDavid van Moolenbroek 	isc_event_free(&event);
2370*00b67f09SDavid van Moolenbroek 
2371*00b67f09SDavid van Moolenbroek 	if (client->shutdown != NULL) {
2372*00b67f09SDavid van Moolenbroek 		(client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
2373*00b67f09SDavid van Moolenbroek 		client->shutdown = NULL;
2374*00b67f09SDavid van Moolenbroek 		client->shutdown_arg = NULL;
2375*00b67f09SDavid van Moolenbroek 	}
2376*00b67f09SDavid van Moolenbroek 
2377*00b67f09SDavid van Moolenbroek 	if (client->newstate > NS_CLIENTSTATE_READY)
2378*00b67f09SDavid van Moolenbroek 		client->newstate = NS_CLIENTSTATE_READY;
2379*00b67f09SDavid van Moolenbroek 	(void)exit_check(client);
2380*00b67f09SDavid van Moolenbroek }
2381*00b67f09SDavid van Moolenbroek 
2382*00b67f09SDavid van Moolenbroek static isc_result_t
get_clientmctx(ns_clientmgr_t * manager,isc_mem_t ** mctxp)2383*00b67f09SDavid van Moolenbroek get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
2384*00b67f09SDavid van Moolenbroek 	isc_mem_t *clientmctx;
2385*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2386*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2387*00b67f09SDavid van Moolenbroek 	unsigned int nextmctx;
2388*00b67f09SDavid van Moolenbroek #endif
2389*00b67f09SDavid van Moolenbroek 
2390*00b67f09SDavid van Moolenbroek 	MTRACE("clientmctx");
2391*00b67f09SDavid van Moolenbroek 
2392*00b67f09SDavid van Moolenbroek 	/*
2393*00b67f09SDavid van Moolenbroek 	 * Caller must be holding the manager lock.
2394*00b67f09SDavid van Moolenbroek 	 */
2395*00b67f09SDavid van Moolenbroek 	if (ns_g_clienttest) {
2396*00b67f09SDavid van Moolenbroek 		result = isc_mem_create(0, 0, mctxp);
2397*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
2398*00b67f09SDavid van Moolenbroek 			isc_mem_setname(*mctxp, "client", NULL);
2399*00b67f09SDavid van Moolenbroek 		return (result);
2400*00b67f09SDavid van Moolenbroek 	}
2401*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2402*00b67f09SDavid van Moolenbroek 	nextmctx = manager->nextmctx++;
2403*00b67f09SDavid van Moolenbroek 	if (manager->nextmctx == NMCTXS)
2404*00b67f09SDavid van Moolenbroek 		manager->nextmctx = 0;
2405*00b67f09SDavid van Moolenbroek 
2406*00b67f09SDavid van Moolenbroek 	INSIST(nextmctx < NMCTXS);
2407*00b67f09SDavid van Moolenbroek 
2408*00b67f09SDavid van Moolenbroek 	clientmctx = manager->mctxpool[nextmctx];
2409*00b67f09SDavid van Moolenbroek 	if (clientmctx == NULL) {
2410*00b67f09SDavid van Moolenbroek 		result = isc_mem_create(0, 0, &clientmctx);
2411*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
2412*00b67f09SDavid van Moolenbroek 			return (result);
2413*00b67f09SDavid van Moolenbroek 		isc_mem_setname(clientmctx, "client", NULL);
2414*00b67f09SDavid van Moolenbroek 
2415*00b67f09SDavid van Moolenbroek 		manager->mctxpool[nextmctx] = clientmctx;
2416*00b67f09SDavid van Moolenbroek 	}
2417*00b67f09SDavid van Moolenbroek #else
2418*00b67f09SDavid van Moolenbroek 	clientmctx = manager->mctx;
2419*00b67f09SDavid van Moolenbroek #endif
2420*00b67f09SDavid van Moolenbroek 
2421*00b67f09SDavid van Moolenbroek 	isc_mem_attach(clientmctx, mctxp);
2422*00b67f09SDavid van Moolenbroek 
2423*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2424*00b67f09SDavid van Moolenbroek }
2425*00b67f09SDavid van Moolenbroek 
2426*00b67f09SDavid van Moolenbroek static isc_result_t
client_create(ns_clientmgr_t * manager,ns_client_t ** clientp)2427*00b67f09SDavid van Moolenbroek client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
2428*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
2429*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2430*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx = NULL;
2431*00b67f09SDavid van Moolenbroek 
2432*00b67f09SDavid van Moolenbroek 	/*
2433*00b67f09SDavid van Moolenbroek 	 * Caller must be holding the manager lock.
2434*00b67f09SDavid van Moolenbroek 	 *
2435*00b67f09SDavid van Moolenbroek 	 * Note: creating a client does not add the client to the
2436*00b67f09SDavid van Moolenbroek 	 * manager's client list or set the client's manager pointer.
2437*00b67f09SDavid van Moolenbroek 	 * The caller is responsible for that.
2438*00b67f09SDavid van Moolenbroek 	 */
2439*00b67f09SDavid van Moolenbroek 
2440*00b67f09SDavid van Moolenbroek 	REQUIRE(clientp != NULL && *clientp == NULL);
2441*00b67f09SDavid van Moolenbroek 
2442*00b67f09SDavid van Moolenbroek 	result = get_clientmctx(manager, &mctx);
2443*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2444*00b67f09SDavid van Moolenbroek 		return (result);
2445*00b67f09SDavid van Moolenbroek 
2446*00b67f09SDavid van Moolenbroek 	client = isc_mem_get(mctx, sizeof(*client));
2447*00b67f09SDavid van Moolenbroek 	if (client == NULL) {
2448*00b67f09SDavid van Moolenbroek 		isc_mem_detach(&mctx);
2449*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
2450*00b67f09SDavid van Moolenbroek 	}
2451*00b67f09SDavid van Moolenbroek 	client->mctx = mctx;
2452*00b67f09SDavid van Moolenbroek 
2453*00b67f09SDavid van Moolenbroek 	client->task = NULL;
2454*00b67f09SDavid van Moolenbroek 	result = isc_task_create(manager->taskmgr, 0, &client->task);
2455*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2456*00b67f09SDavid van Moolenbroek 		goto cleanup_client;
2457*00b67f09SDavid van Moolenbroek 	isc_task_setname(client->task, "client", client);
2458*00b67f09SDavid van Moolenbroek 
2459*00b67f09SDavid van Moolenbroek 	client->timer = NULL;
2460*00b67f09SDavid van Moolenbroek 	result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
2461*00b67f09SDavid van Moolenbroek 				  NULL, NULL, client->task, client_timeout,
2462*00b67f09SDavid van Moolenbroek 				  client, &client->timer);
2463*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2464*00b67f09SDavid van Moolenbroek 		goto cleanup_task;
2465*00b67f09SDavid van Moolenbroek 	client->timerset = ISC_FALSE;
2466*00b67f09SDavid van Moolenbroek 
2467*00b67f09SDavid van Moolenbroek 	client->delaytimer = NULL;
2468*00b67f09SDavid van Moolenbroek 
2469*00b67f09SDavid van Moolenbroek 	client->message = NULL;
2470*00b67f09SDavid van Moolenbroek 	result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
2471*00b67f09SDavid van Moolenbroek 				    &client->message);
2472*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2473*00b67f09SDavid van Moolenbroek 		goto cleanup_timer;
2474*00b67f09SDavid van Moolenbroek 
2475*00b67f09SDavid van Moolenbroek 	/* XXXRTH  Hardwired constants */
2476*00b67f09SDavid van Moolenbroek 
2477*00b67f09SDavid van Moolenbroek 	client->sendevent = isc_socket_socketevent(client->mctx, client,
2478*00b67f09SDavid van Moolenbroek 						   ISC_SOCKEVENT_SENDDONE,
2479*00b67f09SDavid van Moolenbroek 						   client_senddone, client);
2480*00b67f09SDavid van Moolenbroek 	if (client->sendevent == NULL) {
2481*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2482*00b67f09SDavid van Moolenbroek 		goto cleanup_message;
2483*00b67f09SDavid van Moolenbroek 	}
2484*00b67f09SDavid van Moolenbroek 
2485*00b67f09SDavid van Moolenbroek 	client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
2486*00b67f09SDavid van Moolenbroek 	if  (client->recvbuf == NULL) {
2487*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2488*00b67f09SDavid van Moolenbroek 		goto cleanup_sendevent;
2489*00b67f09SDavid van Moolenbroek 	}
2490*00b67f09SDavid van Moolenbroek 
2491*00b67f09SDavid van Moolenbroek 	client->recvevent = isc_socket_socketevent(client->mctx, client,
2492*00b67f09SDavid van Moolenbroek 						   ISC_SOCKEVENT_RECVDONE,
2493*00b67f09SDavid van Moolenbroek 						   client_request, client);
2494*00b67f09SDavid van Moolenbroek 	if (client->recvevent == NULL) {
2495*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2496*00b67f09SDavid van Moolenbroek 		goto cleanup_recvbuf;
2497*00b67f09SDavid van Moolenbroek 	}
2498*00b67f09SDavid van Moolenbroek 
2499*00b67f09SDavid van Moolenbroek 	client->magic = NS_CLIENT_MAGIC;
2500*00b67f09SDavid van Moolenbroek 	client->manager = NULL;
2501*00b67f09SDavid van Moolenbroek 	client->state = NS_CLIENTSTATE_INACTIVE;
2502*00b67f09SDavid van Moolenbroek 	client->newstate = NS_CLIENTSTATE_MAX;
2503*00b67f09SDavid van Moolenbroek 	client->naccepts = 0;
2504*00b67f09SDavid van Moolenbroek 	client->nreads = 0;
2505*00b67f09SDavid van Moolenbroek 	client->nsends = 0;
2506*00b67f09SDavid van Moolenbroek 	client->nrecvs = 0;
2507*00b67f09SDavid van Moolenbroek 	client->nupdates = 0;
2508*00b67f09SDavid van Moolenbroek 	client->nctls = 0;
2509*00b67f09SDavid van Moolenbroek 	client->references = 0;
2510*00b67f09SDavid van Moolenbroek 	client->attributes = 0;
2511*00b67f09SDavid van Moolenbroek 	client->view = NULL;
2512*00b67f09SDavid van Moolenbroek 	client->dispatch = NULL;
2513*00b67f09SDavid van Moolenbroek 	client->udpsocket = NULL;
2514*00b67f09SDavid van Moolenbroek 	client->tcplistener = NULL;
2515*00b67f09SDavid van Moolenbroek 	client->tcpsocket = NULL;
2516*00b67f09SDavid van Moolenbroek 	client->tcpmsg_valid = ISC_FALSE;
2517*00b67f09SDavid van Moolenbroek 	client->tcpbuf = NULL;
2518*00b67f09SDavid van Moolenbroek 	client->opt = NULL;
2519*00b67f09SDavid van Moolenbroek 	client->udpsize = 512;
2520*00b67f09SDavid van Moolenbroek 	client->dscp = -1;
2521*00b67f09SDavid van Moolenbroek 	client->extflags = 0;
2522*00b67f09SDavid van Moolenbroek 	client->ednsversion = -1;
2523*00b67f09SDavid van Moolenbroek 	client->next = NULL;
2524*00b67f09SDavid van Moolenbroek 	client->shutdown = NULL;
2525*00b67f09SDavid van Moolenbroek 	client->shutdown_arg = NULL;
2526*00b67f09SDavid van Moolenbroek 	client->signer = NULL;
2527*00b67f09SDavid van Moolenbroek 	dns_name_init(&client->signername, NULL);
2528*00b67f09SDavid van Moolenbroek 	client->mortal = ISC_FALSE;
2529*00b67f09SDavid van Moolenbroek 	client->tcpquota = NULL;
2530*00b67f09SDavid van Moolenbroek 	client->recursionquota = NULL;
2531*00b67f09SDavid van Moolenbroek 	client->interface = NULL;
2532*00b67f09SDavid van Moolenbroek 	client->peeraddr_valid = ISC_FALSE;
2533*00b67f09SDavid van Moolenbroek #ifdef ALLOW_FILTER_AAAA
2534*00b67f09SDavid van Moolenbroek 	client->filter_aaaa = dns_aaaa_ok;
2535*00b67f09SDavid van Moolenbroek #endif
2536*00b67f09SDavid van Moolenbroek 	client->needshutdown = ns_g_clienttest;
2537*00b67f09SDavid van Moolenbroek 
2538*00b67f09SDavid van Moolenbroek 	ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
2539*00b67f09SDavid van Moolenbroek 		       NS_EVENT_CLIENTCONTROL, client_start, client, client,
2540*00b67f09SDavid van Moolenbroek 		       NULL, NULL);
2541*00b67f09SDavid van Moolenbroek 	/*
2542*00b67f09SDavid van Moolenbroek 	 * Initialize FORMERR cache to sentinel value that will not match
2543*00b67f09SDavid van Moolenbroek 	 * any actual FORMERR response.
2544*00b67f09SDavid van Moolenbroek 	 */
2545*00b67f09SDavid van Moolenbroek 	isc_sockaddr_any(&client->formerrcache.addr);
2546*00b67f09SDavid van Moolenbroek 	client->formerrcache.time = 0;
2547*00b67f09SDavid van Moolenbroek 	client->formerrcache.id = 0;
2548*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(client, link);
2549*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(client, rlink);
2550*00b67f09SDavid van Moolenbroek 	ISC_QLINK_INIT(client, ilink);
2551*00b67f09SDavid van Moolenbroek 
2552*00b67f09SDavid van Moolenbroek 	/*
2553*00b67f09SDavid van Moolenbroek 	 * We call the init routines for the various kinds of client here,
2554*00b67f09SDavid van Moolenbroek 	 * after we have created an otherwise valid client, because some
2555*00b67f09SDavid van Moolenbroek 	 * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
2556*00b67f09SDavid van Moolenbroek 	 */
2557*00b67f09SDavid van Moolenbroek 	result = ns_query_init(client);
2558*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2559*00b67f09SDavid van Moolenbroek 		goto cleanup_recvevent;
2560*00b67f09SDavid van Moolenbroek 
2561*00b67f09SDavid van Moolenbroek 	result = isc_task_onshutdown(client->task, client_shutdown, client);
2562*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2563*00b67f09SDavid van Moolenbroek 		goto cleanup_query;
2564*00b67f09SDavid van Moolenbroek 
2565*00b67f09SDavid van Moolenbroek 	CTRACE("create");
2566*00b67f09SDavid van Moolenbroek 
2567*00b67f09SDavid van Moolenbroek 	*clientp = client;
2568*00b67f09SDavid van Moolenbroek 
2569*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2570*00b67f09SDavid van Moolenbroek 
2571*00b67f09SDavid van Moolenbroek  cleanup_query:
2572*00b67f09SDavid van Moolenbroek 	ns_query_free(client);
2573*00b67f09SDavid van Moolenbroek 
2574*00b67f09SDavid van Moolenbroek  cleanup_recvevent:
2575*00b67f09SDavid van Moolenbroek 	isc_event_free((isc_event_t **)&client->recvevent);
2576*00b67f09SDavid van Moolenbroek 
2577*00b67f09SDavid van Moolenbroek  cleanup_recvbuf:
2578*00b67f09SDavid van Moolenbroek 	isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
2579*00b67f09SDavid van Moolenbroek 
2580*00b67f09SDavid van Moolenbroek  cleanup_sendevent:
2581*00b67f09SDavid van Moolenbroek 	isc_event_free((isc_event_t **)&client->sendevent);
2582*00b67f09SDavid van Moolenbroek 
2583*00b67f09SDavid van Moolenbroek 	client->magic = 0;
2584*00b67f09SDavid van Moolenbroek 
2585*00b67f09SDavid van Moolenbroek  cleanup_message:
2586*00b67f09SDavid van Moolenbroek 	dns_message_destroy(&client->message);
2587*00b67f09SDavid van Moolenbroek 
2588*00b67f09SDavid van Moolenbroek  cleanup_timer:
2589*00b67f09SDavid van Moolenbroek 	isc_timer_detach(&client->timer);
2590*00b67f09SDavid van Moolenbroek 
2591*00b67f09SDavid van Moolenbroek  cleanup_task:
2592*00b67f09SDavid van Moolenbroek 	isc_task_detach(&client->task);
2593*00b67f09SDavid van Moolenbroek 
2594*00b67f09SDavid van Moolenbroek  cleanup_client:
2595*00b67f09SDavid van Moolenbroek 	isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
2596*00b67f09SDavid van Moolenbroek 
2597*00b67f09SDavid van Moolenbroek 	return (result);
2598*00b67f09SDavid van Moolenbroek }
2599*00b67f09SDavid van Moolenbroek 
2600*00b67f09SDavid van Moolenbroek static void
client_read(ns_client_t * client)2601*00b67f09SDavid van Moolenbroek client_read(ns_client_t *client) {
2602*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2603*00b67f09SDavid van Moolenbroek 
2604*00b67f09SDavid van Moolenbroek 	CTRACE("read");
2605*00b67f09SDavid van Moolenbroek 
2606*00b67f09SDavid van Moolenbroek 	result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
2607*00b67f09SDavid van Moolenbroek 					client_request, client);
2608*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2609*00b67f09SDavid van Moolenbroek 		goto fail;
2610*00b67f09SDavid van Moolenbroek 
2611*00b67f09SDavid van Moolenbroek 	/*
2612*00b67f09SDavid van Moolenbroek 	 * Set a timeout to limit the amount of time we will wait
2613*00b67f09SDavid van Moolenbroek 	 * for a request on this TCP connection.
2614*00b67f09SDavid van Moolenbroek 	 */
2615*00b67f09SDavid van Moolenbroek 	ns_client_settimeout(client, 30);
2616*00b67f09SDavid van Moolenbroek 
2617*00b67f09SDavid van Moolenbroek 	client->state = client->newstate = NS_CLIENTSTATE_READING;
2618*00b67f09SDavid van Moolenbroek 	INSIST(client->nreads == 0);
2619*00b67f09SDavid van Moolenbroek 	INSIST(client->recursionquota == NULL);
2620*00b67f09SDavid van Moolenbroek 	client->nreads++;
2621*00b67f09SDavid van Moolenbroek 
2622*00b67f09SDavid van Moolenbroek 	return;
2623*00b67f09SDavid van Moolenbroek  fail:
2624*00b67f09SDavid van Moolenbroek 	ns_client_next(client, result);
2625*00b67f09SDavid van Moolenbroek }
2626*00b67f09SDavid van Moolenbroek 
2627*00b67f09SDavid van Moolenbroek static void
client_newconn(isc_task_t * task,isc_event_t * event)2628*00b67f09SDavid van Moolenbroek client_newconn(isc_task_t *task, isc_event_t *event) {
2629*00b67f09SDavid van Moolenbroek 	ns_client_t *client = event->ev_arg;
2630*00b67f09SDavid van Moolenbroek 	isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
2631*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2632*00b67f09SDavid van Moolenbroek 
2633*00b67f09SDavid van Moolenbroek 	REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
2634*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
2635*00b67f09SDavid van Moolenbroek 	REQUIRE(client->task == task);
2636*00b67f09SDavid van Moolenbroek 
2637*00b67f09SDavid van Moolenbroek 	UNUSED(task);
2638*00b67f09SDavid van Moolenbroek 
2639*00b67f09SDavid van Moolenbroek 	INSIST(client->state == NS_CLIENTSTATE_READY);
2640*00b67f09SDavid van Moolenbroek 
2641*00b67f09SDavid van Moolenbroek 	INSIST(client->naccepts == 1);
2642*00b67f09SDavid van Moolenbroek 	client->naccepts--;
2643*00b67f09SDavid van Moolenbroek 
2644*00b67f09SDavid van Moolenbroek 	LOCK(&client->interface->lock);
2645*00b67f09SDavid van Moolenbroek 	INSIST(client->interface->ntcpcurrent > 0);
2646*00b67f09SDavid van Moolenbroek 	client->interface->ntcpcurrent--;
2647*00b67f09SDavid van Moolenbroek 	UNLOCK(&client->interface->lock);
2648*00b67f09SDavid van Moolenbroek 
2649*00b67f09SDavid van Moolenbroek 	/*
2650*00b67f09SDavid van Moolenbroek 	 * We must take ownership of the new socket before the exit
2651*00b67f09SDavid van Moolenbroek 	 * check to make sure it gets destroyed if we decide to exit.
2652*00b67f09SDavid van Moolenbroek 	 */
2653*00b67f09SDavid van Moolenbroek 	if (nevent->result == ISC_R_SUCCESS) {
2654*00b67f09SDavid van Moolenbroek 		client->tcpsocket = nevent->newsocket;
2655*00b67f09SDavid van Moolenbroek 		isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
2656*00b67f09SDavid van Moolenbroek 		client->state = NS_CLIENTSTATE_READING;
2657*00b67f09SDavid van Moolenbroek 		INSIST(client->recursionquota == NULL);
2658*00b67f09SDavid van Moolenbroek 
2659*00b67f09SDavid van Moolenbroek 		(void)isc_socket_getpeername(client->tcpsocket,
2660*00b67f09SDavid van Moolenbroek 					     &client->peeraddr);
2661*00b67f09SDavid van Moolenbroek 		client->peeraddr_valid = ISC_TRUE;
2662*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2663*00b67f09SDavid van Moolenbroek 			   NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2664*00b67f09SDavid van Moolenbroek 			   "new TCP connection");
2665*00b67f09SDavid van Moolenbroek 	} else {
2666*00b67f09SDavid van Moolenbroek 		/*
2667*00b67f09SDavid van Moolenbroek 		 * XXXRTH  What should we do?  We're trying to accept but
2668*00b67f09SDavid van Moolenbroek 		 *	   it didn't work.  If we just give up, then TCP
2669*00b67f09SDavid van Moolenbroek 		 *	   service may eventually stop.
2670*00b67f09SDavid van Moolenbroek 		 *
2671*00b67f09SDavid van Moolenbroek 		 *	   For now, we just go idle.
2672*00b67f09SDavid van Moolenbroek 		 *
2673*00b67f09SDavid van Moolenbroek 		 *	   Going idle is probably the right thing if the
2674*00b67f09SDavid van Moolenbroek 		 *	   I/O was canceled.
2675*00b67f09SDavid van Moolenbroek 		 */
2676*00b67f09SDavid van Moolenbroek 		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2677*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2678*00b67f09SDavid van Moolenbroek 			      "accept failed: %s",
2679*00b67f09SDavid van Moolenbroek 			      isc_result_totext(nevent->result));
2680*00b67f09SDavid van Moolenbroek 	}
2681*00b67f09SDavid van Moolenbroek 
2682*00b67f09SDavid van Moolenbroek 	if (exit_check(client))
2683*00b67f09SDavid van Moolenbroek 		goto freeevent;
2684*00b67f09SDavid van Moolenbroek 
2685*00b67f09SDavid van Moolenbroek 	if (nevent->result == ISC_R_SUCCESS) {
2686*00b67f09SDavid van Moolenbroek 		int match;
2687*00b67f09SDavid van Moolenbroek 		isc_netaddr_t netaddr;
2688*00b67f09SDavid van Moolenbroek 
2689*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
2690*00b67f09SDavid van Moolenbroek 
2691*00b67f09SDavid van Moolenbroek 		if (ns_g_server->blackholeacl != NULL &&
2692*00b67f09SDavid van Moolenbroek 		    dns_acl_match(&netaddr, NULL,
2693*00b67f09SDavid van Moolenbroek 				  ns_g_server->blackholeacl,
2694*00b67f09SDavid van Moolenbroek 				  &ns_g_server->aclenv,
2695*00b67f09SDavid van Moolenbroek 				  &match, NULL) == ISC_R_SUCCESS &&
2696*00b67f09SDavid van Moolenbroek 		    match > 0)
2697*00b67f09SDavid van Moolenbroek 		{
2698*00b67f09SDavid van Moolenbroek 			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2699*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2700*00b67f09SDavid van Moolenbroek 				      "blackholed connection attempt");
2701*00b67f09SDavid van Moolenbroek 			client->newstate = NS_CLIENTSTATE_READY;
2702*00b67f09SDavid van Moolenbroek 			(void)exit_check(client);
2703*00b67f09SDavid van Moolenbroek 			goto freeevent;
2704*00b67f09SDavid van Moolenbroek 		}
2705*00b67f09SDavid van Moolenbroek 
2706*00b67f09SDavid van Moolenbroek 		INSIST(client->tcpmsg_valid == ISC_FALSE);
2707*00b67f09SDavid van Moolenbroek 		dns_tcpmsg_init(client->mctx, client->tcpsocket,
2708*00b67f09SDavid van Moolenbroek 				&client->tcpmsg);
2709*00b67f09SDavid van Moolenbroek 		client->tcpmsg_valid = ISC_TRUE;
2710*00b67f09SDavid van Moolenbroek 
2711*00b67f09SDavid van Moolenbroek 		/*
2712*00b67f09SDavid van Moolenbroek 		 * Let a new client take our place immediately, before
2713*00b67f09SDavid van Moolenbroek 		 * we wait for a request packet.  If we don't,
2714*00b67f09SDavid van Moolenbroek 		 * telnetting to port 53 (once per CPU) will
2715*00b67f09SDavid van Moolenbroek 		 * deny service to legitimate TCP clients.
2716*00b67f09SDavid van Moolenbroek 		 */
2717*00b67f09SDavid van Moolenbroek 		result = isc_quota_attach(&ns_g_server->tcpquota,
2718*00b67f09SDavid van Moolenbroek 					  &client->tcpquota);
2719*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
2720*00b67f09SDavid van Moolenbroek 			result = ns_client_replace(client);
2721*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
2722*00b67f09SDavid van Moolenbroek 			ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2723*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
2724*00b67f09SDavid van Moolenbroek 				      "no more TCP clients: %s",
2725*00b67f09SDavid van Moolenbroek 				      isc_result_totext(result));
2726*00b67f09SDavid van Moolenbroek 		}
2727*00b67f09SDavid van Moolenbroek 
2728*00b67f09SDavid van Moolenbroek 		client_read(client);
2729*00b67f09SDavid van Moolenbroek 	}
2730*00b67f09SDavid van Moolenbroek 
2731*00b67f09SDavid van Moolenbroek  freeevent:
2732*00b67f09SDavid van Moolenbroek 	isc_event_free(&event);
2733*00b67f09SDavid van Moolenbroek }
2734*00b67f09SDavid van Moolenbroek 
2735*00b67f09SDavid van Moolenbroek static void
client_accept(ns_client_t * client)2736*00b67f09SDavid van Moolenbroek client_accept(ns_client_t *client) {
2737*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2738*00b67f09SDavid van Moolenbroek 
2739*00b67f09SDavid van Moolenbroek 	CTRACE("accept");
2740*00b67f09SDavid van Moolenbroek 
2741*00b67f09SDavid van Moolenbroek 	result = isc_socket_accept(client->tcplistener, client->task,
2742*00b67f09SDavid van Moolenbroek 				   client_newconn, client);
2743*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2744*00b67f09SDavid van Moolenbroek 		UNEXPECTED_ERROR(__FILE__, __LINE__,
2745*00b67f09SDavid van Moolenbroek 				 "isc_socket_accept() failed: %s",
2746*00b67f09SDavid van Moolenbroek 				 isc_result_totext(result));
2747*00b67f09SDavid van Moolenbroek 		/*
2748*00b67f09SDavid van Moolenbroek 		 * XXXRTH  What should we do?  We're trying to accept but
2749*00b67f09SDavid van Moolenbroek 		 *	   it didn't work.  If we just give up, then TCP
2750*00b67f09SDavid van Moolenbroek 		 *	   service may eventually stop.
2751*00b67f09SDavid van Moolenbroek 		 *
2752*00b67f09SDavid van Moolenbroek 		 *	   For now, we just go idle.
2753*00b67f09SDavid van Moolenbroek 		 */
2754*00b67f09SDavid van Moolenbroek 		return;
2755*00b67f09SDavid van Moolenbroek 	}
2756*00b67f09SDavid van Moolenbroek 	INSIST(client->naccepts == 0);
2757*00b67f09SDavid van Moolenbroek 	client->naccepts++;
2758*00b67f09SDavid van Moolenbroek 	LOCK(&client->interface->lock);
2759*00b67f09SDavid van Moolenbroek 	client->interface->ntcpcurrent++;
2760*00b67f09SDavid van Moolenbroek 	UNLOCK(&client->interface->lock);
2761*00b67f09SDavid van Moolenbroek }
2762*00b67f09SDavid van Moolenbroek 
2763*00b67f09SDavid van Moolenbroek static void
client_udprecv(ns_client_t * client)2764*00b67f09SDavid van Moolenbroek client_udprecv(ns_client_t *client) {
2765*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2766*00b67f09SDavid van Moolenbroek 	isc_region_t r;
2767*00b67f09SDavid van Moolenbroek 
2768*00b67f09SDavid van Moolenbroek 	CTRACE("udprecv");
2769*00b67f09SDavid van Moolenbroek 
2770*00b67f09SDavid van Moolenbroek 	r.base = client->recvbuf;
2771*00b67f09SDavid van Moolenbroek 	r.length = RECV_BUFFER_SIZE;
2772*00b67f09SDavid van Moolenbroek 	result = isc_socket_recv2(client->udpsocket, &r, 1,
2773*00b67f09SDavid van Moolenbroek 				  client->task, client->recvevent, 0);
2774*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2775*00b67f09SDavid van Moolenbroek 		UNEXPECTED_ERROR(__FILE__, __LINE__,
2776*00b67f09SDavid van Moolenbroek 				 "isc_socket_recv2() failed: %s",
2777*00b67f09SDavid van Moolenbroek 				 isc_result_totext(result));
2778*00b67f09SDavid van Moolenbroek 		/*
2779*00b67f09SDavid van Moolenbroek 		 * This cannot happen in the current implementation, since
2780*00b67f09SDavid van Moolenbroek 		 * isc_socket_recv2() cannot fail if flags == 0.
2781*00b67f09SDavid van Moolenbroek 		 *
2782*00b67f09SDavid van Moolenbroek 		 * If this does fail, we just go idle.
2783*00b67f09SDavid van Moolenbroek 		 */
2784*00b67f09SDavid van Moolenbroek 		return;
2785*00b67f09SDavid van Moolenbroek 	}
2786*00b67f09SDavid van Moolenbroek 	INSIST(client->nrecvs == 0);
2787*00b67f09SDavid van Moolenbroek 	client->nrecvs++;
2788*00b67f09SDavid van Moolenbroek }
2789*00b67f09SDavid van Moolenbroek 
2790*00b67f09SDavid van Moolenbroek void
ns_client_attach(ns_client_t * source,ns_client_t ** targetp)2791*00b67f09SDavid van Moolenbroek ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
2792*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(source));
2793*00b67f09SDavid van Moolenbroek 	REQUIRE(targetp != NULL && *targetp == NULL);
2794*00b67f09SDavid van Moolenbroek 
2795*00b67f09SDavid van Moolenbroek 	source->references++;
2796*00b67f09SDavid van Moolenbroek 	ns_client_log(source, NS_LOGCATEGORY_CLIENT,
2797*00b67f09SDavid van Moolenbroek 		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2798*00b67f09SDavid van Moolenbroek 		      "ns_client_attach: ref = %d", source->references);
2799*00b67f09SDavid van Moolenbroek 	*targetp = source;
2800*00b67f09SDavid van Moolenbroek }
2801*00b67f09SDavid van Moolenbroek 
2802*00b67f09SDavid van Moolenbroek void
ns_client_detach(ns_client_t ** clientp)2803*00b67f09SDavid van Moolenbroek ns_client_detach(ns_client_t **clientp) {
2804*00b67f09SDavid van Moolenbroek 	ns_client_t *client = *clientp;
2805*00b67f09SDavid van Moolenbroek 
2806*00b67f09SDavid van Moolenbroek 	client->references--;
2807*00b67f09SDavid van Moolenbroek 	INSIST(client->references >= 0);
2808*00b67f09SDavid van Moolenbroek 	*clientp = NULL;
2809*00b67f09SDavid van Moolenbroek 	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2810*00b67f09SDavid van Moolenbroek 		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2811*00b67f09SDavid van Moolenbroek 		      "ns_client_detach: ref = %d", client->references);
2812*00b67f09SDavid van Moolenbroek 	(void)exit_check(client);
2813*00b67f09SDavid van Moolenbroek }
2814*00b67f09SDavid van Moolenbroek 
2815*00b67f09SDavid van Moolenbroek isc_boolean_t
ns_client_shuttingdown(ns_client_t * client)2816*00b67f09SDavid van Moolenbroek ns_client_shuttingdown(ns_client_t *client) {
2817*00b67f09SDavid van Moolenbroek 	return (ISC_TF(client->newstate == NS_CLIENTSTATE_FREED));
2818*00b67f09SDavid van Moolenbroek }
2819*00b67f09SDavid van Moolenbroek 
2820*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_replace(ns_client_t * client)2821*00b67f09SDavid van Moolenbroek ns_client_replace(ns_client_t *client) {
2822*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2823*00b67f09SDavid van Moolenbroek 
2824*00b67f09SDavid van Moolenbroek 	CTRACE("replace");
2825*00b67f09SDavid van Moolenbroek 
2826*00b67f09SDavid van Moolenbroek 	REQUIRE(client != NULL);
2827*00b67f09SDavid van Moolenbroek 	REQUIRE(client->manager != NULL);
2828*00b67f09SDavid van Moolenbroek 
2829*00b67f09SDavid van Moolenbroek 	result = get_client(client->manager, client->interface,
2830*00b67f09SDavid van Moolenbroek 			    client->dispatch, TCP_CLIENT(client));
2831*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2832*00b67f09SDavid van Moolenbroek 		return (result);
2833*00b67f09SDavid van Moolenbroek 
2834*00b67f09SDavid van Moolenbroek 	/*
2835*00b67f09SDavid van Moolenbroek 	 * The responsibility for listening for new requests is hereby
2836*00b67f09SDavid van Moolenbroek 	 * transferred to the new client.  Therefore, the old client
2837*00b67f09SDavid van Moolenbroek 	 * should refrain from listening for any more requests.
2838*00b67f09SDavid van Moolenbroek 	 */
2839*00b67f09SDavid van Moolenbroek 	client->mortal = ISC_TRUE;
2840*00b67f09SDavid van Moolenbroek 
2841*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2842*00b67f09SDavid van Moolenbroek }
2843*00b67f09SDavid van Moolenbroek 
2844*00b67f09SDavid van Moolenbroek /***
2845*00b67f09SDavid van Moolenbroek  *** Client Manager
2846*00b67f09SDavid van Moolenbroek  ***/
2847*00b67f09SDavid van Moolenbroek 
2848*00b67f09SDavid van Moolenbroek static void
clientmgr_destroy(ns_clientmgr_t * manager)2849*00b67f09SDavid van Moolenbroek clientmgr_destroy(ns_clientmgr_t *manager) {
2850*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2851*00b67f09SDavid van Moolenbroek 	int i;
2852*00b67f09SDavid van Moolenbroek #endif
2853*00b67f09SDavid van Moolenbroek 
2854*00b67f09SDavid van Moolenbroek 	REQUIRE(ISC_LIST_EMPTY(manager->clients));
2855*00b67f09SDavid van Moolenbroek 
2856*00b67f09SDavid van Moolenbroek 	MTRACE("clientmgr_destroy");
2857*00b67f09SDavid van Moolenbroek 
2858*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2859*00b67f09SDavid van Moolenbroek 	for (i = 0; i < NMCTXS; i++) {
2860*00b67f09SDavid van Moolenbroek 		if (manager->mctxpool[i] != NULL)
2861*00b67f09SDavid van Moolenbroek 			isc_mem_detach(&manager->mctxpool[i]);
2862*00b67f09SDavid van Moolenbroek 	}
2863*00b67f09SDavid van Moolenbroek #endif
2864*00b67f09SDavid van Moolenbroek 
2865*00b67f09SDavid van Moolenbroek 	ISC_QUEUE_DESTROY(manager->inactive);
2866*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&manager->lock);
2867*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&manager->listlock);
2868*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&manager->reclock);
2869*00b67f09SDavid van Moolenbroek 	manager->magic = 0;
2870*00b67f09SDavid van Moolenbroek 	isc_mem_put(manager->mctx, manager, sizeof(*manager));
2871*00b67f09SDavid van Moolenbroek }
2872*00b67f09SDavid van Moolenbroek 
2873*00b67f09SDavid van Moolenbroek isc_result_t
ns_clientmgr_create(isc_mem_t * mctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,ns_clientmgr_t ** managerp)2874*00b67f09SDavid van Moolenbroek ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
2875*00b67f09SDavid van Moolenbroek 		    isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
2876*00b67f09SDavid van Moolenbroek {
2877*00b67f09SDavid van Moolenbroek 	ns_clientmgr_t *manager;
2878*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2879*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2880*00b67f09SDavid van Moolenbroek 	int i;
2881*00b67f09SDavid van Moolenbroek #endif
2882*00b67f09SDavid van Moolenbroek 
2883*00b67f09SDavid van Moolenbroek 	manager = isc_mem_get(mctx, sizeof(*manager));
2884*00b67f09SDavid van Moolenbroek 	if (manager == NULL)
2885*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
2886*00b67f09SDavid van Moolenbroek 
2887*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&manager->lock);
2888*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2889*00b67f09SDavid van Moolenbroek 		goto cleanup_manager;
2890*00b67f09SDavid van Moolenbroek 
2891*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&manager->listlock);
2892*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2893*00b67f09SDavid van Moolenbroek 		goto cleanup_lock;
2894*00b67f09SDavid van Moolenbroek 
2895*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&manager->reclock);
2896*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2897*00b67f09SDavid van Moolenbroek 		goto cleanup_listlock;
2898*00b67f09SDavid van Moolenbroek 
2899*00b67f09SDavid van Moolenbroek 	manager->mctx = mctx;
2900*00b67f09SDavid van Moolenbroek 	manager->taskmgr = taskmgr;
2901*00b67f09SDavid van Moolenbroek 	manager->timermgr = timermgr;
2902*00b67f09SDavid van Moolenbroek 	manager->exiting = ISC_FALSE;
2903*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(manager->clients);
2904*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(manager->recursing);
2905*00b67f09SDavid van Moolenbroek 	ISC_QUEUE_INIT(manager->inactive, ilink);
2906*00b67f09SDavid van Moolenbroek #if NMCTXS > 0
2907*00b67f09SDavid van Moolenbroek 	manager->nextmctx = 0;
2908*00b67f09SDavid van Moolenbroek 	for (i = 0; i < NMCTXS; i++)
2909*00b67f09SDavid van Moolenbroek 		manager->mctxpool[i] = NULL; /* will be created on-demand */
2910*00b67f09SDavid van Moolenbroek #endif
2911*00b67f09SDavid van Moolenbroek 	manager->magic = MANAGER_MAGIC;
2912*00b67f09SDavid van Moolenbroek 
2913*00b67f09SDavid van Moolenbroek 	MTRACE("create");
2914*00b67f09SDavid van Moolenbroek 
2915*00b67f09SDavid van Moolenbroek 	*managerp = manager;
2916*00b67f09SDavid van Moolenbroek 
2917*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2918*00b67f09SDavid van Moolenbroek 
2919*00b67f09SDavid van Moolenbroek  cleanup_listlock:
2920*00b67f09SDavid van Moolenbroek 	(void) isc_mutex_destroy(&manager->listlock);
2921*00b67f09SDavid van Moolenbroek 
2922*00b67f09SDavid van Moolenbroek  cleanup_lock:
2923*00b67f09SDavid van Moolenbroek 	(void) isc_mutex_destroy(&manager->lock);
2924*00b67f09SDavid van Moolenbroek 
2925*00b67f09SDavid van Moolenbroek  cleanup_manager:
2926*00b67f09SDavid van Moolenbroek 	isc_mem_put(manager->mctx, manager, sizeof(*manager));
2927*00b67f09SDavid van Moolenbroek 
2928*00b67f09SDavid van Moolenbroek 	return (result);
2929*00b67f09SDavid van Moolenbroek }
2930*00b67f09SDavid van Moolenbroek 
2931*00b67f09SDavid van Moolenbroek void
ns_clientmgr_destroy(ns_clientmgr_t ** managerp)2932*00b67f09SDavid van Moolenbroek ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
2933*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2934*00b67f09SDavid van Moolenbroek 	ns_clientmgr_t *manager;
2935*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
2936*00b67f09SDavid van Moolenbroek 	isc_boolean_t need_destroy = ISC_FALSE, unlock = ISC_FALSE;
2937*00b67f09SDavid van Moolenbroek 
2938*00b67f09SDavid van Moolenbroek 	REQUIRE(managerp != NULL);
2939*00b67f09SDavid van Moolenbroek 	manager = *managerp;
2940*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_MANAGER(manager));
2941*00b67f09SDavid van Moolenbroek 
2942*00b67f09SDavid van Moolenbroek 	MTRACE("destroy");
2943*00b67f09SDavid van Moolenbroek 
2944*00b67f09SDavid van Moolenbroek 	/*
2945*00b67f09SDavid van Moolenbroek 	 * Check for success because we may already be task-exclusive
2946*00b67f09SDavid van Moolenbroek 	 * at this point.  Only if we succeed at obtaining an exclusive
2947*00b67f09SDavid van Moolenbroek 	 * lock now will we need to relinquish it later.
2948*00b67f09SDavid van Moolenbroek 	 */
2949*00b67f09SDavid van Moolenbroek 	result = isc_task_beginexclusive(ns_g_server->task);
2950*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
2951*00b67f09SDavid van Moolenbroek 		unlock = ISC_TRUE;
2952*00b67f09SDavid van Moolenbroek 
2953*00b67f09SDavid van Moolenbroek 	manager->exiting = ISC_TRUE;
2954*00b67f09SDavid van Moolenbroek 
2955*00b67f09SDavid van Moolenbroek 	for (client = ISC_LIST_HEAD(manager->clients);
2956*00b67f09SDavid van Moolenbroek 	     client != NULL;
2957*00b67f09SDavid van Moolenbroek 	     client = ISC_LIST_NEXT(client, link))
2958*00b67f09SDavid van Moolenbroek 		isc_task_shutdown(client->task);
2959*00b67f09SDavid van Moolenbroek 
2960*00b67f09SDavid van Moolenbroek 	if (ISC_LIST_EMPTY(manager->clients))
2961*00b67f09SDavid van Moolenbroek 		need_destroy = ISC_TRUE;
2962*00b67f09SDavid van Moolenbroek 
2963*00b67f09SDavid van Moolenbroek 	if (unlock)
2964*00b67f09SDavid van Moolenbroek 		isc_task_endexclusive(ns_g_server->task);
2965*00b67f09SDavid van Moolenbroek 
2966*00b67f09SDavid van Moolenbroek 	if (need_destroy)
2967*00b67f09SDavid van Moolenbroek 		clientmgr_destroy(manager);
2968*00b67f09SDavid van Moolenbroek 
2969*00b67f09SDavid van Moolenbroek 	*managerp = NULL;
2970*00b67f09SDavid van Moolenbroek }
2971*00b67f09SDavid van Moolenbroek 
2972*00b67f09SDavid van Moolenbroek static isc_result_t
get_client(ns_clientmgr_t * manager,ns_interface_t * ifp,dns_dispatch_t * disp,isc_boolean_t tcp)2973*00b67f09SDavid van Moolenbroek get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
2974*00b67f09SDavid van Moolenbroek 	   dns_dispatch_t *disp, isc_boolean_t tcp)
2975*00b67f09SDavid van Moolenbroek {
2976*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_SUCCESS;
2977*00b67f09SDavid van Moolenbroek 	isc_event_t *ev;
2978*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
2979*00b67f09SDavid van Moolenbroek 	MTRACE("get client");
2980*00b67f09SDavid van Moolenbroek 
2981*00b67f09SDavid van Moolenbroek 	REQUIRE(manager != NULL);
2982*00b67f09SDavid van Moolenbroek 
2983*00b67f09SDavid van Moolenbroek 	if (manager->exiting)
2984*00b67f09SDavid van Moolenbroek 		return (ISC_R_SHUTTINGDOWN);
2985*00b67f09SDavid van Moolenbroek 
2986*00b67f09SDavid van Moolenbroek 	/*
2987*00b67f09SDavid van Moolenbroek 	 * Allocate a client.  First try to get a recycled one;
2988*00b67f09SDavid van Moolenbroek 	 * if that fails, make a new one.
2989*00b67f09SDavid van Moolenbroek 	 */
2990*00b67f09SDavid van Moolenbroek 	client = NULL;
2991*00b67f09SDavid van Moolenbroek 	if (!ns_g_clienttest)
2992*00b67f09SDavid van Moolenbroek 		ISC_QUEUE_POP(manager->inactive, ilink, client);
2993*00b67f09SDavid van Moolenbroek 
2994*00b67f09SDavid van Moolenbroek 	if (client != NULL)
2995*00b67f09SDavid van Moolenbroek 		MTRACE("recycle");
2996*00b67f09SDavid van Moolenbroek 	else {
2997*00b67f09SDavid van Moolenbroek 		MTRACE("create new");
2998*00b67f09SDavid van Moolenbroek 
2999*00b67f09SDavid van Moolenbroek 		LOCK(&manager->lock);
3000*00b67f09SDavid van Moolenbroek 		result = client_create(manager, &client);
3001*00b67f09SDavid van Moolenbroek 		UNLOCK(&manager->lock);
3002*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
3003*00b67f09SDavid van Moolenbroek 			return (result);
3004*00b67f09SDavid van Moolenbroek 
3005*00b67f09SDavid van Moolenbroek 		LOCK(&manager->listlock);
3006*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(manager->clients, client, link);
3007*00b67f09SDavid van Moolenbroek 		UNLOCK(&manager->listlock);
3008*00b67f09SDavid van Moolenbroek 	}
3009*00b67f09SDavid van Moolenbroek 
3010*00b67f09SDavid van Moolenbroek 	client->manager = manager;
3011*00b67f09SDavid van Moolenbroek 	ns_interface_attach(ifp, &client->interface);
3012*00b67f09SDavid van Moolenbroek 	client->state = NS_CLIENTSTATE_READY;
3013*00b67f09SDavid van Moolenbroek 	INSIST(client->recursionquota == NULL);
3014*00b67f09SDavid van Moolenbroek 
3015*00b67f09SDavid van Moolenbroek 	client->dscp = ifp->dscp;
3016*00b67f09SDavid van Moolenbroek 
3017*00b67f09SDavid van Moolenbroek 	if (tcp) {
3018*00b67f09SDavid van Moolenbroek 		client->attributes |= NS_CLIENTATTR_TCP;
3019*00b67f09SDavid van Moolenbroek 		isc_socket_attach(ifp->tcpsocket,
3020*00b67f09SDavid van Moolenbroek 				  &client->tcplistener);
3021*00b67f09SDavid van Moolenbroek 	} else {
3022*00b67f09SDavid van Moolenbroek 		isc_socket_t *sock;
3023*00b67f09SDavid van Moolenbroek 
3024*00b67f09SDavid van Moolenbroek 		dns_dispatch_attach(disp, &client->dispatch);
3025*00b67f09SDavid van Moolenbroek 		sock = dns_dispatch_getsocket(client->dispatch);
3026*00b67f09SDavid van Moolenbroek 		isc_socket_attach(sock, &client->udpsocket);
3027*00b67f09SDavid van Moolenbroek 	}
3028*00b67f09SDavid van Moolenbroek 
3029*00b67f09SDavid van Moolenbroek 	INSIST(client->nctls == 0);
3030*00b67f09SDavid van Moolenbroek 	client->nctls++;
3031*00b67f09SDavid van Moolenbroek 	ev = &client->ctlevent;
3032*00b67f09SDavid van Moolenbroek 	isc_task_send(client->task, &ev);
3033*00b67f09SDavid van Moolenbroek 
3034*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
3035*00b67f09SDavid van Moolenbroek }
3036*00b67f09SDavid van Moolenbroek 
3037*00b67f09SDavid van Moolenbroek isc_result_t
ns_clientmgr_createclients(ns_clientmgr_t * manager,unsigned int n,ns_interface_t * ifp,isc_boolean_t tcp)3038*00b67f09SDavid van Moolenbroek ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
3039*00b67f09SDavid van Moolenbroek 			   ns_interface_t *ifp, isc_boolean_t tcp)
3040*00b67f09SDavid van Moolenbroek {
3041*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_SUCCESS;
3042*00b67f09SDavid van Moolenbroek 	unsigned int disp;
3043*00b67f09SDavid van Moolenbroek 
3044*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_MANAGER(manager));
3045*00b67f09SDavid van Moolenbroek 	REQUIRE(n > 0);
3046*00b67f09SDavid van Moolenbroek 
3047*00b67f09SDavid van Moolenbroek 	MTRACE("createclients");
3048*00b67f09SDavid van Moolenbroek 
3049*00b67f09SDavid van Moolenbroek 	for (disp = 0; disp < n; disp++) {
3050*00b67f09SDavid van Moolenbroek 		result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
3051*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
3052*00b67f09SDavid van Moolenbroek 			break;
3053*00b67f09SDavid van Moolenbroek 	}
3054*00b67f09SDavid van Moolenbroek 
3055*00b67f09SDavid van Moolenbroek 	return (result);
3056*00b67f09SDavid van Moolenbroek }
3057*00b67f09SDavid van Moolenbroek 
3058*00b67f09SDavid van Moolenbroek isc_sockaddr_t *
ns_client_getsockaddr(ns_client_t * client)3059*00b67f09SDavid van Moolenbroek ns_client_getsockaddr(ns_client_t *client) {
3060*00b67f09SDavid van Moolenbroek 	return (&client->peeraddr);
3061*00b67f09SDavid van Moolenbroek }
3062*00b67f09SDavid van Moolenbroek 
3063*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_checkaclsilent(ns_client_t * client,isc_netaddr_t * netaddr,dns_acl_t * acl,isc_boolean_t default_allow)3064*00b67f09SDavid van Moolenbroek ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
3065*00b67f09SDavid van Moolenbroek 			 dns_acl_t *acl, isc_boolean_t default_allow)
3066*00b67f09SDavid van Moolenbroek {
3067*00b67f09SDavid van Moolenbroek 	isc_result_t result;
3068*00b67f09SDavid van Moolenbroek 	isc_netaddr_t tmpnetaddr;
3069*00b67f09SDavid van Moolenbroek 	int match;
3070*00b67f09SDavid van Moolenbroek 
3071*00b67f09SDavid van Moolenbroek 	if (acl == NULL) {
3072*00b67f09SDavid van Moolenbroek 		if (default_allow)
3073*00b67f09SDavid van Moolenbroek 			goto allow;
3074*00b67f09SDavid van Moolenbroek 		else
3075*00b67f09SDavid van Moolenbroek 			goto deny;
3076*00b67f09SDavid van Moolenbroek 	}
3077*00b67f09SDavid van Moolenbroek 
3078*00b67f09SDavid van Moolenbroek 	if (netaddr == NULL) {
3079*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&tmpnetaddr, &client->peeraddr);
3080*00b67f09SDavid van Moolenbroek 		netaddr = &tmpnetaddr;
3081*00b67f09SDavid van Moolenbroek 	}
3082*00b67f09SDavid van Moolenbroek 
3083*00b67f09SDavid van Moolenbroek 	result = dns_acl_match(netaddr, client->signer, acl,
3084*00b67f09SDavid van Moolenbroek 			       &ns_g_server->aclenv, &match, NULL);
3085*00b67f09SDavid van Moolenbroek 
3086*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
3087*00b67f09SDavid van Moolenbroek 		goto deny; /* Internal error, already logged. */
3088*00b67f09SDavid van Moolenbroek 	if (match > 0)
3089*00b67f09SDavid van Moolenbroek 		goto allow;
3090*00b67f09SDavid van Moolenbroek 	goto deny; /* Negative match or no match. */
3091*00b67f09SDavid van Moolenbroek 
3092*00b67f09SDavid van Moolenbroek  allow:
3093*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
3094*00b67f09SDavid van Moolenbroek 
3095*00b67f09SDavid van Moolenbroek  deny:
3096*00b67f09SDavid van Moolenbroek 	return (DNS_R_REFUSED);
3097*00b67f09SDavid van Moolenbroek }
3098*00b67f09SDavid van Moolenbroek 
3099*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_checkacl(ns_client_t * client,isc_sockaddr_t * sockaddr,const char * opname,dns_acl_t * acl,isc_boolean_t default_allow,int log_level)3100*00b67f09SDavid van Moolenbroek ns_client_checkacl(ns_client_t *client, isc_sockaddr_t *sockaddr,
3101*00b67f09SDavid van Moolenbroek 		   const char *opname, dns_acl_t *acl,
3102*00b67f09SDavid van Moolenbroek 		   isc_boolean_t default_allow, int log_level)
3103*00b67f09SDavid van Moolenbroek {
3104*00b67f09SDavid van Moolenbroek 	isc_result_t result;
3105*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
3106*00b67f09SDavid van Moolenbroek 
3107*00b67f09SDavid van Moolenbroek 	if (sockaddr != NULL)
3108*00b67f09SDavid van Moolenbroek 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
3109*00b67f09SDavid van Moolenbroek 
3110*00b67f09SDavid van Moolenbroek 	result = ns_client_checkaclsilent(client, sockaddr ? &netaddr : NULL,
3111*00b67f09SDavid van Moolenbroek 					  acl, default_allow);
3112*00b67f09SDavid van Moolenbroek 
3113*00b67f09SDavid van Moolenbroek 	pfilter_notify(result, client, opname);
3114*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
3115*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
3116*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
3117*00b67f09SDavid van Moolenbroek 			      "%s approved", opname);
3118*00b67f09SDavid van Moolenbroek 	else
3119*00b67f09SDavid van Moolenbroek 		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
3120*00b67f09SDavid van Moolenbroek 			      NS_LOGMODULE_CLIENT,
3121*00b67f09SDavid van Moolenbroek 			      log_level, "%s denied", opname);
3122*00b67f09SDavid van Moolenbroek 	return (result);
3123*00b67f09SDavid van Moolenbroek }
3124*00b67f09SDavid van Moolenbroek 
3125*00b67f09SDavid van Moolenbroek static void
ns_client_name(ns_client_t * client,char * peerbuf,size_t len)3126*00b67f09SDavid van Moolenbroek ns_client_name(ns_client_t *client, char *peerbuf, size_t len) {
3127*00b67f09SDavid van Moolenbroek 	if (client->peeraddr_valid)
3128*00b67f09SDavid van Moolenbroek 		isc_sockaddr_format(&client->peeraddr, peerbuf,
3129*00b67f09SDavid van Moolenbroek 				    (unsigned int)len);
3130*00b67f09SDavid van Moolenbroek 	else
3131*00b67f09SDavid van Moolenbroek 		snprintf(peerbuf, len, "@%p", client);
3132*00b67f09SDavid van Moolenbroek }
3133*00b67f09SDavid van Moolenbroek 
3134*00b67f09SDavid van Moolenbroek void
ns_client_logv(ns_client_t * client,isc_logcategory_t * category,isc_logmodule_t * module,int level,const char * fmt,va_list ap)3135*00b67f09SDavid van Moolenbroek ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
3136*00b67f09SDavid van Moolenbroek 	       isc_logmodule_t *module, int level, const char *fmt, va_list ap)
3137*00b67f09SDavid van Moolenbroek {
3138*00b67f09SDavid van Moolenbroek 	char msgbuf[4096];
3139*00b67f09SDavid van Moolenbroek 	char peerbuf[ISC_SOCKADDR_FORMATSIZE];
3140*00b67f09SDavid van Moolenbroek 	char signerbuf[DNS_NAME_FORMATSIZE], qnamebuf[DNS_NAME_FORMATSIZE];
3141*00b67f09SDavid van Moolenbroek 	const char *viewname = "";
3142*00b67f09SDavid van Moolenbroek 	const char *sep1 = "", *sep2 = "", *sep3 = "", *sep4 = "";
3143*00b67f09SDavid van Moolenbroek 	const char *signer = "", *qname = "";
3144*00b67f09SDavid van Moolenbroek 	dns_name_t *q = NULL;
3145*00b67f09SDavid van Moolenbroek 
3146*00b67f09SDavid van Moolenbroek 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3147*00b67f09SDavid van Moolenbroek 
3148*00b67f09SDavid van Moolenbroek 	ns_client_name(client, peerbuf, sizeof(peerbuf));
3149*00b67f09SDavid van Moolenbroek 
3150*00b67f09SDavid van Moolenbroek 	if (client->signer != NULL) {
3151*00b67f09SDavid van Moolenbroek 		dns_name_format(client->signer, signerbuf, sizeof(signerbuf));
3152*00b67f09SDavid van Moolenbroek 		sep1 = "/key ";
3153*00b67f09SDavid van Moolenbroek 		signer = signerbuf;
3154*00b67f09SDavid van Moolenbroek 	}
3155*00b67f09SDavid van Moolenbroek 
3156*00b67f09SDavid van Moolenbroek 	q = client->query.origqname != NULL
3157*00b67f09SDavid van Moolenbroek 		? client->query.origqname : client->query.qname;
3158*00b67f09SDavid van Moolenbroek 	if (q != NULL) {
3159*00b67f09SDavid van Moolenbroek 		dns_name_format(q, qnamebuf, sizeof(qnamebuf));
3160*00b67f09SDavid van Moolenbroek 		sep2 = " (";
3161*00b67f09SDavid van Moolenbroek 		sep3 = ")";
3162*00b67f09SDavid van Moolenbroek 		qname = qnamebuf;
3163*00b67f09SDavid van Moolenbroek 	}
3164*00b67f09SDavid van Moolenbroek 
3165*00b67f09SDavid van Moolenbroek 	if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
3166*00b67f09SDavid van Moolenbroek 	    strcmp(client->view->name, "_default") != 0) {
3167*00b67f09SDavid van Moolenbroek 		sep4 = ": view ";
3168*00b67f09SDavid van Moolenbroek 		viewname = client->view->name;
3169*00b67f09SDavid van Moolenbroek 	}
3170*00b67f09SDavid van Moolenbroek 
3171*00b67f09SDavid van Moolenbroek 	isc_log_write(ns_g_lctx, category, module, level,
3172*00b67f09SDavid van Moolenbroek 		      "client %s%s%s%s%s%s%s%s: %s",
3173*00b67f09SDavid van Moolenbroek 		      peerbuf, sep1, signer, sep2, qname, sep3,
3174*00b67f09SDavid van Moolenbroek 		      sep4, viewname, msgbuf);
3175*00b67f09SDavid van Moolenbroek }
3176*00b67f09SDavid van Moolenbroek 
3177*00b67f09SDavid van Moolenbroek void
ns_client_log(ns_client_t * client,isc_logcategory_t * category,isc_logmodule_t * module,int level,const char * fmt,...)3178*00b67f09SDavid van Moolenbroek ns_client_log(ns_client_t *client, isc_logcategory_t *category,
3179*00b67f09SDavid van Moolenbroek 	   isc_logmodule_t *module, int level, const char *fmt, ...)
3180*00b67f09SDavid van Moolenbroek {
3181*00b67f09SDavid van Moolenbroek 	va_list ap;
3182*00b67f09SDavid van Moolenbroek 
3183*00b67f09SDavid van Moolenbroek 	if (! isc_log_wouldlog(ns_g_lctx, level))
3184*00b67f09SDavid van Moolenbroek 		return;
3185*00b67f09SDavid van Moolenbroek 
3186*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
3187*00b67f09SDavid van Moolenbroek 	ns_client_logv(client, category, module, level, fmt, ap);
3188*00b67f09SDavid van Moolenbroek 	va_end(ap);
3189*00b67f09SDavid van Moolenbroek }
3190*00b67f09SDavid van Moolenbroek 
3191*00b67f09SDavid van Moolenbroek void
ns_client_aclmsg(const char * msg,dns_name_t * name,dns_rdatatype_t type,dns_rdataclass_t rdclass,char * buf,size_t len)3192*00b67f09SDavid van Moolenbroek ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
3193*00b67f09SDavid van Moolenbroek 		 dns_rdataclass_t rdclass, char *buf, size_t len)
3194*00b67f09SDavid van Moolenbroek {
3195*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
3196*00b67f09SDavid van Moolenbroek 	char typebuf[DNS_RDATATYPE_FORMATSIZE];
3197*00b67f09SDavid van Moolenbroek 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
3198*00b67f09SDavid van Moolenbroek 
3199*00b67f09SDavid van Moolenbroek 	dns_name_format(name, namebuf, sizeof(namebuf));
3200*00b67f09SDavid van Moolenbroek 	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
3201*00b67f09SDavid van Moolenbroek 	dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
3202*00b67f09SDavid van Moolenbroek 	(void)snprintf(buf, len, "%s '%s/%s/%s'", msg, namebuf, typebuf,
3203*00b67f09SDavid van Moolenbroek 		       classbuf);
3204*00b67f09SDavid van Moolenbroek }
3205*00b67f09SDavid van Moolenbroek 
3206*00b67f09SDavid van Moolenbroek static void
ns_client_dumpmessage(ns_client_t * client,const char * reason)3207*00b67f09SDavid van Moolenbroek ns_client_dumpmessage(ns_client_t *client, const char *reason) {
3208*00b67f09SDavid van Moolenbroek 	isc_buffer_t buffer;
3209*00b67f09SDavid van Moolenbroek 	char *buf = NULL;
3210*00b67f09SDavid van Moolenbroek 	int len = 1024;
3211*00b67f09SDavid van Moolenbroek 	isc_result_t result;
3212*00b67f09SDavid van Moolenbroek 
3213*00b67f09SDavid van Moolenbroek 	if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1)))
3214*00b67f09SDavid van Moolenbroek 		return;
3215*00b67f09SDavid van Moolenbroek 
3216*00b67f09SDavid van Moolenbroek 	/*
3217*00b67f09SDavid van Moolenbroek 	 * Note that these are multiline debug messages.  We want a newline
3218*00b67f09SDavid van Moolenbroek 	 * to appear in the log after each message.
3219*00b67f09SDavid van Moolenbroek 	 */
3220*00b67f09SDavid van Moolenbroek 
3221*00b67f09SDavid van Moolenbroek 	do {
3222*00b67f09SDavid van Moolenbroek 		buf = isc_mem_get(client->mctx, len);
3223*00b67f09SDavid van Moolenbroek 		if (buf == NULL)
3224*00b67f09SDavid van Moolenbroek 			break;
3225*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&buffer, buf, len);
3226*00b67f09SDavid van Moolenbroek 		result = dns_message_totext(client->message,
3227*00b67f09SDavid van Moolenbroek 					    &dns_master_style_debug,
3228*00b67f09SDavid van Moolenbroek 					    0, &buffer);
3229*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_NOSPACE) {
3230*00b67f09SDavid van Moolenbroek 			isc_mem_put(client->mctx, buf, len);
3231*00b67f09SDavid van Moolenbroek 			len += 1024;
3232*00b67f09SDavid van Moolenbroek 		} else if (result == ISC_R_SUCCESS)
3233*00b67f09SDavid van Moolenbroek 			ns_client_log(client, NS_LOGCATEGORY_UNMATCHED,
3234*00b67f09SDavid van Moolenbroek 				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
3235*00b67f09SDavid van Moolenbroek 				      "%s\n%.*s", reason,
3236*00b67f09SDavid van Moolenbroek 				       (int)isc_buffer_usedlength(&buffer),
3237*00b67f09SDavid van Moolenbroek 				       buf);
3238*00b67f09SDavid van Moolenbroek 	} while (result == ISC_R_NOSPACE);
3239*00b67f09SDavid van Moolenbroek 
3240*00b67f09SDavid van Moolenbroek 	if (buf != NULL)
3241*00b67f09SDavid van Moolenbroek 		isc_mem_put(client->mctx, buf, len);
3242*00b67f09SDavid van Moolenbroek }
3243*00b67f09SDavid van Moolenbroek 
3244*00b67f09SDavid van Moolenbroek void
ns_client_dumprecursing(FILE * f,ns_clientmgr_t * manager)3245*00b67f09SDavid van Moolenbroek ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) {
3246*00b67f09SDavid van Moolenbroek 	ns_client_t *client;
3247*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
3248*00b67f09SDavid van Moolenbroek 	char original[DNS_NAME_FORMATSIZE];
3249*00b67f09SDavid van Moolenbroek 	char peerbuf[ISC_SOCKADDR_FORMATSIZE];
3250*00b67f09SDavid van Moolenbroek 	char typebuf[DNS_RDATATYPE_FORMATSIZE];
3251*00b67f09SDavid van Moolenbroek 	char classbuf[DNS_RDATACLASS_FORMATSIZE];
3252*00b67f09SDavid van Moolenbroek 	const char *name;
3253*00b67f09SDavid van Moolenbroek 	const char *sep;
3254*00b67f09SDavid van Moolenbroek 	const char *origfor;
3255*00b67f09SDavid van Moolenbroek 	dns_rdataset_t *rdataset;
3256*00b67f09SDavid van Moolenbroek 
3257*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_MANAGER(manager));
3258*00b67f09SDavid van Moolenbroek 
3259*00b67f09SDavid van Moolenbroek 	LOCK(&manager->reclock);
3260*00b67f09SDavid van Moolenbroek 	client = ISC_LIST_HEAD(manager->recursing);
3261*00b67f09SDavid van Moolenbroek 	while (client != NULL) {
3262*00b67f09SDavid van Moolenbroek 		INSIST(client->state == NS_CLIENTSTATE_RECURSING);
3263*00b67f09SDavid van Moolenbroek 
3264*00b67f09SDavid van Moolenbroek 		ns_client_name(client, peerbuf, sizeof(peerbuf));
3265*00b67f09SDavid van Moolenbroek 		if (client->view != NULL &&
3266*00b67f09SDavid van Moolenbroek 		    strcmp(client->view->name, "_bind") != 0 &&
3267*00b67f09SDavid van Moolenbroek 		    strcmp(client->view->name, "_default") != 0) {
3268*00b67f09SDavid van Moolenbroek 			name = client->view->name;
3269*00b67f09SDavid van Moolenbroek 			sep = ": view ";
3270*00b67f09SDavid van Moolenbroek 		} else {
3271*00b67f09SDavid van Moolenbroek 			name = "";
3272*00b67f09SDavid van Moolenbroek 			sep = "";
3273*00b67f09SDavid van Moolenbroek 		}
3274*00b67f09SDavid van Moolenbroek 
3275*00b67f09SDavid van Moolenbroek 		LOCK(&client->query.fetchlock);
3276*00b67f09SDavid van Moolenbroek 		INSIST(client->query.qname != NULL);
3277*00b67f09SDavid van Moolenbroek 		dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
3278*00b67f09SDavid van Moolenbroek 		if (client->query.qname != client->query.origqname &&
3279*00b67f09SDavid van Moolenbroek 		    client->query.origqname != NULL) {
3280*00b67f09SDavid van Moolenbroek 			origfor = " for ";
3281*00b67f09SDavid van Moolenbroek 			dns_name_format(client->query.origqname, original,
3282*00b67f09SDavid van Moolenbroek 					sizeof(original));
3283*00b67f09SDavid van Moolenbroek 		} else {
3284*00b67f09SDavid van Moolenbroek 			origfor = "";
3285*00b67f09SDavid van Moolenbroek 			original[0] = '\0';
3286*00b67f09SDavid van Moolenbroek 		}
3287*00b67f09SDavid van Moolenbroek 		rdataset = ISC_LIST_HEAD(client->query.qname->list);
3288*00b67f09SDavid van Moolenbroek 		if (rdataset == NULL && client->query.origqname != NULL)
3289*00b67f09SDavid van Moolenbroek 			rdataset = ISC_LIST_HEAD(client->query.origqname->list);
3290*00b67f09SDavid van Moolenbroek 		if (rdataset != NULL) {
3291*00b67f09SDavid van Moolenbroek 			dns_rdatatype_format(rdataset->type, typebuf,
3292*00b67f09SDavid van Moolenbroek 					     sizeof(typebuf));
3293*00b67f09SDavid van Moolenbroek 			dns_rdataclass_format(rdataset->rdclass, classbuf,
3294*00b67f09SDavid van Moolenbroek 					      sizeof(classbuf));
3295*00b67f09SDavid van Moolenbroek 		} else {
3296*00b67f09SDavid van Moolenbroek 			strcpy(typebuf, "-");
3297*00b67f09SDavid van Moolenbroek 			strcpy(classbuf, "-");
3298*00b67f09SDavid van Moolenbroek 		}
3299*00b67f09SDavid van Moolenbroek 		UNLOCK(&client->query.fetchlock);
3300*00b67f09SDavid van Moolenbroek 		fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s "
3301*00b67f09SDavid van Moolenbroek 			"requesttime %d\n", peerbuf, sep, name,
3302*00b67f09SDavid van Moolenbroek 			client->message->id, namebuf, typebuf, classbuf,
3303*00b67f09SDavid van Moolenbroek 			origfor, original, client->requesttime);
3304*00b67f09SDavid van Moolenbroek 		client = ISC_LIST_NEXT(client, rlink);
3305*00b67f09SDavid van Moolenbroek 	}
3306*00b67f09SDavid van Moolenbroek 	UNLOCK(&manager->reclock);
3307*00b67f09SDavid van Moolenbroek }
3308*00b67f09SDavid van Moolenbroek 
3309*00b67f09SDavid van Moolenbroek void
ns_client_qnamereplace(ns_client_t * client,dns_name_t * name)3310*00b67f09SDavid van Moolenbroek ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
3311*00b67f09SDavid van Moolenbroek 	LOCK(&client->query.fetchlock);
3312*00b67f09SDavid van Moolenbroek 	if (client->query.restarts > 0) {
3313*00b67f09SDavid van Moolenbroek 		/*
3314*00b67f09SDavid van Moolenbroek 		 * client->query.qname was dynamically allocated.
3315*00b67f09SDavid van Moolenbroek 		 */
3316*00b67f09SDavid van Moolenbroek 		dns_message_puttempname(client->message,
3317*00b67f09SDavid van Moolenbroek 					&client->query.qname);
3318*00b67f09SDavid van Moolenbroek 	}
3319*00b67f09SDavid van Moolenbroek 	client->query.qname = name;
3320*00b67f09SDavid van Moolenbroek 	UNLOCK(&client->query.fetchlock);
3321*00b67f09SDavid van Moolenbroek }
3322*00b67f09SDavid van Moolenbroek 
3323*00b67f09SDavid van Moolenbroek isc_result_t
ns_client_sourceip(dns_clientinfo_t * ci,isc_sockaddr_t ** addrp)3324*00b67f09SDavid van Moolenbroek ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp) {
3325*00b67f09SDavid van Moolenbroek 	ns_client_t *client = (ns_client_t *) ci->data;
3326*00b67f09SDavid van Moolenbroek 
3327*00b67f09SDavid van Moolenbroek 	REQUIRE(NS_CLIENT_VALID(client));
3328*00b67f09SDavid van Moolenbroek 	REQUIRE(addrp != NULL);
3329*00b67f09SDavid van Moolenbroek 
3330*00b67f09SDavid van Moolenbroek 	*addrp = &client->peeraddr;
3331*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
3332*00b67f09SDavid van Moolenbroek }
3333