2 * Copyright (C) 2004, 2005 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.17.18.2 2005/04/29 00:15:23 marka Exp $ */
24 #include <isc/socket.h>
25 #include <isc/string.h>
33 #include <named/types.h>
34 #include <named/log.h>
35 #include <named/lwresd.h>
36 #include <named/lwdclient.h>
38 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
41 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
);
44 ns_lwdclient_log(int level
, const char *format
, ...) {
47 va_start(args
, format
);
48 isc_log_vwrite(dns_lctx
,
49 DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ADB
,
50 ISC_LOG_DEBUG(level
), format
, args
);
55 ns_lwdclientmgr_create(ns_lwreslistener_t
*listener
, unsigned int nclients
,
56 isc_taskmgr_t
*taskmgr
)
58 ns_lwresd_t
*lwresd
= listener
->manager
;
59 ns_lwdclientmgr_t
*cm
;
60 ns_lwdclient_t
*client
;
62 isc_result_t result
= ISC_R_FAILURE
;
64 cm
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclientmgr_t
));
66 return (ISC_R_NOMEMORY
);
69 ns_lwreslistener_attach(listener
, &cm
->listener
);
70 cm
->mctx
= lwresd
->mctx
;
72 isc_socket_attach(listener
->sock
, &cm
->sock
);
73 cm
->view
= lwresd
->view
;
77 ISC_LINK_INIT(cm
, link
);
78 ISC_LIST_INIT(cm
->idle
);
79 ISC_LIST_INIT(cm
->running
);
81 if (lwres_context_create(&cm
->lwctx
, cm
->mctx
,
82 ns__lwresd_memalloc
, ns__lwresd_memfree
,
83 LWRES_CONTEXT_SERVERMODE
)
87 for (i
= 0; i
< nclients
; i
++) {
88 client
= isc_mem_get(lwresd
->mctx
, sizeof(ns_lwdclient_t
));
90 ns_lwdclient_log(50, "created client %p, manager %p",
92 ns_lwdclient_initialize(client
, cm
);
97 * If we could create no clients, clean up and return.
99 if (ISC_LIST_EMPTY(cm
->idle
))
102 result
= isc_task_create(taskmgr
, 0, &cm
->task
);
103 if (result
!= ISC_R_SUCCESS
)
107 * This MUST be last, since there is no way to cancel an onshutdown...
109 result
= isc_task_onshutdown(cm
->task
, lwdclientmgr_shutdown_callback
,
111 if (result
!= ISC_R_SUCCESS
)
114 ns_lwreslistener_linkcm(listener
, cm
);
116 return (ISC_R_SUCCESS
);
119 client
= ISC_LIST_HEAD(cm
->idle
);
120 while (client
!= NULL
) {
121 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
122 isc_mem_put(lwresd
->mctx
, client
, sizeof(*client
));
123 client
= ISC_LIST_HEAD(cm
->idle
);
126 if (cm
->task
!= NULL
)
127 isc_task_detach(&cm
->task
);
129 if (cm
->lwctx
!= NULL
)
130 lwres_context_destroy(&cm
->lwctx
);
132 isc_mem_put(lwresd
->mctx
, cm
, sizeof(*cm
));
137 lwdclientmgr_destroy(ns_lwdclientmgr_t
*cm
) {
138 ns_lwdclient_t
*client
;
139 ns_lwreslistener_t
*listener
;
141 if (!SHUTTINGDOWN(cm
))
145 * run through the idle list and free the clients there. Idle
146 * clients do not have a recv running nor do they have any finds
147 * or similar running.
149 client
= ISC_LIST_HEAD(cm
->idle
);
150 while (client
!= NULL
) {
151 ns_lwdclient_log(50, "destroying client %p, manager %p",
153 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
154 isc_mem_put(cm
->mctx
, client
, sizeof(*client
));
155 client
= ISC_LIST_HEAD(cm
->idle
);
158 if (!ISC_LIST_EMPTY(cm
->running
))
161 lwres_context_destroy(&cm
->lwctx
);
163 isc_socket_detach(&cm
->sock
);
164 isc_task_detach(&cm
->task
);
166 listener
= cm
->listener
;
167 ns_lwreslistener_unlinkcm(listener
, cm
);
168 ns_lwdclient_log(50, "destroying manager %p", cm
);
169 isc_mem_put(cm
->mctx
, cm
, sizeof(*cm
));
170 ns_lwreslistener_detach(&listener
);
174 process_request(ns_lwdclient_t
*client
) {
178 lwres_buffer_init(&b
, client
->buffer
, client
->recvlength
);
179 lwres_buffer_add(&b
, client
->recvlength
);
181 result
= lwres_lwpacket_parseheader(&b
, &client
->pkt
);
182 if (result
!= ISC_R_SUCCESS
) {
183 ns_lwdclient_log(50, "invalid packet header received");
187 ns_lwdclient_log(50, "opcode %08x", client
->pkt
.opcode
);
189 switch (client
->pkt
.opcode
) {
190 case LWRES_OPCODE_GETADDRSBYNAME
:
191 ns_lwdclient_processgabn(client
, &b
);
193 case LWRES_OPCODE_GETNAMEBYADDR
:
194 ns_lwdclient_processgnba(client
, &b
);
196 case LWRES_OPCODE_GETRDATABYNAME
:
197 ns_lwdclient_processgrbn(client
, &b
);
199 case LWRES_OPCODE_NOOP
:
200 ns_lwdclient_processnoop(client
, &b
);
203 ns_lwdclient_log(50, "unknown opcode %08x", client
->pkt
.opcode
);
211 ns_lwdclient_log(50, "restarting client %p...", client
);
212 ns_lwdclient_stateidle(client
);
216 ns_lwdclient_recv(isc_task_t
*task
, isc_event_t
*ev
) {
218 ns_lwdclient_t
*client
= ev
->ev_arg
;
219 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
220 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
222 INSIST(dev
->region
.base
== client
->buffer
);
223 INSIST(NS_LWDCLIENT_ISRECV(client
));
225 NS_LWDCLIENT_SETRECVDONE(client
);
227 INSIST((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0);
228 cm
->flags
&= ~NS_LWDCLIENTMGR_FLAGRECVPENDING
;
231 "event received: task %p, length %u, result %u (%s)",
232 task
, dev
->n
, dev
->result
,
233 isc_result_totext(dev
->result
));
235 if (dev
->result
!= ISC_R_SUCCESS
) {
242 ns_lwdclient_stateidle(client
);
247 client
->recvlength
= dev
->n
;
248 client
->address
= dev
->address
;
249 if ((dev
->attributes
& ISC_SOCKEVENTATTR_PKTINFO
) != 0) {
250 client
->pktinfo
= dev
->pktinfo
;
251 client
->pktinfo_valid
= ISC_TRUE
;
253 client
->pktinfo_valid
= ISC_FALSE
;
257 result
= ns_lwdclient_startrecv(cm
);
258 if (result
!= ISC_R_SUCCESS
)
259 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
260 NS_LOGMODULE_LWRESD
, ISC_LOG_ERROR
,
261 "could not start lwres "
262 "client handler: %s",
263 isc_result_totext(result
));
265 process_request(client
);
269 * This function will start a new recv() on a socket for this client manager.
272 ns_lwdclient_startrecv(ns_lwdclientmgr_t
*cm
) {
273 ns_lwdclient_t
*client
;
277 if (SHUTTINGDOWN(cm
)) {
278 lwdclientmgr_destroy(cm
);
279 return (ISC_R_SUCCESS
);
283 * If a recv is already running, don't bother.
285 if ((cm
->flags
& NS_LWDCLIENTMGR_FLAGRECVPENDING
) != 0)
286 return (ISC_R_SUCCESS
);
289 * If we have no idle slots, just return success.
291 client
= ISC_LIST_HEAD(cm
->idle
);
293 return (ISC_R_SUCCESS
);
294 INSIST(NS_LWDCLIENT_ISIDLE(client
));
297 * Issue the recv. If it fails, return that it did.
299 r
.base
= client
->buffer
;
300 r
.length
= LWRES_RECVLENGTH
;
301 result
= isc_socket_recv(cm
->sock
, &r
, 0, cm
->task
, ns_lwdclient_recv
,
303 if (result
!= ISC_R_SUCCESS
)
307 * Set the flag to say we've issued a recv() call.
309 cm
->flags
|= NS_LWDCLIENTMGR_FLAGRECVPENDING
;
312 * Remove the client from the idle list, and put it on the running
315 NS_LWDCLIENT_SETRECV(client
);
316 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
317 ISC_LIST_APPEND(cm
->running
, client
, link
);
319 return (ISC_R_SUCCESS
);
323 lwdclientmgr_shutdown_callback(isc_task_t
*task
, isc_event_t
*ev
) {
324 ns_lwdclientmgr_t
*cm
= ev
->ev_arg
;
325 ns_lwdclient_t
*client
;
327 REQUIRE(!SHUTTINGDOWN(cm
));
329 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
333 * run through the idle list and free the clients there. Idle
334 * clients do not have a recv running nor do they have any finds
335 * or similar running.
337 client
= ISC_LIST_HEAD(cm
->idle
);
338 while (client
!= NULL
) {
339 ns_lwdclient_log(50, "destroying client %p, manager %p",
341 ISC_LIST_UNLINK(cm
->idle
, client
, link
);
342 isc_mem_put(cm
->mctx
, client
, sizeof(*client
));
343 client
= ISC_LIST_HEAD(cm
->idle
);
347 * Cancel any pending I/O.
349 isc_socket_cancel(cm
->sock
, task
, ISC_SOCKCANCEL_ALL
);
352 * Run through the running client list and kill off any finds
355 client
= ISC_LIST_HEAD(cm
->running
);
356 while (client
!= NULL
) {
357 if (client
->find
!= client
->v4find
358 && client
->find
!= client
->v6find
)
359 dns_adb_cancelfind(client
->find
);
360 if (client
->v4find
!= NULL
)
361 dns_adb_cancelfind(client
->v4find
);
362 if (client
->v6find
!= NULL
)
363 dns_adb_cancelfind(client
->v6find
);
364 client
= ISC_LIST_NEXT(client
, link
);
367 cm
->flags
|= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN
;
373 * Do all the crap needed to move a client from the run queue to the idle
377 ns_lwdclient_stateidle(ns_lwdclient_t
*client
) {
378 ns_lwdclientmgr_t
*cm
;
381 cm
= client
->clientmgr
;
383 INSIST(client
->sendbuf
== NULL
);
384 INSIST(client
->sendlength
== 0);
385 INSIST(client
->arg
== NULL
);
386 INSIST(client
->v4find
== NULL
);
387 INSIST(client
->v6find
== NULL
);
389 ISC_LIST_UNLINK(cm
->running
, client
, link
);
390 ISC_LIST_PREPEND(cm
->idle
, client
, link
);
392 NS_LWDCLIENT_SETIDLE(client
);
394 result
= ns_lwdclient_startrecv(cm
);
395 if (result
!= ISC_R_SUCCESS
)
396 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
397 NS_LOGMODULE_LWRESD
, ISC_LOG_ERROR
,
398 "could not start lwres "
399 "client handler: %s",
400 isc_result_totext(result
));
404 ns_lwdclient_send(isc_task_t
*task
, isc_event_t
*ev
) {
405 ns_lwdclient_t
*client
= ev
->ev_arg
;
406 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
407 isc_socketevent_t
*dev
= (isc_socketevent_t
*)ev
;
412 INSIST(NS_LWDCLIENT_ISSEND(client
));
413 INSIST(client
->sendbuf
== dev
->region
.base
);
415 ns_lwdclient_log(50, "task %p for client %p got send-done event",
418 if (client
->sendbuf
!= client
->buffer
)
419 lwres_context_freemem(cm
->lwctx
, client
->sendbuf
,
421 client
->sendbuf
= NULL
;
422 client
->sendlength
= 0;
424 ns_lwdclient_stateidle(client
);
430 ns_lwdclient_sendreply(ns_lwdclient_t
*client
, isc_region_t
*r
) {
431 struct in6_pktinfo
*pktinfo
;
432 ns_lwdclientmgr_t
*cm
= client
->clientmgr
;
434 if (client
->pktinfo_valid
)
435 pktinfo
= &client
->pktinfo
;
438 return (isc_socket_sendto(cm
->sock
, r
, cm
->task
, ns_lwdclient_send
,
439 client
, &client
->address
, pktinfo
));
443 ns_lwdclient_initialize(ns_lwdclient_t
*client
, ns_lwdclientmgr_t
*cmgr
) {
444 client
->clientmgr
= cmgr
;
445 ISC_LINK_INIT(client
, link
);
446 NS_LWDCLIENT_SETIDLE(client
);
449 client
->recvlength
= 0;
451 client
->sendbuf
= NULL
;
452 client
->sendlength
= 0;
455 client
->v4find
= NULL
;
456 client
->v6find
= NULL
;
457 client
->find_wanted
= 0;
460 client
->byaddr
= NULL
;
462 client
->lookup
= NULL
;
464 client
->pktinfo_valid
= ISC_FALSE
;
466 ISC_LIST_APPEND(cmgr
->idle
, client
, link
);