client.c 87.5 KB
Newer Older
Bob Halley's avatar
add    
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2014  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
29
#include <isc/random.h>
#include <isc/serial.h>
30
#include <isc/stats.h>
31
#include <isc/stdio.h>
32
#include <isc/string.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
33
#include <isc/task.h>
Bob Halley's avatar
add    
Bob Halley committed
34
#include <isc/timer.h>
Michael Graff's avatar
Michael Graff committed
35
#include <isc/util.h>
Bob Halley's avatar
add    
Bob Halley committed
36

37
38
39
40
41
42
#ifdef AES_SIT
#include <isc/aes.h>
#else
#include <isc/hmacsha.h>
#endif

43
#include <dns/db.h>
Bob Halley's avatar
add    
Bob Halley committed
44
45
46
#include <dns/dispatch.h>
#include <dns/events.h>
#include <dns/message.h>
47
#include <dns/peer.h>
48
#include <dns/rcode.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
49
#include <dns/rdata.h>
50
#include <dns/rdataclass.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
51
52
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
53
#include <dns/resolver.h>
54
#include <dns/stats.h>
55
#include <dns/tsig.h>
56
#include <dns/view.h>
57
#include <dns/zone.h>
Bob Halley's avatar
add    
Bob Halley committed
58

59
#include <named/interfacemgr.h>
60
#include <named/log.h>
61
#include <named/notify.h>
62
#include <named/os.h>
63
#include <named/server.h>
64
#include <named/update.h>
65
66
67
68
69

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

70
71
72
/*! \file
 * Client Routines
 *
73
74
75
76
77
78
79
80
81
 * 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
82
83
84

#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
85
#define CTRACE(m)	ns_client_log(client, \
86
87
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
88
				      ISC_LOG_DEBUG(3), \
89
				      "%s", (m))
90
91
92
#define MTRACE(m)	isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
93
				      ISC_LOG_DEBUG(3), \
94
				      "clientmgr @%p: %s", manager, (m))
95
96
97
#else
#define CTRACE(m)	((void)(m))
#define MTRACE(m)	((void)(m))
Bob Halley's avatar
add    
Bob Halley committed
98
99
#endif

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

102
#define TCP_BUFFER_SIZE			(65535 + 2)
103
104
#define SEND_BUFFER_SIZE		4096
#define RECV_BUFFER_SIZE		4096
Bob Halley's avatar
add    
Bob Halley committed
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#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

124
125
#define SIT_SIZE 24 /* 8 + 4 + 4 + 8 */

126
/*% nameserver client manager structure */
Bob Halley's avatar
add    
Bob Halley committed
127
128
129
struct ns_clientmgr {
	/* Unlocked. */
	unsigned int			magic;
130
131
132
133

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

Bob Halley's avatar
add    
Bob Halley committed
134
135
136
	isc_mem_t *			mctx;
	isc_taskmgr_t *			taskmgr;
	isc_timermgr_t *		timermgr;
137
138

	/* Lock covers manager state. */
Bob Halley's avatar
add    
Bob Halley committed
139
140
	isc_mutex_t			lock;
	isc_boolean_t			exiting;
141
142
143
144
145
146
147
148
149

	/* 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 */

150
151
152
153
154
#if NMCTXS > 0
	/*%< mctx pool for clients. */
	unsigned int			nextmctx;
	isc_mem_t *			mctxpool[NMCTXS];
#endif
Bob Halley's avatar
add    
Bob Halley committed
155
156
};

157
158
#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
159

Automatic Updater's avatar
Automatic Updater committed
160
/*!
161
162
163
164
165
166
167
168
169
170
 * 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
171

172
#define NS_CLIENTSTATE_FREED    0
173
/*%<
174
175
 * The client object no longer exists.
 */
Bob Halley's avatar
add    
Bob Halley committed
176

177
#define NS_CLIENTSTATE_INACTIVE 1
178
/*%<
179
 * The client object exists and has a task and timer.
180
181
182
183
 * 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
184

185
#define NS_CLIENTSTATE_READY    2
186
/*%<
187
188
189
 * 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
190
 *
191
 * If it is a TCP client object, it has a TCP listener socket
192
 * and an outstanding TCP listen request.
Bob Halley's avatar
add    
Bob Halley committed
193
 *
194
195
 * 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
196
197
 */

198
#define NS_CLIENTSTATE_READING  3
199
/*%<
200
 * The client object is a TCP client object that has received
201
 * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
202
203
204
205
206
 * outstanding TCP read request.  This state is not used for
 * UDP client objects.
 */

