client.c 55.4 KB
Newer Older
Bob Halley's avatar
add    
Bob Halley committed
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 1999-2001  Internet Software Consortium.
3
 *
Bob Halley's avatar
add    
Bob Halley committed
4
5
6
 * 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.
7
 *
8
9
10
11
12
13
14
15
 * 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.
Bob Halley's avatar
add    
Bob Halley committed
16
17
 */

Brian Wellington's avatar
Brian Wellington committed
18
/* $Id: client.c,v 1.159 2001/03/12 22:27:14 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
19

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

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

29
#include <dns/db.h>
Bob Halley's avatar
add    
Bob Halley committed
30
31
32
#include <dns/dispatch.h>
#include <dns/events.h>
#include <dns/message.h>
33
#ifdef DNS_OPT_NEWCODES
34
#include <dns/opt.h>
35
#endif /* DNS_OPT_NEWCODES */
Bob Halley's avatar
EDNS0    
Bob Halley committed
36
#include <dns/rdata.h>
37
#include <dns/rdataclass.h>
Bob Halley's avatar
EDNS0    
Bob Halley committed
38
39
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
40
#include <dns/tsig.h>
41
#include <dns/view.h>
42
#include <dns/zone.h>
Bob Halley's avatar
add    
Bob Halley committed
43

44
#include <named/interfacemgr.h>
45
#include <named/log.h>
46
#include <named/notify.h>
47
#include <named/server.h>
48
#include <named/update.h>
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

/***
 *** 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
64
65
66

#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
67
#define CTRACE(m)	ns_client_log(client, \
68
69
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
70
				      ISC_LOG_DEBUG(3), \
71
				      "%s", (m))
72
73
74
#define MTRACE(m)	isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_CLIENT, \
Bob Halley's avatar
Bob Halley committed
75
				      ISC_LOG_DEBUG(3), \
76
				      "clientmgr @%p: %s", manager, (m))
77
78
79
#else
#define CTRACE(m)	((void)(m))
#define MTRACE(m)	((void)(m))
Bob Halley's avatar
add    
Bob Halley committed
80
81
#endif

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

84
#define TCP_BUFFER_SIZE			(65535 + 2)
85
86
#define SEND_BUFFER_SIZE		4096
#define RECV_BUFFER_SIZE		4096
Bob Halley's avatar
add    
Bob Halley committed
87
88
89
90
91
92
93
94
95
96

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;
97
	client_list_t			active; 	/* Active clients */
98
	client_list_t 			inactive;	/* To be recycled */
Bob Halley's avatar
add    
Bob Halley committed
99
100
101
102
103
104
};

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

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

117
118
119
120
#define NS_CLIENTSTATE_FREED    0
/*
 * The client object no longer exists.
 */
Bob Halley's avatar
add    
Bob Halley committed
121

122
123
#define NS_CLIENTSTATE_INACTIVE 1
/*
124
 * The client object exists and has a task and timer.
125
126
127
128
 * 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
129

130
#define NS_CLIENTSTATE_READY    2
Bob Halley's avatar
add    
Bob Halley committed
131
/*
132
133
134
 * 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
135
 *
136
 * If it is a TCP client object, it has a TCP listener socket
137
 * and an outstanding TCP listen request.
Bob Halley's avatar
add    
Bob Halley committed
138
 *
139
140
 * 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
141
142
 */

143
144
145
#define NS_CLIENTSTATE_READING  3
/*
 * The client object is a TCP client object that has received
146
 * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
147
148
149
150
151
152
153
154
 * 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,
155
 * recursion quota, and an outstanding write request.
156
 */
157
158
159
160
161
162
163
164
165
166
167

#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);
168
static void client_udprecv(ns_client_t *client);
169
170
171
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);
172
static void ns_client_checkactive(ns_client_t *client);
173
174
static void client_start(isc_task_t *task, isc_event_t *event);
static void client_request(isc_task_t *task, isc_event_t *event);
175

176
/*
177
178
179
180
 * Enter the inactive state.
 *
 * Requires:
 *	No requests are outstanding.
181
182
 */
