client.c 110 KB
Newer Older
Bob Halley's avatar
add    
Bob Halley committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
Bob Halley's avatar
add    
Bob Halley committed
10
11
12
13
 */

#include <config.h>

14
#include <inttypes.h>
15
#include <stdbool.h>
16

17
#include <isc/aes.h>
18
#include <isc/formatcheck.h>
19
#include <isc/fuzz.h>
20
#include <isc/hmac.h>
Brian Wellington's avatar
Brian Wellington committed
21
#include <isc/mutex.h>
22
#include <isc/once.h>
23
#include <isc/nonce.h>
24
#include <isc/platform.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
25
#include <isc/print.h>
26
#include <isc/queue.h>
27
#include <isc/random.h>
28
#include <isc/safe.h>
29
#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
#include <dns/adb.h>
Evan Hunt's avatar
Evan Hunt committed
38
#include <dns/badcache.h>
39
#include <dns/db.h>
Bob Halley's avatar
add    
Bob Halley committed
40
#include <dns/dispatch.h>
Evan Hunt's avatar
Evan Hunt committed
41
#include <dns/dnstap.h>
42
#include <dns/cache.h>
43
#include <dns/edns.h>
Bob Halley's avatar
add    
Bob Halley committed
44
45
#include <dns/events.h>
#include <dns/message.h>
46
#include <dns/peer.h>
47
#include <dns/rcode.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
48
#include <dns/rdata.h>
49
#include <dns/rdataclass.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
50
51
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
52
#include <dns/resolver.h>
53
#include <dns/stats.h>
54
#include <dns/tsig.h>
55
#include <dns/view.h>
56
#include <dns/zone.h>
Bob Halley's avatar
add    
Bob Halley committed
57

58
59
60
61
62
63
#include <ns/interfacemgr.h>
#include <ns/log.h>
#include <ns/notify.h>
#include <ns/server.h>
#include <ns/stats.h>
#include <ns/update.h>
64
65
66
67
68

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

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

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

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

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

105
106
107
108
109
110
111
112
113
114
#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.
 */

115
#define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
Evan Hunt's avatar
Evan Hunt committed
116
#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
117

118
119
#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
120
121
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
122

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

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

Bob Halley's avatar
add    
Bob Halley committed
131
	isc_mem_t *			mctx;
132
	ns_server_t *			sctx;
Bob Halley's avatar
add    
Bob Halley committed
133
134
	isc_taskmgr_t *			taskmgr;
	isc_timermgr_t *		timermgr;
135
	isc_task_t *			excl;
136
137

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

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

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

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

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

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

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

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

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

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

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

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

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

232
LIBNS_EXTERNAL_DATA unsigned int ns_client_requests;
233

234
235
static void read_settimeout(ns_client_t *client, bool newconn);
static void client_read(ns_client_t *client, bool newconn);
236
static void client_accept(ns_client_t *client);
237
static void client_udprecv(ns_client_t *client);
238
static void clientmgr_destroy(ns_clientmgr_t *manager);
239
static bool exit_check(ns_client_t *client);
240
static void ns_client_endrequest(ns_client_t *client);
241
static void client_start(isc_task_t *task, isc_event_t *event);
242
static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
243
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
244
			       dns_dispatch_t *disp, bool tcp);
245
static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
246
			       isc_socket_t *sock);
247
248
static void compute_cookie(ns_client_t *client, uint32_t when,
			   uint32_t nonce, const unsigned char *secret,
249
			   isc_buffer_t *buf);
250

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

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

