lwdclient.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000, 2001  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: lwdclient.c,v 1.22 2007/06/18 23:47:18 tbox Exp $ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <isc/socket.h>
00025 #include <isc/string.h>
00026 #include <isc/task.h>
00027 #include <isc/util.h>
00028 
00029 #include <dns/adb.h>
00030 #include <dns/view.h>
00031 #include <dns/log.h>
00032 
00033 #include <named/types.h>
00034 #include <named/log.h>
00035 #include <named/lwresd.h>
00036 #include <named/lwdclient.h>
00037 
00038 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
00039 
00040 static void
00041 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
00042 
00043 void
00044 ns_lwdclient_log(int level, const char *format, ...) {
00045         va_list args;
00046 
00047         va_start(args, format);
00048         isc_log_vwrite(dns_lctx,
00049                        DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
00050                        ISC_LOG_DEBUG(level), format, args);
00051         va_end(args);
00052 }
00053 
00054 isc_result_t
00055 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
00056                     isc_taskmgr_t *taskmgr)
00057 {
00058         ns_lwresd_t *lwresd = listener->manager;
00059         ns_lwdclientmgr_t *cm;
00060         ns_lwdclient_t *client;
00061         unsigned int i;
00062         isc_result_t result = ISC_R_FAILURE;
00063 
00064         cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
00065         if (cm == NULL)
00066                 return (ISC_R_NOMEMORY);
00067 
00068         cm->listener = NULL;
00069         ns_lwreslistener_attach(listener, &cm->listener);
00070         cm->mctx = lwresd->mctx;
00071         cm->sock = NULL;
00072         isc_socket_attach(listener->sock, &cm->sock);
00073         cm->view = lwresd->view;
00074         cm->lwctx = NULL;
00075         cm->task = NULL;
00076         cm->flags = 0;
00077         ISC_LINK_INIT(cm, link);
00078         ISC_LIST_INIT(cm->idle);
00079         ISC_LIST_INIT(cm->running);
00080 
00081         if (lwres_context_create(&cm->lwctx, cm->mctx,
00082                                  ns__lwresd_memalloc, ns__lwresd_memfree,
00083                                  LWRES_CONTEXT_SERVERMODE)
00084             != ISC_R_SUCCESS)
00085                 goto errout;
00086 
00087         for (i = 0; i < nclients; i++) {
00088                 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
00089                 if (client != NULL) {
00090                         ns_lwdclient_log(50, "created client %p, manager %p",
00091                                          client, cm);
00092                         ns_lwdclient_initialize(client, cm);
00093                 }
00094         }
00095 
00096         /*
00097          * If we could create no clients, clean up and return.
00098          */
00099         if (ISC_LIST_EMPTY(cm->idle))
00100                 goto errout;
00101 
00102         result = isc_task_create(taskmgr, 0, &cm->task);
00103         if (result != ISC_R_SUCCESS)
00104                 goto errout;
00105         isc_task_setname(cm->task, "lwdclient", NULL);
00106 
00107         /*
00108          * This MUST be last, since there is no way to cancel an onshutdown...
00109          */
00110         result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
00111                                      cm);
00112         if (result != ISC_R_SUCCESS)
00113                 goto errout;
00114 
00115         ns_lwreslistener_linkcm(listener, cm);
00116 
00117         return (ISC_R_SUCCESS);
00118 
00119  errout:
00120         client = ISC_LIST_HEAD(cm->idle);
00121         while (client != NULL) {
00122                 ISC_LIST_UNLINK(cm->idle, client, link);
00123                 isc_mem_put(lwresd->mctx, client, sizeof(*client));
00124                 client = ISC_LIST_HEAD(cm->idle);
00125         }
00126 
00127         if (cm->task != NULL)
00128                 isc_task_detach(&cm->task);
00129 
00130         if (cm->lwctx != NULL)
00131                 lwres_context_destroy(&cm->lwctx);
00132 
00133         isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
00134         return (result);
00135 }
00136 
00137 static void
00138 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
00139         ns_lwdclient_t *client;
00140         ns_lwreslistener_t *listener;
00141 
00142         if (!SHUTTINGDOWN(cm))
00143                 return;
00144 
00145         /*
00146          * run through the idle list and free the clients there.  Idle
00147          * clients do not have a recv running nor do they have any finds
00148          * or similar running.
00149          */
00150         client = ISC_LIST_HEAD(cm->idle);
00151         while (client != NULL) {
00152                 ns_lwdclient_log(50, "destroying client %p, manager %p",
00153                                  client, cm);
00154                 ISC_LIST_UNLINK(cm->idle, client, link);
00155                 isc_mem_put(cm->mctx, client, sizeof(*client));
00156                 client = ISC_LIST_HEAD(cm->idle);
00157         }
00158 
00159         if (!ISC_LIST_EMPTY(cm->running))
00160                 return;
00161 
00162         lwres_context_destroy(&cm->lwctx);
00163         cm->view = NULL;
00164         isc_socket_detach(&cm->sock);
00165         isc_task_detach(&cm->task);
00166 
00167         listener = cm->listener;
00168         ns_lwreslistener_unlinkcm(listener, cm);
00169         ns_lwdclient_log(50, "destroying manager %p", cm);
00170         isc_mem_put(cm->mctx, cm, sizeof(*cm));
00171         ns_lwreslistener_detach(&listener);
00172 }
00173 
00174 static void
00175 process_request(ns_lwdclient_t *client) {
00176         lwres_buffer_t b;
00177         isc_result_t result;
00178 
00179         lwres_buffer_init(&b, client->buffer, client->recvlength);
00180         lwres_buffer_add(&b, client->recvlength);
00181 
00182         result = lwres_lwpacket_parseheader(&b, &client->pkt);
00183         if (result != ISC_R_SUCCESS) {
00184                 ns_lwdclient_log(50, "invalid packet header received");
00185                 goto restart;
00186         }
00187 
00188         ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
00189 
00190         switch (client->pkt.opcode) {
00191         case LWRES_OPCODE_GETADDRSBYNAME:
00192                 ns_lwdclient_processgabn(client, &b);
00193                 return;
00194         case LWRES_OPCODE_GETNAMEBYADDR:
00195                 ns_lwdclient_processgnba(client, &b);
00196                 return;
00197         case LWRES_OPCODE_GETRDATABYNAME:
00198                 ns_lwdclient_processgrbn(client, &b);
00199                 return;
00200         case LWRES_OPCODE_NOOP:
00201                 ns_lwdclient_processnoop(client, &b);
00202                 return;
00203         default:
00204                 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
00205                 goto restart;
00206         }
00207 
00208         /*
00209          * Drop the packet.
00210          */
00211  restart:
00212         ns_lwdclient_log(50, "restarting client %p...", client);
00213         ns_lwdclient_stateidle(client);
00214 }
00215 
00216 void
00217 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
00218         isc_result_t result;
00219         ns_lwdclient_t *client = ev->ev_arg;
00220         ns_lwdclientmgr_t *cm = client->clientmgr;
00221         isc_socketevent_t *dev = (isc_socketevent_t *)ev;
00222 
00223         INSIST(dev->region.base == client->buffer);
00224         INSIST(NS_LWDCLIENT_ISRECV(client));
00225 
00226         NS_LWDCLIENT_SETRECVDONE(client);
00227 
00228         INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
00229         cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
00230 
00231         ns_lwdclient_log(50,
00232                          "event received: task %p, length %u, result %u (%s)",
00233                          task, dev->n, dev->result,
00234                          isc_result_totext(dev->result));
00235 
00236         if (dev->result != ISC_R_SUCCESS) {
00237                 isc_event_free(&ev);
00238                 dev = NULL;
00239 
00240                 /*
00241                  * Go idle.
00242                  */
00243                 ns_lwdclient_stateidle(client);
00244 
00245                 return;
00246         }
00247 
00248         client->recvlength = dev->n;
00249         client->address = dev->address;
00250         if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
00251                 client->pktinfo = dev->pktinfo;
00252                 client->pktinfo_valid = ISC_TRUE;
00253         } else
00254                 client->pktinfo_valid = ISC_FALSE;
00255         isc_event_free(&ev);
00256         dev = NULL;
00257 
00258         result = ns_lwdclient_startrecv(cm);
00259         if (result != ISC_R_SUCCESS)
00260                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
00261                               NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
00262                               "could not start lwres "
00263                               "client handler: %s",
00264                               isc_result_totext(result));
00265 
00266         process_request(client);
00267 }
00268 
00269 /*
00270  * This function will start a new recv() on a socket for this client manager.
00271  */
00272 isc_result_t
00273 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
00274         ns_lwdclient_t *client;
00275         isc_result_t result;
00276         isc_region_t r;
00277 
00278         if (SHUTTINGDOWN(cm)) {
00279                 lwdclientmgr_destroy(cm);
00280                 return (ISC_R_SUCCESS);
00281         }
00282 
00283         /*
00284          * If a recv is already running, don't bother.
00285          */
00286         if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
00287                 return (ISC_R_SUCCESS);
00288 
00289         /*
00290          * If we have no idle slots, just return success.
00291          */
00292         client = ISC_LIST_HEAD(cm->idle);
00293         if (client == NULL)
00294                 return (ISC_R_SUCCESS);
00295         INSIST(NS_LWDCLIENT_ISIDLE(client));
00296 
00297         /*
00298          * Issue the recv.  If it fails, return that it did.
00299          */
00300         r.base = client->buffer;
00301         r.length = LWRES_RECVLENGTH;
00302         result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
00303                                  client);
00304         if (result != ISC_R_SUCCESS)
00305                 return (result);
00306 
00307         /*
00308          * Set the flag to say we've issued a recv() call.
00309          */
00310         cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
00311 
00312         /*
00313          * Remove the client from the idle list, and put it on the running
00314          * list.
00315          */
00316         NS_LWDCLIENT_SETRECV(client);
00317         ISC_LIST_UNLINK(cm->idle, client, link);
00318         ISC_LIST_APPEND(cm->running, client, link);
00319 
00320         return (ISC_R_SUCCESS);
00321 }
00322 
00323 static void
00324 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
00325         ns_lwdclientmgr_t *cm = ev->ev_arg;
00326         ns_lwdclient_t *client;
00327 
00328         REQUIRE(!SHUTTINGDOWN(cm));
00329 
00330         ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
00331                          task, cm);
00332 
00333         /*
00334          * run through the idle list and free the clients there.  Idle
00335          * clients do not have a recv running nor do they have any finds
00336          * or similar running.
00337          */
00338         client = ISC_LIST_HEAD(cm->idle);
00339         while (client != NULL) {
00340                 ns_lwdclient_log(50, "destroying client %p, manager %p",
00341                                  client, cm);
00342                 ISC_LIST_UNLINK(cm->idle, client, link);
00343                 isc_mem_put(cm->mctx, client, sizeof(*client));
00344                 client = ISC_LIST_HEAD(cm->idle);
00345         }
00346 
00347         /*
00348          * Cancel any pending I/O.
00349          */
00350         isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
00351 
00352         /*
00353          * Run through the running client list and kill off any finds
00354          * in progress.
00355          */
00356         client = ISC_LIST_HEAD(cm->running);
00357         while (client != NULL) {
00358                 if (client->find != client->v4find
00359                     && client->find != client->v6find)
00360                         dns_adb_cancelfind(client->find);
00361                 if (client->v4find != NULL)
00362                         dns_adb_cancelfind(client->v4find);
00363                 if (client->v6find != NULL)
00364                         dns_adb_cancelfind(client->v6find);
00365                 client = ISC_LIST_NEXT(client, link);
00366         }
00367 
00368         cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
00369 
00370         isc_event_free(&ev);
00371 }
00372 
00373 /*
00374  * Do all the crap needed to move a client from the run queue to the idle
00375  * queue.
00376  */
00377 void
00378 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
00379         ns_lwdclientmgr_t *cm;
00380         isc_result_t result;
00381 
00382         cm = client->clientmgr;
00383 
00384         INSIST(client->sendbuf == NULL);
00385         INSIST(client->sendlength == 0);
00386         INSIST(client->arg == NULL);
00387         INSIST(client->v4find == NULL);
00388         INSIST(client->v6find == NULL);
00389 
00390         ISC_LIST_UNLINK(cm->running, client, link);
00391         ISC_LIST_PREPEND(cm->idle, client, link);
00392 
00393         NS_LWDCLIENT_SETIDLE(client);
00394 
00395         result = ns_lwdclient_startrecv(cm);
00396         if (result != ISC_R_SUCCESS)
00397                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
00398                               NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
00399                               "could not start lwres "
00400                               "client handler: %s",
00401                               isc_result_totext(result));
00402 }
00403 
00404 void
00405 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
00406         ns_lwdclient_t *client = ev->ev_arg;
00407         ns_lwdclientmgr_t *cm = client->clientmgr;
00408         isc_socketevent_t *dev = (isc_socketevent_t *)ev;
00409 
00410         UNUSED(task);
00411         UNUSED(dev);
00412 
00413         INSIST(NS_LWDCLIENT_ISSEND(client));
00414         INSIST(client->sendbuf == dev->region.base);
00415 
00416         ns_lwdclient_log(50, "task %p for client %p got send-done event",
00417                          task, client);
00418 
00419         if (client->sendbuf != client->buffer)
00420                 lwres_context_freemem(cm->lwctx, client->sendbuf,
00421                                       client->sendlength);
00422         client->sendbuf = NULL;
00423         client->sendlength = 0;
00424 
00425         ns_lwdclient_stateidle(client);
00426 
00427         isc_event_free(&ev);
00428 }
00429 
00430 isc_result_t
00431 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
00432         struct in6_pktinfo *pktinfo;
00433         ns_lwdclientmgr_t *cm = client->clientmgr;
00434 
00435         if (client->pktinfo_valid)
00436                 pktinfo = &client->pktinfo;
00437         else
00438                 pktinfo = NULL;
00439         return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
00440                                   client, &client->address, pktinfo));
00441 }
00442 
00443 void
00444 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
00445         client->clientmgr = cmgr;
00446         ISC_LINK_INIT(client, link);
00447         NS_LWDCLIENT_SETIDLE(client);
00448         client->arg = NULL;
00449 
00450         client->recvlength = 0;
00451 
00452         client->sendbuf = NULL;
00453         client->sendlength = 0;
00454 
00455         client->find = NULL;
00456         client->v4find = NULL;
00457         client->v6find = NULL;
00458         client->find_wanted = 0;
00459 
00460         client->options = 0;
00461         client->byaddr = NULL;
00462 
00463         client->lookup = NULL;
00464 
00465         client->pktinfo_valid = ISC_FALSE;
00466 
00467         ISC_LIST_APPEND(cmgr->idle, client, link);
00468 }

Generated on Tue Apr 28 17:40:54 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha