]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/bind9/bin/named/lwdclient.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / bind9 / bin / named / lwdclient.c
1 /*
2  * Copyright (C) 2004, 2005  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.17.18.2 2005/04/29 00:15:23 marka 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
106         /*
107          * This MUST be last, since there is no way to cancel an onshutdown...
108          */
109         result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
110                                      cm);
111         if (result != ISC_R_SUCCESS)
112                 goto errout;
113
114         ns_lwreslistener_linkcm(listener, cm);
115
116         return (ISC_R_SUCCESS);
117
118  errout:
119         client = ISC_LIST_HEAD(cm->idle);
120         while (client != NULL) {
121                 ISC_LIST_UNLINK(cm->idle, client, link);
122                 isc_mem_put(lwresd->mctx, client, sizeof(*client));
123                 client = ISC_LIST_HEAD(cm->idle);
124         }
125
126         if (cm->task != NULL)
127                 isc_task_detach(&cm->task);
128
129         if (cm->lwctx != NULL)
130                 lwres_context_destroy(&cm->lwctx);
131
132         isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
133         return (result);
134 }
135
136 static void
137 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
138         ns_lwdclient_t *client;
139         ns_lwreslistener_t *listener;
140
141         if (!SHUTTINGDOWN(cm))
142                 return;
143
144         /*
145          * run through the idle list and free the clients there.  Idle
146          * clients do not have a recv running nor do they have any finds
147          * or similar running.
148          */
149         client = ISC_LIST_HEAD(cm->idle);
150         while (client != NULL) {
151                 ns_lwdclient_log(50, "destroying client %p, manager %p",
152                                  client, cm);
153                 ISC_LIST_UNLINK(cm->idle, client, link);
154                 isc_mem_put(cm->mctx, client, sizeof(*client));
155                 client = ISC_LIST_HEAD(cm->idle);
156         }
157
158         if (!ISC_LIST_EMPTY(cm->running))
159                 return;
160
161         lwres_context_destroy(&cm->lwctx);
162         cm->view = NULL;
163         isc_socket_detach(&cm->sock);
164         isc_task_detach(&cm->task);
165
166         listener = cm->listener;
167         ns_lwreslistener_unlinkcm(listener, cm);
168         ns_lwdclient_log(50, "destroying manager %p", cm);
169         isc_mem_put(cm->mctx, cm, sizeof(*cm));
170         ns_lwreslistener_detach(&listener);
171 }
172
173 static void
174 process_request(ns_lwdclient_t *client) {
175         lwres_buffer_t b;
176         isc_result_t result;
177
178         lwres_buffer_init(&b, client->buffer, client->recvlength);
179         lwres_buffer_add(&b, client->recvlength);
180
181         result = lwres_lwpacket_parseheader(&b, &client->pkt);
182         if (result != ISC_R_SUCCESS) {
183                 ns_lwdclient_log(50, "invalid packet header received");
184                 goto restart;
185         }
186
187         ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
188
189         switch (client->pkt.opcode) {
190         case LWRES_OPCODE_GETADDRSBYNAME:
191                 ns_lwdclient_processgabn(client, &b);
192                 return;
193         case LWRES_OPCODE_GETNAMEBYADDR:
194                 ns_lwdclient_processgnba(client, &b);
195                 return;
196         case LWRES_OPCODE_GETRDATABYNAME:
197                 ns_lwdclient_processgrbn(client, &b);
198                 return;
199         case LWRES_OPCODE_NOOP:
200                 ns_lwdclient_processnoop(client, &b);
201                 return;
202         default:
203                 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
204                 goto restart;
205         }
206
207         /*
208          * Drop the packet.
209          */
210  restart:
211         ns_lwdclient_log(50, "restarting client %p...", client);
212         ns_lwdclient_stateidle(client);
213 }
214
215 void
216 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
217         isc_result_t result;
218         ns_lwdclient_t *client = ev->ev_arg;
219         ns_lwdclientmgr_t *cm = client->clientmgr;
220         isc_socketevent_t *dev = (isc_socketevent_t *)ev;
221
222         INSIST(dev->region.base == client->buffer);
223         INSIST(NS_LWDCLIENT_ISRECV(client));
224
225         NS_LWDCLIENT_SETRECVDONE(client);
226
227         INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
228         cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
229
230         ns_lwdclient_log(50,
231                          "event received: task %p, length %u, result %u (%s)",
232                          task, dev->n, dev->result,
233                          isc_result_totext(dev->result));
234
235         if (dev->result != ISC_R_SUCCESS) {
236                 isc_event_free(&ev);
237                 dev = NULL;
238
239                 /*
240                  * Go idle.
241                  */
242                 ns_lwdclient_stateidle(client);
243
244                 return;
245         }
246
247         client->recvlength = dev->n;
248         client->address = dev->address;
249         if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
250                 client->pktinfo = dev->pktinfo;
251                 client->pktinfo_valid = ISC_TRUE;
252         } else
253                 client->pktinfo_valid = ISC_FALSE;
254         isc_event_free(&ev);
255         dev = NULL;
256
257         result = ns_lwdclient_startrecv(cm);
258         if (result != ISC_R_SUCCESS)
259                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
260                               NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
261                               "could not start lwres "
262                               "client handler: %s",
263                               isc_result_totext(result));
264
265         process_request(client);
266 }
267
268 /*
269  * This function will start a new recv() on a socket for this client manager.
270  */
271 isc_result_t
272 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
273         ns_lwdclient_t *client;
274         isc_result_t result;
275         isc_region_t r;
276
277         if (SHUTTINGDOWN(cm)) {
278                 lwdclientmgr_destroy(cm);
279                 return (ISC_R_SUCCESS);
280         }
281
282         /*
283          * If a recv is already running, don't bother.
284          */
285         if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
286                 return (ISC_R_SUCCESS);
287
288         /*
289          * If we have no idle slots, just return success.
290          */
291         client = ISC_LIST_HEAD(cm->idle);
292         if (client == NULL)
293                 return (ISC_R_SUCCESS);
294         INSIST(NS_LWDCLIENT_ISIDLE(client));
295
296         /*
297          * Issue the recv.  If it fails, return that it did.
298          */
299         r.base = client->buffer;
300         r.length = LWRES_RECVLENGTH;
301         result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
302                                  client);
303         if (result != ISC_R_SUCCESS)
304                 return (result);
305
306         /*
307          * Set the flag to say we've issued a recv() call.
308          */
309         cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
310
311         /*
312          * Remove the client from the idle list, and put it on the running
313          * list.
314          */
315         NS_LWDCLIENT_SETRECV(client);
316         ISC_LIST_UNLINK(cm->idle, client, link);
317         ISC_LIST_APPEND(cm->running, client, link);
318
319         return (ISC_R_SUCCESS);
320 }
321
322 static void
323 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
324         ns_lwdclientmgr_t *cm = ev->ev_arg;
325         ns_lwdclient_t *client;
326
327         REQUIRE(!SHUTTINGDOWN(cm));
328
329         ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
330                          task, cm);
331
332         /*
333          * run through the idle list and free the clients there.  Idle
334          * clients do not have a recv running nor do they have any finds
335          * or similar running.
336          */
337         client = ISC_LIST_HEAD(cm->idle);
338         while (client != NULL) {
339                 ns_lwdclient_log(50, "destroying client %p, manager %p",
340                                  client, cm);
341                 ISC_LIST_UNLINK(cm->idle, client, link);
342                 isc_mem_put(cm->mctx, client, sizeof(*client));
343                 client = ISC_LIST_HEAD(cm->idle);
344         }
345
346         /*
347          * Cancel any pending I/O.
348          */
349         isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
350
351         /*
352          * Run through the running client list and kill off any finds
353          * in progress.
354          */
355         client = ISC_LIST_HEAD(cm->running);
356         while (client != NULL) {
357                 if (client->find != client->v4find
358                     && client->find != client->v6find)
359                         dns_adb_cancelfind(client->find);
360                 if (client->v4find != NULL)
361                         dns_adb_cancelfind(client->v4find);
362                 if (client->v6find != NULL)
363                         dns_adb_cancelfind(client->v6find);
364                 client = ISC_LIST_NEXT(client, link);
365         }
366
367         cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
368
369         isc_event_free(&ev);
370 }
371
372 /*
373  * Do all the crap needed to move a client from the run queue to the idle
374  * queue.
375  */
376 void
377 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
378         ns_lwdclientmgr_t *cm;
379         isc_result_t result;
380
381         cm = client->clientmgr;
382
383         INSIST(client->sendbuf == NULL);
384         INSIST(client->sendlength == 0);
385         INSIST(client->arg == NULL);
386         INSIST(client->v4find == NULL);
387         INSIST(client->v6find == NULL);
388
389         ISC_LIST_UNLINK(cm->running, client, link);
390         ISC_LIST_PREPEND(cm->idle, client, link);
391
392         NS_LWDCLIENT_SETIDLE(client);
393
394         result = ns_lwdclient_startrecv(cm);
395         if (result != ISC_R_SUCCESS)
396                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
397                               NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
398                               "could not start lwres "
399                               "client handler: %s",
400                               isc_result_totext(result));
401 }
402
403 void
404 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
405         ns_lwdclient_t *client = ev->ev_arg;
406         ns_lwdclientmgr_t *cm = client->clientmgr;
407         isc_socketevent_t *dev = (isc_socketevent_t *)ev;
408
409         UNUSED(task);
410         UNUSED(dev);
411
412         INSIST(NS_LWDCLIENT_ISSEND(client));
413         INSIST(client->sendbuf == dev->region.base);
414
415         ns_lwdclient_log(50, "task %p for client %p got send-done event",
416                          task, client);
417
418         if (client->sendbuf != client->buffer)
419                 lwres_context_freemem(cm->lwctx, client->sendbuf,
420                                       client->sendlength);
421         client->sendbuf = NULL;
422         client->sendlength = 0;
423
424         ns_lwdclient_stateidle(client);
425
426         isc_event_free(&ev);
427 }
428
429 isc_result_t
430 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
431         struct in6_pktinfo *pktinfo;
432         ns_lwdclientmgr_t *cm = client->clientmgr;
433
434         if (client->pktinfo_valid)
435                 pktinfo = &client->pktinfo;
436         else
437                 pktinfo = NULL;
438         return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
439                                   client, &client->address, pktinfo));
440 }
441
442 void
443 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
444         client->clientmgr = cmgr;
445         ISC_LINK_INIT(client, link);
446         NS_LWDCLIENT_SETIDLE(client);
447         client->arg = NULL;
448
449         client->recvlength = 0;
450
451         client->sendbuf = NULL;
452         client->sendlength = 0;
453
454         client->find = NULL;
455         client->v4find = NULL;
456         client->v6find = NULL;
457         client->find_wanted = 0;
458
459         client->options = 0;
460         client->byaddr = NULL;
461
462         client->lookup = NULL;
463
464         client->pktinfo_valid = ISC_FALSE;
465
466         ISC_LIST_APPEND(cmgr->idle, client, link);
467 }