262
263
264
265
266
void
ns_client_killoldestquery(ns_client_t *client) {
	ns_client_t *oldest;
	REQUIRE(NS_CLIENT_VALID(client));

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

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

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

295
static void
296
read_settimeout(ns_client_t *client, bool newconn) {
297
298
299
300
301
	isc_result_t result;
	isc_interval_t interval;
	unsigned int ds;

	if (newconn)
302
		ds = client->sctx->initialtimo;
303
	else if (USEKEEPALIVE(client))
304
		ds = client->sctx->keepalivetimo;
305
	else
306
		ds = client->sctx->idletimo;
307
308
309

	isc_interval_set(&interval, ds / 10, 100000000 * (ds % 10));
	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
310
311
				 &interval, false);
	client->timerset = true;
312
313
314
315
316
317
318
319
320
	if (result != ISC_R_SUCCESS) {
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
			      "setting timeout: %s",
			      isc_result_totext(result));
		/* Continue anyway. */
	}
}

321
/*%
322
 * Check for a deactivation or shutdown request and take appropriate
323
 * action.  Returns true if either is in progress; in this case
324
325
326
 * the caller must no longer use the client object as it may have been
 * freed.
 */
327
static bool
328
exit_check(ns_client_t *client) {
329
	bool destroy_manager = false;
330
	ns_clientmgr_t *manager = NULL;
331

332
	REQUIRE(NS_CLIENT_VALID(client));
333
	manager = client->manager;
334
335

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

338
	INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
339

340
341
342
343
344
345
	/*
	 * 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
346
347
	 *  - The client does not detach from the view until references is zero
	 *  - references does not go to zero until the resolver has shut down
348
	 *
349
	 * Keep the view attached until any outstanding updates complete.
350
	 */
Automatic Updater's avatar
Automatic Updater committed
351
	if (client->nupdates == 0 &&
352
	    client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
353
		dns_view_detach(&client->view);
354

355
356
357
	if (client->state == NS_CLIENTSTATE_WORKING ||
	    client->state == NS_CLIENTSTATE_RECURSING)
	{
358
		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
359
360
361
362
		/*
		 * Let the update processing complete.
		 */
		if (client->nupdates > 0)
363
			return (true);
364

365
366
367
368
		/*
		 * We are trying to abort request processing.
		 */
		if (client->nsends > 0) {
369
			isc_socket_t *sock;
370
			if (TCP_CLIENT(client))
371
				sock = client->tcpsocket;
372
			else
373
374
				sock = client->udpsocket;
			isc_socket_cancel(sock, client->task,
375
376
					  ISC_SOCKCANCEL_SEND);
		}
377

378
379
380
		if (! (client->nsends == 0 && client->nrecvs == 0 &&
		       client->references == 0))
		{
381
382
383
384
			/*
			 * Still waiting for I/O cancel completion.
			 * or lingering references.
			 */
385
			return (true);
386
		}
387

388
389
		/*
		 * I/O cancel is complete.  Burn down all state
390
		 * related to the current request.  Ensure that
391
		 * the client is no longer on the recursing list.
392
393
394
395
		 *
		 * 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()
396
		 */
397
398
		if (client->state == NS_CLIENTSTATE_RECURSING) {
			LOCK(&manager->reclock);
399
400
401
			if (ISC_LINK_LINKED(client, rlink))
				ISC_LIST_UNLINK(manager->recursing,
						client, rlink);
402
			UNLOCK(&manager->reclock);
403
		}
404
405
406
		ns_client_endrequest(client);

		client->state = NS_CLIENTSTATE_READING;
407
		INSIST(client->recursionquota == NULL);
408

409
		if (NS_CLIENTSTATE_READING == client->newstate) {
410
			if (!client->pipelined) {
411
				client_read(client, false);
412
				client->newstate = NS_CLIENTSTATE_MAX;
413
				return (true); /* We're done. */
414
415
			} else if (client->mortal) {
				client->newstate = NS_CLIENTSTATE_INACTIVE;
416
			} else
417
				return (false);
418
419
420
421
422
423
424
425
		}
	}

	if (client->state == NS_CLIENTSTATE_READING) {
		/*
		 * We are trying to abort the current TCP connection,
		 * if any.
		 */
426
		INSIST(client->recursionquota == NULL);
427
428
429
		INSIST(client->newstate <= NS_CLIENTSTATE_READY);
		if (client->nreads > 0)
			dns_tcpmsg_cancelread(&client->tcpmsg);
430
		if (client->nreads != 0) {
431
			/* Still waiting for read cancel completion. */
432
			return (true);
433
434
435
436
		}

		if (client->tcpmsg_valid) {
			dns_tcpmsg_invalidate(&client->tcpmsg);
437
			client->tcpmsg_valid = false;
438
		}
439
440
		if (client->tcpsocket != NULL) {
			CTRACE("closetcp");
441
			isc_socket_detach(&client->tcpsocket);
442
		}
443
444
445

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

447
		if (client->timerset) {
Andreas Gustafsson's avatar
spacing    
Andreas Gustafsson committed
448
449
			(void)isc_timer_reset(client->timer,
					      isc_timertype_inactive,
450
451
					      NULL, NULL, true);
			client->timerset = false;
452
		}
453

454
		client->pipelined = false;
455

456
		client->peeraddr_valid = false;
457

458
		client->state = NS_CLIENTSTATE_READY;
459
		INSIST(client->recursionquota == NULL);
460
461
462
463
464
465

		/*
		 * 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.
466
467
468
469
		 *
		 * 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.
470
		 */
471
472
473
		if (client->mortal && TCP_CLIENT(client) &&
		    ((client->sctx->options & NS_SERVER_CLIENTTEST) == 0))
		{
474
475
476
			LOCK(&client->interface->lock);
			if (client->interface->ntcpcurrent <
				    client->interface->ntcptarget)
477
				client->mortal = false;
478
479
480
481
482
483
484
485
486
487
488
			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;
		}
489

490
491
492
		if (NS_CLIENTSTATE_READY == client->newstate) {
			if (TCP_CLIENT(client)) {
				client_accept(client);
493
494
			} else
				client_udprecv(client);
495
			client->newstate = NS_CLIENTSTATE_MAX;
496
			return (true);
497
498
499
500
		}
	}

	if (client->state == NS_CLIENTSTATE_READY) {
501
		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
502

503
504
505
506
507
508
		/*
		 * We are trying to enter the inactive state.
		 */
		if (client->naccepts > 0)
			isc_socket_cancel(client->tcplistener, client->task,
					  ISC_SOCKCANCEL_ACCEPT);
509

510
511
		/* Still waiting for accept cancel completion. */
		if (! (client->naccepts == 0))
512
			return (true);
513

514
		/* Accept cancel is complete. */
515
516
517
		if (client->nrecvs > 0)
			isc_socket_cancel(client->udpsocket, client->task,
					  ISC_SOCKCANCEL_RECV);
518
519
520

		/* Still waiting for recv cancel completion. */
		if (! (client->nrecvs == 0))
521
			return (true);
522

523
524
		/* Still waiting for control event to be delivered */
		if (client->nctls > 0)
525
			return (true);
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542

		/* 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;
543
		client->mortal = false;
544
		client->sendcb = NULL;
545

546
547
548
549
550
551
		if (client->keytag != NULL) {
			isc_mem_put(client->mctx, client->keytag,
				    client->keytag_len);
			client->keytag_len = 0;
		}

552
553
554
555
556
557
558
		/*
		 * 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.
		 */
559
		client->state = NS_CLIENTSTATE_INACTIVE;
560
		INSIST(client->recursionquota == NULL);
561

562
563
		if (client->state == client->newstate) {
			client->newstate = NS_CLIENTSTATE_MAX;
564
565
566
567
			if ((client->sctx->options &
			     NS_SERVER_CLIENTTEST) == 0 &&
			    manager != NULL && !manager->exiting)
			{
568
569
				ISC_QUEUE_PUSH(manager->inactive, client,
					       ilink);
570
			}
571
572
			if (client->needshutdown)
				isc_task_shutdown(client->task);
573
			return (true);
574
575
576
577
578
579
580
		}
	}

	if (client->state == NS_CLIENTSTATE_INACTIVE) {
		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
		/*
		 * We are trying to free the client.
581
582
583
584
585
		 *
		 * 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.
586
		 */
587
588
589
		REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);

		INSIST(client->recursionquota == NULL);
590
		INSIST(!ISC_QLINK_LINKED(client, ilink));
591

592
593
594
595
596
597
		if (manager != NULL) {
			LOCK(&manager->listlock);
			ISC_LIST_UNLINK(manager->clients, client, link);
			LOCK(&manager->lock);
			if (manager->exiting &&
			    ISC_LIST_EMPTY(manager->clients))
598
				destroy_manager = true;
599
600
601
602
			UNLOCK(&manager->lock);
			UNLOCK(&manager->listlock);
		}

603
604
605
606
607
		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);
608
609
		if (client->delaytimer != NULL)
			isc_timer_detach(&client->delaytimer);
610
611

		if (client->tcpbuf != NULL)
612
613
			isc_mem_put(client->mctx, client->tcpbuf,
				    TCP_BUFFER_SIZE);
614
615
616
		if (client->opt != NULL) {
			INSIST(dns_rdataset_isassociated(client->opt));
			dns_rdataset_disassociate(client->opt);
617
618
			dns_message_puttemprdataset(client->message,
						    &client->opt);
619
		}
620
621
622
623
624
		if (client->keytag != NULL) {
			isc_mem_put(client->mctx, client->keytag,
				    client->keytag_len);
			client->keytag_len = 0;
		}
625

626
		dns_message_destroy(&client->message);
627

628
629
630
631
632
633
634
635
636
637
		/*
		 * 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;
638

639
640
641
642
		/*
		 * Check that there are no other external references to
		 * the memory context.
		 */
643
644
645
		if ((client->sctx->options & NS_SERVER_CLIENTTEST) != 0 &&
		    isc_mem_references(client->mctx) != 1)
		{
646
647
			isc_mem_stats(client->mctx, stderr);
			INSIST(0);
648
			ISC_UNREACHABLE();
649
		}
650
651
652
653
654

		/*
		 * Destroy the fetchlock mutex that was created in
		 * ns_query_init().
		 */
655
		isc_mutex_destroy(&client->query.fetchlock);
656

657
658
659
		if (client->sctx != NULL)
			ns_server_detach(&client->sctx);

660
		isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
661
662
	}

