Skip to content

Commit

Permalink
after a decade+.. finally try to stop notifying ourselves. In convoluted
Browse files Browse the repository at this point in the history
cases involving REUSE_PORT and binding to 0.0.0.0 and ::, it might be
possible that we guess wrong, in which case we now provide & document
the setting prevent-self-notification which you could then set to off.
  • Loading branch information
ahupowerdns committed May 5, 2013
1 parent 159fe44 commit 8db49a6
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 14 deletions.
2 changes: 1 addition & 1 deletion pdns/common_startup.cc
Expand Up @@ -96,7 +96,7 @@ void declareArguments()
::arg().setSwitch("guardian","Run within a guardian process")="no";
::arg().setSwitch("strict-rfc-axfrs","Perform strictly rfc compliant axfrs (very slow)")="no";
::arg().setSwitch("send-root-referral","Send out old-fashioned root-referral instead of ServFail in case of no authority")="no";

::arg().setSwitch("prevent-self-notification","Don't send notifications to what we think is ourself")="yes";
::arg().setSwitch("webserver","Start a webserver for monitoring")="no";
::arg().setSwitch("webserver-print-arguments","If the webserver should print arguments")="no";
::arg().setSwitch("edns-subnet-processing","If we should act on EDNS Subnet options")="no";
Expand Down
1 change: 1 addition & 0 deletions pdns/communicator.cc
Expand Up @@ -58,6 +58,7 @@ void CommunicatorClass::go()
for(int n=0; n < ::arg().asNum("retrieval-threads"); ++n)
pthread_create(&tid, 0, &retrieveLaunchhelper, this); // Starts CommunicatorClass::retrievalLoopThread()

d_preventSelfNotification =::arg().mustDo("prevent-self-notification");
}

void CommunicatorClass::mainloop(void)
Expand Down
1 change: 1 addition & 0 deletions pdns/communicator.hh
Expand Up @@ -198,6 +198,7 @@ private:
bool d_masterschanged, d_slaveschanged;
set<DomainInfo> d_tocheck;
vector<DNSPacket> d_potentialsupermasters;
bool d_preventSelfNotification;
};

#endif
6 changes: 6 additions & 0 deletions pdns/docs/pdns.xml
Expand Up @@ -14628,6 +14628,12 @@ Tell PowerDNS to log all incoming DNS queries. This will lead to a lot of loggin
<listitem><para>
ABI version to use for the pipe backend. See <xref linkend="pipebackend-protocol"/>.
</para></listitem></varlistentry>
<varlistentry><term>prevent-self-notification | prevent-self-notification = yes | prevent-self-notification = no</term>
<listitem><para>
Available as of 3.3. PowerDNS Authoritative Server attempts to not send out notifications to itself in master mode.
In very complicated situations we could guess wrong and not notify a server that should be notified. In that case,

This comment has been minimized.

Copy link
@justinclift

justinclift May 6, 2013

Contributor

Guess what wrong? The address to send notification to?

This comment has been minimized.

Copy link
@ahupowerdns

ahupowerdns May 6, 2013

Author Contributor

So, if you bind to 0.0.0.0, you could potentially have both BIND and PowerDNS listening on 0.0.0.0 with some settings (reuseaddr). In that case, we would never notify any IP address that is bound to your machine, since they would all appear to be 'us'.

So we'd guess wrong on if the remote end is 'us' exactly (ie, the same powerdns binary).

Perhaps it's not possible now, but SO_REUSEPORT might make this possible. If stupid.

I basically want to be able to tell people to switch off this logic if it turns out it breaks.

This comment has been minimized.

Copy link
@justinclift

justinclift May 6, 2013

Contributor

Thanks Bert. The question was more "this comment is unclear and should be expanded" type of thing, instead of myself just asking directly.

Bad wording on my part. ;)

This comment has been minimized.

Copy link
@Habbie

Habbie May 6, 2013

Member

I have a rule that says "if people ask, fix the docs, then ask if that helps". But it's hard to remember to apply that rule always!

set prevent-self-notification to "no".
</para></listitem></varlistentry>

<varlistentry><term>query-cache-ttl=...</term>
<listitem><para>
Expand Down
5 changes: 4 additions & 1 deletion pdns/mastercommunicator.cc
Expand Up @@ -25,6 +25,7 @@
#include "dnsbackend.hh"
#include "ueberbackend.hh"
#include "packethandler.hh"
#include "nameserver.hh"
#include "resolver.hh"
#include "logger.hh"
#include "dns.hh"
Expand Down Expand Up @@ -168,6 +169,9 @@ time_t CommunicatorClass::doNotifications()
if((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) ||
(d_nsock4 < 0 && remote.sin4.sin_family == AF_INET))
continue; // don't try to notify what we can't!
if(d_preventSelfNotification && AddressIsUs(remote))
continue;

