You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

260 lines
9.8 KiB

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