#define NS_CLIENTSTATE_WORKING  4
207
/*%<
208
 * The client object has received a request and is working
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
209
 * on it.  It has a view, and it may have any of a non-reset OPT,
210
 * recursion quota, and an outstanding write request.
211
 */
212

213
214
215
216
217
218
#define NS_CLIENTSTATE_RECURSING  5
/*%<
 * The client object is recursing.  It will be on the 'recursing'
 * list.
 */

219
#define NS_CLIENTSTATE_MAX      9
220
/*%<
221
222
223
224
225
 * 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.
 */

226
227
228
229
230
231
232
/*
 * Enable ns_client_dropport() by default.
 */
#ifndef NS_CLIENT_DROPPORT
#define NS_CLIENT_DROPPORT 1
#endif

233
unsigned int ns_client_requests;
234
235
236

static void client_read(ns_client_t *client);
static void client_accept(ns_client_t *client);
237
static void client_udprecv(ns_client_t *client);
238
239
240
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);
241
242
static void client_start(isc_task_t *task, isc_event_t *event);
static void client_request(isc_task_t *task, isc_event_t *event);
243
static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
244
245
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
			       dns_dispatch_t *disp, isc_boolean_t tcp);
Evan Hunt's avatar
Evan Hunt committed
246
247
static inline isc_boolean_t
allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
248
249
250
251
#ifdef ISC_PLATFORM_USESIT
static void compute_sit(ns_client_t *client, isc_uint32_t when,
			isc_uint32_t nonce, isc_buffer_t *buf);
#endif
252

253
void
254
ns_client_recursing(ns_client_t *client) {
255
	REQUIRE(NS_CLIENT_VALID(client));
256
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
257

258
	LOCK(&client->manager->reclock);
259
	client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
260
	ISC_LIST_APPEND(client->manager->recursing, client, rlink);
261
	UNLOCK(&client->manager->reclock);
262
263
}

264
265
266
267
268
void
ns_client_killoldestquery(ns_client_t *client) {
	ns_client_t *oldest;
	REQUIRE(NS_CLIENT_VALID(client));

269
	LOCK(&client->manager->reclock);
270
271
	oldest = ISC_LIST_HEAD(client->manager->recursing);
	if (oldest != NULL) {
272
273
		ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
		UNLOCK(&client->manager->reclock);
274
		ns_query_cancel(oldest);
275
276
	} else
		UNLOCK(&client->manager->reclock);
277
278
}

279
280
void
ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
281
282
	isc_result_t result;
	isc_interval_t interval;
283

284
285
286
	isc_interval_set(&interval, seconds, 0);
	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
				 &interval, ISC_FALSE);
287
	client->timerset = ISC_TRUE;
288
	if (result != ISC_R_SUCCESS) {
289
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
290
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
291
			      "setting timeout: %s",
292
293
294
295
296
			      isc_result_totext(result));
		/* Continue anyway. */
	}
}

297
/*%
298
299
300
301
302
303
304
 * 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) {
305
306
	isc_boolean_t destroy_manager = ISC_FALSE;
	ns_clientmgr_t *manager = NULL;
307

308
	REQUIRE(NS_CLIENT_VALID(client));
309
	manager = client->manager;
310
311
312
313

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

314
	INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
315

316
317
318
319
320
321
	/*
	 * 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
322
323
	 *  - The client does not detach from the view until references is zero
	 *  - references does not go to zero until the resolver has shut down
324
	 *
325
	 * Keep the view attached until any outstanding updates complete.
326
	 */
