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