client.c 80.7 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
add  
Bob Halley committed
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
add  
Bob Halley committed
16 17
 */

Automatic Updater's avatar
Automatic Updater committed
18
/* $Id: client.c,v 1.286 2012/01/31 23:47:30 tbox Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Bob Halley's avatar
add  
Bob Halley committed
20 21
#include <config.h>

22
#include <isc/formatcheck.h>
Brian Wellington's avatar
Brian Wellington committed
23
#include <isc/mutex.h>
24
#include <isc/once.h>
25
#include <isc/platform.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
26
#include <isc/print.h>
27
#include <isc/queue.h>
28
#include <isc/stats.h>
29
#include <isc/stdio.h>
30
#include <isc/string.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
31
#include <isc/task.h>
Bob Halley's avatar
add  
Bob Halley committed
32
#include <isc/timer.h>
Michael Graff's avatar
Michael Graff committed
33
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
34

35
#include <dns/db.h>
Bob Halley's avatar
add  
Bob Halley committed
36 37 38
#include <dns/dispatch.h>
#include <dns/events.h>
#include <dns/message.h>
39
#include <dns/peer.h>
40
#include <dns/rcode.h>
Bob Halley's avatar
EDNS0  
Bob Halley committed
41
#include <dns/rdata.h>
42
#include <dns/rdataclass.h>
Bob Halley's avatar
EDNS0  
Bob Halley committed
43 44
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
45
#include <dns/resolver.h>
46
#include <dns/stats.h>
47
#include <dns/tsig.h>
48
#include <dns/view.h>
49
#include <dns/zone.h>
Bob Halley's avatar
add  
Bob Halley committed
50

51
#include <named/interfacemgr.h>
52
#include <named/log.h>
53
#include <named/notify.h>
54
#include <named/os.h>
55
#include <named/server.h>
56
#include <named/update.h>
57 58 59 60 61

/***
 *** Client
 ***/

62 63 64
/*! \file
 * Client Routines
 *
65 66 67 68 69 70 71 72 73
 * Important note!
 *
 * All client state changes, other than that from idle to listening, occur
 * as a result of events.  This guarantees serialization and avoids the
 * need for locking.
 *
 * If a routine is ever created that allows someone other than the client's
 * task to change the client, then the client will have to be locked.
 */
Bob Halley's avatar
add  
Bob Halley committed
74 75 76

#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
77
#define CTRACE(m)	ns_client_log(client, \
78 79
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
80
				      ISC_LOG_DEBUG(3), \
81
				      "%s", (m))
82 83 84
#define MTRACE(m)	isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
85
				      ISC_LOG_DEBUG(3), \
86
				      "clientmgr @%p: %s", manager, (m))
87 88 89
#else
#define CTRACE(m)	((void)(m))
#define MTRACE(m)	((void)(m))
Bob Halley's avatar
add  
Bob Halley committed
90 91
#endif

Bob Halley's avatar
Bob Halley committed
92 93
#define TCP_CLIENT(c)	(((c)->attributes & NS_CLIENTATTR_TCP) != 0)

94
#define TCP_BUFFER_SIZE			(65535 + 2)
95 96
#define SEND_BUFFER_SIZE		4096
#define RECV_BUFFER_SIZE		4096
Bob Halley's avatar
add  
Bob Halley committed
97

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
#ifdef ISC_PLATFORM_USETHREADS
#define NMCTXS				100
/*%<
 * Number of 'mctx pools' for clients. (Should this be configurable?)
 * When enabling threads, we use a pool of memory contexts shared by
 * client objects, since concurrent access to a shared context would cause
 * heavy contentions.  The above constant is expected to be enough for
 * completely avoiding contentions among threads for an authoritative-only
 * server.
 */
#else
#define NMCTXS				0
/*%<
 * If named with built without thread, simply share manager's context.  Using
 * a separate context in this case would simply waste memory.
 */
#endif

116
/*% nameserver client manager structure */
Bob Halley's avatar
add  
Bob Halley committed
117 118 119
struct ns_clientmgr {
	/* Unlocked. */
	unsigned int			magic;
120 121 122 123

