client.c 42.7 KB
Newer Older
Bob Halley's avatar
add    
Bob Halley committed
1
/*
Bob Halley's avatar
Bob Halley committed
2
 * Copyright (C) 1999, 2000  Internet Software Consortium.
Bob Halley's avatar
add    
Bob Halley committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM 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.
 */

#include <config.h>

20
#include <isc/print.h>
21
22
#include <isc/task.h>
#include <isc/string.h>
Bob Halley's avatar
add    
Bob Halley committed
23
#include <isc/timer.h>
Michael Graff's avatar
Michael Graff committed
24
#include <isc/util.h>
Bob Halley's avatar
add    
Bob Halley committed
25
26
27
28

#include <dns/dispatch.h>
#include <dns/events.h>
#include <dns/message.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
29
30
31
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
32
#include <dns/view.h>
33
#include <dns/zone.h>
Bob Halley's avatar
add    
Bob Halley committed
34

35
#include <named/interfacemgr.h>
36
#include <named/log.h>
37
#include <named/notify.h>
38
#include <named/server.h>
39
#include <named/update.h>
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

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

/*
 * 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
55
56
57

#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
58
#define CTRACE(m)	ns_client_log(client, \
59
60
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
61
				      ISC_LOG_DEBUG(3), \
62
				      "%s", (m))
63
64
65
#define MTRACE(m)	isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
66
				      ISC_LOG_DEBUG(3), \
67
				      "clientmgr @%p: %s", manager, (m))
68
69
70
#else
#define CTRACE(m)	((void)(m))
#define MTRACE(m)	((void)(m))
Bob Halley's avatar
add    
Bob Halley committed
71
72
#endif

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

75
#define TCP_BUFFER_SIZE			(65535 + 2)
76
#define SEND_BUFFER_SIZE		2048
77
#define RECV_BUFFER_SIZE		2048
Bob Halley's avatar
add    
Bob Halley committed
78
79
80
81
82
83
84
85
86
87

struct ns_clientmgr {
	/* Unlocked. */
	unsigned int			magic;
	isc_mem_t *			mctx;
	isc_taskmgr_t *			taskmgr;
	isc_timermgr_t *		timermgr;
	isc_mutex_t			lock;
	/* Locked by lock. */
	isc_boolean_t			exiting;
88
	client_list_t			active; 	/* Active clients */
89
	client_list_t 			inactive;	/* To be recycled */
Bob Halley's avatar
add    
Bob Halley committed
90
91
92
93
94
95
};

#define MANAGER_MAGIC			0x4E53436DU	/* NSCm */
#define VALID_MANAGER(m)		((m) != NULL && \
					 (m)->magic == MANAGER_MAGIC)

96
97
98
99
100
101
102
103
104
105
106
/*
 * 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
107

108
109
110
111
#define NS_CLIENTSTATE_FREED    0
/*
 * The client object no longer exists.
 */
Bob Halley's avatar
add    
Bob Halley committed
112

113
114
115
116
117
118
119
#define NS_CLIENTSTATE_INACTIVE 1
/*
 * The client object exists and has a task and timer.  
 * 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
120

121
#define NS_CLIENTSTATE_READY    2
Bob Halley's avatar
add    
Bob Halley committed
122
/*
123
124
125
 * 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
126
 *
127
128
 * If it is a TCP client object, it has a TCP listener socket
 * and an outstading TCP listen request.
Bob Halley's avatar
add    
Bob Halley committed
129
 *
130
131
 * If it is a UDP client object, it is associated with a
 * dispatch and has an outstanding dispatch request.
Bob Halley's avatar
add    
Bob Halley committed
132
133
 */

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#define NS_CLIENTSTATE_READING  3
/*
 * The client object is a TCP client object that has received
 * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an 
 * outstanding TCP read request.  This state is not used for
 * UDP client objects.
 */

#define NS_CLIENTSTATE_WORKING  4
/*
 * The client object has received a request and is working
 * on it.  It has a view, and it may  have any of a non-reset OPT,
 * recursion quota, and an outstanding write request.  If it
 * is a UDP client object, it has a dispatch event.
 */ 

#define NS_CLIENTSTATE_MAX      9
/*
 * 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.
 */


static void client_read(ns_client_t *client);
static void client_accept(ns_client_t *client);
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);
163
static void ns_client_checkactive(ns_client_t *client);
164

165
/*
166
167
168
169
 * Enter the inactive state.
 *
 * Requires:
 *	No requests are outstanding.
170
171
 */
static void
172
173
client_deactivate(ns_client_t *client) {
	REQUIRE(NS_CLIENT_VALID(client));
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
174
	
175
176
177
	if (client->interface)
		ns_interface_detach(&client->interface);

178
179
180
181
	INSIST(client->naccepts == 0);	
	if (client->tcplistener != NULL)
		isc_socket_detach(&client->tcplistener);

182
183
184
185
186
187
	if (client->dispentry != NULL) {
		dns_dispatchevent_t **deventp;
		if (client->dispevent != NULL)
			deventp = &client->dispevent;
		else
			deventp = NULL;
188
		dns_dispatch_removerequest(&client->dispentry, deventp);
189
190
191
192
193
194
	}
	if (client->dispatch != NULL)
		dns_dispatch_detach(&client->dispatch);
	
	client->attributes = 0;	
	client->mortal = ISC_FALSE;
195
196
197
198
199
200

	LOCK(&client->manager->lock);
	ISC_LIST_UNLINK(client->manager->active, client, link);
	ISC_LIST_APPEND(client->manager->inactive, client, link);
	client->list = &client->manager->inactive;
	UNLOCK(&client->manager->lock);
201
202
}