Automatic Updater's avatar
Automatic Updater committed
327
	if (client->nupdates == 0 &&
328
	    client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
329
		dns_view_detach(&client->view);
330

331
332
333
	if (client->state == NS_CLIENTSTATE_WORKING ||
	    client->state == NS_CLIENTSTATE_RECURSING)
	{
334
		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
335
336
337
338
339
		/*
		 * Let the update processing complete.
		 */
		if (client->nupdates > 0)
			return (ISC_TRUE);
340

341
342
343
344
345
346
347
348
		/*
		 * We are trying to abort request processing.
		 */
		if (client->nsends > 0) {
			isc_socket_t *socket;
			if (TCP_CLIENT(client))
				socket = client->tcpsocket;
			else
349
				socket = client->udpsocket;
350
351
352
			isc_socket_cancel(socket, client->task,
					  ISC_SOCKCANCEL_SEND);
		}
353

354
355
356
		if (! (client->nsends == 0 && client->nrecvs == 0 &&
		       client->references == 0))
		{
357
358
359
360
361
362
			/*
			 * Still waiting for I/O cancel completion.
			 * or lingering references.
			 */
			return (ISC_TRUE);
		}
363

364
365
		/*
		 * I/O cancel is complete.  Burn down all state
366
		 * related to the current request.  Ensure that
367
		 * the client is no longer on the recursing list.
368
369
370
371
		 *
		 * 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()
372
		 */
373
374
		if (client->state == NS_CLIENTSTATE_RECURSING) {
			LOCK(&manager->reclock);
375
376
377
			if (ISC_LINK_LINKED(client, rlink))
				ISC_LIST_UNLINK(manager->recursing,
						client, rlink);
378
			UNLOCK(&manager->reclock);
379
		}
380
381
382
		ns_client_endrequest(client);

		client->state = NS_CLIENTSTATE_READING;
383
		INSIST(client->recursionquota == NULL);
384

385
386
387
388
389
390
391
392
393
394
395
396
		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.
		 */
397
		INSIST(client->recursionquota == NULL);
398
399
400
401
402
403
404
405
406
407
408
409
		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;
		}
410
411
		if (client->tcpsocket != NULL) {
			CTRACE("closetcp");
412
			isc_socket_detach(&client->tcpsocket);
413
		}
414
415
416

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

418
		if (client->timerset) {
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
419
420
421
			(void)isc_timer_reset(client->timer,
					      isc_timertype_inactive,
					      NULL, NULL, ISC_TRUE);
422
423
			client->timerset = ISC_FALSE;
		}
424

425
426
		client->peeraddr_valid = ISC_FALSE;

427
		client->state = NS_CLIENTSTATE_READY;
428
		INSIST(client->recursionquota == NULL);
429
430
431
432
433
434

		/*
		 * 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.
435
436
437
438
		 *
		 * 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.
439
		 */
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
		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;
		}
456

457
458
459
		if (NS_CLIENTSTATE_READY == client->newstate) {
			if (TCP_CLIENT(client)) {
				client_accept(client);
460
461
			} else
				client_udprecv(client);
462
463
464
465
466
467
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE);
		}
	}

	if (client->state == NS_CLIENTSTATE_READY) {
468
		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
469

470
471
472
473
474
475
		/*
		 * We are trying to enter the inactive state.
		 */
		if (client->naccepts > 0)
			isc_socket_cancel(client->tcplistener, client->task,
					  ISC_SOCKCANCEL_ACCEPT);
476

477
478
		/* Still waiting for accept cancel completion. */
		if (! (client->naccepts == 0))
479
			return (ISC_TRUE);
480

481
		/* Accept cancel is complete. */
482
483
484
		if (client->nrecvs > 0)
			isc_socket_cancel(client->udpsocket, client->task,
					  ISC_SOCKCANCEL_RECV);
485
486
487

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

490
491
		/* Still waiting for control event to be delivered */
		if (client->nctls > 0)
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
			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.
		 */
519
		client->state = NS_CLIENTSTATE_INACTIVE;
520
		INSIST(client->recursionquota == NULL);
521

522
523
		if (client->state == client->newstate) {
			client->newstate = NS_CLIENTSTATE_MAX;
524
525
526
527
			if (!ns_g_clienttest && manager != NULL &&
			    !manager->exiting)
				ISC_QUEUE_PUSH(manager->inactive, client,
					       ilink);
528
529
			if (client->needshutdown)
				isc_task_shutdown(client->task);
530
			return (ISC_TRUE);
531
532
533
534
535
536
537
		}
	}

	if (client->state == NS_CLIENTSTATE_INACTIVE) {
		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
		/*
		 * We are trying to free the client.
538
539
540
541
542
		 *
		 * 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.
543
		 */
544
545
546
		REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);

		INSIST(client->recursionquota == NULL);
547
		INSIST(!ISC_QLINK_LINKED(client, ilink));
548
549
550
551
552
553

		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);
554
555
		if (client->delaytimer != NULL)
			isc_timer_detach(&client->delaytimer);
556
557

		if (client->tcpbuf != NULL)
558
559
			isc_mem_put(client->mctx, client->tcpbuf,
				    TCP_BUFFER_SIZE);