	/* The queue object has its own locks */
	client_queue_t			inactive;     /*%< To be recycled */

Bob Halley's avatar
add  
Bob Halley committed
124 125 126
	isc_mem_t *			mctx;
	isc_taskmgr_t *			taskmgr;
	isc_timermgr_t *		timermgr;
127 128

	/* Lock covers manager state. */
Bob Halley's avatar
add  
Bob Halley committed
129 130
	isc_mutex_t			lock;
	isc_boolean_t			exiting;
131 132 133 134 135 136 137 138 139

	/* Lock covers the clients list */
	isc_mutex_t			listlock;
	client_list_t			clients;      /*%< All active clients */

	/* Lock covers the recursing list */
	isc_mutex_t			reclock;
	client_list_t			recursing;    /*%< Recursing clients */

140 141 142 143 144
#if NMCTXS > 0
	/*%< mctx pool for clients. */
	unsigned int			nextmctx;
	isc_mem_t *			mctxpool[NMCTXS];
#endif
Bob Halley's avatar
add  
Bob Halley committed
145 146
};

147 148
#define MANAGER_MAGIC			ISC_MAGIC('N', 'S', 'C', 'm')
#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, MANAGER_MAGIC)
Bob Halley's avatar
add  
Bob Halley committed
149

Automatic Updater's avatar
Automatic Updater committed
150
/*!
151 152 153 154 155 156 157 158 159 160
 * Client object states.  Ordering is significant: higher-numbered
 * states are generally "more active", meaning that the client can
 * have more dynamically allocated data, outstanding events, etc.
 * In the list below, any such properties listed for state N
 * also apply to any state > N.
 *
 * To force the client into a less active state, set client->newstate
 * to that state and call exit_check().  This will cause any
 * activities defined for higher-numbered states to be aborted.
 */
Bob Halley's avatar
add  
Bob Halley committed
161

162
#define NS_CLIENTSTATE_FREED    0
163
/*%<
164 165
 * The client object no longer exists.
 */
Bob Halley's avatar
add  
Bob Halley committed
166

167
#define NS_CLIENTSTATE_INACTIVE 1
168
/*%<
169
 * The client object exists and has a task and timer.
170 171 172 173
 * Its "query" struct and sendbuf are initialized.
 * It is on the client manager's list of inactive clients.
 * It has a message and OPT, both in the reset state.
 */
Bob Halley's avatar
add  
Bob Halley committed
174

175
#define NS_CLIENTSTATE_READY    2
176
/*%<
177 178 179
 * The client object is either a TCP or a UDP one, and
 * it is associated with a network interface.  It is on the
 * client manager's list of active clients.
Bob Halley's avatar
add  
Bob Halley committed
180
 *
181
 * If it is a TCP client object, it has a TCP listener socket
182
 * and an outstanding TCP listen request.
Bob Halley's avatar
add  
Bob Halley committed
183
 *
184 185
 * If it is a UDP client object, it has a UDP listener socket
 * and an outstanding UDP receive request.
Bob Halley's avatar
add  
Bob Halley committed
186 187
 */

188
#define NS_CLIENTSTATE_READING  3
189
/*%<
190
 * The client object is a TCP client object that has received
191
 * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
192 193 194 195 196
 * outstanding TCP read request.  This state is not used for
 * UDP client objects.
 */

#define NS_CLIENTSTATE_WORKING  4
197
/*%<
198
 * The client object has received a request and is working
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
199
 * on it.  It has a view, and it may have any of a non-reset OPT,
200
 * recursion quota, and an outstanding write request.
201
 */
202

203 204 205 206 207 208
#define NS_CLIENTSTATE_RECURSING  5
/*%<
 * The client object is recursing.  It will be on the 'recursing'
 * list.
 */

209
#define NS_CLIENTSTATE_MAX      9
210
/*%<
211 212 213 214 215
 * Sentinel value used to indicate "no state".  When client->newstate
 * has this value, we are not attempting to exit the current state.
 * Must be greater than any valid state.
 */