static void
183
184
client_deactivate(ns_client_t *client) {
	REQUIRE(NS_CLIENT_VALID(client));
185

186
187
188
	if (client->interface)
		ns_interface_detach(&client->interface);

189
	INSIST(client->naccepts == 0);
190
	INSIST(client->recursionquota == NULL);
191
192
193
	if (client->tcplistener != NULL)
		isc_socket_detach(&client->tcplistener);

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

197
198
	if (client->dispatch != NULL)
		dns_dispatch_detach(&client->dispatch);
199
200

	client->attributes = 0;
201
	client->mortal = ISC_FALSE;
202
203
204
205
206
207

	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);
208
209
}

210
/*
211
212
213
 * Clean up a client object and free its memory.
 * Requires:
 *   The client is in the inactive state.
214
 */
215

216
static void
217
client_free(ns_client_t *client) {
218
	isc_boolean_t need_clientmgr_destroy = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
219
	ns_clientmgr_t *manager = NULL;
220

221
	REQUIRE(NS_CLIENT_VALID(client));
222
223
224
225
226
227
228

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

231
232
	INSIST(client->recursionquota == NULL);

Bob Halley's avatar
Bob Halley committed
233
	ns_query_free(client);
234
	isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
235
236
	isc_event_free((isc_event_t **)&client->sendevent);
	isc_event_free((isc_event_t **)&client->recvevent);
Bob Halley's avatar
add    
Bob Halley committed
237
	isc_timer_detach(&client->timer);
238

239
240
	if (client->tcpbuf != NULL)
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
241
242
243
244
245
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
246
247
248
249
250
251
252
253
254
#ifdef DNS_OPT_NEWCODES
	if (client->opt_zone != NULL) {
		isc_mem_put(client->mctx, client->opt_zone,
			    sizeof(*client->opt_zone));
		client->opt_zone = NULL;
	}
	if (client->opt_view != NULL)
		isc_buffer_free(&client->opt_view);
#endif /* DNS_OPT_NEWCODES */
255
	dns_message_destroy(&client->message);
256
	if (client->manager != NULL) {
257
		manager = client->manager;
258
		LOCK(&manager->lock);
259
260
261
		ISC_LIST_UNLINK(*client->list, client, link);
		client->list = NULL;
		if (manager->exiting &&
262
263
264
		    ISC_LIST_EMPTY(manager->active) &&
		    ISC_LIST_EMPTY(manager->inactive))
			need_clientmgr_destroy = ISC_TRUE;
265
		UNLOCK(&manager->lock);
Bob Halley's avatar
Bob Halley committed
266
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
267
268
269
270
271
272
273
	/*
	 * 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);
274

275
	CTRACE("free");
Bob Halley's avatar
add    
Bob Halley committed
276
	client->magic = 0;
Bob Halley's avatar
Bob Halley committed
277
	isc_mem_put(client->mctx, client, sizeof *client);
278

Bob Halley's avatar
add    
Bob Halley committed
279
	if (need_clientmgr_destroy)
280
		clientmgr_destroy(manager);
Bob Halley's avatar
add    
Bob Halley committed
281
282
}

283
284
void
ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
285
286
	isc_result_t result;
	isc_interval_t interval;
287

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

301
302
303
304
305
306
307
308
309
310
311
312
313
/*
 * 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. */

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

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
326
	 *
	 */
	if (client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
327
		dns_view_detach(&client->view);
328
329
330
331
332
333
334
335
336
337
338

	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
339
				socket = client->udpsocket;
340
341
342
			isc_socket_cancel(socket, client->task,
					  ISC_SOCKCANCEL_SEND);
		}
343

344
345
346
		if (! (client->nsends == 0 && client->nrecvs == 0 &&
		       client->references == 0))
		{
347
348
349
350
351
352
353
354
355
356
357
358
359
			/*
			 * 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;
360
		INSIST(client->recursionquota == NULL);
361
362
363
364
365
366
367
368
369
370
371
372
		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.
		 */
373
		INSIST(client->recursionquota == NULL);
374
375
376
377
378
379
380
381
382
383
384
385
		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;
		}
386
387
		if (client->tcpsocket != NULL) {
			CTRACE("closetcp");
388
			isc_socket_detach(&client->tcpsocket);
389
		}
390
391
392

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

394
395
396
397
398
399
		if (client->timerset) {
			(void) isc_timer_reset(client->timer,
					       isc_timertype_inactive,
					       NULL, NULL, ISC_TRUE);
			client->timerset = ISC_FALSE;
		}
400

401
402
		client->peeraddr_valid = ISC_FALSE;

403
		client->state = NS_CLIENTSTATE_READY;
404
		INSIST(client->recursionquota == NULL);
405
406
407
408
409
410
411
412

		/*
		 * 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);
413

414
415
416
		if (NS_CLIENTSTATE_READY == client->newstate) {
			if (TCP_CLIENT(client)) {
				client_accept(client);
417
418
			} else
				client_udprecv(client);
419
420
421
422
423
424
			client->newstate = NS_CLIENTSTATE_MAX;
			return (ISC_TRUE);
		}
	}

	if (client->state == NS_CLIENTSTATE_READY) {
425
		INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
426
427
428
429
430
431
		/*
		 * We are trying to enter the inactive state.
		 */
		if (client->naccepts > 0)
			isc_socket_cancel(client->tcplistener, client->task,
					  ISC_SOCKCANCEL_ACCEPT);
432

433
434
435
436
437
		if (! (client->naccepts == 0)) {
			/* Still waiting for accept cancel completion. */
			return (ISC_TRUE);
		}
		/* Accept cancel is complete. */
438
439
440
441
442
443
444
445
446
447

		if (client->nrecvs > 0)
			isc_socket_cancel(client->udpsocket, client->task,
					  ISC_SOCKCANCEL_RECV);
		if (! (client->nrecvs == 0)) {
			/* Still waiting for recv cancel completion. */
			return (ISC_TRUE);
		}
		/* Recv cancel is complete. */

448
449
		client_deactivate(client);
		client->state = NS_CLIENTSTATE_INACTIVE;
450
		INSIST(client->recursionquota == NULL);
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
		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);
}

