1 /* $NetBSD: lwdclient.c,v 1.4 2014/12/10 04:37:51 christos Exp $ */
4 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000, 2001 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: lwdclient.c,v 1.22 2007/06/18 23:47:18 tbox Exp */
26 #include <isc/socket.h>
27 #include <isc/string.h>
35 #include <named/types.h>
36 #include <named/log.h>
37 #include <named/lwresd.h>
38 #include <named/lwdclient.h>
40 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
43 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
);
46 ns_lwdclient_log(int level
, const char *format
, ...) {
49 va_start(args
, format
);
50 isc_log_vwrite(dns_lctx
,
51 DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ADB
,
52 ISC_LOG_DEBUG(level
), format
, args
);
57 ns_lwdclientmgr_create(ns_lwreslistener_t
*listener
, unsigned int nclients
,
58 isc_taskmgr_t
*taskmgr
)
60 ns_lwresd_t
*lwresd
= listener
->manager
;
61 ns_lwdclientmgr_t
*cm
;
62 ns_lwdclient_t
*client
;
64 isc_result_t result
= ISC_R_FAILURE
;
66 cm
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclientmgr_t
));
68 return (ISC_R_NOMEMORY
);
71 ns_lwreslistener_attach(listener
, &cm
->listener
);
72 cm
->mctx
= lwresd
->mctx
;
74 isc_socket_attach(listener
->sock
, &cm
->sock
);
75 cm
->view
= lwresd
->view
;
79 ISC_LINK_INIT(cm
, link
);
80 ISC_LIST_INIT(cm
->idle
);
81 ISC_LIST_INIT(cm
->running
);
83 if (lwres_context_create(&cm
->lwctx
, cm
->mctx
,
84 ns__lwresd_memalloc
, ns__lwresd_memfree
,
85 LWRES_CONTEXT_SERVERMODE
)
89 for (i
= 0; i
< nclients
; i
++) {
90 client
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclient_t
));
92 ns_lwdclient_log(50, "created client %p, manager %p",
94 ns_lwdclient_initialize(client
, cm
);
99 * If we could create no clients, clean up and return.
101 if (ISC_LIST_EMPTY(cm
->idle
))
104 result
= isc_task_create(taskmgr
, 0, &cm
->task
);
105 if (result
!= ISC_R_SUCCESS
)
107 isc_task_setname(cm
->task
, "lwdclient", NULL
);
110 * This MUST be last, since there is no way to cancel an onshutdown...
112 result
= isc_task_onshutdown(cm
->task
, lwdclientmgr_shutdown_callback
,
114 if (result
!= ISC_R_SUCCESS
)
117 ns_lwreslistener_linkcm(listener
, cm
);
119 return (ISC_R_SUCCESS
);
122 client
= ISC_LIST_HEAD(cm
->idle
);
123 while (client
!= NULL
) {
124 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
125 isc_mem_put(lwresd
->mctx
, client
, sizeof(*client
));
126 client
= ISC_LIST_HEAD(cm
->idle
);
129 if (cm
->task
!= NULL
)
130 isc_task_detach(&cm
->task
);
132 if (cm
->lwctx
!= NULL
)
133 lwres_context_destroy(&cm
->lwctx
);
135 isc_mem_put(lwresd
->mctx
, cm
, sizeof(*cm
));
140 lwdclientmgr_destroy(ns_lwdclientmgr_t
*cm
) {
141 ns_lwdclient_t
*client
;
142 ns_lwreslistener_t
*listener
;
144 if (!SHUTTINGDOWN(cm
))
148 * run through the idle list and free the clients there. Idle
149 * clients do not have a recv running nor do they have any finds
150 * or similar running.
152 client
= ISC_LIST_HEAD(cm
->idle
);
153 while (client
!= NULL
) {
154 ns_lwdclient_log(50, "destroying client %p, manager %p",
156 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
157 isc_mem_put(cm
->mctx
, client
, sizeof(*client
));
158 client
= ISC_LIST_HEAD(cm
->idle
);
161 if (!ISC_LIST_EMPTY(cm
->running
))
164 lwres_context_destroy(&cm
->lwctx
);
166 isc_socket_detach(&cm
->sock
);
167 isc_task_detach(&cm
->task
);
169 listener
= cm
->listener
;
170 ns_lwreslistener_unlinkcm(listener
, cm
);
171 ns_lwdclient_log(50, "destroying manager %p", cm
);
172 isc_mem_put(cm
->mctx
, cm
, sizeof(*cm
));
173 ns_lwreslistener_detach(&listener
);
177 process_request(ns_lwdclient_t
*client
) {
181 lwres_buffer_init(&b
, client
->buffer
, client
->recvlength
);
182 lwres_buffer_add(&b
, client
->recvlength
);
184 result
= lwres_lwpacket_parseheader(&b
, &client
->pkt
);
185 if (result
!= ISC_R_SUCCESS
) {
186 ns_lwdclient_log(50, "invalid packet header received");
190 ns_lwdclient_log(50, "opcode %08x", client
->pkt
.opcode
);
192 switch (client
->pkt
.opcode
) {
193 case LWRES_OPCODE_GETADDRSBYNAME
:
194 ns_lwdclient_processgabn(client
, &b
);
196 case LWRES_OPCODE_GETNAMEBYADDR
:
197 ns_lwdclient_processgnba(client
, &b
);
199 case LWRES_OPCODE_GETRDATABYNAME
:
200 ns_lwdclient_processgrbn(client
, &b
);
202 case LWRES_OPCODE_NOOP
:
203 ns_lwdclient_processnoop(client
, &b
);
206 ns_lwdclient_log(50, "unknown opcode %08x", client
->pkt
.opcode
);
214 ns_lwdclient_log(50, "restarting client %p...", client
);
215 ns_lwdclient_stateidle(client
);
219 ns_lwdclient_recv(isc_task_t
*task
, isc_event_t
*ev
) {
221 ns_lwdclient_t
*client
= ev
->ev_arg
;
222 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
223 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
225 INSIST(dev
->region
.base
== client
->buffer
);
226 INSIST(NS_LWDCLIENT_ISRECV(client
));
228 NS_LWDCLIENT_SETRECVDONE(client
);
230 INSIST((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0);
231 cm
->flags
&= ~NS_LWDCLIENTMGR_FLAGRECVPENDING
;
234 "event received: task %p, length %u, result %u (%s)",
235 task
, dev
->n
, dev
->result
,
236 isc_result_totext(dev
->result
));
238 if (dev
->result
!= ISC_R_SUCCESS
) {
245 ns_lwdclient_stateidle(client
);
250 client
->recvlength
= dev
->n
;
251 client
->address
= dev
->address
;
252 if ((dev
->attributes
& ISC_SOCKEVENTATTR_PKTINFO
) != 0) {
253 client
->pktinfo
= dev
->pktinfo
;
254 client
->pktinfo_valid
= ISC_TRUE
;
256 client
->pktinfo_valid
= ISC_FALSE
;
260 result
= ns_lwdclient_startrecv(cm
);
261 if (result
!= ISC_R_SUCCESS
)
262 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
263 NS_LOGMODULE_LWRESD
, ISC_LOG_ERROR
,
264 "could not start lwres "
265 "client handler: %s",
266 isc_result_totext(result
));
268 process_request(client
);
272 * This function will start a new recv() on a socket for this client manager.
275 ns_lwdclient_startrecv(ns_lwdclientmgr_t
*cm
) {
276 ns_lwdclient_t
*client
;
280 if (SHUTTINGDOWN(cm
)) {
281 lwdclientmgr_destroy(cm
);
282 return (ISC_R_SUCCESS
);
286 * If a recv is already running, don't bother.
288 if ((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0)
289 return (ISC_R_SUCCESS
);
292 * If we have no idle slots, just return success.
294 client
= ISC_LIST_HEAD(cm
->idle
);
296 return (ISC_R_SUCCESS
);
297 INSIST(NS_LWDCLIENT_ISIDLE(client
));
300 * Issue the recv. If it fails, return that it did.
302 r
.base
= client
->buffer
;
303 r
.length
= LWRES_RECVLENGTH
;
304 result
= isc_socket_recv(cm
->sock
, &r
, 0, cm
->task
, ns_lwdclient_recv
,
306 if (result
!= ISC_R_SUCCESS
)
310 * Set the flag to say we've issued a recv() call.
312 cm
->flags
|= NS_LWDCLIENTMGR_FLAGRECVPENDING
;
315 * Remove the client from the idle list, and put it on the running
318 NS_LWDCLIENT_SETRECV(client
);
319 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
320 ISC_LIST_APPEND(cm
->running
, client
, link
);
322 return (ISC_R_SUCCESS
);
326 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
) {
327 ns_lwdclientmgr_t
*cm
= ev
->ev_arg
;
328 ns_lwdclient_t
*client
;
330 REQUIRE(!SHUTTINGDOWN(cm
));
332 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
336 * run through the idle list and free the clients there. Idle
337 * clients do not have a recv running nor do they have any finds
338 * or similar running.
340 client
= ISC_LIST_HEAD(cm
->idle
);
341 while (client
!= NULL
) {
342 ns_lwdclient_log(50, "destroying client %p, manager %p",
344 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
345 isc_mem_put(cm
->mctx
, client
, sizeof(*client
));
346 client
= ISC_LIST_HEAD(cm
->idle
);
350 * Cancel any pending I/O.
352 isc_socket_cancel(cm
->sock
, task
, ISC_SOCKCANCEL_ALL
);
355 * Run through the running client list and kill off any finds
358 client
= ISC_LIST_HEAD(cm
->running
);
359 while (client
!= NULL
) {
360 if (client
->find
!= client
->v4find
361 && client
->find
!= client
->v6find
)
362 dns_adb_cancelfind(client
->find
);
363 if (client
->v4find
!= NULL
)
364 dns_adb_cancelfind(client
->v4find
);
365 if (client
->v6find
!= NULL
)
366 dns_adb_cancelfind(client
->v6find
);
367 client
= ISC_LIST_NEXT(client
, link
);
370 cm
->flags
|= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN
;
376 * Do all the crap needed to move a client from the run queue to the idle
380 ns_lwdclient_stateidle(ns_lwdclient_t
*client
) {
381 ns_lwdclientmgr_t
*cm
;
384 cm
= client
->clientmgr
;
386 INSIST(client
->sendbuf
== NULL
);
387 INSIST(client
->sendlength
== 0);
388 INSIST(client
->arg
== NULL
);
389 INSIST(client
->v4find
== NULL
);
390 INSIST(client
->v6find
== NULL
);
392 ISC_LIST_UNLINK(cm
->running
, client
, link
);
393 ISC_LIST_PREPEND(cm
->idle
, client
, link
);
395 NS_LWDCLIENT_SETIDLE(client
);
397 result
= ns_lwdclient_startrecv(cm
);
398 if (result
!= ISC_R_SUCCESS
)
399 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
400 NS_LOGMODULE_LWRESD
, ISC_LOG_ERROR
,
401 "could not start lwres "
402 "client handler: %s",
403 isc_result_totext(result
));
407 ns_lwdclient_send(isc_task_t
*task
, isc_event_t
*ev
) {
408 ns_lwdclient_t
*client
= ev
->ev_arg
;
409 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
410 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
415 INSIST(NS_LWDCLIENT_ISSEND(client
));
416 INSIST(client
->sendbuf
== dev
->region
.base
);
418 ns_lwdclient_log(50, "task %p for client %p got send-done event",
421 if (client
->sendbuf
!= client
->buffer
)
422 lwres_context_freemem(cm
->lwctx
, client
->sendbuf
,
424 client
->sendbuf
= NULL
;
425 client
->sendlength
= 0;
427 ns_lwdclient_stateidle(client
);
433 ns_lwdclient_sendreply(ns_lwdclient_t
*client
, isc_region_t
*r
) {
434 struct in6_pktinfo
*pktinfo
;
435 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
437 if (client
->pktinfo_valid
)
438 pktinfo
= &client
->pktinfo
;
441 return (isc_socket_sendto(cm
->sock
, r
, cm
->task
, ns_lwdclient_send
,
442 client
, &client
->address
, pktinfo
));
446 ns_lwdclient_initialize(ns_lwdclient_t
*client
, ns_lwdclientmgr_t
*cmgr
) {
447 client
->clientmgr
= cmgr
;
448 ISC_LINK_INIT(client
, link
);
449 NS_LWDCLIENT_SETIDLE(client
);
452 client
->recvlength
= 0;
454 client
->sendbuf
= NULL
;
455 client
->sendlength
= 0;
458 client
->v4find
= NULL
;
459 client
->v6find
= NULL
;
460 client
->find_wanted
= 0;
463 client
->byaddr
= NULL
;
465 client
->lookup
= NULL
;
467 client
->pktinfo_valid
= ISC_FALSE
;
469 ISC_LIST_APPEND(cmgr
->idle
, client
, link
);