663
664
	if (destroy_manager && manager != NULL)
		clientmgr_destroy(manager);
665

666
	return (true);
667
668
}

669
/*%
670
671
672
673
674
675
676
677
 * 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);
678

679
680
	UNUSED(task);

681
682
683
684
685
686
	INSIST(client->nctls == 1);
	client->nctls--;

	if (exit_check(client))
		return;

687
	if (TCP_CLIENT(client)) {
688
		if (client->pipelined) {
689
			client_read(client, false);
690
691
692
		} else {
			client_accept(client);
		}
693
	} else {
694
		client_udprecv(client);
695
696
697
698
	}
}


699
/*%
700
701
 * The client's task has received a shutdown event.
 */
Bob Halley's avatar
add    
Bob Halley committed
702
703
704
705
706
static void
client_shutdown(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
707
708
	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
709
710
711
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
712
713
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
714
715
716
717
	CTRACE("shutdown");

	isc_event_free(&event);

718
719
720
721
722
723
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}

724
725
726
	if (ISC_QLINK_LINKED(client, ilink))
		ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);

727
	client->newstate = NS_CLIENTSTATE_FREED;
728
	client->needshutdown = false;
729
	(void)exit_check(client);
730
}
Bob Halley's avatar
add    
Bob Halley committed
731

