xref: /minix/external/bsd/bind/dist/bin/named/lwdclient.c (revision 00b67f09)
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
ns_lwdclient_log(int level,const char * format,...)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
ns_lwdclientmgr_create(ns_lwreslistener_t * listener,unsigned int nclients,isc_taskmgr_t * taskmgr)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
lwdclientmgr_destroy(ns_lwdclientmgr_t * cm)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
process_request(ns_lwdclient_t * client)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
ns_lwdclient_recv(isc_task_t * task,isc_event_t * ev)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
ns_lwdclient_startrecv(ns_lwdclientmgr_t * cm)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
lwdclientmgr_shutdown_callback(isc_task_t * task,isc_event_t * ev)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
ns_lwdclient_stateidle(ns_lwdclient_t * client)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
ns_lwdclient_send(isc_task_t * task,isc_event_t * ev)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
ns_lwdclient_sendreply(ns_lwdclient_t * client,isc_region_t * r)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
ns_lwdclient_initialize(ns_lwdclient_t * client,ns_lwdclientmgr_t * cmgr)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