216 217 218 219 220 221 222
/*
 * Enable ns_client_dropport() by default.
 */
#ifndef NS_CLIENT_DROPPORT
#define NS_CLIENT_DROPPORT 1
#endif

223
unsigned int ns_client_requests;
224 225 226

static void client_read(ns_client_t *client);
static void client_accept(ns_client_t *client);
227
static void client_udprecv(ns_client_t *client);
228 229 230
static void clientmgr_destroy(ns_clientmgr_t *manager);
static isc_boolean_t exit_check(ns_client_t *client);
static void ns_client_endrequest(ns_client_t *client);
231 232
static void client_start(isc_task_t *task, isc_event_t *event);
static void client_request(isc_task_t *task, isc_event_t *event);
233
static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
234 235
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
			       dns_dispatch_t *disp, isc_boolean_t tcp);
236

237
void
238
ns_client_recursing(ns_client_t *client) {
239
	REQUIRE(NS_CLIENT_VALID(client));
240
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
241

242
	LOCK(&client->manager->reclock);
243
	client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
244
	ISC_LIST_APPEND(client->manager->recursing, client, rlink);
245
	UNLOCK(&client->manager->reclock);
246 247
}

248 249 250 251 252
void
ns_client_killoldestquery(ns_client_t *client) {
	ns_client_t *oldest;
	REQUIRE(NS_CLIENT_VALID(client));

253
	LOCK(&client->manager->reclock);
254 255
	oldest = ISC_LIST_HEAD(client->manager->recursing);
	if (oldest != NULL) {
256 257
		ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
		UNLOCK(&client->manager->reclock);
258
		ns_query_cancel(oldest);
259 260
	} else
		UNLOCK(&client->manager->reclock);
261 262
}

263 264
void
ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
265 266
	isc_result_t result;
	isc_interval_t interval;
267

268 269 270
	isc_interval_set(&interval, seconds, 0);
	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
				 &interval, ISC_FALSE);
271
	client->timerset = ISC_TRUE;
272
	if (result != ISC_R_SUCCESS) {
273
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
274
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
275
			      "setting timeout: %s",
276 277 278 279 280
			      isc_result_totext(result));
		/* Continue anyway. */
	}
}

281
/*%
282 283 284 285 286 287 288
 * Check for a deactivation or shutdown request and take appropriate
 * action.  Returns ISC_TRUE if either is in progress; in this case
 * the caller must no longer use the client object as it may have been
 * freed.
 */
