1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id$ */
19135446Strhodes
20135446Strhodes#include <config.h>
21135446Strhodes
22135446Strhodes#include <isc/formatcheck.h>
23135446Strhodes#include <isc/mutex.h>
24135446Strhodes#include <isc/once.h>
25170222Sdougb#include <isc/platform.h>
26135446Strhodes#include <isc/print.h>
27254897Serwin#include <isc/queue.h>
28193149Sdougb#include <isc/stats.h>
29135446Strhodes#include <isc/stdio.h>
30135446Strhodes#include <isc/string.h>
31135446Strhodes#include <isc/task.h>
32135446Strhodes#include <isc/timer.h>
33135446Strhodes#include <isc/util.h>
34135446Strhodes
35135446Strhodes#include <dns/db.h>
36135446Strhodes#include <dns/dispatch.h>
37135446Strhodes#include <dns/events.h>
38135446Strhodes#include <dns/message.h>
39170222Sdougb#include <dns/peer.h>
40135446Strhodes#include <dns/rcode.h>
41135446Strhodes#include <dns/rdata.h>
42135446Strhodes#include <dns/rdataclass.h>
43135446Strhodes#include <dns/rdatalist.h>
44135446Strhodes#include <dns/rdataset.h>
45170222Sdougb#include <dns/resolver.h>
46193149Sdougb#include <dns/stats.h>
47135446Strhodes#include <dns/tsig.h>
48135446Strhodes#include <dns/view.h>
49135446Strhodes#include <dns/zone.h>
50135446Strhodes
51135446Strhodes#include <named/interfacemgr.h>
52135446Strhodes#include <named/log.h>
53135446Strhodes#include <named/notify.h>
54193149Sdougb#include <named/os.h>
55135446Strhodes#include <named/server.h>
56135446Strhodes#include <named/update.h>
57135446Strhodes
58135446Strhodes/***
59135446Strhodes *** Client
60135446Strhodes ***/
61135446Strhodes
62170222Sdougb/*! \file
63170222Sdougb * Client Routines
64170222Sdougb *
65135446Strhodes * Important note!
66135446Strhodes *
67135446Strhodes * All client state changes, other than that from idle to listening, occur
68135446Strhodes * as a result of events.  This guarantees serialization and avoids the
69135446Strhodes * need for locking.
70135446Strhodes *
71135446Strhodes * If a routine is ever created that allows someone other than the client's
72135446Strhodes * task to change the client, then the client will have to be locked.
73135446Strhodes */
74135446Strhodes
75135446Strhodes#define NS_CLIENT_TRACE
76135446Strhodes#ifdef NS_CLIENT_TRACE
77135446Strhodes#define CTRACE(m)	ns_client_log(client, \
78135446Strhodes				      NS_LOGCATEGORY_CLIENT, \
79135446Strhodes				      NS_LOGMODULE_CLIENT, \
80135446Strhodes				      ISC_LOG_DEBUG(3), \
81135446Strhodes				      "%s", (m))
82135446Strhodes#define MTRACE(m)	isc_log_write(ns_g_lctx, \
83135446Strhodes				      NS_LOGCATEGORY_GENERAL, \
84135446Strhodes				      NS_LOGMODULE_CLIENT, \
85135446Strhodes				      ISC_LOG_DEBUG(3), \
86135446Strhodes				      "clientmgr @%p: %s", manager, (m))
87135446Strhodes#else
88135446Strhodes#define CTRACE(m)	((void)(m))
89135446Strhodes#define MTRACE(m)	((void)(m))
90135446Strhodes#endif
91135446Strhodes
92135446Strhodes#define TCP_CLIENT(c)	(((c)->attributes & NS_CLIENTATTR_TCP) != 0)
93135446Strhodes
94135446Strhodes#define TCP_BUFFER_SIZE			(65535 + 2)
95135446Strhodes#define SEND_BUFFER_SIZE		4096
96135446Strhodes#define RECV_BUFFER_SIZE		4096
97135446Strhodes
98170222Sdougb#ifdef ISC_PLATFORM_USETHREADS
99170222Sdougb#define NMCTXS				100
100170222Sdougb/*%<
101170222Sdougb * Number of 'mctx pools' for clients. (Should this be configurable?)
102170222Sdougb * When enabling threads, we use a pool of memory contexts shared by
103170222Sdougb * client objects, since concurrent access to a shared context would cause
104170222Sdougb * heavy contentions.  The above constant is expected to be enough for
105170222Sdougb * completely avoiding contentions among threads for an authoritative-only
106170222Sdougb * server.
107170222Sdougb */
108170222Sdougb#else
109170222Sdougb#define NMCTXS				0
110170222Sdougb/*%<
111170222Sdougb * If named with built without thread, simply share manager's context.  Using
112170222Sdougb * a separate context in this case would simply waste memory.
113170222Sdougb */
114170222Sdougb#endif
115170222Sdougb
116170222Sdougb/*% nameserver client manager structure */
117135446Strhodesstruct ns_clientmgr {
118135446Strhodes	/* Unlocked. */
119135446Strhodes	unsigned int			magic;
120254897Serwin
121254897Serwin	/* The queue object has its own locks */
122254897Serwin	client_queue_t			inactive;     /*%< To be recycled */
123254897Serwin
124135446Strhodes	isc_mem_t *			mctx;
125135446Strhodes	isc_taskmgr_t *			taskmgr;
126135446Strhodes	isc_timermgr_t *		timermgr;
127254897Serwin
128254897Serwin	/* Lock covers manager state. */
129135446Strhodes	isc_mutex_t			lock;
130135446Strhodes	isc_boolean_t			exiting;
131254897Serwin
132254897Serwin	/* Lock covers the clients list */
133254897Serwin	isc_mutex_t			listlock;
134254897Serwin	client_list_t			clients;      /*%< All active clients */
135254897Serwin
136254897Serwin	/* Lock covers the recursing list */
137254897Serwin	isc_mutex_t			reclock;
138254897Serwin	client_list_t			recursing;    /*%< Recursing clients */
139254897Serwin
140170222Sdougb#if NMCTXS > 0
141170222Sdougb	/*%< mctx pool for clients. */
142170222Sdougb	unsigned int			nextmctx;
143170222Sdougb	isc_mem_t *			mctxpool[NMCTXS];
144170222Sdougb#endif
145135446Strhodes};
146135446Strhodes
147135446Strhodes#define MANAGER_MAGIC			ISC_MAGIC('N', 'S', 'C', 'm')
148135446Strhodes#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, MANAGER_MAGIC)
149135446Strhodes
150186462Sdougb/*!
151135446Strhodes * Client object states.  Ordering is significant: higher-numbered
152135446Strhodes * states are generally "more active", meaning that the client can
153135446Strhodes * have more dynamically allocated data, outstanding events, etc.
154135446Strhodes * In the list below, any such properties listed for state N
155135446Strhodes * also apply to any state > N.
156135446Strhodes *
157135446Strhodes * To force the client into a less active state, set client->newstate
158135446Strhodes * to that state and call exit_check().  This will cause any
159135446Strhodes * activities defined for higher-numbered states to be aborted.
160135446Strhodes */
161135446Strhodes
162135446Strhodes#define NS_CLIENTSTATE_FREED    0
163170222Sdougb/*%<
164135446Strhodes * The client object no longer exists.
165135446Strhodes */
166135446Strhodes
167135446Strhodes#define NS_CLIENTSTATE_INACTIVE 1
168170222Sdougb/*%<
169135446Strhodes * The client object exists and has a task and timer.
170135446Strhodes * Its "query" struct and sendbuf are initialized.
171135446Strhodes * It is on the client manager's list of inactive clients.
172135446Strhodes * It has a message and OPT, both in the reset state.
173135446Strhodes */
174135446Strhodes
175135446Strhodes#define NS_CLIENTSTATE_READY    2
176170222Sdougb/*%<
177135446Strhodes * The client object is either a TCP or a UDP one, and
178135446Strhodes * it is associated with a network interface.  It is on the
179135446Strhodes * client manager's list of active clients.
180135446Strhodes *
181135446Strhodes * If it is a TCP client object, it has a TCP listener socket
182135446Strhodes * and an outstanding TCP listen request.
183135446Strhodes *
184135446Strhodes * If it is a UDP client object, it has a UDP listener socket
185135446Strhodes * and an outstanding UDP receive request.
186135446Strhodes */
187135446Strhodes
188135446Strhodes#define NS_CLIENTSTATE_READING  3
189170222Sdougb/*%<
190135446Strhodes * The client object is a TCP client object that has received
191135446Strhodes * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
192135446Strhodes * outstanding TCP read request.  This state is not used for
193135446Strhodes * UDP client objects.
194135446Strhodes */
195135446Strhodes
196135446Strhodes#define NS_CLIENTSTATE_WORKING  4
197170222Sdougb/*%<
198135446Strhodes * The client object has received a request and is working
199135446Strhodes * on it.  It has a view, and it may have any of a non-reset OPT,
200135446Strhodes * recursion quota, and an outstanding write request.
201135446Strhodes */
202135446Strhodes
203254897Serwin#define NS_CLIENTSTATE_RECURSING  5
204254897Serwin/*%<
205254897Serwin * The client object is recursing.  It will be on the 'recursing'
206254897Serwin * list.
207254897Serwin */
208254897Serwin
209135446Strhodes#define NS_CLIENTSTATE_MAX      9
210170222Sdougb/*%<
211135446Strhodes * Sentinel value used to indicate "no state".  When client->newstate
212135446Strhodes * has this value, we are not attempting to exit the current state.
213135446Strhodes * Must be greater than any valid state.
214135446Strhodes */
215135446Strhodes
216165071Sdougb/*
217165071Sdougb * Enable ns_client_dropport() by default.
218165071Sdougb */
219165071Sdougb#ifndef NS_CLIENT_DROPPORT
220165071Sdougb#define NS_CLIENT_DROPPORT 1
221165071Sdougb#endif
222135446Strhodes
223170222Sdougbunsigned int ns_client_requests;
224170222Sdougb
225135446Strhodesstatic void client_read(ns_client_t *client);
226135446Strhodesstatic void client_accept(ns_client_t *client);
227135446Strhodesstatic void client_udprecv(ns_client_t *client);
228135446Strhodesstatic void clientmgr_destroy(ns_clientmgr_t *manager);
229135446Strhodesstatic isc_boolean_t exit_check(ns_client_t *client);
230135446Strhodesstatic void ns_client_endrequest(ns_client_t *client);
231135446Strhodesstatic void client_start(isc_task_t *task, isc_event_t *event);
232135446Strhodesstatic void client_request(isc_task_t *task, isc_event_t *event);
233135446Strhodesstatic void ns_client_dumpmessage(ns_client_t *client, const char *reason);
234254897Serwinstatic isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
235254897Serwin			       dns_dispatch_t *disp, isc_boolean_t tcp);
236135446Strhodes
237135446Strhodesvoid
238153816Sdougbns_client_recursing(ns_client_t *client) {
239135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
240254897Serwin	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
241135446Strhodes
242254897Serwin	LOCK(&client->manager->reclock);
243254897Serwin	client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
244254897Serwin	ISC_LIST_APPEND(client->manager->recursing, client, rlink);
245254897Serwin	UNLOCK(&client->manager->reclock);
246135446Strhodes}
247135446Strhodes
248135446Strhodesvoid
249153816Sdougbns_client_killoldestquery(ns_client_t *client) {
250153816Sdougb	ns_client_t *oldest;
251153816Sdougb	REQUIRE(NS_CLIENT_VALID(client));
252153816Sdougb
253254897Serwin	LOCK(&client->manager->reclock);
254153816Sdougb	oldest = ISC_LIST_HEAD(client->manager->recursing);
255153816Sdougb	if (oldest != NULL) {
256254897Serwin		ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
257254897Serwin		UNLOCK(&client->manager->reclock);
258153816Sdougb		ns_query_cancel(oldest);
259254897Serwin	} else
260254897Serwin		UNLOCK(&client->manager->reclock);
261153816Sdougb}
262153816Sdougb
263153816Sdougbvoid
264135446Strhodesns_client_settimeout(ns_client_t *client, unsigned int seconds) {
265135446Strhodes	isc_result_t result;
266135446Strhodes	isc_interval_t interval;
267135446Strhodes
268135446Strhodes	isc_interval_set(&interval, seconds, 0);
269135446Strhodes	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
270135446Strhodes				 &interval, ISC_FALSE);
271135446Strhodes	client->timerset = ISC_TRUE;
272135446Strhodes	if (result != ISC_R_SUCCESS) {
273135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
274135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
275135446Strhodes			      "setting timeout: %s",
276135446Strhodes			      isc_result_totext(result));
277135446Strhodes		/* Continue anyway. */
278135446Strhodes	}
279135446Strhodes}
280135446Strhodes
281170222Sdougb/*%
282135446Strhodes * Check for a deactivation or shutdown request and take appropriate
283135446Strhodes * action.  Returns ISC_TRUE if either is in progress; in this case
284135446Strhodes * the caller must no longer use the client object as it may have been
285135446Strhodes * freed.
286135446Strhodes */
287135446Strhodesstatic isc_boolean_t
288135446Strhodesexit_check(ns_client_t *client) {
289254897Serwin	isc_boolean_t destroy_manager = ISC_FALSE;
290254897Serwin	ns_clientmgr_t *manager = NULL;
291135446Strhodes
292135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
293254897Serwin	manager = client->manager;
294135446Strhodes
295135446Strhodes	if (client->state <= client->newstate)
296135446Strhodes		return (ISC_FALSE); /* Business as usual. */
297135446Strhodes
298254897Serwin	INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
299135446Strhodes
300135446Strhodes	/*
301135446Strhodes	 * We need to detach from the view early when shutting down
302135446Strhodes	 * the server to break the following vicious circle:
303135446Strhodes	 *
304135446Strhodes	 *  - The resolver will not shut down until the view refcount is zero
305135446Strhodes	 *  - The view refcount does not go to zero until all clients detach
306135446Strhodes	 *  - The client does not detach from the view until references is zero
307135446Strhodes	 *  - references does not go to zero until the resolver has shut down
308135446Strhodes	 *
309135446Strhodes	 * Keep the view attached until any outstanding updates complete.
310135446Strhodes	 */
311186462Sdougb	if (client->nupdates == 0 &&
312135446Strhodes	    client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
313135446Strhodes		dns_view_detach(&client->view);
314135446Strhodes
315254897Serwin	if (client->state == NS_CLIENTSTATE_WORKING ||
316254897Serwin	    client->state == NS_CLIENTSTATE_RECURSING)
317254897Serwin	{
318135446Strhodes		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
319135446Strhodes		/*
320135446Strhodes		 * Let the update processing complete.
321135446Strhodes		 */
322135446Strhodes		if (client->nupdates > 0)
323135446Strhodes			return (ISC_TRUE);
324254897Serwin
325135446Strhodes		/*
326135446Strhodes		 * We are trying to abort request processing.
327135446Strhodes		 */
328135446Strhodes		if (client->nsends > 0) {
329135446Strhodes			isc_socket_t *socket;
330135446Strhodes			if (TCP_CLIENT(client))
331135446Strhodes				socket = client->tcpsocket;
332135446Strhodes			else
333135446Strhodes				socket = client->udpsocket;
334135446Strhodes			isc_socket_cancel(socket, client->task,
335135446Strhodes					  ISC_SOCKCANCEL_SEND);
336135446Strhodes		}
337135446Strhodes
338135446Strhodes		if (! (client->nsends == 0 && client->nrecvs == 0 &&
339135446Strhodes		       client->references == 0))
340135446Strhodes		{
341135446Strhodes			/*
342135446Strhodes			 * Still waiting for I/O cancel completion.
343135446Strhodes			 * or lingering references.
344135446Strhodes			 */
345135446Strhodes			return (ISC_TRUE);
346135446Strhodes		}
347254897Serwin
348135446Strhodes		/*
349135446Strhodes		 * I/O cancel is complete.  Burn down all state
350165071Sdougb		 * related to the current request.  Ensure that
351254897Serwin		 * the client is no longer on the recursing list.
352254897Serwin		 *
353254897Serwin		 * We need to check whether the client is still linked,
354254897Serwin		 * because it may already have been removed from the
355254897Serwin		 * recursing list by ns_client_killoldestquery()
356135446Strhodes		 */
357254897Serwin		if (client->state == NS_CLIENTSTATE_RECURSING) {
358254897Serwin			LOCK(&manager->reclock);
359254897Serwin			if (ISC_LINK_LINKED(client, rlink))
360254897Serwin				ISC_LIST_UNLINK(manager->recursing,
361254897Serwin						client, rlink);
362254897Serwin			UNLOCK(&manager->reclock);
363165071Sdougb		}
364135446Strhodes		ns_client_endrequest(client);
365135446Strhodes
366135446Strhodes		client->state = NS_CLIENTSTATE_READING;
367135446Strhodes		INSIST(client->recursionquota == NULL);
368254897Serwin
369135446Strhodes		if (NS_CLIENTSTATE_READING == client->newstate) {
370135446Strhodes			client_read(client);
371135446Strhodes			client->newstate = NS_CLIENTSTATE_MAX;
372135446Strhodes			return (ISC_TRUE); /* We're done. */
373135446Strhodes		}
374135446Strhodes	}
375135446Strhodes
376135446Strhodes	if (client->state == NS_CLIENTSTATE_READING) {
377135446Strhodes		/*
378135446Strhodes		 * We are trying to abort the current TCP connection,
379135446Strhodes		 * if any.
380135446Strhodes		 */
381135446Strhodes		INSIST(client->recursionquota == NULL);
382135446Strhodes		INSIST(client->newstate <= NS_CLIENTSTATE_READY);
383135446Strhodes		if (client->nreads > 0)
384135446Strhodes			dns_tcpmsg_cancelread(&client->tcpmsg);
385135446Strhodes		if (! client->nreads == 0) {
386135446Strhodes			/* Still waiting for read cancel completion. */
387135446Strhodes			return (ISC_TRUE);
388135446Strhodes		}
389135446Strhodes
390135446Strhodes		if (client->tcpmsg_valid) {
391135446Strhodes			dns_tcpmsg_invalidate(&client->tcpmsg);
392135446Strhodes			client->tcpmsg_valid = ISC_FALSE;
393135446Strhodes		}
394135446Strhodes		if (client->tcpsocket != NULL) {
395135446Strhodes			CTRACE("closetcp");
396135446Strhodes			isc_socket_detach(&client->tcpsocket);
397135446Strhodes		}
398135446Strhodes
399135446Strhodes		if (client->tcpquota != NULL)
400135446Strhodes			isc_quota_detach(&client->tcpquota);
401135446Strhodes
402135446Strhodes		if (client->timerset) {
403135446Strhodes			(void)isc_timer_reset(client->timer,
404135446Strhodes					      isc_timertype_inactive,
405135446Strhodes					      NULL, NULL, ISC_TRUE);
406135446Strhodes			client->timerset = ISC_FALSE;
407135446Strhodes		}
408135446Strhodes
409135446Strhodes		client->peeraddr_valid = ISC_FALSE;
410135446Strhodes
411135446Strhodes		client->state = NS_CLIENTSTATE_READY;
412135446Strhodes		INSIST(client->recursionquota == NULL);
413135446Strhodes
414135446Strhodes		/*
415135446Strhodes		 * Now the client is ready to accept a new TCP connection
416135446Strhodes		 * or UDP request, but we may have enough clients doing
417135446Strhodes		 * that already.  Check whether this client needs to remain
418135446Strhodes		 * active and force it to go inactive if not.
419254897Serwin		 *
420254897Serwin		 * UDP clients go inactive at this point, but TCP clients
421254897Serwin		 * may remain active if we have fewer active TCP client
422254897Serwin		 * objects than desired due to an earlier quota exhaustion.
423135446Strhodes		 */
424254897Serwin		if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
425254897Serwin			LOCK(&client->interface->lock);
426254897Serwin			if (client->interface->ntcpcurrent <
427254897Serwin				    client->interface->ntcptarget)
428254897Serwin				client->mortal = ISC_FALSE;
429254897Serwin			UNLOCK(&client->interface->lock);
430254897Serwin		}
431135446Strhodes
432254897Serwin		/*
433254897Serwin		 * We don't need the client; send it to the inactive
434254897Serwin		 * queue for recycling.
435254897Serwin		 */
436254897Serwin		if (client->mortal) {
437254897Serwin			if (client->newstate > NS_CLIENTSTATE_INACTIVE)
438254897Serwin				client->newstate = NS_CLIENTSTATE_INACTIVE;
439254897Serwin		}
440254897Serwin
441135446Strhodes		if (NS_CLIENTSTATE_READY == client->newstate) {
442135446Strhodes			if (TCP_CLIENT(client)) {
443135446Strhodes				client_accept(client);
444135446Strhodes			} else
445135446Strhodes				client_udprecv(client);
446135446Strhodes			client->newstate = NS_CLIENTSTATE_MAX;
447135446Strhodes			return (ISC_TRUE);
448135446Strhodes		}
449135446Strhodes	}
450135446Strhodes
451135446Strhodes	if (client->state == NS_CLIENTSTATE_READY) {
452135446Strhodes		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
453254897Serwin
454135446Strhodes		/*
455135446Strhodes		 * We are trying to enter the inactive state.
456135446Strhodes		 */
457135446Strhodes		if (client->naccepts > 0)
458135446Strhodes			isc_socket_cancel(client->tcplistener, client->task,
459135446Strhodes					  ISC_SOCKCANCEL_ACCEPT);
460135446Strhodes
461254897Serwin		/* Still waiting for accept cancel completion. */
462254897Serwin		if (! (client->naccepts == 0))
463135446Strhodes			return (ISC_TRUE);
464254897Serwin
465135446Strhodes		/* Accept cancel is complete. */
466135446Strhodes		if (client->nrecvs > 0)
467135446Strhodes			isc_socket_cancel(client->udpsocket, client->task,
468135446Strhodes					  ISC_SOCKCANCEL_RECV);
469254897Serwin
470254897Serwin		/* Still waiting for recv cancel completion. */
471254897Serwin		if (! (client->nrecvs == 0))
472135446Strhodes			return (ISC_TRUE);
473135446Strhodes
474254897Serwin		/* Still waiting for control event to be delivered */
475254897Serwin		if (client->nctls > 0)
476135446Strhodes			return (ISC_TRUE);
477135446Strhodes
478135446Strhodes		/* Deactivate the client. */
479135446Strhodes		if (client->interface)
480135446Strhodes			ns_interface_detach(&client->interface);
481135446Strhodes
482135446Strhodes		INSIST(client->naccepts == 0);
483135446Strhodes		INSIST(client->recursionquota == NULL);
484135446Strhodes		if (client->tcplistener != NULL)
485135446Strhodes			isc_socket_detach(&client->tcplistener);
486135446Strhodes
487135446Strhodes		if (client->udpsocket != NULL)
488135446Strhodes			isc_socket_detach(&client->udpsocket);
489135446Strhodes
490135446Strhodes		if (client->dispatch != NULL)
491135446Strhodes			dns_dispatch_detach(&client->dispatch);
492135446Strhodes
493135446Strhodes		client->attributes = 0;
494135446Strhodes		client->mortal = ISC_FALSE;
495135446Strhodes
496135446Strhodes		/*
497135446Strhodes		 * Put the client on the inactive list.  If we are aiming for
498135446Strhodes		 * the "freed" state, it will be removed from the inactive
499135446Strhodes		 * list shortly, and we need to keep the manager locked until
500135446Strhodes		 * that has been done, lest the manager decide to reactivate
501135446Strhodes		 * the dying client inbetween.
502135446Strhodes		 */
503135446Strhodes		client->state = NS_CLIENTSTATE_INACTIVE;
504135446Strhodes		INSIST(client->recursionquota == NULL);
505135446Strhodes
506135446Strhodes		if (client->state == client->newstate) {
507135446Strhodes			client->newstate = NS_CLIENTSTATE_MAX;
508254897Serwin			if (!ns_g_clienttest && manager != NULL &&
509254897Serwin			    !manager->exiting)
510254897Serwin				ISC_QUEUE_PUSH(manager->inactive, client,
511254897Serwin					       ilink);
512193149Sdougb			if (client->needshutdown)
513193149Sdougb				isc_task_shutdown(client->task);
514254897Serwin			return (ISC_TRUE);
515135446Strhodes		}
516135446Strhodes	}
517135446Strhodes
518135446Strhodes	if (client->state == NS_CLIENTSTATE_INACTIVE) {
519135446Strhodes		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
520135446Strhodes		/*
521135446Strhodes		 * We are trying to free the client.
522135446Strhodes		 *
523135446Strhodes		 * When "shuttingdown" is true, either the task has received
524135446Strhodes		 * its shutdown event or no shutdown event has ever been
525135446Strhodes		 * set up.  Thus, we have no outstanding shutdown
526135446Strhodes		 * event at this point.
527135446Strhodes		 */
528135446Strhodes		REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
529135446Strhodes
530135446Strhodes		INSIST(client->recursionquota == NULL);
531254897Serwin		INSIST(!ISC_QLINK_LINKED(client, ilink));
532135446Strhodes
533135446Strhodes		ns_query_free(client);
534135446Strhodes		isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
535135446Strhodes		isc_event_free((isc_event_t **)&client->sendevent);
536135446Strhodes		isc_event_free((isc_event_t **)&client->recvevent);
537135446Strhodes		isc_timer_detach(&client->timer);
538135446Strhodes
539135446Strhodes		if (client->tcpbuf != NULL)
540254897Serwin			isc_mem_put(client->mctx, client->tcpbuf,
541254897Serwin				    TCP_BUFFER_SIZE);
542135446Strhodes		if (client->opt != NULL) {
543135446Strhodes			INSIST(dns_rdataset_isassociated(client->opt));
544135446Strhodes			dns_rdataset_disassociate(client->opt);
545254897Serwin			dns_message_puttemprdataset(client->message,
546254897Serwin						    &client->opt);
547135446Strhodes		}
548254897Serwin
549135446Strhodes		dns_message_destroy(&client->message);
550254897Serwin		if (manager != NULL) {
551254897Serwin			LOCK(&manager->listlock);
552254897Serwin			ISC_LIST_UNLINK(manager->clients, client, link);
553254897Serwin			LOCK(&manager->lock);
554135446Strhodes			if (manager->exiting &&
555254897Serwin			    ISC_LIST_EMPTY(manager->clients))
556254897Serwin				destroy_manager = ISC_TRUE;
557254897Serwin			UNLOCK(&manager->lock);
558254897Serwin			UNLOCK(&manager->listlock);
559135446Strhodes		}
560254897Serwin
561135446Strhodes		/*
562135446Strhodes		 * Detaching the task must be done after unlinking from
563135446Strhodes		 * the manager's lists because the manager accesses
564135446Strhodes		 * client->task.
565135446Strhodes		 */
566135446Strhodes		if (client->task != NULL)
567135446Strhodes			isc_task_detach(&client->task);
568135446Strhodes
569135446Strhodes		CTRACE("free");
570135446Strhodes		client->magic = 0;
571254897Serwin
572193149Sdougb		/*
573193149Sdougb		 * Check that there are no other external references to
574193149Sdougb		 * the memory context.
575193149Sdougb		 */
576193149Sdougb		if (ns_g_clienttest && isc_mem_references(client->mctx) != 1) {
577193149Sdougb			isc_mem_stats(client->mctx, stderr);
578193149Sdougb			INSIST(0);
579193149Sdougb		}
580170222Sdougb		isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
581135446Strhodes	}
582135446Strhodes
583254897Serwin	if (destroy_manager && manager != NULL)
584254897Serwin		clientmgr_destroy(manager);
585135446Strhodes
586135446Strhodes	return (ISC_TRUE);
587135446Strhodes}
588135446Strhodes
589170222Sdougb/*%
590135446Strhodes * The client's task has received the client's control event
591135446Strhodes * as part of the startup process.
592135446Strhodes */
593135446Strhodesstatic void
594135446Strhodesclient_start(isc_task_t *task, isc_event_t *event) {
595135446Strhodes	ns_client_t *client = (ns_client_t *) event->ev_arg;
596135446Strhodes
597135446Strhodes	INSIST(task == client->task);
598135446Strhodes
599135446Strhodes	UNUSED(task);
600135446Strhodes
601135446Strhodes	INSIST(client->nctls == 1);
602135446Strhodes	client->nctls--;
603135446Strhodes
604135446Strhodes	if (exit_check(client))
605135446Strhodes		return;
606135446Strhodes
607135446Strhodes	if (TCP_CLIENT(client)) {
608135446Strhodes		client_accept(client);
609135446Strhodes	} else {
610135446Strhodes		client_udprecv(client);
611135446Strhodes	}
612135446Strhodes}
613135446Strhodes
614135446Strhodes
615170222Sdougb/*%
616135446Strhodes * The client's task has received a shutdown event.
617135446Strhodes */
618135446Strhodesstatic void
619135446Strhodesclient_shutdown(isc_task_t *task, isc_event_t *event) {
620135446Strhodes	ns_client_t *client;
621135446Strhodes
622135446Strhodes	REQUIRE(event != NULL);
623135446Strhodes	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
624135446Strhodes	client = event->ev_arg;
625135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
626135446Strhodes	REQUIRE(task == client->task);
627135446Strhodes
628135446Strhodes	UNUSED(task);
629135446Strhodes
630135446Strhodes	CTRACE("shutdown");
631135446Strhodes
632135446Strhodes	isc_event_free(&event);
633135446Strhodes
634135446Strhodes	if (client->shutdown != NULL) {
635135446Strhodes		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
636135446Strhodes		client->shutdown = NULL;
637135446Strhodes		client->shutdown_arg = NULL;
638135446Strhodes	}
639135446Strhodes
640254897Serwin	if (ISC_QLINK_LINKED(client, ilink))
641254897Serwin		ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
642254897Serwin
643135446Strhodes	client->newstate = NS_CLIENTSTATE_FREED;
644193149Sdougb	client->needshutdown = ISC_FALSE;
645135446Strhodes	(void)exit_check(client);
646135446Strhodes}
647135446Strhodes
648135446Strhodesstatic void
649135446Strhodesns_client_endrequest(ns_client_t *client) {
650135446Strhodes	INSIST(client->naccepts == 0);
651135446Strhodes	INSIST(client->nreads == 0);
652135446Strhodes	INSIST(client->nsends == 0);
653135446Strhodes	INSIST(client->nrecvs == 0);
654135446Strhodes	INSIST(client->nupdates == 0);
655254897Serwin	INSIST(client->state == NS_CLIENTSTATE_WORKING ||
656254897Serwin	       client->state == NS_CLIENTSTATE_RECURSING);
657135446Strhodes
658135446Strhodes	CTRACE("endrequest");
659135446Strhodes
660135446Strhodes	if (client->next != NULL) {
661135446Strhodes		(client->next)(client);
662135446Strhodes		client->next = NULL;
663135446Strhodes	}
664135446Strhodes
665135446Strhodes	if (client->view != NULL)
666135446Strhodes		dns_view_detach(&client->view);
667135446Strhodes	if (client->opt != NULL) {
668135446Strhodes		INSIST(dns_rdataset_isassociated(client->opt));
669135446Strhodes		dns_rdataset_disassociate(client->opt);
670135446Strhodes		dns_message_puttemprdataset(client->message, &client->opt);
671135446Strhodes	}
672135446Strhodes
673225361Sdougb	client->signer = NULL;
674135446Strhodes	client->udpsize = 512;
675135446Strhodes	client->extflags = 0;
676170222Sdougb	client->ednsversion = -1;
677135446Strhodes	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
678135446Strhodes
679135446Strhodes	if (client->recursionquota != NULL)
680135446Strhodes		isc_quota_detach(&client->recursionquota);
681135446Strhodes
682135446Strhodes	/*
683135446Strhodes	 * Clear all client attributes that are specific to
684135446Strhodes	 * the request; that's all except the TCP flag.
685135446Strhodes	 */
686135446Strhodes	client->attributes &= NS_CLIENTATTR_TCP;
687135446Strhodes}
688135446Strhodes
689135446Strhodesvoid
690135446Strhodesns_client_next(ns_client_t *client, isc_result_t result) {
691135446Strhodes	int newstate;
692135446Strhodes
693135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
694135446Strhodes	REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
695254897Serwin		client->state == NS_CLIENTSTATE_RECURSING ||
696135446Strhodes		client->state == NS_CLIENTSTATE_READING);
697135446Strhodes
698135446Strhodes	CTRACE("next");
699135446Strhodes
700135446Strhodes	if (result != ISC_R_SUCCESS)
701135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
702135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
703135446Strhodes			      "request failed: %s", isc_result_totext(result));
704135446Strhodes
705135446Strhodes	/*
706135446Strhodes	 * An error processing a TCP request may have left
707135446Strhodes	 * the connection out of sync.  To be safe, we always
708135446Strhodes	 * sever the connection when result != ISC_R_SUCCESS.
709135446Strhodes	 */
710135446Strhodes	if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
711135446Strhodes		newstate = NS_CLIENTSTATE_READING;
712135446Strhodes	else
713135446Strhodes		newstate = NS_CLIENTSTATE_READY;
714135446Strhodes
715135446Strhodes	if (client->newstate > newstate)
716135446Strhodes		client->newstate = newstate;
717135446Strhodes	(void)exit_check(client);
718135446Strhodes}
719135446Strhodes
720135446Strhodes
721135446Strhodesstatic void
722135446Strhodesclient_senddone(isc_task_t *task, isc_event_t *event) {
723135446Strhodes	ns_client_t *client;
724135446Strhodes	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
725135446Strhodes
726135446Strhodes	REQUIRE(sevent != NULL);
727135446Strhodes	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
728135446Strhodes	client = sevent->ev_arg;
729135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
730135446Strhodes	REQUIRE(task == client->task);
731135446Strhodes	REQUIRE(sevent == client->sendevent);
732135446Strhodes
733135446Strhodes	UNUSED(task);
734135446Strhodes
735135446Strhodes	CTRACE("senddone");
736135446Strhodes
737135446Strhodes	if (sevent->result != ISC_R_SUCCESS)
738135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
739135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
740135446Strhodes			      "error sending response: %s",
741135446Strhodes			      isc_result_totext(sevent->result));
742135446Strhodes
743135446Strhodes	INSIST(client->nsends > 0);
744135446Strhodes	client->nsends--;
745135446Strhodes
746135446Strhodes	if (client->tcpbuf != NULL) {
747135446Strhodes		INSIST(TCP_CLIENT(client));
748135446Strhodes		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
749135446Strhodes		client->tcpbuf = NULL;
750135446Strhodes	}
751135446Strhodes
752135446Strhodes	ns_client_next(client, ISC_R_SUCCESS);
753135446Strhodes}
754135446Strhodes
755170222Sdougb/*%
756135446Strhodes * We only want to fail with ISC_R_NOSPACE when called from
757135446Strhodes * ns_client_sendraw() and not when called from ns_client_send(),
758135446Strhodes * tcpbuffer is NULL when called from ns_client_sendraw() and
759135446Strhodes * length != 0.  tcpbuffer != NULL when called from ns_client_send()
760135446Strhodes * and length == 0.
761135446Strhodes */
762135446Strhodes
763135446Strhodesstatic isc_result_t
764135446Strhodesclient_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
765135446Strhodes		    isc_buffer_t *tcpbuffer, isc_uint32_t length,
766135446Strhodes		    unsigned char *sendbuf, unsigned char **datap)
767135446Strhodes{
768135446Strhodes	unsigned char *data;
769135446Strhodes	isc_uint32_t bufsize;
770135446Strhodes	isc_result_t result;
771135446Strhodes
772135446Strhodes	INSIST(datap != NULL);
773135446Strhodes	INSIST((tcpbuffer == NULL && length != 0) ||
774135446Strhodes	       (tcpbuffer != NULL && length == 0));
775135446Strhodes
776135446Strhodes	if (TCP_CLIENT(client)) {
777135446Strhodes		INSIST(client->tcpbuf == NULL);
778135446Strhodes		if (length + 2 > TCP_BUFFER_SIZE) {
779135446Strhodes			result = ISC_R_NOSPACE;
780135446Strhodes			goto done;
781135446Strhodes		}
782135446Strhodes		client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
783135446Strhodes		if (client->tcpbuf == NULL) {
784135446Strhodes			result = ISC_R_NOMEMORY;
785135446Strhodes			goto done;
786135446Strhodes		}
787135446Strhodes		data = client->tcpbuf;
788135446Strhodes		if (tcpbuffer != NULL) {
789135446Strhodes			isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
790135446Strhodes			isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
791135446Strhodes		} else {
792135446Strhodes			isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
793135446Strhodes			INSIST(length <= 0xffff);
794135446Strhodes			isc_buffer_putuint16(buffer, (isc_uint16_t)length);
795135446Strhodes		}
796135446Strhodes	} else {
797135446Strhodes		data = sendbuf;
798135446Strhodes		if (client->udpsize < SEND_BUFFER_SIZE)
799135446Strhodes			bufsize = client->udpsize;
800135446Strhodes		else
801135446Strhodes			bufsize = SEND_BUFFER_SIZE;
802135446Strhodes		if (length > bufsize) {
803135446Strhodes			result = ISC_R_NOSPACE;
804135446Strhodes			goto done;
805135446Strhodes		}
806135446Strhodes		isc_buffer_init(buffer, data, bufsize);
807135446Strhodes	}
808135446Strhodes	*datap = data;
809135446Strhodes	result = ISC_R_SUCCESS;
810135446Strhodes
811135446Strhodes done:
812135446Strhodes	return (result);
813135446Strhodes}
814135446Strhodes
815135446Strhodesstatic isc_result_t
816135446Strhodesclient_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
817135446Strhodes	struct in6_pktinfo *pktinfo;
818135446Strhodes	isc_result_t result;
819135446Strhodes	isc_region_t r;
820135446Strhodes	isc_sockaddr_t *address;
821135446Strhodes	isc_socket_t *socket;
822135446Strhodes	isc_netaddr_t netaddr;
823135446Strhodes	int match;
824135446Strhodes	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
825135446Strhodes
826135446Strhodes	if (TCP_CLIENT(client)) {
827135446Strhodes		socket = client->tcpsocket;
828135446Strhodes		address = NULL;
829135446Strhodes	} else {
830135446Strhodes		socket = client->udpsocket;
831135446Strhodes		address = &client->peeraddr;
832135446Strhodes
833135446Strhodes		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
834135446Strhodes		if (ns_g_server->blackholeacl != NULL &&
835135446Strhodes		    dns_acl_match(&netaddr, NULL,
836186462Sdougb				  ns_g_server->blackholeacl,
837135446Strhodes				  &ns_g_server->aclenv,
838135446Strhodes				  &match, NULL) == ISC_R_SUCCESS &&
839135446Strhodes		    match > 0)
840135446Strhodes			return (DNS_R_BLACKHOLED);
841135446Strhodes		sockflags |= ISC_SOCKFLAG_NORETRY;
842135446Strhodes	}
843135446Strhodes
844135446Strhodes	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
845135446Strhodes	    (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
846135446Strhodes		pktinfo = &client->pktinfo;
847135446Strhodes	else
848135446Strhodes		pktinfo = NULL;
849135446Strhodes
850135446Strhodes	isc_buffer_usedregion(buffer, &r);
851135446Strhodes
852135446Strhodes	CTRACE("sendto");
853186462Sdougb
854135446Strhodes	result = isc_socket_sendto2(socket, &r, client->task,
855135446Strhodes				    address, pktinfo,
856135446Strhodes				    client->sendevent, sockflags);
857135446Strhodes	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
858135446Strhodes		client->nsends++;
859135446Strhodes		if (result == ISC_R_SUCCESS)
860135446Strhodes			client_senddone(client->task,
861135446Strhodes					(isc_event_t *)client->sendevent);
862135446Strhodes		result = ISC_R_SUCCESS;
863135446Strhodes	}
864135446Strhodes	return (result);
865135446Strhodes}
866135446Strhodes
867135446Strhodesvoid
868135446Strhodesns_client_sendraw(ns_client_t *client, dns_message_t *message) {
869135446Strhodes	isc_result_t result;
870135446Strhodes	unsigned char *data;
871135446Strhodes	isc_buffer_t buffer;
872135446Strhodes	isc_region_t r;
873135446Strhodes	isc_region_t *mr;
874135446Strhodes	unsigned char sendbuf[SEND_BUFFER_SIZE];
875135446Strhodes
876135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
877135446Strhodes
878135446Strhodes	CTRACE("sendraw");
879135446Strhodes
880135446Strhodes	mr = dns_message_getrawmessage(message);
881135446Strhodes	if (mr == NULL) {
882135446Strhodes		result = ISC_R_UNEXPECTEDEND;
883135446Strhodes		goto done;
884135446Strhodes	}
885135446Strhodes
886135446Strhodes	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
887135446Strhodes				     sendbuf, &data);
888135446Strhodes	if (result != ISC_R_SUCCESS)
889135446Strhodes		goto done;
890135446Strhodes
891135446Strhodes	/*
892135446Strhodes	 * Copy message to buffer and fixup id.
893135446Strhodes	 */
894135446Strhodes	isc_buffer_availableregion(&buffer, &r);
895135446Strhodes	result = isc_buffer_copyregion(&buffer, mr);
896135446Strhodes	if (result != ISC_R_SUCCESS)
897135446Strhodes		goto done;
898135446Strhodes	r.base[0] = (client->message->id >> 8) & 0xff;
899135446Strhodes	r.base[1] = client->message->id & 0xff;
900135446Strhodes
901135446Strhodes	result = client_sendpkg(client, &buffer);
902135446Strhodes	if (result == ISC_R_SUCCESS)
903135446Strhodes		return;
904135446Strhodes
905135446Strhodes done:
906135446Strhodes	if (client->tcpbuf != NULL) {
907135446Strhodes		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
908135446Strhodes		client->tcpbuf = NULL;
909135446Strhodes	}
910135446Strhodes	ns_client_next(client, result);
911135446Strhodes}
912135446Strhodes
913135446Strhodesvoid
914135446Strhodesns_client_send(ns_client_t *client) {
915135446Strhodes	isc_result_t result;
916135446Strhodes	unsigned char *data;
917135446Strhodes	isc_buffer_t buffer;
918135446Strhodes	isc_buffer_t tcpbuffer;
919135446Strhodes	isc_region_t r;
920135446Strhodes	dns_compress_t cctx;
921135446Strhodes	isc_boolean_t cleanup_cctx = ISC_FALSE;
922135446Strhodes	unsigned char sendbuf[SEND_BUFFER_SIZE];
923224092Sdougb	unsigned int render_opts;
924135446Strhodes	unsigned int preferred_glue;
925193149Sdougb	isc_boolean_t opt_included = ISC_FALSE;
926135446Strhodes
927135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
928135446Strhodes
929135446Strhodes	CTRACE("send");
930135446Strhodes
931135446Strhodes	if ((client->attributes & NS_CLIENTATTR_RA) != 0)
932135446Strhodes		client->message->flags |= DNS_MESSAGEFLAG_RA;
933135446Strhodes
934135446Strhodes	if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
935224092Sdougb		render_opts = 0;
936135446Strhodes	else
937224092Sdougb		render_opts = DNS_MESSAGERENDER_OMITDNSSEC;
938234010Sdougb
939234010Sdougb	preferred_glue = 0;
940234010Sdougb	if (client->view != NULL) {
941234010Sdougb		if (client->view->preferred_glue == dns_rdatatype_a)
942234010Sdougb			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
943234010Sdougb		else if (client->view->preferred_glue == dns_rdatatype_aaaa)
944234010Sdougb			preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
945234010Sdougb	}
946234010Sdougb
947224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
948224092Sdougb	/*
949224092Sdougb	 * filter-aaaa-on-v4 yes or break-dnssec option to suppress
950224092Sdougb	 * AAAA records
951224092Sdougb	 * We already know that request came via IPv4,
952224092Sdougb	 * that we have both AAAA and A records,
953224092Sdougb	 * and that we either have no signatures that the client wants
954224092Sdougb	 * or we are supposed to break DNSSEC.
955234010Sdougb	 *
956234010Sdougb	 * Override preferred glue if necessary.
957224092Sdougb	 */
958234010Sdougb	if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
959224092Sdougb		render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
960234010Sdougb		if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
961135446Strhodes			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
962135446Strhodes	}
963234010Sdougb#endif
964135446Strhodes
965135446Strhodes	/*
966135446Strhodes	 * XXXRTH  The following doesn't deal with TCP buffer resizing.
967135446Strhodes	 */
968135446Strhodes	result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
969135446Strhodes				     sendbuf, &data);
970135446Strhodes	if (result != ISC_R_SUCCESS)
971135446Strhodes		goto done;
972135446Strhodes
973135446Strhodes	result = dns_compress_init(&cctx, -1, client->mctx);
974135446Strhodes	if (result != ISC_R_SUCCESS)
975135446Strhodes		goto done;
976262706Serwin	dns_compress_setsensitive(&cctx, ISC_TRUE);
977135446Strhodes	cleanup_cctx = ISC_TRUE;
978135446Strhodes
979135446Strhodes	result = dns_message_renderbegin(client->message, &cctx, &buffer);
980135446Strhodes	if (result != ISC_R_SUCCESS)
981135446Strhodes		goto done;
982193149Sdougb
983135446Strhodes	if (client->opt != NULL) {
984135446Strhodes		result = dns_message_setopt(client->message, client->opt);
985193149Sdougb		opt_included = ISC_TRUE;
986135446Strhodes		client->opt = NULL;
987135446Strhodes		if (result != ISC_R_SUCCESS)
988135446Strhodes			goto done;
989135446Strhodes	}
990135446Strhodes	result = dns_message_rendersection(client->message,
991135446Strhodes					   DNS_SECTION_QUESTION, 0);
992135446Strhodes	if (result == ISC_R_NOSPACE) {
993135446Strhodes		client->message->flags |= DNS_MESSAGEFLAG_TC;
994135446Strhodes		goto renderend;
995135446Strhodes	}
996135446Strhodes	if (result != ISC_R_SUCCESS)
997135446Strhodes		goto done;
998262706Serwin#ifdef USE_RRL
999262706Serwin	/*
1000262706Serwin	 * Stop after the question if TC was set for rate limiting.
1001262706Serwin	 */
1002262706Serwin	if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1003262706Serwin		goto renderend;
1004262706Serwin#endif /* USE_RRL */
1005135446Strhodes	result = dns_message_rendersection(client->message,
1006135446Strhodes					   DNS_SECTION_ANSWER,
1007135446Strhodes					   DNS_MESSAGERENDER_PARTIAL |
1008224092Sdougb					   render_opts);
1009135446Strhodes	if (result == ISC_R_NOSPACE) {
1010135446Strhodes		client->message->flags |= DNS_MESSAGEFLAG_TC;
1011135446Strhodes		goto renderend;
1012135446Strhodes	}
1013135446Strhodes	if (result != ISC_R_SUCCESS)
1014135446Strhodes		goto done;
1015135446Strhodes	result = dns_message_rendersection(client->message,
1016135446Strhodes					   DNS_SECTION_AUTHORITY,
1017135446Strhodes					   DNS_MESSAGERENDER_PARTIAL |
1018224092Sdougb					   render_opts);
1019135446Strhodes	if (result == ISC_R_NOSPACE) {
1020135446Strhodes		client->message->flags |= DNS_MESSAGEFLAG_TC;
1021135446Strhodes		goto renderend;
1022135446Strhodes	}
1023135446Strhodes	if (result != ISC_R_SUCCESS)
1024135446Strhodes		goto done;
1025135446Strhodes	result = dns_message_rendersection(client->message,
1026135446Strhodes					   DNS_SECTION_ADDITIONAL,
1027224092Sdougb					   preferred_glue | render_opts);
1028135446Strhodes	if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
1029135446Strhodes		goto done;
1030135446Strhodes renderend:
1031135446Strhodes	result = dns_message_renderend(client->message);
1032135446Strhodes
1033135446Strhodes	if (result != ISC_R_SUCCESS)
1034135446Strhodes		goto done;
1035135446Strhodes
1036135446Strhodes	if (cleanup_cctx) {
1037135446Strhodes		dns_compress_invalidate(&cctx);
1038135446Strhodes		cleanup_cctx = ISC_FALSE;
1039135446Strhodes	}
1040135446Strhodes
1041135446Strhodes	if (TCP_CLIENT(client)) {
1042135446Strhodes		isc_buffer_usedregion(&buffer, &r);
1043135446Strhodes		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
1044135446Strhodes		isc_buffer_add(&tcpbuffer, r.length);
1045135446Strhodes		result = client_sendpkg(client, &tcpbuffer);
1046135446Strhodes	} else
1047135446Strhodes		result = client_sendpkg(client, &buffer);
1048193149Sdougb
1049193149Sdougb	/* update statistics (XXXJT: is it okay to access message->xxxkey?) */
1050193149Sdougb	isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_response);
1051193149Sdougb	if (opt_included) {
1052193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1053193149Sdougb				    dns_nsstatscounter_edns0out);
1054193149Sdougb	}
1055193149Sdougb	if (client->message->tsigkey != NULL) {
1056193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1057193149Sdougb				    dns_nsstatscounter_tsigout);
1058193149Sdougb	}
1059193149Sdougb	if (client->message->sig0key != NULL) {
1060193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1061193149Sdougb				    dns_nsstatscounter_sig0out);
1062193149Sdougb	}
1063193149Sdougb	if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
1064193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1065193149Sdougb				    dns_nsstatscounter_truncatedresp);
1066193149Sdougb
1067135446Strhodes	if (result == ISC_R_SUCCESS)
1068135446Strhodes		return;
1069135446Strhodes
1070135446Strhodes done:
1071135446Strhodes	if (client->tcpbuf != NULL) {
1072135446Strhodes		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
1073135446Strhodes		client->tcpbuf = NULL;
1074135446Strhodes	}
1075135446Strhodes
1076135446Strhodes	if (cleanup_cctx)
1077135446Strhodes		dns_compress_invalidate(&cctx);
1078135446Strhodes
1079135446Strhodes	ns_client_next(client, result);
1080135446Strhodes}
1081135446Strhodes
1082165071Sdougb#if NS_CLIENT_DROPPORT
1083165071Sdougb#define DROPPORT_NO		0
1084165071Sdougb#define DROPPORT_REQUEST	1
1085165071Sdougb#define DROPPORT_RESPONSE	2
1086165071Sdougb/*%
1087165071Sdougb * ns_client_dropport determines if certain requests / responses
1088165071Sdougb * should be dropped based on the port number.
1089165071Sdougb *
1090165071Sdougb * Returns:
1091165071Sdougb * \li	0:	Don't drop.
1092165071Sdougb * \li	1:	Drop request.
1093165071Sdougb * \li	2:	Drop (error) response.
1094165071Sdougb */
1095165071Sdougbstatic int
1096165071Sdougbns_client_dropport(in_port_t port) {
1097165071Sdougb	switch (port) {
1098165071Sdougb	case 7: /* echo */
1099165071Sdougb	case 13: /* daytime */
1100165071Sdougb	case 19: /* chargen */
1101165071Sdougb	case 37: /* time */
1102165071Sdougb		return (DROPPORT_REQUEST);
1103165071Sdougb	case 464: /* kpasswd */
1104165071Sdougb		return (DROPPORT_RESPONSE);
1105165071Sdougb	}
1106165071Sdougb	return (DROPPORT_NO);
1107165071Sdougb}
1108165071Sdougb#endif
1109165071Sdougb
1110135446Strhodesvoid
1111135446Strhodesns_client_error(ns_client_t *client, isc_result_t result) {
1112135446Strhodes	dns_rcode_t rcode;
1113135446Strhodes	dns_message_t *message;
1114135446Strhodes
1115135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
1116135446Strhodes
1117135446Strhodes	CTRACE("error");
1118135446Strhodes
1119135446Strhodes	message = client->message;
1120135446Strhodes	rcode = dns_result_torcode(result);
1121135446Strhodes
1122165071Sdougb#if NS_CLIENT_DROPPORT
1123135446Strhodes	/*
1124165071Sdougb	 * Don't send FORMERR to ports on the drop port list.
1125165071Sdougb	 */
1126165071Sdougb	if (rcode == dns_rcode_formerr &&
1127165071Sdougb	    ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) !=
1128165071Sdougb	    DROPPORT_NO) {
1129165071Sdougb		char buf[64];
1130165071Sdougb		isc_buffer_t b;
1131165071Sdougb
1132165071Sdougb		isc_buffer_init(&b, buf, sizeof(buf) - 1);
1133165071Sdougb		if (dns_rcode_totext(rcode, &b) != ISC_R_SUCCESS)
1134165071Sdougb			isc_buffer_putstr(&b, "UNKNOWN RCODE");
1135165071Sdougb		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1136165071Sdougb			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1137165071Sdougb			      "dropped error (%.*s) response: suspicious port",
1138165071Sdougb			      (int)isc_buffer_usedlength(&b), buf);
1139165071Sdougb		ns_client_next(client, ISC_R_SUCCESS);
1140165071Sdougb		return;
1141165071Sdougb	}
1142165071Sdougb#endif
1143165071Sdougb
1144262706Serwin#ifdef USE_RRL
1145165071Sdougb	/*
1146262706Serwin	 * Try to rate limit error responses.
1147262706Serwin	 */
1148262706Serwin	if (client->view != NULL && client->view->rrl != NULL) {
1149262706Serwin		isc_boolean_t wouldlog;
1150262706Serwin		char log_buf[DNS_RRL_LOG_BUF_LEN];
1151262706Serwin		dns_rrl_result_t rrl_result;
1152262706Serwin
1153262706Serwin		INSIST(rcode != dns_rcode_noerror &&
1154262706Serwin		       rcode != dns_rcode_nxdomain);
1155262706Serwin		wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
1156262706Serwin		rrl_result = dns_rrl(client->view, &client->peeraddr,
1157262706Serwin				     TCP_CLIENT(client),
1158262706Serwin				     dns_rdataclass_in, dns_rdatatype_none,
1159262706Serwin				     NULL, result, client->now,
1160262706Serwin				     wouldlog, log_buf, sizeof(log_buf));
1161262706Serwin		if (rrl_result != DNS_RRL_RESULT_OK) {
1162262706Serwin			/*
1163262706Serwin			 * Log dropped errors in the query category
1164262706Serwin			 * so that they are not lost in silence.
1165262706Serwin			 * Starts of rate-limited bursts are logged in
1166262706Serwin			 * NS_LOGCATEGORY_RRL.
1167262706Serwin			 */
1168262706Serwin			if (wouldlog) {
1169262706Serwin				ns_client_log(client,
1170262706Serwin					      NS_LOGCATEGORY_QUERY_EERRORS,
1171262706Serwin					      NS_LOGMODULE_CLIENT,
1172262706Serwin					      DNS_RRL_LOG_DROP,
1173262706Serwin					      "%s", log_buf);
1174262706Serwin			}
1175262706Serwin			/*
1176262706Serwin			 * Some error responses cannot be 'slipped',
1177262706Serwin			 * so don't try to slip any error responses.
1178262706Serwin			 */
1179262706Serwin			if (!client->view->rrl->log_only) {
1180262706Serwin				isc_stats_increment(ns_g_server->nsstats,
1181262706Serwin						dns_nsstatscounter_ratedropped);
1182262706Serwin				isc_stats_increment(ns_g_server->nsstats,
1183262706Serwin						dns_nsstatscounter_dropped);
1184262706Serwin				ns_client_next(client, DNS_R_DROP);
1185262706Serwin				return;
1186262706Serwin			}
1187262706Serwin		}
1188262706Serwin	}
1189262706Serwin#endif /* USE_RRL */
1190262706Serwin
1191262706Serwin	/*
1192135446Strhodes	 * Message may be an in-progress reply that we had trouble
1193135446Strhodes	 * with, in which case QR will be set.  We need to clear QR before
1194135446Strhodes	 * calling dns_message_reply() to avoid triggering an assertion.
1195135446Strhodes	 */
1196135446Strhodes	message->flags &= ~DNS_MESSAGEFLAG_QR;
1197135446Strhodes	/*
1198135446Strhodes	 * AA and AD shouldn't be set.
1199135446Strhodes	 */
1200135446Strhodes	message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
1201135446Strhodes	result = dns_message_reply(message, ISC_TRUE);
1202135446Strhodes	if (result != ISC_R_SUCCESS) {
1203135446Strhodes		/*
1204135446Strhodes		 * It could be that we've got a query with a good header,
1205135446Strhodes		 * but a bad question section, so we try again with
1206135446Strhodes		 * want_question_section set to ISC_FALSE.
1207135446Strhodes		 */
1208135446Strhodes		result = dns_message_reply(message, ISC_FALSE);
1209135446Strhodes		if (result != ISC_R_SUCCESS) {
1210135446Strhodes			ns_client_next(client, result);
1211135446Strhodes			return;
1212135446Strhodes		}
1213135446Strhodes	}
1214135446Strhodes	message->rcode = rcode;
1215135446Strhodes
1216135446Strhodes	/*
1217135446Strhodes	 * FORMERR loop avoidance:  If we sent a FORMERR message
1218135446Strhodes	 * with the same ID to the same client less than two
1219186462Sdougb	 * seconds ago, assume that we are in an infinite error
1220186462Sdougb	 * packet dialog with a server for some protocol whose
1221135446Strhodes	 * error responses look enough like DNS queries to
1222135446Strhodes	 * elicit a FORMERR response.  Drop a packet to break
1223135446Strhodes	 * the loop.
1224135446Strhodes	 */
1225135446Strhodes	if (rcode == dns_rcode_formerr) {
1226135446Strhodes		if (isc_sockaddr_equal(&client->peeraddr,
1227135446Strhodes				       &client->formerrcache.addr) &&
1228135446Strhodes		    message->id == client->formerrcache.id &&
1229135446Strhodes		    client->requesttime - client->formerrcache.time < 2) {
1230135446Strhodes			/* Drop packet. */
1231135446Strhodes			ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1232135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
1233135446Strhodes				      "possible error packet loop, "
1234135446Strhodes				      "FORMERR dropped");
1235135446Strhodes			ns_client_next(client, result);
1236135446Strhodes			return;
1237135446Strhodes		}
1238135446Strhodes		client->formerrcache.addr = client->peeraddr;
1239135446Strhodes		client->formerrcache.time = client->requesttime;
1240135446Strhodes		client->formerrcache.id = message->id;
1241135446Strhodes	}
1242135446Strhodes	ns_client_send(client);
1243135446Strhodes}
1244135446Strhodes
1245135446Strhodesstatic inline isc_result_t
1246135446Strhodesclient_addopt(ns_client_t *client) {
1247262706Serwin	char nsid[BUFSIZ], *nsidp;
1248135446Strhodes	isc_result_t result;
1249135446Strhodes	dns_view_t *view;
1250135446Strhodes	dns_resolver_t *resolver;
1251135446Strhodes	isc_uint16_t udpsize;
1252262706Serwin	dns_ednsopt_t ednsopts[2];
1253262706Serwin	int count = 0;
1254262706Serwin	unsigned int flags;
1255135446Strhodes
1256135446Strhodes	REQUIRE(client->opt == NULL);	/* XXXRTH free old. */
1257135446Strhodes
1258135446Strhodes	view = client->view;
1259135446Strhodes	resolver = (view != NULL) ? view->resolver : NULL;
1260135446Strhodes	if (resolver != NULL)
1261135446Strhodes		udpsize = dns_resolver_getudpsize(resolver);
1262135446Strhodes	else
1263135446Strhodes		udpsize = ns_g_udpsize;
1264135446Strhodes
1265262706Serwin	flags = client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE;
1266135446Strhodes
1267193149Sdougb	/* Set EDNS options if applicable */
1268262706Serwin	if ((client->attributes & NS_CLIENTATTR_WANTNSID) != 0 &&
1269193149Sdougb	    (ns_g_server->server_id != NULL ||
1270193149Sdougb	     ns_g_server->server_usehostname)) {
1271193149Sdougb		if (ns_g_server->server_usehostname) {
1272193149Sdougb			isc_result_t result;
1273193149Sdougb			result = ns_os_gethostname(nsid, sizeof(nsid));
1274193149Sdougb			if (result != ISC_R_SUCCESS) {
1275193149Sdougb				goto no_nsid;
1276193149Sdougb			}
1277193149Sdougb			nsidp = nsid;
1278193149Sdougb		} else
1279193149Sdougb			nsidp = ns_g_server->server_id;
1280193149Sdougb
1281262706Serwin		ednsopts[count].code = DNS_OPT_NSID;
1282262706Serwin		ednsopts[count].length = strlen(nsidp);
1283262706Serwin		ednsopts[count].value = (unsigned char *)nsidp;
1284262706Serwin		count++;
1285193149Sdougb	}
1286262706Serwin no_nsid:
1287262706Serwin	result = dns_message_buildopt(client->message, &client->opt, 0,
1288262706Serwin				      udpsize, flags, ednsopts, count);
1289262706Serwin	return (result);
1290135446Strhodes}
1291135446Strhodes
1292135446Strhodesstatic inline isc_boolean_t
1293135446Strhodesallowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
1294135446Strhodes	int match;
1295135446Strhodes	isc_result_t result;
1296135446Strhodes
1297135446Strhodes	if (acl == NULL)
1298135446Strhodes		return (ISC_TRUE);
1299135446Strhodes	result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
1300135446Strhodes			       &match, NULL);
1301135446Strhodes	if (result == ISC_R_SUCCESS && match > 0)
1302135446Strhodes		return (ISC_TRUE);
1303135446Strhodes	return (ISC_FALSE);
1304135446Strhodes}
1305135446Strhodes
1306135446Strhodes/*
1307170222Sdougb * Callback to see if a non-recursive query coming from 'srcaddr' to
1308170222Sdougb * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
1309170222Sdougb * delivered to 'myview'.
1310170222Sdougb *
1311170222Sdougb * We run this unlocked as both the view list and the interface list
1312193149Sdougb * are updated when the appropriate task has exclusivity.
1313170222Sdougb */
1314170222Sdougbisc_boolean_t
1315170222Sdougbns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
1316170222Sdougb		 isc_sockaddr_t *srcaddr, isc_sockaddr_t *dstaddr,
1317170222Sdougb		 dns_rdataclass_t rdclass, void *arg)
1318170222Sdougb{
1319170222Sdougb	dns_view_t *view;
1320174187Sdougb	dns_tsigkey_t *key = NULL;
1321174187Sdougb	dns_name_t *tsig = NULL;
1322170222Sdougb	isc_netaddr_t netsrc;
1323170222Sdougb	isc_netaddr_t netdst;
1324170222Sdougb
1325170222Sdougb	UNUSED(arg);
1326170222Sdougb
1327225361Sdougb	/*
1328225361Sdougb	 * ns_g_server->interfacemgr is task exclusive locked.
1329225361Sdougb	 */
1330225361Sdougb	if (ns_g_server->interfacemgr == NULL)
1331225361Sdougb		return (ISC_TRUE);
1332225361Sdougb
1333170222Sdougb	if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
1334170222Sdougb		return (ISC_FALSE);
1335170222Sdougb
1336170222Sdougb	isc_netaddr_fromsockaddr(&netsrc, srcaddr);
1337170222Sdougb	isc_netaddr_fromsockaddr(&netdst, dstaddr);
1338170222Sdougb
1339170222Sdougb	for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
1340170222Sdougb	     view != NULL;
1341170222Sdougb	     view = ISC_LIST_NEXT(view, link)) {
1342170222Sdougb
1343170222Sdougb		if (view->matchrecursiveonly)
1344170222Sdougb			continue;
1345170222Sdougb
1346170222Sdougb		if (rdclass != view->rdclass)
1347170222Sdougb			continue;
1348170222Sdougb
1349170222Sdougb		if (mykey != NULL) {
1350170222Sdougb			isc_boolean_t match;
1351170222Sdougb			isc_result_t result;
1352170222Sdougb
1353193149Sdougb			result = dns_view_gettsig(view, &mykey->name, &key);
1354170222Sdougb			if (result != ISC_R_SUCCESS)
1355170222Sdougb				continue;
1356170222Sdougb			match = dst_key_compare(mykey->key, key->key);
1357170222Sdougb			dns_tsigkey_detach(&key);
1358170222Sdougb			if (!match)
1359170222Sdougb				continue;
1360193149Sdougb			tsig = dns_tsigkey_identity(mykey);
1361170222Sdougb		}
1362170222Sdougb
1363170222Sdougb		if (allowed(&netsrc, tsig, view->matchclients) &&
1364170222Sdougb		    allowed(&netdst, tsig, view->matchdestinations))
1365170222Sdougb			break;
1366170222Sdougb	}
1367170222Sdougb	return (ISC_TF(view == myview));
1368170222Sdougb}
1369170222Sdougb
1370262706Serwinstatic isc_result_t
1371262706Serwinprocess_opt(ns_client_t *client, dns_rdataset_t *opt) {
1372262706Serwin	dns_rdata_t rdata;
1373262706Serwin	isc_buffer_t optbuf;
1374262706Serwin	isc_result_t result;
1375262706Serwin	isc_uint16_t optcode;
1376262706Serwin	isc_uint16_t optlen;
1377262706Serwin
1378262706Serwin	/*
1379262706Serwin	 * Set the client's UDP buffer size.
1380262706Serwin	 */
1381262706Serwin	client->udpsize = opt->rdclass;
1382262706Serwin
1383262706Serwin	/*
1384262706Serwin	 * If the requested UDP buffer size is less than 512,
1385262706Serwin	 * ignore it and use 512.
1386262706Serwin	 */
1387262706Serwin	if (client->udpsize < 512)
1388262706Serwin		client->udpsize = 512;
1389262706Serwin
1390262706Serwin	/*
1391262706Serwin	 * Get the flags out of the OPT record.
1392262706Serwin	 */
1393262706Serwin	client->extflags = (isc_uint16_t)(opt->ttl & 0xFFFF);
1394262706Serwin
1395262706Serwin	/*
1396262706Serwin	 * Do we understand this version of EDNS?
1397262706Serwin	 *
1398262706Serwin	 * XXXRTH need library support for this!
1399262706Serwin	 */
1400262706Serwin	client->ednsversion = (opt->ttl & 0x00FF0000) >> 16;
1401262706Serwin	if (client->ednsversion > 0) {
1402262706Serwin		isc_stats_increment(ns_g_server->nsstats,
1403262706Serwin				    dns_nsstatscounter_badednsver);
1404262706Serwin		result = client_addopt(client);
1405262706Serwin		if (result == ISC_R_SUCCESS)
1406262706Serwin			result = DNS_R_BADVERS;
1407262706Serwin		ns_client_error(client, result);
1408262706Serwin		goto cleanup;
1409262706Serwin	}
1410262706Serwin
1411262706Serwin	/* Check for NSID request */
1412262706Serwin	result = dns_rdataset_first(opt);
1413262706Serwin	if (result == ISC_R_SUCCESS) {
1414262706Serwin		dns_rdata_init(&rdata);
1415262706Serwin		dns_rdataset_current(opt, &rdata);
1416262706Serwin		isc_buffer_init(&optbuf, rdata.data, rdata.length);
1417262706Serwin		isc_buffer_add(&optbuf, rdata.length);
1418262706Serwin		while (isc_buffer_remaininglength(&optbuf) >= 4) {
1419262706Serwin			optcode = isc_buffer_getuint16(&optbuf);
1420262706Serwin			optlen = isc_buffer_getuint16(&optbuf);
1421262706Serwin			switch (optcode) {
1422262706Serwin			case DNS_OPT_NSID:
1423262706Serwin				client->attributes |= NS_CLIENTATTR_WANTNSID;
1424262706Serwin				isc_buffer_forward(&optbuf, optlen);
1425262706Serwin				break;
1426262706Serwin			default:
1427262706Serwin				isc_buffer_forward(&optbuf, optlen);
1428262706Serwin				break;
1429262706Serwin			}
1430262706Serwin		}
1431262706Serwin	}
1432262706Serwin
1433262706Serwin	isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_edns0in);
1434262706Serwin
1435262706Serwin	/*
1436262706Serwin	 * Create an OPT for our reply.
1437262706Serwin	 */
1438262706Serwin	result = client_addopt(client);
1439262706Serwin	if (result != ISC_R_SUCCESS) {
1440262706Serwin		ns_client_error(client, result);
1441262706Serwin		goto cleanup;
1442262706Serwin	}
1443262706Serwin cleanup:
1444262706Serwin	return (result);
1445262706Serwin}
1446262706Serwin
1447170222Sdougb/*
1448135446Strhodes * Handle an incoming request event from the socket (UDP case)
1449135446Strhodes * or tcpmsg (TCP case).
1450135446Strhodes */
1451135446Strhodesstatic void
1452135446Strhodesclient_request(isc_task_t *task, isc_event_t *event) {
1453135446Strhodes	ns_client_t *client;
1454135446Strhodes	isc_socketevent_t *sevent;
1455135446Strhodes	isc_result_t result;
1456135446Strhodes	isc_result_t sigresult = ISC_R_SUCCESS;
1457135446Strhodes	isc_buffer_t *buffer;
1458135446Strhodes	isc_buffer_t tbuffer;
1459135446Strhodes	dns_view_t *view;
1460135446Strhodes	dns_rdataset_t *opt;
1461193149Sdougb	dns_name_t *signame;
1462193149Sdougb	isc_boolean_t ra;	/* Recursion available. */
1463135446Strhodes	isc_netaddr_t netaddr;
1464135446Strhodes	int match;
1465135446Strhodes	dns_messageid_t id;
1466135446Strhodes	unsigned int flags;
1467135446Strhodes	isc_boolean_t notimp;
1468135446Strhodes
1469135446Strhodes	REQUIRE(event != NULL);
1470135446Strhodes	client = event->ev_arg;
1471135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
1472135446Strhodes	REQUIRE(task == client->task);
1473135446Strhodes
1474135446Strhodes	INSIST(client->recursionquota == NULL);
1475135446Strhodes
1476254402Serwin	INSIST(client->state == (TCP_CLIENT(client) ?
1477254402Serwin				       NS_CLIENTSTATE_READING :
1478254402Serwin				       NS_CLIENTSTATE_READY));
1479135446Strhodes
1480170222Sdougb	ns_client_requests++;
1481170222Sdougb
1482135446Strhodes	if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
1483135446Strhodes		INSIST(!TCP_CLIENT(client));
1484135446Strhodes		sevent = (isc_socketevent_t *)event;
1485135446Strhodes		REQUIRE(sevent == client->recvevent);
1486135446Strhodes		isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
1487135446Strhodes		isc_buffer_add(&tbuffer, sevent->n);
1488135446Strhodes		buffer = &tbuffer;
1489135446Strhodes		result = sevent->result;
1490135446Strhodes		if (result == ISC_R_SUCCESS) {
1491135446Strhodes			client->peeraddr = sevent->address;
1492135446Strhodes			client->peeraddr_valid = ISC_TRUE;
1493135446Strhodes		}
1494135446Strhodes		if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
1495135446Strhodes			client->attributes |= NS_CLIENTATTR_PKTINFO;
1496135446Strhodes			client->pktinfo = sevent->pktinfo;
1497135446Strhodes		}
1498135446Strhodes		if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
1499135446Strhodes			client->attributes |= NS_CLIENTATTR_MULTICAST;
1500135446Strhodes		client->nrecvs--;
1501135446Strhodes	} else {
1502135446Strhodes		INSIST(TCP_CLIENT(client));
1503135446Strhodes		REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
1504135446Strhodes		REQUIRE(event->ev_sender == &client->tcpmsg);
1505135446Strhodes		buffer = &client->tcpmsg.buffer;
1506135446Strhodes		result = client->tcpmsg.result;
1507135446Strhodes		INSIST(client->nreads == 1);
1508135446Strhodes		/*
1509135446Strhodes		 * client->peeraddr was set when the connection was accepted.
1510135446Strhodes		 */
1511135446Strhodes		client->nreads--;
1512135446Strhodes	}
1513135446Strhodes
1514135446Strhodes	if (exit_check(client))
1515135446Strhodes		goto cleanup;
1516135446Strhodes	client->state = client->newstate = NS_CLIENTSTATE_WORKING;
1517135446Strhodes
1518135446Strhodes	isc_task_getcurrenttime(task, &client->requesttime);
1519135446Strhodes	client->now = client->requesttime;
1520135446Strhodes
1521135446Strhodes	if (result != ISC_R_SUCCESS) {
1522135446Strhodes		if (TCP_CLIENT(client)) {
1523135446Strhodes			ns_client_next(client, result);
1524135446Strhodes		} else {
1525135446Strhodes			if  (result != ISC_R_CANCELED)
1526135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_CLIENT,
1527135446Strhodes					      NS_LOGMODULE_CLIENT,
1528135446Strhodes					      ISC_LOG_ERROR,
1529135446Strhodes					      "UDP client handler shutting "
1530135446Strhodes					      "down due to fatal receive "
1531135446Strhodes					      "error: %s",
1532135446Strhodes					      isc_result_totext(result));
1533135446Strhodes			isc_task_shutdown(client->task);
1534135446Strhodes		}
1535135446Strhodes		goto cleanup;
1536135446Strhodes	}
1537135446Strhodes
1538135446Strhodes	isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
1539135446Strhodes
1540165071Sdougb#if NS_CLIENT_DROPPORT
1541165071Sdougb	if (ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) ==
1542165071Sdougb	    DROPPORT_REQUEST) {
1543165071Sdougb		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1544165071Sdougb			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1545165071Sdougb			      "dropped request: suspicious port");
1546165071Sdougb		ns_client_next(client, ISC_R_SUCCESS);
1547165071Sdougb		goto cleanup;
1548165071Sdougb	}
1549165071Sdougb#endif
1550165071Sdougb
1551135446Strhodes	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1552135446Strhodes		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1553135446Strhodes		      "%s request",
1554135446Strhodes		      TCP_CLIENT(client) ? "TCP" : "UDP");
1555135446Strhodes
1556135446Strhodes	/*
1557135446Strhodes	 * Check the blackhole ACL for UDP only, since TCP is done in
1558135446Strhodes	 * client_newconn.
1559135446Strhodes	 */
1560135446Strhodes	if (!TCP_CLIENT(client)) {
1561135446Strhodes
1562135446Strhodes		if (ns_g_server->blackholeacl != NULL &&
1563135446Strhodes		    dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
1564135446Strhodes				  &ns_g_server->aclenv,
1565135446Strhodes				  &match, NULL) == ISC_R_SUCCESS &&
1566135446Strhodes		    match > 0)
1567135446Strhodes		{
1568135446Strhodes			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1569135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
1570135446Strhodes				      "blackholed UDP datagram");
1571135446Strhodes			ns_client_next(client, ISC_R_SUCCESS);
1572135446Strhodes			goto cleanup;
1573135446Strhodes		}
1574135446Strhodes	}
1575135446Strhodes
1576135446Strhodes	/*
1577135446Strhodes	 * Silently drop multicast requests for the present.
1578224092Sdougb	 * XXXMPA revisit this as mDNS spec was published.
1579135446Strhodes	 */
1580135446Strhodes	if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
1581135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1582135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
1583135446Strhodes			      "dropping multicast request");
1584135446Strhodes		ns_client_next(client, DNS_R_REFUSED);
1585165071Sdougb		goto cleanup;
1586135446Strhodes	}
1587135446Strhodes
1588135446Strhodes	result = dns_message_peekheader(buffer, &id, &flags);
1589135446Strhodes	if (result != ISC_R_SUCCESS) {
1590135446Strhodes		/*
1591135446Strhodes		 * There isn't enough header to determine whether
1592135446Strhodes		 * this was a request or a response.  Drop it.
1593135446Strhodes		 */
1594135446Strhodes		ns_client_next(client, result);
1595135446Strhodes		goto cleanup;
1596135446Strhodes	}
1597135446Strhodes
1598135446Strhodes	/*
1599135446Strhodes	 * The client object handles requests, not responses.
1600135446Strhodes	 * If this is a UDP response, forward it to the dispatcher.
1601135446Strhodes	 * If it's a TCP response, discard it here.
1602135446Strhodes	 */
1603135446Strhodes	if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
1604135446Strhodes		if (TCP_CLIENT(client)) {
1605135446Strhodes			CTRACE("unexpected response");
1606135446Strhodes			ns_client_next(client, DNS_R_FORMERR);
1607135446Strhodes			goto cleanup;
1608135446Strhodes		} else {
1609135446Strhodes			dns_dispatch_importrecv(client->dispatch, event);
1610135446Strhodes			ns_client_next(client, ISC_R_SUCCESS);
1611135446Strhodes			goto cleanup;
1612135446Strhodes		}
1613135446Strhodes	}
1614135446Strhodes
1615135446Strhodes	/*
1616193149Sdougb	 * Update some statistics counters.  Don't count responses.
1617193149Sdougb	 */
1618193149Sdougb	if (isc_sockaddr_pf(&client->peeraddr) == PF_INET) {
1619193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1620193149Sdougb				    dns_nsstatscounter_requestv4);
1621193149Sdougb	} else {
1622193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1623193149Sdougb				    dns_nsstatscounter_requestv6);
1624193149Sdougb	}
1625193149Sdougb	if (TCP_CLIENT(client))
1626193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1627193149Sdougb				    dns_nsstatscounter_tcp);
1628193149Sdougb
1629193149Sdougb	/*
1630135446Strhodes	 * It's a request.  Parse it.
1631135446Strhodes	 */
1632135446Strhodes	result = dns_message_parse(client->message, buffer, 0);
1633135446Strhodes	if (result != ISC_R_SUCCESS) {
1634135446Strhodes		/*
1635135446Strhodes		 * Parsing the request failed.  Send a response
1636135446Strhodes		 * (typically FORMERR or SERVFAIL).
1637135446Strhodes		 */
1638135446Strhodes		ns_client_error(client, result);
1639135446Strhodes		goto cleanup;
1640135446Strhodes	}
1641135446Strhodes
1642193149Sdougb	dns_opcodestats_increment(ns_g_server->opcodestats,
1643193149Sdougb				  client->message->opcode);
1644135446Strhodes	switch (client->message->opcode) {
1645135446Strhodes	case dns_opcode_query:
1646135446Strhodes	case dns_opcode_update:
1647135446Strhodes	case dns_opcode_notify:
1648135446Strhodes		notimp = ISC_FALSE;
1649135446Strhodes		break;
1650135446Strhodes	case dns_opcode_iquery:
1651135446Strhodes	default:
1652135446Strhodes		notimp = ISC_TRUE;
1653135446Strhodes		break;
1654135446Strhodes	}
1655135446Strhodes
1656135446Strhodes	client->message->rcode = dns_rcode_noerror;
1657135446Strhodes
1658135446Strhodes	/* RFC1123 section 6.1.3.2 */
1659135446Strhodes	if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
1660135446Strhodes		client->message->flags &= ~DNS_MESSAGEFLAG_RD;
1661135446Strhodes
1662135446Strhodes	/*
1663135446Strhodes	 * Deal with EDNS.
1664135446Strhodes	 */
1665135446Strhodes	opt = dns_message_getopt(client->message);
1666135446Strhodes	if (opt != NULL) {
1667262706Serwin		result = process_opt(client, opt);
1668262706Serwin		if (result != ISC_R_SUCCESS)
1669135446Strhodes			goto cleanup;
1670135446Strhodes	}
1671135446Strhodes
1672135446Strhodes	if (client->message->rdclass == 0) {
1673135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1674135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
1675135446Strhodes			      "message class could not be determined");
1676135446Strhodes		ns_client_dumpmessage(client,
1677135446Strhodes				      "message class could not be determined");
1678135446Strhodes		ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
1679135446Strhodes		goto cleanup;
1680135446Strhodes	}
1681135446Strhodes
1682135446Strhodes	/*
1683135446Strhodes	 * Determine the destination address.  If the receiving interface is
1684135446Strhodes	 * bound to a specific address, we simply use it regardless of the
1685135446Strhodes	 * address family.  All IPv4 queries should fall into this case.
1686135446Strhodes	 * Otherwise, if this is a TCP query, get the address from the
1687135446Strhodes	 * receiving socket (this needs a system call and can be heavy).
1688135446Strhodes	 * For IPv6 UDP queries, we get this from the pktinfo structure (if
1689135446Strhodes	 * supported).
1690135446Strhodes	 * If all the attempts fail (this can happen due to memory shortage,
1691186462Sdougb	 * etc), we regard this as an error for safety.
1692135446Strhodes	 */
1693135446Strhodes	if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
1694224092Sdougb		isc_netaddr_fromsockaddr(&client->destaddr,
1695224092Sdougb					 &client->interface->addr);
1696135446Strhodes	else {
1697224092Sdougb		isc_sockaddr_t sockaddr;
1698135446Strhodes		result = ISC_R_FAILURE;
1699135446Strhodes
1700224092Sdougb		if (TCP_CLIENT(client))
1701135446Strhodes			result = isc_socket_getsockname(client->tcpsocket,
1702224092Sdougb							&sockaddr);
1703224092Sdougb		if (result == ISC_R_SUCCESS)
1704224092Sdougb			isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
1705135446Strhodes		if (result != ISC_R_SUCCESS &&
1706135446Strhodes		    client->interface->addr.type.sa.sa_family == AF_INET6 &&
1707135446Strhodes		    (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
1708135446Strhodes			/*
1709135446Strhodes			 * XXXJT technically, we should convert the receiving
1710135446Strhodes			 * interface ID to a proper scope zone ID.  However,
1711135446Strhodes			 * due to the fact there is no standard API for this,
1712135446Strhodes			 * we only handle link-local addresses and use the
1713135446Strhodes			 * interface index as link ID.  Despite the assumption,
1714135446Strhodes			 * it should cover most typical cases.
1715135446Strhodes			 */
1716224092Sdougb			isc_netaddr_fromin6(&client->destaddr,
1717224092Sdougb					    &client->pktinfo.ipi6_addr);
1718135446Strhodes			if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
1719224092Sdougb				isc_netaddr_setzone(&client->destaddr,
1720224092Sdougb						client->pktinfo.ipi6_ifindex);
1721135446Strhodes			result = ISC_R_SUCCESS;
1722135446Strhodes		}
1723135446Strhodes		if (result != ISC_R_SUCCESS) {
1724135446Strhodes			UNEXPECTED_ERROR(__FILE__, __LINE__,
1725135446Strhodes					 "failed to get request's "
1726135446Strhodes					 "destination: %s",
1727135446Strhodes					 isc_result_totext(result));
1728174187Sdougb			ns_client_next(client, ISC_R_SUCCESS);
1729135446Strhodes			goto cleanup;
1730135446Strhodes		}
1731135446Strhodes	}
1732135446Strhodes
1733135446Strhodes	/*
1734135446Strhodes	 * Find a view that matches the client's source address.
1735135446Strhodes	 */
1736135446Strhodes	for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
1737135446Strhodes	     view != NULL;
1738135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
1739135446Strhodes		if (client->message->rdclass == view->rdclass ||
1740135446Strhodes		    client->message->rdclass == dns_rdataclass_any)
1741135446Strhodes		{
1742135446Strhodes			dns_name_t *tsig = NULL;
1743193149Sdougb
1744135446Strhodes			sigresult = dns_message_rechecksig(client->message,
1745135446Strhodes							   view);
1746135446Strhodes			if (sigresult == ISC_R_SUCCESS)
1747193149Sdougb				tsig = dns_tsigkey_identity(client->message->tsigkey);
1748186462Sdougb
1749135446Strhodes			if (allowed(&netaddr, tsig, view->matchclients) &&
1750224092Sdougb			    allowed(&client->destaddr, tsig,
1751224092Sdougb				    view->matchdestinations) &&
1752135446Strhodes			    !((client->message->flags & DNS_MESSAGEFLAG_RD)
1753135446Strhodes			      == 0 && view->matchrecursiveonly))
1754135446Strhodes			{
1755135446Strhodes				dns_view_attach(view, &client->view);
1756135446Strhodes				break;
1757135446Strhodes			}
1758135446Strhodes		}
1759135446Strhodes	}
1760135446Strhodes
1761135446Strhodes	if (view == NULL) {
1762135446Strhodes		char classname[DNS_RDATACLASS_FORMATSIZE];
1763135446Strhodes
1764135446Strhodes		/*
1765135446Strhodes		 * Do a dummy TSIG verification attempt so that the
1766135446Strhodes		 * response will have a TSIG if the query did, as
1767135446Strhodes		 * required by RFC2845.
1768135446Strhodes		 */
1769135446Strhodes		isc_buffer_t b;
1770135446Strhodes		isc_region_t *r;
1771135446Strhodes
1772135446Strhodes		dns_message_resetsig(client->message);
1773135446Strhodes
1774135446Strhodes		r = dns_message_getrawmessage(client->message);
1775135446Strhodes		isc_buffer_init(&b, r->base, r->length);
1776135446Strhodes		isc_buffer_add(&b, r->length);
1777135446Strhodes		(void)dns_tsig_verify(&b, client->message, NULL, NULL);
1778135446Strhodes
1779135446Strhodes		dns_rdataclass_format(client->message->rdclass, classname,
1780135446Strhodes				      sizeof(classname));
1781135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1782135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
1783135446Strhodes			      "no matching view in class '%s'", classname);
1784135446Strhodes		ns_client_dumpmessage(client, "no matching view in class");
1785135446Strhodes		ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
1786135446Strhodes		goto cleanup;
1787135446Strhodes	}
1788135446Strhodes
1789135446Strhodes	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
1790135446Strhodes		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
1791135446Strhodes		      "using view '%s'", view->name);
1792135446Strhodes
1793135446Strhodes	/*
1794135446Strhodes	 * Check for a signature.  We log bad signatures regardless of
1795135446Strhodes	 * whether they ultimately cause the request to be rejected or
1796135446Strhodes	 * not.  We do not log the lack of a signature unless we are
1797135446Strhodes	 * debugging.
1798135446Strhodes	 */
1799135446Strhodes	client->signer = NULL;
1800135446Strhodes	dns_name_init(&client->signername, NULL);
1801135446Strhodes	result = dns_message_signer(client->message, &client->signername);
1802193149Sdougb	if (result != ISC_R_NOTFOUND) {
1803193149Sdougb		signame = NULL;
1804193149Sdougb		if (dns_message_gettsig(client->message, &signame) != NULL) {
1805193149Sdougb			isc_stats_increment(ns_g_server->nsstats,
1806193149Sdougb					    dns_nsstatscounter_tsigin);
1807193149Sdougb		} else {
1808193149Sdougb			isc_stats_increment(ns_g_server->nsstats,
1809193149Sdougb					    dns_nsstatscounter_sig0in);
1810193149Sdougb		}
1811193149Sdougb
1812193149Sdougb	}
1813135446Strhodes	if (result == ISC_R_SUCCESS) {
1814224092Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
1815224092Sdougb		dns_name_format(&client->signername, namebuf, sizeof(namebuf));
1816135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1817135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1818224092Sdougb			      "request has valid signature: %s", namebuf);
1819135446Strhodes		client->signer = &client->signername;
1820135446Strhodes	} else if (result == ISC_R_NOTFOUND) {
1821135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1822135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1823135446Strhodes			      "request is not signed");
1824135446Strhodes	} else if (result == DNS_R_NOIDENTITY) {
1825135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1826135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
1827135446Strhodes			      "request is signed by a nonauthoritative key");
1828135446Strhodes	} else {
1829135446Strhodes		char tsigrcode[64];
1830135446Strhodes		isc_buffer_t b;
1831174187Sdougb		dns_rcode_t status;
1832174187Sdougb		isc_result_t tresult;
1833135446Strhodes
1834135446Strhodes		/* There is a signature, but it is bad. */
1835193149Sdougb		isc_stats_increment(ns_g_server->nsstats,
1836193149Sdougb				    dns_nsstatscounter_invalidsig);
1837193149Sdougb		signame = NULL;
1838193149Sdougb		if (dns_message_gettsig(client->message, &signame) != NULL) {
1839135446Strhodes			char namebuf[DNS_NAME_FORMATSIZE];
1840193149Sdougb			char cnamebuf[DNS_NAME_FORMATSIZE];
1841193149Sdougb			dns_name_format(signame, namebuf, sizeof(namebuf));
1842174187Sdougb			status = client->message->tsigstatus;
1843174187Sdougb			isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
1844174187Sdougb			tresult = dns_tsigrcode_totext(status, &b);
1845174187Sdougb			INSIST(tresult == ISC_R_SUCCESS);
1846174187Sdougb			tsigrcode[isc_buffer_usedlength(&b)] = '\0';
1847193149Sdougb			if (client->message->tsigkey->generated) {
1848193149Sdougb				dns_name_format(client->message->tsigkey->creator,
1849193149Sdougb						cnamebuf, sizeof(cnamebuf));
1850193149Sdougb				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1851193149Sdougb					      NS_LOGMODULE_CLIENT,
1852193149Sdougb					      ISC_LOG_ERROR,
1853193149Sdougb					      "request has invalid signature: "
1854193149Sdougb					      "TSIG %s (%s): %s (%s)", namebuf,
1855193149Sdougb					      cnamebuf,
1856193149Sdougb					      isc_result_totext(result),
1857193149Sdougb					      tsigrcode);
1858193149Sdougb			} else {
1859193149Sdougb				ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1860193149Sdougb					      NS_LOGMODULE_CLIENT,
1861193149Sdougb					      ISC_LOG_ERROR,
1862193149Sdougb					      "request has invalid signature: "
1863193149Sdougb					      "TSIG %s: %s (%s)", namebuf,
1864193149Sdougb					      isc_result_totext(result),
1865193149Sdougb					      tsigrcode);
1866193149Sdougb			}
1867135446Strhodes		} else {
1868174187Sdougb			status = client->message->sig0status;
1869174187Sdougb			isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
1870174187Sdougb			tresult = dns_tsigrcode_totext(status, &b);
1871174187Sdougb			INSIST(tresult == ISC_R_SUCCESS);
1872174187Sdougb			tsigrcode[isc_buffer_usedlength(&b)] = '\0';
1873135446Strhodes			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1874135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
1875135446Strhodes				      "request has invalid signature: %s (%s)",
1876135446Strhodes				      isc_result_totext(result), tsigrcode);
1877135446Strhodes		}
1878135446Strhodes		/*
1879135446Strhodes		 * Accept update messages signed by unknown keys so that
1880135446Strhodes		 * update forwarding works transparently through slaves
1881135446Strhodes		 * that don't have all the same keys as the master.
1882135446Strhodes		 */
1883135446Strhodes		if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
1884135446Strhodes		      client->message->opcode == dns_opcode_update)) {
1885135446Strhodes			ns_client_error(client, sigresult);
1886135446Strhodes			goto cleanup;
1887135446Strhodes		}
1888135446Strhodes	}
1889135446Strhodes
1890135446Strhodes	/*
1891135446Strhodes	 * Decide whether recursive service is available to this client.
1892135446Strhodes	 * We do this here rather than in the query code so that we can
1893135446Strhodes	 * set the RA bit correctly on all kinds of responses, not just
1894165071Sdougb	 * responses to ordinary queries.  Note if you can't query the
1895165071Sdougb	 * cache there is no point in setting RA.
1896135446Strhodes	 */
1897135446Strhodes	ra = ISC_FALSE;
1898135446Strhodes	if (client->view->resolver != NULL &&
1899135446Strhodes	    client->view->recursion == ISC_TRUE &&
1900193149Sdougb	    ns_client_checkaclsilent(client, NULL,
1901193149Sdougb				     client->view->recursionacl,
1902165071Sdougb				     ISC_TRUE) == ISC_R_SUCCESS &&
1903193149Sdougb	    ns_client_checkaclsilent(client, NULL,
1904216175Sdougb				     client->view->cacheacl,
1905193149Sdougb				     ISC_TRUE) == ISC_R_SUCCESS &&
1906224092Sdougb	    ns_client_checkaclsilent(client, &client->destaddr,
1907193149Sdougb				     client->view->recursiononacl,
1908193149Sdougb				     ISC_TRUE) == ISC_R_SUCCESS &&
1909224092Sdougb	    ns_client_checkaclsilent(client, &client->destaddr,
1910216175Sdougb				     client->view->cacheonacl,
1911135446Strhodes				     ISC_TRUE) == ISC_R_SUCCESS)
1912135446Strhodes		ra = ISC_TRUE;
1913135446Strhodes
1914135446Strhodes	if (ra == ISC_TRUE)
1915135446Strhodes		client->attributes |= NS_CLIENTATTR_RA;
1916135446Strhodes
1917135446Strhodes	ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT,
1918135446Strhodes		      ISC_LOG_DEBUG(3), ra ? "recursion available" :
1919186462Sdougb					     "recursion not available");
1920135446Strhodes
1921135446Strhodes	/*
1922170222Sdougb	 * Adjust maximum UDP response size for this client.
1923170222Sdougb	 */
1924170222Sdougb	if (client->udpsize > 512) {
1925170222Sdougb		dns_peer_t *peer = NULL;
1926170222Sdougb		isc_uint16_t udpsize = view->maxudp;
1927170222Sdougb		(void) dns_peerlist_peerbyaddr(view->peers, &netaddr, &peer);
1928170222Sdougb		if (peer != NULL)
1929170222Sdougb			dns_peer_getmaxudp(peer, &udpsize);
1930170222Sdougb		if (client->udpsize > udpsize)
1931170222Sdougb			client->udpsize = udpsize;
1932170222Sdougb	}
1933170222Sdougb
1934170222Sdougb	/*
1935135446Strhodes	 * Dispatch the request.
1936135446Strhodes	 */
1937135446Strhodes	switch (client->message->opcode) {
1938135446Strhodes	case dns_opcode_query:
1939135446Strhodes		CTRACE("query");
1940135446Strhodes		ns_query_start(client);
1941135446Strhodes		break;
1942135446Strhodes	case dns_opcode_update:
1943135446Strhodes		CTRACE("update");
1944135446Strhodes		ns_client_settimeout(client, 60);
1945135446Strhodes		ns_update_start(client, sigresult);
1946135446Strhodes		break;
1947135446Strhodes	case dns_opcode_notify:
1948135446Strhodes		CTRACE("notify");
1949135446Strhodes		ns_client_settimeout(client, 60);
1950135446Strhodes		ns_notify_start(client);
1951135446Strhodes		break;
1952135446Strhodes	case dns_opcode_iquery:
1953135446Strhodes		CTRACE("iquery");
1954135446Strhodes		ns_client_error(client, DNS_R_NOTIMP);
1955135446Strhodes		break;
1956135446Strhodes	default:
1957135446Strhodes		CTRACE("unknown opcode");
1958135446Strhodes		ns_client_error(client, DNS_R_NOTIMP);
1959135446Strhodes	}
1960135446Strhodes
1961135446Strhodes cleanup:
1962135446Strhodes	return;
1963135446Strhodes}
1964135446Strhodes
1965135446Strhodesstatic void
1966135446Strhodesclient_timeout(isc_task_t *task, isc_event_t *event) {
1967135446Strhodes	ns_client_t *client;
1968135446Strhodes
1969135446Strhodes	REQUIRE(event != NULL);
1970135446Strhodes	REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
1971135446Strhodes		event->ev_type == ISC_TIMEREVENT_IDLE);
1972135446Strhodes	client = event->ev_arg;
1973135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
1974135446Strhodes	REQUIRE(task == client->task);
1975135446Strhodes	REQUIRE(client->timer != NULL);
1976135446Strhodes
1977135446Strhodes	UNUSED(task);
1978135446Strhodes
1979135446Strhodes	CTRACE("timeout");
1980135446Strhodes
1981135446Strhodes	isc_event_free(&event);
1982135446Strhodes
1983135446Strhodes	if (client->shutdown != NULL) {
1984135446Strhodes		(client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
1985135446Strhodes		client->shutdown = NULL;
1986135446Strhodes		client->shutdown_arg = NULL;
1987135446Strhodes	}
1988135446Strhodes
1989135446Strhodes	if (client->newstate > NS_CLIENTSTATE_READY)
1990135446Strhodes		client->newstate = NS_CLIENTSTATE_READY;
1991135446Strhodes	(void)exit_check(client);
1992135446Strhodes}
1993135446Strhodes
1994135446Strhodesstatic isc_result_t
1995170222Sdougbget_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
1996170222Sdougb	isc_mem_t *clientmctx;
1997170222Sdougb	isc_result_t result;
1998254897Serwin#if NMCTXS > 0
1999254897Serwin	unsigned int nextmctx;
2000254897Serwin#endif
2001170222Sdougb
2002254897Serwin	MTRACE("clientmctx");
2003254897Serwin
2004170222Sdougb	/*
2005170222Sdougb	 * Caller must be holding the manager lock.
2006170222Sdougb	 */
2007193149Sdougb	if (ns_g_clienttest) {
2008193149Sdougb		result = isc_mem_create(0, 0, mctxp);
2009193149Sdougb		if (result == ISC_R_SUCCESS)
2010193149Sdougb			isc_mem_setname(*mctxp, "client", NULL);
2011193149Sdougb		return (result);
2012193149Sdougb	}
2013170222Sdougb#if NMCTXS > 0
2014254897Serwin	nextmctx = manager->nextmctx++;
2015254897Serwin	if (manager->nextmctx == NMCTXS)
2016254897Serwin		manager->nextmctx = 0;
2017254897Serwin
2018254897Serwin	INSIST(nextmctx < NMCTXS);
2019254897Serwin
2020254897Serwin	clientmctx = manager->mctxpool[nextmctx];
2021170222Sdougb	if (clientmctx == NULL) {
2022170222Sdougb		result = isc_mem_create(0, 0, &clientmctx);
2023170222Sdougb		if (result != ISC_R_SUCCESS)
2024170222Sdougb			return (result);
2025193149Sdougb		isc_mem_setname(clientmctx, "client", NULL);
2026170222Sdougb
2027254897Serwin		manager->mctxpool[nextmctx] = clientmctx;
2028170222Sdougb	}
2029170222Sdougb#else
2030170222Sdougb	clientmctx = manager->mctx;
2031170222Sdougb#endif
2032170222Sdougb
2033170222Sdougb	isc_mem_attach(clientmctx, mctxp);
2034170222Sdougb
2035170222Sdougb	return (ISC_R_SUCCESS);
2036170222Sdougb}
2037170222Sdougb
2038170222Sdougbstatic isc_result_t
2039153816Sdougbclient_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
2040135446Strhodes	ns_client_t *client;
2041135446Strhodes	isc_result_t result;
2042170222Sdougb	isc_mem_t *mctx = NULL;
2043135446Strhodes
2044135446Strhodes	/*
2045135446Strhodes	 * Caller must be holding the manager lock.
2046135446Strhodes	 *
2047135446Strhodes	 * Note: creating a client does not add the client to the
2048135446Strhodes	 * manager's client list or set the client's manager pointer.
2049135446Strhodes	 * The caller is responsible for that.
2050135446Strhodes	 */
2051135446Strhodes
2052135446Strhodes	REQUIRE(clientp != NULL && *clientp == NULL);
2053135446Strhodes
2054170222Sdougb	result = get_clientmctx(manager, &mctx);
2055170222Sdougb	if (result != ISC_R_SUCCESS)
2056170222Sdougb		return (result);
2057170222Sdougb
2058170222Sdougb	client = isc_mem_get(mctx, sizeof(*client));
2059170222Sdougb	if (client == NULL) {
2060170222Sdougb		isc_mem_detach(&mctx);
2061135446Strhodes		return (ISC_R_NOMEMORY);
2062170222Sdougb	}
2063170222Sdougb	client->mctx = mctx;
2064135446Strhodes
2065135446Strhodes	client->task = NULL;
2066135446Strhodes	result = isc_task_create(manager->taskmgr, 0, &client->task);
2067135446Strhodes	if (result != ISC_R_SUCCESS)
2068135446Strhodes		goto cleanup_client;
2069135446Strhodes	isc_task_setname(client->task, "client", client);
2070135446Strhodes
2071135446Strhodes	client->timer = NULL;
2072135446Strhodes	result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
2073135446Strhodes				  NULL, NULL, client->task, client_timeout,
2074135446Strhodes				  client, &client->timer);
2075135446Strhodes	if (result != ISC_R_SUCCESS)
2076135446Strhodes		goto cleanup_task;
2077135446Strhodes	client->timerset = ISC_FALSE;
2078135446Strhodes
2079135446Strhodes	client->message = NULL;
2080170222Sdougb	result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
2081135446Strhodes				    &client->message);
2082135446Strhodes	if (result != ISC_R_SUCCESS)
2083135446Strhodes		goto cleanup_timer;
2084135446Strhodes
2085135446Strhodes	/* XXXRTH  Hardwired constants */
2086135446Strhodes
2087135446Strhodes	client->sendevent = (isc_socketevent_t *)
2088170222Sdougb			    isc_event_allocate(client->mctx, client,
2089135446Strhodes					       ISC_SOCKEVENT_SENDDONE,
2090135446Strhodes					       client_senddone, client,
2091135446Strhodes					       sizeof(isc_socketevent_t));
2092135446Strhodes	if (client->sendevent == NULL) {
2093135446Strhodes		result = ISC_R_NOMEMORY;
2094135446Strhodes		goto cleanup_message;
2095135446Strhodes	}
2096135446Strhodes
2097170222Sdougb	client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
2098135446Strhodes	if  (client->recvbuf == NULL) {
2099135446Strhodes		result = ISC_R_NOMEMORY;
2100135446Strhodes		goto cleanup_sendevent;
2101135446Strhodes	}
2102135446Strhodes
2103135446Strhodes	client->recvevent = (isc_socketevent_t *)
2104170222Sdougb			    isc_event_allocate(client->mctx, client,
2105135446Strhodes					       ISC_SOCKEVENT_RECVDONE,
2106135446Strhodes					       client_request, client,
2107135446Strhodes					       sizeof(isc_socketevent_t));
2108135446Strhodes	if (client->recvevent == NULL) {
2109135446Strhodes		result = ISC_R_NOMEMORY;
2110135446Strhodes		goto cleanup_recvbuf;
2111135446Strhodes	}
2112135446Strhodes
2113135446Strhodes	client->magic = NS_CLIENT_MAGIC;
2114135446Strhodes	client->manager = NULL;
2115135446Strhodes	client->state = NS_CLIENTSTATE_INACTIVE;
2116135446Strhodes	client->newstate = NS_CLIENTSTATE_MAX;
2117135446Strhodes	client->naccepts = 0;
2118135446Strhodes	client->nreads = 0;
2119135446Strhodes	client->nsends = 0;
2120135446Strhodes	client->nrecvs = 0;
2121135446Strhodes	client->nupdates = 0;
2122135446Strhodes	client->nctls = 0;
2123135446Strhodes	client->references = 0;
2124135446Strhodes	client->attributes = 0;
2125135446Strhodes	client->view = NULL;
2126135446Strhodes	client->dispatch = NULL;
2127135446Strhodes	client->udpsocket = NULL;
2128135446Strhodes	client->tcplistener = NULL;
2129135446Strhodes	client->tcpsocket = NULL;
2130135446Strhodes	client->tcpmsg_valid = ISC_FALSE;
2131135446Strhodes	client->tcpbuf = NULL;
2132135446Strhodes	client->opt = NULL;
2133135446Strhodes	client->udpsize = 512;
2134135446Strhodes	client->extflags = 0;
2135170222Sdougb	client->ednsversion = -1;
2136135446Strhodes	client->next = NULL;
2137135446Strhodes	client->shutdown = NULL;
2138135446Strhodes	client->shutdown_arg = NULL;
2139225361Sdougb	client->signer = NULL;
2140135446Strhodes	dns_name_init(&client->signername, NULL);
2141135446Strhodes	client->mortal = ISC_FALSE;
2142135446Strhodes	client->tcpquota = NULL;
2143135446Strhodes	client->recursionquota = NULL;
2144135446Strhodes	client->interface = NULL;
2145135446Strhodes	client->peeraddr_valid = ISC_FALSE;
2146234010Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
2147234010Sdougb	client->filter_aaaa = dns_v4_aaaa_ok;
2148234010Sdougb#endif
2149254897Serwin	client->needshutdown = ns_g_clienttest;
2150254897Serwin
2151135446Strhodes	ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
2152135446Strhodes		       NS_EVENT_CLIENTCONTROL, client_start, client, client,
2153135446Strhodes		       NULL, NULL);
2154135446Strhodes	/*
2155135446Strhodes	 * Initialize FORMERR cache to sentinel value that will not match
2156135446Strhodes	 * any actual FORMERR response.
2157135446Strhodes	 */
2158135446Strhodes	isc_sockaddr_any(&client->formerrcache.addr);
2159135446Strhodes	client->formerrcache.time = 0;
2160135446Strhodes	client->formerrcache.id = 0;
2161135446Strhodes	ISC_LINK_INIT(client, link);
2162254897Serwin	ISC_LINK_INIT(client, rlink);
2163254897Serwin	ISC_QLINK_INIT(client, ilink);
2164135446Strhodes
2165135446Strhodes	/*
2166135446Strhodes	 * We call the init routines for the various kinds of client here,
2167135446Strhodes	 * after we have created an otherwise valid client, because some
2168135446Strhodes	 * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
2169135446Strhodes	 */
2170135446Strhodes	result = ns_query_init(client);
2171135446Strhodes	if (result != ISC_R_SUCCESS)
2172135446Strhodes		goto cleanup_recvevent;
2173135446Strhodes
2174135446Strhodes	result = isc_task_onshutdown(client->task, client_shutdown, client);
2175135446Strhodes	if (result != ISC_R_SUCCESS)
2176135446Strhodes		goto cleanup_query;
2177135446Strhodes
2178135446Strhodes	CTRACE("create");
2179135446Strhodes
2180135446Strhodes	*clientp = client;
2181135446Strhodes
2182135446Strhodes	return (ISC_R_SUCCESS);
2183135446Strhodes
2184135446Strhodes cleanup_query:
2185135446Strhodes	ns_query_free(client);
2186135446Strhodes
2187135446Strhodes cleanup_recvevent:
2188135446Strhodes	isc_event_free((isc_event_t **)&client->recvevent);
2189135446Strhodes
2190135446Strhodes cleanup_recvbuf:
2191170222Sdougb	isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
2192135446Strhodes
2193135446Strhodes cleanup_sendevent:
2194135446Strhodes	isc_event_free((isc_event_t **)&client->sendevent);
2195135446Strhodes
2196135446Strhodes	client->magic = 0;
2197135446Strhodes
2198135446Strhodes cleanup_message:
2199135446Strhodes	dns_message_destroy(&client->message);
2200135446Strhodes
2201135446Strhodes cleanup_timer:
2202135446Strhodes	isc_timer_detach(&client->timer);
2203135446Strhodes
2204135446Strhodes cleanup_task:
2205135446Strhodes	isc_task_detach(&client->task);
2206135446Strhodes
2207135446Strhodes cleanup_client:
2208170222Sdougb	isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
2209135446Strhodes
2210135446Strhodes	return (result);
2211135446Strhodes}
2212135446Strhodes
2213135446Strhodesstatic void
2214135446Strhodesclient_read(ns_client_t *client) {
2215135446Strhodes	isc_result_t result;
2216135446Strhodes
2217135446Strhodes	CTRACE("read");
2218135446Strhodes
2219135446Strhodes	result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
2220135446Strhodes					client_request, client);
2221135446Strhodes	if (result != ISC_R_SUCCESS)
2222135446Strhodes		goto fail;
2223135446Strhodes
2224135446Strhodes	/*
2225135446Strhodes	 * Set a timeout to limit the amount of time we will wait
2226135446Strhodes	 * for a request on this TCP connection.
2227135446Strhodes	 */
2228135446Strhodes	ns_client_settimeout(client, 30);
2229135446Strhodes
2230135446Strhodes	client->state = client->newstate = NS_CLIENTSTATE_READING;
2231135446Strhodes	INSIST(client->nreads == 0);
2232135446Strhodes	INSIST(client->recursionquota == NULL);
2233135446Strhodes	client->nreads++;
2234135446Strhodes
2235135446Strhodes	return;
2236135446Strhodes fail:
2237135446Strhodes	ns_client_next(client, result);
2238135446Strhodes}
2239135446Strhodes
2240135446Strhodesstatic void
2241135446Strhodesclient_newconn(isc_task_t *task, isc_event_t *event) {
2242135446Strhodes	ns_client_t *client = event->ev_arg;
2243135446Strhodes	isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
2244135446Strhodes	isc_result_t result;
2245135446Strhodes
2246135446Strhodes	REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
2247135446Strhodes	REQUIRE(NS_CLIENT_VALID(client));
2248135446Strhodes	REQUIRE(client->task == task);
2249135446Strhodes
2250135446Strhodes	UNUSED(task);
2251135446Strhodes
2252135446Strhodes	INSIST(client->state == NS_CLIENTSTATE_READY);
2253135446Strhodes
2254135446Strhodes	INSIST(client->naccepts == 1);
2255135446Strhodes	client->naccepts--;
2256135446Strhodes
2257135446Strhodes	LOCK(&client->interface->lock);
2258135446Strhodes	INSIST(client->interface->ntcpcurrent > 0);
2259135446Strhodes	client->interface->ntcpcurrent--;
2260135446Strhodes	UNLOCK(&client->interface->lock);
2261135446Strhodes
2262135446Strhodes	/*
2263135446Strhodes	 * We must take ownership of the new socket before the exit
2264135446Strhodes	 * check to make sure it gets destroyed if we decide to exit.
2265135446Strhodes	 */
2266135446Strhodes	if (nevent->result == ISC_R_SUCCESS) {
2267135446Strhodes		client->tcpsocket = nevent->newsocket;
2268193149Sdougb		isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
2269135446Strhodes		client->state = NS_CLIENTSTATE_READING;
2270135446Strhodes		INSIST(client->recursionquota == NULL);
2271135446Strhodes
2272135446Strhodes		(void)isc_socket_getpeername(client->tcpsocket,
2273135446Strhodes					     &client->peeraddr);
2274135446Strhodes		client->peeraddr_valid = ISC_TRUE;
2275135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2276135446Strhodes			   NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2277135446Strhodes			   "new TCP connection");
2278135446Strhodes	} else {
2279135446Strhodes		/*
2280135446Strhodes		 * XXXRTH  What should we do?  We're trying to accept but
2281193149Sdougb		 *	   it didn't work.  If we just give up, then TCP
2282135446Strhodes		 *	   service may eventually stop.
2283135446Strhodes		 *
2284135446Strhodes		 *	   For now, we just go idle.
2285135446Strhodes		 *
2286135446Strhodes		 *	   Going idle is probably the right thing if the
2287135446Strhodes		 *	   I/O was canceled.
2288135446Strhodes		 */
2289135446Strhodes		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2290135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2291135446Strhodes			      "accept failed: %s",
2292135446Strhodes			      isc_result_totext(nevent->result));
2293135446Strhodes	}
2294135446Strhodes
2295135446Strhodes	if (exit_check(client))
2296135446Strhodes		goto freeevent;
2297135446Strhodes
2298135446Strhodes	if (nevent->result == ISC_R_SUCCESS) {
2299135446Strhodes		int match;
2300135446Strhodes		isc_netaddr_t netaddr;
2301135446Strhodes
2302135446Strhodes		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
2303135446Strhodes
2304135446Strhodes		if (ns_g_server->blackholeacl != NULL &&
2305135446Strhodes		    dns_acl_match(&netaddr, NULL,
2306186462Sdougb				  ns_g_server->blackholeacl,
2307135446Strhodes				  &ns_g_server->aclenv,
2308135446Strhodes				  &match, NULL) == ISC_R_SUCCESS &&
2309135446Strhodes		    match > 0)
2310135446Strhodes		{
2311135446Strhodes			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2312135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2313135446Strhodes				      "blackholed connection attempt");
2314135446Strhodes			client->newstate = NS_CLIENTSTATE_READY;
2315135446Strhodes			(void)exit_check(client);
2316135446Strhodes			goto freeevent;
2317135446Strhodes		}
2318135446Strhodes
2319135446Strhodes		INSIST(client->tcpmsg_valid == ISC_FALSE);
2320135446Strhodes		dns_tcpmsg_init(client->mctx, client->tcpsocket,
2321135446Strhodes				&client->tcpmsg);
2322135446Strhodes		client->tcpmsg_valid = ISC_TRUE;
2323135446Strhodes
2324135446Strhodes		/*
2325135446Strhodes		 * Let a new client take our place immediately, before
2326135446Strhodes		 * we wait for a request packet.  If we don't,
2327135446Strhodes		 * telnetting to port 53 (once per CPU) will
2328193149Sdougb		 * deny service to legitimate TCP clients.
2329135446Strhodes		 */
2330135446Strhodes		result = isc_quota_attach(&ns_g_server->tcpquota,
2331135446Strhodes					  &client->tcpquota);
2332135446Strhodes		if (result == ISC_R_SUCCESS)
2333135446Strhodes			result = ns_client_replace(client);
2334135446Strhodes		if (result != ISC_R_SUCCESS) {
2335135446Strhodes			ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2336135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
2337135446Strhodes				      "no more TCP clients: %s",
2338135446Strhodes				      isc_result_totext(result));
2339135446Strhodes		}
2340135446Strhodes
2341135446Strhodes		client_read(client);
2342135446Strhodes	}
2343135446Strhodes
2344135446Strhodes freeevent:
2345135446Strhodes	isc_event_free(&event);
2346135446Strhodes}
2347135446Strhodes
2348135446Strhodesstatic void
2349135446Strhodesclient_accept(ns_client_t *client) {
2350135446Strhodes	isc_result_t result;
2351135446Strhodes
2352135446Strhodes	CTRACE("accept");
2353135446Strhodes
2354135446Strhodes	result = isc_socket_accept(client->tcplistener, client->task,
2355135446Strhodes				   client_newconn, client);
2356135446Strhodes	if (result != ISC_R_SUCCESS) {
2357135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
2358135446Strhodes				 "isc_socket_accept() failed: %s",
2359135446Strhodes				 isc_result_totext(result));
2360135446Strhodes		/*
2361135446Strhodes		 * XXXRTH  What should we do?  We're trying to accept but
2362193149Sdougb		 *	   it didn't work.  If we just give up, then TCP
2363135446Strhodes		 *	   service may eventually stop.
2364135446Strhodes		 *
2365135446Strhodes		 *	   For now, we just go idle.
2366135446Strhodes		 */
2367135446Strhodes		return;
2368135446Strhodes	}
2369135446Strhodes	INSIST(client->naccepts == 0);
2370135446Strhodes	client->naccepts++;
2371135446Strhodes	LOCK(&client->interface->lock);
2372135446Strhodes	client->interface->ntcpcurrent++;
2373135446Strhodes	UNLOCK(&client->interface->lock);
2374135446Strhodes}
2375135446Strhodes
2376135446Strhodesstatic void
2377135446Strhodesclient_udprecv(ns_client_t *client) {
2378135446Strhodes	isc_result_t result;
2379135446Strhodes	isc_region_t r;
2380135446Strhodes
2381135446Strhodes	CTRACE("udprecv");
2382135446Strhodes
2383135446Strhodes	r.base = client->recvbuf;
2384135446Strhodes	r.length = RECV_BUFFER_SIZE;
2385135446Strhodes	result = isc_socket_recv2(client->udpsocket, &r, 1,
2386135446Strhodes				  client->task, client->recvevent, 0);
2387135446Strhodes	if (result != ISC_R_SUCCESS) {
2388135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
2389143731Sdougb				 "isc_socket_recv2() failed: %s",
2390135446Strhodes				 isc_result_totext(result));
2391135446Strhodes		/*
2392135446Strhodes		 * This cannot happen in the current implementation, since
2393135446Strhodes		 * isc_socket_recv2() cannot fail if flags == 0.
2394135446Strhodes		 *
2395135446Strhodes		 * If this does fail, we just go idle.
2396135446Strhodes		 */
2397135446Strhodes		return;
2398135446Strhodes	}
2399135446Strhodes	INSIST(client->nrecvs == 0);
2400135446Strhodes	client->nrecvs++;
2401135446Strhodes}
2402135446Strhodes
2403135446Strhodesvoid
2404135446Strhodesns_client_attach(ns_client_t *source, ns_client_t **targetp) {
2405135446Strhodes	REQUIRE(NS_CLIENT_VALID(source));
2406135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
2407135446Strhodes
2408135446Strhodes	source->references++;
2409135446Strhodes	ns_client_log(source, NS_LOGCATEGORY_CLIENT,
2410135446Strhodes		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2411135446Strhodes		      "ns_client_attach: ref = %d", source->references);
2412135446Strhodes	*targetp = source;
2413135446Strhodes}
2414135446Strhodes
2415135446Strhodesvoid
2416135446Strhodesns_client_detach(ns_client_t **clientp) {
2417135446Strhodes	ns_client_t *client = *clientp;
2418135446Strhodes
2419135446Strhodes	client->references--;
2420135446Strhodes	INSIST(client->references >= 0);
2421135446Strhodes	*clientp = NULL;
2422135446Strhodes	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
2423135446Strhodes		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
2424135446Strhodes		      "ns_client_detach: ref = %d", client->references);
2425135446Strhodes	(void)exit_check(client);
2426135446Strhodes}
2427135446Strhodes
2428135446Strhodesisc_boolean_t
2429135446Strhodesns_client_shuttingdown(ns_client_t *client) {
2430135446Strhodes	return (ISC_TF(client->newstate == NS_CLIENTSTATE_FREED));
2431135446Strhodes}
2432135446Strhodes
2433135446Strhodesisc_result_t
2434135446Strhodesns_client_replace(ns_client_t *client) {
2435135446Strhodes	isc_result_t result;
2436135446Strhodes
2437135446Strhodes	CTRACE("replace");
2438135446Strhodes
2439254402Serwin	REQUIRE(client != NULL);
2440254402Serwin	REQUIRE(client->manager != NULL);
2441254402Serwin
2442254897Serwin	result = get_client(client->manager, client->interface,
2443254897Serwin			    client->dispatch, TCP_CLIENT(client));
2444135446Strhodes	if (result != ISC_R_SUCCESS)
2445135446Strhodes		return (result);
2446135446Strhodes
2447135446Strhodes	/*
2448135446Strhodes	 * The responsibility for listening for new requests is hereby
2449135446Strhodes	 * transferred to the new client.  Therefore, the old client
2450135446Strhodes	 * should refrain from listening for any more requests.
2451135446Strhodes	 */
2452135446Strhodes	client->mortal = ISC_TRUE;
2453135446Strhodes
2454135446Strhodes	return (ISC_R_SUCCESS);
2455135446Strhodes}
2456135446Strhodes
2457135446Strhodes/***
2458135446Strhodes *** Client Manager
2459135446Strhodes ***/
2460135446Strhodes
2461135446Strhodesstatic void
2462135446Strhodesclientmgr_destroy(ns_clientmgr_t *manager) {
2463170222Sdougb#if NMCTXS > 0
2464170222Sdougb	int i;
2465170222Sdougb#endif
2466170222Sdougb
2467254897Serwin	REQUIRE(ISC_LIST_EMPTY(manager->clients));
2468135446Strhodes
2469135446Strhodes	MTRACE("clientmgr_destroy");
2470135446Strhodes
2471170222Sdougb#if NMCTXS > 0
2472170222Sdougb	for (i = 0; i < NMCTXS; i++) {
2473170222Sdougb		if (manager->mctxpool[i] != NULL)
2474170222Sdougb			isc_mem_detach(&manager->mctxpool[i]);
2475170222Sdougb	}
2476170222Sdougb#endif
2477170222Sdougb
2478254897Serwin	ISC_QUEUE_DESTROY(manager->inactive);
2479135446Strhodes	DESTROYLOCK(&manager->lock);
2480254897Serwin	DESTROYLOCK(&manager->listlock);
2481254897Serwin	DESTROYLOCK(&manager->reclock);
2482135446Strhodes	manager->magic = 0;
2483135446Strhodes	isc_mem_put(manager->mctx, manager, sizeof(*manager));
2484135446Strhodes}
2485135446Strhodes
2486135446Strhodesisc_result_t
2487135446Strhodesns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
2488135446Strhodes		    isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
2489135446Strhodes{
2490135446Strhodes	ns_clientmgr_t *manager;
2491135446Strhodes	isc_result_t result;
2492170222Sdougb#if NMCTXS > 0
2493170222Sdougb	int i;
2494170222Sdougb#endif
2495135446Strhodes
2496135446Strhodes	manager = isc_mem_get(mctx, sizeof(*manager));
2497135446Strhodes	if (manager == NULL)
2498135446Strhodes		return (ISC_R_NOMEMORY);
2499135446Strhodes
2500135446Strhodes	result = isc_mutex_init(&manager->lock);
2501135446Strhodes	if (result != ISC_R_SUCCESS)
2502135446Strhodes		goto cleanup_manager;
2503135446Strhodes
2504254897Serwin	result = isc_mutex_init(&manager->listlock);
2505254897Serwin	if (result != ISC_R_SUCCESS)
2506254897Serwin		goto cleanup_lock;
2507254897Serwin
2508254897Serwin	result = isc_mutex_init(&manager->reclock);
2509254897Serwin	if (result != ISC_R_SUCCESS)
2510254897Serwin		goto cleanup_listlock;
2511254897Serwin
2512135446Strhodes	manager->mctx = mctx;
2513135446Strhodes	manager->taskmgr = taskmgr;
2514135446Strhodes	manager->timermgr = timermgr;
2515135446Strhodes	manager->exiting = ISC_FALSE;
2516254897Serwin	ISC_LIST_INIT(manager->clients);
2517135446Strhodes	ISC_LIST_INIT(manager->recursing);
2518254897Serwin	ISC_QUEUE_INIT(manager->inactive, ilink);
2519170222Sdougb#if NMCTXS > 0
2520170222Sdougb	manager->nextmctx = 0;
2521170222Sdougb	for (i = 0; i < NMCTXS; i++)
2522170222Sdougb		manager->mctxpool[i] = NULL; /* will be created on-demand */
2523170222Sdougb#endif
2524135446Strhodes	manager->magic = MANAGER_MAGIC;
2525135446Strhodes
2526135446Strhodes	MTRACE("create");
2527135446Strhodes
2528135446Strhodes	*managerp = manager;
2529135446Strhodes
2530135446Strhodes	return (ISC_R_SUCCESS);
2531135446Strhodes
2532254897Serwin cleanup_listlock:
2533254897Serwin	(void) isc_mutex_destroy(&manager->listlock);
2534254897Serwin
2535254897Serwin cleanup_lock:
2536254897Serwin	(void) isc_mutex_destroy(&manager->lock);
2537254897Serwin
2538135446Strhodes cleanup_manager:
2539135446Strhodes	isc_mem_put(manager->mctx, manager, sizeof(*manager));
2540135446Strhodes
2541135446Strhodes	return (result);
2542135446Strhodes}
2543135446Strhodes
2544135446Strhodesvoid
2545135446Strhodesns_clientmgr_destroy(ns_clientmgr_t **managerp) {
2546254897Serwin	isc_result_t result;
2547135446Strhodes	ns_clientmgr_t *manager;
2548135446Strhodes	ns_client_t *client;
2549254897Serwin	isc_boolean_t need_destroy = ISC_FALSE, unlock = ISC_FALSE;
2550135446Strhodes
2551135446Strhodes	REQUIRE(managerp != NULL);
2552135446Strhodes	manager = *managerp;
2553135446Strhodes	REQUIRE(VALID_MANAGER(manager));
2554135446Strhodes
2555135446Strhodes	MTRACE("destroy");
2556135446Strhodes
2557254897Serwin	/*
2558254897Serwin	 * Check for success because we may already be task-exclusive
2559254897Serwin	 * at this point.  Only if we succeed at obtaining an exclusive
2560254897Serwin	 * lock now will we need to relinquish it later.
2561254897Serwin	 */
2562254897Serwin	result = isc_task_beginexclusive(ns_g_server->task);
2563254897Serwin	if (result == ISC_R_SUCCESS)
2564254897Serwin		unlock = ISC_TRUE;
2565135446Strhodes
2566135446Strhodes	manager->exiting = ISC_TRUE;
2567135446Strhodes
2568254897Serwin	for (client = ISC_LIST_HEAD(manager->clients);
2569135446Strhodes	     client != NULL;
2570135446Strhodes	     client = ISC_LIST_NEXT(client, link))
2571135446Strhodes		isc_task_shutdown(client->task);
2572135446Strhodes
2573254897Serwin	if (ISC_LIST_EMPTY(manager->clients))
2574135446Strhodes		need_destroy = ISC_TRUE;
2575135446Strhodes
2576254897Serwin	if (unlock)
2577254897Serwin		isc_task_endexclusive(ns_g_server->task);
2578135446Strhodes
2579135446Strhodes	if (need_destroy)
2580135446Strhodes		clientmgr_destroy(manager);
2581135446Strhodes
2582135446Strhodes	*managerp = NULL;
2583135446Strhodes}
2584135446Strhodes
2585254897Serwinstatic isc_result_t
2586254897Serwinget_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
2587254897Serwin	   dns_dispatch_t *disp, isc_boolean_t tcp)
2588135446Strhodes{
2589135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2590254897Serwin	isc_event_t *ev;
2591135446Strhodes	ns_client_t *client;
2592254897Serwin	MTRACE("get client");
2593135446Strhodes
2594254897Serwin	REQUIRE(manager != NULL);
2595135446Strhodes
2596254897Serwin	if (manager->exiting)
2597254897Serwin		return (ISC_R_SHUTTINGDOWN);
2598135446Strhodes
2599135446Strhodes	/*
2600254897Serwin	 * Allocate a client.  First try to get a recycled one;
2601254897Serwin	 * if that fails, make a new one.
2602135446Strhodes	 */
2603254897Serwin	client = NULL;
2604254897Serwin	if (!ns_g_clienttest)
2605254897Serwin		ISC_QUEUE_POP(manager->inactive, ilink, client);
2606135446Strhodes
2607254897Serwin	if (client != NULL)
2608254897Serwin		MTRACE("recycle");
2609254897Serwin	else {
2610254897Serwin		MTRACE("create new");
2611135446Strhodes
2612254897Serwin		LOCK(&manager->lock);
2613254897Serwin		result = client_create(manager, &client);
2614254897Serwin		UNLOCK(&manager->lock);
2615254897Serwin		if (result != ISC_R_SUCCESS)
2616254897Serwin			return (result);
2617135446Strhodes
2618254897Serwin		LOCK(&manager->listlock);
2619254897Serwin		ISC_LIST_APPEND(manager->clients, client, link);
2620254897Serwin		UNLOCK(&manager->listlock);
2621254897Serwin	}
2622135446Strhodes
2623254897Serwin	client->manager = manager;
2624254897Serwin	ns_interface_attach(ifp, &client->interface);
2625254897Serwin	client->state = NS_CLIENTSTATE_READY;
2626254897Serwin	INSIST(client->recursionquota == NULL);
2627135446Strhodes
2628254897Serwin	if (tcp) {
2629254897Serwin		client->attributes |= NS_CLIENTATTR_TCP;
2630254897Serwin		isc_socket_attach(ifp->tcpsocket,
2631254897Serwin				  &client->tcplistener);
2632254897Serwin	} else {
2633254897Serwin		isc_socket_t *sock;
2634135446Strhodes
2635254897Serwin		dns_dispatch_attach(disp, &client->dispatch);
2636254897Serwin		sock = dns_dispatch_getsocket(client->dispatch);
2637254897Serwin		isc_socket_attach(sock, &client->udpsocket);
2638135446Strhodes	}
2639254897Serwin
2640254897Serwin	INSIST(client->nctls == 0);
2641254897Serwin	client->nctls++;
2642254897Serwin	ev = &client->ctlevent;
2643254897Serwin	isc_task_send(client->task, &ev);
2644254897Serwin
2645254897Serwin	return (ISC_R_SUCCESS);
2646254897Serwin}
2647254897Serwin
2648254897Serwinisc_result_t
2649254897Serwinns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
2650254897Serwin			   ns_interface_t *ifp, isc_boolean_t tcp)
2651254897Serwin{
2652254897Serwin	isc_result_t result = ISC_R_SUCCESS;
2653254897Serwin	unsigned int disp;
2654254897Serwin
2655254897Serwin	REQUIRE(VALID_MANAGER(manager));
2656254897Serwin	REQUIRE(n > 0);
2657254897Serwin
2658254897Serwin	MTRACE("createclients");
2659254897Serwin
2660254897Serwin	for (disp = 0; disp < n; disp++) {
2661254897Serwin		result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
2662254897Serwin		if (result != ISC_R_SUCCESS)
2663254897Serwin			break;
2664135446Strhodes	}
2665135446Strhodes
2666135446Strhodes	return (result);
2667135446Strhodes}
2668135446Strhodes
2669135446Strhodesisc_sockaddr_t *
2670135446Strhodesns_client_getsockaddr(ns_client_t *client) {
2671135446Strhodes	return (&client->peeraddr);
2672135446Strhodes}
2673135446Strhodes
2674135446Strhodesisc_result_t
2675224092Sdougbns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
2676193149Sdougb			 dns_acl_t *acl, isc_boolean_t default_allow)
2677135446Strhodes{
2678135446Strhodes	isc_result_t result;
2679224092Sdougb	isc_netaddr_t tmpnetaddr;
2680135446Strhodes	int match;
2681135446Strhodes
2682135446Strhodes	if (acl == NULL) {
2683135446Strhodes		if (default_allow)
2684135446Strhodes			goto allow;
2685135446Strhodes		else
2686135446Strhodes			goto deny;
2687135446Strhodes	}
2688135446Strhodes
2689224092Sdougb	if (netaddr == NULL) {
2690224092Sdougb		isc_netaddr_fromsockaddr(&tmpnetaddr, &client->peeraddr);
2691224092Sdougb		netaddr = &tmpnetaddr;
2692224092Sdougb	}
2693135446Strhodes
2694224092Sdougb	result = dns_acl_match(netaddr, client->signer, acl,
2695224092Sdougb			       &ns_g_server->aclenv, &match, NULL);
2696193149Sdougb
2697135446Strhodes	if (result != ISC_R_SUCCESS)
2698135446Strhodes		goto deny; /* Internal error, already logged. */
2699135446Strhodes	if (match > 0)
2700135446Strhodes		goto allow;
2701135446Strhodes	goto deny; /* Negative match or no match. */
2702135446Strhodes
2703135446Strhodes allow:
2704135446Strhodes	return (ISC_R_SUCCESS);
2705135446Strhodes
2706135446Strhodes deny:
2707135446Strhodes	return (DNS_R_REFUSED);
2708135446Strhodes}
2709135446Strhodes
2710135446Strhodesisc_result_t
2711193149Sdougbns_client_checkacl(ns_client_t *client, isc_sockaddr_t *sockaddr,
2712135446Strhodes		   const char *opname, dns_acl_t *acl,
2713135446Strhodes		   isc_boolean_t default_allow, int log_level)
2714135446Strhodes{
2715224092Sdougb	isc_result_t result;
2716224092Sdougb	isc_netaddr_t netaddr;
2717135446Strhodes
2718224092Sdougb	if (sockaddr != NULL)
2719224092Sdougb		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
2720224092Sdougb
2721224092Sdougb	result = ns_client_checkaclsilent(client, sockaddr ? &netaddr : NULL,
2722224092Sdougb					  acl, default_allow);
2723224092Sdougb
2724186462Sdougb	if (result == ISC_R_SUCCESS)
2725135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2726135446Strhodes			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
2727135446Strhodes			      "%s approved", opname);
2728135446Strhodes	else
2729135446Strhodes		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
2730135446Strhodes			      NS_LOGMODULE_CLIENT,
2731135446Strhodes			      log_level, "%s denied", opname);
2732135446Strhodes	return (result);
2733135446Strhodes}
2734135446Strhodes
2735135446Strhodesstatic void
2736135446Strhodesns_client_name(ns_client_t *client, char *peerbuf, size_t len) {
2737135446Strhodes	if (client->peeraddr_valid)
2738262706Serwin		isc_sockaddr_format(&client->peeraddr, peerbuf,
2739262706Serwin				    (unsigned int)len);
2740135446Strhodes	else
2741135446Strhodes		snprintf(peerbuf, len, "@%p", client);
2742135446Strhodes}
2743135446Strhodes
2744135446Strhodesvoid
2745135446Strhodesns_client_logv(ns_client_t *client, isc_logcategory_t *category,
2746193149Sdougb	       isc_logmodule_t *module, int level, const char *fmt, va_list ap)
2747135446Strhodes{
2748135446Strhodes	char msgbuf[2048];
2749135446Strhodes	char peerbuf[ISC_SOCKADDR_FORMATSIZE];
2750254897Serwin	char signerbuf[DNS_NAME_FORMATSIZE], qnamebuf[DNS_NAME_FORMATSIZE];
2751254897Serwin	const char *viewname = "";
2752254897Serwin	const char *sep1 = "", *sep2 = "", *sep3 = "", *sep4 = "";
2753254897Serwin	const char *signer = "", *qname = "";
2754254897Serwin	dns_name_t *q = NULL;
2755135446Strhodes
2756135446Strhodes	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2757254897Serwin
2758135446Strhodes	ns_client_name(client, peerbuf, sizeof(peerbuf));
2759254897Serwin
2760254897Serwin	if (client->signer != NULL) {
2761254897Serwin		dns_name_format(client->signer, signerbuf, sizeof(signerbuf));
2762254897Serwin		sep1 = "/key ";
2763254897Serwin		signer = signerbuf;
2764254897Serwin	}
2765254897Serwin
2766254897Serwin	q = client->query.origqname != NULL
2767254897Serwin		? client->query.origqname : client->query.qname;
2768254897Serwin	if (q != NULL) {
2769254897Serwin		dns_name_format(q, qnamebuf, sizeof(qnamebuf));
2770254897Serwin		sep2 = " (";
2771254897Serwin		sep3 = ")";
2772254897Serwin		qname = qnamebuf;
2773254897Serwin	}
2774254897Serwin
2775135446Strhodes	if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
2776135446Strhodes	    strcmp(client->view->name, "_default") != 0) {
2777254897Serwin		sep4 = ": view ";
2778254897Serwin		viewname = client->view->name;
2779135446Strhodes	}
2780135446Strhodes
2781135446Strhodes	isc_log_write(ns_g_lctx, category, module, level,
2782254897Serwin		      "client %s%s%s%s%s%s%s%s: %s",
2783254897Serwin		      peerbuf, sep1, signer, sep2, qname, sep3,
2784254897Serwin		      sep4, viewname, msgbuf);
2785135446Strhodes}
2786135446Strhodes
2787135446Strhodesvoid
2788135446Strhodesns_client_log(ns_client_t *client, isc_logcategory_t *category,
2789135446Strhodes	   isc_logmodule_t *module, int level, const char *fmt, ...)
2790135446Strhodes{
2791135446Strhodes	va_list ap;
2792135446Strhodes
2793135446Strhodes	if (! isc_log_wouldlog(ns_g_lctx, level))
2794135446Strhodes		return;
2795135446Strhodes
2796135446Strhodes	va_start(ap, fmt);
2797135446Strhodes	ns_client_logv(client, category, module, level, fmt, ap);
2798135446Strhodes	va_end(ap);
2799135446Strhodes}
2800135446Strhodes
2801135446Strhodesvoid
2802135446Strhodesns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
2803186462Sdougb		 dns_rdataclass_t rdclass, char *buf, size_t len)
2804135446Strhodes{
2805186462Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
2806186462Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
2807186462Sdougb	char classbuf[DNS_RDATACLASS_FORMATSIZE];
2808135446Strhodes
2809186462Sdougb	dns_name_format(name, namebuf, sizeof(namebuf));
2810186462Sdougb	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
2811186462Sdougb	dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
2812186462Sdougb	(void)snprintf(buf, len, "%s '%s/%s/%s'", msg, namebuf, typebuf,
2813135446Strhodes		       classbuf);
2814135446Strhodes}
2815135446Strhodes
2816135446Strhodesstatic void
2817135446Strhodesns_client_dumpmessage(ns_client_t *client, const char *reason) {
2818135446Strhodes	isc_buffer_t buffer;
2819135446Strhodes	char *buf = NULL;
2820135446Strhodes	int len = 1024;
2821135446Strhodes	isc_result_t result;
2822135446Strhodes
2823135446Strhodes	/*
2824135446Strhodes	 * Note that these are multiline debug messages.  We want a newline
2825135446Strhodes	 * to appear in the log after each message.
2826135446Strhodes	 */
2827135446Strhodes
2828135446Strhodes	do {
2829135446Strhodes		buf = isc_mem_get(client->mctx, len);
2830135446Strhodes		if (buf == NULL)
2831135446Strhodes			break;
2832135446Strhodes		isc_buffer_init(&buffer, buf, len);
2833135446Strhodes		result = dns_message_totext(client->message,
2834135446Strhodes					    &dns_master_style_debug,
2835135446Strhodes					    0, &buffer);
2836135446Strhodes		if (result == ISC_R_NOSPACE) {
2837135446Strhodes			isc_mem_put(client->mctx, buf, len);
2838135446Strhodes			len += 1024;
2839135446Strhodes		} else if (result == ISC_R_SUCCESS)
2840186462Sdougb			ns_client_log(client, NS_LOGCATEGORY_UNMATCHED,
2841135446Strhodes				      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
2842135446Strhodes				      "%s\n%.*s", reason,
2843135446Strhodes				       (int)isc_buffer_usedlength(&buffer),
2844135446Strhodes				       buf);
2845135446Strhodes	} while (result == ISC_R_NOSPACE);
2846135446Strhodes
2847135446Strhodes	if (buf != NULL)
2848135446Strhodes		isc_mem_put(client->mctx, buf, len);
2849135446Strhodes}
2850135446Strhodes
2851135446Strhodesvoid
2852135446Strhodesns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) {
2853135446Strhodes	ns_client_t *client;
2854135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
2855224092Sdougb	char original[DNS_NAME_FORMATSIZE];
2856135446Strhodes	char peerbuf[ISC_SOCKADDR_FORMATSIZE];
2857224092Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
2858224092Sdougb	char classbuf[DNS_RDATACLASS_FORMATSIZE];
2859135446Strhodes	const char *name;
2860135446Strhodes	const char *sep;
2861224092Sdougb	const char *origfor;
2862224092Sdougb	dns_rdataset_t *rdataset;
2863135446Strhodes
2864135446Strhodes	REQUIRE(VALID_MANAGER(manager));
2865186462Sdougb
2866254897Serwin	LOCK(&manager->reclock);
2867135446Strhodes	client = ISC_LIST_HEAD(manager->recursing);
2868135446Strhodes	while (client != NULL) {
2869254897Serwin		INSIST(client->state == NS_CLIENTSTATE_RECURSING);
2870254897Serwin
2871135446Strhodes		ns_client_name(client, peerbuf, sizeof(peerbuf));
2872135446Strhodes		if (client->view != NULL &&
2873135446Strhodes		    strcmp(client->view->name, "_bind") != 0 &&
2874135446Strhodes		    strcmp(client->view->name, "_default") != 0) {
2875135446Strhodes			name = client->view->name;
2876135446Strhodes			sep = ": view ";
2877135446Strhodes		} else {
2878135446Strhodes			name = "";
2879135446Strhodes			sep = "";
2880135446Strhodes		}
2881254897Serwin
2882254897Serwin		LOCK(&client->query.fetchlock);
2883254897Serwin		INSIST(client->query.qname != NULL);
2884135446Strhodes		dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
2885224092Sdougb		if (client->query.qname != client->query.origqname &&
2886224092Sdougb		    client->query.origqname != NULL) {
2887224092Sdougb			origfor = " for ";
2888224092Sdougb			dns_name_format(client->query.origqname, original,
2889224092Sdougb					sizeof(original));
2890224092Sdougb		} else {
2891224092Sdougb			origfor = "";
2892224092Sdougb			original[0] = '\0';
2893224092Sdougb		}
2894224092Sdougb		rdataset = ISC_LIST_HEAD(client->query.qname->list);
2895224092Sdougb		if (rdataset == NULL && client->query.origqname != NULL)
2896224092Sdougb			rdataset = ISC_LIST_HEAD(client->query.origqname->list);
2897224092Sdougb		if (rdataset != NULL) {
2898224092Sdougb			dns_rdatatype_format(rdataset->type, typebuf,
2899224092Sdougb					     sizeof(typebuf));
2900224092Sdougb			dns_rdataclass_format(rdataset->rdclass, classbuf,
2901224092Sdougb					      sizeof(classbuf));
2902224092Sdougb		} else {
2903224092Sdougb			strcpy(typebuf, "-");
2904224092Sdougb			strcpy(classbuf, "-");
2905224092Sdougb		}
2906254897Serwin		UNLOCK(&client->query.fetchlock);
2907224092Sdougb		fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s "
2908224092Sdougb			"requesttime %d\n", peerbuf, sep, name,
2909224092Sdougb			client->message->id, namebuf, typebuf, classbuf,
2910224092Sdougb			origfor, original, client->requesttime);
2911254897Serwin		client = ISC_LIST_NEXT(client, rlink);
2912135446Strhodes	}
2913254897Serwin	UNLOCK(&manager->reclock);
2914135446Strhodes}
2915165071Sdougb
2916165071Sdougbvoid
2917165071Sdougbns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
2918254897Serwin	LOCK(&client->query.fetchlock);
2919165071Sdougb	if (client->query.restarts > 0) {
2920165071Sdougb		/*
2921165071Sdougb		 * client->query.qname was dynamically allocated.
2922165071Sdougb		 */
2923165071Sdougb		dns_message_puttempname(client->message,
2924165071Sdougb					&client->query.qname);
2925165071Sdougb	}
2926165071Sdougb	client->query.qname = name;
2927254897Serwin	UNLOCK(&client->query.fetchlock);
2928165071Sdougb}
2929254897Serwin
2930254897Serwinisc_result_t
2931254897Serwinns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp) {
2932254897Serwin	ns_client_t *client = (ns_client_t *) ci->data;
2933254897Serwin
2934254897Serwin	REQUIRE(NS_CLIENT_VALID(client));
2935254897Serwin	REQUIRE(addrp != NULL);
2936254897Serwin
2937254897Serwin	*addrp = &client->peeraddr;
2938254897Serwin	return (ISC_R_SUCCESS);
2939254897Serwin}
2940