]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/communicator.hh
Merge pull request #5067 from cynthia/master
[thirdparty/pdns.git] / pdns / communicator.hh
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifndef PDNS_COMMUNICATOR_HH
23 #define PDNS_COMMUNICATOR_HH
24
25 #include <pthread.h>
26 #include <string>
27 #include <semaphore.h>
28 #include <queue>
29 #include <list>
30 #include <limits>
31 #include <boost/multi_index_container.hpp>
32 #include <boost/multi_index/identity.hpp>
33 #include <boost/multi_index/sequenced_index.hpp>
34 #include <boost/scoped_ptr.hpp>
35 using namespace boost::multi_index;
36
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <netdb.h>
40
41 #include "lock.hh"
42 #include "packethandler.hh"
43
44 #include "namespaces.hh"
45 #include "dns_random.hh"
46
47 struct SuckRequest
48 {
49 DNSName domain;
50 string master;
51 bool operator<(const SuckRequest& b) const
52 {
53 return tie(domain, master) < tie(b.domain, b.master);
54 }
55 };
56
57 struct IDTag{};
58
59 typedef multi_index_container<
60 SuckRequest,
61 indexed_by<
62 sequenced<>,
63 ordered_unique<tag<IDTag>, identity<SuckRequest> >
64 >
65 > UniQueue;
66 typedef UniQueue::index<IDTag>::type domains_by_name_t;
67
68 class NotificationQueue
69 {
70 public:
71 void add(const DNSName &domain, const string &ip)
72 {
73 const ComboAddress caIp(ip);
74
75 NotificationRequest nr;
76 nr.domain = domain;
77 nr.ip = caIp.toStringWithPort();
78 nr.attempts = 0;
79 nr.id = dns_random(0xffff);
80 nr.next = time(0);
81
82 d_nqueue.push_back(nr);
83 }
84
85 bool removeIf(const string &remote, uint16_t id, const DNSName &domain)
86 {
87 ServiceTuple stRemote, stQueued;
88 parseService(remote, stRemote);
89
90 for(d_nqueue_t::iterator i=d_nqueue.begin(); i!=d_nqueue.end(); ++i) {
91 parseService(i->ip, stQueued);
92 if(i->id==id && stQueued.host == stRemote.host && i->domain==domain) {
93 d_nqueue.erase(i);
94 return true;
95 }
96 }
97 return false;
98 }
99
100 bool getOne(DNSName &domain, string &ip, uint16_t *id, bool &purged)
101 {
102 for(d_nqueue_t::iterator i=d_nqueue.begin();i!=d_nqueue.end();++i)
103 if(i->next <= time(0)) {
104 i->attempts++;
105 purged=false;
106 i->next=time(0)+1+(1<<i->attempts);
107 domain=i->domain;
108 ip=i->ip;
109 *id=i->id;
110 purged=false;
111 if(i->attempts>4) {
112 purged=true;
113 d_nqueue.erase(i);
114 }
115 return true;
116 }
117 return false;
118 }
119
120 time_t earliest()
121 {
122 time_t early=std::numeric_limits<time_t>::max() - 1;
123 for(d_nqueue_t::const_iterator i=d_nqueue.begin();i!=d_nqueue.end();++i)
124 early=min(early,i->next);
125 return early-time(0);
126 }
127
128 void dump();
129
130 private:
131 struct NotificationRequest
132 {
133 DNSName domain;
134 string ip;
135 time_t next;
136 int attempts;
137 uint16_t id;
138 };
139
140 typedef std::list<NotificationRequest> d_nqueue_t;
141 d_nqueue_t d_nqueue;
142
143 };
144
145 struct ZoneStatus;
146
147 /** this class contains a thread that communicates with other nameserver and does housekeeping.
148 Initially, it is notified only of zones that need to be pulled in because they have been updated. */
149
150 class CommunicatorClass
151 {
152 public:
153 CommunicatorClass()
154 {
155 pthread_mutex_init(&d_lock,0);
156 pthread_mutex_init(&d_holelock,0);
157
158 d_tickinterval=60;
159 d_masterschanged=d_slaveschanged=true;
160 d_nsock4 = -1;
161 d_nsock6 = -1;
162 d_preventSelfNotification = false;
163 }
164 time_t doNotifications();
165 void go();
166
167
168 void drillHole(const DNSName &domain, const string &ip);
169 bool justNotified(const DNSName &domain, const string &ip);
170 void addSuckRequest(const DNSName &domain, const string &master);
171 void addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote);
172 void addTrySuperMasterRequest(DNSPacket *p);
173 void notify(const DNSName &domain, const string &ip);
174 void mainloop();
175 void retrievalLoopThread();
176 void sendNotification(int sock, const DNSName &domain, const ComboAddress& remote, uint16_t id);
177
178 static void *launchhelper(void *p)
179 {
180 static_cast<CommunicatorClass *>(p)->mainloop();
181 return 0;
182 }
183 static void *retrieveLaunchhelper(void *p)
184 {
185 static_cast<CommunicatorClass *>(p)->retrievalLoopThread();
186 return 0;
187 }
188 bool notifyDomain(const DNSName &domain);
189 private:
190 void makeNotifySockets();
191 void queueNotifyDomain(const DomainInfo& di, UeberBackend* B);
192 int d_nsock4, d_nsock6;
193 map<pair<DNSName,string>,time_t>d_holes;
194 pthread_mutex_t d_holelock;
195 void suck(const DNSName &domain, const string &remote);
196 void ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, boost::scoped_ptr<AuthLua>& pdl,
197 ZoneStatus& zs, vector<DNSRecord>* axfr);
198
199 void slaveRefresh(PacketHandler *P);
200 void masterUpdateCheck(PacketHandler *P);
201 pthread_mutex_t d_lock;
202
203 UniQueue d_suckdomains;
204 set<DNSName> d_inprogress;
205
206 Semaphore d_suck_sem;
207 Semaphore d_any_sem;
208 time_t d_tickinterval;
209 set<DomainInfo> d_tocheck;
210 vector<DNSPacket> d_potentialsupermasters;
211 set<string> d_alsoNotify;
212 NotificationQueue d_nq;
213 NetmaskGroup d_onlyNotify;
214 bool d_masterschanged, d_slaveschanged;
215 bool d_preventSelfNotification;
216
217 // Used to keep some state on domains that failed their freshness checks.
218 // uint64_t == counter of the number of failures (increased by 1 every consecutive slave-cycle-interval that the domain fails)
219 // time_t == wait at least until this time before attempting a new check
220 map<DNSName, pair<uint64_t, time_t> > d_failedSlaveRefresh;
221
222 struct RemoveSentinel
223 {
224 explicit RemoveSentinel(const DNSName& dn, CommunicatorClass* cc) : d_dn(dn), d_cc(cc)
225 {}
226
227 ~RemoveSentinel()
228 {
229 try {
230 Lock l(&d_cc->d_lock);
231 d_cc->d_inprogress.erase(d_dn);
232 }
233 catch(...) {
234 }
235 }
236 DNSName d_dn;
237 CommunicatorClass* d_cc;
238 };
239
240 };
241
242 // class that one day might be more than a function to help you get IP addresses for a nameserver
243 class FindNS
244 {
245 public:
246 vector<string> lookup(const DNSName &name, UeberBackend *b)
247 {
248 vector<string> addresses;
249
250 this->resolve_name(&addresses, name);
251
252 if(b) {
253 b->lookup(QType(QType::ANY),name);
254 DNSZoneRecord rr;
255 while(b->get(rr))
256 if(rr.dr.d_type == QType::A || rr.dr.d_type==QType::AAAA)
257 addresses.push_back(rr.dr.d_content->getZoneRepresentation()); // SOL if you have a CNAME for an NS
258 }
259 return addresses;
260 }
261
262 private:
263 void resolve_name(vector<string>* addresses, const DNSName& name)
264 {
265 struct addrinfo* res;
266 struct addrinfo hints;
267 memset(&hints, 0, sizeof(hints));
268 hints.ai_socktype = SOCK_DGRAM; // otherwise we get everything in triplicate (!)
269 for(int n = 0; n < 2; ++n) {
270 hints.ai_family = n ? AF_INET : AF_INET6;
271 ComboAddress remote;
272 remote.sin4.sin_family = AF_INET6;
273 if(!getaddrinfo(name.toString().c_str(), 0, &hints, &res)) {
274 struct addrinfo* address = res;
275 do {
276 if (address->ai_addrlen <= sizeof(remote)) {
277 memcpy(&remote, address->ai_addr, address->ai_addrlen);
278 addresses->push_back(remote.toString());
279 }
280 } while((address = address->ai_next));
281 freeaddrinfo(res);
282 }
283 }
284 }
285 };
286
287
288 #endif