]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/communicator.hh
dnsdist: Fix DNS over plain HTTP broken by `reloadAllCertificates()`
[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 #pragma once
23 #include <pthread.h>
24 #include <string>
25 #include <semaphore.h>
26 #include <queue>
27 #include <list>
28 #include <limits>
29 #include <boost/multi_index_container.hpp>
30 #include <boost/multi_index/identity.hpp>
31 #include <boost/multi_index/sequenced_index.hpp>
32 using namespace boost::multi_index;
33
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <netdb.h>
37
38 #include "lock.hh"
39 #include "packethandler.hh"
40
41 #include "namespaces.hh"
42 #include "dns_random.hh"
43
44 struct SuckRequest
45 {
46 DNSName domain;
47 ComboAddress primary;
48 bool force;
49 enum RequestPriority : uint8_t
50 {
51 PdnsControl,
52 Api,
53 Notify,
54 SerialRefresh,
55 SignaturesRefresh
56 };
57 std::pair<RequestPriority, uint64_t> priorityAndOrder;
58 bool operator<(const SuckRequest& b) const
59 {
60 return std::tie(domain, primary) < std::tie(b.domain, b.primary);
61 }
62 };
63
64 struct IDTag
65 {
66 };
67
68 using UniQueue = multi_index_container<
69 SuckRequest,
70 indexed_by<
71 ordered_unique<member<SuckRequest, std::pair<SuckRequest::RequestPriority, uint64_t>, &SuckRequest::priorityAndOrder>>,
72 ordered_unique<tag<IDTag>, identity<SuckRequest>>>>;
73 using domains_by_name_t = UniQueue::index<IDTag>::type;
74
75 class NotificationQueue
76 {
77 public:
78 void add(const DNSName& domain, const string& ipstring, time_t delay = 0)
79 {
80 const ComboAddress ipaddress(ipstring);
81 add(domain, ipaddress, delay);
82 }
83
84 void add(const DNSName& domain, const ComboAddress& ipaddress, time_t delay = 0)
85 {
86 NotificationRequest nr;
87 nr.domain = domain;
88 nr.ip = ipaddress.toStringWithPort();
89 nr.attempts = 0;
90 nr.id = dns_random_uint16();
91 nr.next = time(nullptr) + delay;
92
93 d_nqueue.push_back(nr);
94 }
95
96 bool removeIf(const ComboAddress& remote, uint16_t id, const DNSName& domain)
97 {
98 for (auto i = d_nqueue.begin(); i != d_nqueue.end(); ++i) {
99 ComboAddress stQueued{i->ip};
100 if (i->id == id && stQueued == remote && i->domain == domain) {
101 d_nqueue.erase(i);
102 return true;
103 }
104 }
105 return false;
106 }
107
108 bool getOne(DNSName& domain, string& ip, uint16_t* id, bool& purged)
109 {
110 for (d_nqueue_t::iterator i = d_nqueue.begin(); i != d_nqueue.end(); ++i)
111 if (i->next <= time(nullptr)) {
112 i->attempts++;
113 purged = false;
114 i->next = time(nullptr) + 1 + (1 << i->attempts);
115 domain = i->domain;
116 ip = i->ip;
117 *id = i->id;
118 purged = false;
119 if (i->attempts > 4) {
120 purged = true;
121 d_nqueue.erase(i);
122 }
123 return true;
124 }
125 return false;
126 }
127
128 time_t earliest()
129 {
130 time_t early = std::numeric_limits<time_t>::max() - 1;
131 for (d_nqueue_t::const_iterator i = d_nqueue.begin(); i != d_nqueue.end(); ++i)
132 early = min(early, i->next);
133 return early - time(nullptr);
134 }
135
136 void dump();
137
138 private:
139 struct NotificationRequest
140 {
141 DNSName domain;
142 string ip;
143 time_t next;
144 int attempts;
145 uint16_t id;
146 };
147
148 using d_nqueue_t = std::list<NotificationRequest>;
149 d_nqueue_t d_nqueue;
150 };
151
152 struct ZoneStatus;
153
154 /** this class contains a thread that communicates with other nameserver and does housekeeping.
155 Initially, it is notified only of zones that need to be pulled in because they have been updated. */
156
157 class CommunicatorClass
158 {
159 public:
160 CommunicatorClass()
161 {
162 d_tickinterval = 60;
163 d_secondarieschanged = true;
164 d_nsock4 = -1;
165 d_nsock6 = -1;
166 d_preventSelfNotification = false;
167 }
168 time_t doNotifications(PacketHandler* P);
169 void go();
170
171 void drillHole(const DNSName& domain, const string& ip);
172 bool justNotified(const DNSName& domain, const string& ip);
173 void addSuckRequest(const DNSName& domain, const ComboAddress& primary, SuckRequest::RequestPriority, bool force = false);
174 void addSecondaryCheckRequest(const DomainInfo& di, const ComboAddress& remote);
175 void addTryAutoPrimaryRequest(const DNSPacket& p);
176 void notify(const DNSName& domain, const string& ip);
177 void mainloop();
178 void retrievalLoopThread();
179 void sendNotification(int sock, const DNSName& domain, const ComboAddress& remote, uint16_t id, UeberBackend* B);
180 bool notifyDomain(const DNSName& domain, UeberBackend* B);
181 vector<pair<DNSName, ComboAddress>> getSuckRequests();
182 size_t getSuckRequestsWaiting();
183
184 private:
185 static void loadArgsIntoSet(const char* listname, set<string>& listset);
186 void makeNotifySockets();
187 void queueNotifyDomain(const DomainInfo& di, UeberBackend* B);
188 int d_nsock4, d_nsock6;
189 LockGuarded<map<pair<DNSName, string>, time_t>> d_holes;
190
191 void suck(const DNSName& domain, const ComboAddress& remote, bool force = false);
192 void ixfrSuck(const DNSName& domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, ZoneStatus& zs, vector<DNSRecord>* axfr);
193
194 void secondaryRefresh(PacketHandler* P);
195 void primaryUpdateCheck(PacketHandler* P);
196 void getUpdatedProducers(UeberBackend* B, vector<DomainInfo>& domains, const std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes);
197
198 Semaphore d_suck_sem;
199 Semaphore d_any_sem;
200
201 set<string> d_alsoNotify;
202 NetmaskGroup d_onlyNotify;
203 NotificationQueue d_nq;
204
205 time_t d_tickinterval;
206 bool d_secondarieschanged;
207 bool d_preventSelfNotification;
208 time_t d_delayNotifications{0};
209
210 struct Data
211 {
212 uint64_t d_sorthelper{0};
213 UniQueue d_suckdomains;
214 set<DNSName> d_inprogress;
215
216 set<DomainInfo> d_tocheck;
217 struct cmp
218 {
219 bool operator()(const DNSPacket& a, const DNSPacket& b) const
220 {
221 return a.qdomain < b.qdomain;
222 };
223 };
224
225 std::set<DNSPacket, cmp> d_potentialautoprimaries;
226
227 // Used to keep some state on domains that failed their freshness checks.
228 // uint64_t == counter of the number of failures (increased by 1 every consecutive slave-cycle-interval that the domain fails)
229 // time_t == wait at least until this time before attempting a new check
230 map<DNSName, pair<uint64_t, time_t>> d_failedSecondaryRefresh;
231 };
232
233 LockGuarded<Data> d_data;
234
235 struct RemoveSentinel
236 {
237 explicit RemoveSentinel(const DNSName& dn, CommunicatorClass* cc) :
238 d_dn(dn), d_cc(cc)
239 {}
240
241 ~RemoveSentinel()
242 {
243 try {
244 d_cc->d_data.lock()->d_inprogress.erase(d_dn);
245 }
246 catch (...) {
247 }
248 }
249 DNSName d_dn;
250 CommunicatorClass* d_cc;
251 };
252 };
253
254 // class that one day might be more than a function to help you get IP addresses for a nameserver
255 class FindNS
256 {
257 public:
258 vector<string> lookup(const DNSName& name, UeberBackend* b)
259 {
260 vector<string> addresses;
261
262 this->resolve_name(&addresses, name);
263
264 if (b) {
265 b->lookup(QType(QType::ANY), name, -1);
266 DNSZoneRecord rr;
267 while (b->get(rr))
268 if (rr.dr.d_type == QType::A || rr.dr.d_type == QType::AAAA)
269 addresses.push_back(rr.dr.getContent()->getZoneRepresentation()); // SOL if you have a CNAME for an NS
270 }
271 return addresses;
272 }
273
274 private:
275 void resolve_name(vector<string>* addresses, const DNSName& name)
276 {
277 struct addrinfo* res;
278 struct addrinfo hints;
279 memset(&hints, 0, sizeof(hints));
280 hints.ai_socktype = SOCK_DGRAM; // otherwise we get everything in triplicate (!)
281 for (int n = 0; n < 2; ++n) {
282 hints.ai_family = n ? AF_INET : AF_INET6;
283 ComboAddress remote;
284 remote.sin4.sin_family = AF_INET6;
285 if (!getaddrinfo(name.toString().c_str(), 0, &hints, &res)) {
286 struct addrinfo* address = res;
287 do {
288 if (address->ai_addrlen <= sizeof(remote)) {
289 remote.setSockaddr(address->ai_addr, address->ai_addrlen);
290 addresses->push_back(remote.toString());
291 }
292 } while ((address = address->ai_next));
293 freeaddrinfo(res);
294 }
295 }
296 }
297 };