203
/*
204
205
206
 * Clean up a client object and free its memory.
 * Requires:
 *   The client is in the inactive state.
207
 */
208

209
static void
210
client_free(ns_client_t *client) {
211
	isc_boolean_t need_clientmgr_destroy = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
212
	ns_clientmgr_t *manager = NULL;
213
	
214
	REQUIRE(NS_CLIENT_VALID(client));
215
216
217
218
219
220
221

	/*
	 * 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.
	 */
222
	REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
223

Bob Halley's avatar
Bob Halley committed
224
	ns_query_free(client);
225
	isc_mem_put(client->mctx, client->sendbuf, SEND_BUFFER_SIZE);
Bob Halley's avatar
add    
Bob Halley committed
226
	isc_timer_detach(&client->timer);
227
	
228
229
	if (client->sendbuf != NULL)
		isc_mem_put(client->mctx, client->sendbuf, TCP_BUFFER_SIZE);
230
231
232
233
234
235
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
	dns_message_destroy(&client->message);
236
237
238
	if (client->task != NULL)
		isc_task_detach(&client->task);
	if (client->manager != NULL) {
239
		manager = client->manager;
240
		LOCK(&manager->lock);
241
242
243
		ISC_LIST_UNLINK(*client->list, client, link);
		client->list = NULL;
		if (manager->exiting &&
244
245
246
		    ISC_LIST_EMPTY(manager->active) &&
		    ISC_LIST_EMPTY(manager->inactive))
			need_clientmgr_destroy = ISC_TRUE;
247
		UNLOCK(&manager->lock);
Bob Halley's avatar
Bob Halley committed
248
	}
249

250
	CTRACE("free");
Bob Halley's avatar
add    
Bob Halley committed
251
	client->magic = 0;
Bob Halley's avatar
Bob Halley committed
252
	isc_mem_put(client->mctx, client, sizeof *client);
Bob Halley's avatar
add    
Bob Halley committed
253
254
	
	if (need_clientmgr_destroy)
255
		clientmgr_destroy(manager);
Bob Halley's avatar
add    
Bob Halley committed
256
257
}

258
259
260
261
static void
set_timeout(ns_client_t *client, unsigned int seconds) {
	isc_result_t result;
	isc_interval_t interval;
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
262
	
263
264
265
266
	isc_interval_set(&interval, seconds, 0);
	result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
				 &interval, ISC_FALSE);
	if (result != ISC_R_SUCCESS) {
267
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
268
269
270
271
272
273
274
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
			      "setting timouet: %s",
			      isc_result_totext(result));
		/* Continue anyway. */
	}
}

275
276
277
278
279
280
281
282
283
284
285
286
287
/*
 * 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) {
	REQUIRE(NS_CLIENT_VALID(client));

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

288
289
	INSIST(client->newstate < NS_CLIENTSTATE_WORKING);

290
291
292
293
294
295
	/*
	 * 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
296
297
	 *  - The client does not detach from the view until references is zero
	 *  - references does not go to zero until the resolver has shut down
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	 *
	 */
	if (client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
		dns_view_detach(&client->view);	

	if (client->state == NS_CLIENTSTATE_WORKING) {
		INSIST(client->newstate <= NS_CLIENTSTATE_READING);
		/*
		 * We are trying to abort request processing.
		 */
		if (client->nsends > 0) {
			isc_socket_t *socket;
			if (TCP_CLIENT(client))
				socket = client->tcpsocket;
			else
				socket =
				dns_dispatch_getsocket(client->dispatch);
			isc_socket_cancel(socket, client->task,
					  ISC_SOCKCANCEL_SEND);
		}
318

319
		if (! (client->nsends == 0 && client->references == 0)) {
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
			/*
			 * Still waiting for I/O cancel completion.
			 * or lingering references.
			 */
			return (ISC_TRUE);
		}
		/*
		 * I/O cancel is complete.  Burn down all state
		 * related to the current request.
		 */
		ns_client_endrequest(client);

		client->state = NS_CLIENTSTATE_READING;
		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.
		 */
		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;
		}
357
358
		if (client->tcpsocket != NULL) {
			CTRACE("closetcp");
359
			isc_socket_detach(&client->tcpsocket);
360
		}
361
362
363

		if (client->tcpquota != NULL)
			isc_quota_detach(&client->tcpquota);
364
365
366

		(void) isc_timer_reset(client->timer, isc_timertype_inactive,
				       NULL, NULL, ISC_TRUE);
367

368
369
		client->peeraddr_valid = ISC_FALSE;

370
		client->state = NS_CLIENTSTATE_READY;
371
372
373
374
375
376
377
378
379

		/*
		 * 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.
		 */
		ns_client_checkactive(client);
		
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
		if (NS_CLIENTSTATE_READY == client->newstate) {
			if (TCP_CLIENT(client)) {
				client_accept(client);
			} else {
				/*
				 * Give the processed dispatch event back to
				 * the dispatch. This tells the dispatch
				 * that we are ready to receive the next event.
				 */
				dns_dispatch_freeevent(client->dispatch,
						       client->dispentry,
						       &client->dispevent);
			}
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE);
		}
	}

	if (client->state == NS_CLIENTSTATE_READY) {
		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);	    
		/*
		 * We are trying to enter the inactive state.
		 */
		if (client->naccepts > 0)
			isc_socket_cancel(client->tcplistener, client->task,
					  ISC_SOCKCANCEL_ACCEPT);
		
		if (! (client->naccepts == 0)) {
			/* Still waiting for accept cancel completion. */
			return (ISC_TRUE);
		}
		/* Accept cancel is complete. */
		client_deactivate(client);
		client->state = NS_CLIENTSTATE_INACTIVE;
		if (client->state == client->newstate) {
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE); /* We're done. */
		}
	}

	if (client->state == NS_CLIENTSTATE_INACTIVE) {
		INSIST(client->newstate == NS_CLIENTSTATE_FREED);
		/*
		 * We are trying to free the client.
		 */
		client_free(client);
		return (ISC_TRUE);
	}

	return (ISC_TRUE);
}

