Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / bind-9.3 / bin / named / lwdclient.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: lwdclient.c,v 1.13.12.5 2004/03/08 09:04:15 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/socket.h>
23 #include <isc/string.h>
24 #include <isc/task.h>
25 #include <isc/util.h>
26
27 #include <dns/adb.h>
28 #include <dns/view.h>
29 #include <dns/log.h>
30
31 #include <named/types.h>
32 #include <named/log.h>
33 #include <named/lwresd.h>
34 #include <named/lwdclient.h>
35
36 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
37
38 static void
39 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
40
41 void
42 ns_lwdclient_log(int level, const char *format, ...) {
43         va_list args;
44
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);
49         va_end(args);
50 }
51
52 isc_result_t
53 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
54                     isc_taskmgr_t *taskmgr)
55 {
56         ns_lwresd_t *lwresd = listener->manager;
57         ns_lwdclientmgr_t *cm;
58         ns_lwdclient_t *client;
59         unsigned int i;
60         isc_result_t result = ISC_R_FAILURE;
61
62         cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
63         if (cm == NULL)
64                 return (ISC_R_NOMEMORY);
65
66         cm->listener = NULL;
67         ns_lwreslistener_attach(listener, &cm->listener);
68         cm->mctx = lwresd->mctx;
69         cm->sock = NULL;
70         isc_socket_attach(listener->sock, &cm->sock);
71         cm->view = lwresd->view;
72         cm->lwctx = NULL;
73         cm->task = NULL;
74         cm->flags = 0;
75         ISC_LINK_INIT(cm, link);
76         ISC_LIST_INIT(cm->idle);
77         ISC_LIST_INIT(cm->running);
78
79         if (lwres_context_create(&cm->lwctx, cm->mctx,
80                                  ns__lwresd_memalloc, ns__lwresd_memfree,
81                                  LWRES_CONTEXT_SERVERMODE)
82             != ISC_R_SUCCESS)
83                 goto errout;
84
85         for (i = 0; i < nclients; i++) {
86                 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
87                 if (client != NULL) {
88                         ns_lwdclient_log(50, "created client %p, manager %p",
89                                          client, cm);
90                         ns_lwdclient_initialize(client, cm);
91                 }
92         }
93
94         /*
95          * If we could create no clients, clean up and return.
96          */
97         if (ISC_LIST_EMPTY(cm->idle))
98                 goto errout;
99
100         result = isc_task_create(taskmgr, 0, &cm->task);
101         if (result != ISC_R_SUCCESS)
102                 goto errout;
103
104         /*
105          * This MUST be last, since there is no way to cancel an onshutdown...
106          */
107         result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
108                                      cm);
109         if (result != ISC_R_SUCCESS)
110                 goto errout;
111
112         ns_lwreslistener_linkcm(listener, cm);
113
114         return (ISC_R_SUCCESS);
115
116  errout:
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);
122         }
123
124         if (cm->task != NULL)
125                 isc_task_detach(&cm->task);
126
127         if (cm->lwctx != NULL)
128                 lwres_context_destroy(&cm->lwctx);
129
130         isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
131         return (result);
132 }
133
134 static void
135 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
136         ns_lwdclient_t *client;
137         ns_lwreslistener_t *listener;
138
139         if (!SHUTTINGDOWN(cm))
140                 return;
141
142         /*
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.
146          */
147         client = ISC_LIST_HEAD(cm->idle);
148         while (client != NULL) {
149                 ns_lwdclient_log(50, "destroying client %p, manager %p",
150                                  client, cm);
151                 ISC_LIST_UNLINK(cm->idle, client, link);
152                 isc_mem_put(cm->mctx, client, sizeof(*client));
153                 client = ISC_LIST_HEAD(cm->idle);
154         }
155
156         if (!ISC_LIST_EMPTY(cm->running))
157                 return;
158
159         lwres_context_destroy(&cm->lwctx);
160         cm->view = NULL;
161         isc_socket_detach(&cm->sock);
162         isc_task_detach(&cm->task);
163
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);
169 }
170
171 static void
172 process_request(ns_lwdclient_t *client) {
173         lwres_buffer_t b;
174         isc_result_t result;
175
176         lwres_buffer_init(&b, client->buffer, client->recvlength);
177         lwres_buffer_add(&b, client->recvlength);
178
179         result = lwres_lwpacket_parseheader(&b, &client->pkt);
180         if (result != ISC_R_SUCCESS) {
181                 ns_lwdclient_log(50, "invalid packet header received");
182                 goto restart;
183         }
184
185         ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
186
187         switch (client->pkt.opcode) {
188         case LWRES_OPCODE_GETADDRSBYNAME:
189                 ns_lwdclient_processgabn(client, &b);
190                 return;
191         case LWRES_OPCODE_GETNAMEBYADDR:
192                 ns_lwdclient_processgnba(client, &b);
193                 return;
194         case LWRES_OPCODE_GETRDATABYNAME:
195                 ns_lwdclient_processgrbn(client, &b);
196                 return;
197         case LWRES_OPCODE_NOOP:
198                 ns_lwdclient_processnoop(client, &b);
199                 return;
200         default:
201                 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
202                 goto restart;
203         }
204
205         /*
206          * Drop the packet.
207          */
208  restart:
209         ns_lwdclient_log(50, "restarting client %p...", client);
210         ns_lwdclient_stateidle(client);
211 }
212
213 void
214 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
215         isc_result_t result;
216         ns_lwdclient_t *client = ev->ev_arg;
217         ns_lwdclientmgr_t *cm = client->clientmgr;
218         isc_socketevent_t *dev = (isc_socketevent_t *)ev;
219
220         INSIST(dev->region.base == client->buffer);
221         INSIST(NS_LWDCLIENT_ISRECV(client));
222
223         NS_LWDCLIENT_SETRECVDONE(client);
224
225         INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
226         cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
227
228         ns_lwdclient_log(50,
229                          "event received: task %p, length %u, result %u (%s)",
230                          task, dev->n, dev->result,
231                          isc_result_totext(dev->result));
232
233         if (dev->result != ISC_R_SUCCESS) {
234                 isc_event_free(&ev);
235                 dev = NULL;
236
237                 /*
238                  * Go idle.
239                  */
240                 ns_lwdclient_stateidle(client);
241
242                 return;
243         }
244
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;
250         } else
251                 client->pktinfo_valid = ISC_FALSE;
252         isc_event_free(&ev);
253         dev = NULL;
254
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));
262
263         process_request(client);
264 }
265
266 /*
267  * This function will start a new recv() on a socket for this client manager.
268  */
269 isc_result_t
270 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
271         ns_lwdclient_t *client;
272         isc_result_t result;
273         isc_region_t r;
274
275         if (SHUTTINGDOWN(cm)) {
276                 lwdclientmgr_destroy(cm);
277                 return (ISC_R_SUCCESS);
278         }
279
280         /*
281          * If a recv is already running, don't bother.
282          */
283         if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
284                 return (ISC_R_SUCCESS);
285
286         /*
287          * If we have no idle slots, just return success.
288          */
289         client = ISC_LIST_HEAD(cm->idle);
290         if (client == NULL)
291                 return (ISC_R_SUCCESS);
292         INSIST(NS_LWDCLIENT_ISIDLE(client));
293
294         /*
295          * Issue the recv.  If it fails, return that it did.
296          */
297         r.base = client->buffer;
298         r.length = LWRES_RECVLENGTH;
299         result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
300                                  client);
301         if (result != ISC_R_SUCCESS)
302                 return (result);
303
304         /*
305          * Set the flag to say we've issued a recv() call.
306          */
307         cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
308
309         /*
310          * Remove the client from the idle list, and put it on the running
311          * list.
312          */
313         NS_LWDCLIENT_SETRECV(client);
314         ISC_LIST_UNLINK(cm->idle, client, link);
315         ISC_LIST_APPEND(cm->running, client, link);
316
317         return (ISC_R_SUCCESS);
318 }
319
320 static void
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;
324
325         REQUIRE(!SHUTTINGDOWN(cm));
326
327         ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
328                          task, cm);
329
330         /*
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.
334          */
335         client = ISC_LIST_HEAD(cm->idle);
336         while (client != NULL) {
337                 ns_lwdclient_log(50, "destroying client %p, manager %p",
338                                  client, cm);
339                 ISC_LIST_UNLINK(cm->idle, client, link);
340                 isc_mem_put(cm->mctx, client, sizeof(*client));
341                 client = ISC_LIST_HEAD(cm->idle);
342         }
343
344         /*
345          * Cancel any pending I/O.
346          */
347         isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
348
349         /*
350          * Run through the running client list and kill off any finds
351          * in progress.
352          */
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);
363         }
364
365         cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
366
367         isc_event_free(&ev);
368 }
369
370 /*
371  * Do all the crap needed to move a client from the run queue to the idle
372  * queue.
373  */
374 void
375 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
376         ns_lwdclientmgr_t *cm;
377         isc_result_t result;
378
379         cm = client->clientmgr;
380
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);
386
387         ISC_LIST_UNLINK(cm->running, client, link);
388         ISC_LIST_PREPEND(cm->idle, client, link);
389
390         NS_LWDCLIENT_SETIDLE(client);
391
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));
399 }
400
401 void
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;
406
407         UNUSED(task);
408         UNUSED(dev);
409
410         INSIST(NS_LWDCLIENT_ISSEND(client));
411         INSIST(client->sendbuf == dev->region.base);
412
413         ns_lwdclient_log(50, "task %p for client %p got send-done event",
414                          task, client);
415
416         if (client->sendbuf != client->buffer)
417                 lwres_context_freemem(cm->lwctx, client->sendbuf,
418                                       client->sendlength);
419         client->sendbuf = NULL;
420         client->sendlength = 0;
421
422         ns_lwdclient_stateidle(client);
423
424         isc_event_free(&ev);
425 }
426
427 isc_result_t
428 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
429         struct in6_pktinfo *pktinfo;
430         ns_lwdclientmgr_t *cm = client->clientmgr;
431
432         if (client->pktinfo_valid)
433                 pktinfo = &client->pktinfo;
434         else
435                 pktinfo = NULL;
436         return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
437                                   client, &client->address, pktinfo));
438 }
439
440 void
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);
445         client->arg = NULL;
446
447         client->recvlength = 0;
448
449         client->sendbuf = NULL;
450         client->sendlength = 0;
451
452         client->find = NULL;
453         client->v4find = NULL;
454         client->v6find = NULL;
455         client->find_wanted = 0;
456
457         client->options = 0;
458         client->byaddr = NULL;
459
460         client->lookup = NULL;
461
462         client->pktinfo_valid = ISC_FALSE;
463
464         ISC_LIST_APPEND(cmgr->idle, client, link);
465 }