diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index b308a6b5a9..045beaf43d 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -49,7 +49,7 @@ EXTERN isc_timermgr_t * ns_g_timermgr INIT(NULL); EXTERN isc_socketmgr_t * ns_g_socketmgr INIT(NULL); EXTERN omapi_object_t * ns_g_omapimgr INIT(NULL); EXTERN const char * ns_g_version INIT(VERSION); -EXTERN in_port_t ns_g_port INIT(53); +EXTERN in_port_t ns_g_port INIT(0); EXTERN ns_server_t * ns_g_server INIT(NULL); diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h index d0535217d5..871b090819 100644 --- a/bin/named/include/named/interfacemgr.h +++ b/bin/named/include/named/interfacemgr.h @@ -90,6 +90,13 @@ ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, dns_dispatchmgr_t *dispatchmgr, ns_clientmgr_t *clientmgr, ns_interfacemgr_t **mgrp); +/* + * Create a new interface manager. + * + * Initially, the new manager will not listen on any interfaces. + * Call ns_interfacemgr_setlistenon() and/or ns_interfacemgr_setlistenon6() + * to set nonempty listen-on lists. + */ void ns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target); @@ -113,10 +120,17 @@ ns_interfacemgr_scan(ns_interfacemgr_t *mgr); */ void -ns_interfacemgr_setlistenon(ns_interfacemgr_t *mgr, ns_listenlist_t *value); +ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value); /* - * Set the "listen-on" list of 'mgr' to 'value'. - * The previous listen-on list is freed. + * Set the IPv4 "listen-on" list of 'mgr' to 'value'. + * The previous IPv4 listen-on list is freed. + */ + +void +ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value); +/* + * Set the IPv6 "listen-on" list of 'mgr' to 'value'. + * The previous IPv6 listen-on list is freed. */ isc_result_t diff --git a/bin/named/include/named/listenlist.h b/bin/named/include/named/listenlist.h index 00ab6cbc06..8066a809e7 100644 --- a/bin/named/include/named/listenlist.h +++ b/bin/named/include/named/listenlist.h @@ -60,18 +60,33 @@ struct ns_listenlist { isc_result_t ns_listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl, ns_listenelt_t **target); +/* + * Create a listen-on list element. + */ void ns_listenelt_destroy(ns_listenelt_t *elt); +/* + * Destroy a listen-on list element. + */ isc_result_t ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target); +/* + * Create a new, empty listen-on list. + */ void ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target); +/* + * Attach '*target' to '*source'. + */ void ns_listenlist_detach(ns_listenlist_t **listp); +/* + * Detach 'listp'. + */ isc_result_t ns_listenlist_default(isc_mem_t *mctx, in_port_t port, diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index 46c2597895..67a1bba3da 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -45,7 +45,8 @@ struct ns_interfacemgr { dns_dispatchmgr_t * dispatchmgr; ns_clientmgr_t * clientmgr; /* Client manager. */ unsigned int generation; /* Current generation no. */ - ns_listenlist_t * listenon; + ns_listenlist_t * listenon4; + ns_listenlist_t * listenon6; dns_aclenv_t aclenv; /* Localhost/localnets ACLs */ ISC_LIST(ns_interface_t) interfaces; /* List of interfaces. */ }; @@ -80,13 +81,17 @@ ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, mgr->dispatchmgr = dispatchmgr; mgr->clientmgr = clientmgr; mgr->generation = 1; - mgr->listenon = NULL; + mgr->listenon4 = NULL; + mgr->listenon6 = NULL; ISC_LIST_INIT(mgr->interfaces); - result = ns_listenlist_default(mctx, ns_g_port, - &mgr->listenon); + /* + * The listen-on lists are initially empty. + */ + result = ns_listenlist_create(mctx, &mgr->listenon4); if (result != ISC_R_SUCCESS) goto cleanup_mem; + ns_listenlist_attach(mgr->listenon4, &mgr->listenon6); result = dns_aclenv_init(mctx, &mgr->aclenv); if (result != ISC_R_SUCCESS) @@ -98,7 +103,8 @@ ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, return (ISC_R_SUCCESS); cleanup_listenon: - ns_listenlist_detach(&mgr->listenon); + ns_listenlist_detach(&mgr->listenon4); + ns_listenlist_detach(&mgr->listenon6); cleanup_mem: isc_mem_put(mctx, mgr, sizeof(*mgr)); return (result); @@ -108,7 +114,8 @@ static void ns_interfacemgr_destroy(ns_interfacemgr_t *mgr) { REQUIRE(NS_INTERFACEMGR_VALID(mgr)); dns_aclenv_destroy(&mgr->aclenv); - ns_listenlist_detach(&mgr->listenon); + ns_listenlist_detach(&mgr->listenon4); + ns_listenlist_detach(&mgr->listenon6); isc_mutex_destroy(&mgr->lock); mgr->magic = 0; isc_mem_put(mgr->mctx, mgr, sizeof *mgr); @@ -505,7 +512,7 @@ do_ipv4(ns_interfacemgr_t *mgr) { if (result != ISC_R_SUCCESS) goto ignore_interface; - for (le = ISC_LIST_HEAD(mgr->listenon->elts); + for (le = ISC_LIST_HEAD(mgr->listenon4->elts); le != NULL; le = ISC_LIST_NEXT(le, link)) { @@ -577,23 +584,69 @@ do_ipv4(ns_interfacemgr_t *mgr) { isc_interfaceiter_destroy(&iter); } +static isc_boolean_t +listenon_is_ip6_none(ns_listenlist_t *p) { + ns_listenelt_t *elt; + if (ISC_LIST_EMPTY(p->elts)) + return (ISC_TRUE); /* No listen-on-v6 statements */ + elt = ISC_LIST_HEAD(p->elts); + if (ISC_LIST_NEXT(elt, link) != NULL) + return (ISC_FALSE); /* More than one listen-on-v6 stmt */ + if (elt->acl->length == 0) + return (ISC_TRUE); /* listen-on-v6 { } */ + if (elt->acl->length > 1) + return (ISC_FALSE); /* listen-on-v6 { ...; ...; } */ + if (elt->acl->elements[0].negative == ISC_TRUE && + elt->acl->elements[0].type == dns_aclelementtype_any) + return (ISC_TRUE); /* listen-on-v6 { none; } */ + return (ISC_FALSE); /* All others */ +} + +static isc_boolean_t +listenon_is_ip6_any(ns_listenlist_t *p, in_port_t *portp) { + ns_listenelt_t *elt; + if (ISC_LIST_EMPTY(p->elts)) + return (ISC_FALSE); /* No listen-on-v6 statements */ + elt = ISC_LIST_HEAD(p->elts); + if (ISC_LIST_NEXT(elt, link) != NULL) + return (ISC_FALSE); /* More than one listen-on-v6 stmt */ + if (elt->acl->length != 1) + return (ISC_FALSE); + if (elt->acl->elements[0].negative == ISC_FALSE && + elt->acl->elements[0].type == dns_aclelementtype_any) { + *portp = elt->port; + return (ISC_TRUE); /* listen-on-v6 { any; } */ + } + return (ISC_FALSE); /* All others */ +} + static void do_ipv6(ns_interfacemgr_t *mgr) { isc_result_t result; ns_interface_t *ifp; isc_sockaddr_t listen_addr; struct in6_addr in6a; + in_port_t port; + if (listenon_is_ip6_none(mgr->listenon6)) + return; + + if (! listenon_is_ip6_any(mgr->listenon6, &port)) { + isc_log_write(IFMGR_COMMON_LOGARGS, + ISC_LOG_ERROR, + "bad IPv6 listen-on list: must be 'any' or 'none'"); + return; + } + in6a = in6addr_any; - isc_sockaddr_fromin6(&listen_addr, &in6a, ns_g_port); + isc_sockaddr_fromin6(&listen_addr, &in6a, port); ifp = find_matching_interface(mgr, &listen_addr); if (ifp != NULL) { ifp->generation = mgr->generation; } else { isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, - "listening on IPv6 interfaces, port %u", - ns_g_port); + "listening on IPv6 interfaces, port %u", port); result = ns_interface_setup(mgr, &listen_addr, "", &ifp); if (result != ISC_R_SUCCESS) { isc_log_write(IFMGR_COMMON_LOGARGS, @@ -640,10 +693,18 @@ ns_interfacemgr_scan(ns_interfacemgr_t *mgr) { } void -ns_interfacemgr_setlistenon(ns_interfacemgr_t *mgr, ns_listenlist_t *value) { +ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value) { LOCK(&mgr->lock); - ns_listenlist_detach(&mgr->listenon); - ns_listenlist_attach(value, &mgr->listenon); + ns_listenlist_detach(&mgr->listenon4); + ns_listenlist_attach(value, &mgr->listenon4); + UNLOCK(&mgr->lock); +} + +void +ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value) { + LOCK(&mgr->lock); + ns_listenlist_detach(&mgr->listenon6); + ns_listenlist_attach(value, &mgr->listenon6); UNLOCK(&mgr->lock); } diff --git a/bin/named/server.c b/bin/named/server.c index 1fb39e2070..20fb0ecf86 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -400,6 +400,7 @@ configure_view(dns_view_t *view, dns_c_ctx_t *cctx, dns_c_view_t *cview, isc_mem_t *cmctx; dns_dispatch_t *dispatch4 = NULL; dns_dispatch_t *dispatch6 = NULL; + in_port_t port; REQUIRE(DNS_VIEW_VALID(view)); @@ -407,7 +408,15 @@ configure_view(dns_view_t *view, dns_c_ctx_t *cctx, dns_c_view_t *cview, cmctx = NULL; RWLOCK(&view->conflock, isc_rwlocktype_write); - + + /* + * Set the view's port number for outgoing queries. + */ + result = dns_c_ctx_getport(cctx, &port); + if (result != ISC_R_SUCCESS) + port = 53; + dns_view_setdstport(view, port); + /* * Configure the view's cache. Try to reuse an existing * cache if possible, otherwise create a new cache. @@ -1102,6 +1111,7 @@ load_configuration(const char *filename, ns_server_t *server, dns_dispatch_t *dispatchv6 = NULL; char *pidfilename; isc_uint32_t interface_interval; + in_port_t listen_port; dns_aclconfctx_init(&aclconfctx); @@ -1154,6 +1164,16 @@ load_configuration(const char *filename, ns_server_t *server, dns_zonemgr_settransfersperns(server->zonemgr, transfersperns); } + /* + * Determine which port to use for listening for incoming connections. + */ + if (ns_g_port != 0) { + listen_port = ns_g_port; + } else { + result = dns_c_ctx_getport(cctx, &listen_port); + if (result != ISC_R_SUCCESS) + listen_port = 53; + } /* * Configure the interface manager according to the "listen-on" * statement. @@ -1171,12 +1191,21 @@ load_configuration(const char *filename, ns_server_t *server, &listenon); } else { /* Not specified, use default. */ - CHECK(ns_listenlist_default(ns_g_mctx, ns_g_port, + CHECK(ns_listenlist_default(ns_g_mctx, listen_port, &listenon)); } - ns_interfacemgr_setlistenon(server->interfacemgr, listenon); + ns_interfacemgr_setlistenon4(server->interfacemgr, listenon); ns_listenlist_detach(&listenon); } + /* + * Ditto for IPv6. + */ + { + ns_listenlist_t *listenon = NULL; + CHECK(ns_listenlist_default(ns_g_mctx, listen_port, &listenon)); + ns_interfacemgr_setlistenon6(server->interfacemgr, listenon); + ns_listenlist_detach(&listenon); + } /* * Rescan the interface list to pick up changes in the diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 219b724cfe..16c51f0f0c 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -303,7 +303,26 @@ dns_view_setkeyring(dns_view_t *view, dns_tsig_keyring_t *ring); * The static TSIG keyring of 'view' is 'ring'. */ - +void +dns_view_setdstport(dns_view_t *view, in_port_t dstport); +/* + * Set the view's destination port. This is the port to + * which outgoing queries are sent. The default is 53, + * the standard DNS port. + * + * Requires: + * + * 'view' is a valid view. + * + * 'dstport' is a valid TCP/UDP port number. + * + * Ensures: + * External name servers will be assumed to be listning + * on 'dstport'. For servers whose address has already + * obtained obtained at the time of the call, the view may + * continue to use the previously set port until the address + * times out from the view's address database. + */ isc_result_t diff --git a/lib/dns/view.c b/lib/dns/view.c index 61b3085841..e7b8d66a01 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -515,6 +515,12 @@ dns_view_setkeyring(dns_view_t *view, dns_tsig_keyring_t *ring) { view->statickeys = ring; } +void +dns_view_setdstport(dns_view_t *view, in_port_t dstport) { + REQUIRE(DNS_VIEW_VALID(view)); + view->dstport = dstport; +} + isc_result_t dns_view_addzone(dns_view_t *view, dns_zone_t *zone) { isc_result_t result;