static isc_boolean_t
exit_check(ns_client_t *client) {
289 290
	isc_boolean_t destroy_manager = ISC_FALSE;
	ns_clientmgr_t *manager = NULL;
291

292
	REQUIRE(NS_CLIENT_VALID(client));
293
	manager = client->manager;
294 295 296 297

	if (client->state <= client->newstate)
		return (ISC_FALSE); /* Business as usual. */

298
	INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
299

300 301 302 303 304 305
	/*
	 * We need to detach from the view early when shutting down
	 * the server to break the following vicious circle:
	 *
	 *  - The resolver will not shut down until the view refcount is zero
	 *  - The view refcount does not go to zero until all clients detach
306 307
	 *  - The client does not detach from the view until references is zero
	 *  - references does not go to zero until the resolver has shut down
308
	 *
309
	 * Keep the view attached until any outstanding updates complete.
310
	 */
Automatic Updater's avatar
Automatic Updater committed
311
	if (client->nupdates == 0 &&
312
	    client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
313
		dns_view_detach(&client->view);
314

315 316 317
	if (client->state == NS_CLIENTSTATE_WORKING ||
	    client->state == NS_CLIENTSTATE_RECURSING)
	{
318
		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
319 320 321 322 323
		/*
		 * Let the update processing complete.
		 */
		if (client->nupdates > 0)
			return (ISC_TRUE);
324

325 326 327 328 329 330 331 332
		/*
		 * We are trying to abort request processing.
		 */
		if (client->nsends > 0) {
			isc_socket_t *socket;
			if (TCP_CLIENT(client))
				socket = client->tcpsocket;
			else
333
				socket = client->udpsocket;
334 335 336
			isc_socket_cancel(socket, client->task,
					  ISC_SOCKCANCEL_SEND);
		}
337

338 339 340
		if (! (client->nsends == 0 && client->nrecvs == 0 &&
		       client->references == 0))
		{
341 342 343 344 345 346
			/*
			 * Still waiting for I/O cancel completion.
			 * or lingering references.
			 */
			return (ISC_TRUE);
		}
347

348 349
		/*
		 * I/O cancel is complete.  Burn down all state
350
		 * related to the current request.  Ensure that
351
		 * the client is no longer on the recursing list.
352 353 354 355
		 *
		 * We need to check whether the client is still linked,
		 * because it may already have been removed from the
		 * recursing list by ns_client_killoldestquery()
356
		 */
357 358
		if (client->state == NS_CLIENTSTATE_RECURSING) {
			LOCK(&manager->reclock);
359 360 361
			if (ISC_LINK_LINKED(client, rlink))
				ISC_LIST_UNLINK(manager->recursing,
						client, rlink);
362
			UNLOCK(&manager->reclock);
363
		}
364 365 366
		ns_client_endrequest(client);

		client->state = NS_CLIENTSTATE_READING;
367
		INSIST(client->recursionquota == NULL);
368

369 370 371 372 373 374 375 376 377 378 379 380
		if (NS_CLIENTSTATE_READING == client->newstate) {
			client_read(client);
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE); /* We're done. */
		}
	}

	if (client->state == NS_CLIENTSTATE_READING) {
		/*
		 * We are trying to abort the current TCP connection,
		 * if any.
		 */
381
		INSIST(client->recursionquota == NULL);
382 383 384 385 386 387 388 389 390 391 392 393
		INSIST(client->newstate <= NS_CLIENTSTATE_READY);
		if (client->nreads > 0)
			dns_tcpmsg_cancelread(&client->tcpmsg);
		if (! client->nreads == 0) {
			/* Still waiting for read cancel completion. */
			return (ISC_TRUE);
		}

		if (client->tcpmsg_valid) {
			dns_tcpmsg_invalidate(&client->tcpmsg);
			client->tcpmsg_valid = ISC_FALSE;
		}
394 395
		if (client->tcpsocket != NULL) {
			CTRACE("closetcp");
396
			isc_socket_detach(&client->tcpsocket);
397
		}
398 399 400

		if (client->tcpquota != NULL)
			isc_quota_detach(&client->tcpquota);
401

402
		if (client->timerset) {
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
403 404 405
			(void)isc_timer_reset(client->timer,
					      isc_timertype_inactive,
					      NULL, NULL, ISC_TRUE);
406 407
			client->timerset = ISC_FALSE;
		}
408

409 410
		client->peeraddr_valid = ISC_FALSE;

411
		client->state = NS_CLIENTSTATE_READY;
412
		INSIST(client->recursionquota == NULL);
413 414 415 416 417 418

		/*
		 * Now the client is ready to accept a new TCP connection
		 * or UDP request, but we may have enough clients doing
		 * that already.  Check whether this client needs to remain
		 * active and force it to go inactive if not.
419 420 421 422
		 *
		 * UDP clients go inactive at this point, but TCP clients
		 * may remain active if we have fewer active TCP client
		 * objects than desired due to an earlier quota exhaustion.
423
		 */
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
		if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
			LOCK(&client->interface->lock);
			if (client->interface->ntcpcurrent <
				    client->interface->ntcptarget)
				client->mortal = ISC_FALSE;
			UNLOCK(&client->interface->lock);
		}

		/*
		 * We don't need the client; send it to the inactive
		 * queue for recycling.
		 */
		if (client->mortal) {
			if (client->newstate > NS_CLIENTSTATE_INACTIVE)
				client->newstate = NS_CLIENTSTATE_INACTIVE;
		}
440

441 442 443
		if (NS_CLIENTSTATE_READY == client->newstate) {
			if (TCP_CLIENT(client)) {
				client_accept(client);
444 445
			} else
				client_udprecv(client);
446 447 448 449 450 451
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE);
		}
	}

	if (client->state == NS_CLIENTSTATE_READY) {
452
		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
453

454 455 456 457 458 459
		/*
		 * We are trying to enter the inactive state.
		 */
		if (client->naccepts > 0)
			isc_socket_cancel(client->tcplistener, client->task,
					  ISC_SOCKCANCEL_ACCEPT);
460

461 462
		/* Still waiting for accept cancel completion. */
		if (! (client->naccepts == 0))
463
			return (ISC_TRUE);
464

465
		/* Accept cancel is complete. */
466 467 468
		if (client->nrecvs > 0)
			isc_socket_cancel(client->udpsocket, client->task,
					  ISC_SOCKCANCEL_RECV);
469 470 471

		/* Still waiting for recv cancel completion. */
		if (! (client->nrecvs == 0))
472 473
			return (ISC_TRUE);

474 475
		/* Still waiting for control event to be delivered */
		if (client->nctls > 0)
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
			return (ISC_TRUE);

		/* Deactivate the client. */
		if (client->interface)
			ns_interface_detach(&client->interface);

		INSIST(client->naccepts == 0);
		INSIST(client->recursionquota == NULL);
		if (client->tcplistener != NULL)
			isc_socket_detach(&client->tcplistener);

		if (client->udpsocket != NULL)
			isc_socket_detach(&client->udpsocket);

		if (client->dispatch != NULL)
			dns_dispatch_detach(&client->dispatch);

		client->attributes = 0;
		client->mortal = ISC_FALSE;

		/*
		 * Put the client on the inactive list.  If we are aiming for
		 * the "freed" state, it will be removed from the inactive
		 * list shortly, and we need to keep the manager locked until
		 * that has been done, lest the manager decide to reactivate
		 * the dying client inbetween.
		 */
503
		client->state = NS_CLIENTSTATE_INACTIVE;
504
		INSIST(client->recursionquota == NULL);
505

506 507
		if (client->state == client->newstate) {
			client->newstate = NS_CLIENTSTATE_MAX;
508 509 510 511
			if (!ns_g_clienttest && manager != NULL &&
			    !manager->exiting)
				ISC_QUEUE_PUSH(manager->inactive, client,
					       ilink);
512 513
			if (client->needshutdown)
				isc_task_shutdown(client->task);
514
			return (ISC_TRUE);
515 516 517 518 519 520 521
		}
	}

	if (client->state == NS_CLIENTSTATE_INACTIVE) {
		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
		/*
		 * We are trying to free the client.
522 523 524 525 526
		 *
		 * When "shuttingdown" is true, either the task has received
		 * its shutdown event or no shutdown event has ever been
		 * set up.  Thus, we have no outstanding shutdown
		 * event at this point.
527
		 */
528 529 530
		REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);

		INSIST(client->recursionquota == NULL);
