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