432
433
434
/*
 * The client's task has received a shutdown event.
 */
Bob Halley's avatar
add    
Bob Halley committed
435
436
437
438
439
static void
client_shutdown(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
440
441
	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
442
443
444
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
445
446
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
447
448
449
450
	CTRACE("shutdown");

	isc_event_free(&event);

451
452
453
454
455
456
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}

457
	client->newstate = NS_CLIENTSTATE_FREED;
458
	(void)exit_check(client);
459
}
Bob Halley's avatar
add    
Bob Halley committed
460
461


462
463
static void
ns_client_endrequest(ns_client_t *client) {
464
465
466
	INSIST(client->naccepts == 0);
	INSIST(client->nreads == 0);
	INSIST(client->nsends == 0);
467
	INSIST(client->state == NS_CLIENTSTATE_WORKING);
Bob Halley's avatar
add    
Bob Halley committed
468

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
469
470
	CTRACE("endrequest");

471
	if (client->next != NULL) {
472
		(client->next)(client);
473
474
475
		client->next = NULL;
	}

476
477
	if (client->view != NULL)
		dns_view_detach(&client->view);
Bob Halley's avatar
Bob Halley committed
478
479
480
481
482
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
483

484
	client->udpsize = 512;
Bob Halley's avatar
add    
Bob Halley committed
485
	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
486

487
488
	if (client->recursionquota != NULL)
		isc_quota_detach(&client->recursionquota);
489
}
490

491
492
static void
ns_client_checkactive(ns_client_t *client) {
493
494
	if (client->mortal) {
		/*
495
496
497
498
		 * This client object should normally go inactive
		 * at this point, but if we have fewer active client 
		 * objects than  desired due to earlier quota exhaustion,
		 * keep it active to make up for the shortage.
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
		 */
		isc_boolean_t need_another_client = ISC_FALSE;
		if (TCP_CLIENT(client)) {
			LOCK(&client->interface->lock);
			if (client->interface->ntcpcurrent <
			    client->interface->ntcptarget) 
				need_another_client = ISC_TRUE;
			UNLOCK(&client->interface->lock);
		} else {
			/*
			 * The UDP client quota is enforced by making
			 * requests fail rather than by not listening
			 * for new ones.  Therefore, there is always a
			 * full set of UDP clients listening.
			 */
		}
		if (! need_another_client) {
516
517
518
			/*
			 * We don't need this client object.  Recycle it.
			 */
519
520
			if (client->newstate >= NS_CLIENTSTATE_INACTIVE)
				client->newstate = NS_CLIENTSTATE_INACTIVE;
521
		}
522
	}
523
}
524

525
526
527
void
ns_client_next(ns_client_t *client, isc_result_t result) {
	int newstate;
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
528
	
529
530
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
531

532
533
	CTRACE("next");
	
534
	if (result != ISC_R_SUCCESS)
535
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
			      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;
	(void) exit_check(client);
Bob Halley's avatar
add    
Bob Halley committed
552
553
}

554