531
		INSIST(!ISC_QLINK_LINKED(client, ilink));
532 533 534 535 536 537

		ns_query_free(client);
		isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
		isc_event_free((isc_event_t **)&client->sendevent);
		isc_event_free((isc_event_t **)&client->recvevent);
		isc_timer_detach(&client->timer);
538 539
		if (client->delaytimer != NULL)
			isc_timer_detach(&client->delaytimer);
540 541

		if (client->tcpbuf != NULL)
542 543
			isc_mem_put(client->mctx, client->tcpbuf,
				    TCP_BUFFER_SIZE);
544 545 546
		if (client->opt != NULL) {
			INSIST(dns_rdataset_isassociated(client->opt));
			dns_rdataset_disassociate(client->opt);
547 548
			dns_message_puttemprdataset(client->message,
						    &client->opt);
549
		}
550

551
		dns_message_destroy(&client->message);
552 553 554 555
		if (manager != NULL) {
			LOCK(&manager->listlock);
			ISC_LIST_UNLINK(manager->clients, client, link);
			LOCK(&manager->lock);
556
			if (manager->exiting &&
557 558 559 560
			    ISC_LIST_EMPTY(manager->clients))
				destroy_manager = ISC_TRUE;
			UNLOCK(&manager->lock);
			UNLOCK(&manager->listlock);
561
		}