732
733
static void
ns_client_endrequest(ns_client_t *client) {
734
735
736
	INSIST(client->naccepts == 0);
	INSIST(client->nreads == 0);
	INSIST(client->nsends == 0);
737
	INSIST(client->nrecvs == 0);
738
	INSIST(client->nupdates == 0);
739
740
	INSIST(client->state == NS_CLIENTSTATE_WORKING ||
	       client->state == NS_CLIENTSTATE_RECURSING);
Bob Halley's avatar
add    
Bob Halley committed
741

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
742
743
	CTRACE("endrequest");

744
	if (client->next != NULL) {
745
		(client->next)(client);
746
747
748
		client->next = NULL;
	}

749
750
	if (client->view != NULL) {
#ifdef ENABLE_AFL
751
		if (client->sctx->fuzztype == isc_fuzz_resolver) {
752
753
754
755
			dns_cache_clean(client->view->cache, INT_MAX);
			dns_adb_flush(client->view->adb);
		}
#endif
756
		dns_view_detach(&client->view);
757
	}
Bob Halley's avatar
Bob Halley committed
758
759
760
761
762
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
763

764
	client->signer = NULL;
765
	client->udpsize = 512;
766
	client->extflags = 0;
767
	client->ednsversion = -1;
Bob Halley's avatar
add    
Bob Halley committed
768
	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
769

770
	if (client->recursionquota != NULL) {
771
		isc_quota_detach(&client->recursionquota);
772
773
		ns_stats_decrement(client->sctx->nsstats,
				   ns_statscounter_recursclients);
774
	}
775
776

	/*
777
	 * Clear all client attributes that are specific to
778
779
780
	 * the request; that's all except the TCP flag.
	 */
	client->attributes &= NS_CLIENTATTR_TCP;
781
#ifdef ENABLE_AFL
782
783
784
785
786
787
	if (client->sctx->fuzznotify != NULL &&
	    (client->sctx->fuzztype == isc_fuzz_client ||
	     client->sctx->fuzztype == isc_fuzz_tcpclient ||
	     client->sctx->fuzztype == isc_fuzz_resolver))
	{
		client->sctx->fuzznotify();
788
789
790
	}
#endif /* ENABLE_AFL */

791
}
792

