]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/mastercommunicator.cc
and the final bit of whitespace/tab cleanup
[thirdparty/pdns.git] / pdns / mastercommunicator.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2009 PowerDNS.COM BV
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 #include "packetcache.hh"
19 #include "utility.hh"
20 #include <errno.h>
21 #include "communicator.hh"
22 #include <set>
23 #include <boost/utility.hpp>
24 #include "dnsbackend.hh"
25 #include "ueberbackend.hh"
26 #include "packethandler.hh"
27 #include "resolver.hh"
28 #include "logger.hh"
29 #include "dns.hh"
30 #include "arguments.hh"
31 #include "session.hh"
32 #include "packetcache.hh"
33 #include <boost/lexical_cast.hpp>
34
35 #include "namespaces.hh"
36
37 // class that one day might be more than a function to help you get IP addresses for a nameserver
38 class FindNS
39 {
40 public:
41 vector<string>lookup(const string &name, DNSBackend *B)
42 {
43 vector<string>addresses;
44 struct hostent *h;
45 h=gethostbyname(name.c_str());
46
47 if(h) {
48 for(char **h_addr_list=h->h_addr_list;*h_addr_list;++h_addr_list) {
49 ostringstream os;
50 unsigned char *p=reinterpret_cast<unsigned char *>(*h_addr_list);
51 os<<(int)*p++<<".";
52 os<<(int)*p++<<".";
53 os<<(int)*p++<<".";
54 os<<(int)*p++;
55
56 addresses.push_back(os.str());
57 }
58 }
59
60 B->lookup(QType(QType::A),name);
61 DNSResourceRecord rr;
62 while(B->get(rr))
63 addresses.push_back(rr.content); // SOL if you have a CNAME for an NS
64
65 return addresses;
66 }
67 }d_fns;
68
69 void CommunicatorClass::queueNotifyDomain(const string &domain, DNSBackend *B)
70 {
71 set<string> ips;
72
73 DNSResourceRecord rr;
74 set<string>nsset;
75
76 B->lookup(QType(QType::NS),domain);
77 while(B->get(rr))
78 nsset.insert(rr.content);
79
80 for(set<string>::const_iterator j=nsset.begin();j!=nsset.end();++j) {
81 vector<string>nsips=d_fns.lookup(*j, B);
82 if(nsips.empty())
83 L<<Logger::Warning<<"Unable to queue notification of domain '"<<domain<<"': nameservers do not resolve!"<<endl;
84 for(vector<string>::const_iterator k=nsips.begin();k!=nsips.end();++k)
85 ips.insert(*k);
86 }
87
88 // make calls to d_nq.add(domain, ip);
89 for(set<string>::const_iterator j=ips.begin();j!=ips.end();++j) {
90 L<<Logger::Warning<<"Queued notification of domain '"<<domain<<"' to "<<*j<<endl;
91 d_nq.add(domain,*j);
92 }
93
94 set<string>alsoNotify;
95 B->alsoNotifies(domain, &alsoNotify);
96
97 for(set<string>::const_iterator j=alsoNotify.begin();j!=alsoNotify.end();++j) {
98 L<<Logger::Warning<<"Queued also-notification of domain '"<<domain<<"' to "<<*j<<endl;
99 d_nq.add(domain,*j);
100 }
101 }
102
103 bool CommunicatorClass::notifyDomain(const string &domain)
104 {
105 DomainInfo di;
106 PacketHandler P;
107 if(!P.getBackend()->getDomainInfo(domain, di)) {
108 L<<Logger::Error<<"No such domain '"<<domain<<"' in our database"<<endl;
109 return false;
110 }
111 queueNotifyDomain(domain, P.getBackend());
112 // call backend and tell them we sent out the notification - even though that is premature
113 di.backend->setNotified(di.id, di.serial);
114
115 return true;
116 }
117
118
119 void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
120 {
121 if(!::arg().mustDo("master"))
122 return;
123
124 UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
125 vector<DomainInfo> cmdomains;
126 B->getUpdatedMasters(&cmdomains);
127
128 if(cmdomains.empty()) {
129 if(d_masterschanged)
130 L<<Logger::Warning<<"No master domains need notifications"<<endl;
131 d_masterschanged=false;
132 }
133 else {
134 d_masterschanged=true;
135 L<<Logger::Error<<cmdomains.size()<<" domain"<<(cmdomains.size()>1 ? "s" : "")<<" for which we are master need"<<
136 (cmdomains.size()>1 ? "" : "s")<<
137 " notifications"<<endl;
138 }
139
140 // figure out A records of everybody needing notification
141 // do this via the FindNS class, d_fns
142
143 for(vector<DomainInfo>::const_iterator i=cmdomains.begin();i!=cmdomains.end();++i) {
144 extern PacketCache PC;
145 vector<string> topurge;
146 topurge.push_back(i->zone);
147 PC.purge(topurge); // fixes cvstrac ticket #30
148 queueNotifyDomain(i->zone,P->getBackend());
149 i->backend->setNotified(i->id,i->serial);
150 }
151 }
152
153 time_t CommunicatorClass::doNotifications()
154 {
155 ComboAddress from;
156 Utility::socklen_t fromlen=sizeof(from);
157 char buffer[1500];
158 int size;
159 static Resolver d_nresolver;
160 // receive incoming notifications on the nonblocking socket and take them off the list
161
162 while((size=recvfrom(d_nsock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen))>0) {
163 DNSPacket p;
164
165 p.setRemote(&from);
166
167 if(p.parse(buffer,size)<0) {
168 L<<Logger::Warning<<"Unable to parse SOA notification answer from "<<p.getRemote()<<endl;
169 continue;
170 }
171
172 if(p.d.rcode)
173 L<<Logger::Warning<<"Received unsuccesful notification report for '"<<p.qdomain<<"' from "<<p.getRemote()<<", rcode: "<<p.d.rcode<<endl;
174
175 if(d_nq.removeIf(p.getRemote(), p.d.id, p.qdomain))
176 L<<Logger::Warning<<"Removed from notification list: '"<<p.qdomain<<"' to "<<p.getRemote()<< (p.d.rcode ? "" : " (was acknowledged)")<<endl;
177 else
178 L<<Logger::Warning<<"Received spurious notify answer for '"<<p.qdomain<<"' from "<<p.getRemote()<<endl;
179 }
180
181 // send out possible new notifications
182 string domain, ip;
183 uint16_t id;
184
185 bool purged;
186 while(d_nq.getOne(domain, ip, &id, purged)) {
187 if(!purged) {
188 try {
189 d_nresolver.notify(d_nsock, domain, ip, id);
190 drillHole(domain, ip);
191 }
192 catch(ResolverException &re) {
193 L<<Logger::Error<<"Error trying to resolve '"+ip+"' for notifying '"+domain+"' to server: "+re.reason<<endl;
194 }
195 }
196 else
197 L<<Logger::Error<<Logger::NTLog<<"Notification for "<<domain<<" to "<<ip<<" failed after retries"<<endl;
198 }
199
200 return d_nq.earliest();
201 }
202
203 void CommunicatorClass::drillHole(const string &domain, const string &ip)
204 {
205 Lock l(&d_holelock);
206 d_holes[make_pair(domain,ip)]=time(0);
207 }
208
209 bool CommunicatorClass::justNotified(const string &domain, const string &ip)
210 {
211 Lock l(&d_holelock);
212 if(d_holes.find(make_pair(domain,ip))==d_holes.end()) // no hole
213 return false;
214
215 if(d_holes[make_pair(domain,ip)]>time(0)-900) // recent hole
216 return true;
217
218 // do we want to purge this? XXX FIXME
219 return false;
220 }
221
222 void CommunicatorClass::makeNotifySocket()
223 {
224 if((d_nsock=socket(AF_INET, SOCK_DGRAM,0))<0)
225 throw AhuException(string("notification socket: ")+strerror(errno));
226
227 struct sockaddr_in sin;
228 memset((char *)&sin,0, sizeof(sin));
229
230 sin.sin_family = AF_INET;
231
232 // Bind to a specific IP (query-local-address) if specified
233 string querylocaladdress(::arg()["query-local-address"]);
234 if (querylocaladdress=="") {
235 sin.sin_addr.s_addr = INADDR_ANY;
236 }
237 else
238 {
239 struct hostent *h=0;
240 h=gethostbyname(querylocaladdress.c_str());
241 if(!h) {
242 Utility::closesocket(d_nsock);
243 d_nsock=-1;
244 throw AhuException("Unable to resolve query local address");
245 }
246
247 sin.sin_addr.s_addr = *(int*)h->h_addr;
248 }
249
250 int n=0;
251 for(;n<10;n++) {
252 sin.sin_port = htons(10000+(Utility::random()%50000));
253
254 if(::bind(d_nsock, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
255 break;
256 }
257 if(n==10) {
258 Utility::closesocket(d_nsock);
259 d_nsock=-1;
260 throw AhuException(string("binding notify socket: ")+strerror(errno));
261 }
262 if( !Utility::setNonBlocking( d_nsock ))
263 throw AhuException(string("error getting or setting notify socket non-blocking: ")+strerror(errno));
264
265 }
266
267 void CommunicatorClass::notify(const string &domain, const string &ip)
268 {
269 d_nq.add(domain, ip);
270
271 d_any_sem.post();
272 }
273