Bob Halley's avatar
add    
Bob Halley committed
555
556
557
static void
client_senddone(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;
558
	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
Bob Halley's avatar
add    
Bob Halley committed
559

560
561
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
562
	REQUIRE(sevent != NULL);
563
564
	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
	client = sevent->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
565
566
567
568
569
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

	CTRACE("senddone");

570
571
572
573
574
575
	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
576
	INSIST(client->nsends > 0);
Bob Halley's avatar
add    
Bob Halley committed
577
	client->nsends--;
578
579
580
581
582
	
	if (client->tcpbuf != NULL) {
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add    
Bob Halley committed
583
584
585

	isc_event_free(&event);

586
	if (exit_check(client))
587
588
		return;

589
	ns_client_next(client, ISC_R_SUCCESS);
Bob Halley's avatar
add    
Bob Halley committed
590
591
}

Bob Halley's avatar
Bob Halley committed
592
593
void
ns_client_send(ns_client_t *client) {
Bob Halley's avatar
add    
Bob Halley committed
594
595
596
	isc_result_t result;
	unsigned char *data;
	isc_buffer_t buffer;
Bob Halley's avatar
Bob Halley committed
597
	isc_buffer_t tcpbuffer;
Bob Halley's avatar
add    
Bob Halley committed
598
	isc_region_t r;
Bob Halley's avatar
Bob Halley committed
599
600
	isc_socket_t *socket;
	isc_sockaddr_t *address;
601
	struct in6_pktinfo *pktinfo;
Bob Halley's avatar
EDNS0    
Bob Halley committed
602
	unsigned int bufsize = 512;
Bob Halley's avatar
add    
Bob Halley committed
603
604
605
606
607

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("send");

608
609
610
	if ((client->attributes & NS_CLIENTATTR_RA) != 0)
		client->message->flags |= DNS_MESSAGEFLAG_RA;
	
Bob Halley's avatar
Bob Halley committed
611
	/*
612
	 * XXXRTH  The following doesn't deal with TSIGs, TCP buffer resizing,
Bob Halley's avatar
EDNS0    
Bob Halley committed
613
	 *         or ENDS1 more data packets.
Bob Halley's avatar
Bob Halley committed
614
	 */
Bob Halley's avatar
Bob Halley committed
615
616
617
618
	if (TCP_CLIENT(client)) {
		/*
		 * XXXRTH  "tcpbuffer" is a hack to get things working.
		 */
619
620
621
622
623
624
		client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
		if (client->tcpbuf == NULL)
			goto done;
		data = client->tcpbuf;
		isc_buffer_init(&tcpbuffer, data, TCP_BUFFER_SIZE);
		isc_buffer_init(&buffer, data + 2, TCP_BUFFER_SIZE - 2);
Bob Halley's avatar
Bob Halley committed
625
	} else {
626
		data = client->sendbuf;
627
628
629
630
		if (client->udpsize < SEND_BUFFER_SIZE)
			bufsize = client->udpsize;
		else
			bufsize = SEND_BUFFER_SIZE;
631
		isc_buffer_init(&buffer, data, bufsize);
Bob Halley's avatar
Bob Halley committed
632
633
	}

Bob Halley's avatar
add    
Bob Halley committed
634
635
636
	result = dns_message_renderbegin(client->message, &buffer);
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
EDNS0    
Bob Halley committed
637
638
	if (client->opt != NULL) {
		result = dns_message_setopt(client->message, client->opt);
639
		if (result != ISC_R_SUCCESS) 
Bob Halley's avatar
EDNS0    
Bob Halley committed
640
			goto done;
Bob Halley's avatar
Bob Halley committed
641
642
643
644
		/*
		 * XXXRTH dns_message_setopt() should probably do this...
		 */
		client->opt = NULL;
Bob Halley's avatar
EDNS0    
Bob Halley committed
645
	}
Bob Halley's avatar
add    
Bob Halley committed
646
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
647
					   DNS_SECTION_QUESTION, 0);
Bob Halley's avatar
add    
Bob Halley committed
648
649
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
650
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
651
					   DNS_SECTION_ANSWER, 0);
Bob Halley's avatar
Bob Halley committed
652
653
654
655
	if (result == ISC_R_NOSPACE) {
		client->message->flags |= DNS_MESSAGEFLAG_TC;
		goto renderend;
	}
Bob Halley's avatar
Bob Halley committed
656
657
658
	if (result != ISC_R_SUCCESS)
		goto done;
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
659
					   DNS_SECTION_AUTHORITY, 0);
Bob Halley's avatar
Bob Halley committed
660
661
662
663
	if (result == ISC_R_NOSPACE) {
		client->message->flags |= DNS_MESSAGEFLAG_TC;
		goto renderend;
	}
Bob Halley's avatar
Bob Halley committed
664
665
666
	if (result != ISC_R_SUCCESS)
		goto done;
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
667
					   DNS_SECTION_ADDITIONAL, 0);
Bob Halley's avatar
Bob Halley committed
668
669
	if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
		goto done;
Bob Halley's avatar
Bob Halley committed
670
 renderend:
Bob Halley's avatar
add    
Bob Halley committed
671
	result = dns_message_renderend(client->message);
672

Bob Halley's avatar
add    
Bob Halley committed
673
674
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
675
676
677
678

	if (TCP_CLIENT(client)) {
		socket = client->tcpsocket;
		address = NULL;
679
		isc_buffer_usedregion(&buffer, &r);
680
		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
Bob Halley's avatar
Bob Halley committed
681
		isc_buffer_add(&tcpbuffer, r.length);
682
		isc_buffer_usedregion(&tcpbuffer, &r);
Bob Halley's avatar
Bob Halley committed
683
684
685
	} else {
		socket = dns_dispatch_getsocket(client->dispatch);
		address = &client->dispevent->addr;
686
		isc_buffer_usedregion(&buffer, &r);
Bob Halley's avatar
Bob Halley committed
687
	}
Bob Halley's avatar
add    
Bob Halley committed
688
	CTRACE("sendto");
689
690
691
692
	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0)
		pktinfo = &client->pktinfo;
	else
		pktinfo = NULL;
Bob Halley's avatar
Bob Halley committed
693
	result = isc_socket_sendto(socket, &r, client->task, client_senddone,
694
				   client, address, pktinfo);