sendNotification(remote.sin4.sin_family == AF_INET ? d_nsock4 : d_nsock6, domain, remote, id);
drillHole(domain, ip);
}
Expand Down Expand Up @@ -225,7 +229,6 @@ void CommunicatorClass::makeNotifySockets()
void CommunicatorClass::notify(const string &domain, const string &ip)
{
d_nq.add(domain, ip);

d_any_sem.post();
}

55 changes: 45 additions & 10 deletions pdns/nameserver.cc
Expand Up @@ -83,6 +83,7 @@ extern StatBag S;
#define GEN_IP_PKTINFO IP_RECVDSTADDR
#endif

vector<ComboAddress> g_localaddresses;

void UDPNameserver::bindIPv4()
{
Expand All @@ -95,7 +96,7 @@ void UDPNameserver::bindIPv4()
int s;
for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
string localname(*i);
struct sockaddr_in locala;
ComboAddress locala;

s=socket(AF_INET,SOCK_DGRAM,0);

Expand All @@ -108,12 +109,12 @@ void UDPNameserver::bindIPv4()
throw AhuException("Unable to set UDP socket to non-blocking: "+stringerror());

memset(&locala,0,sizeof(locala));
locala.sin_family=AF_INET;
locala.sin4.sin_family=AF_INET;

if(localname=="0.0.0.0") {
int val=1;
setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val));
locala.sin_addr.s_addr = INADDR_ANY;
locala.sin4.sin_addr.s_addr = INADDR_ANY;
}
else
{
Expand All @@ -122,17 +123,17 @@ void UDPNameserver::bindIPv4()
if(!h)
throw AhuException("Unable to resolve local address");

locala.sin_addr.s_addr=*(int*)h->h_addr;
locala.sin4.sin_addr.s_addr=*(int*)h->h_addr;
}

locala.sin_port=htons(::arg().asNum("local-port"));

if(::bind(s, (sockaddr*)&locala,sizeof(locala))<0) {
L<<Logger::Error<<"binding UDP socket to '"+localname+"' port "+lexical_cast<string>(ntohs(locala.sin_port))+": "<<strerror(errno)<<endl;
locala.sin4.sin_port=htons(::arg().asNum("local-port"));
g_localaddresses.push_back(locala);
if(::bind(s, (sockaddr*)&locala, locala.getSocklen()) < 0) {
L<<Logger::Error<<"binding UDP socket to '"+localname+"' port "+lexical_cast<string>(ntohs(locala.sin4.sin_port))+": "<<strerror(errno)<<endl;
throw AhuException("Unable to bind to UDP socket");
}
d_sockets.push_back(s);
L<<Logger::Error<<"UDP server bound to "<<inet_ntoa(locala.sin_addr)<<":"<<::arg().asNum("local-port")<<endl;
L<<Logger::Error<<"UDP server bound to "<<inet_ntoa(locala.sin4.sin_addr)<<":"<<::arg().asNum("local-port")<<endl;
struct pollfd pfd;
pfd.fd = s;
pfd.events = POLLIN;
Expand All @@ -151,6 +152,40 @@ static bool IsAnyAddress(const ComboAddress& addr)
return false;
}


bool AddressIsUs(const ComboAddress& remote)
{
BOOST_FOREACH(const ComboAddress& us, g_localaddresses) {
if(remote == us)
return true;
if(IsAnyAddress(us)) {
int s = socket(AF_INET, SOCK_DGRAM, 0);
if(s < 0)
continue;

if(connect(s, (struct sockaddr*)&remote, remote.getSocklen()) < 0) {
close(s);
continue;
}

ComboAddress actualLocal;
actualLocal.sin4.sin_family = remote.sin4.sin_family;
socklen_t socklen = actualLocal.getSocklen();

if(getsockname(s, (struct sockaddr*) &actualLocal, &socklen) < 0) {
close(s);
continue;
}
close(s);
actualLocal.sin4.sin_port = us.sin4.sin_port;
if(actualLocal == remote)
return true;
}
}
return false;
}


void UDPNameserver::bindIPv6()
{
#if !WIN32 && HAVE_IPV6
Expand Down Expand Up @@ -178,7 +213,7 @@ void UDPNameserver::bindIPv6()
setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); // if this fails, we report an error in tcpreceiver too
}

g_localaddresses.push_back(locala);
if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) {
L<<Logger::Error<<"binding to UDP ipv6 socket: "<<strerror(errno)<<endl;
throw AhuException("Unable to bind to UDP ipv6 socket");
Expand Down
3 changes: 1 addition & 2 deletions pdns/nameserver.hh
Expand Up @@ -83,7 +83,6 @@ private:
vector<pollfd> d_rfds;
};



bool AddressIsUs(const ComboAddress& remote);

#endif

0 comments on commit 8db49a6

Please sign in to comment.