560
561
562
		if (client->opt != NULL) {
			INSIST(dns_rdataset_isassociated(client->opt));
			dns_rdataset_disassociate(client->opt);
563
564
			dns_message_puttemprdataset(client->message,
						    &client->opt);
565
		}
566

567
		dns_message_destroy(&client->message);
568
569
570
571
		if (manager != NULL) {
			LOCK(&manager->listlock);
			ISC_LIST_UNLINK(manager->clients, client, link);
			LOCK(&manager->lock);
572
			if (manager->exiting &&
573
574
575
576
			    ISC_LIST_EMPTY(manager->clients))
				destroy_manager = ISC_TRUE;
			UNLOCK(&manager->lock);
			UNLOCK(&manager->listlock);
577
		}
578

579
580
581
582
583
584
585
586
587
588
		/*
		 * 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;
589

590
591
592
593
594
595
596
597
		/*
		 * 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);
		}
598
		isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
599
600
	}

601
602
	if (destroy_manager && manager != NULL)
		clientmgr_destroy(manager);
603

604
605
606
	return (ISC_TRUE);
}

607
/*%
608
609
610
611
612
613
614
615
 * 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);
616

617
618
	UNUSED(task);

619
620
621
622
623
624
	INSIST(client->nctls == 1);
	client->nctls--;

	if (exit_check(client))
		return;

625
626
627
	if (TCP_CLIENT(client)) {
		client_accept(client);
	} else {
628
		client_udprecv(client);
629
630
631
632
	}
}


633
/*%
634
635
 * The client's task has received a shutdown event.
 */
Bob Halley's avatar
add    
Bob Halley committed
636
637
638
639
640
static void
client_shutdown(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
641
642
	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
643
644
645
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
646
647
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
648
649
650
651
	CTRACE("shutdown");

	isc_event_free(&event);

652
653
654
655
656
657
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}

658
659
660
	if (ISC_QLINK_LINKED(client, ilink))
		ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);

661
	client->newstate = NS_CLIENTSTATE_FREED;
662
	client->needshutdown = ISC_FALSE;
663
	(void)exit_check(client);
664
}
Bob Halley's avatar
add    
Bob Halley committed
665

666
667
static void
ns_client_endrequest(ns_client_t *client) {
668
669
670
	INSIST(client->naccepts == 0);
	INSIST(client->nreads == 0);
	INSIST(client->nsends == 0);
671
	INSIST(client->nrecvs == 0);
672
	INSIST(client->nupdates == 0);
673
674
	INSIST(client->state == NS_CLIENTSTATE_WORKING ||
	       client->state == NS_CLIENTSTATE_RECURSING);
Bob Halley's avatar
add    
Bob Halley committed
675

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
676
677
	CTRACE("endrequest");

678
	if (client->next != NULL) {
679
		(client->next)(client);
680
681
682
		client->next = NULL;
	}

683
684
	if (client->view != NULL)
		dns_view_detach(&client->view);
Bob Halley's avatar
Bob Halley committed
685
686
687
688
689
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
690

691
	client->signer = NULL;
692
	client->udpsize = 512;
693
	client->extflags = 0;
694
	client->ednsversion = -1;
Bob Halley's avatar
add    
Bob Halley committed
695
	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
696

697
	if (client->recursionquota != NULL) {
698
		isc_quota_detach(&client->recursionquota);
699
700
701
		isc_stats_decrement(ns_g_server->nsstats,
				    dns_nsstatscounter_recursclients);
	}
702
703

	/*
704
	 * Clear all client attributes that are specific to
705
706
707
	 * the request; that's all except the TCP flag.
	 */
	client->attributes &= NS_CLIENTATTR_TCP;
708
}
709

710
711
712
void
ns_client_next(ns_client_t *client, isc_result_t result) {
	int newstate;
713

714
	REQUIRE(NS_CLIENT_VALID(client));
715
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
716
		client->state == NS_CLIENTSTATE_RECURSING ||
717
		client->state == NS_CLIENTSTATE_READING);
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
718

719
	CTRACE("next");
720

721
	if (result != ISC_R_SUCCESS)
722
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
			      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
738
	(void)exit_check(client);
Bob Halley's avatar
add    
Bob Halley committed
739
740
}

741

Bob Halley's avatar
add    
Bob Halley committed
742
743
744
static void
client_senddone(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;
745
	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
Bob Halley's avatar
add    
Bob Halley committed
746
747

	REQUIRE(sevent != NULL);
748
749
	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
	client = sevent->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
750
751
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);
752
	REQUIRE(sevent == client->sendevent);
