]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ixfr.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.
24 #include "dns_random.hh"
25 #include "dnsrecords.hh"
26 #include "dnssecinfra.hh"
27 #include "tsigverifier.hh"
29 vector
< pair
< vector
< DNSRecord
>, vector
< DNSRecord
> > > processIXFRRecords ( const ComboAddress
& master
, const DNSName
& zone
,
30 const vector
< DNSRecord
>& records
, const std :: shared_ptr
< SOARecordContent
>& masterSOA
)
32 vector
< pair
< vector
< DNSRecord
>, vector
< DNSRecord
> > > ret
;
34 if ( records
. size () == 0 || masterSOA
== nullptr ) {
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
42 for ( unsigned int pos
= 1 ; pos
< records
. size (); ) {
43 vector
< DNSRecord
> remove
, add
;
45 // cerr<<"Looking at record in position "<<pos<<" of type "<<QType(records[pos].d_type).getName()<<endl;
47 if ( records
[ pos
]. d_type
!= QType :: SOA
) {
48 // this is an actual AXFR!
49 return {{ remove
, records
}};
52 auto sr
= getRR
< SOARecordContent
>( records
[ pos
]);
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 ()+ "'" );
57 // cerr<<"Serial is "<<sr->d_st.serial<<", final serial is "<<masterSOA->d_st.serial<<endl;
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
) {
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
}};
68 // if it's the final SOA, there is nothing for us to see
72 remove
. push_back ( records
[ pos
]); // this adds the SOA
75 for ( pos
++; pos
< records
. size () && records
[ pos
]. d_type
!= QType :: SOA
; ++ pos
) {
76 remove
. push_back ( records
[ pos
]);
79 if ( pos
>= records
. size ()) {
80 throw std :: runtime_error ( "No SOA record to finish the removals part of the IXFR sequence of zone '" + zone
. toLogString () + "' from " + master
. toStringWithPort ());
83 sr
= getRR
< SOARecordContent
>( records
[ pos
]);
85 throw std :: runtime_error ( "Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone
. toLogString () + "' from " + master
. toStringWithPort ());
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
95 for ( pos
++; pos
< records
. size () && records
[ pos
]. d_type
!= QType :: SOA
; ++ pos
) {
96 add
. push_back ( records
[ pos
]);
99 if ( pos
>= records
. size ()) {
100 throw std :: runtime_error ( "No SOA record to finish the additions part of the IXFR sequence of zone '" + zone
. toLogString () + "' from " + master
. toStringWithPort ());
103 sr
= getRR
< SOARecordContent
>( records
[ pos
]);
105 throw std :: runtime_error ( "Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone
. toLogString () + "' from " + master
. toStringWithPort ());
108 if ( sr
-> d_st
. serial
!= newSerial
) {
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 ());
112 if ( newSerial
== masterSOA
-> d_st
. serial
) {
113 // this was the last sequence
114 if ( pos
!= ( records
. size () - 1 )) {
115 throw std :: runtime_error ( "Trailing records after the last IXFR sequence of zone '" + zone
. toLogString () + "' from " + master
. toStringWithPort ());
119 ret
. push_back ( make_pair ( remove
, add
));
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
,
127 const TSIGTriplet
& tt
, const ComboAddress
* laddr
, size_t maxReceivedBytes
)
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_uint16 ();
135 pw
. startRecord ( zone
, QType :: SOA
, 0 , QClass :: IN
, DNSResourceRecord :: AUTHORITY
);
136 oursr
. d_content
-> toPacket ( pw
);
139 TSIGRecordContent trc
;
140 TSIGTCPVerifier
tsigVerifier ( tt
, master
, trc
);
141 if (! tt
. algo
. empty ()) {
143 getTSIGHashEnum ( tt
. algo
, the
);
145 trc
. d_algoName
= getTSIGAlgoName ( the
);
146 } catch ( PDNSException
& pe
) {
147 throw std :: runtime_error ( "TSIG algorithm '" + tt
. algo
. toLogString ()+ "' is unknown." );
149 trc
. d_time
= time (( time_t *) NULL
);
151 trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
153 addTSIG ( pw
, trc
, tt
. name
, tt
. secret
, "" , false );
155 uint16_t len
= htons ( packet
. size ());
156 string
msg (( const char *)& len
, 2 );
157 msg
. append (( const char *)& packet
[ 0 ], packet
. size ());
159 Socket
s ( master
. sin4
. sin_family
, SOCK_STREAM
);
160 // cout<<"going to connect"<<endl;
164 // cout<<"Connected"<<endl;
167 // CURRENT MASTER SOA
169 // SOA WHERE THIS DELTA STARTS
171 // SOA WHERE THIS DELTA GOES
173 // CURRENT MASTER SOA
174 std :: shared_ptr
< SOARecordContent
> masterSOA
= nullptr ;
175 vector
< DNSRecord
> records
;
176 size_t receivedBytes
= 0 ;
177 int8_t ixfrInProgress
= - 2 ;
182 if ( ixfrInProgress
>= 0 )
185 if ( s
. read (( char *)& len
, sizeof ( len
)) != sizeof ( len
))
189 // cout<<"Got chunk of "<<len<<" bytes"<<endl;
193 if ( maxReceivedBytes
> 0 && ( maxReceivedBytes
- receivedBytes
) < ( size_t ) len
)
194 throw std :: runtime_error ( "Reached the maximum number of received bytes in an IXFR delta for zone '" + zone
. toLogString ()+ "' from master " + master
. toStringWithPort ());
197 readn2 ( s
. getHandle (), & reply
. at ( 0 ), len
);
198 receivedBytes
+= len
;
200 MOADNSParser
mdp ( false , reply
);
201 if ( mdp
. d_header
. rcode
)
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
));
204 // cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
206 if (! tt
. algo
. empty ()) { // TSIG verify message
207 tsigVerifier
. check ( reply
, mdp
);
210 for ( auto & r
: mdp
. d_answers
) {
211 // cout<<r.first.d_name<< " " <<r.first.d_content->getZoneRepresentation()<<endl;
213 // we have not seen the first SOA record yet
214 if ( r
. first
. d_type
!= QType :: SOA
) {
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 ()+ ")" );
218 auto sr
= getRR
< SOARecordContent
>( r
. first
);
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 ()+ "'" );
223 if ( sr
-> d_st
. serial
== std :: dynamic_pointer_cast
< SOARecordContent
>( oursr
. d_content
)-> d_st
. serial
) {
228 } else if ( r
. first
. d_type
== QType :: SOA
) {
229 auto sr
= getRR
< SOARecordContent
>( r
. first
);
231 throw std :: runtime_error ( "Error getting the content of SOA record of IXFR answer for zone '" + zone
. toLogString ()+ "' from master '" + master
. toStringWithPort ()+ "'" );
234 // we hit the last SOA record
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
) {
241 if ( r
. first
. d_place
!= DNSResourceRecord :: ANSWER
) {
242 if ( r
. first
. d_type
== QType :: TSIG
)
245 if ( r
. first
. d_type
== QType :: OPT
)
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 ());
251 r
. first
. d_name
. makeUsRelative ( zone
);
252 records
. push_back ( r
. first
);
256 // cout<<"Got "<<records.size()<<" records"<<endl;
258 return processIXFRRecords ( master
, zone
, records
, masterSOA
);