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