Bob Halley's avatar
add    
Bob Halley committed
753

754
755
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
756
757
	CTRACE("senddone");

758
759
760
761
762
763
	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
764
	INSIST(client->nsends > 0);
Bob Halley's avatar
add    
Bob Halley committed
765
	client->nsends--;
766

767
	if (client->tcpbuf != NULL) {
768
		INSIST(TCP_CLIENT(client));
769
770
771
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add    
Bob Halley committed
772

773
	ns_client_next(client, ISC_R_SUCCESS);
Bob Halley's avatar
add    
Bob Halley committed
774
775
}

776
/*%
777
778
779
780
781
782
783
 * 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.
 */

784
785
786
static isc_result_t
client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
		    isc_buffer_t *tcpbuffer, isc_uint32_t length,
787
		    unsigned char *sendbuf, unsigned char **datap)
788
{
789
	unsigned char *data;
790
791
	isc_uint32_t bufsize;
	isc_result_t result;
792

793
	INSIST(datap != NULL);
794
795
	INSIST((tcpbuffer == NULL && length != 0) ||
	       (tcpbuffer != NULL && length == 0));
796
797
798

	if (TCP_CLIENT(client)) {
		INSIST(client->tcpbuf == NULL);
799
		if (length + 2 > TCP_BUFFER_SIZE) {
800
801
802
803
804
805
806
807
808
			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;
809
810
811
812
813
		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
814
815
			INSIST(length <= 0xffff);
			isc_buffer_putuint16(buffer, (isc_uint16_t)length);
816
		}
817
	} else {
818
		data = sendbuf;
819
820
821
822
823
824
825
826
827
828
829
830
831
#ifdef ISC_PLATFORM_USESIT
		if ((client->attributes & NS_CLIENTATTR_HAVESIT) == 0) {
			if (client->view != NULL)
				bufsize = client->view->situdp;
			else
				bufsize = 512;
		} else
			bufsize = client->udpsize;
		if (bufsize > client->udpsize)
			bufsize = client->udpsize;
		if (bufsize > SEND_BUFFER_SIZE)
			bufsize = SEND_BUFFER_SIZE;
#else
832
833
834
835
		if (client->udpsize < SEND_BUFFER_SIZE)
			bufsize = client->udpsize;
		else
			bufsize = SEND_BUFFER_SIZE;
836
#endif
837
		if (length > bufsize) {
838
839
840
			result = ISC_R_NOSPACE;
			goto done;
		}
841
		isc_buffer_init(buffer, data, bufsize);
842
	}
843
844
	*datap = data;
	result = ISC_R_SUCCESS;
845

846
847
848
849
850
851
852
853
854
855
856
 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;
857
858
	isc_netaddr_t netaddr;
	int match;
859
	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
Evan Hunt's avatar
Evan Hunt committed
860
	isc_dscp_t dispdscp = -1;
861
862
863
864
865

	if (TCP_CLIENT(client)) {
		socket = client->tcpsocket;
		address = NULL;
	} else {
866
867
		socket = client->udpsocket;
		address = &client->peeraddr;
868
869
870
871

		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
		if (ns_g_server->blackholeacl != NULL &&
		    dns_acl_match(&netaddr, NULL,
872
				  ns_g_server->blackholeacl,
873
874
				  &ns_g_server->aclenv,
				  &match, NULL) == ISC_R_SUCCESS &&
875
876
		    match > 0)
			return (DNS_R_BLACKHOLED);
877
		sockflags |= ISC_SOCKFLAG_NORETRY;
878
	}
879

880
	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
881
	    (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
882
883
884
		pktinfo = &client->pktinfo;
	else
		pktinfo = NULL;
885

Evan Hunt's avatar
Evan Hunt committed
886
887
888
889
890
891
892
893
894
895
896
897
898
899
	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;
	}

900
901
902
	isc_buffer_usedregion(buffer, &r);

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

904
905
	result = isc_socket_sendto2(socket, &r, client->task,
				    address, pktinfo,
906
				    client->sendevent, sockflags);
907
	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
908
		client->nsends++;
909
910
911
		if (result == ISC_R_SUCCESS)
			client_senddone(client->task,
					(isc_event_t *)client->sendevent);
912
		result = ISC_R_SUCCESS;
913
	}
914
915
916
917
918
919
920
921
922
923
	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;