793
794
795
void
ns_client_next(ns_client_t *client, isc_result_t result) {
	int newstate;
796

797
	REQUIRE(NS_CLIENT_VALID(client));
798
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
799
		client->state == NS_CLIENTSTATE_RECURSING ||
800
		client->state == NS_CLIENTSTATE_READING);
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
801

802
	CTRACE("next");
803

804
	if (result != ISC_R_SUCCESS)
805
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
			      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
821
	(void)exit_check(client);
Bob Halley's avatar
add    
Bob Halley committed
822
823
}

824

Bob Halley's avatar
add    
Bob Halley committed
825
826
827
static void
client_senddone(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;
828
	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
Bob Halley's avatar
add    
Bob Halley committed
829
830

	REQUIRE(sevent != NULL);
831
832
	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
	client = sevent->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
833
834
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);
835
	REQUIRE(sevent == client->sendevent);
Bob Halley's avatar
add    
Bob Halley committed
836

837
838
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
839
840
	CTRACE("senddone");

841
842
843
844
845
846
	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
847
	INSIST(client->nsends > 0);
Bob Halley's avatar
add    
Bob Halley committed
848
	client->nsends--;
849

850
	if (client->tcpbuf != NULL) {
851
		INSIST(TCP_CLIENT(client));
852
853
854
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add    
Bob Halley committed
855

856
	ns_client_next(client, ISC_R_SUCCESS);
Bob Halley's avatar
add    
Bob Halley committed
857
858
}

859
/*%
860
861
862
863
864
865
866
 * 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.
 */

867
868
static isc_result_t
client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
869
		    isc_buffer_t *tcpbuffer, uint32_t length,
870
		    unsigned char *sendbuf, unsigned char **datap)
