]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ixfr.cc
Basic speedtest for NetmaksGroup
[thirdparty/pdns.git] / pdns / ixfr.cc
CommitLineData
12471842
PL
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 */
39ec5d29 22#include "ixfr.hh"
23#include "sstuff.hh"
24#include "dns_random.hh"
25#include "dnsrecords.hh"
189f9579 26#include "dnssecinfra.hh"
60a1c204 27#include "tsigverifier.hh"
7eafc52f 28
d67ae3b4 29vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const ComboAddress& master, const DNSName& zone,
7afd3f74 30 const vector<DNSRecord>& records, const std::shared_ptr<SOARecordContent>& masterSOA)
d67ae3b4
RG
31{
32 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > ret;
33
34 if (records.size() == 0 || masterSOA == nullptr) {
35 return ret;
36 }
37
38 // we start at 1 to skip the first SOA record
39 // we don't increase pos because the final SOA
40 // of the previous sequence is also the first SOA
41 // of this one
42 for(unsigned int pos = 1; pos < records.size(); ) {
43 vector<DNSRecord> remove, add;
44
45 // cerr<<"Looking at record in position "<<pos<<" of type "<<QType(records[pos].d_type).getName()<<endl;
46
47 if (records[pos].d_type != QType::SOA) {
48 // this is an actual AXFR!
49 return {{remove, records}};
50 }
51
52 auto sr = getRR<SOARecordContent>(records[pos]);
53 if (!sr) {
86f1af1c 54 throw std::runtime_error("Error getting the content of the first SOA record of this IXFR sequence for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
d67ae3b4
RG
55 }
56
57 // cerr<<"Serial is "<<sr->d_st.serial<<", final serial is "<<masterSOA->d_st.serial<<endl;
58
59 // the serial of this SOA record is the serial of the
60 // zone before the removals and updates of this sequence
61 if (sr->d_st.serial == masterSOA->d_st.serial) {
98b33176
RG
62 if (records.size() == 2) {
63 // if the entire update is two SOAs records with the same
64 // serial, this is actually an empty AXFR!
65 return {{remove, records}};
66 }
67
d67ae3b4
RG
68 // if it's the final SOA, there is nothing for us to see
69 break;
70 }
71
72 remove.push_back(records[pos]); // this adds the SOA
73
74 // process removals
75 for(pos++; pos < records.size() && records[pos].d_type != QType::SOA; ++pos) {
76 remove.push_back(records[pos]);
77 }
78
79 if (pos >= records.size()) {
86f1af1c 80 throw std::runtime_error("No SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
81 }
82
83 sr = getRR<SOARecordContent>(records[pos]);
84 if (!sr) {
86f1af1c 85 throw std::runtime_error("Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
86 }
87
88 // this is the serial of the zone after the removals
89 // and updates, but that might not be the final serial
90 // because there might be several sequences
91 uint32_t newSerial = sr->d_st.serial;
92 add.push_back(records[pos]); // this adds the new SOA
93
94 // process additions
95 for(pos++; pos < records.size() && records[pos].d_type != QType::SOA; ++pos) {
96 add.push_back(records[pos]);
97 }
98
99 if (pos >= records.size()) {
86f1af1c 100 throw std::runtime_error("No SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
101 }
102
103 sr = getRR<SOARecordContent>(records[pos]);
104 if (!sr) {
86f1af1c 105 throw std::runtime_error("Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
106 }
107
108 if (sr->d_st.serial != newSerial) {
86f1af1c 109 throw std::runtime_error("Invalid serial (" + std::to_string(sr->d_st.serial) + ", expecting " + std::to_string(newSerial) + ") in the SOA record finishing the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
110 }
111
112 if (newSerial == masterSOA->d_st.serial) {
113 // this was the last sequence
114 if (pos != (records.size() - 1)) {
86f1af1c 115 throw std::runtime_error("Trailing records after the last IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
d67ae3b4
RG
116 }
117 }
118
119 ret.push_back(make_pair(remove,add));
120 }
121
122 return ret;
123}
124
3e7dcee6 125// Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR!
126vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAddress& master, const DNSName& zone, const DNSRecord& oursr,
db8f9152 127 const TSIGTriplet& tt, const ComboAddress* laddr, size_t maxReceivedBytes)
39ec5d29 128{
129 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > ret;
130 vector<uint8_t> packet;
131 DNSPacketWriter pw(packet, zone, QType::IXFR);
132 pw.getHeader()->qr=0;
133 pw.getHeader()->rd=0;
134 pw.getHeader()->id=dns_random(0xffff);
189f9579 135 pw.startRecord(zone, QType::SOA, 0, QClass::IN, DNSResourceRecord::AUTHORITY);
39ec5d29 136 oursr.d_content->toPacket(pw);
189f9579 137
39ec5d29 138 pw.commit();
60a1c204
RG
139 TSIGRecordContent trc;
140 TSIGTCPVerifier tsigVerifier(tt, master, trc);
98c9ec39 141 if(!tt.algo.empty()) {
bd051ad6
PL
142 TSIGHashEnum the;
143 getTSIGHashEnum(tt.algo, the);
bd051ad6
PL
144 try {
145 trc.d_algoName = getTSIGAlgoName(the);
146 } catch(PDNSException& pe) {
86f1af1c 147 throw std::runtime_error("TSIG algorithm '"+tt.algo.toLogString()+"' is unknown.");
bd051ad6 148 }
189f9579 149 trc.d_time = time((time_t*)NULL);
150 trc.d_fudge = 300;
151 trc.d_origID=ntohs(pw.getHeader()->id);
152 trc.d_eRcode=0;
ea3816cf 153 addTSIG(pw, trc, tt.name, tt.secret, "", false);
189f9579 154 }
39ec5d29 155 uint16_t len=htons(packet.size());
156 string msg((const char*)&len, 2);
157 msg.append((const char*)&packet[0], packet.size());
158
159 Socket s(master.sin4.sin_family, SOCK_STREAM);
160 // cout<<"going to connect"<<endl;
3e7dcee6 161 if(laddr)
162 s.bind(*laddr);
39ec5d29 163 s.connect(master);
164 // cout<<"Connected"<<endl;
165 s.writen(msg);
166
167 // CURRENT MASTER SOA
168 // REPEAT:
169 // SOA WHERE THIS DELTA STARTS
170 // RECORDS TO REMOVE
171 // SOA WHERE THIS DELTA GOES
172 // RECORDS TO ADD
173 // CURRENT MASTER SOA
d67ae3b4 174 std::shared_ptr<SOARecordContent> masterSOA = nullptr;
39ec5d29 175 vector<DNSRecord> records;
db8f9152 176 size_t receivedBytes = 0;
397ed71d 177 int8_t ixfrInProgress = -2;
32fc3b03 178 std::string reply;
60a1c204 179
39ec5d29 180 for(;;) {
4f5cde49
LX
181 // IXFR end
182 if (ixfrInProgress >= 0)
397ed71d
LX
183 break;
184
d67ae3b4 185 if(s.read((char*)&len, sizeof(len)) != sizeof(len))
39ec5d29 186 break;
d67ae3b4 187
39ec5d29 188 len=ntohs(len);
189 // cout<<"Got chunk of "<<len<<" bytes"<<endl;
190 if(!len)
191 break;
db8f9152
RG
192
193 if (maxReceivedBytes > 0 && (maxReceivedBytes - receivedBytes) < (size_t) len)
86f1af1c 194 throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toLogString()+"' from master "+master.toStringWithPort());
db8f9152 195
32fc3b03
RG
196 reply.resize(len);
197 readn2(s.getHandle(), &reply.at(0), len);
db8f9152 198 receivedBytes += len;
d67ae3b4 199
32fc3b03 200 MOADNSParser mdp(false, reply);
189f9579 201 if(mdp.d_header.rcode)
86f1af1c 202 throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
189f9579 203
39ec5d29 204 // cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
60a1c204
RG
205
206 if(!tt.algo.empty()) { // TSIG verify message
32fc3b03 207 tsigVerifier.check(reply, mdp);
60a1c204
RG
208 }
209
39ec5d29 210 for(auto& r: mdp.d_answers) {
211 // cout<<r.first.d_name<< " " <<r.first.d_content->getZoneRepresentation()<<endl;
d67ae3b4
RG
212 if(!masterSOA) {
213 // we have not seen the first SOA record yet
214 if (r.first.d_type != QType::SOA) {
86f1af1c 215 throw std::runtime_error("The first record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"' is not a SOA ("+QType(r.first.d_type).getName()+")");
d67ae3b4
RG
216 }
217
397ed71d
LX
218 auto sr = getRR<SOARecordContent>(r.first);
219 if (!sr) {
86f1af1c 220 throw std::runtime_error("Error getting the content of the first SOA record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
d67ae3b4
RG
221 }
222
223 if(sr->d_st.serial == std::dynamic_pointer_cast<SOARecordContent>(oursr.d_content)->d_st.serial) {
224 // we are up to date
225 return ret;
226 }
227 masterSOA = sr;
4f5cde49
LX
228 } else if (r.first.d_type == QType::SOA) {
229 auto sr = getRR<SOARecordContent>(r.first);
230 if (!sr) {
231 throw std::runtime_error("Error getting the content of SOA record of IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
232 }
233
397ed71d 234 // we hit the last SOA record
4f5cde49
LX
235 // IXFR is considered to be done if we hit the last SOA record twice
236 if (masterSOA->d_st.serial == sr->d_st.serial) {
397ed71d
LX
237 ixfrInProgress++;
238 }
39ec5d29 239 }
7eafc52f 240
d67ae3b4
RG
241 if(r.first.d_place != DNSResourceRecord::ANSWER) {
242 if(r.first.d_type == QType::TSIG)
243 continue;
39ec5d29 244
d67ae3b4
RG
245 if(r.first.d_type == QType::OPT)
246 continue;
247
86f1af1c 248 throw std::runtime_error("Unexpected record (" +QType(r.first.d_type).getName()+") in non-answer section ("+std::to_string(r.first.d_place)+")in IXFR response for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort());
d67ae3b4
RG
249 }
250
251 r.first.d_name.makeUsRelative(zone);
252 records.push_back(r.first);
39ec5d29 253 }
39ec5d29 254 }
d67ae3b4
RG
255
256 // cout<<"Got "<<records.size()<<" records"<<endl;
257
258 return processIXFRRecords(master, zone, records, masterSOA);
39ec5d29 259}