]>
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
. toString ()+ "' 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 it's the final SOA, there is nothing for us to see
66 remove
. push_back ( records
[ pos
]); // this adds the SOA
69 for ( pos
++; pos
< records
. size () && records
[ pos
]. d_type
!= QType :: SOA
; ++ pos
) {
70 remove
. push_back ( records
[ pos
]);
73 if ( pos
>= records
. size ()) {
74 throw std :: runtime_error ( "No SOA record to finish the removals part of the IXFR sequence of zone '" + zone
. toString () + "' from " + master
. toStringWithPort ());
77 sr
= getRR
< SOARecordContent
>( records
[ pos
]);
79 throw std :: runtime_error ( "Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone
. toString () + "' from " + master
. toStringWithPort ());
82 // this is the serial of the zone after the removals
83 // and updates, but that might not be the final serial
84 // because there might be several sequences
85 uint32_t newSerial
= sr
-> d_st
. serial
;
86 add
. push_back ( records
[ pos
]); // this adds the new SOA
89 for ( pos
++; pos
< records
. size () && records
[ pos
]. d_type
!= QType :: SOA
; ++ pos
) {
90 add
. push_back ( records
[ pos
]);
93 if ( pos
>= records
. size ()) {
94 throw std :: runtime_error ( "No SOA record to finish the additions part of the IXFR sequence of zone '" + zone
. toString () + "' from " + master
. toStringWithPort ());
97 sr
= getRR
< SOARecordContent
>( records
[ pos
]);
99 throw std :: runtime_error ( "Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone
. toString () + "' from " + master
. toStringWithPort ());
102 if ( sr
-> d_st
. serial
!= newSerial
) {
103 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
. toString () + "' from " + master
. toStringWithPort ());
106 if ( newSerial
== masterSOA
-> d_st
. serial
) {
107 // this was the last sequence
108 if ( pos
!= ( records
. size () - 1 )) {
109 throw std :: runtime_error ( "Trailing records after the last IXFR sequence of zone '" + zone
. toString () + "' from " + master
. toStringWithPort ());
113 ret
. push_back ( make_pair ( remove
, add
));
119 // Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR!
120 vector
< pair
< vector
< DNSRecord
>, vector
< DNSRecord
> > > getIXFRDeltas ( const ComboAddress
& master
, const DNSName
& zone
, const DNSRecord
& oursr
,
121 const TSIGTriplet
& tt
, const ComboAddress
* laddr
, size_t maxReceivedBytes
)
123 vector
< pair
< vector
< DNSRecord
>, vector
< DNSRecord
> > > ret
;
124 vector
< uint8_t > packet
;
125 DNSPacketWriter
pw ( packet
, zone
, QType :: IXFR
);
126 pw
. getHeader ()-> qr
= 0 ;
127 pw
. getHeader ()-> rd
= 0 ;
128 pw
. getHeader ()-> id
= dns_random ( 0xffff );
129 pw
. startRecord ( zone
, QType :: SOA
, 0 , QClass :: IN
, DNSResourceRecord :: AUTHORITY
);
130 oursr
. d_content
-> toPacket ( pw
);
133 TSIGRecordContent trc
;
134 TSIGTCPVerifier
tsigVerifier ( tt
, master
, trc
);
135 if (! tt
. algo
. empty ()) {
137 getTSIGHashEnum ( tt
. algo
, the
);
139 trc
. d_algoName
= getTSIGAlgoName ( the
);
140 } catch ( PDNSException
& pe
) {
141 throw std :: runtime_error ( "TSIG algorithm '" + tt
. algo
. toString ()+ "' is unknown." );
143 trc
. d_time
= time (( time_t *) NULL
);
145 trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
147 addTSIG ( pw
, trc
, tt
. name
, tt
. secret
, "" , false );
149 uint16_t len
= htons ( packet
. size ());
150 string
msg (( const char *)& len
, 2 );
151 msg
. append (( const char *)& packet
[ 0 ], packet
. size ());
153 Socket
s ( master
. sin4
. sin_family
, SOCK_STREAM
);
154 // cout<<"going to connect"<<endl;
158 // cout<<"Connected"<<endl;
161 // CURRENT MASTER SOA
163 // SOA WHERE THIS DELTA STARTS
165 // SOA WHERE THIS DELTA GOES
167 // CURRENT MASTER SOA
168 std :: shared_ptr
< SOARecordContent
> masterSOA
= nullptr ;
169 vector
< DNSRecord
> records
;
170 size_t receivedBytes
= 0 ;
173 if ( s
. read (( char *)& len
, sizeof ( len
)) != sizeof ( len
))
177 // cout<<"Got chunk of "<<len<<" bytes"<<endl;
181 if ( maxReceivedBytes
> 0 && ( maxReceivedBytes
- receivedBytes
) < ( size_t ) len
)
182 throw std :: runtime_error ( "Reached the maximum number of received bytes in an IXFR delta for zone '" + zone
. toString ()+ "' from master " + master
. toStringWithPort ());
185 readn2 ( s
. getHandle (), reply
, len
);
186 receivedBytes
+= len
;
188 MOADNSParser
mdp ( false , string ( reply
, len
));
189 if ( mdp
. d_header
. rcode
)
190 throw std :: runtime_error ( "Got an error trying to IXFR zone '" + zone
. toString ()+ "' from master '" + master
. toStringWithPort ()+ "': " + RCode :: to_s ( mdp
. d_header
. rcode
));
192 // cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
194 if (! tt
. algo
. empty ()) { // TSIG verify message
195 tsigVerifier
. check ( std :: string ( reply
, len
), mdp
);
198 for ( auto & r
: mdp
. d_answers
) {
199 // cout<<r.first.d_name<< " " <<r.first.d_content->getZoneRepresentation()<<endl;
201 // we have not seen the first SOA record yet
202 if ( r
. first
. d_type
!= QType :: SOA
) {
203 throw std :: runtime_error ( "The first record of the IXFR answer for zone '" + zone
. toString ()+ "' from master '" + master
. toStringWithPort ()+ "' is not a SOA (" + QType ( r
. first
. d_type
). getName ()+ ")" );
206 auto sr
= getRR
< SOARecordContent
>( r
. first
);
208 throw std :: runtime_error ( "Error getting the content of the first SOA record of the IXFR answer for zone '" + zone
. toString ()+ "' from master '" + master
. toStringWithPort ()+ "'" );
211 if ( sr
-> d_st
. serial
== std :: dynamic_pointer_cast
< SOARecordContent
>( oursr
. d_content
)-> d_st
. serial
) {
218 if ( r
. first
. d_place
!= DNSResourceRecord :: ANSWER
) {
219 if ( r
. first
. d_type
== QType :: TSIG
)
222 if ( r
. first
. d_type
== QType :: OPT
)
225 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
. toString ()+ "' from master '" + master
. toStringWithPort ());
228 r
. first
. d_name
. makeUsRelative ( zone
);
229 records
. push_back ( r
. first
);
233 // cout<<"Got "<<records.size()<<" records"<<endl;
235 return processIXFRRecords ( master
, zone
, records
, masterSOA
);