562

563 564 565 566 567 568 569 570 571 572
		/*
		 * Detaching the task must be done after unlinking from
		 * the manager's lists because the manager accesses
		 * client->task.
		 */
		if (client->task != NULL)
			isc_task_detach(&client->task);

		CTRACE("free");
		client->magic = 0;
573

574 575 576 577 578 579 580 581
		/*
		 * Check that there are no other external references to
		 * the memory context.
		 */
		if (ns_g_clienttest && isc_mem_references(client->mctx) != 1) {
			isc_mem_stats(client->mctx, stderr);
			INSIST(0);
		}
582
		isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
583 584
	}

585 586
	if (destroy_manager && manager != NULL)
		clientmgr_destroy(manager);
587

588 589 590
	return (ISC_TRUE);
}

591
/*%
592 593 594 595 596 597 598 599
 * The client's task has received the client's control event
 * as part of the startup process.
 */
static void
client_start(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client = (ns_client_t *) event->ev_arg;

	INSIST(task == client->task);
600

601 602
	UNUSED(task);

603 604 605 606 607 608
	INSIST(client->nctls == 1);
	client->nctls--;

	if (exit_check(client))
		return;

609 610 611
	if (TCP_CLIENT(client)) {
		client_accept(client);
	} else {
612
		client_udprecv(client);
613 614 615 616
	}
}


617
/*%
618 619
 * The client's task has received a shutdown event.
 */
Bob Halley's avatar
add  
Bob Halley committed
620 621 622 623 624
static void
client_shutdown(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
625 626
	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
	client = event->ev_arg;
Bob Halley's avatar
add  
Bob Halley committed
627 628 629
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
630 631
	UNUSED(task);

Bob Halley's avatar
add  
Bob Halley committed
632 633 634 635
	CTRACE("shutdown");

	isc_event_free(&event);

636 637 638 639 640 641
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}

642 643 644
	if (ISC_QLINK_LINKED(client, ilink))
		ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);

645
	client->newstate = NS_CLIENTSTATE_FREED;
646
	client->needshutdown = ISC_FALSE;
647
	(void)exit_check(client);
648
}
Bob Halley's avatar
add  
Bob Halley committed
649

650 651
static void
ns_client_endrequest(ns_client_t *client) {
652 653 654
	INSIST(client->naccepts == 0);
	INSIST(client->nreads == 0);
	INSIST(client->nsends == 0);
655
	INSIST(client->nrecvs == 0);
656
	INSIST(client->nupdates == 0);
657 658
	INSIST(client->state == NS_CLIENTSTATE_WORKING ||
	       client->state == NS_CLIENTSTATE_RECURSING);
Bob Halley's avatar
add  
Bob Halley committed
659

Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
660 661
	CTRACE("endrequest");

662
	if (client->next != NULL) {
663
		(client->next)(client);
664 665 666
		client->next = NULL;
	}

667 668
	if (client->view != NULL)
		dns_view_detach(&client->view);
Bob Halley's avatar
Bob Halley committed
669 670 671 672 673
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
674

675
	client->signer = NULL;
676
	client->udpsize = 512;
677
	client->extflags = 0;
678
	client->ednsversion = -1;
Bob Halley's avatar
add  
Bob Halley committed
679
	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
680

681
	if (client->recursionquota != NULL) {
682
		isc_quota_detach(&client->recursionquota);
683 684 685
		isc_stats_decrement(ns_g_server->nsstats,
				    dns_nsstatscounter_recursclients);
	}
686 687

	/*
688
	 * Clear all client attributes that are specific to
689 690 691
	 * the request; that's all except the TCP flag.
	 */
	client->attributes &= NS_CLIENTATTR_TCP;
692
}
693

694 695 696
void
ns_client_next(ns_client_t *client, isc_result_t result) {
	int newstate;
697

698
	REQUIRE(NS_CLIENT_VALID(client));
699
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
700
		client->state == NS_CLIENTSTATE_RECURSING ||
701
		client->state == NS_CLIENTSTATE_READING);
Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
702

