From 3bdf879a5301bce17e0a955585eb3decc541ba79 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 30 Aug 2002 02:05:30 +0000 Subject: [PATCH] developer: jinmei reviewer: marka 1371 [bug] notify-source-v6, transfer-source-v6 and query-source-v6 with explict addresses and using the same ports as named was listening on could interfere with nameds ability to answer queries sent to those addresses. --- CHANGES | 6 + bin/named/include/named/interfacemgr.h | 16 +- bin/named/interfacemgr.c | 201 ++++++++++++++++++------- bin/named/server.c | 134 ++++++++++++++++- 4 files changed, 297 insertions(+), 60 deletions(-) diff --git a/CHANGES b/CHANGES index 8238110f91..5f9a618030 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +1371 [bug] notify-source-v6, transfer-source-v6 and + query-source-v6 with explict addresses and using the + same ports as named was listening on could interfere + with nameds ability to answer queries sent to those + addresses. + 1370. [bug] dig '+[no]recurse' was incorrectly documented. 1369. [bug] Adding an NS record as the lexicographically last diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h index c8b1a47a80..5ddb8f1303 100644 --- a/bin/named/include/named/interfacemgr.h +++ b/bin/named/include/named/interfacemgr.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.h,v 1.23 2001/08/28 03:58:00 marka Exp $ */ +/* $Id: interfacemgr.h,v 1.24 2002/08/30 02:05:30 marka Exp $ */ #ifndef NAMED_INTERFACEMGR_H #define NAMED_INTERFACEMGR_H 1 @@ -120,6 +120,20 @@ ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose); * in named.conf. */ +void +ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list, + isc_boolean_t verbose); +/* + * Similar to ns_interfacemgr_scan(), but this function also tries to see the + * need for an explicit listen-on when a list element in 'list' is going to + * override an already-listening a wildcard interface. + * + * This function does not update localhost and localnets ACLs. + * + * This should be called once on server startup, after configuring views and + * zones. + */ + void ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value); /* diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index c538e2a128..1183fad5f0 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.c,v 1.69 2002/08/17 03:00:49 marka Exp $ */ +/* $Id: interfacemgr.c,v 1.70 2002/08/30 02:05:29 marka Exp $ */ #include @@ -339,7 +339,8 @@ ns_interface_accepttcp(ns_interface_t *ifp) { static isc_result_t ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, - const char *name, ns_interface_t **ifpret) + const char *name, ns_interface_t **ifpret, + isc_boolean_t accept_tcp) { isc_result_t result; ns_interface_t *ifp = NULL; @@ -353,15 +354,17 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, if (result != ISC_R_SUCCESS) goto cleanup_interface; - result = ns_interface_accepttcp(ifp); - if (result != ISC_R_SUCCESS) { - /* - * XXXRTH We don't currently have a way to easily stop dispatch - * service, so we return currently return ISC_R_SUCCESS (the - * UDP stuff will work even if TCP creation failed). This will - * be fixed later. - */ - result = ISC_R_SUCCESS; + if (accept_tcp == ISC_TRUE) { + result = ns_interface_accepttcp(ifp); + if (result != ISC_R_SUCCESS) { + /* + * XXXRTH We don't currently have a way to easily stop + * dispatch service, so we return currently return + * ISC_R_SUCCESS (the UDP stuff will work even if TCP + * creation failed). This will be fixed later. + */ + result = ISC_R_SUCCESS; + } } *ifpret = ifp; return (ISC_R_SUCCESS); @@ -489,16 +492,65 @@ listenon_is_ip6_any(ns_listenelt_t *elt) { } static isc_result_t -do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { +setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) { + isc_result_t result; + dns_aclelement_t elt; + unsigned int family; + unsigned int prefixlen; + + family = interface->address.family; + + elt.type = dns_aclelementtype_ipprefix; + elt.negative = ISC_FALSE; + elt.u.ip_prefix.address = interface->address; + elt.u.ip_prefix.prefixlen = (family == AF_INET) ? 32 : 128; + result = dns_acl_appendelement(mgr->aclenv.localhost, &elt); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_netaddr_masktoprefixlen(&interface->netmask, + &prefixlen); + + /* Non contigious netmasks not allowed by IPv6 arch. */ + if (result != ISC_R_SUCCESS && family == AF_INET6) + return (result); + + if (result != ISC_R_SUCCESS) { + isc_log_write(IFMGR_COMMON_LOGARGS, + ISC_LOG_WARNING, + "omitting IPv4 interface %s from " + "localnets ACL: %s", + interface->name, + isc_result_totext(result)); + } else { + elt.u.ip_prefix.prefixlen = prefixlen; + /* XXX suppress duplicates */ + result = dns_acl_appendelement(mgr->aclenv.localnets, + &elt); + if (result != ISC_R_SUCCESS) + return (result); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, + isc_boolean_t verbose) +{ isc_interfaceiter_t *iter = NULL; isc_boolean_t scan_ipv4 = ISC_FALSE; isc_boolean_t scan_ipv6 = ISC_FALSE; + isc_boolean_t adjusting = ISC_FALSE; isc_result_t result; isc_netaddr_t zero_address, zero_address6; ns_listenelt_t *le; isc_sockaddr_t listen_addr; ns_interface_t *ifp; + if (ext_listen != NULL) + adjusting = ISC_TRUE; + if (isc_net_probeipv6() == ISC_R_SUCCESS) scan_ipv6 = ISC_TRUE; #ifdef WANT_IPV6 @@ -539,7 +591,8 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { "interfaces, port %u", le->port); result = ns_interface_setup(mgr, &listen_addr, - "", &ifp); + "", &ifp, + ISC_TRUE); if (result != ISC_R_SUCCESS) isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, @@ -557,12 +610,14 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { if (result != ISC_R_SUCCESS) return (result); - result = clearacl(mgr->mctx, &mgr->aclenv.localhost); - if (result != ISC_R_SUCCESS) - goto cleanup_iter; - result = clearacl(mgr->mctx, &mgr->aclenv.localnets); - if (result != ISC_R_SUCCESS) - goto cleanup_iter; + if (adjusting == ISC_FALSE) { + result = clearacl(mgr->mctx, &mgr->aclenv.localhost); + if (result != ISC_R_SUCCESS) + goto cleanup_iter; + result = clearacl(mgr->mctx, &mgr->aclenv.localnets); + if (result != ISC_R_SUCCESS) + goto cleanup_iter; + } for (result = isc_interfaceiter_first(iter); result == ISC_R_SUCCESS; @@ -570,8 +625,6 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { { isc_interface_t interface; ns_listenlist_t *ll; - dns_aclelement_t elt; - unsigned int prefixlen; unsigned int family; result = isc_interfaceiter_current(iter, &interface); @@ -602,33 +655,8 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { continue; } - elt.type = dns_aclelementtype_ipprefix; - elt.negative = ISC_FALSE; - elt.u.ip_prefix.address = interface.address; - elt.u.ip_prefix.prefixlen = (family == AF_INET) ? 32 : 128; - result = dns_acl_appendelement(mgr->aclenv.localhost, &elt); - if (result != ISC_R_SUCCESS) - goto ignore_interface; - - result = isc_netaddr_masktoprefixlen(&interface.netmask, - &prefixlen); - - /* Non contigious netmasks not allowed by IPv6 arch. */ - if (result != ISC_R_SUCCESS && family == AF_INET6) - goto ignore_interface; - - if (result != ISC_R_SUCCESS) { - isc_log_write(IFMGR_COMMON_LOGARGS, - ISC_LOG_WARNING, - "omitting IPv4 interface %s from " - "localnets ACL: %s", - interface.name, - isc_result_totext(result)); - } else { - elt.u.ip_prefix.prefixlen = prefixlen; - /* XXX suppress duplicates */ - result = dns_acl_appendelement(mgr->aclenv.localnets, - &elt); + if (adjusting == ISC_FALSE) { + result = setup_locals(mgr, &interface); if (result != ISC_R_SUCCESS) goto ignore_interface; } @@ -639,13 +667,10 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { le = ISC_LIST_NEXT(le, link)) { int match; + isc_boolean_t ipv6_wildcard = ISC_FALSE; isc_netaddr_t listen_netaddr; isc_sockaddr_t listen_sockaddr; - /* the case of "any" IPv6 address was already done. */ - if (family == AF_INET6 && listenon_is_ip6_any(le)) - continue; - /* * Construct a socket address for this IP/port * combination. @@ -671,17 +696,58 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { if (match <= 0) continue; + /* + * The case of "any" IPv6 address will require + * special considerations later, so remember it. + */ + if (family == AF_INET6 && listenon_is_ip6_any(le)) + ipv6_wildcard = ISC_TRUE; + + /* + * When adjusting interfaces with extra a listening + * list, see if the address matches the extra list. + * If it does, and is also covered by a wildcard + * interface, we need to listen on the address + * explicitly. + */ + if (adjusting == ISC_TRUE) { + ns_listenelt_t *ele; + + match = 0; + for (ele = ISC_LIST_HEAD(ext_listen->elts); + ele != NULL; + ele = ISC_LIST_NEXT(ele, link)) { + dns_acl_match(&listen_netaddr, NULL, + ele->acl, NULL, + &match, NULL); + if (match > 0 && ele->port == le->port) + break; + else + match = 0; + } + if (ipv6_wildcard == ISC_TRUE && match == 0) + continue; + } + ifp = find_matching_interface(mgr, &listen_sockaddr); if (ifp != NULL) { ifp->generation = mgr->generation; } else { char sabuf[ISC_SOCKADDR_FORMATSIZE]; + + if (adjusting == ISC_FALSE && + ipv6_wildcard == ISC_TRUE) + continue; + isc_sockaddr_format(&listen_sockaddr, sabuf, sizeof(sabuf)); isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, + "%s" "listening on %s interface " "%s, %s", + (adjusting == ISC_TRUE) ? + "additionally " : "", (family == AF_INET) ? "IPv4" : "IPv6", interface.name, sabuf); @@ -689,7 +755,11 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { result = ns_interface_setup(mgr, &listen_sockaddr, interface.name, - &ifp); + &ifp, + (adjusting == ISC_TRUE) ? + ISC_FALSE : + ISC_TRUE); + if (result != ISC_R_SUCCESS) { isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, @@ -725,15 +795,17 @@ do_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { return (result); } -void -ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { +static void +ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, + isc_boolean_t verbose) +{ isc_boolean_t purge = ISC_TRUE; REQUIRE(NS_INTERFACEMGR_VALID(mgr)); mgr->generation++; /* Increment the generation count. */ - if (do_scan(mgr, verbose) != ISC_R_SUCCESS) + if (do_scan(mgr, ext_listen, verbose) != ISC_R_SUCCESS) purge = ISC_FALSE; /* @@ -750,9 +822,23 @@ ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { * we're in lwresd-only mode, in which case that is to * be expected. */ - if (ISC_LIST_EMPTY(mgr->interfaces) && ! ns_g_lwresdonly) + if (ext_listen == NULL && + ISC_LIST_EMPTY(mgr->interfaces) && ! ns_g_lwresdonly) { isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING, "not listening on any interfaces"); + } +} + +void +ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) { + ns_interfacemgr_scan0(mgr, NULL, verbose); +} + +void +ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list, + isc_boolean_t verbose) +{ + ns_interfacemgr_scan0(mgr, list, verbose); } void @@ -770,4 +856,3 @@ ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value) { ns_listenlist_attach(value, &mgr->listenon6); UNLOCK(&mgr->lock); } - diff --git a/bin/named/server.c b/bin/named/server.c index 6a68971d27..fe38b2f43e 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.382 2002/08/17 00:23:19 marka Exp $ */ +/* $Id: server.c,v 1.383 2002/08/30 02:05:29 marka Exp $ */ #include @@ -1400,6 +1400,132 @@ scan_interfaces(ns_server_t *server, isc_boolean_t verbose) { server->aclenv.match_mapped = match_mapped; } +static isc_result_t +add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr) { + ns_listenelt_t *lelt = NULL; + dns_acl_t *src_acl = NULL; + dns_aclelement_t aelt; + isc_result_t result; + isc_sockaddr_t any_sa6; + + REQUIRE(isc_sockaddr_pf(addr) == AF_INET6); + + isc_sockaddr_any6(&any_sa6); + if (!isc_sockaddr_equal(&any_sa6, addr)) { + aelt.type = dns_aclelementtype_ipprefix; + aelt.negative = ISC_FALSE; + aelt.u.ip_prefix.prefixlen = 128; + isc_netaddr_fromin6(&aelt.u.ip_prefix.address, + &addr->type.sin6.sin6_addr); + + result = dns_acl_create(mctx, 1, &src_acl); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_acl_appendelement(src_acl, &aelt); + if (result != ISC_R_SUCCESS) + goto clean; + + result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr), + src_acl, &lelt); + if (result != ISC_R_SUCCESS) + goto clean; + ISC_LIST_APPEND(list->elts, lelt, link); + } + + return (ISC_R_SUCCESS); + + clean: + INSIST(lelt == NULL); + if (src_acl != NULL) + dns_acl_detach(&src_acl); + + return (result); +} + +/* + * Make a list of xxx-source addresses and call ns_interfacemgr_adjust() + * to update the listening interfaces accordingly. + * We currently only consider IPv6, because this only affects IPv6 wildcard + * sockets. + */ +static void +adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { + isc_result_t result; + ns_listenlist_t *list = NULL; + dns_view_t *view; + dns_zone_t *zone, *next; + isc_sockaddr_t addr, *addrp; + + result = ns_listenlist_create(mctx, &list); + if (result != ISC_R_SUCCESS) + return; + + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL; + view = ISC_LIST_NEXT(view, link)) { + dns_dispatch_t *dispatch6; + + dispatch6 = dns_resolver_dispatchv6(view->resolver); + INSIST(dispatch6 != NULL); + result = dns_dispatch_getlocaladdress(dispatch6, &addr); + if (result != ISC_R_SUCCESS) + goto fail; + result = add_listenelt(mctx, list, &addr); + if (result != ISC_R_SUCCESS) + goto fail; + } + + zone = NULL; + for (result = dns_zone_first(server->zonemgr, &zone); + result == ISC_R_SUCCESS; + next = NULL, result = dns_zone_next(zone, &next), zone = next) { + dns_view_t *zoneview; + + /* + * At this point the zone list may contain a stale zone + * just removed from the configuration. To see the validity, + * check if the corresponding view is in our current view list. + */ + zoneview = dns_zone_getview(zone); + INSIST(zoneview != NULL); + for (view = ISC_LIST_HEAD(server->viewlist); + view != NULL && view != zoneview; + view = ISC_LIST_NEXT(view, link)) + ; + if (view == NULL) + continue; + + addrp = dns_zone_getnotifysrc6(zone); + INSIST(addrp != NULL); + result = add_listenelt(mctx, list, addrp); + if (result != ISC_R_SUCCESS) + goto fail; + + addrp = dns_zone_getxfrsource6(zone); + INSIST(addrp != NULL); + result = add_listenelt(mctx, list, addrp); + if (result != ISC_R_SUCCESS) + goto fail; + } + + ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE); + + clean: + ns_listenlist_detach(&list); + return; + + fail: + /* + * Even when we failed the procedure, most of other interfaces + * should work correctly. We therefore just warn it. + */ + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "could not adjust the listen-on list; " + "some interfaces may not work"); + goto clean; +} + /* * This event callback is invoked to do periodic network * interface scanning. @@ -2060,6 +2186,12 @@ load_configuration(const char *filename, ns_server_t *server, } + /* + * Adjust the listening interfaces in accordance with the source + * addresses specified in views and zones. + */ + adjust_interfaces(server, ns_g_mctx); + if (dispatchv4 != NULL) dns_dispatch_detach(&dispatchv4); if (dispatchv6 != NULL)