695
	if (result == ISC_R_SUCCESS) {
Bob Halley's avatar
add    
Bob Halley committed
696
		client->nsends++;
697
698
		return;
	}
699

Bob Halley's avatar
add    
Bob Halley committed
700
 done:
701
702
703
704
	if (client->tcpbuf != NULL) {
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add    
Bob Halley committed
705
706
707
708
709
710
	ns_client_next(client, result);
}
	
void
ns_client_error(ns_client_t *client, isc_result_t result) {
	dns_rcode_t rcode;
711
	dns_message_t *message;
Bob Halley's avatar
add    
Bob Halley committed
712
713
714
715
716

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("error");

717
	message = client->message;
Bob Halley's avatar
add    
Bob Halley committed
718
719
	rcode = dns_result_torcode(result);

Bob Halley's avatar
Bob Halley committed
720
	/*
721
	 * message may be an in-progress reply that we had trouble
Bob Halley's avatar
Bob Halley committed
722
723
724
	 * with, in which case QR will be set.  We need to clear QR before
	 * calling dns_message_reply() to avoid triggering an assertion.
	 */
725
	message->flags &= ~DNS_MESSAGEFLAG_QR;
726
727
728
729
	/*
	 * AA and AD shouldn't be set.
	 */
	message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
730
	result = dns_message_reply(message, ISC_TRUE);
Bob Halley's avatar
add    
Bob Halley committed
731
	if (result != ISC_R_SUCCESS) {
732
733
734
735
736
737
738
739
740
741
		/*
		 * It could be that we've got a query with a good header,
		 * but a bad question section, so we try again with
		 * want_question_section set to ISC_FALSE.
		 */
		result = dns_message_reply(message, ISC_FALSE);
		if (result != ISC_R_SUCCESS) {
			ns_client_next(client, result);
			return;
		}
Bob Halley's avatar
add    
Bob Halley committed
742
	}
743
	message->rcode = rcode;
Bob Halley's avatar
Bob Halley committed
744
	ns_client_send(client);
Bob Halley's avatar
add    
Bob Halley committed
745
746
}

Bob Halley's avatar
EDNS0    
Bob Halley committed
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
static inline isc_result_t
client_addopt(ns_client_t *client) {
	dns_rdataset_t *rdataset;
	dns_rdatalist_t *rdatalist;
	dns_rdata_t *rdata;
	isc_result_t result;

	REQUIRE(client->opt == NULL);	/* XXXRTH free old. */

	rdatalist = NULL;
	result = dns_message_gettemprdatalist(client->message, &rdatalist);
	if (result != ISC_R_SUCCESS)
		return (result);
	rdata = NULL;
	result = dns_message_gettemprdata(client->message, &rdata);
	if (result != ISC_R_SUCCESS)
		return (result);
764
765
766
767
768
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
	if (result != ISC_R_SUCCESS)
		return (result);
	dns_rdataset_init(rdataset);
Bob Halley's avatar
EDNS0    
Bob Halley committed
769
770
771
772
773
774
775

	rdatalist->type = dns_rdatatype_opt;
	rdatalist->covers = 0;

	/*
	 * Set Maximum UDP buffer size.
	 */
776
	rdatalist->rdclass = RECV_BUFFER_SIZE;
Bob Halley's avatar
EDNS0    
Bob Halley committed
777
778
779
780
781
782
783
784
785
786
787

	/*
	 * Set EXTENDED-RCODE, VERSION, and Z to 0.
	 */
	rdatalist->ttl = 0;

	/*
	 * No ENDS options.
	 */
	rdata->data = NULL;
	rdata->length = 0;
788
789
	rdata->rdclass = rdatalist->rdclass;
	rdata->type = rdatalist->type;
Bob Halley's avatar
EDNS0    
Bob Halley committed
790
791
792
793
794
795
796
797
798
799

	ISC_LIST_INIT(rdatalist->rdata);
	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	dns_rdatalist_tordataset(rdatalist, rdataset);
	
	client->opt = rdataset;

	return (ISC_R_SUCCESS);
}

800
801
802
803
/*
 * Handle an incoming request event from the dispatch (UDP case)
 * or tcpmsg (TCP case).
 */