703
	CTRACE("next");
704

705
	if (result != ISC_R_SUCCESS)
706
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
			      "request failed: %s", isc_result_totext(result));

	/*
	 * An error processing a TCP request may have left
	 * the connection out of sync.  To be safe, we always
	 * sever the connection when result != ISC_R_SUCCESS.
	 */
	if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
		newstate = NS_CLIENTSTATE_READING;
	else
		newstate = NS_CLIENTSTATE_READY;

	if (client->newstate > newstate)
		client->newstate = newstate;
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
722
	(void)exit_check(client);
Bob Halley's avatar
add  
Bob Halley committed
723 724
}

725

Bob Halley's avatar
add  
Bob Halley committed
726 727 728
static void
client_senddone(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;
729
	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
Bob Halley's avatar
add  
Bob Halley committed
730 731

	REQUIRE(sevent != NULL);
732 733
	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
	client = sevent->ev_arg;
Bob Halley's avatar
add  
Bob Halley committed
734 735
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);
736
	REQUIRE(sevent == client->sendevent);
Bob Halley's avatar
add  
Bob Halley committed
737

738 739
	UNUSED(task);

Bob Halley's avatar
add  
Bob Halley committed
740 741
	CTRACE("senddone");

742 743 744 745 746 747
	if (sevent->result != ISC_R_SUCCESS)
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
			      NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
			      "error sending response: %s",
			      isc_result_totext(sevent->result));

Bob Halley's avatar
Bob Halley committed
748
	INSIST(client->nsends > 0);
Bob Halley's avatar
add  
Bob Halley committed
749
	client->nsends--;
750

751
	if (client->tcpbuf != NULL) {
752
		INSIST(TCP_CLIENT(client));
753 754 755
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add  
Bob Halley committed
756

757
	ns_client_next(client, ISC_R_SUCCESS);
Bob Halley's avatar
add  
Bob Halley committed
758 759
}

760
/*%
761 762 763 764 765 766 767
 * We only want to fail with ISC_R_NOSPACE when called from
 * ns_client_sendraw() and not when called from ns_client_send(),
 * tcpbuffer is NULL when called from ns_client_sendraw() and
 * length != 0.  tcpbuffer != NULL when called from ns_client_send()
 * and length == 0.
 */

768 769 770
static isc_result_t
client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
		    isc_buffer_t *tcpbuffer, isc_uint32_t length,
771
		    unsigned char *sendbuf, unsigned char **datap)
772
{
773
	unsigned char *data;
774 775
	isc_uint32_t bufsize;
	isc_result_t result;
776

777
	INSIST(datap != NULL);
778 779
	INSIST((tcpbuffer == NULL && length != 0) ||
	       (tcpbuffer != NULL && length == 0));
780 781 782

	if (TCP_CLIENT(client)) {
		INSIST(client->tcpbuf == NULL);
783
		if (length + 2 > TCP_BUFFER_SIZE) {
784 785 786 787 788 789 790 791 792
			result = ISC_R_NOSPACE;
			goto done;
		}
		client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
		if (client->tcpbuf == NULL) {
			result = ISC_R_NOMEMORY;
			goto done;
		}
		data = client->tcpbuf;
793 794 795 796 797
		if (tcpbuffer != NULL) {
			isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
			isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
		} else {
			isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
Brian Wellington's avatar
Brian Wellington committed
798 799
			INSIST(length <= 0xffff);
			isc_buffer_putuint16(buffer, (isc_uint16_t)length);
800
		}
801
	} else {
802
		data = sendbuf;
803 804 805 806
		if (client->udpsize < SEND_BUFFER_SIZE)
			bufsize = client->udpsize;
		else
			bufsize = SEND_BUFFER_SIZE;
807
		if (length > bufsize) {
808 809 810
			result = ISC_R_NOSPACE;
			goto done;
		}
811
		isc_buffer_init(buffer, data, bufsize);
812
	}
813 814
	*datap = data;
	result = ISC_R_SUCCESS;
815

816 817 818 819 820 821 822 823 824 825 826
 done:
	return (result);
}