469
470
471
472
473
474
475
476
477
/*
 * 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);
478

479
480
481
482
483
	UNUSED(task);

	if (TCP_CLIENT(client)) {
		client_accept(client);
	} else {
484
		client_udprecv(client);
485
486
487
488
	}
}


489
490
491
/*
 * The client's task has received a shutdown event.
 */
Bob Halley's avatar
add    
Bob Halley committed
492
493
494
495
496
static void
client_shutdown(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;

	REQUIRE(event != NULL);
497
498
	REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
499
500
501
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
502
503
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
504
505
506
507
	CTRACE("shutdown");

	isc_event_free(&event);

508
509
510
511
512
513
	if (client->shutdown != NULL) {
		(client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
		client->shutdown = NULL;
		client->shutdown_arg = NULL;
	}

514
	client->newstate = NS_CLIENTSTATE_FREED;
515
	(void)exit_check(client);
516
}
Bob Halley's avatar
add    
Bob Halley committed
517
518


519
520
static void
ns_client_endrequest(ns_client_t *client) {
521
522
523
	INSIST(client->naccepts == 0);
	INSIST(client->nreads == 0);
	INSIST(client->nsends == 0);
524
	INSIST(client->nrecvs == 0);
525
	INSIST(client->state == NS_CLIENTSTATE_WORKING);
Bob Halley's avatar
add    
Bob Halley committed
526

Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
527
528
	CTRACE("endrequest");

529
	if (client->next != NULL) {
530
		(client->next)(client);
531
532
533
		client->next = NULL;
	}

534
535
	if (client->view != NULL)
		dns_view_detach(&client->view);
Bob Halley's avatar
Bob Halley committed
536
537
538
539
540
	if (client->opt != NULL) {
		INSIST(dns_rdataset_isassociated(client->opt));
		dns_rdataset_disassociate(client->opt);
		dns_message_puttemprdataset(client->message, &client->opt);
	}
541

542
	client->udpsize = 512;
543
	client->extflags = 0;
Bob Halley's avatar
add    
Bob Halley committed
544
	dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
545

546
547
	if (client->recursionquota != NULL)
		isc_quota_detach(&client->recursionquota);
548
549

	/*
550
	 * Clear all client attributes that are specific to
551
552
553
	 * the request; that's all except the TCP flag.
	 */
	client->attributes &= NS_CLIENTATTR_TCP;
554
}
555

556
557
static void
ns_client_checkactive(ns_client_t *client) {
558
559
	if (client->mortal) {
		/*
560
		 * This client object should normally go inactive
561
		 * at this point, but if we have fewer active client
562
563
		 * objects than  desired due to earlier quota exhaustion,
		 * keep it active to make up for the shortage.
564
565
566
567
568
		 */
		isc_boolean_t need_another_client = ISC_FALSE;
		if (TCP_CLIENT(client)) {
			LOCK(&client->interface->lock);
			if (client->interface->ntcpcurrent <
569
			    client->interface->ntcptarget)
570
571
572
573
574
575
576
577
578
579
580
				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) {
581
582
583
			/*
			 * We don't need this client object.  Recycle it.
			 */
584
585
			if (client->newstate >= NS_CLIENTSTATE_INACTIVE)
				client->newstate = NS_CLIENTSTATE_INACTIVE;
586
		}
587
	}
588
}
589

590
591
592
void
ns_client_next(ns_client_t *client, isc_result_t result) {
	int newstate;
593

594
595
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
Andreas Gustafsson's avatar
style    
Andreas Gustafsson committed
596

597
	CTRACE("next");
598

599
	if (result != ISC_R_SUCCESS)
600
		ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
			      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
617
618
}

619

Bob Halley's avatar
add    
Bob Halley committed
620
621
622
static void
client_senddone(isc_task_t *task, isc_event_t *event) {
	ns_client_t *client;
623
	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
Bob Halley's avatar
add    
Bob Halley committed
624
625

	REQUIRE(sevent != NULL);
626
627
	REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
	client = sevent->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
628
629
	REQUIRE(NS_CLIENT_VALID(client));
	REQUIRE(task == client->task);
630
	REQUIRE(sevent == client->sendevent);
Bob Halley's avatar
add    
Bob Halley committed
631

632
633
	UNUSED(task);

Bob Halley's avatar
add    
Bob Halley committed
634
635
	CTRACE("senddone");

636
637
638
639
640
641
	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
642
	INSIST(client->nsends > 0);
Bob Halley's avatar
add    
Bob Halley committed
643
	client->nsends--;
644

645
	if (client->tcpbuf != NULL) {
646
		INSIST(TCP_CLIENT(client));
647
648
649
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
Bob Halley's avatar
add    
Bob Halley committed
650

651
	if (exit_check(client))
652
653
		return;

654
	ns_client_next(client, ISC_R_SUCCESS);
Bob Halley's avatar
add    
Bob Halley committed
655
656
}

657
658
659
660
661
662
663
664
/*
 * 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.
 */

665
666
667
static isc_result_t
client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
		    isc_buffer_t *tcpbuffer, isc_uint32_t length,
668
		    unsigned char *sendbuf, unsigned char **datap)
669
{
670
	unsigned char *data;
671
672
	isc_uint32_t bufsize;
	isc_result_t result;
673

674
	INSIST(datap != NULL);
675
676
	INSIST((tcpbuffer == NULL && length != 0) ||
	       (tcpbuffer != NULL && length == 0));
677
678
679

	if (TCP_CLIENT(client)) {
		INSIST(client->tcpbuf == NULL);
680
		if (length + 2 > TCP_BUFFER_SIZE) {
681
682
683
684
685
686
687
688
689
			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;
690
691
692
693
694
		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
695
696
			INSIST(length <= 0xffff);
			isc_buffer_putuint16(buffer, (isc_uint16_t)length);
697
		}
698
	} else {
699
		data = sendbuf;
700
701
702
703
		if (client->udpsize < SEND_BUFFER_SIZE)
			bufsize = client->udpsize;
		else
			bufsize = SEND_BUFFER_SIZE;
704
		if (length > bufsize) {
705
706
707
			result = ISC_R_NOSPACE;
			goto done;
		}
708
		isc_buffer_init(buffer, data, bufsize);
709
	}
710
711
	*datap = data;
	result = ISC_R_SUCCESS;
712

713
714
715
716
717
718
719
720
721
722
723
 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;
724
725
	isc_netaddr_t netaddr;
	int match;
726
	unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
727
728
729
730
731

	if (TCP_CLIENT(client)) {
		socket = client->tcpsocket;
		address = NULL;
	} else {
732
733
		socket = client->udpsocket;
		address = &client->peeraddr;
734
735
736
737
738
739
740
741

		isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
		if (ns_g_server->blackholeacl != NULL &&
		    dns_acl_match(&netaddr, NULL,
			    	  ns_g_server->blackholeacl,
				  NULL, &match, NULL) == ISC_R_SUCCESS &&
		    match > 0)
			return (DNS_R_BLACKHOLED);
742
		sockflags |= ISC_SOCKFLAG_NORETRY;
743
	}
744

745
746
747
748
	if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0)
		pktinfo = &client->pktinfo;
	else
		pktinfo = NULL;
749
750
751
752

	isc_buffer_usedregion(buffer, &r);

	CTRACE("sendto");
753
	
754
755
	result = isc_socket_sendto2(socket, &r, client->task,
				    address, pktinfo,
756
				    client->sendevent, sockflags);
757
	if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
758
		client->nsends++;
759
760
761
		if (result == ISC_R_SUCCESS)
			client_senddone(client->task,
					(isc_event_t *)client->sendevent);
762
		result = ISC_R_SUCCESS;
763
	}
