]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ixfrutils.cc
clang-tidy: modernize deprecated header invarious places
[thirdparty/pdns.git] / pdns / ixfrutils.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
23 #include <cinttypes>
24 #include <dirent.h>
25 #include <cerrno>
26 #include "ixfrutils.hh"
27 #include "sstuff.hh"
28 #include "dnssecinfra.hh"
29 #include "zoneparser-tng.hh"
30 #include "dnsparser.hh"
31
32 uint32_t getSerialFromMaster(const ComboAddress& master, const DNSName& zone, shared_ptr<SOARecordContent>& sr, const TSIGTriplet& tt, const uint16_t timeout)
33 {
34 vector<uint8_t> packet;
35 DNSPacketWriter pw(packet, zone, QType::SOA);
36 if(!tt.algo.empty()) {
37 TSIGRecordContent trc;
38 trc.d_algoName = tt.algo;
39 trc.d_time = time(nullptr);
40 trc.d_fudge = 300;
41 trc.d_origID=ntohs(pw.getHeader()->id);
42 trc.d_eRcode=0;
43 addTSIG(pw, trc, tt.name, tt.secret, "", false);
44 }
45
46 Socket s(master.sin4.sin_family, SOCK_DGRAM);
47 s.connect(master);
48 string msg((const char*)&packet[0], packet.size());
49 s.writen(msg);
50
51 string reply;
52 reply.resize(4096);
53 // will throw a NetworkError on timeout
54 ssize_t got = s.readWithTimeout(&reply[0], reply.size(), timeout);
55 if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader)) {
56 throw std::runtime_error("Invalid response size " + std::to_string(got));
57 }
58
59 reply.resize(got);
60
61 MOADNSParser mdp(false, reply);
62 if(mdp.d_header.rcode) {
63 throw std::runtime_error("RCODE from response is not NoError but " + RCode::to_s(mdp.d_header.rcode));
64 }
65 for(const auto& r: mdp.d_answers) {
66 if(r.first.d_type == QType::SOA) {
67 sr = getRR<SOARecordContent>(r.first);
68 if(sr != nullptr) {
69 return sr->d_st.serial;
70 }
71 }
72 }
73 return 0;
74 }
75
76 uint32_t getSerialFromDir(const std::string& dir)
77 {
78 uint32_t ret=0;
79 DIR* dirhdl=opendir(dir.c_str());
80 if(!dirhdl)
81 throw runtime_error("Could not open IXFR directory '" + dir + "': " + stringerror());
82 struct dirent *entry;
83
84 while((entry = readdir(dirhdl))) {
85 uint32_t num = atoi(entry->d_name);
86 if(std::to_string(num) == entry->d_name)
87 ret = max(num, ret);
88 }
89 closedir(dirhdl);
90 return ret;
91 }
92
93 uint32_t getSerialFromRecords(const records_t& records, DNSRecord& soaret)
94 {
95 uint16_t t=QType::SOA;
96
97 auto found = records.equal_range(std::tie(g_rootdnsname, t));
98
99 for(auto iter = found.first; iter != found.second; ++iter) {
100 auto soa = std::dynamic_pointer_cast<SOARecordContent>(iter->d_content);
101 if (soa) {
102 soaret = *iter;
103 return soa->d_st.serial;
104 }
105 }
106 return 0;
107 }
108
109 static void writeRecords(FILE* fp, const records_t& records)
110 {
111 for(const auto& r: records) {
112 if(fprintf(fp, "%s\t%" PRIu32 "\tIN\t%s\t%s\n",
113 r.d_name.isRoot() ? "@" : r.d_name.toStringNoDot().c_str(),
114 r.d_ttl,
115 DNSRecordContent::NumberToType(r.d_type).c_str(),
116 r.d_content->getZoneRepresentation().c_str()) < 0) {
117 throw runtime_error(stringerror());
118 }
119 }
120 }
121
122 void writeZoneToDisk(const records_t& records, const DNSName& zone, const std::string& directory)
123 {
124 DNSRecord soa;
125 auto serial = getSerialFromRecords(records, soa);
126 string fname=directory +"/"+std::to_string(serial);
127 auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen((fname+".partial").c_str(), "w"), fclose);
128 if (!fp) {
129 throw runtime_error("Unable to open file '"+fname+".partial' for writing: "+stringerror());
130 }
131
132 records_t soarecord;
133 soarecord.insert(soa);
134 if (fprintf(fp.get(), "$ORIGIN %s\n", zone.toString().c_str()) < 0) {
135 string error = "Error writing to zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
136 fp.reset();
137 unlink((fname+".partial").c_str());
138 throw std::runtime_error(error);
139 }
140
141 try {
142 writeRecords(fp.get(), soarecord);
143 writeRecords(fp.get(), records);
144 writeRecords(fp.get(), soarecord);
145 } catch (runtime_error &e) {
146 fp.reset();
147 unlink((fname+".partial").c_str());
148 throw runtime_error("Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + e.what());
149 }
150
151 if (fclose(fp.release()) != 0) {
152 string error = "Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
153 unlink((fname+".partial").c_str());
154 throw std::runtime_error(error);
155 }
156
157 if (rename( (fname+".partial").c_str(), fname.c_str()) != 0) {
158 throw std::runtime_error("Unable to move the zone file for " + zone.toLogString() + " from " + fname + ".partial to " + fname + ": " + stringerror());
159 }
160 }
161
162 void loadZoneFromDisk(records_t& records, const string& fname, const DNSName& zone)
163 {
164 ZoneParserTNG zpt(fname, zone);
165
166 zpt.disableGenerate();
167 DNSResourceRecord rr;
168 bool seenSOA=false;
169 while(zpt.get(rr)) {
170 if(rr.qtype.getCode() == QType::CNAME && rr.content.empty())
171 rr.content=".";
172 rr.qname = rr.qname.makeRelative(zone);
173
174 if(rr.qtype.getCode() != QType::SOA || seenSOA==false)
175 records.insert(DNSRecord(rr));
176 if(rr.qtype.getCode() == QType::SOA) {
177 seenSOA=true;
178 }
179 }
180 if(!(rr.qtype.getCode() == QType::SOA && seenSOA)) {
181 records.clear();
182 throw runtime_error("Zone not complete!");
183 }
184 }
185
186 /*
187 * Load the zone `zone` from `fname` and put the first found SOA into `soa`
188 * Does NOT check for nullptr
189 */
190 void loadSOAFromDisk(const DNSName& zone, const string& fname, shared_ptr<SOARecordContent>& soa, uint32_t& soaTTL)
191 {
192 ZoneParserTNG zpt(fname, zone);
193 zpt.disableGenerate();
194 DNSResourceRecord rr;
195
196 while(zpt.get(rr)) {
197 if (rr.qtype == QType::SOA) {
198 soa = getRR<SOARecordContent>(DNSRecord(rr));
199 soaTTL = rr.ttl;
200 return;
201 }
202 }
203 }