]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ixfrutils.cc
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[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 <errno.h>
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 getSerialsFromDir(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 + "': " + strerror(errno));
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 DNSName root(".");
96 uint16_t t=QType::SOA;
97
98 auto found = records.equal_range(tie(root, t));
99
100 for(auto iter = found.first; iter != found.second; ++iter) {
101 auto soa = std::dynamic_pointer_cast<SOARecordContent>(iter->d_content);
102 if (soa) {
103 soaret = *iter;
104 return soa->d_st.serial;
105 }
106 }
107 return 0;
108 }
109
110 static void writeRecords(FILE* fp, const records_t& records)
111 {
112 for(const auto& r: records) {
113 fprintf(fp, "%s\t%" PRIu32 "\tIN\t%s\t%s\n",
114 r.d_name.isRoot() ? "@" : r.d_name.toStringNoDot().c_str(),
115 r.d_ttl,
116 DNSRecordContent::NumberToType(r.d_type).c_str(),
117 r.d_content->getZoneRepresentation().c_str());
118 }
119 }
120
121 void writeZoneToDisk(const records_t& records, const DNSName& zone, const std::string& directory)
122 {
123 DNSRecord soa;
124 auto serial = getSerialFromRecords(records, soa);
125 string fname=directory +"/"+std::to_string(serial);
126 FILE* fp=fopen((fname+".partial").c_str(), "w");
127 if(!fp)
128 throw runtime_error("Unable to open file '"+fname+".partial' for writing: "+string(strerror(errno)));
129
130 records_t soarecord;
131 soarecord.insert(soa);
132 fprintf(fp, "$ORIGIN %s\n", zone.toString().c_str());
133
134 writeRecords(fp, soarecord);
135 writeRecords(fp, records);
136 writeRecords(fp, soarecord);
137
138 fclose(fp);
139 if (rename( (fname+".partial").c_str(), fname.c_str()) != 0) {
140 throw std::runtime_error("Unable to move the zone file for " + zone.toLogString() + " from " + fname + ".partial to " + fname + ": " + string(strerror(errno)));
141 }
142 }
143
144 void loadZoneFromDisk(records_t& records, const string& fname, const DNSName& zone)
145 {
146 ZoneParserTNG zpt(fname, zone);
147
148 DNSResourceRecord rr;
149 bool seenSOA=false;
150 while(zpt.get(rr)) {
151 if(rr.qtype.getCode() == QType::CNAME && rr.content.empty())
152 rr.content=".";
153 rr.qname = rr.qname.makeRelative(zone);
154
155 if(rr.qtype.getCode() != QType::SOA || seenSOA==false)
156 records.insert(DNSRecord(rr));
157 if(rr.qtype.getCode() == QType::SOA) {
158 seenSOA=true;
159 }
160 }
161 if(!(rr.qtype.getCode() == QType::SOA && seenSOA)) {
162 records.clear();
163 throw runtime_error("Zone not complete!");
164 }
165 }
166
167 /*
168 * Load the zone `zone` from `fname` and put the first found SOA into `soa`
169 * Does NOT check for nullptr
170 */
171 void loadSOAFromDisk(const DNSName& zone, const string& fname, shared_ptr<SOARecordContent>& soa, uint32_t& soaTTL)
172 {
173 ZoneParserTNG zpt(fname, zone);
174 DNSResourceRecord rr;
175
176 while(zpt.get(rr)) {
177 if (rr.qtype == QType::SOA) {
178 soa = getRR<SOARecordContent>(DNSRecord(rr));
179 soaTTL = rr.ttl;
180 return;
181 }
182 }
183 }