764
765
766
767
768
769
770
771
772
773
	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;
774
	unsigned char sendbuf[SEND_BUFFER_SIZE];
775
776
777
778
779
780
781
782
783
784
785

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("sendraw");

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

786
787
	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
				     sendbuf, &data);
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
	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;
804
805
806
807
808
809
810
811
812

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

Bob Halley's avatar
Bob Halley committed
813
814
void
ns_client_send(ns_client_t *client) {
Bob Halley's avatar
add    
Bob Halley committed
815
816
817
	isc_result_t result;
	unsigned char *data;
	isc_buffer_t buffer;
Bob Halley's avatar
Bob Halley committed
818
	isc_buffer_t tcpbuffer;
Bob Halley's avatar
add    
Bob Halley committed
819
	isc_region_t r;
820
821
	dns_compress_t cctx;
	isc_boolean_t cleanup_cctx = ISC_FALSE;
822
	unsigned char sendbuf[SEND_BUFFER_SIZE];
Bob Halley's avatar
add    
Bob Halley committed
823
824
825
826
827

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("send");

828
829
	if ((client->attributes & NS_CLIENTATTR_RA) != 0)
		client->message->flags |= DNS_MESSAGEFLAG_RA;
830

Bob Halley's avatar
Bob Halley committed
831
	/*
Brian Wellington's avatar
Brian Wellington committed
832
	 * XXXRTH  The following doesn't deal with TCP buffer resizing.
Bob Halley's avatar
Bob Halley committed
833
	 */
834
835
	result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
				     sendbuf, &data);
