lwdclient.c revision 222395
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 40static void 41lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev); 42 43void 44ns_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 54isc_result_t 55ns_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 137static void 138lwdclientmgr_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 174static void 175process_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 216void 217ns_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 */ 272isc_result_t 273ns_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 323static void 324lwdclientmgr_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 */ 377void 378ns_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 404void 405ns_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 430isc_result_t 431ns_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 443void 444ns_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} 469