Bob Halley's avatar
add    
Bob Halley committed
804
static void
Bob Halley's avatar
Bob Halley committed
805
client_request(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
add    
Bob Halley committed
806
	ns_client_t *client;
Bob Halley's avatar
Bob Halley committed
807
	dns_dispatchevent_t *devent;
Bob Halley's avatar
add    
Bob Halley committed
808
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
809
	isc_buffer_t *buffer;
810
	dns_view_t *view;
Bob Halley's avatar
EDNS0    
Bob Halley committed
811
	dns_rdataset_t *opt;
812
	isc_boolean_t ra; 	/* Recursion available. */
Bob Halley's avatar
add    
Bob Halley committed
813

Bob Halley's avatar
Bob Halley committed
814
	REQUIRE(event != NULL);
815
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
816
817
818
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

819
820
	UNUSED(task);
	
821
	INSIST(client->recursionquota == NULL);
822

823
824
825
826
827
	INSIST(client->state ==
	       TCP_CLIENT(client) ?
	       NS_CLIENTSTATE_READING :
	       NS_CLIENTSTATE_READY);

828
829
	RWLOCK(&ns_g_server->conflock, isc_rwlocktype_read);
	dns_zonemgr_lockconf(ns_g_server->zonemgr, isc_rwlocktype_read);
830

831
	if (event->ev_type == DNS_EVENT_DISPATCH) {
832
		INSIST(!TCP_CLIENT(client));
Bob Halley's avatar
Bob Halley committed
833
834
835
836
837
		devent = (dns_dispatchevent_t *)event;
		REQUIRE(client->dispentry != NULL);
		client->dispevent = devent;
		buffer = &devent->buffer;
		result = devent->result;
838
		client->peeraddr = devent->addr;
839
		client->peeraddr_valid = ISC_TRUE;
840
		if ((devent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
841
842
843
844
845
			client->attributes |= NS_CLIENTATTR_PKTINFO;
			client->pktinfo = devent->pktinfo;
		} else {
			client->attributes &= ~NS_CLIENTATTR_PKTINFO;
		}
Michael Graff's avatar
Michael Graff committed
846
847
848
849
		if ((devent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
			client->attributes |= NS_CLIENTATTR_MULTICAST;
		else
			client->attributes &= ~NS_CLIENTATTR_MULTICAST;
Bob Halley's avatar
Bob Halley committed
850
	} else {
851
		INSIST(TCP_CLIENT(client));
852
853
		REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
		REQUIRE(event->ev_sender == &client->tcpmsg);
Bob Halley's avatar
Bob Halley committed
854
855
		buffer = &client->tcpmsg.buffer;
		result = client->tcpmsg.result;
856
		INSIST(client->nreads == 1);
857
858
859
		/*
		 * client->peeraddr was set when the connection was accepted.
		 */
860
		client->nreads--;
Bob Halley's avatar
Bob Halley committed
861
862
	}

863
	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
864
		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
865
866
		      "%s request",
		      TCP_CLIENT(client) ? "TCP" : "UDP");
Bob Halley's avatar
Bob Halley committed
867

868
	if (exit_check(client))
869
		goto cleanup_serverlock;
870
	client->state = client->newstate = NS_CLIENTSTATE_WORKING;
Michael Graff's avatar
Michael Graff committed
871

872
	isc_stdtime_get(&client->requesttime);
Bob Halley's avatar
Bob Halley committed
873
	client->now = client->requesttime;
Bob Halley's avatar
add    
Bob Halley committed
874

875
876
	set_timeout(client, 60);

Bob Halley's avatar
Bob Halley committed
877
878
879
880
881
	if (result != ISC_R_SUCCESS) {
		if (TCP_CLIENT(client))
			ns_client_next(client, result);
		else
			isc_task_shutdown(client->task);
882
		goto cleanup_serverlock;
Bob Halley's avatar
add    
Bob Halley committed
883
884
	}

Michael Graff's avatar
Michael Graff committed
885
886
887
	if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
888
889
			      "multicast request");
#if 0
Michael Graff's avatar
Michael Graff committed
890
		ns_client_error(client, DNS_R_REFUSED);
891
#endif
Michael Graff's avatar
Michael Graff committed
892
893
	}

894
	result = dns_message_parse(client->message, buffer, ISC_FALSE);
Bob Halley's avatar
add    
Bob Halley committed
895
896
	if (result != ISC_R_SUCCESS) {
		ns_client_error(client, result);
897
		goto cleanup_serverlock;
Bob Halley's avatar
add    
Bob Halley committed
898
	}
899
900
901
902
903
904
905
906
907

	/*
	 * We expect a query, not a response.  Unexpected UDP responses
	 * are discarded early by the dispatcher, but TCP responses
	 * bypass the dispatcher and must be discarded here.
	 */
	if ((client->message->flags & DNS_MESSAGEFLAG_QR) != 0) {
		CTRACE("unexpected response");
		ns_client_next(client, DNS_R_FORMERR);
908
		goto cleanup_serverlock;
909
	}
Bob Halley's avatar
add    
Bob Halley committed
910

Bob Halley's avatar
EDNS0    
Bob Halley committed
911
912
913
914
915
916
917
	/*
	 * Deal with EDNS.
	 */
	opt = dns_message_getopt(client->message);
	if (opt != NULL) {
		unsigned int version;

918
919
920
921
922
		/*
		 * Set the client's UDP buffer size.
		 */
		client->udpsize = opt->rdclass;

Bob Halley's avatar
EDNS0    
Bob Halley committed
923
924
925
926
927
928
		/*
		 * Create an OPT for our reply.
		 */
		result = client_addopt(client);
		if (result != ISC_R_SUCCESS) {
			ns_client_error(client, result);
929
			goto cleanup_serverlock;
Bob Halley's avatar
EDNS0    
Bob Halley committed
930
931
932
933
934
935
936
937
938
939
		}

		/*
		 * Do we understand this version of ENDS?
		 *
		 * XXXRTH need library support for this!
		 */
		version = (opt->ttl & 0x00FF0000) >> 16;
		if (version != 0) {
			ns_client_error(client, DNS_R_BADVERS);
940
			goto cleanup_serverlock;
Bob Halley's avatar
EDNS0    
Bob Halley committed
941
942
943
		}
	}

Brian Wellington's avatar
Brian Wellington committed
944
	/*
Andreas Gustafsson's avatar
Andreas Gustafsson committed
945
946
	 * Find a view that matches the client's source address.
	 * 
Brian Wellington's avatar
Brian Wellington committed
947
948
949
950
951
952
	 * XXXRTH  View list management code will be moving to its own module
	 *         soon.
	 */
	for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
	     view != NULL;
	     view = ISC_LIST_NEXT(view, link)) {
953
954
955
		if (client->message->rdclass == view->rdclass ||
		    client->message->rdclass == dns_rdataclass_any)
		{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
956
957
958
959
960
961
962
963
964
965
966
967
			isc_netaddr_t netaddr;
			int match;
			isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
			if (view->matchclients == NULL ||
			    (dns_acl_match(&netaddr, NULL, view->matchclients,
					   &ns_g_server->aclenv,
					   &match, NULL) == ISC_R_SUCCESS &&
			     match > 0))
			{
				dns_view_attach(view, &client->view);
				break;
			}
Brian Wellington's avatar
Brian Wellington committed
968
969
970
971
		}
	}

	if (view == NULL) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