836
837
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
838

839
840
841
842
843
844
	result = dns_compress_init(&cctx, -1, client->mctx);
	if (result != ISC_R_SUCCESS)
		goto done;
	cleanup_cctx = ISC_TRUE;

	result = dns_message_renderbegin(client->message, &cctx, &buffer);
Bob Halley's avatar
add    
Bob Halley committed
845
846
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
EDNS0    
Bob Halley committed
847
848
	if (client->opt != NULL) {
		result = dns_message_setopt(client->message, client->opt);
Bob Halley's avatar
Bob Halley committed
849
850
851
852
		/*
		 * XXXRTH dns_message_setopt() should probably do this...
		 */
		client->opt = NULL;
853
854
		if (result != ISC_R_SUCCESS)
			goto done;
Bob Halley's avatar
EDNS0    
Bob Halley committed
855
	}
Bob Halley's avatar
add    
Bob Halley committed
856
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
857
					   DNS_SECTION_QUESTION, 0);
858
859
860
861
	if (result == ISC_R_NOSPACE) {
		client->message->flags |= DNS_MESSAGEFLAG_TC;
		goto renderend;
	}
Bob Halley's avatar
add    
Bob Halley committed
862
863
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
864
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
865
					   DNS_SECTION_ANSWER, 0);
Bob Halley's avatar
Bob Halley committed
866
867
868
869
	if (result == ISC_R_NOSPACE) {
		client->message->flags |= DNS_MESSAGEFLAG_TC;
		goto renderend;
	}
