From 20829585a24691bcdb05471ba429a9fc27a08ff6 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 16 Mar 2020 18:34:36 +0100 Subject: [PATCH] Centralize Query Local Address handling --- pdns/Makefile.am | 5 ++ pdns/axfr-retriever.cc | 8 +- pdns/common_startup.cc | 4 + pdns/lwres.cc | 5 +- pdns/mastercommunicator.cc | 9 +- pdns/pdns_recursor.cc | 90 +++++++------------- pdns/query-local-address.cc | 100 +++++++++++++++++++++++ pdns/query-local-address.hh | 59 +++++++++++++ pdns/recursordist/Makefile.am | 2 + pdns/recursordist/query-local-address.cc | 1 + pdns/recursordist/query-local-address.hh | 1 + pdns/resolver.cc | 23 +++--- pdns/rfc2136handler.cc | 9 +- pdns/rpzloader.cc | 5 +- pdns/slavecommunicator.cc | 13 ++- pdns/syncres.hh | 1 - pdns/tsig-tests.cc | 4 +- 17 files changed, 241 insertions(+), 98 deletions(-) create mode 100644 pdns/query-local-address.cc create mode 100644 pdns/query-local-address.hh create mode 120000 pdns/recursordist/query-local-address.cc create mode 120000 pdns/recursordist/query-local-address.hh diff --git a/pdns/Makefile.am b/pdns/Makefile.am index f6a5510bd24c..d63a2fadcab1 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -204,6 +204,7 @@ pdns_server_SOURCES = \ packethandler.cc packethandler.hh \ pdnsexception.hh \ qtype.cc qtype.hh \ + query-local-address.hh query-local-address.cc \ rcpgenerator.cc \ receiver.cc \ resolver.cc resolver.hh \ @@ -634,6 +635,7 @@ ixfrdist_SOURCES = \ misc.cc misc.hh \ mplexer.hh \ nsecrecords.cc \ + query-local-address.hh query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ @@ -689,6 +691,7 @@ ixplore_SOURCES = \ logger.cc \ misc.cc misc.hh \ nsecrecords.cc \ + query-local-address.hh query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ @@ -840,9 +843,11 @@ tsig_tests_SOURCES = \ dnssecinfra.cc \ dnswriter.cc dnswriter.hh \ gss_context.cc gss_context.hh \ + iputils.cc \ logger.cc \ misc.cc misc.hh \ nsecrecords.cc \ + query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ diff --git a/pdns/axfr-retriever.cc b/pdns/axfr-retriever.cc index cfac004079d2..4d2718efd34c 100644 --- a/pdns/axfr-retriever.cc +++ b/pdns/axfr-retriever.cc @@ -25,6 +25,7 @@ #include "dns_random.hh" #include "utility.hh" #include "resolver.hh" +#include "query-local-address.hh" using pdns::resolver::parseResult; @@ -40,11 +41,10 @@ AXFRRetriever::AXFRRetriever(const ComboAddress& remote, if (laddr != nullptr) { local = ComboAddress(*laddr); } else { - string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6"; - if (::arg()[qlas].empty()) { - throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset"); + if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) { + throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries"); } - local=ComboAddress(::arg()[qlas]); + local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0); } d_sock = -1; try { diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index da3c8e337153..8ba1ce6dfcf1 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -31,6 +31,7 @@ #include "dnsseckeeper.hh" #include "threadname.hh" #include "misc.hh" +#include "query-local-address.hh" #include @@ -628,6 +629,9 @@ void mainthread() } } + pdns::parseQueryLocalAddress(::arg()["query-local-address"]); + pdns::parseQueryLocalAddress(::arg()["query-local-address6"]); + // NOW SAFE TO CREATE THREADS! dl->go(); diff --git a/pdns/lwres.cc b/pdns/lwres.cc index 2fb111fde1a8..cc40ae9b66ad 100644 --- a/pdns/lwres.cc +++ b/pdns/lwres.cc @@ -47,6 +47,7 @@ #include #include "validate-recursor.hh" #include "ednssubnet.hh" +#include "query-local-address.hh" #ifdef HAVE_PROTOBUF @@ -55,6 +56,8 @@ #ifdef HAVE_FSTRM #include "rec-dnstap.hh" #include "fstrm_logger.hh" + + bool g_syslog; static bool isEnabledForQueries(const std::shared_ptr>>& fstreamLoggers) @@ -315,7 +318,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d Socket s(ip.sin4.sin_family, SOCK_STREAM); s.setNonBlocking(); - ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0); + ComboAddress local = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0); s.bind(local); diff --git a/pdns/mastercommunicator.cc b/pdns/mastercommunicator.cc index 4219e1d356bd..7b45c76c89d8 100644 --- a/pdns/mastercommunicator.cc +++ b/pdns/mastercommunicator.cc @@ -40,6 +40,7 @@ #include "packetcache.hh" #include "base64.hh" #include "namespaces.hh" +#include "query-local-address.hh" void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B) @@ -299,13 +300,13 @@ bool CommunicatorClass::justNotified(const DNSName &domain, const string &ip) void CommunicatorClass::makeNotifySockets() { - if(!::arg()["query-local-address"].empty()) { - d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind")); + if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) { + d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind")); } else { d_nsock4 = -1; } - if(!::arg()["query-local-address6"].empty()) { - d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind")); + if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) { + d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind")); } else { d_nsock6 = -1; } diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index bfaf00ba43a7..d94076e61e5d 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -93,6 +93,7 @@ #ifdef NOD_ENABLED #include "nod.hh" #endif /* NOD_ENABLED */ +#include "query-local-address.hh" #include "rec-protobuf.hh" #include "rec-snmp.hh" @@ -190,10 +191,8 @@ static deferredAdd_t g_deferredAdds; typedef vector tcpListenSockets_t; typedef map listenSocketsAddresses_t; // is shared across all threads right now -static const ComboAddress g_local4("0.0.0.0"), g_local6("::"); static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now static set g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism -static vector g_localQueryAddresses4, g_localQueryAddresses6; static AtomicCounter counter; static std::shared_ptr g_initialDomainMap; // new threads needs this to be setup static std::shared_ptr g_initialAllowFrom; // new thread needs to be setup with this @@ -464,7 +463,7 @@ string GenUDPQueryResponse(const ComboAddress& dest, const string& query) { Socket s(dest.sin4.sin_family, SOCK_DGRAM); s.setNonBlocking(); - ComboAddress local = getQueryLocalAddress(dest.sin4.sin_family, 0); + ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0); s.bind(local); s.connect(dest); @@ -490,28 +489,6 @@ string GenUDPQueryResponse(const ComboAddress& dest, const string& query) return data; } -//! pick a random query local address -ComboAddress getQueryLocalAddress(int family, uint16_t port) -{ - ComboAddress ret; - if(family==AF_INET) { - if(g_localQueryAddresses4.empty()) - ret = g_local4; - else - ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())]; - ret.sin4.sin_port = htons(port); - } - else { - if(g_localQueryAddresses6.empty()) - ret = g_local6; - else - ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())]; - - ret.sin6.sin6_port = htons(port); - } - return ret; -} - static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&); static void setSocketBuffer(int fd, int optname, uint32_t size) @@ -627,7 +604,7 @@ class UDPClientSocks while (s_avoidUdpSourcePorts.count(port)); } - sin=getQueryLocalAddress(family, port); // does htons for us + sin=pdns::getQueryLocalAddress(family, port); // does htons for us if (::bind(ret, (struct sockaddr *)&sin, sin.getSocklen()) >= 0) break; @@ -4013,30 +3990,22 @@ static int serviceMain(int argc, char*argv[]) checkLinuxIPv6Limits(); try { - vector addrs; - if(!::arg()["query-local-address6"].empty()) { - SyncRes::s_doIPv6=true; - g_log< g_localQueryAddresses4; + static vector g_localQueryAddresses6; + + ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port) { + ComboAddress ret; + if (family==AF_INET) { + if (g_localQueryAddresses4.empty()) { + ret = local4; + } else if (g_localQueryAddresses4.size() == 1) { + ret = g_localQueryAddresses4.at(0); + } else { + ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())]; + } + ret.sin4.sin_port = htons(port); + } + else { + if (g_localQueryAddresses6.empty()) { + ret = local6; + } else if (g_localQueryAddresses6.size() == 1) { + ret = g_localQueryAddresses6.at(0); + } else { + ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())]; + } + ret.sin6.sin6_port = htons(port); + } + return ret; + } + + ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family) { + if (family == AF_INET) { + for (const auto& addr : pdns::g_localQueryAddresses4) { + if (!IsAnyAddress(addr)) { + return addr; + } + } + } + if (family == AF_INET6) { + for (const auto& addr : pdns::g_localQueryAddresses6) { + if (!IsAnyAddress(addr)) { + return addr; + } + } + } + ComboAddress ret("0.0.0.0"); + ret.reset(); // Ensure all is zero, even the addr family + return ret; + } + + void parseQueryLocalAddress(const std::string &qla) { + vector addrs; + stringtok(addrs, qla, ", ;"); + for(const string& addr : addrs) { + ComboAddress tmp(addr); + if (tmp.isIPv4()) { + g_localQueryAddresses4.push_back(tmp); + continue; + } + g_localQueryAddresses6.push_back(tmp); + } + } + + bool isQueryLocalAddressFamilyEnabled(const sa_family_t family) { + if (family == AF_INET) { + return !g_localQueryAddresses4.empty(); + } + if (family == AF_INET6) { + return !g_localQueryAddresses6.empty(); + } + return false; + } +} // namespace pdns diff --git a/pdns/query-local-address.hh b/pdns/query-local-address.hh new file mode 100644 index 000000000000..9515f4a358c0 --- /dev/null +++ b/pdns/query-local-address.hh @@ -0,0 +1,59 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include "iputils.hh" + +namespace pdns { + /*! pick a random query local address for family + * + * Will always return a ComboAddress. + * + * @param family Address Family, only AF_INET and AF_INET6 are supported + * @param port Port to set in the returned ComboAddress + */ + ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port); + + /*! Returns a non-Any address QLA, or an empty QLA when the QLA is any + * + * @param family Address Family + */ + ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family); + + /*! Populate the query local address vectors + * + * Will throw when an address can't be parsed + * + * @param qla A string of one or more ip addresses, separated by + * spaces, semi-colons or commas + */ + void parseQueryLocalAddress(const std::string &qla); + + /*! Is the address family explicitly enabled + * + * i.e. was there an address parsed by parseQueryLocalAddress belonging + * to this family + * + * @param family Address Family, only AF_INET and AF_INET6 are supported + */ + bool isQueryLocalAddressFamilyEnabled(const sa_family_t family); +} // namespace pdns diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 6e13a653b7f5..7cc26f45d9eb 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -148,6 +148,7 @@ pdns_recursor_SOURCES = \ pubsuffix.hh pubsuffix.cc \ pubsuffixloader.cc \ qtype.hh qtype.cc \ + query-local-address.hh query-local-address.cc \ rcpgenerator.cc rcpgenerator.hh \ rec-carbon.cc \ rec-lua-conf.hh rec-lua-conf.cc \ @@ -253,6 +254,7 @@ testrunner_SOURCES = \ pollmplexer.cc \ protobuf.cc protobuf.hh \ qtype.cc qtype.hh \ + query-local-address.hh query-local-address.cc \ rcpgenerator.cc \ rec-protobuf.cc rec-protobuf.hh \ recpacketcache.cc recpacketcache.hh \ diff --git a/pdns/recursordist/query-local-address.cc b/pdns/recursordist/query-local-address.cc new file mode 120000 index 000000000000..fd2ad3c41926 --- /dev/null +++ b/pdns/recursordist/query-local-address.cc @@ -0,0 +1 @@ +../query-local-address.cc \ No newline at end of file diff --git a/pdns/recursordist/query-local-address.hh b/pdns/recursordist/query-local-address.hh new file mode 120000 index 000000000000..18e39c852bf5 --- /dev/null +++ b/pdns/recursordist/query-local-address.hh @@ -0,0 +1 @@ +../query-local-address.hh \ No newline at end of file diff --git a/pdns/resolver.cc b/pdns/resolver.cc index d8977b8db518..c9c456d5191a 100644 --- a/pdns/resolver.cc +++ b/pdns/resolver.cc @@ -44,7 +44,7 @@ #include "base64.hh" #include "dnswriter.hh" #include "dnsparser.hh" - +#include "query-local-address.hh" #include "dns_random.hh" #include @@ -102,10 +102,12 @@ Resolver::Resolver() locals["default4"] = -1; locals["default6"] = -1; try { - if(!::arg()["query-local-address"].empty()) - locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind")); - if(!::arg()["query-local-address6"].empty()) - locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind")); + if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) { + locals["default4"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind")); + } + if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) { + locals["default6"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind")); + } } catch(...) { if(locals["default4"]>=0) @@ -158,12 +160,13 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& l // choose socket based on local if (local.sin4.sin_family == 0) { // up to us. - sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"]; - if (sock == -1) { - string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6"; - string qla = remote.sin4.sin_family == AF_INET ? "" : "6"; - throw ResolverException("No IPv" + ipv + " socket available, is query-local-address" + qla + " unset?"); + if (remote.sin4.sin_family == AF_INET && !pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) { + throw ResolverException("No IPv4 socket available, is query-local-address set?"); + } + if (remote.sin4.sin_family == AF_INET6 && !pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) { + throw ResolverException("No IPv6 socket available, is query-local-address6 set?"); } + sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"]; } else { std::string lstr = local.toString(); std::map::iterator lptr; diff --git a/pdns/rfc2136handler.cc b/pdns/rfc2136handler.cc index 71173d015960..ca5a3406530d 100644 --- a/pdns/rfc2136handler.cc +++ b/pdns/rfc2136handler.cc @@ -16,6 +16,7 @@ #include "dns_random.hh" #include "backends/gsql/ssql.hh" #include "communicator.hh" +#include "query-local-address.hh" extern StatBag S; extern CommunicatorClass Communicator; @@ -597,14 +598,10 @@ int PacketHandler::forwardPacket(const string &msgPrefix, const DNSPacket& p, co for(const auto& remote : di.masters) { g_log< loadRPZFromServer(const ComboAddress& master ComboAddress local(localAddress); if (local == ComboAddress()) - local = getQueryLocalAddress(master.sin4.sin_family, 0); + local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0); AXFRRetriever axfr(master, zoneName, tt, &local, maxReceivedBytes, axfrTimeout); unsigned int nrecords=0; @@ -433,7 +434,7 @@ void RPZIXFRTracker(const std::vector& masters, boost::optional pipefunc_t; void broadcastFunction(const pipefunc_t& func); void distributeAsyncFunction(const std::string& question, const pipefunc_t& func); diff --git a/pdns/tsig-tests.cc b/pdns/tsig-tests.cc index d65bd563ab42..e63e29ebc5ec 100644 --- a/pdns/tsig-tests.cc +++ b/pdns/tsig-tests.cc @@ -14,6 +14,7 @@ #include "axfr-retriever.hh" #include "arguments.hh" #include "dns_random.hh" +#include "query-local-address.hh" StatBag S; @@ -26,8 +27,7 @@ ArgvMap& arg() int main(int argc, char** argv) try { - ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0"; - ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::"; + pdns::parseQueryLocalAddress(":: 0.0.0.0"); reportAllTypes();