]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ixfrdist.cc
783880195e3d337891b0f0589c0db89ad3d62bf1
[thirdparty/pdns.git] / pdns / ixfrdist.cc
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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <boost/program_options.hpp>
26 #include <arpa/inet.h>
27 #include <sys/types.h>
28 #include <grp.h>
29 #include <pwd.h>
30 #include <sys/stat.h>
31 #include <mutex>
32 #include <thread>
33 #include "threadname.hh"
34 #include <dirent.h>
35 #include <queue>
36 #include <condition_variable>
37 #include "ixfr.hh"
38 #include "ixfrutils.hh"
39 #include "resolver.hh"
40 #include "dns_random.hh"
41 #include "sstuff.hh"
42 #include "mplexer.hh"
43 #include "misc.hh"
44 #include "iputils.hh"
45 #include "logger.hh"
46 #include "ixfrdist-stats.hh"
47 #include "ixfrdist-web.hh"
48 #include <yaml-cpp/yaml.h>
49
50 /* BEGIN Needed because of deeper dependencies */
51 #include "arguments.hh"
52 #include "statbag.hh"
53 StatBag S;
54
55 ArgvMap &arg()
56 {
57 static ArgvMap theArg;
58 return theArg;
59 }
60 /* END Needed because of deeper dependencies */
61
62 // Allows reading/writing ComboAddresses and DNSNames in YAML-cpp
63 namespace YAML {
64 template<>
65 struct convert<ComboAddress> {
66 static Node encode(const ComboAddress& rhs) {
67 return Node(rhs.toStringWithPort());
68 }
69 static bool decode(const Node& node, ComboAddress& rhs) {
70 if (!node.IsScalar()) {
71 return false;
72 }
73 try {
74 rhs = ComboAddress(node.as<string>(), 53);
75 return true;
76 } catch(const runtime_error &e) {
77 return false;
78 } catch (const PDNSException &e) {
79 return false;
80 }
81 }
82 };
83
84 template<>
85 struct convert<DNSName> {
86 static Node encode(const DNSName& rhs) {
87 return Node(rhs.toStringRootDot());
88 }
89 static bool decode(const Node& node, DNSName& rhs) {
90 if (!node.IsScalar()) {
91 return false;
92 }
93 try {
94 rhs = DNSName(node.as<string>());
95 return true;
96 } catch(const runtime_error &e) {
97 return false;
98 } catch (const PDNSException &e) {
99 return false;
100 }
101 }
102 };
103
104 template<>
105 struct convert<Netmask> {
106 static Node encode(const Netmask& rhs) {
107 return Node(rhs.toString());
108 }
109 static bool decode(const Node& node, Netmask& rhs) {
110 if (!node.IsScalar()) {
111 return false;
112 }
113 try {
114 rhs = Netmask(node.as<string>());
115 return true;
116 } catch(const runtime_error &e) {
117 return false;
118 } catch (const PDNSException &e) {
119 return false;
120 }
121 }
122 };
123 } // namespace YAML
124
125 struct ixfrdiff_t {
126 shared_ptr<SOARecordContent> oldSOA;
127 shared_ptr<SOARecordContent> newSOA;
128 vector<DNSRecord> removals;
129 vector<DNSRecord> additions;
130 };
131
132 struct ixfrinfo_t {
133 shared_ptr<SOARecordContent> soa; // The SOA of the latest AXFR
134 records_t latestAXFR; // The most recent AXFR
135 vector<std::shared_ptr<ixfrdiff_t>> ixfrDiffs;
136 };
137
138 // Why a struct? This way we can add more options to a domain in the future
139 struct ixfrdistdomain_t {
140 set<ComboAddress> masters; // A set so we can do multiple master addresses in the future
141 };
142
143 // This contains the configuration for each domain
144 static map<DNSName, ixfrdistdomain_t> g_domainConfigs;
145
146 // Map domains and their data
147 static std::map<DNSName, std::shared_ptr<ixfrinfo_t>> g_soas;
148 static std::mutex g_soas_mutex;
149
150 // Condition variable for TCP handling
151 static std::condition_variable g_tcpHandlerCV;
152 static std::queue<pair<int, ComboAddress>> g_tcpRequestFDs;
153 static std::mutex g_tcpRequestFDsMutex;
154
155 namespace po = boost::program_options;
156
157 static bool g_exiting = false;
158
159 static NetmaskGroup g_acl;
160 static bool g_compress = false;
161
162 static ixfrdistStats g_stats;
163
164 // g_stats is static, so local to this file. But the webserver needs this info
165 string doGetStats() {
166 return g_stats.getStats();
167 }
168
169 static void handleSignal(int signum) {
170 g_log<<Logger::Notice<<"Got "<<strsignal(signum)<<" signal";
171 if (g_exiting) {
172 g_log<<Logger::Notice<<", this is the second time we were asked to stop, forcefully exiting"<<endl;
173 exit(EXIT_FAILURE);
174 }
175 g_log<<Logger::Notice<<", stopping, this may take a few second due to in-progress transfers and cleanup. Send this signal again to forcefully stop"<<endl;
176 g_exiting = true;
177 }
178
179 static void usage(po::options_description &desc) {
180 cerr << "Usage: ixfrdist [OPTION]..."<<endl;
181 cerr << desc << "\n";
182 }
183
184 // The compiler does not like using rfc1982LessThan in std::sort directly
185 static bool sortSOA(uint32_t i, uint32_t j) {
186 return rfc1982LessThan(i, j);
187 }
188
189 static void cleanUpDomain(const DNSName& domain, const uint16_t& keep, const string& workdir) {
190 string dir = workdir + "/" + domain.toString();
191 DIR *dp;
192 dp = opendir(dir.c_str());
193 if (dp == nullptr) {
194 return;
195 }
196 vector<uint32_t> zoneVersions;
197 struct dirent *d;
198 while ((d = readdir(dp)) != nullptr) {
199 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) {
200 continue;
201 }
202 zoneVersions.push_back(std::stoi(d->d_name));
203 }
204 closedir(dp);
205 g_log<<Logger::Info<<"Found "<<zoneVersions.size()<<" versions of "<<domain<<", asked to keep "<<keep<<", ";
206 if (zoneVersions.size() <= keep) {
207 g_log<<Logger::Info<<"not cleaning up"<<endl;
208 return;
209 }
210 g_log<<Logger::Info<<"cleaning up the oldest "<<zoneVersions.size() - keep<<endl;
211
212 // Sort the versions
213 std::sort(zoneVersions.begin(), zoneVersions.end(), sortSOA);
214
215 // And delete all the old ones
216 {
217 // Lock to ensure no one reads this.
218 std::lock_guard<std::mutex> guard(g_soas_mutex);
219 for (auto iter = zoneVersions.cbegin(); iter != zoneVersions.cend() - keep; ++iter) {
220 string fname = dir + "/" + std::to_string(*iter);
221 g_log<<Logger::Debug<<"Removing "<<fname<<endl;
222 unlink(fname.c_str());
223 }
224 }
225 }
226
227 static shared_ptr<SOARecordContent> getSOAFromRecords(const records_t& records) {
228 for (const auto& dnsrecord : records) {
229 if (dnsrecord.d_type == QType::SOA) {
230 auto soa = getRR<SOARecordContent>(dnsrecord);
231 if (soa == nullptr) {
232 throw PDNSException("Unable to determine SOARecordContent from old records");
233 }
234 return soa;
235 }
236 }
237 throw PDNSException("No SOA in supplied records");
238 }
239
240 static void makeIXFRDiff(const records_t& from, const records_t& to, std::shared_ptr<ixfrdiff_t>& diff, const shared_ptr<SOARecordContent>& fromSOA = nullptr, const shared_ptr<SOARecordContent>& toSOA = nullptr) {
241 set_difference(from.cbegin(), from.cend(), to.cbegin(), to.cend(), back_inserter(diff->removals), from.value_comp());
242 set_difference(to.cbegin(), to.cend(), from.cbegin(), from.cend(), back_inserter(diff->additions), from.value_comp());
243 diff->oldSOA = fromSOA;
244 if (fromSOA == nullptr) {
245 diff->oldSOA = getSOAFromRecords(from);
246 }
247 diff->newSOA = toSOA;
248 if (toSOA == nullptr) {
249 diff->newSOA = getSOAFromRecords(to);
250 }
251 }
252
253 /* you can _never_ alter the content of the resulting shared pointer */
254 static std::shared_ptr<ixfrinfo_t> getCurrentZoneInfo(const DNSName& domain)
255 {
256 std::lock_guard<std::mutex> guard(g_soas_mutex);
257 return g_soas[domain];
258 }
259
260 static void updateCurrentZoneInfo(const DNSName& domain, std::shared_ptr<ixfrinfo_t>& newInfo)
261 {
262 std::lock_guard<std::mutex> guard(g_soas_mutex);
263 g_soas[domain] = newInfo;
264 g_stats.setSOASerial(domain, newInfo->soa->d_st.serial);
265 // FIXME: also report zone size?
266 }
267
268 void updateThread(const string& workdir, const uint16_t& keep, const uint16_t& axfrTimeout, const uint16_t& soaRetry) {
269 setThreadName("ixfrdist/update");
270 std::map<DNSName, time_t> lastCheck;
271
272 // Initialize the serials we have
273 for (const auto &domainConfig : g_domainConfigs) {
274 DNSName domain = domainConfig.first;
275 lastCheck[domain] = 0;
276 string dir = workdir + "/" + domain.toString();
277 try {
278 g_log<<Logger::Info<<"Trying to initially load domain "<<domain<<" from disk"<<endl;
279 auto serial = getSerialsFromDir(dir);
280 shared_ptr<SOARecordContent> soa;
281 {
282 string fname = workdir + "/" + domain.toString() + "/" + std::to_string(serial);
283 loadSOAFromDisk(domain, fname, soa);
284 records_t records;
285 if (soa != nullptr) {
286 loadZoneFromDisk(records, fname, domain);
287 }
288 auto zoneInfo = std::make_shared<ixfrinfo_t>();
289 zoneInfo->latestAXFR = std::move(records);
290 zoneInfo->soa = soa;
291 updateCurrentZoneInfo(domain, zoneInfo);
292 }
293 if (soa != nullptr) {
294 g_log<<Logger::Notice<<"Loaded zone "<<domain<<" with serial "<<soa->d_st.serial<<endl;
295 // Initial cleanup
296 cleanUpDomain(domain, keep, workdir);
297 }
298 } catch (runtime_error &e) {
299 // Most likely, the directory does not exist.
300 g_log<<Logger::Info<<e.what()<<", attempting to create"<<endl;
301 // Attempt to create it, if _that_ fails, there is no hope
302 if (mkdir(dir.c_str(), 0777) == -1 && errno != EEXIST) {
303 g_log<<Logger::Error<<"Could not create '"<<dir<<"': "<<strerror(errno)<<endl;
304 exit(EXIT_FAILURE);
305 }
306 }
307 }
308
309 g_log<<Logger::Notice<<"Update Thread started"<<endl;
310
311 while (true) {
312 if (g_exiting) {
313 g_log<<Logger::Notice<<"UpdateThread stopped"<<endl;
314 break;
315 }
316 time_t now = time(nullptr);
317 for (const auto &domainConfig : g_domainConfigs) {
318
319 if (g_exiting) {
320 break;
321 }
322
323 DNSName domain = domainConfig.first;
324 shared_ptr<SOARecordContent> current_soa;
325 const auto& zoneInfo = getCurrentZoneInfo(domain);
326 if (zoneInfo != nullptr) {
327 current_soa = zoneInfo->soa;
328 }
329
330 auto& zoneLastCheck = lastCheck[domain];
331 if ((current_soa != nullptr && now - zoneLastCheck < current_soa->d_st.refresh) || // Only check if we have waited `refresh` seconds
332 (current_soa == nullptr && now - zoneLastCheck < soaRetry)) { // Or if we could not get an update at all still, every 30 seconds
333 continue;
334 }
335
336 // TODO Keep track of 'down' masters
337 set<ComboAddress>::const_iterator it(domainConfig.second.masters.begin());
338 std::advance(it, random() % domainConfig.second.masters.size());
339 ComboAddress master = *it;
340
341 string dir = workdir + "/" + domain.toString();
342 g_log<<Logger::Info<<"Attempting to retrieve SOA Serial update for '"<<domain<<"' from '"<<master.toStringWithPort()<<"'"<<endl;
343 shared_ptr<SOARecordContent> sr;
344 try {
345 zoneLastCheck = now;
346 g_stats.incrementSOAChecks(domain);
347 auto newSerial = getSerialFromMaster(master, domain, sr); // TODO TSIG
348 if(current_soa != nullptr) {
349 g_log<<Logger::Info<<"Got SOA Serial for "<<domain<<" from "<<master.toStringWithPort()<<": "<< newSerial<<", had Serial: "<<current_soa->d_st.serial;
350 if (newSerial == current_soa->d_st.serial) {
351 g_log<<Logger::Info<<", not updating."<<endl;
352 continue;
353 }
354 g_log<<Logger::Info<<", will update."<<endl;
355 }
356 } catch (runtime_error &e) {
357 g_log<<Logger::Warning<<"Unable to get SOA serial update for '"<<domain<<"' from master "<<master.toStringWithPort()<<": "<<e.what()<<endl;
358 g_stats.incrementSOAChecksFailed(domain);
359 continue;
360 }
361 // Now get the full zone!
362 g_log<<Logger::Info<<"Attempting to receive full zonedata for '"<<domain<<"'"<<endl;
363 ComboAddress local = master.isIPv4() ? ComboAddress("0.0.0.0") : ComboAddress("::");
364 TSIGTriplet tt;
365
366 // The *new* SOA
367 shared_ptr<SOARecordContent> soa;
368 records_t records;
369 try {
370 AXFRRetriever axfr(master, domain, tt, &local);
371 unsigned int nrecords=0;
372 Resolver::res_t nop;
373 vector<DNSRecord> chunk;
374 time_t t_start = time(nullptr);
375 time_t axfr_now = time(nullptr);
376 while(axfr.getChunk(nop, &chunk, (axfr_now - t_start + axfrTimeout))) {
377 for(auto& dr : chunk) {
378 if(dr.d_type == QType::TSIG)
379 continue;
380 if(!dr.d_name.isPartOf(domain)) {
381 throw PDNSException("Out-of-zone data received during AXFR of "+domain.toLogString());
382 }
383 dr.d_name.makeUsRelative(domain);
384 records.insert(dr);
385 nrecords++;
386 if (dr.d_type == QType::SOA) {
387 soa = getRR<SOARecordContent>(dr);
388 }
389 }
390 axfr_now = time(nullptr);
391 if (axfr_now - t_start > axfrTimeout) {
392 g_stats.incrementAXFRFailures(domain);
393 throw PDNSException("Total AXFR time exceeded!");
394 }
395 }
396 if (soa == nullptr) {
397 g_stats.incrementAXFRFailures(domain);
398 g_log<<Logger::Warning<<"No SOA was found in the AXFR of "<<domain<<endl;
399 continue;
400 }
401 g_log<<Logger::Notice<<"Retrieved all zone data for "<<domain<<". Received "<<nrecords<<" records."<<endl;
402 } catch (PDNSException &e) {
403 g_stats.incrementAXFRFailures(domain);
404 g_log<<Logger::Warning<<"Could not retrieve AXFR for '"<<domain<<"': "<<e.reason<<endl;
405 continue;
406 } catch (runtime_error &e) {
407 g_stats.incrementAXFRFailures(domain);
408 g_log<<Logger::Warning<<"Could not retrieve AXFR for zone '"<<domain<<"': "<<e.what()<<endl;
409 continue;
410 }
411
412 try {
413
414 writeZoneToDisk(records, domain, dir);
415 g_log<<Logger::Notice<<"Wrote zonedata for "<<domain<<" with serial "<<soa->d_st.serial<<" to "<<dir<<endl;
416
417 const auto oldZoneInfo = getCurrentZoneInfo(domain);
418 auto zoneInfo = std::make_shared<ixfrinfo_t>();
419
420 if (oldZoneInfo && !oldZoneInfo->latestAXFR.empty()) {
421 auto diff = std::make_shared<ixfrdiff_t>();
422 zoneInfo->ixfrDiffs = oldZoneInfo->ixfrDiffs;
423 g_log<<Logger::Debug<<"Calculating diff for "<<domain<<endl;
424 makeIXFRDiff(oldZoneInfo->latestAXFR, records, diff, oldZoneInfo->soa, soa);
425 g_log<<Logger::Debug<<"Calculated diff for "<<domain<<", we had "<<diff->removals.size()<<" removals and "<<diff->additions.size()<<" additions"<<endl;
426 zoneInfo->ixfrDiffs.push_back(std::move(diff));
427 }
428
429 // Clean up the diffs
430 while (zoneInfo->ixfrDiffs.size() > keep) {
431 zoneInfo->ixfrDiffs.erase(zoneInfo->ixfrDiffs.begin());
432 }
433
434 g_log<<Logger::Debug<<"Zone "<<domain<<" previously contained "<<(oldZoneInfo ? oldZoneInfo->latestAXFR.size() : 0)<<" entries, "<<records.size()<<" now"<<endl;
435 zoneInfo->latestAXFR = std::move(records);
436 zoneInfo->soa = soa;
437 updateCurrentZoneInfo(domain, zoneInfo);
438 } catch (PDNSException &e) {
439 g_stats.incrementAXFRFailures(domain);
440 g_log<<Logger::Warning<<"Could not save zone '"<<domain<<"' to disk: "<<e.reason<<endl;
441 } catch (runtime_error &e) {
442 g_stats.incrementAXFRFailures(domain);
443 g_log<<Logger::Warning<<"Could not save zone '"<<domain<<"' to disk: "<<e.what()<<endl;
444 }
445
446 // Now clean up the directory
447 cleanUpDomain(domain, keep, workdir);
448 } /* for (const auto &domain : domains) */
449 sleep(1);
450 } /* while (true) */
451 } /* updateThread */
452
453 static bool checkQuery(const MOADNSParser& mdp, const ComboAddress& saddr, const bool udp = true, const string& logPrefix="") {
454 vector<string> info_msg;
455
456 g_log<<Logger::Debug<<logPrefix<<"Had "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" query from "<<saddr.toStringWithPort()<<endl;
457
458 if (udp && mdp.d_qtype != QType::SOA && mdp.d_qtype != QType::IXFR) {
459 info_msg.push_back("QType is unsupported (" + QType(mdp.d_qtype).getName() + " is not in {SOA,IXFR})");
460 }
461
462 if (!udp && mdp.d_qtype != QType::SOA && mdp.d_qtype != QType::IXFR && mdp.d_qtype != QType::AXFR) {
463 info_msg.push_back("QType is unsupported (" + QType(mdp.d_qtype).getName() + " is not in {SOA,IXFR,AXFR})");
464 }
465
466 {
467 if (g_domainConfigs.find(mdp.d_qname) == g_domainConfigs.end()) {
468 info_msg.push_back("Domain name '" + mdp.d_qname.toLogString() + "' is not configured for distribution");
469 }
470 else {
471 const auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
472 if (zoneInfo == nullptr) {
473 info_msg.push_back("Domain has not been transferred yet");
474 }
475 }
476 }
477
478 if (!info_msg.empty()) {
479 g_log<<Logger::Warning<<logPrefix<<"Refusing "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" query from "<<saddr.toStringWithPort();
480 g_log<<Logger::Warning<<": ";
481 bool first = true;
482 for (const auto& s : info_msg) {
483 if (!first) {
484 g_log<<Logger::Warning<<", ";
485 }
486 first = false;
487 g_log<<Logger::Warning<<s;
488 }
489 g_log<<Logger::Warning<<endl;
490 return false;
491 }
492
493 return true;
494 }
495
496 /*
497 * Returns a vector<uint8_t> that represents the full positive response to a SOA
498 * query. QNAME is read from mdp.
499 */
500 static bool makeSOAPacket(const MOADNSParser& mdp, vector<uint8_t>& packet) {
501
502 auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
503 if (zoneInfo == nullptr) {
504 return false;
505 }
506
507 DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
508 pw.getHeader()->id = mdp.d_header.id;
509 pw.getHeader()->rd = mdp.d_header.rd;
510 pw.getHeader()->qr = 1;
511
512 pw.startRecord(mdp.d_qname, QType::SOA);
513 zoneInfo->soa->toPacket(pw);
514 pw.commit();
515
516 return true;
517 }
518
519 /*
520 * Returns a vector<uint8_t> that represents the full REFUSED response to a
521 * query. QNAME and type are read from mdp.
522 */
523 static bool makeRefusedPacket(const MOADNSParser& mdp, vector<uint8_t>& packet) {
524 DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
525 pw.getHeader()->id = mdp.d_header.id;
526 pw.getHeader()->rd = mdp.d_header.rd;
527 pw.getHeader()->qr = 1;
528 pw.getHeader()->rcode = RCode::Refused;
529
530 return true;
531 }
532
533 static vector<uint8_t> getSOAPacket(const MOADNSParser& mdp, const shared_ptr<SOARecordContent>& soa) {
534 vector<uint8_t> packet;
535 DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
536 pw.getHeader()->id = mdp.d_header.id;
537 pw.getHeader()->rd = mdp.d_header.rd;
538 pw.getHeader()->qr = 1;
539
540 // Add the first SOA
541 pw.startRecord(mdp.d_qname, QType::SOA);
542 soa->toPacket(pw);
543 pw.commit();
544 return packet;
545 }
546
547 static bool sendPacketOverTCP(int fd, const std::vector<uint8_t>& packet)
548 {
549 char sendBuf[2];
550 sendBuf[0]=packet.size()/256;
551 sendBuf[1]=packet.size()%256;
552
553 ssize_t send = writen2(fd, sendBuf, 2);
554 send += writen2(fd, &packet[0], packet.size());
555 return true;
556 }
557
558 static bool addRecordToWriter(DNSPacketWriter& pw, const DNSName& zoneName, const DNSRecord& record, bool compress)
559 {
560 pw.startRecord(record.d_name + zoneName, record.d_type, record.d_ttl, QClass::IN, DNSResourceRecord::ANSWER, compress);
561 record.d_content->toPacket(pw);
562 if (pw.size() > 65535) {
563 pw.rollback();
564 return false;
565 }
566 return true;
567 }
568
569 template <typename T> static bool sendRecordsOverTCP(int fd, const MOADNSParser& mdp, const T& records)
570 {
571 vector<uint8_t> packet;
572
573 for (auto it = records.cbegin(); it != records.cend();) {
574 bool recordsAdded = false;
575 packet.clear();
576 DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
577 pw.getHeader()->id = mdp.d_header.id;
578 pw.getHeader()->rd = mdp.d_header.rd;
579 pw.getHeader()->qr = 1;
580
581 while (it != records.cend()) {
582 if (it->d_type == QType::SOA) {
583 it++;
584 continue;
585 }
586
587 if (addRecordToWriter(pw, mdp.d_qname, *it, g_compress)) {
588 recordsAdded = true;
589 it++;
590 }
591 else {
592 if (recordsAdded) {
593 pw.commit();
594 sendPacketOverTCP(fd, packet);
595 }
596 if (it == records.cbegin()) {
597 /* something is wrong */
598 return false;
599 }
600
601 break;
602 }
603 }
604
605 if (it == records.cend() && recordsAdded) {
606 pw.commit();
607 sendPacketOverTCP(fd, packet);
608 }
609 }
610
611 return true;
612 }
613
614
615 static bool handleAXFR(int fd, const MOADNSParser& mdp) {
616 /* we get a shared pointer of the zone info that we can't modify, ever.
617 A newer one may arise in the meantime, but this one will stay valid
618 until we release it.
619 */
620
621 g_stats.incrementAXFRinQueries(mdp.d_qname);
622
623 auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
624 if (zoneInfo == nullptr) {
625 return false;
626 }
627
628 shared_ptr<SOARecordContent> soa = zoneInfo->soa;
629 const records_t& records = zoneInfo->latestAXFR;
630
631 // Initial SOA
632 const auto soaPacket = getSOAPacket(mdp, soa);
633 if (!sendPacketOverTCP(fd, soaPacket)) {
634 return false;
635 }
636
637 if (!sendRecordsOverTCP(fd, mdp, records)) {
638 return false;
639 }
640
641 // Final SOA
642 if (!sendPacketOverTCP(fd, soaPacket)) {
643 return false;
644 }
645
646 return true;
647 }
648
649 /* Produces an IXFR if one can be made according to the rules in RFC 1995 and
650 * creates a SOA or AXFR packet when required by the RFC.
651 */
652 static bool handleIXFR(int fd, const ComboAddress& destination, const MOADNSParser& mdp, const shared_ptr<SOARecordContent>& clientSOA) {
653 vector<std::shared_ptr<ixfrdiff_t>> toSend;
654
655 /* we get a shared pointer of the zone info that we can't modify, ever.
656 A newer one may arise in the meantime, but this one will stay valid
657 until we release it.
658 */
659
660 g_stats.incrementIXFRinQueries(mdp.d_qname);
661
662 auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
663 if (zoneInfo == nullptr) {
664 return false;
665 }
666
667 uint32_t ourLatestSerial = zoneInfo->soa->d_st.serial;
668
669 if (rfc1982LessThan(ourLatestSerial, clientSOA->d_st.serial) || ourLatestSerial == clientSOA->d_st.serial) {
670 /* RFC 1995 Section 2
671 * If an IXFR query with the same or newer version number than that of
672 * the server is received, it is replied to with a single SOA record of
673 * the server's current version.
674 */
675 vector<uint8_t> packet;
676 bool ret = makeSOAPacket(mdp, packet);
677 if (ret) {
678 sendPacketOverTCP(fd, packet);
679 }
680 return ret;
681 }
682
683 // as we use push_back in the updater, we know the vector is sorted as oldest first
684 bool shouldAdd = false;
685 // Get all relevant IXFR differences
686 for (const auto& diff : zoneInfo->ixfrDiffs) {
687 if (shouldAdd) {
688 toSend.push_back(diff);
689 continue;
690 }
691 if (diff->oldSOA->d_st.serial == clientSOA->d_st.serial) {
692 toSend.push_back(diff);
693 // Add all consecutive diffs
694 shouldAdd = true;
695 }
696 }
697
698 if (toSend.empty()) {
699 // FIXME: incrementIXFRFallbacks
700 g_log<<Logger::Warning<<"No IXFR available from serial "<<clientSOA->d_st.serial<<" for zone "<<mdp.d_qname<<", attempting to send AXFR"<<endl;
701 return handleAXFR(fd, mdp);
702 }
703
704 std::vector<std::vector<uint8_t>> packets;
705 for (const auto& diff : toSend) {
706 /* An IXFR packet's ANSWER section looks as follows:
707 * SOA new_serial
708 * SOA old_serial
709 * ... removed records ...
710 * SOA new_serial
711 * ... added records ...
712 * SOA new_serial
713 */
714
715 const auto newSOAPacket = getSOAPacket(mdp, diff->newSOA);
716 const auto oldSOAPacket = getSOAPacket(mdp, diff->oldSOA);
717
718 if (!sendPacketOverTCP(fd, newSOAPacket)) {
719 return false;
720 }
721
722 if (!sendPacketOverTCP(fd, oldSOAPacket)) {
723 return false;
724 }
725
726 if (!sendRecordsOverTCP(fd, mdp, diff->removals)) {
727 return false;
728 }
729
730 if (!sendPacketOverTCP(fd, newSOAPacket)) {
731 return false;
732 }
733
734 if (!sendRecordsOverTCP(fd, mdp, diff->additions)) {
735 return false;
736 }
737
738 if (!sendPacketOverTCP(fd, newSOAPacket)) {
739 return false;
740 }
741 }
742
743 return true;
744 }
745
746 static bool allowedByACL(const ComboAddress& addr) {
747 return g_acl.match(addr);
748 }
749
750 static void handleUDPRequest(int fd, boost::any&) {
751 // TODO make the buffer-size configurable
752 char buf[4096];
753 ComboAddress saddr;
754 socklen_t fromlen = sizeof(saddr);
755 int res = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*) &saddr, &fromlen);
756
757 if (res == 0) {
758 g_log<<Logger::Warning<<"Got an empty message from "<<saddr.toStringWithPort()<<endl;
759 return;
760 }
761
762 if(res < 0) {
763 auto savedErrno = errno;
764 g_log<<Logger::Warning<<"Could not read message from "<<saddr.toStringWithPort()<<": "<<strerror(savedErrno)<<endl;
765 return;
766 }
767
768 if (saddr == ComboAddress("0.0.0.0", 0)) {
769 g_log<<Logger::Warning<<"Could not determine source of message"<<endl;
770 return;
771 }
772
773 if (!allowedByACL(saddr)) {
774 g_log<<Logger::Warning<<"UDP query from "<<saddr.toString()<<" is not allowed, dropping"<<endl;
775 return;
776 }
777
778 MOADNSParser mdp(true, string(buf, res));
779 vector<uint8_t> packet;
780 if (checkQuery(mdp, saddr)) {
781 /* RFC 1995 Section 2
782 * Transport of a query may be by either UDP or TCP. If an IXFR query
783 * is via UDP, the IXFR server may attempt to reply using UDP if the
784 * entire response can be contained in a single DNS packet. If the UDP
785 * reply does not fit, the query is responded to with a single SOA
786 * record of the server's current version to inform the client that a
787 * TCP query should be initiated.
788 *
789 * Let's not complicate this with IXFR over UDP (and looking if we need to truncate etc).
790 * Just send the current SOA and let the client try over TCP
791 */
792 g_stats.incrementSOAinQueries(mdp.d_qname); // FIXME: this also counts IXFR queries (but the response is the same as to a SOA query)
793 makeSOAPacket(mdp, packet);
794 } else {
795 makeRefusedPacket(mdp, packet);
796 }
797
798 if(sendto(fd, &packet[0], packet.size(), 0, (struct sockaddr*) &saddr, fromlen) < 0) {
799 auto savedErrno = errno;
800 g_log<<Logger::Warning<<"Could not send reply for "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" to "<<saddr.toStringWithPort()<<": "<<strerror(savedErrno)<<endl;
801 }
802 return;
803 }
804
805 static void handleTCPRequest(int fd, boost::any&) {
806 ComboAddress saddr;
807 int cfd = 0;
808
809 try {
810 cfd = SAccept(fd, saddr);
811 setBlocking(cfd);
812 } catch(runtime_error &e) {
813 g_log<<Logger::Error<<e.what()<<endl;
814 return;
815 }
816
817 if (saddr == ComboAddress("0.0.0.0", 0)) {
818 g_log<<Logger::Warning<<"Could not determine source of message"<<endl;
819 close(cfd);
820 return;
821 }
822
823 if (!allowedByACL(saddr)) {
824 g_log<<Logger::Warning<<"TCP query from "<<saddr.toString()<<" is not allowed, dropping"<<endl;
825 close(cfd);
826 return;
827 }
828
829 {
830 std::lock_guard<std::mutex> lg(g_tcpRequestFDsMutex);
831 g_tcpRequestFDs.push({cfd, saddr});
832 }
833 g_tcpHandlerCV.notify_one();
834 }
835
836 /* Thread to handle TCP traffic
837 */
838 static void tcpWorker(int tid) {
839 setThreadName("ixfrdist/tcpWor");
840 string prefix = "TCP Worker " + std::to_string(tid) + ": ";
841
842 while(true) {
843 g_log<<Logger::Debug<<prefix<<"ready for a new request!"<<endl;
844 std::unique_lock<std::mutex> lk(g_tcpRequestFDsMutex);
845 g_tcpHandlerCV.wait(lk, []{return g_tcpRequestFDs.size() || g_exiting ;});
846 if (g_exiting) {
847 g_log<<Logger::Debug<<prefix<<"Stopping thread"<<endl;
848 break;
849 }
850 g_log<<Logger::Debug<<prefix<<"Going to handle a query"<<endl;
851 auto request = g_tcpRequestFDs.front();
852 g_tcpRequestFDs.pop();
853 lk.unlock();
854
855 int cfd = request.first;
856 ComboAddress saddr = request.second;
857
858 char buf[4096];
859 ssize_t res;
860 try {
861 uint16_t toRead;
862 readn2(cfd, &toRead, sizeof(toRead));
863 toRead = std::min(ntohs(toRead), static_cast<uint16_t>(sizeof(buf)));
864 res = readn2WithTimeout(cfd, &buf, toRead, 2);
865 g_log<<Logger::Debug<<prefix<<"Had message of "<<std::to_string(toRead)<<" bytes from "<<saddr.toStringWithPort()<<endl;
866 } catch (runtime_error &e) {
867 g_log<<Logger::Warning<<prefix<<"Could not read message from "<<saddr.toStringWithPort()<<": "<<e.what()<<endl;
868 close(cfd);
869 continue;
870 }
871
872 try {
873 MOADNSParser mdp(true, string(buf, res));
874
875 if (!checkQuery(mdp, saddr, false, prefix)) {
876 close(cfd);
877 continue;
878 }
879
880 if (mdp.d_qtype == QType::SOA) {
881 vector<uint8_t> packet;
882 bool ret = makeSOAPacket(mdp, packet);
883 if (!ret) {
884 close(cfd);
885 continue;
886 }
887 sendPacketOverTCP(cfd, packet);
888 }
889 else if (mdp.d_qtype == QType::AXFR) {
890 if (!handleAXFR(cfd, mdp)) {
891 close(cfd);
892 continue;
893 }
894 }
895 else if (mdp.d_qtype == QType::IXFR) {
896 /* RFC 1995 section 3:
897 * The IXFR query packet format is the same as that of a normal DNS
898 * query, but with the query type being IXFR and the authority section
899 * containing the SOA record of client's version of the zone.
900 */
901 shared_ptr<SOARecordContent> clientSOA;
902 for (auto &answer : mdp.d_answers) {
903 // from dnsparser.hh:
904 // typedef vector<pair<DNSRecord, uint16_t > > answers_t;
905 if (answer.first.d_type == QType::SOA && answer.first.d_place == DNSResourceRecord::AUTHORITY) {
906 clientSOA = getRR<SOARecordContent>(answer.first);
907 if (clientSOA != nullptr) {
908 break;
909 }
910 }
911 } /* for (auto const &answer : mdp.d_answers) */
912
913 if (clientSOA == nullptr) {
914 g_log<<Logger::Warning<<prefix<<"IXFR request packet did not contain a SOA record in the AUTHORITY section"<<endl;
915 close(cfd);
916 continue;
917 }
918
919 if (!handleIXFR(cfd, saddr, mdp, clientSOA)) {
920 close(cfd);
921 continue;
922 }
923 } /* if (mdp.d_qtype == QType::IXFR) */
924
925 shutdown(cfd, 2);
926 } catch (const MOADNSException &mde) {
927 g_log<<Logger::Warning<<prefix<<"Could not parse DNS packet from "<<saddr.toStringWithPort()<<": "<<mde.what()<<endl;
928 } catch (runtime_error &e) {
929 g_log<<Logger::Warning<<prefix<<"Could not write reply to "<<saddr.toStringWithPort()<<": "<<e.what()<<endl;
930 }
931 // bye!
932 close(cfd);
933
934 if (g_exiting) {
935 break;
936 }
937 }
938 }
939
940 /* Parses the configuration file in configpath into config, adding defaults for
941 * missing parameters (if applicable), returning true if the config file was
942 * good, false otherwise. Will log all issues with the config
943 */
944 static bool parseAndCheckConfig(const string& configpath, YAML::Node& config) {
945 g_log<<Logger::Info<<"Loading configuration file from "<<configpath<<endl;
946 try {
947 config = YAML::LoadFile(configpath);
948 } catch (const runtime_error &e) {
949 g_log<<Logger::Error<<"Unable to load configuration file '"<<configpath<<"': "<<e.what()<<endl;
950 return false;
951 }
952
953 bool retval = true;
954
955 if (config["keep"]) {
956 try {
957 config["keep"].as<uint16_t>();
958 } catch (const runtime_error &e) {
959 g_log<<Logger::Error<<"Unable to read 'keep' value: "<<e.what()<<endl;
960 retval = false;
961 }
962 } else {
963 config["keep"] = 20;
964 }
965
966 if (config["axfr-timeout"]) {
967 try {
968 config["axfr-timeout"].as<uint16_t>();
969 } catch (const runtime_error &e) {
970 g_log<<Logger::Error<<"Unable to read 'axfr-timeout' value: "<<e.what()<<endl;
971 }
972 } else {
973 config["axfr-timeout"] = 20;
974 }
975
976 if (config["failed-soa-retry"]) {
977 try {
978 config["failed-soa-retry"].as<uint16_t>();
979 } catch (const runtime_error &e) {
980 g_log<<Logger::Error<<"Unable to read 'failed-soa-retry' value: "<<e.what()<<endl;
981 }
982 } else {
983 config["failed-soa-retry"] = 30;
984 }
985
986 if (config["tcp-in-threads"]) {
987 try {
988 config["tcp-in-threads"].as<uint16_t>();
989 } catch (const runtime_error &e) {
990 g_log<<Logger::Error<<"Unable to read 'tcp-in-threads' value: "<<e.what()<<endl;
991 }
992 } else {
993 config["tcp-in-threads"] = 10;
994 }
995
996 if (config["listen"]) {
997 try {
998 config["listen"].as<vector<ComboAddress>>();
999 } catch (const runtime_error &e) {
1000 g_log<<Logger::Error<<"Unable to read 'listen' value: "<<e.what()<<endl;
1001 retval = false;
1002 }
1003 } else {
1004 config["listen"].push_back("127.0.0.1:53");
1005 config["listen"].push_back("[::1]:53");
1006 }
1007
1008 if (config["acl"]) {
1009 try {
1010 config["acl"].as<vector<string>>();
1011 } catch (const runtime_error &e) {
1012 g_log<<Logger::Error<<"Unable to read 'acl' value: "<<e.what()<<endl;
1013 retval = false;
1014 }
1015 } else {
1016 config["acl"].push_back("127.0.0.0/8");
1017 config["acl"].push_back("::1/128");
1018 }
1019
1020 if (config["work-dir"]) {
1021 try {
1022 config["work-dir"].as<string>();
1023 } catch(const runtime_error &e) {
1024 g_log<<Logger::Error<<"Unable to read 'work-dir' value: "<<e.what()<<endl;
1025 retval = false;
1026 }
1027 } else {
1028 char tmp[512];
1029 config["work-dir"] = getcwd(tmp, sizeof(tmp)) ? string(tmp) : "";;
1030 }
1031
1032 if (config["uid"]) {
1033 try {
1034 config["uid"].as<string>();
1035 } catch(const runtime_error &e) {
1036 g_log<<Logger::Error<<"Unable to read 'uid' value: "<<e.what()<<endl;
1037 retval = false;
1038 }
1039 }
1040
1041 if (config["gid"]) {
1042 try {
1043 config["gid"].as<string>();
1044 } catch(const runtime_error &e) {
1045 g_log<<Logger::Error<<"Unable to read 'gid' value: "<<e.what()<<endl;
1046 retval = false;
1047 }
1048 }
1049
1050 if (config["domains"]) {
1051 if (config["domains"].size() == 0) {
1052 g_log<<Logger::Error<<"No domains configured"<<endl;
1053 retval = false;
1054 }
1055 for (auto const &domain : config["domains"]) {
1056 try {
1057 if (!domain["domain"]) {
1058 g_log<<Logger::Error<<"An entry in 'domains' is missing a 'domain' key!"<<endl;
1059 retval = false;
1060 continue;
1061 }
1062 domain["domain"].as<DNSName>();
1063 } catch (const runtime_error &e) {
1064 g_log<<Logger::Error<<"Unable to read domain '"<<domain["domain"].as<string>()<<"': "<<e.what()<<endl;
1065 }
1066 try {
1067 if (!domain["master"]) {
1068 g_log<<Logger::Error<<"Domain '"<<domain["domain"].as<string>()<<"' has no master configured!"<<endl;
1069 retval = false;
1070 continue;
1071 }
1072 domain["master"].as<ComboAddress>();
1073 } catch (const runtime_error &e) {
1074 g_log<<Logger::Error<<"Unable to read domain '"<<domain["domain"].as<string>()<<"' master address: "<<e.what()<<endl;
1075 retval = false;
1076 }
1077 }
1078 } else {
1079 g_log<<Logger::Error<<"No domains configured"<<endl;
1080 retval = false;
1081 }
1082
1083 if (config["compress"]) {
1084 try {
1085 config["compress"].as<bool>();
1086 }
1087 catch (const runtime_error &e) {
1088 g_log<<Logger::Error<<"Unable to read 'compress' value: "<<e.what()<<endl;
1089 retval = false;
1090 }
1091 }
1092 else {
1093 config["compress"] = false;
1094 }
1095
1096 if (config["webserver-address"]) {
1097 try {
1098 config["webserver-address"].as<ComboAddress>();
1099 }
1100 catch (const runtime_error &e) {
1101 g_log<<Logger::Error<<"Unable to read 'webserver-address' value: "<<e.what()<<endl;
1102 retval = false;
1103 }
1104 }
1105
1106 if (config["webserver-acl"]) {
1107 try {
1108 config["webserver-acl"].as<vector<Netmask>>();
1109 }
1110 catch (const runtime_error &e) {
1111 g_log<<Logger::Error<<"Unable to read 'webserver-acl' value: "<<e.what()<<endl;
1112 retval = false;
1113 }
1114 }
1115
1116 return retval;
1117 }
1118
1119 int main(int argc, char** argv) {
1120 g_log.setLoglevel(Logger::Notice);
1121 g_log.toConsole(Logger::Notice);
1122 g_log.setPrefixed(true);
1123 g_log.disableSyslog(true);
1124 g_log.setTimestamps(false);
1125 po::variables_map g_vm;
1126 try {
1127 po::options_description desc("IXFR distribution tool");
1128 desc.add_options()
1129 ("help", "produce help message")
1130 ("version", "Display the version of ixfrdist")
1131 ("verbose", "Be verbose")
1132 ("debug", "Be even more verbose")
1133 ("config", po::value<string>()->default_value(SYSCONFDIR + string("/ixfrdist.yml")), "Configuration file to use")
1134 ;
1135
1136 po::store(po::command_line_parser(argc, argv).options(desc).run(), g_vm);
1137 po::notify(g_vm);
1138
1139 if (g_vm.count("help") > 0) {
1140 usage(desc);
1141 return EXIT_SUCCESS;
1142 }
1143
1144 if (g_vm.count("version") > 0) {
1145 cout<<"ixfrdist "<<VERSION<<endl;
1146 return EXIT_SUCCESS;
1147 }
1148 } catch (po::error &e) {
1149 g_log<<Logger::Error<<e.what()<<". See `ixfrdist --help` for valid options"<<endl;
1150 return(EXIT_FAILURE);
1151 }
1152
1153 bool had_error = false;
1154
1155 if (g_vm.count("verbose")) {
1156 g_log.setLoglevel(Logger::Info);
1157 g_log.toConsole(Logger::Info);
1158 }
1159
1160 if (g_vm.count("debug") > 0) {
1161 g_log.setLoglevel(Logger::Debug);
1162 g_log.toConsole(Logger::Debug);
1163 }
1164
1165 g_log<<Logger::Notice<<"IXFR distributor version "<<VERSION<<" starting up!"<<endl;
1166
1167 YAML::Node config;
1168 if (!parseAndCheckConfig(g_vm["config"].as<string>(), config)) {
1169 // parseAndCheckConfig already logged whatever was wrong
1170 return EXIT_FAILURE;
1171 }
1172
1173 /* From hereon out, we known that all the values in config are valid. */
1174
1175 for (auto const &domain : config["domains"]) {
1176 set<ComboAddress> s;
1177 s.insert(domain["master"].as<ComboAddress>());
1178 g_domainConfigs[domain["domain"].as<DNSName>()].masters = s;
1179 g_stats.registerDomain(domain["domain"].as<DNSName>());
1180 }
1181
1182 for (const auto &addr : config["acl"].as<vector<string>>()) {
1183 try {
1184 g_acl.addMask(addr);
1185 } catch (const NetmaskException &e) {
1186 g_log<<Logger::Error<<e.reason<<endl;
1187 had_error = true;
1188 }
1189 }
1190 g_log<<Logger::Notice<<"ACL set to "<<g_acl.toString()<<"."<<endl;
1191
1192 if (config["compress"]) {
1193 g_compress = config["compress"].as<bool>();
1194 if (g_compress) {
1195 g_log<<Logger::Notice<<"Record compression is enabled."<<endl;
1196 }
1197 }
1198
1199 FDMultiplexer* fdm = FDMultiplexer::getMultiplexerSilent();
1200 if (fdm == nullptr) {
1201 g_log<<Logger::Error<<"Could not enable a multiplexer for the listen sockets!"<<endl;
1202 return EXIT_FAILURE;
1203 }
1204
1205 set<int> allSockets;
1206 for (const auto& addr : config["listen"].as<vector<ComboAddress>>()) {
1207 for (const auto& stype : {SOCK_DGRAM, SOCK_STREAM}) {
1208 try {
1209 int s = SSocket(addr.sin4.sin_family, stype, 0);
1210 setNonBlocking(s);
1211 setReuseAddr(s);
1212 SBind(s, addr);
1213 if (stype == SOCK_STREAM) {
1214 SListen(s, 30); // TODO make this configurable
1215 }
1216 fdm->addReadFD(s, stype == SOCK_DGRAM ? handleUDPRequest : handleTCPRequest);
1217 allSockets.insert(s);
1218 } catch(runtime_error &e) {
1219 g_log<<Logger::Error<<e.what()<<endl;
1220 had_error = true;
1221 continue;
1222 }
1223 }
1224 }
1225
1226 int newgid = 0;
1227
1228 if (config["gid"]) {
1229 string gid = config["gid"].as<string>();
1230 if (!(newgid = atoi(gid.c_str()))) {
1231 struct group *gr = getgrnam(gid.c_str());
1232 if (gr == nullptr) {
1233 g_log<<Logger::Error<<"Can not determine group-id for gid "<<gid<<endl;
1234 had_error = true;
1235 } else {
1236 newgid = gr->gr_gid;
1237 }
1238 }
1239 g_log<<Logger::Notice<<"Dropping effective group-id to "<<newgid<<endl;
1240 if (setgid(newgid) < 0) {
1241 g_log<<Logger::Error<<"Could not set group id to "<<newgid<<": "<<stringerror()<<endl;
1242 had_error = true;
1243 }
1244 }
1245
1246 if (config["webserver-address"]) {
1247 NetmaskGroup wsACL;
1248 wsACL.addMask("127.0.0.0/8");
1249 wsACL.addMask("::1/128");
1250
1251 if (config["webserver-acl"]) {
1252 wsACL.clear();
1253 for (const auto &acl : config["webserver-acl"].as<vector<Netmask>>()) {
1254 wsACL.addMask(acl);
1255 }
1256 }
1257
1258 // Launch the webserver!
1259 std::thread(&IXFRDistWebServer::go, IXFRDistWebServer(config["webserver-address"].as<ComboAddress>(), wsACL)).detach();
1260 }
1261
1262 int newuid = 0;
1263
1264 if (config["uid"]) {
1265 string uid = config["uid"].as<string>();
1266 if (!(newuid = atoi(uid.c_str()))) {
1267 struct passwd *pw = getpwnam(uid.c_str());
1268 if (pw == nullptr) {
1269 g_log<<Logger::Error<<"Can not determine user-id for uid "<<uid<<endl;
1270 had_error = true;
1271 } else {
1272 newuid = pw->pw_uid;
1273 }
1274 }
1275
1276 struct passwd *pw = getpwuid(newuid);
1277 if (pw == nullptr) {
1278 if (setgroups(0, nullptr) < 0) {
1279 g_log<<Logger::Error<<"Unable to drop supplementary gids: "<<stringerror()<<endl;
1280 had_error = true;
1281 }
1282 } else {
1283 if (initgroups(pw->pw_name, newgid) < 0) {
1284 g_log<<Logger::Error<<"Unable to set supplementary groups: "<<stringerror()<<endl;
1285 had_error = true;
1286 }
1287 }
1288
1289 g_log<<Logger::Notice<<"Dropping effective user-id to "<<newuid<<endl;
1290 if (setuid(newuid) < 0) {
1291 g_log<<Logger::Error<<"Could not set user id to "<<newuid<<": "<<stringerror()<<endl;
1292 had_error = true;
1293 }
1294 }
1295
1296 if (had_error) {
1297 // We have already sent the errors to stderr, just die
1298 return EXIT_FAILURE;
1299 }
1300
1301 // It all starts here
1302 signal(SIGTERM, handleSignal);
1303 signal(SIGINT, handleSignal);
1304 signal(SIGPIPE, SIG_IGN);
1305
1306 // Init the things we need
1307 reportAllTypes();
1308
1309 dns_random_init();
1310
1311 std::thread ut(updateThread,
1312 config["work-dir"].as<string>(),
1313 config["keep"].as<uint16_t>(),
1314 config["axfr-timeout"].as<uint16_t>(),
1315 config["failed-soa-retry"].as<uint16_t>());
1316
1317 vector<std::thread> tcpHandlers;
1318 tcpHandlers.reserve(config["tcp-in-threads"].as<uint16_t>());
1319 for (size_t i = 0; i < tcpHandlers.capacity(); ++i) {
1320 tcpHandlers.push_back(std::thread(tcpWorker, i));
1321 }
1322
1323 struct timeval now;
1324 for(;;) {
1325 gettimeofday(&now, 0);
1326 fdm->run(&now);
1327 if (g_exiting) {
1328 g_log<<Logger::Debug<<"Closing listening sockets"<<endl;
1329 for (const int& fd : allSockets) {
1330 try {
1331 closesocket(fd);
1332 } catch(PDNSException &e) {
1333 g_log<<Logger::Error<<e.reason<<endl;
1334 }
1335 }
1336 break;
1337 }
1338 }
1339 g_log<<Logger::Debug<<"Waiting for all threads to stop"<<endl;
1340 g_tcpHandlerCV.notify_all();
1341 ut.join();
1342 for (auto &t : tcpHandlers) {
1343 t.join();
1344 }
1345 g_log<<Logger::Notice<<"IXFR distributor stopped"<<endl;
1346 return EXIT_SUCCESS;
1347 }