Merge branch 'vendor/GCC44'
[dragonfly.git] / contrib / bind-9.3 / bin / named / lwresd.c
1 /*
2  * Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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: lwresd.c,v 1.37.2.2.2.8 2006/02/28 06:32:53 marka Exp $ */
19
20 /*
21  * Main program for the Lightweight Resolver Daemon.
22  *
23  * To paraphrase the old saying about X11, "It's not a lightweight deamon
24  * for resolvers, it's a deamon for lightweight resolvers".
25  */
26
27 #include <config.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <isc/list.h>
33 #include <isc/magic.h>
34 #include <isc/mem.h>
35 #include <isc/once.h>
36 #include <isc/print.h>
37 #include <isc/socket.h>
38 #include <isc/task.h>
39 #include <isc/util.h>
40
41 #include <isccfg/namedconf.h>
42
43 #include <dns/log.h>
44 #include <dns/result.h>
45 #include <dns/view.h>
46
47 #include <named/config.h>
48 #include <named/globals.h>
49 #include <named/log.h>
50 #include <named/lwaddr.h>
51 #include <named/lwresd.h>
52 #include <named/lwdclient.h>
53 #include <named/lwsearch.h>
54 #include <named/server.h>
55
56 #define LWRESD_MAGIC            ISC_MAGIC('L', 'W', 'R', 'D')
57 #define VALID_LWRESD(l)         ISC_MAGIC_VALID(l, LWRESD_MAGIC)
58
59 #define LWRESLISTENER_MAGIC     ISC_MAGIC('L', 'W', 'R', 'L')
60 #define VALID_LWRESLISTENER(l)  ISC_MAGIC_VALID(l, LWRESLISTENER_MAGIC)
61
62 /*
63  * The total number of clients we can handle will be NTASKS * NRECVS.
64  */
65 #define NTASKS          2       /* tasks to create to handle lwres queries */
66 #define NRECVS          2       /* max clients per task */
67
68 typedef ISC_LIST(ns_lwreslistener_t) ns_lwreslistenerlist_t;
69
70 static ns_lwreslistenerlist_t listeners;
71 static isc_mutex_t listeners_lock;
72 static isc_once_t once = ISC_ONCE_INIT;
73
74
75 static void
76 initialize_mutex(void) {
77         RUNTIME_CHECK(isc_mutex_init(&listeners_lock) == ISC_R_SUCCESS);
78 }
79
80
81 /*
82  * Wrappers around our memory management stuff, for the lwres functions.
83  */
84 void *
85 ns__lwresd_memalloc(void *arg, size_t size) {
86         return (isc_mem_get(arg, size));
87 }
88
89 void
90 ns__lwresd_memfree(void *arg, void *mem, size_t size) {
91         isc_mem_put(arg, mem, size);
92 }
93
94
95 #define CHECK(op)                                               \
96         do { result = (op);                                     \
97                 if (result != ISC_R_SUCCESS) goto cleanup;      \
98         } while (0)
99
100 static isc_result_t
101 buffer_putstr(isc_buffer_t *b, const char *s) {
102         unsigned int len = strlen(s);
103         if (isc_buffer_availablelength(b) <= len)
104                 return (ISC_R_NOSPACE);
105         isc_buffer_putmem(b, (const unsigned char *)s, len);
106         return (ISC_R_SUCCESS);
107 }
108
109 /*
110  * Convert a resolv.conf file into a config structure.
111  */
112 isc_result_t
113 ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx,
114                            cfg_obj_t **configp)
115 {
116         char text[4096];
117         char str[16];
118         isc_buffer_t b;
119         lwres_context_t *lwctx = NULL;
120         lwres_conf_t *lwc = NULL;
121         isc_sockaddr_t sa;
122         isc_netaddr_t na;
123         int i;
124         isc_result_t result;
125         lwres_result_t lwresult;
126
127         lwctx = NULL;
128         lwresult = lwres_context_create(&lwctx, mctx, ns__lwresd_memalloc,
129                                         ns__lwresd_memfree,
130                                         LWRES_CONTEXT_SERVERMODE);
131         if (lwresult != LWRES_R_SUCCESS) {
132                 result = ISC_R_NOMEMORY;
133                 goto cleanup;
134         }
135
136         lwresult = lwres_conf_parse(lwctx, lwresd_g_resolvconffile);
137         if (lwresult != LWRES_R_SUCCESS) {
138                 result = DNS_R_SYNTAX;
139                 goto cleanup;
140         }
141
142         lwc = lwres_conf_get(lwctx);
143         INSIST(lwc != NULL);
144
145         isc_buffer_init(&b, text, sizeof(text));
146
147         CHECK(buffer_putstr(&b, "options {\n"));
148
149         /*
150          * Build the list of forwarders.
151          */
152         if (lwc->nsnext > 0) {
153                 CHECK(buffer_putstr(&b, "\tforwarders {\n"));
154
155                 for (i = 0; i < lwc->nsnext; i++) {
156                         CHECK(lwaddr_sockaddr_fromlwresaddr(
157                                                         &sa,
158                                                         &lwc->nameservers[i],
159                                                         ns_g_port));
160                         isc_netaddr_fromsockaddr(&na, &sa);
161                         CHECK(buffer_putstr(&b, "\t\t"));
162                         CHECK(isc_netaddr_totext(&na, &b));
163                         CHECK(buffer_putstr(&b, ";\n"));
164                 }
165                 CHECK(buffer_putstr(&b, "\t};\n"));
166         }
167
168         /*
169          * Build the sortlist
170          */
171         if (lwc->sortlistnxt > 0) {
172                 CHECK(buffer_putstr(&b, "\tsortlist {\n"));
173                 CHECK(buffer_putstr(&b, "\t\t{\n"));
174                 CHECK(buffer_putstr(&b, "\t\t\tany;\n"));
175                 CHECK(buffer_putstr(&b, "\t\t\t{\n"));
176                 for (i = 0; i < lwc->sortlistnxt; i++) {
177                         lwres_addr_t *lwaddr = &lwc->sortlist[i].addr;
178                         lwres_addr_t *lwmask = &lwc->sortlist[i].mask;
179                         unsigned int mask;
180
181                         CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwmask, 0));
182                         isc_netaddr_fromsockaddr(&na, &sa);
183                         result = isc_netaddr_masktoprefixlen(&na, &mask);
184                         if (result != ISC_R_SUCCESS) {
185                                 char addrtext[ISC_NETADDR_FORMATSIZE];
186                                 isc_netaddr_format(&na, addrtext,
187                                                    sizeof(addrtext));
188                                 isc_log_write(ns_g_lctx,
189                                               NS_LOGCATEGORY_GENERAL,
190                                               NS_LOGMODULE_LWRESD,
191                                               ISC_LOG_ERROR,
192                                               "processing sortlist: '%s' is "
193                                               "not a valid netmask",
194                                               addrtext);
195                                 goto cleanup;
196                         }
197
198                         CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwaddr, 0));
199                         isc_netaddr_fromsockaddr(&na, &sa);
200
201                         CHECK(buffer_putstr(&b, "\t\t\t\t"));
202                         CHECK(isc_netaddr_totext(&na, &b));
203                         snprintf(str, sizeof(str), "%u", mask);
204                         CHECK(buffer_putstr(&b, "/"));
205                         CHECK(buffer_putstr(&b, str));
206                         CHECK(buffer_putstr(&b, ";\n"));
207                 }
208                 CHECK(buffer_putstr(&b, "\t\t\t};\n"));
209                 CHECK(buffer_putstr(&b, "\t\t};\n"));
210                 CHECK(buffer_putstr(&b, "\t};\n"));
211         }
212
213         CHECK(buffer_putstr(&b, "};\n\n"));
214
215         CHECK(buffer_putstr(&b, "lwres {\n"));
216
217         /*
218          * Build the search path
219          */
220         if (lwc->searchnxt > 0) {
221                 if (lwc->searchnxt > 0) {
222                         CHECK(buffer_putstr(&b, "\tsearch {\n"));
223                         for (i = 0; i < lwc->searchnxt; i++) {
224                                 CHECK(buffer_putstr(&b, "\t\t\""));
225                                 CHECK(buffer_putstr(&b, lwc->search[i]));
226                                 CHECK(buffer_putstr(&b, "\";\n"));
227                         }
228                         CHECK(buffer_putstr(&b, "\t};\n"));
229                 }
230         }
231
232         /*
233          * Build the ndots line
234          */
235         if (lwc->ndots != 1) {
236                 CHECK(buffer_putstr(&b, "\tndots "));
237                 snprintf(str, sizeof(str), "%u", lwc->ndots);
238                 CHECK(buffer_putstr(&b, str));
239                 CHECK(buffer_putstr(&b, ";\n"));
240         }
241
242         /*
243          * Build the listen-on line
244          */
245         if (lwc->lwnext > 0) {
246                 CHECK(buffer_putstr(&b, "\tlisten-on {\n"));
247
248                 for (i = 0; i < lwc->lwnext; i++) {
249                         CHECK(lwaddr_sockaddr_fromlwresaddr(&sa,
250                                                             &lwc->lwservers[i],
251                                                             0));
252                         isc_netaddr_fromsockaddr(&na, &sa);
253                         CHECK(buffer_putstr(&b, "\t\t"));
254                         CHECK(isc_netaddr_totext(&na, &b));
255                         CHECK(buffer_putstr(&b, ";\n"));
256                 }
257                 CHECK(buffer_putstr(&b, "\t};\n"));
258         }
259
260         CHECK(buffer_putstr(&b, "};\n"));
261
262 #if 0
263         printf("%.*s\n",
264                (int)isc_buffer_usedlength(&b),
265                (char *)isc_buffer_base(&b));
266 #endif
267
268         lwres_conf_clear(lwctx);
269         lwres_context_destroy(&lwctx);
270
271         return (cfg_parse_buffer(pctx, &b, &cfg_type_namedconf, configp));
272
273  cleanup:
274
275         if (lwctx != NULL) {
276                 lwres_conf_clear(lwctx);
277                 lwres_context_destroy(&lwctx);
278         }
279
280         return (result);
281 }
282
283
284 /*
285  * Handle lwresd manager objects
286  */
287 isc_result_t
288 ns_lwdmanager_create(isc_mem_t *mctx, const cfg_obj_t *lwres,
289                      ns_lwresd_t **lwresdp)
290 {
291         ns_lwresd_t *lwresd;
292         const char *vname;
293         dns_rdataclass_t vclass;
294         const cfg_obj_t *obj, *viewobj, *searchobj;
295         const cfg_listelt_t *element;
296         isc_result_t result;
297
298         INSIST(lwresdp != NULL && *lwresdp == NULL);
299
300         lwresd = isc_mem_get(mctx, sizeof(ns_lwresd_t));
301         if (lwresd == NULL)
302                 return (ISC_R_NOMEMORY);
303
304         lwresd->mctx = NULL;
305         isc_mem_attach(mctx, &lwresd->mctx);
306         lwresd->view = NULL;
307         lwresd->search = NULL;
308         lwresd->refs = 1;
309
310         obj = NULL;
311         (void)cfg_map_get(lwres, "ndots", &obj);
312         if (obj != NULL)
313                 lwresd->ndots = cfg_obj_asuint32(obj);
314         else
315                 lwresd->ndots = 1;
316
317         RUNTIME_CHECK(isc_mutex_init(&lwresd->lock) == ISC_R_SUCCESS);
318
319         lwresd->shutting_down = ISC_FALSE;
320
321         viewobj = NULL;
322         (void)cfg_map_get(lwres, "view", &viewobj);
323         if (viewobj != NULL) {
324                 vname = cfg_obj_asstring(cfg_tuple_get(viewobj, "name"));
325                 obj = cfg_tuple_get(viewobj, "class");
326                 result = ns_config_getclass(obj, dns_rdataclass_in, &vclass);
327                 if (result != ISC_R_SUCCESS)
328                         goto fail;
329         } else {
330                 vname = "_default";
331                 vclass = dns_rdataclass_in;
332         }
333
334         result = dns_viewlist_find(&ns_g_server->viewlist, vname, vclass,
335                                    &lwresd->view);
336         if (result != ISC_R_SUCCESS) {
337                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
338                               NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
339                               "couldn't find view %s", vname);
340                 goto fail;
341         }
342
343         searchobj = NULL;
344         (void)cfg_map_get(lwres, "search", &searchobj);
345         if (searchobj != NULL) {
346                 lwresd->search = NULL;
347                 result = ns_lwsearchlist_create(lwresd->mctx,
348                                                 &lwresd->search);
349                 if (result != ISC_R_SUCCESS) {
350                         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
351                                       NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
352                                       "couldn't create searchlist");
353                         goto fail;
354                 }
355                 for (element = cfg_list_first(searchobj);
356                      element != NULL;
357                      element = cfg_list_next(element))
358                 {
359                         const cfg_obj_t *search;
360                         const char *searchstr;
361                         isc_buffer_t namebuf;
362                         dns_fixedname_t fname;
363                         dns_name_t *name;
364
365                         search = cfg_listelt_value(element);
366                         searchstr = cfg_obj_asstring(search);
367
368                         dns_fixedname_init(&fname);
369                         name = dns_fixedname_name(&fname);
370                         isc_buffer_init(&namebuf, searchstr,
371                                         strlen(searchstr));
372                         isc_buffer_add(&namebuf, strlen(searchstr));
373                         result = dns_name_fromtext(name, &namebuf,
374                                                    dns_rootname, ISC_FALSE,
375                                                    NULL);
376                         if (result != ISC_R_SUCCESS) {
377                                 isc_log_write(ns_g_lctx,
378                                               NS_LOGCATEGORY_GENERAL,
379                                               NS_LOGMODULE_LWRESD,
380                                               ISC_LOG_WARNING,
381                                               "invalid name %s in searchlist",
382                                               searchstr);
383                                 continue;
384                         }
385
386                         result = ns_lwsearchlist_append(lwresd->search, name);
387                         if (result != ISC_R_SUCCESS) {
388                                 isc_log_write(ns_g_lctx,
389                                               NS_LOGCATEGORY_GENERAL,
390                                               NS_LOGMODULE_LWRESD,
391                                               ISC_LOG_WARNING,
392                                               "couldn't update searchlist");
393                                 goto fail;
394                         }
395                 }
396         }
397
398         lwresd->magic = LWRESD_MAGIC;
399
400         *lwresdp = lwresd;
401         return (ISC_R_SUCCESS);
402
403  fail:
404         if (lwresd->view != NULL)
405                 dns_view_detach(&lwresd->view);
406         if (lwresd->search != NULL)
407                 ns_lwsearchlist_detach(&lwresd->search);
408         if (lwresd->mctx != NULL)
409                 isc_mem_detach(&lwresd->mctx);
410         isc_mem_put(mctx, lwresd, sizeof(ns_lwresd_t));
411         return (result);
412 }
413
414 void
415 ns_lwdmanager_attach(ns_lwresd_t *source, ns_lwresd_t **targetp) {
416         INSIST(VALID_LWRESD(source));
417         INSIST(targetp != NULL && *targetp == NULL);
418
419         LOCK(&source->lock);
420         source->refs++;
421         UNLOCK(&source->lock);
422
423         *targetp = source;
424 }
425
426 void
427 ns_lwdmanager_detach(ns_lwresd_t **lwresdp) {
428         ns_lwresd_t *lwresd;
429         isc_mem_t *mctx;
430         isc_boolean_t done = ISC_FALSE;
431
432         INSIST(lwresdp != NULL && *lwresdp != NULL);
433         INSIST(VALID_LWRESD(*lwresdp));
434
435         lwresd = *lwresdp;
436         *lwresdp = NULL;
437
438         LOCK(&lwresd->lock);
439         INSIST(lwresd->refs > 0);
440         lwresd->refs--;
441         if (lwresd->refs == 0)
442                 done = ISC_TRUE;
443         UNLOCK(&lwresd->lock);
444
445         if (!done)
446                 return;
447
448         dns_view_detach(&lwresd->view);
449         if (lwresd->search != NULL)
450                 ns_lwsearchlist_detach(&lwresd->search);
451         mctx = lwresd->mctx;
452         lwresd->magic = 0;
453         isc_mem_put(mctx, lwresd, sizeof(*lwresd));
454         isc_mem_detach(&mctx);
455 }
456
457
458 /*
459  * Handle listener objects
460  */
461 void
462 ns_lwreslistener_attach(ns_lwreslistener_t *source,
463                         ns_lwreslistener_t **targetp)
464 {
465         INSIST(VALID_LWRESLISTENER(source));
466         INSIST(targetp != NULL && *targetp == NULL);
467
468         LOCK(&source->lock);
469         source->refs++;
470         UNLOCK(&source->lock);
471
472         *targetp = source;
473 }
474
475 void
476 ns_lwreslistener_detach(ns_lwreslistener_t **listenerp) {
477         ns_lwreslistener_t *listener;
478         isc_mem_t *mctx;
479         isc_boolean_t done = ISC_FALSE;
480
481         INSIST(listenerp != NULL && *listenerp != NULL);
482         INSIST(VALID_LWRESLISTENER(*listenerp));
483
484         listener = *listenerp;
485
486         LOCK(&listener->lock);
487         INSIST(listener->refs > 0);
488         listener->refs--;
489         if (listener->refs == 0)
490                 done = ISC_TRUE;
491         UNLOCK(&listener->lock);
492
493         if (!done)
494                 return;
495
496         if (listener->manager != NULL)
497                 ns_lwdmanager_detach(&listener->manager);
498
499         if (listener->sock != NULL)
500                 isc_socket_detach(&listener->sock);
501
502         listener->magic = 0;
503         mctx = listener->mctx;
504         isc_mem_put(mctx, listener, sizeof(*listener));
505         isc_mem_detach(&mctx);
506         listenerp = NULL;
507 }
508
509 static isc_result_t
510 listener_create(isc_mem_t *mctx, ns_lwresd_t *lwresd,
511                 ns_lwreslistener_t **listenerp)
512 {
513         ns_lwreslistener_t *listener;
514
515         REQUIRE(listenerp != NULL && *listenerp == NULL);
516
517         listener = isc_mem_get(mctx, sizeof(ns_lwreslistener_t));
518         if (listener == NULL)
519                 return (ISC_R_NOMEMORY);
520         RUNTIME_CHECK(isc_mutex_init(&listener->lock) == ISC_R_SUCCESS);
521
522         listener->magic = LWRESLISTENER_MAGIC;
523         listener->refs = 1;
524
525         listener->sock = NULL;
526
527         listener->manager = NULL;
528         ns_lwdmanager_attach(lwresd, &listener->manager);
529
530         listener->mctx = NULL;
531         isc_mem_attach(mctx, &listener->mctx);
532
533         ISC_LINK_INIT(listener, link);
534         ISC_LIST_INIT(listener->cmgrs);
535
536         *listenerp = listener;
537         return (ISC_R_SUCCESS);
538 }
539
540 static isc_result_t
541 listener_bind(ns_lwreslistener_t *listener, isc_sockaddr_t *address) {
542         isc_socket_t *sock = NULL;
543         isc_result_t result = ISC_R_SUCCESS;
544         int pf;
545
546         pf = isc_sockaddr_pf(address);
547         if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
548             (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
549                 return (ISC_R_FAMILYNOSUPPORT);
550
551         listener->address = *address;
552
553         if (isc_sockaddr_getport(&listener->address) == 0) {
554                 in_port_t port;
555                 port = lwresd_g_listenport;
556                 if (port == 0)
557                         port = LWRES_UDP_PORT;
558                 isc_sockaddr_setport(&listener->address, port);
559         }
560
561         sock = NULL;
562         result = isc_socket_create(ns_g_socketmgr, pf,
563                                    isc_sockettype_udp, &sock);
564         if (result != ISC_R_SUCCESS) {
565                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
566                               NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
567                               "failed to create lwres socket: %s",
568                               isc_result_totext(result));
569                 return (result);
570         }
571
572         result = isc_socket_bind(sock, &listener->address);
573         if (result != ISC_R_SUCCESS) {
574                 char socktext[ISC_SOCKADDR_FORMATSIZE];
575                 isc_sockaddr_format(&listener->address, socktext,
576                                     sizeof(socktext));
577                 isc_socket_detach(&sock);
578                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
579                               NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
580                               "failed to add lwres socket: %s: %s",
581                               socktext, isc_result_totext(result));
582                 return (result);
583         }
584         listener->sock = sock;
585         return (ISC_R_SUCCESS);
586 }
587
588 static void
589 listener_copysock(ns_lwreslistener_t *oldlistener,
590                   ns_lwreslistener_t *newlistener)
591 {
592         newlistener->address = oldlistener->address;
593         isc_socket_attach(oldlistener->sock, &newlistener->sock);
594 }
595
596 static isc_result_t
597 listener_startclients(ns_lwreslistener_t *listener) {
598         ns_lwdclientmgr_t *cm;
599         unsigned int i;
600         isc_result_t result;
601
602         /*
603          * Create the client managers.
604          */
605         result = ISC_R_SUCCESS;
606         for (i = 0; i < NTASKS && result == ISC_R_SUCCESS; i++)
607                 result = ns_lwdclientmgr_create(listener, NRECVS,
608                                                 ns_g_taskmgr);
609
610         /*
611          * Ensure that we have created at least one.
612          */
613         if (ISC_LIST_EMPTY(listener->cmgrs))
614                 return (result);
615
616         /*
617          * Walk the list of clients and start each one up.
618          */
619         LOCK(&listener->lock);
620         cm = ISC_LIST_HEAD(listener->cmgrs);
621         while (cm != NULL) {
622                 result = ns_lwdclient_startrecv(cm);
623                 if (result != ISC_R_SUCCESS)
624                         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
625                                       NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
626                                       "could not start lwres "
627                                       "client handler: %s",
628                                       isc_result_totext(result));
629                 cm = ISC_LIST_NEXT(cm, link);
630         }
631         UNLOCK(&listener->lock);
632
633         return (ISC_R_SUCCESS);
634 }
635
636 static void
637 listener_shutdown(ns_lwreslistener_t *listener) {
638         ns_lwdclientmgr_t *cm;
639
640         cm = ISC_LIST_HEAD(listener->cmgrs);
641         while (cm != NULL) {
642                 isc_task_shutdown(cm->task);
643                 cm = ISC_LIST_NEXT(cm, link);
644         }
645 }
646
647 static isc_result_t
648 find_listener(isc_sockaddr_t *address, ns_lwreslistener_t **listenerp) {
649         ns_lwreslistener_t *listener;
650
651         INSIST(listenerp != NULL && *listenerp == NULL);
652
653         for (listener = ISC_LIST_HEAD(listeners);
654              listener != NULL;
655              listener = ISC_LIST_NEXT(listener, link))
656         {
657                 if (!isc_sockaddr_equal(address, &listener->address))
658                         continue;
659                 *listenerp = listener;
660                 return (ISC_R_SUCCESS);
661         }
662         return (ISC_R_NOTFOUND);
663 }
664
665 void
666 ns_lwreslistener_unlinkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm)
667 {
668         REQUIRE(VALID_LWRESLISTENER(listener));
669
670         LOCK(&listener->lock);
671         ISC_LIST_UNLINK(listener->cmgrs, cm, link);
672         UNLOCK(&listener->lock);
673 }
674
675 void
676 ns_lwreslistener_linkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm) {
677         REQUIRE(VALID_LWRESLISTENER(listener));
678
679         /*
680          * This does no locking, since it's called early enough that locking
681          * isn't needed.
682          */
683         ISC_LIST_APPEND(listener->cmgrs, cm, link);
684 }
685
686 static isc_result_t
687 configure_listener(isc_sockaddr_t *address, ns_lwresd_t *lwresd,
688                    isc_mem_t *mctx, ns_lwreslistenerlist_t *newlisteners)
689 {
690         ns_lwreslistener_t *listener, *oldlistener = NULL;
691         char socktext[ISC_SOCKADDR_FORMATSIZE];
692         isc_result_t result;
693
694         (void)find_listener(address, &oldlistener);
695         listener = NULL;
696         result = listener_create(mctx, lwresd, &listener);
697         if (result != ISC_R_SUCCESS) {
698                 isc_sockaddr_format(address, socktext, sizeof(socktext));
699                 isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
700                               NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
701                               "lwres failed to configure %s: %s",
702                               socktext, isc_result_totext(result));
703                 return (result);
704         }
705
706         /*
707          * If there's already a listener, don't rebind the socket.
708          */
709         if (oldlistener == NULL) {
710                 result = listener_bind(listener, address);
711                 if (result != ISC_R_SUCCESS) {
712                         ns_lwreslistener_detach(&listener);
713                         return (ISC_R_SUCCESS);
714                 }
715         } else
716                 listener_copysock(oldlistener, listener);
717
718         result = listener_startclients(listener);
719         if (result != ISC_R_SUCCESS) {
720                 isc_sockaddr_format(address, socktext, sizeof(socktext));
721                 isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
722                               NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
723                               "lwres: failed to start %s: %s", socktext,
724                               isc_result_totext(result));
725                 ns_lwreslistener_detach(&listener);
726                 return (ISC_R_SUCCESS);
727         }
728
729         if (oldlistener != NULL) {
730                 /*
731                  * Remove the old listener from the old list and shut it down.
732                  */
733                 ISC_LIST_UNLINK(listeners, oldlistener, link);
734                 listener_shutdown(oldlistener);
735                 ns_lwreslistener_detach(&oldlistener);
736         } else {
737                 isc_sockaddr_format(address, socktext, sizeof(socktext));
738                 isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
739                               NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
740                               "lwres listening on %s", socktext);
741         }
742
743         ISC_LIST_APPEND(*newlisteners, listener, link);
744         return (result);
745 }
746
747 isc_result_t
748 ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config) {
749         const cfg_obj_t *lwreslist = NULL;
750         const cfg_obj_t *lwres = NULL;
751         const cfg_obj_t *listenerslist = NULL;
752         const cfg_listelt_t *element = NULL;
753         ns_lwreslistener_t *listener;
754         ns_lwreslistenerlist_t newlisteners;
755         isc_result_t result;
756         char socktext[ISC_SOCKADDR_FORMATSIZE];
757         isc_sockaddr_t *addrs = NULL;
758         ns_lwresd_t *lwresd = NULL;
759         isc_uint32_t count = 0;
760
761         REQUIRE(mctx != NULL);
762         REQUIRE(config != NULL);
763
764         RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
765
766         ISC_LIST_INIT(newlisteners);
767
768         result = cfg_map_get(config, "lwres", &lwreslist);
769         if (result != ISC_R_SUCCESS)
770                 return (ISC_R_SUCCESS);
771
772         LOCK(&listeners_lock);
773         /*
774          * Run through the new lwres address list, noting sockets that
775          * are already being listened on and moving them to the new list.
776          *
777          * Identifying duplicates addr/port combinations is left to either
778          * the underlying config code, or to the bind attempt getting an
779          * address-in-use error.
780          */
781         for (element = cfg_list_first(lwreslist);
782              element != NULL;
783              element = cfg_list_next(element))
784         {
785                 in_port_t port;
786
787                 lwres = cfg_listelt_value(element);
788                 CHECK(ns_lwdmanager_create(mctx, lwres, &lwresd));
789
790                 port = lwresd_g_listenport;
791                 if (port == 0)
792                         port = LWRES_UDP_PORT;
793
794                 listenerslist = NULL;
795                 (void)cfg_map_get(lwres, "listen-on", &listenerslist);
796                 if (listenerslist == NULL) {
797                         struct in_addr localhost;
798                         isc_sockaddr_t address;
799
800                         localhost.s_addr = htonl(INADDR_LOOPBACK);
801                         isc_sockaddr_fromin(&address, &localhost, port);
802                         CHECK(configure_listener(&address, lwresd, mctx,
803                                                  &newlisteners));
804                 } else {
805                         isc_uint32_t i;
806
807                         CHECK(ns_config_getiplist(config, listenerslist,
808                                                   port, mctx, &addrs, &count));
809                         for (i = 0; i < count; i++)
810                                 CHECK(configure_listener(&addrs[i], lwresd,
811                                                          mctx, &newlisteners));
812                         ns_config_putiplist(mctx, &addrs, count);
813                 }
814                 ns_lwdmanager_detach(&lwresd);
815         }
816
817         /*
818          * Shutdown everything on the listeners list, and remove them from
819          * the list.  Then put all of the new listeners on it.
820          */
821
822         while (!ISC_LIST_EMPTY(listeners)) {
823                 listener = ISC_LIST_HEAD(listeners);
824                 ISC_LIST_UNLINK(listeners, listener, link);
825
826                 isc_sockaddr_format(&listener->address,
827                                     socktext, sizeof(socktext));
828
829                 listener_shutdown(listener);
830                 ns_lwreslistener_detach(&listener);
831
832                 isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
833                               NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
834                               "lwres no longer listening on %s", socktext);
835         }
836
837  cleanup:
838         ISC_LIST_APPENDLIST(listeners, newlisteners, link);
839
840         if (addrs != NULL)
841                 ns_config_putiplist(mctx, &addrs, count);
842
843         if (lwresd != NULL)
844                 ns_lwdmanager_detach(&lwresd);
845
846         UNLOCK(&listeners_lock);
847
848         return (result);
849 }
850
851 void
852 ns_lwresd_shutdown(void) {
853         ns_lwreslistener_t *listener;
854
855         RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
856
857         while (!ISC_LIST_EMPTY(listeners)) {
858                 listener = ISC_LIST_HEAD(listeners);
859                 ISC_LIST_UNLINK(listeners, listener, link);
860                 ns_lwreslistener_detach(&listener);
861         }
862 }