Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / named / lwdclient.c
blob67c3a8833315091ffecad5679d7bff8c029adaf8
1 /*
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.2.1 2004/03/09 06:09:18 marka Exp $ */
20 #include <config.h>
22 #include <isc/socket.h>
23 #include <isc/string.h>
24 #include <isc/task.h>
25 #include <isc/util.h>
27 #include <dns/adb.h>
28 #include <dns/view.h>
29 #include <dns/log.h>
31 #include <named/types.h>
32 #include <named/lwresd.h>
33 #include <named/lwdclient.h>
35 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
37 static void
38 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
40 void
41 ns_lwdclient_log(int level, const char *format, ...) {
42 va_list args;
44 va_start(args, format);
45 isc_log_vwrite(dns_lctx,
46 DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
47 ISC_LOG_DEBUG(level), format, args);
48 va_end(args);
51 isc_result_t
52 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
53 isc_taskmgr_t *taskmgr)
55 ns_lwresd_t *lwresd = listener->manager;
56 ns_lwdclientmgr_t *cm;
57 ns_lwdclient_t *client;
58 unsigned int i;
59 isc_result_t result = ISC_R_FAILURE;
61 cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
62 if (cm == NULL)
63 return (ISC_R_NOMEMORY);
65 cm->listener = NULL;
66 ns_lwreslistener_attach(listener, &cm->listener);
67 cm->mctx = lwresd->mctx;
68 cm->sock = NULL;
69 isc_socket_attach(listener->sock, &cm->sock);
70 cm->view = lwresd->view;
71 cm->lwctx = NULL;
72 cm->task = NULL;
73 cm->flags = 0;
74 ISC_LINK_INIT(cm, link);
75 ISC_LIST_INIT(cm->idle);
76 ISC_LIST_INIT(cm->running);
78 if (lwres_context_create(&cm->lwctx, cm->mctx,
79 ns__lwresd_memalloc, ns__lwresd_memfree,
80 LWRES_CONTEXT_SERVERMODE)
81 != ISC_R_SUCCESS)
82 goto errout;
84 for (i = 0 ; i < nclients ; i++) {
85 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
86 if (client != NULL) {
87 ns_lwdclient_log(50, "created client %p, manager %p",
88 client, cm);
89 ns_lwdclient_initialize(client, cm);
94 * If we could create no clients, clean up and return.
96 if (ISC_LIST_EMPTY(cm->idle))
97 goto errout;
99 result = isc_task_create(taskmgr, 0, &cm->task);
100 if (result != ISC_R_SUCCESS)
101 goto errout;
104 * This MUST be last, since there is no way to cancel an onshutdown...
106 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
107 cm);
108 if (result != ISC_R_SUCCESS)
109 goto errout;
111 ns_lwreslistener_linkcm(listener, cm);
113 return (ISC_R_SUCCESS);
115 errout:
116 client = ISC_LIST_HEAD(cm->idle);
117 while (client != NULL) {
118 ISC_LIST_UNLINK(cm->idle, client, link);
119 isc_mem_put(lwresd->mctx, client, sizeof (*client));
120 client = ISC_LIST_HEAD(cm->idle);
123 if (cm->task != NULL)
124 isc_task_detach(&cm->task);
126 if (cm->lwctx != NULL)
127 lwres_context_destroy(&cm->lwctx);
129 isc_mem_put(lwresd->mctx, cm, sizeof (*cm));
130 return (result);
133 static void
134 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
135 ns_lwdclient_t *client;
136 ns_lwreslistener_t *listener;
138 if (!SHUTTINGDOWN(cm))
139 return;
142 * run through the idle list and free the clients there. Idle
143 * clients do not have a recv running nor do they have any finds
144 * or similar running.
146 client = ISC_LIST_HEAD(cm->idle);
147 while (client != NULL) {
148 ns_lwdclient_log(50, "destroying client %p, manager %p",
149 client, cm);
150 ISC_LIST_UNLINK(cm->idle, client, link);
151 isc_mem_put(cm->mctx, client, sizeof (*client));
152 client = ISC_LIST_HEAD(cm->idle);
155 if (!ISC_LIST_EMPTY(cm->running))
156 return;
158 lwres_context_destroy(&cm->lwctx);
159 cm->view = NULL;
160 isc_socket_detach(&cm->sock);
161 isc_task_detach(&cm->task);
163 listener = cm->listener;
164 ns_lwreslistener_unlinkcm(listener, cm);
165 ns_lwdclient_log(50, "destroying manager %p", cm);
166 isc_mem_put(cm->mctx, cm, sizeof (*cm));
167 ns_lwreslistener_detach(&listener);
170 static void
171 process_request(ns_lwdclient_t *client) {
172 lwres_buffer_t b;
173 isc_result_t result;
175 lwres_buffer_init(&b, client->buffer, client->recvlength);
176 lwres_buffer_add(&b, client->recvlength);
178 result = lwres_lwpacket_parseheader(&b, &client->pkt);
179 if (result != ISC_R_SUCCESS) {
180 ns_lwdclient_log(50, "invalid packet header received");
181 goto restart;
184 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
186 switch (client->pkt.opcode) {
187 case LWRES_OPCODE_GETADDRSBYNAME:
188 ns_lwdclient_processgabn(client, &b);
189 return;
190 case LWRES_OPCODE_GETNAMEBYADDR:
191 ns_lwdclient_processgnba(client, &b);
192 return;
193 case LWRES_OPCODE_GETRDATABYNAME:
194 ns_lwdclient_processgrbn(client, &b);
195 return;
196 case LWRES_OPCODE_NOOP:
197 ns_lwdclient_processnoop(client, &b);
198 return;
199 default:
200 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
201 goto restart;
205 * Drop the packet.
207 restart:
208 ns_lwdclient_log(50, "restarting client %p...", client);
209 ns_lwdclient_stateidle(client);
212 void
213 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
214 ns_lwdclient_t *client = ev->ev_arg;
215 ns_lwdclientmgr_t *cm = client->clientmgr;
216 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
218 INSIST(dev->region.base == client->buffer);
219 INSIST(NS_LWDCLIENT_ISRECV(client));
221 NS_LWDCLIENT_SETRECVDONE(client);
223 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
224 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
226 ns_lwdclient_log(50,
227 "event received: task %p, length %u, result %u (%s)",
228 task, dev->n, dev->result,
229 isc_result_totext(dev->result));
231 if (dev->result != ISC_R_SUCCESS) {
232 isc_event_free(&ev);
233 dev = NULL;
236 * Go idle.
238 ns_lwdclient_stateidle(client);
240 return;
243 client->recvlength = dev->n;
244 client->address = dev->address;
245 if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
246 client->pktinfo = dev->pktinfo;
247 client->pktinfo_valid = ISC_TRUE;
248 } else
249 client->pktinfo_valid = ISC_FALSE;
250 isc_event_free(&ev);
251 dev = NULL;
253 ns_lwdclient_startrecv(cm);
255 process_request(client);
259 * This function will start a new recv() on a socket for this client manager.
261 isc_result_t
262 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
263 ns_lwdclient_t *client;
264 isc_result_t result;
265 isc_region_t r;
267 if (SHUTTINGDOWN(cm)) {
268 lwdclientmgr_destroy(cm);
269 return (ISC_R_SUCCESS);
273 * If a recv is already running, don't bother.
275 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
276 return (ISC_R_SUCCESS);
279 * If we have no idle slots, just return success.
281 client = ISC_LIST_HEAD(cm->idle);
282 if (client == NULL)
283 return (ISC_R_SUCCESS);
284 INSIST(NS_LWDCLIENT_ISIDLE(client));
287 * Issue the recv. If it fails, return that it did.
289 r.base = client->buffer;
290 r.length = LWRES_RECVLENGTH;
291 result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
292 client);
293 if (result != ISC_R_SUCCESS)
294 return (result);
297 * Set the flag to say we've issued a recv() call.
299 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
302 * Remove the client from the idle list, and put it on the running
303 * list.
305 NS_LWDCLIENT_SETRECV(client);
306 ISC_LIST_UNLINK(cm->idle, client, link);
307 ISC_LIST_APPEND(cm->running, client, link);
309 return (ISC_R_SUCCESS);
312 static void
313 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
314 ns_lwdclientmgr_t *cm = ev->ev_arg;
315 ns_lwdclient_t *client;
317 REQUIRE(!SHUTTINGDOWN(cm));
319 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
320 task, cm);
323 * run through the idle list and free the clients there. Idle
324 * clients do not have a recv running nor do they have any finds
325 * or similar running.
327 client = ISC_LIST_HEAD(cm->idle);
328 while (client != NULL) {
329 ns_lwdclient_log(50, "destroying client %p, manager %p",
330 client, cm);
331 ISC_LIST_UNLINK(cm->idle, client, link);
332 isc_mem_put(cm->mctx, client, sizeof (*client));
333 client = ISC_LIST_HEAD(cm->idle);
337 * Cancel any pending I/O.
339 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
342 * Run through the running client list and kill off any finds
343 * in progress.
345 client = ISC_LIST_HEAD(cm->running);
346 while (client != NULL) {
347 if (client->find != client->v4find
348 && client->find != client->v6find)
349 dns_adb_cancelfind(client->find);
350 if (client->v4find != NULL)
351 dns_adb_cancelfind(client->v4find);
352 if (client->v6find != NULL)
353 dns_adb_cancelfind(client->v6find);
354 client = ISC_LIST_NEXT(client, link);
357 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
359 isc_event_free(&ev);
363 * Do all the crap needed to move a client from the run queue to the idle
364 * queue.
366 void
367 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
368 ns_lwdclientmgr_t *cm;
370 cm = client->clientmgr;
372 INSIST(client->sendbuf == NULL);
373 INSIST(client->sendlength == 0);
374 INSIST(client->arg == NULL);
375 INSIST(client->v4find == NULL);
376 INSIST(client->v6find == NULL);
378 ISC_LIST_UNLINK(cm->running, client, link);
379 ISC_LIST_PREPEND(cm->idle, client, link);
381 NS_LWDCLIENT_SETIDLE(client);
383 ns_lwdclient_startrecv(cm);
386 void
387 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
388 ns_lwdclient_t *client = ev->ev_arg;
389 ns_lwdclientmgr_t *cm = client->clientmgr;
390 isc_socketevent_t *dev = (isc_socketevent_t *)ev;
392 UNUSED(task);
393 UNUSED(dev);
395 INSIST(NS_LWDCLIENT_ISSEND(client));
396 INSIST(client->sendbuf == dev->region.base);
398 ns_lwdclient_log(50, "task %p for client %p got send-done event",
399 task, client);
401 if (client->sendbuf != client->buffer)
402 lwres_context_freemem(cm->lwctx, client->sendbuf,
403 client->sendlength);
404 client->sendbuf = NULL;
405 client->sendlength = 0;
407 ns_lwdclient_stateidle(client);
409 isc_event_free(&ev);
412 isc_result_t
413 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
414 struct in6_pktinfo *pktinfo;
415 ns_lwdclientmgr_t *cm = client->clientmgr;
417 if (client->pktinfo_valid)
418 pktinfo = &client->pktinfo;
419 else
420 pktinfo = NULL;
421 return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
422 client, &client->address, pktinfo));
425 void
426 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
427 client->clientmgr = cmgr;
428 ISC_LINK_INIT(client, link);
429 NS_LWDCLIENT_SETIDLE(client);
430 client->arg = NULL;
432 client->recvlength = 0;
434 client->sendbuf = NULL;
435 client->sendlength = 0;
437 client->find = NULL;
438 client->v4find = NULL;
439 client->v6find = NULL;
440 client->find_wanted = 0;
442 client->options = 0;
443 client->byaddr = NULL;
445 client->lookup = NULL;
447 client->pktinfo_valid = ISC_FALSE;
449 ISC_LIST_APPEND(cmgr->idle, client, link);