871
{
872
	unsigned char *data;
873
	uint32_t bufsize;
874
	isc_result_t result;
875

876
	INSIST(datap != NULL);
877
878
	INSIST((tcpbuffer == NULL && length != 0) ||
	       (tcpbuffer != NULL && length == 0));
879
880
881

	if (TCP_CLIENT(client)) {
		INSIST(client->tcpbuf == NULL);
882
		if (length + 2 > TCP_BUFFER_SIZE) {
883
884
885
886
887
888
889
890
891
			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;
892
893
894
895
896
		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
897
			INSIST(length <= 0xffff);
898
			isc_buffer_putuint16(buffer, (uint16_t)length);
899
		}
900
	} else {
901
		data = sendbuf;
902
		if ((client->attributes & NS_CLIENTATTR_HAVECOOKIE) == 0) {
903
			if (client->view != NULL)
904
				bufsize = client->view->nocookieudp;
905
906
907
908
909
910
911
912
			else
				bufsize = 512;
		} else
			bufsize = client->udpsize;
		if (bufsize > client->udpsize)
			bufsize = client->udpsize;
		if (bufsize > SEND_BUFFER_SIZE)
			bufsize = SEND_BUFFER_SIZE;
913
		if (length > bufsize) {
914
915
916
			result = ISC_R_NOSPACE;
			goto done;
		}
917
		isc_buffer_init(buffer, data, bufsize);
918
	}
919
920
	*datap = data;
	result = ISC_R_SUCCESS;
921

922
923
924
925
926
927
928
929
930
931
 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;
932
	isc_socket_t *sock;
933
934
	isc_netaddr_t netaddr;
	int match;
935
	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
Evan Hunt's avatar
Evan Hunt committed
936
	isc_dscp_t dispdscp = -1;
937
938

	if (TCP_CLIENT(client)) {
939
		sock = client->tcpsocket;
940
941
		address = NULL;
	} else {
942
943
944
		dns_aclenv_t *env =
			ns_interfacemgr_getaclenv(client->interface->mgr);

945
		sock = client->udpsocket;
946
		address = &client->peeraddr;
947
948

		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
949
		if (client->sctx->blackholeacl != NULL &&
950
951
		    (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
				   env, &match, NULL) == ISC_R_SUCCESS) &&
952
		    match > 0)
953
		{
954
			return (DNS_R_BLACKHOLED);
955
		}
956
		sockflags |= ISC_SOCKFLAG_NORETRY;
957
	}
958

959
	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
960
	    (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
961
962
963
		pktinfo = &client->pktinfo;
	else
		pktinfo = NULL;
964

Evan Hunt's avatar
Evan Hunt committed
965
966
967
968
969
970
971
972
973
974
975
976
977
978
	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;
	}

979
980
	isc_buffer_usedregion(buffer, &r);

981
982
983
984
985
986
	/*
	 * If this is a UDP client and the IPv6 packet can't be
	 * encapsulated without generating a PTB on a 1500 octet
	 * MTU link force fragmentation at 1280 if it is a IPv6
	 * response.
	 */
987
	client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_USEMINMTU;
988
989
990
	if (!TCP_CLIENT(client) && r.length > 1432)
		client->sendevent->attributes |= ISC_SOCKEVENTATTR_USEMINMTU;

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

993
	result = isc_socket_sendto2(sock, &r, client->task,
994
				    address, pktinfo,
995
				    client->sendevent, sockflags);
996
	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
997
		client->nsends++;
998
999
1000
		if (result == ISC_R_SUCCESS)
			client_senddone(client->task,
					(isc_event_t *)client->sendevent);
1001
		result = ISC_R_SUCCESS;
1002
	}
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
	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;
1013
	unsigned char sendbuf[SEND_BUFFER_SIZE];
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("sendraw");

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

1025
1026
	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
				     sendbuf, &data);
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
	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;