2 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or 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.22 2007/06/18 23:47:18 tbox 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)
105 isc_task_setname(cm->task, "lwdclient", NULL);
108 * This MUST be last, since there is no way to cancel an onshutdown...
110 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
112 if (result != ISC_R_SUCCESS)
115 ns_lwreslistener_linkcm(listener, cm);
117 return (ISC_R_SUCCESS);
120 client = ISC_LIST_HEAD(cm->idle);
121 while (client != NULL) {
122 ISC_LIST_UNLINK(cm->idle, client, link);
123 isc_mem_put(lwresd->mctx, client, sizeof(*client));
124 client = ISC_LIST_HEAD(cm->idle);
127 if (cm->task != NULL)
128 isc_task_detach(&cm->task);
130 if (cm->lwctx != NULL)
131 lwres_context_destroy(&cm->lwctx);
133 isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
138 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
139 ns_lwdclient_t *client;
140 ns_lwreslistener_t *listener;
142 if (!SHUTTINGDOWN(cm))
146 * run through the idle list and free the clients there. Idle
147 * clients do not have a recv running nor do they have any finds
148 * or similar running.
150 client = ISC_LIST_HEAD(cm->idle);
151 while (client != NULL) {
152 ns_lwdclient_log(50, "destroying client %p, manager %p",
154 ISC_LIST_UNLINK(cm->idle, client, link);
155 isc_mem_put(cm->mctx, client, sizeof(*client));
156 client = ISC_LIST_HEAD(cm->idle);
159 if (!ISC_LIST_EMPTY(cm->running))
162 lwres_context_destroy(&cm->lwctx);
164 isc_socket_detach(&cm->sock);
165 isc_task_detach(&cm->task);
167 listener = cm->listener;
168 ns_lwreslistener_unlinkcm(listener, cm);
169 ns_lwdclient_log(50, "destroying manager %p", cm);
170 isc_mem_put(cm->mctx, cm, sizeof(*cm));
171 ns_lwreslistener_detach(&listener);
175 process_request(ns_lwdclient_t *client) {
179 lwres_buffer_init(&b, client->buffer, client->recvlength);
180 lwres_buffer_add(&b, client->recvlength);
182 result = lwres_lwpacket_parseheader(&b, &client->pkt);
183 if (result != ISC_R_SUCCESS) {
184 ns_lwdclient_log(50, "invalid packet header received");
188 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
190 switch (client->pkt.opcode) {
191 case LWRES_OPCODE_GETADDRSBYNAME:
192 ns_lwdclient_processgabn(client, &b);
194 case LWRES_OPCODE_GETNAMEBYADDR:
195 ns_lwdclient_processgnba(client, &b);
197 case LWRES_OPCODE_GETRDATABYNAME:
198 ns_lwdclient_processgrbn(client, &b);
200 case LWRES_OPCODE_NOOP:
201 ns_lwdclient_processnoop(client, &b);
204 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
212 ns_lwdclient_log(50, "restarting client %p...", client);
213 ns_lwdclient_stateidle(client);
217 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
219 ns_lwdclient_t *client = ev->ev_arg;
220 ns_lwdclientmgr_t *cm = client->clientmgr;
221 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
223 INSIST(dev->region.base == client->buffer);
224 INSIST(NS_LWDCLIENT_ISRECV(client));
226 NS_LWDCLIENT_SETRECVDONE(client);
228 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
229 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
232 "event received: task %p, length %u, result %u (%s)",
233 task, dev->n, dev->result,
234 isc_result_totext(dev->result));
236 if (dev->result != ISC_R_SUCCESS) {
243 ns_lwdclient_stateidle(client);
248 client->recvlength = dev->n;
249 client->address = dev->address;
250 if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
251 client->pktinfo = dev->pktinfo;
252 client->pktinfo_valid = ISC_TRUE;
254 client->pktinfo_valid = ISC_FALSE;
258 result = ns_lwdclient_startrecv(cm);
259 if (result != ISC_R_SUCCESS)
260 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
261 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
262 "could not start lwres "
263 "client handler: %s",
264 isc_result_totext(result));
266 process_request(client);
270 * This function will start a new recv() on a socket for this client manager.
273 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
274 ns_lwdclient_t *client;
278 if (SHUTTINGDOWN(cm)) {
279 lwdclientmgr_destroy(cm);
280 return (ISC_R_SUCCESS);
284 * If a recv is already running, don't bother.
286 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
287 return (ISC_R_SUCCESS);
290 * If we have no idle slots, just return success.
292 client = ISC_LIST_HEAD(cm->idle);
294 return (ISC_R_SUCCESS);
295 INSIST(NS_LWDCLIENT_ISIDLE(client));
298 * Issue the recv. If it fails, return that it did.
300 r.base = client->buffer;
301 r.length = LWRES_RECVLENGTH;
302 result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
304 if (result != ISC_R_SUCCESS)
308 * Set the flag to say we've issued a recv() call.
310 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
313 * Remove the client from the idle list, and put it on the running
316 NS_LWDCLIENT_SETRECV(client);
317 ISC_LIST_UNLINK(cm->idle, client, link);
318 ISC_LIST_APPEND(cm->running, client, link);
320 return (ISC_R_SUCCESS);
324 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
325 ns_lwdclientmgr_t *cm = ev->ev_arg;
326 ns_lwdclient_t *client;
328 REQUIRE(!SHUTTINGDOWN(cm));
330 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
334 * run through the idle list and free the clients there. Idle
335 * clients do not have a recv running nor do they have any finds
336 * or similar running.
338 client = ISC_LIST_HEAD(cm->idle);
339 while (client != NULL) {
340 ns_lwdclient_log(50, "destroying client %p, manager %p",
342 ISC_LIST_UNLINK(cm->idle, client, link);
343 isc_mem_put(cm->mctx, client, sizeof(*client));
344 client = ISC_LIST_HEAD(cm->idle);
348 * Cancel any pending I/O.
350 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
353 * Run through the running client list and kill off any finds
356 client = ISC_LIST_HEAD(cm->running);
357 while (client != NULL) {
358 if (client->find != client->v4find
359 && client->find != client->v6find)
360 dns_adb_cancelfind(client->find);
361 if (client->v4find != NULL)
362 dns_adb_cancelfind(client->v4find);
363 if (client->v6find != NULL)
364 dns_adb_cancelfind(client->v6find);
365 client = ISC_LIST_NEXT(client, link);
368 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
374 * Do all the crap needed to move a client from the run queue to the idle
378 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
379 ns_lwdclientmgr_t *cm;
382 cm = client->clientmgr;
384 INSIST(client->sendbuf == NULL);
385 INSIST(client->sendlength == 0);
386 INSIST(client->arg == NULL);
387 INSIST(client->v4find == NULL);
388 INSIST(client->v6find == NULL);
390 ISC_LIST_UNLINK(cm->running, client, link);
391 ISC_LIST_PREPEND(cm->idle, client, link);
393 NS_LWDCLIENT_SETIDLE(client);
395 result = ns_lwdclient_startrecv(cm);
396 if (result != ISC_R_SUCCESS)
397 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
398 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
399 "could not start lwres "
400 "client handler: %s",
401 isc_result_totext(result));
405 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
406 ns_lwdclient_t *client = ev->ev_arg;
407 ns_lwdclientmgr_t *cm = client->clientmgr;
408 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
413 INSIST(NS_LWDCLIENT_ISSEND(client));
414 INSIST(client->sendbuf == dev->region.base);
416 ns_lwdclient_log(50, "task %p for client %p got send-done event",
419 if (client->sendbuf != client->buffer)
420 lwres_context_freemem(cm->lwctx, client->sendbuf,
422 client->sendbuf = NULL;
423 client->sendlength = 0;
425 ns_lwdclient_stateidle(client);
431 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
432 struct in6_pktinfo *pktinfo;
433 ns_lwdclientmgr_t *cm = client->clientmgr;
435 if (client->pktinfo_valid)
436 pktinfo = &client->pktinfo;
439 return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
440 client, &client->address, pktinfo));
444 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
445 client->clientmgr = cmgr;
446 ISC_LINK_INIT(client, link);
447 NS_LWDCLIENT_SETIDLE(client);
450 client->recvlength = 0;
452 client->sendbuf = NULL;
453 client->sendlength = 0;
456 client->v4find = NULL;
457 client->v6find = NULL;
458 client->find_wanted = 0;
461 client->byaddr = NULL;
463 client->lookup = NULL;
465 client->pktinfo_valid = ISC_FALSE;
467 ISC_LIST_APPEND(cmgr->idle, client, link);