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.12.5 2004/03/08 09:04:15 marka Exp $ */
22 #include <isc/socket.h>
23 #include <isc/string.h>
31 #include <named/types.h>
32 #include <named/log.h>
33 #include <named/lwresd.h>
34 #include <named/lwdclient.h>
36 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
39 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
42 ns_lwdclient_log(int level, const char *format, ...) {
45 va_start(args, format);
46 isc_log_vwrite(dns_lctx,
47 DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
48 ISC_LOG_DEBUG(level), format, args);
53 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
54 isc_taskmgr_t *taskmgr)
56 ns_lwresd_t *lwresd = listener->manager;
57 ns_lwdclientmgr_t *cm;
58 ns_lwdclient_t *client;
60 isc_result_t result = ISC_R_FAILURE;
62 cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
64 return (ISC_R_NOMEMORY);
67 ns_lwreslistener_attach(listener, &cm->listener);
68 cm->mctx = lwresd->mctx;
70 isc_socket_attach(listener->sock, &cm->sock);
71 cm->view = lwresd->view;
75 ISC_LINK_INIT(cm, link);
76 ISC_LIST_INIT(cm->idle);
77 ISC_LIST_INIT(cm->running);
79 if (lwres_context_create(&cm->lwctx, cm->mctx,
80 ns__lwresd_memalloc, ns__lwresd_memfree,
81 LWRES_CONTEXT_SERVERMODE)
85 for (i = 0; i < nclients; i++) {
86 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
88 ns_lwdclient_log(50, "created client %p, manager %p",
90 ns_lwdclient_initialize(client, cm);
95 * If we could create no clients, clean up and return.
97 if (ISC_LIST_EMPTY(cm->idle))
100 result = isc_task_create(taskmgr, 0, &cm->task);
101 if (result != ISC_R_SUCCESS)
105 * This MUST be last, since there is no way to cancel an onshutdown...
107 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
109 if (result != ISC_R_SUCCESS)
112 ns_lwreslistener_linkcm(listener, cm);
114 return (ISC_R_SUCCESS);
117 client = ISC_LIST_HEAD(cm->idle);
118 while (client != NULL) {
119 ISC_LIST_UNLINK(cm->idle, client, link);
120 isc_mem_put(lwresd->mctx, client, sizeof(*client));
121 client = ISC_LIST_HEAD(cm->idle);
124 if (cm->task != NULL)
125 isc_task_detach(&cm->task);
127 if (cm->lwctx != NULL)
128 lwres_context_destroy(&cm->lwctx);
130 isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
135 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
136 ns_lwdclient_t *client;
137 ns_lwreslistener_t *listener;
139 if (!SHUTTINGDOWN(cm))
143 * run through the idle list and free the clients there. Idle
144 * clients do not have a recv running nor do they have any finds
145 * or similar running.
147 client = ISC_LIST_HEAD(cm->idle);
148 while (client != NULL) {
149 ns_lwdclient_log(50, "destroying client %p, manager %p",
151 ISC_LIST_UNLINK(cm->idle, client, link);
152 isc_mem_put(cm->mctx, client, sizeof(*client));
153 client = ISC_LIST_HEAD(cm->idle);
156 if (!ISC_LIST_EMPTY(cm->running))
159 lwres_context_destroy(&cm->lwctx);
161 isc_socket_detach(&cm->sock);
162 isc_task_detach(&cm->task);
164 listener = cm->listener;
165 ns_lwreslistener_unlinkcm(listener, cm);
166 ns_lwdclient_log(50, "destroying manager %p", cm);
167 isc_mem_put(cm->mctx, cm, sizeof(*cm));
168 ns_lwreslistener_detach(&listener);
172 process_request(ns_lwdclient_t *client) {
176 lwres_buffer_init(&b, client->buffer, client->recvlength);
177 lwres_buffer_add(&b, client->recvlength);
179 result = lwres_lwpacket_parseheader(&b, &client->pkt);
180 if (result != ISC_R_SUCCESS) {
181 ns_lwdclient_log(50, "invalid packet header received");
185 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
187 switch (client->pkt.opcode) {
188 case LWRES_OPCODE_GETADDRSBYNAME:
189 ns_lwdclient_processgabn(client, &b);
191 case LWRES_OPCODE_GETNAMEBYADDR:
192 ns_lwdclient_processgnba(client, &b);
194 case LWRES_OPCODE_GETRDATABYNAME:
195 ns_lwdclient_processgrbn(client, &b);
197 case LWRES_OPCODE_NOOP:
198 ns_lwdclient_processnoop(client, &b);
201 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
209 ns_lwdclient_log(50, "restarting client %p...", client);
210 ns_lwdclient_stateidle(client);
214 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
216 ns_lwdclient_t *client = ev->ev_arg;
217 ns_lwdclientmgr_t *cm = client->clientmgr;
218 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
220 INSIST(dev->region.base == client->buffer);
221 INSIST(NS_LWDCLIENT_ISRECV(client));
223 NS_LWDCLIENT_SETRECVDONE(client);
225 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
226 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
229 "event received: task %p, length %u, result %u (%s)",
230 task, dev->n, dev->result,
231 isc_result_totext(dev->result));
233 if (dev->result != ISC_R_SUCCESS) {
240 ns_lwdclient_stateidle(client);
245 client->recvlength = dev->n;
246 client->address = dev->address;
247 if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
248 client->pktinfo = dev->pktinfo;
249 client->pktinfo_valid = ISC_TRUE;
251 client->pktinfo_valid = ISC_FALSE;
255 result = ns_lwdclient_startrecv(cm);
256 if (result != ISC_R_SUCCESS)
257 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
258 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
259 "could not start lwres "
260 "client handler: %s",
261 isc_result_totext(result));
263 process_request(client);
267 * This function will start a new recv() on a socket for this client manager.
270 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
271 ns_lwdclient_t *client;
275 if (SHUTTINGDOWN(cm)) {
276 lwdclientmgr_destroy(cm);
277 return (ISC_R_SUCCESS);
281 * If a recv is already running, don't bother.
283 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
284 return (ISC_R_SUCCESS);
287 * If we have no idle slots, just return success.
289 client = ISC_LIST_HEAD(cm->idle);
291 return (ISC_R_SUCCESS);
292 INSIST(NS_LWDCLIENT_ISIDLE(client));
295 * Issue the recv. If it fails, return that it did.
297 r.base = client->buffer;
298 r.length = LWRES_RECVLENGTH;
299 result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
301 if (result != ISC_R_SUCCESS)
305 * Set the flag to say we've issued a recv() call.
307 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
310 * Remove the client from the idle list, and put it on the running
313 NS_LWDCLIENT_SETRECV(client);
314 ISC_LIST_UNLINK(cm->idle, client, link);
315 ISC_LIST_APPEND(cm->running, client, link);
317 return (ISC_R_SUCCESS);
321 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
322 ns_lwdclientmgr_t *cm = ev->ev_arg;
323 ns_lwdclient_t *client;
325 REQUIRE(!SHUTTINGDOWN(cm));
327 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
331 * run through the idle list and free the clients there. Idle
332 * clients do not have a recv running nor do they have any finds
333 * or similar running.
335 client = ISC_LIST_HEAD(cm->idle);
336 while (client != NULL) {
337 ns_lwdclient_log(50, "destroying client %p, manager %p",
339 ISC_LIST_UNLINK(cm->idle, client, link);
340 isc_mem_put(cm->mctx, client, sizeof(*client));
341 client = ISC_LIST_HEAD(cm->idle);
345 * Cancel any pending I/O.
347 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
350 * Run through the running client list and kill off any finds
353 client = ISC_LIST_HEAD(cm->running);
354 while (client != NULL) {
355 if (client->find != client->v4find
356 && client->find != client->v6find)
357 dns_adb_cancelfind(client->find);
358 if (client->v4find != NULL)
359 dns_adb_cancelfind(client->v4find);
360 if (client->v6find != NULL)
361 dns_adb_cancelfind(client->v6find);
362 client = ISC_LIST_NEXT(client, link);
365 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
371 * Do all the crap needed to move a client from the run queue to the idle
375 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
376 ns_lwdclientmgr_t *cm;
379 cm = client->clientmgr;
381 INSIST(client->sendbuf == NULL);
382 INSIST(client->sendlength == 0);
383 INSIST(client->arg == NULL);
384 INSIST(client->v4find == NULL);
385 INSIST(client->v6find == NULL);
387 ISC_LIST_UNLINK(cm->running, client, link);
388 ISC_LIST_PREPEND(cm->idle, client, link);
390 NS_LWDCLIENT_SETIDLE(client);
392 result = ns_lwdclient_startrecv(cm);
393 if (result != ISC_R_SUCCESS)
394 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
395 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
396 "could not start lwres "
397 "client handler: %s",
398 isc_result_totext(result));
402 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
403 ns_lwdclient_t *client = ev->ev_arg;
404 ns_lwdclientmgr_t *cm = client->clientmgr;
405 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
410 INSIST(NS_LWDCLIENT_ISSEND(client));
411 INSIST(client->sendbuf == dev->region.base);
413 ns_lwdclient_log(50, "task %p for client %p got send-done event",
416 if (client->sendbuf != client->buffer)
417 lwres_context_freemem(cm->lwctx, client->sendbuf,
419 client->sendbuf = NULL;
420 client->sendlength = 0;
422 ns_lwdclient_stateidle(client);
428 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
429 struct in6_pktinfo *pktinfo;
430 ns_lwdclientmgr_t *cm = client->clientmgr;
432 if (client->pktinfo_valid)
433 pktinfo = &client->pktinfo;
436 return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
437 client, &client->address, pktinfo));
441 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
442 client->clientmgr = cmgr;
443 ISC_LINK_INIT(client, link);
444 NS_LWDCLIENT_SETIDLE(client);
447 client->recvlength = 0;
449 client->sendbuf = NULL;
450 client->sendlength = 0;
453 client->v4find = NULL;
454 client->v6find = NULL;
455 client->find_wanted = 0;
458 client->byaddr = NULL;
460 client->lookup = NULL;
462 client->pktinfo_valid = ISC_FALSE;
464 ISC_LIST_APPEND(cmgr->idle, client, link);