972
973
974
		ns_client_log(client, NS_LOGCATEGORY_CLIENT,
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
			      "no matching view");
Brian Wellington's avatar
Brian Wellington committed
975
		ns_client_error(client, DNS_R_REFUSED);
976
		goto cleanup_serverlock;
Brian Wellington's avatar
Brian Wellington committed
977
978
	}

979
980
981
982
	ns_client_log(client, NS_LOGCATEGORY_CLIENT,
		      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
		      "using view '%s'", view->name);
	
983
984
985
986
987
	/*
	 * Lock the view's configuration data for reading.
	 * We must attach a separate view reference for this
	 * purpose instad of using client->view, because
	 * client->view may or may not be detached at the point
988
	 * when we return from this event handler depending
989
990
991
992
993
994
	 * on whether the request handler causes ns_client_next()
	 * to be called or not.
	 */
	dns_view_attach(client->view, &client->lockview);
	RWLOCK(&client->lockview->conflock, isc_rwlocktype_read);

995
996
997
998
999
1000
	/*
	 * Check for a signature.  We log bad signatures regardless of 
	 * whether they ultimately cause the request to be rejected or
	 * not.  We do not log the lack of a signature unless we are
	 * debugging.
	 */
Brian Wellington's avatar
Brian Wellington committed
1001
1002
1003
	result = dns_message_checksig(client->message, client->view);
	if (result != ISC_R_SUCCESS) {
		ns_client_error(client, result);
1004
		goto cleanup_viewlock;
Brian Wellington's avatar
Brian Wellington committed
1005
1006
	}

1007
	client->signer = NULL;
Brian Wellington's avatar
Brian Wellington committed
1008
	dns_name_init(&client->signername, NULL);
1009
	result = dns_message_signer(client->message, &client->signername);
1010
	if (result == ISC_R_SUCCESS) {
1011
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1012
1013
1014
			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
			      "request has valid signature");
		client->signer = &client->signername;
1015
	} else if (result == ISC_R_NOTFOUND) {
1016
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1017
1018
			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
			      "request is not signed");
Brian Wellington's avatar
Brian Wellington committed
1019
	} else if (result == DNS_R_NOIDENTITY) {
1020
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
Brian Wellington's avatar
Brian Wellington committed
1021
1022
			      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
			      "request is signed by a nonauthoritative key");
1023
1024
	} else {
		/* There is a signature, but it is bad. */
1025
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1026
1027
1028
1029
1030
			      NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
			      "request has invalid signature: %s",
			      isc_result_totext(result));
	}

1031
1032
1033
1034
1035
1036
	/*
	 * Decide whether recursive service is available to this client.
	 * We do this here rather than in the query code so that we can
	 * set the RA bit correctly on all kinds of responses, not just
	 * responses to ordinary queries.
	 */
1037
1038
1039
1040
1041
	ra = ISC_FALSE;
	if (client->view->resolver != NULL &&
	    client->view->recursion == ISC_TRUE &&
	    /* XXX this will log too much too early */
	    ns_client_checkacl(client, "recursion",
1042
			       client->view->recursionacl,
1043
			       ISC_TRUE, ISC_TRUE) == ISC_R_SUCCESS)
1044
		ra = ISC_TRUE;
1045

1046
1047
	if (ra == ISC_TRUE)
		client->attributes |= NS_CLIENTATTR_RA;
1048

Bob Halley's avatar
add    
Bob Halley committed
1049
1050
1051
1052
1053
1054
1055
1056
	/*
	 * Dispatch the request.
	 */
	switch (client->message->opcode) {
	case dns_opcode_query:
		CTRACE("query");
		ns_query_start(client);
		break;
1057
1058
1059
1060
1061
1062
	case dns_opcode_update:
		CTRACE("update");
		ns_update_start(client);
		break;
	case dns_opcode_notify:
		CTRACE("notify");
Mark Andrews's avatar
Mark Andrews committed
1063
		ns_notify_start(client);
1064
		break;
Bob Halley's avatar
add    
Bob Halley committed
1065
1066
	case dns_opcode_iquery:
		CTRACE("iquery");
1067
		ns_client_error(client, DNS_R_NOTIMP);
1068
		break;
Bob Halley's avatar
add    
Bob Halley committed
1069
1070
1071
1072
	default:
		CTRACE("unknown opcode");
		ns_client_error(client, DNS_R_NOTIMP);
	}