924
	unsigned char sendbuf[SEND_BUFFER_SIZE];
925
926
927
928
929
930
931
932
933
934
935

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("sendraw");

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

936
937
	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
				     sendbuf, &data);
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
	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;
954
955
956
957
958
959
960
961
962

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

963
964
static void
client_send(ns_client_t *client) {
Bob Halley's avatar
add    
Bob Halley committed
965
966
967
	isc_result_t result;
	unsigned char *data;
	isc_buffer_t buffer;
Bob Halley's avatar
Bob Halley committed
968
	isc_buffer_t tcpbuffer;
Bob Halley's avatar
add    
Bob Halley committed
969
	isc_region_t r;
970
971
	dns_compress_t cctx;
	isc_boolean_t cleanup_cctx = ISC_FALSE;
972
	unsigned char sendbuf[SEND_BUFFER_SIZE];
973
	unsigned int render_opts;
974
	unsigned int preferred_glue;
975
	isc_boolean_t opt_included = ISC_FALSE;
Bob Halley's avatar
add    
Bob Halley committed
976
977
978
979
980

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("send");

981
982
	if ((client->attributes & NS_CLIENTATTR_RA) != 0)
		client->message->flags |= DNS_MESSAGEFLAG_RA;
983

984
	if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
985
		render_opts = 0;
986
	else
987
		render_opts = DNS_MESSAGERENDER_OMITDNSSEC;
988
989
990
991
992
993
994
995
996

	preferred_glue = 0;
	if (client->view != NULL) {
		if (client->view->preferred_glue == dns_rdatatype_a)
			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
		else if (client->view->preferred_glue == dns_rdatatype_aaaa)
			preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
	}

Evan Hunt's avatar
Evan Hunt committed
997
#ifdef ALLOW_FILTER_AAAA
998
999
	/*
	 * filter-aaaa-on-v4 yes or break-dnssec option to suppress
Evan Hunt's avatar
Evan Hunt committed
1000
1001
	 * AAAA records.
	 *
1002
1003
1004
1005
	 * We already know that request came via IPv4,
	 * that we have both AAAA and A records,
	 * and that we either have no signatures that the client wants
	 * or we are supposed to break DNSSEC.
1006
1007
	 *
	 * Override preferred glue if necessary.
1008
	 */
1009
	if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
1010
		render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
1011
		if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
Mark Andrews's avatar
Mark Andrews committed
1012
1013
			preferred_glue = DNS_MESSAGERENDER_PREFER_A;
	}
1014
#endif
1015

1016
1017
1018
1019
	/*
	 * Create an OPT for our reply.
	 */
	if ((client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
1020
1021
		result = ns_client_addopt(client, client->message,
					  &client->opt);
1022
1023
1024
1025
		if (result != ISC_R_SUCCESS)
			goto done;
	}

Bob Halley's avatar
Bob Halley committed
1026
	/*
Brian Wellington's avatar
Brian Wellington committed
1027
	 * XXXRTH  The following doesn't deal with TCP buffer resizing.
Bob Halley's avatar
Bob Halley committed
1028
	 */
1029
1030
	result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
				     sendbuf, &data);
1031
1032
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
1033

1034
1035
1036
	result = dns_compress_init(&cctx, -1, client->mctx);
	if (result != ISC_R_SUCCESS)
		goto done;
Evan Hunt's avatar
Evan Hunt committed
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
	if (client->peeraddr_valid && client->view != NULL) {
		isc_netaddr_t netaddr;
		dns_name_t *name = NULL;

		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
		if (client->message->tsigkey != NULL)
			name = &client->message->tsigkey->name;
		if (client->view->nocasecompress == NULL ||
		    !allowed(&netaddr, name, client->view->nocasecompress))
		{
			dns_compress_setsensitive(&cctx, ISC_TRUE);
		}
	}
1050
1051
1052
	cleanup_cctx = ISC_TRUE;

	result = dns_message_renderbegin(client->message, &cctx, &buffer);
Bob Halley's avatar
add    
Bob Halley committed
1053
1054
	if (result != ISC_R_SUCCESS)
		goto done;
1055

Bob Halley's avatar
EDNS0    
Bob Halley committed
1056
1057
	if (client->opt != NULL) {
		result = dns_message_setopt(client->message, client->opt);
1058
		opt_included = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
1059
		client->opt = NULL;
1060
1061
		if (result != ISC_R_SUCCESS)
			goto done;