Bob Halley's avatar
Bob Halley committed
870
871
872
	if (result != ISC_R_SUCCESS)
		goto done;
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
873
					   DNS_SECTION_AUTHORITY, 0);
Bob Halley's avatar
Bob Halley committed
874
875
876
877
	if (result == ISC_R_NOSPACE) {
		client->message->flags |= DNS_MESSAGEFLAG_TC;
		goto renderend;
	}
Bob Halley's avatar
Bob Halley committed
878
879
880
	if (result != ISC_R_SUCCESS)
		goto done;
	result = dns_message_rendersection(client->message,
Michael Graff's avatar
Michael Graff committed
881
					   DNS_SECTION_ADDITIONAL, 0);
Bob Halley's avatar
Bob Halley committed
882
883
	if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
		goto done;
Bob Halley's avatar
Bob Halley committed
884
 renderend:
Bob Halley's avatar
add    
Bob Halley committed
885
	result = dns_message_renderend(client->message);
886

Bob Halley's avatar
add    
Bob Halley committed
887
888
	if (result != ISC_R_SUCCESS)
		goto done;
Bob Halley's avatar
Bob Halley committed
889

890
891
892
893
894
	if (cleanup_cctx) {
		dns_compress_invalidate(&cctx);
		cleanup_cctx = ISC_FALSE;
	}

Bob Halley's avatar
Bob Halley committed
895
	if (TCP_CLIENT(client)) {
896
		isc_buffer_usedregion(&buffer, &r);
897
		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
Bob Halley's avatar
Bob Halley committed
898
		isc_buffer_add(&tcpbuffer, r.length);
899
900
901
902
		result = client_sendpkg(client, &tcpbuffer);
	} else
		result = client_sendpkg(client, &buffer);
	if (result == ISC_R_SUCCESS)
903
		return;
904

Bob Halley's avatar
add    
Bob Halley committed
905
 done:
906
907
908
909
	if (client->tcpbuf != NULL) {
		isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
		client->tcpbuf = NULL;
	}
910
911
912
913

	if (cleanup_cctx)
		dns_compress_invalidate(&cctx);

Bob Halley's avatar
add    
Bob Halley committed
914
915
	ns_client_next(client, result);
}
916

