1 /* $NetBSD: lwdclient.c,v 1.4 2014/12/10 04:37:51 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000, 2001 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: lwdclient.c,v 1.22 2007/06/18 23:47:18 tbox Exp */ 21 22 /*! \file */ 23 24 #include <config.h> 25 26 #include <isc/socket.h> 27 #include <isc/string.h> 28 #include <isc/task.h> 29 #include <isc/util.h> 30 31 #include <dns/adb.h> 32 #include <dns/view.h> 33 #include <dns/log.h> 34 35 #include <named/types.h> 36 #include <named/log.h> 37 #include <named/lwresd.h> 38 #include <named/lwdclient.h> 39 40 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0) 41 42 static void 43 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev); 44 45 void 46 ns_lwdclient_log(int level, const char *format, ...) { 47 va_list args; 48 49 va_start(args, format); 50 isc_log_vwrite(dns_lctx, 51 DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, 52 ISC_LOG_DEBUG(level), format, args); 53 va_end(args); 54 } 55 56 isc_result_t 57 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients, 58 isc_taskmgr_t *taskmgr) 59 { 60 ns_lwresd_t *lwresd = listener->manager; 61 ns_lwdclientmgr_t *cm; 62 ns_lwdclient_t *client; 63 unsigned int i; 64 isc_result_t result = ISC_R_FAILURE; 65 66 cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t)); 67 if (cm == NULL) 68 return (ISC_R_NOMEMORY); 69 70 cm->listener = NULL; 71 ns_lwreslistener_attach(listener, &cm->listener); 72 cm->mctx = lwresd->mctx; 73 cm->sock = NULL; 74 isc_socket_attach(listener->sock, &cm->sock); 75 cm->view = lwresd->view; 76 cm->lwctx = NULL; 77 cm->task = NULL; 78 cm->flags = 0; 79 ISC_LINK_INIT(cm, link); 80 ISC_LIST_INIT(cm->idle); 81 ISC_LIST_INIT(cm->running); 82 83 if (lwres_context_create(&cm->lwctx, cm->mctx, 84 ns__lwresd_memalloc, ns__lwresd_memfree, 85 LWRES_CONTEXT_SERVERMODE) 86 != ISC_R_SUCCESS) 87 goto errout; 88 89 for (i = 0; i < nclients; i++) { 90 client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t)); 91 if (client != NULL) { 92 ns_lwdclient_log(50, "created client %p, manager %p", 93 client, cm); 94 ns_lwdclient_initialize(client, cm); 95 } 96 } 97 98 /* 99 * If we could create no clients, clean up and return. 100 */ 101 if (ISC_LIST_EMPTY(cm->idle)) 102 goto errout; 103 104 result = isc_task_create(taskmgr, 0, &cm->task); 105 if (result != ISC_R_SUCCESS) 106 goto errout; 107 isc_task_setname(cm->task, "lwdclient", NULL); 108 109 /* 110 * This MUST be last, since there is no way to cancel an onshutdown... 111 */ 112 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback, 113 cm); 114 if (result != ISC_R_SUCCESS) 115 goto errout; 116 117 ns_lwreslistener_linkcm(listener, cm); 118 119 return (ISC_R_SUCCESS); 120 121 errout: 122 client = ISC_LIST_HEAD(cm->idle); 123 while (client != NULL) { 124 ISC_LIST_UNLINK(cm->idle, client, link); 125 isc_mem_put(lwresd->mctx, client, sizeof(*client)); 126 client = ISC_LIST_HEAD(cm->idle); 127 } 128 129 if (cm->task != NULL) 130 isc_task_detach(&cm->task); 131 132 if (cm->lwctx != NULL) 133 lwres_context_destroy(&cm->lwctx); 134 135 isc_mem_put(lwresd->mctx, cm, sizeof(*cm)); 136 return (result); 137 } 138 139 static void 140 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) { 141 ns_lwdclient_t *client; 142 ns_lwreslistener_t *listener; 143 144 if (!SHUTTINGDOWN(cm)) 145 return; 146 147 /* 148 * run through the idle list and free the clients there. Idle 149 * clients do not have a recv running nor do they have any finds 150 * or similar running. 151 */ 152 client = ISC_LIST_HEAD(cm->idle); 153 while (client != NULL) { 154 ns_lwdclient_log(50, "destroying client %p, manager %p", 155 client, cm); 156 ISC_LIST_UNLINK(cm->idle, client, link); 157 isc_mem_put(cm->mctx, client, sizeof(*client)); 158 client = ISC_LIST_HEAD(cm->idle); 159 } 160 161 if (!ISC_LIST_EMPTY(cm->running)) 162 return; 163 164 lwres_context_destroy(&cm->lwctx); 165 cm->view = NULL; 166 isc_socket_detach(&cm->sock); 167 isc_task_detach(&cm->task); 168 169 listener = cm->listener; 170 ns_lwreslistener_unlinkcm(listener, cm); 171 ns_lwdclient_log(50, "destroying manager %p", cm); 172 isc_mem_put(cm->mctx, cm, sizeof(*cm)); 173 ns_lwreslistener_detach(&listener); 174 } 175 176 static void 177 process_request(ns_lwdclient_t *client) { 178 lwres_buffer_t b; 179 isc_result_t result; 180 181 lwres_buffer_init(&b, client->buffer, client->recvlength); 182 lwres_buffer_add(&b, client->recvlength); 183 184 result = lwres_lwpacket_parseheader(&b, &client->pkt); 185 if (result != ISC_R_SUCCESS) { 186 ns_lwdclient_log(50, "invalid packet header received"); 187 goto restart; 188 } 189 190 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode); 191 192 switch (client->pkt.opcode) { 193 case LWRES_OPCODE_GETADDRSBYNAME: 194 ns_lwdclient_processgabn(client, &b); 195 return; 196 case LWRES_OPCODE_GETNAMEBYADDR: 197 ns_lwdclient_processgnba(client, &b); 198 return; 199 case LWRES_OPCODE_GETRDATABYNAME: 200 ns_lwdclient_processgrbn(client, &b); 201 return; 202 case LWRES_OPCODE_NOOP: 203 ns_lwdclient_processnoop(client, &b); 204 return; 205 default: 206 ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode); 207 goto restart; 208 } 209 210 /* 211 * Drop the packet. 212 */ 213 restart: 214 ns_lwdclient_log(50, "restarting client %p...", client); 215 ns_lwdclient_stateidle(client); 216 } 217 218 void 219 ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) { 220 isc_result_t result; 221 ns_lwdclient_t *client = ev->ev_arg; 222 ns_lwdclientmgr_t *cm = client->clientmgr; 223 isc_socketevent_t *dev = (isc_socketevent_t *)ev; 224 225 INSIST(dev->region.base == client->buffer); 226 INSIST(NS_LWDCLIENT_ISRECV(client)); 227 228 NS_LWDCLIENT_SETRECVDONE(client); 229 230 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0); 231 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING; 232 233 ns_lwdclient_log(50, 234 "event received: task %p, length %u, result %u (%s)", 235 task, dev->n, dev->result, 236 isc_result_totext(dev->result)); 237 238 if (dev->result != ISC_R_SUCCESS) { 239 isc_event_free(&ev); 240 dev = NULL; 241 242 /* 243 * Go idle. 244 */ 245 ns_lwdclient_stateidle(client); 246 247 return; 248 } 249 250 client->recvlength = dev->n; 251 client->address = dev->address; 252 if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) { 253 client->pktinfo = dev->pktinfo; 254 client->pktinfo_valid = ISC_TRUE; 255 } else 256 client->pktinfo_valid = ISC_FALSE; 257 isc_event_free(&ev); 258 dev = NULL; 259 260 result = ns_lwdclient_startrecv(cm); 261 if (result != ISC_R_SUCCESS) 262 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 263 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR, 264 "could not start lwres " 265 "client handler: %s", 266 isc_result_totext(result)); 267 268 process_request(client); 269 } 270 271 /* 272 * This function will start a new recv() on a socket for this client manager. 273 */ 274 isc_result_t 275 ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) { 276 ns_lwdclient_t *client; 277 isc_result_t result; 278 isc_region_t r; 279 280 if (SHUTTINGDOWN(cm)) { 281 lwdclientmgr_destroy(cm); 282 return (ISC_R_SUCCESS); 283 } 284 285 /* 286 * If a recv is already running, don't bother. 287 */ 288 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0) 289 return (ISC_R_SUCCESS); 290 291 /* 292 * If we have no idle slots, just return success. 293 */ 294 client = ISC_LIST_HEAD(cm->idle); 295 if (client == NULL) 296 return (ISC_R_SUCCESS); 297 INSIST(NS_LWDCLIENT_ISIDLE(client)); 298 299 /* 300 * Issue the recv. If it fails, return that it did. 301 */ 302 r.base = client->buffer; 303 r.length = LWRES_RECVLENGTH; 304 result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv, 305 client); 306 if (result != ISC_R_SUCCESS) 307 return (result); 308 309 /* 310 * Set the flag to say we've issued a recv() call. 311 */ 312 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING; 313 314 /* 315 * Remove the client from the idle list, and put it on the running 316 * list. 317 */ 318 NS_LWDCLIENT_SETRECV(client); 319 ISC_LIST_UNLINK(cm->idle, client, link); 320 ISC_LIST_APPEND(cm->running, client, link); 321 322 return (ISC_R_SUCCESS); 323 } 324 325 static void 326 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) { 327 ns_lwdclientmgr_t *cm = ev->ev_arg; 328 ns_lwdclient_t *client; 329 330 REQUIRE(!SHUTTINGDOWN(cm)); 331 332 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p", 333 task, cm); 334 335 /* 336 * run through the idle list and free the clients there. Idle 337 * clients do not have a recv running nor do they have any finds 338 * or similar running. 339 */ 340 client = ISC_LIST_HEAD(cm->idle); 341 while (client != NULL) { 342 ns_lwdclient_log(50, "destroying client %p, manager %p", 343 client, cm); 344 ISC_LIST_UNLINK(cm->idle, client, link); 345 isc_mem_put(cm->mctx, client, sizeof(*client)); 346 client = ISC_LIST_HEAD(cm->idle); 347 } 348 349 /* 350 * Cancel any pending I/O. 351 */ 352 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL); 353 354 /* 355 * Run through the running client list and kill off any finds 356 * in progress. 357 */ 358 client = ISC_LIST_HEAD(cm->running); 359 while (client != NULL) { 360 if (client->find != client->v4find 361 && client->find != client->v6find) 362 dns_adb_cancelfind(client->find); 363 if (client->v4find != NULL) 364 dns_adb_cancelfind(client->v4find); 365 if (client->v6find != NULL) 366 dns_adb_cancelfind(client->v6find); 367 client = ISC_LIST_NEXT(client, link); 368 } 369 370 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN; 371 372 isc_event_free(&ev); 373 } 374 375 /* 376 * Do all the crap needed to move a client from the run queue to the idle 377 * queue. 378 */ 379 void 380 ns_lwdclient_stateidle(ns_lwdclient_t *client) { 381 ns_lwdclientmgr_t *cm; 382 isc_result_t result; 383 384 cm = client->clientmgr; 385 386 INSIST(client->sendbuf == NULL); 387 INSIST(client->sendlength == 0); 388 INSIST(client->arg == NULL); 389 INSIST(client->v4find == NULL); 390 INSIST(client->v6find == NULL); 391 392 ISC_LIST_UNLINK(cm->running, client, link); 393 ISC_LIST_PREPEND(cm->idle, client, link); 394 395 NS_LWDCLIENT_SETIDLE(client); 396 397 result = ns_lwdclient_startrecv(cm); 398 if (result != ISC_R_SUCCESS) 399 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 400 NS_LOGMODULE_LWRESD, ISC_LOG_ERROR, 401 "could not start lwres " 402 "client handler: %s", 403 isc_result_totext(result)); 404 } 405 406 void 407 ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) { 408 ns_lwdclient_t *client = ev->ev_arg; 409 ns_lwdclientmgr_t *cm = client->clientmgr; 410 isc_socketevent_t *dev = (isc_socketevent_t *)ev; 411 412 UNUSED(task); 413 UNUSED(dev); 414 415 INSIST(NS_LWDCLIENT_ISSEND(client)); 416 INSIST(client->sendbuf == dev->region.base); 417 418 ns_lwdclient_log(50, "task %p for client %p got send-done event", 419 task, client); 420 421 if (client->sendbuf != client->buffer) 422 lwres_context_freemem(cm->lwctx, client->sendbuf, 423 client->sendlength); 424 client->sendbuf = NULL; 425 client->sendlength = 0; 426 427 ns_lwdclient_stateidle(client); 428 429 isc_event_free(&ev); 430 } 431 432 isc_result_t 433 ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) { 434 struct in6_pktinfo *pktinfo; 435 ns_lwdclientmgr_t *cm = client->clientmgr; 436 437 if (client->pktinfo_valid) 438 pktinfo = &client->pktinfo; 439 else 440 pktinfo = NULL; 441 return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send, 442 client, &client->address, pktinfo)); 443 } 444 445 void 446 ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) { 447 client->clientmgr = cmgr; 448 ISC_LINK_INIT(client, link); 449 NS_LWDCLIENT_SETIDLE(client); 450 client->arg = NULL; 451 452 client->recvlength = 0; 453 454 client->sendbuf = NULL; 455 client->sendlength = 0; 456 457 client->find = NULL; 458 client->v4find = NULL; 459 client->v6find = NULL; 460 client->find_wanted = 0; 461 462 client->options = 0; 463 client->byaddr = NULL; 464 465 client->lookup = NULL; 466 467 client->pktinfo_valid = ISC_FALSE; 468 469 ISC_LIST_APPEND(cmgr->idle, client, link); 470 } 471