]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ixfrutils.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
26 #include "ixfrutils.hh"
28 #include "dnssecinfra.hh"
29 #include "zoneparser-tng.hh"
30 #include "dnsparser.hh"
32 uint32_t getSerialFromMaster(const ComboAddress
& master
, const DNSName
& zone
, shared_ptr
<SOARecordContent
>& sr
, const TSIGTriplet
& tt
, const uint16_t timeout
)
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);
41 trc
.d_origID
=ntohs(pw
.getHeader()->id
);
43 addTSIG(pw
, trc
, tt
.name
, tt
.secret
, "", false);
46 Socket
s(master
.sin4
.sin_family
, SOCK_DGRAM
);
48 string
msg((const char*)&packet
[0], packet
.size());
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
));
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
));
65 for(const auto& r
: mdp
.d_answers
) {
66 if(r
.first
.d_type
== QType::SOA
) {
67 sr
= getRR
<SOARecordContent
>(r
.first
);
69 return sr
->d_st
.serial
;
76 uint32_t getSerialFromDir(const std::string
& dir
)
79 DIR* dirhdl
=opendir(dir
.c_str());
81 throw runtime_error("Could not open IXFR directory '" + dir
+ "': " + stringerror());
84 while((entry
= readdir(dirhdl
))) {
85 uint32_t num
= atoi(entry
->d_name
);
86 if(std::to_string(num
) == entry
->d_name
)
93 uint32_t getSerialFromRecords(const records_t
& records
, DNSRecord
& soaret
)
95 uint16_t t
=QType::SOA
;
97 auto found
= records
.equal_range(std::tie(g_rootdnsname
, t
));
99 for(auto iter
= found
.first
; iter
!= found
.second
; ++iter
) {
100 auto soa
= std::dynamic_pointer_cast
<SOARecordContent
>(iter
->d_content
);
103 return soa
->d_st
.serial
;
109 static void writeRecords(FILE* fp
, const records_t
& records
)
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(),
115 DNSRecordContent::NumberToType(r
.d_type
).c_str(),
116 r
.d_content
->getZoneRepresentation().c_str()) < 0) {
117 throw runtime_error(stringerror());
122 void writeZoneToDisk(const records_t
& records
, const DNSName
& zone
, const std::string
& directory
)
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
);
129 throw runtime_error("Unable to open file '"+fname
+".partial' for writing: "+stringerror());
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();
137 unlink((fname
+".partial").c_str());
138 throw std::runtime_error(error
);
142 writeRecords(fp
.get(), soarecord
);
143 writeRecords(fp
.get(), records
);
144 writeRecords(fp
.get(), soarecord
);
145 } catch (runtime_error
&e
) {
147 unlink((fname
+".partial").c_str());
148 throw runtime_error("Error closing zone file for " + zone
.toLogString() + " in file " + fname
+ ".partial" + ": " + e
.what());
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
);
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());
162 void loadZoneFromDisk(records_t
& records
, const string
& fname
, const DNSName
& zone
)
164 ZoneParserTNG
zpt(fname
, zone
);
166 zpt
.disableGenerate();
167 DNSResourceRecord rr
;
170 if(rr
.qtype
.getCode() == QType::CNAME
&& rr
.content
.empty())
172 rr
.qname
= rr
.qname
.makeRelative(zone
);
174 if(rr
.qtype
.getCode() != QType::SOA
|| seenSOA
==false)
175 records
.insert(DNSRecord(rr
));
176 if(rr
.qtype
.getCode() == QType::SOA
) {
180 if(!(rr
.qtype
.getCode() == QType::SOA
&& seenSOA
)) {
182 throw runtime_error("Zone not complete!");
187 * Load the zone `zone` from `fname` and put the first found SOA into `soa`
188 * Does NOT check for nullptr
190 void loadSOAFromDisk(const DNSName
& zone
, const string
& fname
, shared_ptr
<SOARecordContent
>& soa
, uint32_t& soaTTL
)
192 ZoneParserTNG
zpt(fname
, zone
);
193 zpt
.disableGenerate();
194 DNSResourceRecord rr
;
197 if (rr
.qtype
== QType::SOA
) {
198 soa
= getRR
<SOARecordContent
>(DNSRecord(rr
));