2 * Copyright (C) 2004, 2005, 2007, 2015 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;
64 cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
66 return (ISC_R_NOMEMORY);
68 result = isc_mutex_init(&cm->lock);
69 if (result != ISC_R_SUCCESS)
73 ns_lwreslistener_attach(listener, &cm->listener);
74 cm->mctx = lwresd->mctx;
76 isc_socket_attach(listener->sock, &cm->sock);
77 cm->view = lwresd->view;
81 ISC_LINK_INIT(cm, link);
82 ISC_LIST_INIT(cm->idle);
83 ISC_LIST_INIT(cm->running);
85 result = lwres_context_create(&cm->lwctx, cm->mctx,
86 ns__lwresd_memalloc, ns__lwresd_memfree,
87 LWRES_CONTEXT_SERVERMODE);
88 if (result != ISC_R_SUCCESS)
91 for (i = 0; i < nclients; i++) {
92 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
94 ns_lwdclient_log(50, "created client %p, manager %p",
96 ns_lwdclient_initialize(client, cm);
101 * If we could create no clients, clean up and return.
103 if (ISC_LIST_EMPTY(cm->idle)) {
104 result = ISC_R_NOMEMORY;
108 result = isc_task_create(taskmgr, 0, &cm->task);
109 if (result != ISC_R_SUCCESS)
111 isc_task_setname(cm->task, "lwdclient", NULL);
114 * This MUST be last, since there is no way to cancel an onshutdown...
116 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
118 if (result != ISC_R_SUCCESS)
121 ns_lwreslistener_linkcm(listener, cm);
123 return (ISC_R_SUCCESS);
126 client = ISC_LIST_HEAD(cm->idle);
127 while (client != NULL) {
128 ISC_LIST_UNLINK(cm->idle, client, link);
129 isc_mem_put(lwresd->mctx, client, sizeof(*client));
130 client = ISC_LIST_HEAD(cm->idle);
133 if (cm->task != NULL)
134 isc_task_detach(&cm->task);
136 if (cm->lwctx != NULL)
137 lwres_context_destroy(&cm->lwctx);
139 DESTROYLOCK(&cm->lock);
142 isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
147 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
148 ns_lwdclient_t *client;
149 ns_lwreslistener_t *listener;
152 if (!SHUTTINGDOWN(cm)) {
158 * Run through the idle list and free the clients there. Idle
159 * clients do not have a recv running nor do they have any finds
160 * or similar running.
162 client = ISC_LIST_HEAD(cm->idle);
163 while (client != NULL) {
164 ns_lwdclient_log(50, "destroying client %p, manager %p",
166 ISC_LIST_UNLINK(cm->idle, client, link);
167 isc_mem_put(cm->mctx, client, sizeof(*client));
168 client = ISC_LIST_HEAD(cm->idle);
171 if (!ISC_LIST_EMPTY(cm->running)) {
178 lwres_context_destroy(&cm->lwctx);
180 isc_socket_detach(&cm->sock);
181 isc_task_detach(&cm->task);
183 DESTROYLOCK(&cm->lock);
185 listener = cm->listener;
186 ns_lwreslistener_unlinkcm(listener, cm);
187 ns_lwdclient_log(50, "destroying manager %p", cm);
188 isc_mem_put(cm->mctx, cm, sizeof(*cm));
189 ns_lwreslistener_detach(&listener);
193 process_request(ns_lwdclient_t *client) {
197 lwres_buffer_init(&b, client->buffer, client->recvlength);
198 lwres_buffer_add(&b, client->recvlength);
200 result = lwres_lwpacket_parseheader(&b, &client->pkt);
201 if (result != ISC_R_SUCCESS) {
202 ns_lwdclient_log(50, "invalid packet header received");
206 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
208 switch (client->pkt.opcode) {
209 case LWRES_OPCODE_GETADDRSBYNAME:
210 ns_lwdclient_processgabn(client, &b);
212 case LWRES_OPCODE_GETNAMEBYADDR:
213 ns_lwdclient_processgnba(client, &b);
215 case LWRES_OPCODE_GETRDATABYNAME:
216 ns_lwdclient_processgrbn(client, &b);
218 case LWRES_OPCODE_NOOP:
219 ns_lwdclient_processnoop(client, &b);
222 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
230 ns_lwdclient_log(50, "restarting client %p...", client);
231 ns_lwdclient_stateidle(client);
235 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
237 ns_lwdclient_t *client = ev->ev_arg;
238 ns_lwdclientmgr_t *cm = client->clientmgr;
239 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
241 INSIST(dev->region.base == client->buffer);
242 INSIST(NS_LWDCLIENT_ISRECV(client));
244 NS_LWDCLIENT_SETRECVDONE(client);
247 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
248 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
252 "event received: task %p, length %u, result %u (%s)",
253 task, dev->n, dev->result,
254 isc_result_totext(dev->result));
256 if (dev->result != ISC_R_SUCCESS) {
263 ns_lwdclient_stateidle(client);
268 client->recvlength = dev->n;
269 client->address = dev->address;
270 if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
271 client->pktinfo = dev->pktinfo;
272 client->pktinfo_valid = ISC_TRUE;
274 client->pktinfo_valid = ISC_FALSE;
278 result = ns_lwdclient_startrecv(cm);
279 if (result != ISC_R_SUCCESS)
280 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
281 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
282 "could not start lwres "
283 "client handler: %s",
284 isc_result_totext(result));
286 process_request(client);
290 * This function will start a new recv() on a socket for this client manager.
293 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
294 ns_lwdclient_t *client;
297 isc_boolean_t destroy = ISC_FALSE;
301 if (SHUTTINGDOWN(cm)) {
303 result = ISC_R_SUCCESS;
308 * If a recv is already running, don't bother.
310 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0) {
311 result = ISC_R_SUCCESS;
316 * If we have no idle slots, just return success.
318 client = ISC_LIST_HEAD(cm->idle);
319 if (client == NULL) {
320 result = ISC_R_SUCCESS;
324 INSIST(NS_LWDCLIENT_ISIDLE(client));
327 * Set the flag to say there is a recv pending. If isc_socket_recv
328 * fails we will clear the flag otherwise it will be cleared by
331 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
334 * Issue the recv. If it fails, return that it did.
336 r.base = client->buffer;
337 r.length = LWRES_RECVLENGTH;
338 result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
340 if (result != ISC_R_SUCCESS) {
341 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
346 * Remove the client from the idle list, and put it on the running
349 NS_LWDCLIENT_SETRECV(client);
350 ISC_LIST_UNLINK(cm->idle, client, link);
351 ISC_LIST_APPEND(cm->running, client, link);
357 lwdclientmgr_destroy(cm);
363 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
364 ns_lwdclientmgr_t *cm = ev->ev_arg;
365 ns_lwdclient_t *client;
367 REQUIRE(!SHUTTINGDOWN(cm));
369 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
373 * run through the idle list and free the clients there. Idle
374 * clients do not have a recv running nor do they have any finds
375 * or similar running.
378 client = ISC_LIST_HEAD(cm->idle);
379 while (client != NULL) {
380 ns_lwdclient_log(50, "destroying client %p, manager %p",
382 ISC_LIST_UNLINK(cm->idle, client, link);
383 isc_mem_put(cm->mctx, client, sizeof(*client));
384 client = ISC_LIST_HEAD(cm->idle);
389 * Cancel any pending I/O.
391 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
394 * Run through the running client list and kill off any finds
398 client = ISC_LIST_HEAD(cm->running);
399 while (client != NULL) {
400 if (client->find != client->v4find
401 && client->find != client->v6find)
402 dns_adb_cancelfind(client->find);
403 if (client->v4find != NULL)
404 dns_adb_cancelfind(client->v4find);
405 if (client->v6find != NULL)
406 dns_adb_cancelfind(client->v6find);
407 client = ISC_LIST_NEXT(client, link);
410 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
418 * Do all the crap needed to move a client from the run queue to the idle
422 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
423 ns_lwdclientmgr_t *cm;
426 cm = client->clientmgr;
428 INSIST(client->sendbuf == NULL);
429 INSIST(client->sendlength == 0);
430 INSIST(client->arg == NULL);
431 INSIST(client->v4find == NULL);
432 INSIST(client->v6find == NULL);
435 ISC_LIST_UNLINK(cm->running, client, link);
436 ISC_LIST_PREPEND(cm->idle, client, link);
439 NS_LWDCLIENT_SETIDLE(client);
441 result = ns_lwdclient_startrecv(cm);
442 if (result != ISC_R_SUCCESS)
443 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
444 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
445 "could not start lwres "
446 "client handler: %s",
447 isc_result_totext(result));
451 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
452 ns_lwdclient_t *client = ev->ev_arg;
453 ns_lwdclientmgr_t *cm = client->clientmgr;
454 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
459 INSIST(NS_LWDCLIENT_ISSEND(client));
460 INSIST(client->sendbuf == dev->region.base);
462 ns_lwdclient_log(50, "task %p for client %p got send-done event",
465 if (client->sendbuf != client->buffer)
466 lwres_context_freemem(cm->lwctx, client->sendbuf,
468 client->sendbuf = NULL;
469 client->sendlength = 0;
471 ns_lwdclient_stateidle(client);
477 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
478 struct in6_pktinfo *pktinfo;
479 ns_lwdclientmgr_t *cm = client->clientmgr;
481 if (client->pktinfo_valid)
482 pktinfo = &client->pktinfo;
485 return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
486 client, &client->address, pktinfo));
490 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
491 client->clientmgr = cmgr;
492 ISC_LINK_INIT(client, link);
493 NS_LWDCLIENT_SETIDLE(client);
496 client->recvlength = 0;
498 client->sendbuf = NULL;
499 client->sendlength = 0;
502 client->v4find = NULL;
503 client->v6find = NULL;
504 client->find_wanted = 0;
507 client->byaddr = NULL;
509 client->lookup = NULL;
511 client->pktinfo_valid = ISC_FALSE;
514 ISC_LIST_APPEND(cmgr->idle, client, link);