]>
Commit | Line | Data |
---|---|---|
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 | 29 | vector<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! |
126 | vector<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 | } |