Bob Halley's avatar
add    
Bob Halley committed
917
918
919
void
ns_client_error(ns_client_t *client, isc_result_t result) {
	dns_rcode_t rcode;
920
	dns_message_t *message;
Bob Halley's avatar
add    
Bob Halley committed
921
922
923
924
925

	REQUIRE(NS_CLIENT_VALID(client));

	CTRACE("error");

926
	message = client->message;
Bob Halley's avatar
add    
Bob Halley committed
927
928
	rcode = dns_result_torcode(result);

Bob Halley's avatar
Bob Halley committed
929
	/*
David Lawrence's avatar
David Lawrence committed
930
	 * Message may be an in-progress reply that we had trouble
Bob Halley's avatar
Bob Halley committed
931
932
933
	 * with, in which case QR will be set.  We need to clear QR before
	 * calling dns_message_reply() to avoid triggering an assertion.
	 */
934
	message->flags &= ~DNS_MESSAGEFLAG_QR;
935
936
937
938
	/*
	 * AA and AD shouldn't be set.
	 */
	message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
939
	result = dns_message_reply(message, ISC_TRUE);
Bob Halley's avatar
add    
Bob Halley committed
940
	if (result != ISC_R_SUCCESS) {
941
942
943
944
945
946
947
948
949
950
		/*
		 * 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
951
	}
952
	message->rcode = rcode;
Bob Halley's avatar
Bob Halley committed
953
	ns_client_send(client);
Bob Halley's avatar
add    
Bob Halley committed
954
955
}

956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
#ifdef DNS_OPT_NEWCODES
static isc_result_t
client_addoptattrs(ns_client_t *client, dns_rdata_t *rdata) {
	isc_result_t result;
	isc_buffer_t *zonebuf = NULL, *buf = NULL;
	dns_optlist_t attrlist;
	dns_optattr_t attrs[CLIENT_NUMATTRS];
	dns_compress_t cctx;
	int i, sizeneeded = 0;

	result = dns_compress_init(&cctx, 0, client->mctx);
	if (result != ISC_R_SUCCESS)
		goto fail1;
	dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
	attrlist.size=2;
	attrlist.used=0;
	attrlist.attrs=attrs;
	for (i=0; i<CLIENT_NUMATTRS; i++) {
		attrs[i].code = 0;
		attrs[i].value.base = NULL;
		attrs[i].value.length = 0;
	}
	if (client->opt_zone != NULL) {
		result = isc_buffer_allocate(client->mctx, &zonebuf,
					     DNS_NAME_MAXWIRE);
		if (result != ISC_R_SUCCESS)
			goto fail2;
		result = dns_name_towire(dns_fixedname_name(client->opt_zone),
					 &cctx, zonebuf);
		if (result != ISC_R_SUCCESS)
			goto fail2;
		attrs[attrlist.used].code = DNS_OPTCODE_ZONE;
		attrs[attrlist.used].value.base = isc_buffer_base(zonebuf);
		attrs[attrlist.used].value.length = 
			isc_buffer_usedlength(zonebuf);
		attrlist.used++;
		sizeneeded += 4 + isc_buffer_usedlength(zonebuf);
	}
	if (client->opt_view != NULL) {
		attrs[attrlist.used].code = DNS_OPTCODE_VIEW;
		attrs[attrlist.used].value.base =
			isc_buffer_base(client->opt_view);
		attrs[attrlist.used].value.length = 
			isc_buffer_usedlength(client->opt_view);
		attrlist.used++;
		sizeneeded += 4 + isc_buffer_usedlength(client->opt_view);
	}
	if (sizeneeded == 0) {
		result = ISC_R_SUCCESS;
		goto fail2;
	}
	result = isc_buffer_allocate(client->mctx, &buf, sizeneeded+1);
	if (result != ISC_R_SUCCESS)
		goto fail2;
	result = dns_opt_add(rdata, &attrlist, buf);
	if (result != ISC_R_SUCCESS)
		goto fail2;
	dns_message_takebuffer(client->message, &buf);
 fail2:
	dns_compress_invalidate(&cctx);
 fail1:
	if (buf != NULL)
		isc_buffer_free(&buf);
	if (zonebuf != NULL)
		isc_buffer_free(&zonebuf);
	return (result);
}
#endif /* DNS_OPT_NEWCODES */
	
Bob Halley's avatar
EDNS0    
Bob Halley committed
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
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);
1042
1043
1044
1045
1046
	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
1047
1048
1049
1050
1051

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

	/*
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1052
	 * Set the maximum UDP buffer size.
Bob Halley's avatar
EDNS0    
Bob Halley committed
1053
	 */
1054
	rdatalist->rdclass = RECV_BUFFER_SIZE;
Bob Halley's avatar
EDNS0    
Bob Halley committed
1055
1056
1057
1058
1059
1060
1061

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

	/*
1062
	 * No ENDS options in the default case.
Bob Halley's avatar
EDNS0    
Bob Halley committed
1063
1064
1065
	 */
	rdata->data = NULL;
	rdata->length = 0;
1066
1067
	rdata->rdclass = rdatalist->rdclass;
	rdata->type = rdatalist->type;
1068
	rdata->flags = 0;
Bob Halley's avatar
EDNS0    
Bob Halley committed
1069

1070
1071
1072
1073
1074
1075
1076
#ifdef DNS_OPT_NEWCODES
	/*
	 * Set the attributes
	 */
	client_addoptattrs(client, rdata);
#endif /* DNS_OPT_NEWCODES */

Bob Halley's avatar
EDNS0    
Bob Halley committed
1077
1078
1079
	ISC_LIST_INIT(rdatalist->rdata);
	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	dns_rdatalist_tordataset(rdatalist, rdataset);
1080

Bob Halley's avatar
EDNS0    
Bob Halley committed
1081
1082
1083
1084
1085
	client->opt = rdataset;

	return (ISC_R_SUCCESS);
}

