]>
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.
28 #include "ixfrutils.hh"
30 #include "dnssecinfra.hh"
31 #include "zoneparser-tng.hh"
32 #include "dnsparser.hh"
34 uint32_t getSerialFromPrimary(const ComboAddress
& primary
, const DNSName
& zone
, shared_ptr
<const SOARecordContent
>& sr
, const TSIGTriplet
& tt
, const uint16_t timeout
)
36 vector
<uint8_t> packet
;
37 DNSPacketWriter
pw(packet
, zone
, QType::SOA
);
38 if(!tt
.algo
.empty()) {
39 TSIGRecordContent trc
;
40 trc
.d_algoName
= tt
.algo
;
41 trc
.d_time
= time(nullptr);
43 trc
.d_origID
=ntohs(pw
.getHeader()->id
);
45 addTSIG(pw
, trc
, tt
.name
, tt
.secret
, "", false);
48 Socket
s(primary
.sin4
.sin_family
, SOCK_DGRAM
);
50 string
msg((const char*)&packet
[0], packet
.size());
55 // will throw a NetworkError on timeout
56 size_t got
= s
.readWithTimeout(reply
.data(), reply
.size(), timeout
);
57 if (got
< sizeof(dnsheader
)) {
58 throw std::runtime_error("Invalid response size " + std::to_string(got
));
63 MOADNSParser
mdp(false, reply
);
64 if(mdp
.d_header
.rcode
) {
65 throw std::runtime_error("RCODE from response is not NoError but " + RCode::to_s(mdp
.d_header
.rcode
));
67 for(const auto& r
: mdp
.d_answers
) {
68 if(r
.d_type
== QType::SOA
) {
69 sr
= getRR
<SOARecordContent
>(r
);
71 return sr
->d_st
.serial
;
78 uint32_t getSerialFromDir(const std::string
& dir
)
81 auto directoryError
= pdns::visit_directory(dir
, [&ret
]([[maybe_unused
]] ino_t inodeNumber
, const std::string_view
& name
) {
83 auto version
= pdns::checked_stoi
<uint32_t>(std::string(name
));
84 if (std::to_string(version
) == name
) {
85 ret
= std::max(version
, ret
);
94 throw runtime_error("Could not open IXFR directory '" + dir
+ "': " + *directoryError
);
100 uint32_t getSerialFromRecords(const records_t
& records
, DNSRecord
& soaret
)
102 uint16_t t
=QType::SOA
;
104 auto found
= records
.equal_range(std::tie(g_rootdnsname
, t
));
106 for(auto iter
= found
.first
; iter
!= found
.second
; ++iter
) {
107 auto soa
= getRR
<SOARecordContent
>(*iter
);
110 return soa
->d_st
.serial
;
116 static void writeRecords(FILE* fp
, const records_t
& records
)
118 for(const auto& r
: records
) {
119 if(fprintf(fp
, "%s\t%" PRIu32
"\tIN\t%s\t%s\n",
120 r
.d_name
.isRoot() ? "@" : r
.d_name
.toStringNoDot().c_str(),
122 DNSRecordContent::NumberToType(r
.d_type
).c_str(),
123 r
.getContent()->getZoneRepresentation().c_str()) < 0) {
124 throw runtime_error(stringerror());
129 void writeZoneToDisk(const records_t
& records
, const DNSName
& zone
, const std::string
& directory
)
132 auto serial
= getSerialFromRecords(records
, soa
);
133 string fname
= directory
+ "/" + std::to_string(serial
);
134 /* ensure that the partial zone file will only be accessible by the current user, not even
135 by other users in the same group, and certainly not by other users. */
136 umask(S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
137 auto filePtr
= pdns::UniqueFilePtr(fopen((fname
+".partial").c_str(), "w"));
139 throw runtime_error("Unable to open file '"+fname
+".partial' for writing: "+stringerror());
143 soarecord
.insert(soa
);
144 if (fprintf(filePtr
.get(), "$ORIGIN %s\n", zone
.toString().c_str()) < 0) {
145 string error
= "Error writing to zone file for " + zone
.toLogString() + " in file " + fname
+ ".partial" + ": " + stringerror();
147 unlink((fname
+".partial").c_str());
148 throw std::runtime_error(error
);
152 writeRecords(filePtr
.get(), soarecord
);
153 writeRecords(filePtr
.get(), records
);
154 writeRecords(filePtr
.get(), soarecord
);
155 } catch (runtime_error
&e
) {
157 unlink((fname
+".partial").c_str());
158 throw runtime_error("Error closing zone file for " + zone
.toLogString() + " in file " + fname
+ ".partial" + ": " + e
.what());
161 if (fclose(filePtr
.release()) != 0) {
162 string error
= "Error closing zone file for " + zone
.toLogString() + " in file " + fname
+ ".partial" + ": " + stringerror();
163 unlink((fname
+".partial").c_str());
164 throw std::runtime_error(error
);
167 if (rename( (fname
+".partial").c_str(), fname
.c_str()) != 0) {
168 throw std::runtime_error("Unable to move the zone file for " + zone
.toLogString() + " from " + fname
+ ".partial to " + fname
+ ": " + stringerror());
172 void loadZoneFromDisk(records_t
& records
, const string
& fname
, const DNSName
& zone
)
174 ZoneParserTNG
zpt(fname
, zone
);
176 zpt
.disableGenerate();
177 DNSResourceRecord rr
;
180 if(rr
.qtype
.getCode() == QType::CNAME
&& rr
.content
.empty())
182 rr
.qname
= rr
.qname
.makeRelative(zone
);
184 if(rr
.qtype
.getCode() != QType::SOA
|| seenSOA
==false)
185 records
.insert(DNSRecord(rr
));
186 if(rr
.qtype
.getCode() == QType::SOA
) {
190 if(!(rr
.qtype
.getCode() == QType::SOA
&& seenSOA
)) {
192 throw runtime_error("Zone not complete!");
197 * Load the zone `zone` from `fname` and put the first found SOA into `soa`
198 * Does NOT check for nullptr
200 void loadSOAFromDisk(const DNSName
& zone
, const string
& fname
, shared_ptr
<const SOARecordContent
>& soa
, uint32_t& soaTTL
)
202 ZoneParserTNG
zpt(fname
, zone
);
203 zpt
.disableGenerate();
204 DNSResourceRecord rr
;
207 if (rr
.qtype
== QType::SOA
) {
208 soa
= getRR
<SOARecordContent
>(DNSRecord(rr
));