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 master;
48   bool force;
49   enum RequestPriority : uint8_t { PdnsControl, Api, Notify, SerialRefresh, SignaturesRefresh };
50   std::pair<RequestPriority, uint64_t> priorityAndOrder;
operator <SuckRequest51   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     ordered_unique<member<SuckRequest,std::pair<SuckRequest::RequestPriority,uint64_t>,&SuckRequest::priorityAndOrder>>,
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:
add(const DNSName & domain,const string & ip)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_uint16();
80     nr.next     = time(0);
81 
82     d_nqueue.push_back(nr);
83   }
84 
removeIf(const string & remote,uint16_t id,const DNSName & domain)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 
getOne(DNSName & domain,string & ip,uint16_t * id,bool & purged)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 
earliest()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:
CommunicatorClass()153   CommunicatorClass()
154   {
155     d_tickinterval=60;
156     d_masterschanged=d_slaveschanged=true;
157     d_nsock4 = -1;
158     d_nsock6 = -1;
159     d_preventSelfNotification = false;
160     d_sorthelper = 0;
161   }
162   time_t doNotifications(PacketHandler *P);
163   void go();
164 
165 
166   void drillHole(const DNSName &domain, const string &ip);
167   bool justNotified(const DNSName &domain, const string &ip);
168   void addSuckRequest(const DNSName &domain, const ComboAddress& master, SuckRequest::RequestPriority, bool force=false);
169   void addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote);
170   void addTrySuperMasterRequest(const DNSPacket& p);
171   void notify(const DNSName &domain, const string &ip);
172   void mainloop();
173   void retrievalLoopThread();
174   void sendNotification(int sock, const DNSName &domain, const ComboAddress& remote, uint16_t id, UeberBackend* B);
175   bool notifyDomain(const DNSName &domain, UeberBackend* B);
176   vector<pair<DNSName, ComboAddress> > getSuckRequests();
177   size_t getSuckRequestsWaiting();
178 private:
179   void loadArgsIntoSet(const char *listname, set<string> &listset);
180   void makeNotifySockets();
181   void queueNotifyDomain(const DomainInfo& di, UeberBackend* B);
182   int d_nsock4, d_nsock6;
183   map<pair<DNSName,string>,time_t>d_holes;
184   std::mutex d_holelock;
185   void suck(const DNSName &domain, const ComboAddress& remote, bool force=false);
186   void ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, std::unique_ptr<AuthLua4>& pdl,
187                 ZoneStatus& zs, vector<DNSRecord>* axfr);
188 
189   void slaveRefresh(PacketHandler *P);
190   void masterUpdateCheck(PacketHandler *P);
191   std::mutex d_lock;
192 
193   uint64_t d_sorthelper;
194   UniQueue d_suckdomains;
195   set<DNSName> d_inprogress;
196 
197   Semaphore d_suck_sem;
198   Semaphore d_any_sem;
199   time_t d_tickinterval;
200   set<DomainInfo> d_tocheck;
201   struct cmp {
operator ()CommunicatorClass::cmp202     bool operator()(const DNSPacket& a, const DNSPacket& b) const {
203       return a.qdomain < b.qdomain;
204     };
205   };
206 
207   std::set<DNSPacket, cmp> d_potentialsupermasters;
208 
209   set<string> d_alsoNotify;
210   NotificationQueue d_nq;
211   NetmaskGroup d_onlyNotify;
212   bool d_masterschanged, d_slaveschanged;
213   bool d_preventSelfNotification;
214 
215   // Used to keep some state on domains that failed their freshness checks.
216   // uint64_t == counter of the number of failures (increased by 1 every consecutive slave-cycle-interval that the domain fails)
217   // time_t == wait at least until this time before attempting a new check
218   map<DNSName, pair<uint64_t, time_t> > d_failedSlaveRefresh;
219 
220   struct RemoveSentinel
221   {
RemoveSentinelCommunicatorClass::RemoveSentinel222     explicit RemoveSentinel(const DNSName& dn, CommunicatorClass* cc) : d_dn(dn), d_cc(cc)
223     {}
224 
~RemoveSentinelCommunicatorClass::RemoveSentinel225     ~RemoveSentinel()
226     {
227       try {
228         std::lock_guard<std::mutex> l(d_cc->d_lock);
229         d_cc->d_inprogress.erase(d_dn);
230       }
231       catch(...) {
232       }
233     }
234     DNSName d_dn;
235     CommunicatorClass* d_cc;
236 };
237 
238 };
239 
240 // class that one day might be more than a function to help you get IP addresses for a nameserver
241 class FindNS
242 {
243 public:
lookup(const DNSName & name,UeberBackend * b)244   vector<string> lookup(const DNSName &name, UeberBackend *b)
245   {
246     vector<string> addresses;
247 
248     this->resolve_name(&addresses, name);
249 
250     if(b) {
251         b->lookup(QType(QType::ANY),name,-1);
252         DNSZoneRecord rr;
253         while(b->get(rr))
254           if(rr.dr.d_type == QType::A || rr.dr.d_type==QType::AAAA)
255             addresses.push_back(rr.dr.d_content->getZoneRepresentation());   // SOL if you have a CNAME for an NS
256     }
257     return addresses;
258   }
259 
260 private:
resolve_name(vector<string> * addresses,const DNSName & name)261   void resolve_name(vector<string>* addresses, const DNSName& name)
262   {
263     struct addrinfo* res;
264     struct addrinfo hints;
265     memset(&hints, 0, sizeof(hints));
266     hints.ai_socktype = SOCK_DGRAM; // otherwise we get everything in triplicate (!)
267     for(int n = 0; n < 2; ++n) {
268       hints.ai_family = n ? AF_INET : AF_INET6;
269       ComboAddress remote;
270       remote.sin4.sin_family = AF_INET6;
271       if(!getaddrinfo(name.toString().c_str(), 0, &hints, &res)) {
272         struct addrinfo* address = res;
273         do {
274           if (address->ai_addrlen <= sizeof(remote)) {
275             remote.setSockaddr(address->ai_addr, address->ai_addrlen);
276             addresses->push_back(remote.toString());
277           }
278         } while((address = address->ai_next));
279         freeaddrinfo(res);
280       }
281     }
282   }
283 };
284