1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
#ifdef DNS_OPT_NEWCODES
static void
client_getoptattrs(ns_client_t *client, dns_rdataset_t *opt) {
	dns_optlist_t optlist;
	dns_optattr_t optattr;
	isc_result_t result, iresult;
	isc_buffer_t zonebuf;
	dns_decompress_t dctx;

	/* If an old set of opts are still around, free them. */
	if (client->opt_zone != NULL) {
		isc_mem_put(client->mctx, client->opt_zone,
			    sizeof(*client->opt_zone));
		client->opt_zone = NULL;
	}
	if (client->opt_view != NULL)
		isc_buffer_free(&client->opt_view);

	/*
	 * If there are any options, extract them here
	 */
	optlist.size = 1;
	optlist.used = 0;
	optlist.next = 0;
	optlist.attrs = &optattr;
	do {
		result = dns_opt_decodeall(&optlist, opt);
		if (result == ISC_R_SUCCESS ||
		    result == DNS_R_MOREDATA) {
			switch (optattr.code) {
			case DNS_OPTCODE_ZONE:
				dns_decompress_init(&dctx, 0,
1118
						    DNS_DECOMPRESS_NONE);
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
				client->opt_zone = isc_mem_get(
					   client->mctx,
					   sizeof(*client->opt_zone));
				if (client->opt_zone == NULL)
					goto zonefail1;
				dns_fixedname_init(client->opt_zone);
				isc_buffer_init(&zonebuf,
						optattr.value.base,
						optattr.value.length);
				isc_buffer_add(&zonebuf,optattr.value.length);
				isc_buffer_setactive(&zonebuf,
						     optattr.value.length);
				iresult = dns_name_fromwire(
					   dns_fixedname_name(
						     client->opt_zone),
					   &zonebuf,
					   &dctx, ISC_FALSE,
					   NULL);
				if (iresult != ISC_R_SUCCESS) {
					dns_fixedname_invalidate(
						    client->opt_zone);
				zonefail1:
					dns_decompress_invalidate(&dctx);
				}
				break;
			case DNS_OPTCODE_VIEW:
				iresult = isc_buffer_allocate(client->mctx,
						    &client->opt_view,
						    optattr.value.length);
				if (iresult != ISC_R_SUCCESS)
					break;
				isc_buffer_putmem(client->opt_view,
						  optattr.value.base,
						  optattr.value.length);
				break;
			}
		}
	} while (result == DNS_R_MOREDATA);
}
#endif /* DNS_OPT_NEWCODES */


1161
/*
1162
 * Handle an incoming request event from the socket (UDP case)
1163
1164
 * or tcpmsg (TCP case).
 */
Bob Halley's avatar
add    
Bob Halley committed
1165
static void
Bob Halley's avatar
Bob Halley committed
1166
client_request(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
add    
Bob Halley committed
1167
	ns_client_t *client;
1168
	isc_socketevent_t *sevent;
Bob Halley's avatar
add    
Bob Halley committed
1169
	isc_result_t result;
1170
	isc_result_t sigresult;
Bob Halley's avatar
Bob Halley committed
1171
	isc_buffer_t *buffer;
1172
	isc_buffer_t tbuffer;
1173
	dns_view_t *view;
Bob Halley's avatar
EDNS0    
Bob Halley committed
1174
	dns_rdataset_t *opt;
1175
	isc_boolean_t ra; 	/* Recursion available. */
Bob Halley's avatar
add    
Bob Halley committed
1176

Bob Halley's avatar
Bob Halley committed
1177
	REQUIRE(event != NULL);
1178
	client = event->ev_arg;
Bob Halley's avatar
add    
Bob Halley committed
1179
1180
1181
	REQUIRE(NS_CLIENT_VALID(client));