static isc_result_t
client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
	struct in6_pktinfo *pktinfo;
	isc_result_t result;
	isc_region_t r;
	isc_sockaddr_t *address;
	isc_socket_t *socket;
827 828
	isc_netaddr_t netaddr;
	int match;
829
	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
Evan Hunt's avatar
Evan Hunt committed
830
	isc_dscp_t dispdscp = -1;
831 832 833 834 835

	if (TCP_CLIENT(client)) {
		socket = client->tcpsocket;
		address = NULL;
	} else {
836 837
		socket = client->udpsocket;
		address = &client->peeraddr;
838 839 840 841

		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
		if (ns_g_server->blackholeacl != NULL &&
		    dns_acl_match(&netaddr, NULL,
842
				  ns_g_server->blackholeacl,
843 844
				  &ns_g_server->aclenv,
				  &match, NULL) == ISC_R_SUCCESS &&
845 846
		    match > 0)
			return (DNS_R_BLACKHOLED);
847
		sockflags |= ISC_SOCKFLAG_NORETRY;
848
	}
849

850
	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
851
	    (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
852 853 854
		pktinfo = &client->pktinfo;
	else
		pktinfo = NULL;
855

Evan Hunt's avatar
Evan Hunt committed
856 857 858 859 860 861 862 863 864 865 866 867 868 869
	if (client->dispatch != NULL) {
		dispdscp = dns_dispatch_getdscp(client->dispatch);
		if (dispdscp != -1)
			client->dscp = dispdscp;
	}

	if (client->dscp == -1) {
		client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
		client->sendevent->dscp = 0;
	} else {
		client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
		client->sendevent->dscp = client->dscp;
	}

870 871 872
	isc_buffer_usedregion(buffer, &r);

	CTRACE("sendto");
Automatic Updater's avatar
Automatic Updater committed
873

874 875
	result = isc_socket_sendto2(socket, &r, client->task,
				    address, pktinfo,
876
				    client->sendevent, sockflags);
877
	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
878
		client->nsends++;
879 880 881
		if (result == ISC_R_SUCCESS)
			client_senddone(client->task,
					(isc_event_t *)client->sendevent);
882
		result = ISC_R_SUCCESS;
883
	}
884 885 886 887 888 889 890 891 892 893
	return (result);
}

void
ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
	isc_result_t result;
	unsigned char *data;
	isc_buffer_t buffer;
	isc_region_t r;
	isc_region_t *mr;
894
	unsigned char sendbuf[SEND_BUFFER_SIZE];
895 896 897 898 899 900 901 902 903 904 905

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("sendraw");

	mr = dns_message_getrawmessage(message);
	if (mr == NULL) {
		result = ISC_R_UNEXPECTEDEND;
		goto done;
	}

906 907
	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
				     sendbuf, &data);
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
	if (result != ISC_R_SUCCESS)
		goto done;

	/*
	 * Copy message to buffer and fixup id.
	 */
	isc_buffer_availableregion(&buffer, &r);
	result = isc_buffer_copyregion(&buffer, mr);
	if (result != ISC_R_SUCCESS)
		goto done;
	r.base[0] = (client->message->id >> 8) & 0xff;
	r.base[1] = client->message->id & 0xff;

	result = client_sendpkg(client, &buffer);
	if (result == ISC_R_SUCCESS)
		return;
924 925 926 927 928 929 930 931 932

 done:
	if (client->tcpbuf != NULL) {
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
	ns_client_next(client, result);
}

933 934
static void
client_send(ns_client_t *client) {
Bob Halley's avatar
add  
Bob Halley committed
935 936 937
	isc_result_t result;
	unsigned char *data;
	isc_buffer_t buffer;
Bob Halley's avatar
Bob Halley committed
938
	isc_buffer_t tcpbuffer;
Bob Halley's avatar
add  
Bob Halley committed
939
	isc_region_t r;
940 941
	dns_compress_t cctx;
	isc_boolean_t cleanup_cctx = ISC_FALSE;
942
	unsigned char sendbuf[SEND_BUFFER_SIZE];