Merge from vendor branch OPENSSH:
[dragonfly.git] / contrib / bind-9.2.4rc7 / 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.2.1 2004/03/09 06:09:18 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/lwresd.h>
33 #include <named/lwdclient.h>
34
35 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
36
37 static void
38 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
39
40 void
41 ns_lwdclient_log(int level, const char *format, ...) {
42         va_list args;
43
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);
49 }
50
51 isc_result_t
52 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
53                     isc_taskmgr_t *taskmgr)
54 {
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;
60
61         cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
62         if (cm == NULL)
63                 return (ISC_R_NOMEMORY);
64
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);
77
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;
83
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);
90                 }
91         }
92
93         /*
94          * If we could create no clients, clean up and return.
95          */
96         if (ISC_LIST_EMPTY(cm->idle))
97                 goto errout;
98
99         result = isc_task_create(taskmgr, 0, &cm->task);
100         if (result != ISC_R_SUCCESS)
101                 goto errout;
102
103         /*
104          * This MUST be last, since there is no way to cancel an onshutdown...
105          */
106         result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
107                                      cm);
108         if (result != ISC_R_SUCCESS)
109                 goto errout;
110
111         ns_lwreslistener_linkcm(listener, cm);
112
113         return (ISC_R_SUCCESS);
114
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);
121         }
122
123         if (cm->task != NULL)
124                 isc_task_detach(&cm->task);
125
126         if (cm->lwctx != NULL)
127                 lwres_context_destroy(&cm->lwctx);
128
129         isc_mem_put(lwresd->mctx, cm, sizeof (*cm));
130         return (result);
131 }
132
133 static void
134 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
135         ns_lwdclient_t *client;
136         ns_lwreslistener_t *listener;
137
138         if (!SHUTTINGDOWN(cm))
139                 return;
140
141         /*
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.
145          */
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);
153         }
154
155         if (!ISC_LIST_EMPTY(cm->running))
156                 return;
157
158         lwres_context_destroy(&cm->lwctx);
159         cm->view = NULL;
160         isc_socket_detach(&cm->sock);
161         isc_task_detach(&cm->task);
162
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);
168 }
169
170 static void
171 process_request(ns_lwdclient_t *client) {
172         lwres_buffer_t b;
173         isc_result_t result;
174
175         lwres_buffer_init(&b, client->buffer, client->recvlength);
176         lwres_buffer_add(&b, client->recvlength);
177
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;
182         }
183
184         ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
185
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;
202         }
203
204         /*
205          * Drop the packet.
206          */
207  restart:
208         ns_lwdclient_log(50, "restarting client %p...", client);
209         ns_lwdclient_stateidle(client);
210 }
211
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;
217
218         INSIST(dev->region.base == client->buffer);
219         INSIST(NS_LWDCLIENT_ISRECV(client));
220
221         NS_LWDCLIENT_SETRECVDONE(client);
222
223         INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
224         cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
225
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));
230
231         if (dev->result != ISC_R_SUCCESS) {
232                 isc_event_free(&ev);
233                 dev = NULL;
234
235                 /*
236                  * Go idle.
237                  */
238                 ns_lwdclient_stateidle(client);
239
240                 return;
241         }
242
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;
252
253         ns_lwdclient_startrecv(cm);
254
255         process_request(client);
256 }
257
258 /*
259  * This function will start a new recv() on a socket for this client manager.
260  */
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;
266
267         if (SHUTTINGDOWN(cm)) {
268                 lwdclientmgr_destroy(cm);
269                 return (ISC_R_SUCCESS);
270         }
271
272         /*
273          * If a recv is already running, don't bother.
274          */
275         if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
276                 return (ISC_R_SUCCESS);
277
278         /*
279          * If we have no idle slots, just return success.
280          */
281         client = ISC_LIST_HEAD(cm->idle);
282         if (client == NULL)
283                 return (ISC_R_SUCCESS);
284         INSIST(NS_LWDCLIENT_ISIDLE(client));
285
286         /*
287          * Issue the recv.  If it fails, return that it did.
288          */
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);
295
296         /*
297          * Set the flag to say we've issued a recv() call.
298          */
299         cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
300
301         /*
302          * Remove the client from the idle list, and put it on the running
303          * list.
304          */
305         NS_LWDCLIENT_SETRECV(client);
306         ISC_LIST_UNLINK(cm->idle, client, link);
307         ISC_LIST_APPEND(cm->running, client, link);
308
309         return (ISC_R_SUCCESS);
310 }
311
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;
316
317         REQUIRE(!SHUTTINGDOWN(cm));
318
319         ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
320                          task, cm);
321
322         /*
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.
326          */
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);
334         }
335
336         /*
337          * Cancel any pending I/O.
338          */
339         isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
340
341         /*
342          * Run through the running client list and kill off any finds
343          * in progress.
344          */
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);
355         }
356
357         cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
358
359         isc_event_free(&ev);
360 }
361
362 /*
363  * Do all the crap needed to move a client from the run queue to the idle
364  * queue.
365  */
366 void
367 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
368         ns_lwdclientmgr_t *cm;
369
370         cm = client->clientmgr;
371
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);
377
378         ISC_LIST_UNLINK(cm->running, client, link);
379         ISC_LIST_PREPEND(cm->idle, client, link);
380
381         NS_LWDCLIENT_SETIDLE(client);
382
383         ns_lwdclient_startrecv(cm);
384 }
385
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;
391
392         UNUSED(task);
393         UNUSED(dev);
394
395         INSIST(NS_LWDCLIENT_ISSEND(client));
396         INSIST(client->sendbuf == dev->region.base);
397
398         ns_lwdclient_log(50, "task %p for client %p got send-done event",
399                          task, client);
400
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;
406
407         ns_lwdclient_stateidle(client);
408
409         isc_event_free(&ev);
410 }
411
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;
416
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));
423 }
424
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;
431
432         client->recvlength = 0;
433
434         client->sendbuf = NULL;
435         client->sendlength = 0;
436
437         client->find = NULL;
438         client->v4find = NULL;
439         client->v6find = NULL;
440         client->find_wanted = 0;
441
442         client->options = 0;
443         client->byaddr = NULL;
444
445         client->lookup = NULL;
446
447         client->pktinfo_valid = ISC_FALSE;
448
449         ISC_LIST_APPEND(cmgr->idle, client, link);
450 }