etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / bin / named / lwdclient.c
blobf3e2afcd0216f2c8fa09ffbd0e7426a46690b648
1 /* $NetBSD: lwdclient.c,v 1.4 2014/12/10 04:37:51 christos Exp $ */
3 /*
4 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000, 2001 Internet Software Consortium.
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.
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.
20 /* Id: lwdclient.c,v 1.22 2007/06/18 23:47:18 tbox Exp */
22 /*! \file */
24 #include <config.h>
26 #include <isc/socket.h>
27 #include <isc/string.h>
28 #include <isc/task.h>
29 #include <isc/util.h>
31 #include <dns/adb.h>
32 #include <dns/view.h>
33 #include <dns/log.h>
35 #include <named/types.h>
36 #include <named/log.h>
37 #include <named/lwresd.h>
38 #include <named/lwdclient.h>
40 #define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
42 static void
43 lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
45 void
46 ns_lwdclient_log(int level, const char *format, ...) {
47 va_list args;
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);
56 isc_result_t
57 ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
58 isc_taskmgr_t *taskmgr)
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;
66 cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
67 if (cm == NULL)
68 return (ISC_R_NOMEMORY);
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);
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;
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);
99 * If we could create no clients, clean up and return.
101 if (ISC_LIST_EMPTY(cm->idle))
102 goto errout;
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);
110 * This MUST be last, since there is no way to cancel an onshutdown...
112 result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
113 cm);
114 if (result != ISC_R_SUCCESS)
115 goto errout;
117 ns_lwreslistener_linkcm(listener, cm);
119 return (ISC_R_SUCCESS);
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);
129 if (cm->task != NULL)
130 isc_task_detach(&cm->task);
132 if (cm->lwctx != NULL)
133 lwres_context_destroy(&cm->lwctx);
135 isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
136 return (result);
139 static void
140 lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
141 ns_lwdclient_t *client;
142 ns_lwreslistener_t *listener;
144 if (!SHUTTINGDOWN(cm))
145 return;
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.
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);
161 if (!ISC_LIST_EMPTY(cm->running))
162 return;
164 lwres_context_destroy(&cm->lwctx);
165 cm->view = NULL;
166 isc_socket_detach(&cm->sock);
167 isc_task_detach(&cm->task);
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);
176 static void
177 process_request(ns_lwdclient_t *client) {
178 lwres_buffer_t b;
179 isc_result_t result;
181 lwres_buffer_init(&b, client->buffer, client->recvlength);
182 lwres_buffer_add(&b, client->recvlength);
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;
190 ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
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;
211 * Drop the packet.
213 restart:
214 ns_lwdclient_log(50, "restarting client %p...", client);
215 ns_lwdclient_stateidle(client);
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;
225 INSIST(dev->region.base == client->buffer);
226 INSIST(NS_LWDCLIENT_ISRECV(client));
228 NS_LWDCLIENT_SETRECVDONE(client);
230 INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
231 cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
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));
238 if (dev->result != ISC_R_SUCCESS) {
239 isc_event_free(&ev);
240 dev = NULL;
243 * Go idle.
245 ns_lwdclient_stateidle(client);
247 return;
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;
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));
268 process_request(client);
272 * This function will start a new recv() on a socket for this client manager.
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;
280 if (SHUTTINGDOWN(cm)) {
281 lwdclientmgr_destroy(cm);
282 return (ISC_R_SUCCESS);
286 * If a recv is already running, don't bother.
288 if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
289 return (ISC_R_SUCCESS);
292 * If we have no idle slots, just return success.
294 client = ISC_LIST_HEAD(cm->idle);
295 if (client == NULL)
296 return (ISC_R_SUCCESS);
297 INSIST(NS_LWDCLIENT_ISIDLE(client));
300 * Issue the recv. If it fails, return that it did.
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);
310 * Set the flag to say we've issued a recv() call.
312 cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
315 * Remove the client from the idle list, and put it on the running
316 * list.
318 NS_LWDCLIENT_SETRECV(client);
319 ISC_LIST_UNLINK(cm->idle, client, link);
320 ISC_LIST_APPEND(cm->running, client, link);
322 return (ISC_R_SUCCESS);
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;
330 REQUIRE(!SHUTTINGDOWN(cm));
332 ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
333 task, cm);
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.
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);
350 * Cancel any pending I/O.
352 isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
355 * Run through the running client list and kill off any finds
356 * in progress.
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);
370 cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
372 isc_event_free(&ev);
376 * Do all the crap needed to move a client from the run queue to the idle
377 * queue.
379 void
380 ns_lwdclient_stateidle(ns_lwdclient_t *client) {
381 ns_lwdclientmgr_t *cm;
382 isc_result_t result;
384 cm = client->clientmgr;
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);
392 ISC_LIST_UNLINK(cm->running, client, link);
393 ISC_LIST_PREPEND(cm->idle, client, link);
395 NS_LWDCLIENT_SETIDLE(client);
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));
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;
412 UNUSED(task);
413 UNUSED(dev);
415 INSIST(NS_LWDCLIENT_ISSEND(client));
416 INSIST(client->sendbuf == dev->region.base);
418 ns_lwdclient_log(50, "task %p for client %p got send-done event",
419 task, client);
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;
427 ns_lwdclient_stateidle(client);
429 isc_event_free(&ev);
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;
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));
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;
452 client->recvlength = 0;
454 client->sendbuf = NULL;
455 client->sendlength = 0;
457 client->find = NULL;
458 client->v4find = NULL;
459 client->v6find = NULL;
460 client->find_wanted = 0;
462 client->options = 0;
463 client->byaddr = NULL;
465 client->lookup = NULL;
467 client->pktinfo_valid = ISC_FALSE;
469 ISC_LIST_APPEND(cmgr->idle, client, link);