1073
1074
1075
1076
1077
1078
1079

 cleanup_viewlock:
	RWUNLOCK(&client->lockview->conflock, isc_rwlocktype_read);
	dns_view_detach(&client->lockview);
 cleanup_serverlock:
	dns_zonemgr_unlockconf(ns_g_server->zonemgr, isc_rwlocktype_read);
	RWUNLOCK(&ns_g_server->conflock, isc_rwlocktype_read);
Bob Halley's avatar
add    
Bob Halley committed
1080
1081
1082
1083
1084
1085
1086
}

static void
client_timeout(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
1087
1088
1089
	REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
		event->ev_type == ISC_TIMEREVENT_IDLE);
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
1090
1091
1092
1093
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);
	REQUIRE(client->timer != NULL);

1094
1095
	UNUSED(task);
	
Bob Halley's avatar
add    
Bob Halley committed
1096
1097
1098
1099
	CTRACE("timeout");

	isc_event_free(&event);

1100
1101
1102
1103
1104
1105
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}
	
1106
1107
1108
	if (client->newstate > NS_CLIENTSTATE_READY)
		client->newstate = NS_CLIENTSTATE_READY;
	(void) exit_check(client);
Bob Halley's avatar
add    
Bob Halley committed
1109
1110
1111
}

static isc_result_t
1112
client_create(ns_clientmgr_t *manager, ns_client_t **clientp)
Bob Halley's avatar
add    
Bob Halley committed
1113
1114
1115
1116
1117
1118
1119
{
	ns_client_t *client;
	isc_result_t result;

	/*
	 * Caller must be holding the manager lock.
	 *
1120
1121
1122
	 * Note: creating a client does not add the client to the 
	 * manager's client list or set the client's manager pointer.
	 * The caller is responsible for that.
Bob Halley's avatar
add    
Bob Halley committed
1123
1124
1125
1126
1127
1128
1129
1130
1131
	 */

	REQUIRE(clientp != NULL && *clientp == NULL);

	client = isc_mem_get(manager->mctx, sizeof *client);
	if (client == NULL)
		return (ISC_R_NOMEMORY);

	client->task = NULL;
Bob Halley's avatar
Bob Halley committed
1132
	result = isc_task_create(manager->taskmgr, 0, &client->task);
Bob Halley's avatar
add    
Bob Halley committed
1133
1134
	if (result != ISC_R_SUCCESS)
		goto cleanup_client;
Bob Halley's avatar
Bob Halley committed
1135
	isc_task_setname(client->task, "client", client);
Bob Halley's avatar
add    
Bob Halley committed
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
	result = isc_task_onshutdown(client->task, client_shutdown, client);
	if (result != ISC_R_SUCCESS)
		goto cleanup_task;

	client->timer = NULL;
	result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
				  NULL, NULL, client->task, client_timeout,
				  client, &client->timer);
	if (result != ISC_R_SUCCESS)
		goto cleanup_task;

	client->message = NULL;
	result = dns_message_create(manager->mctx, DNS_MESSAGE_INTENTPARSE,
				    &client->message);
	if (result != ISC_R_SUCCESS)
		goto cleanup_timer;

	/* XXXRTH  Hardwired constants */
1154
1155
	client->sendbuf = isc_mem_get(manager->mctx, SEND_BUFFER_SIZE);
	if  (client->sendbuf == NULL)
Bob Halley's avatar
add    
Bob Halley committed
1156
1157
		goto cleanup_message;

Bob Halley's avatar
Bob Halley committed
1158
	client->magic = NS_CLIENT_MAGIC;
Bob Halley's avatar
Bob Halley committed
1159
	client->mctx = manager->mctx;
1160
	client->manager = NULL;
1161
1162
	client->state = NS_CLIENTSTATE_INACTIVE;
	client->newstate = NS_CLIENTSTATE_MAX;
1163
1164
1165
	client->naccepts = 0;
	client->nreads = 0;
	client->nsends = 0;
1166
	client->references = 0;
Bob Halley's avatar
add    
Bob Halley committed
1167
	client->attributes = 0;
1168
	client->view = NULL;
1169
	client->lockview = NULL;
Bob Halley's avatar
add    
Bob Halley committed
1170
1171
1172
	client->dispatch = NULL;
	client->dispentry = NULL;
	client->dispevent = NULL;
Bob Halley's avatar
Bob Halley committed
1173
1174
	client->tcplistener = NULL;
	client->tcpsocket = NULL;
1175
	client->tcpmsg_valid = ISC_FALSE;
1176
	client->tcpbuf = NULL;
Bob Halley's avatar
EDNS0    
Bob Halley committed
1177
	client->opt = NULL;
1178
	client->udpsize = 512;
1179
	client->next = NULL;
1180
1181
	client->shutdown = NULL;
	client->shutdown_arg = NULL;
1182
	dns_name_init(&client->signername, NULL);
1183
	client->mortal = ISC_FALSE;
1184