2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: lwdclient.c,v 1.13.2.1 2004/03/09 06:09:18 marka Exp $ */
22 #include <isc/socket.h>
23 #include <isc/string.h>
31 #include <named/types.h>
32 #include <named/lwresd.h>
33 #include <named/lwdclient.h>
35 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
38 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
);
41 ns_lwdclient_log(int level
, const char *format
, ...) {
44 va_start(args
, format
);
45 isc_log_vwrite(dns_lctx
,
46 DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ADB
,
47 ISC_LOG_DEBUG(level
), format
, args
);
52 ns_lwdclientmgr_create(ns_lwreslistener_t
*listener
, unsigned int nclients
,
53 isc_taskmgr_t
*taskmgr
)
55 ns_lwresd_t
*lwresd
= listener
->manager
;
56 ns_lwdclientmgr_t
*cm
;
57 ns_lwdclient_t
*client
;
59 isc_result_t result
= ISC_R_FAILURE
;
61 cm
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclientmgr_t
));
63 return (ISC_R_NOMEMORY
);
66 ns_lwreslistener_attach(listener
, &cm
->listener
);
67 cm
->mctx
= lwresd
->mctx
;
69 isc_socket_attach(listener
->sock
, &cm
->sock
);
70 cm
->view
= lwresd
->view
;
74 ISC_LINK_INIT(cm
, link
);
75 ISC_LIST_INIT(cm
->idle
);
76 ISC_LIST_INIT(cm
->running
);
78 if (lwres_context_create(&cm
->lwctx
, cm
->mctx
,
79 ns__lwresd_memalloc
, ns__lwresd_memfree
,
80 LWRES_CONTEXT_SERVERMODE
)
84 for (i
= 0 ; i
< nclients
; i
++) {
85 client
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclient_t
));
87 ns_lwdclient_log(50, "created client %p, manager %p",
89 ns_lwdclient_initialize(client
, cm
);
94 * If we could create no clients, clean up and return.
96 if (ISC_LIST_EMPTY(cm
->idle
))
99 result
= isc_task_create(taskmgr
, 0, &cm
->task
);
100 if (result
!= ISC_R_SUCCESS
)
104 * This MUST be last, since there is no way to cancel an onshutdown...
106 result
= isc_task_onshutdown(cm
->task
, lwdclientmgr_shutdown_callback
,
108 if (result
!= ISC_R_SUCCESS
)
111 ns_lwreslistener_linkcm(listener
, cm
);
113 return (ISC_R_SUCCESS
);
116 client
= ISC_LIST_HEAD(cm
->idle
);
117 while (client
!= NULL
) {
118 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
119 isc_mem_put(lwresd
->mctx
, client
, sizeof (*client
));
120 client
= ISC_LIST_HEAD(cm
->idle
);
123 if (cm
->task
!= NULL
)
124 isc_task_detach(&cm
->task
);
126 if (cm
->lwctx
!= NULL
)
127 lwres_context_destroy(&cm
->lwctx
);
129 isc_mem_put(lwresd
->mctx
, cm
, sizeof (*cm
));
134 lwdclientmgr_destroy(ns_lwdclientmgr_t
*cm
) {
135 ns_lwdclient_t
*client
;
136 ns_lwreslistener_t
*listener
;
138 if (!SHUTTINGDOWN(cm
))
142 * run through the idle list and free the clients there. Idle
143 * clients do not have a recv running nor do they have any finds
144 * or similar running.
146 client
= ISC_LIST_HEAD(cm
->idle
);
147 while (client
!= NULL
) {
148 ns_lwdclient_log(50, "destroying client %p, manager %p",
150 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
151 isc_mem_put(cm
->mctx
, client
, sizeof (*client
));
152 client
= ISC_LIST_HEAD(cm
->idle
);
155 if (!ISC_LIST_EMPTY(cm
->running
))
158 lwres_context_destroy(&cm
->lwctx
);
160 isc_socket_detach(&cm
->sock
);
161 isc_task_detach(&cm
->task
);
163 listener
= cm
->listener
;
164 ns_lwreslistener_unlinkcm(listener
, cm
);
165 ns_lwdclient_log(50, "destroying manager %p", cm
);
166 isc_mem_put(cm
->mctx
, cm
, sizeof (*cm
));
167 ns_lwreslistener_detach(&listener
);
171 process_request(ns_lwdclient_t
*client
) {
175 lwres_buffer_init(&b
, client
->buffer
, client
->recvlength
);
176 lwres_buffer_add(&b
, client
->recvlength
);
178 result
= lwres_lwpacket_parseheader(&b
, &client
->pkt
);
179 if (result
!= ISC_R_SUCCESS
) {
180 ns_lwdclient_log(50, "invalid packet header received");
184 ns_lwdclient_log(50, "opcode %08x", client
->pkt
.opcode
);
186 switch (client
->pkt
.opcode
) {
187 case LWRES_OPCODE_GETADDRSBYNAME
:
188 ns_lwdclient_processgabn(client
, &b
);
190 case LWRES_OPCODE_GETNAMEBYADDR
:
191 ns_lwdclient_processgnba(client
, &b
);
193 case LWRES_OPCODE_GETRDATABYNAME
:
194 ns_lwdclient_processgrbn(client
, &b
);
196 case LWRES_OPCODE_NOOP
:
197 ns_lwdclient_processnoop(client
, &b
);
200 ns_lwdclient_log(50, "unknown opcode %08x", client
->pkt
.opcode
);
208 ns_lwdclient_log(50, "restarting client %p...", client
);
209 ns_lwdclient_stateidle(client
);
213 ns_lwdclient_recv(isc_task_t
*task
, isc_event_t
*ev
) {
214 ns_lwdclient_t
*client
= ev
->ev_arg
;
215 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
216 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
218 INSIST(dev
->region
.base
== client
->buffer
);
219 INSIST(NS_LWDCLIENT_ISRECV(client
));
221 NS_LWDCLIENT_SETRECVDONE(client
);
223 INSIST((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0);
224 cm
->flags
&= ~NS_LWDCLIENTMGR_FLAGRECVPENDING
;
227 "event received: task %p, length %u, result %u (%s)",
228 task
, dev
->n
, dev
->result
,
229 isc_result_totext(dev
->result
));
231 if (dev
->result
!= ISC_R_SUCCESS
) {
238 ns_lwdclient_stateidle(client
);
243 client
->recvlength
= dev
->n
;
244 client
->address
= dev
->address
;
245 if ((dev
->attributes
& ISC_SOCKEVENTATTR_PKTINFO
) != 0) {
246 client
->pktinfo
= dev
->pktinfo
;
247 client
->pktinfo_valid
= ISC_TRUE
;
249 client
->pktinfo_valid
= ISC_FALSE
;
253 ns_lwdclient_startrecv(cm
);
255 process_request(client
);
259 * This function will start a new recv() on a socket for this client manager.
262 ns_lwdclient_startrecv(ns_lwdclientmgr_t
*cm
) {
263 ns_lwdclient_t
*client
;
267 if (SHUTTINGDOWN(cm
)) {
268 lwdclientmgr_destroy(cm
);
269 return (ISC_R_SUCCESS
);
273 * If a recv is already running, don't bother.
275 if ((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0)
276 return (ISC_R_SUCCESS
);
279 * If we have no idle slots, just return success.
281 client
= ISC_LIST_HEAD(cm
->idle
);
283 return (ISC_R_SUCCESS
);
284 INSIST(NS_LWDCLIENT_ISIDLE(client
));
287 * Issue the recv. If it fails, return that it did.
289 r
.base
= client
->buffer
;
290 r
.length
= LWRES_RECVLENGTH
;
291 result
= isc_socket_recv(cm
->sock
, &r
, 0, cm
->task
, ns_lwdclient_recv
,
293 if (result
!= ISC_R_SUCCESS
)
297 * Set the flag to say we've issued a recv() call.
299 cm
->flags
|= NS_LWDCLIENTMGR_FLAGRECVPENDING
;
302 * Remove the client from the idle list, and put it on the running
305 NS_LWDCLIENT_SETRECV(client
);
306 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
307 ISC_LIST_APPEND(cm
->running
, client
, link
);
309 return (ISC_R_SUCCESS
);
313 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
) {
314 ns_lwdclientmgr_t
*cm
= ev
->ev_arg
;
315 ns_lwdclient_t
*client
;
317 REQUIRE(!SHUTTINGDOWN(cm
));
319 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
323 * run through the idle list and free the clients there. Idle
324 * clients do not have a recv running nor do they have any finds
325 * or similar running.
327 client
= ISC_LIST_HEAD(cm
->idle
);
328 while (client
!= NULL
) {
329 ns_lwdclient_log(50, "destroying client %p, manager %p",
331 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
332 isc_mem_put(cm
->mctx
, client
, sizeof (*client
));
333 client
= ISC_LIST_HEAD(cm
->idle
);
337 * Cancel any pending I/O.
339 isc_socket_cancel(cm
->sock
, task
, ISC_SOCKCANCEL_ALL
);
342 * Run through the running client list and kill off any finds
345 client
= ISC_LIST_HEAD(cm
->running
);
346 while (client
!= NULL
) {
347 if (client
->find
!= client
->v4find
348 && client
->find
!= client
->v6find
)
349 dns_adb_cancelfind(client
->find
);
350 if (client
->v4find
!= NULL
)
351 dns_adb_cancelfind(client
->v4find
);
352 if (client
->v6find
!= NULL
)
353 dns_adb_cancelfind(client
->v6find
);
354 client
= ISC_LIST_NEXT(client
, link
);
357 cm
->flags
|= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN
;
363 * Do all the crap needed to move a client from the run queue to the idle
367 ns_lwdclient_stateidle(ns_lwdclient_t
*client
) {
368 ns_lwdclientmgr_t
*cm
;
370 cm
= client
->clientmgr
;
372 INSIST(client
->sendbuf
== NULL
);
373 INSIST(client
->sendlength
== 0);
374 INSIST(client
->arg
== NULL
);
375 INSIST(client
->v4find
== NULL
);
376 INSIST(client
->v6find
== NULL
);
378 ISC_LIST_UNLINK(cm
->running
, client
, link
);
379 ISC_LIST_PREPEND(cm
->idle
, client
, link
);
381 NS_LWDCLIENT_SETIDLE(client
);
383 ns_lwdclient_startrecv(cm
);
387 ns_lwdclient_send(isc_task_t
*task
, isc_event_t
*ev
) {
388 ns_lwdclient_t
*client
= ev
->ev_arg
;
389 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
390 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
395 INSIST(NS_LWDCLIENT_ISSEND(client
));
396 INSIST(client
->sendbuf
== dev
->region
.base
);
398 ns_lwdclient_log(50, "task %p for client %p got send-done event",
401 if (client
->sendbuf
!= client
->buffer
)
402 lwres_context_freemem(cm
->lwctx
, client
->sendbuf
,
404 client
->sendbuf
= NULL
;
405 client
->sendlength
= 0;
407 ns_lwdclient_stateidle(client
);
413 ns_lwdclient_sendreply(ns_lwdclient_t
*client
, isc_region_t
*r
) {
414 struct in6_pktinfo
*pktinfo
;
415 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
417 if (client
->pktinfo_valid
)
418 pktinfo
= &client
->pktinfo
;
421 return (isc_socket_sendto(cm
->sock
, r
, cm
->task
, ns_lwdclient_send
,
422 client
, &client
->address
, pktinfo
));
426 ns_lwdclient_initialize(ns_lwdclient_t
*client
, ns_lwdclientmgr_t
*cmgr
) {
427 client
->clientmgr
= cmgr
;
428 ISC_LINK_INIT(client
, link
);
429 NS_LWDCLIENT_SETIDLE(client
);
432 client
->recvlength
= 0;
434 client
->sendbuf
= NULL
;
435 client
->sendlength
= 0;
438 client
->v4find
= NULL
;
439 client
->v6find
= NULL
;
440 client
->find_wanted
= 0;
443 client
->byaddr
= NULL
;
445 client
->lookup
= NULL
;
447 client
->pktinfo_valid
= ISC_FALSE
;
449 ISC_LIST_APPEND(cmgr
->idle
, client
, link
);