]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ueberbackend.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.
25 #include <boost/archive/binary_iarchive.hpp>
26 #include <boost/archive/binary_oarchive.hpp>
28 #include "auth-querycache.hh"
29 #include "auth-zonecache.hh"
36 #include <sys/types.h>
44 #include "arguments.hh"
45 #include "dnsbackend.hh"
46 #include "ueberbackend.hh"
47 #include "dnspacket.hh"
53 LockGuarded
< vector
< UeberBackend
*>> UeberBackend :: d_instances
;
55 // initially we are blocked
56 bool UeberBackend :: d_go
= false ;
57 bool UeberBackend :: s_doANYLookupsOnly
= false ;
58 std :: mutex
UeberBackend :: d_mut
;
59 std :: condition_variable
UeberBackend :: d_cond
;
60 AtomicCounter
* UeberBackend :: s_backendQueries
= nullptr ;
62 //! Loads a module and reports it to all UeberBackend threads
63 bool UeberBackend :: loadmodule ( const string
& name
)
65 g_log
<< Logger :: Warning
<< "Loading '" << name
<< "'" << endl
;
67 void * dlib
= dlopen ( name
. c_str (), RTLD_NOW
);
70 g_log
<< Logger :: Error
<< "Unable to load module '" << name
<< "': " << dlerror () << endl
;
77 bool UeberBackend :: loadModules ( const vector
< string
>& modules
, const string
& path
)
79 for ( const auto & module
: modules
) {
81 if ( module
. find ( '.' )== string :: npos
) {
82 res
= UeberBackend :: loadmodule ( path
+ "/lib" + module
+ "backend.so" );
83 } else if ( module
[ 0 ]== '/' || ( module
[ 0 ]== '.' && module
[ 1 ]== '/' ) || ( module
[ 0 ]== '.' && module
[ 1 ]== '.' )) {
84 // absolute or current path
85 res
= UeberBackend :: loadmodule ( module
);
87 res
= UeberBackend :: loadmodule ( path
+ "/" + module
);
97 void UeberBackend :: go ()
99 if (:: arg (). mustDo ( "consistent-backends" )) {
100 s_doANYLookupsOnly
= true ;
103 S
. declare ( "backend-queries" , "Number of queries sent to the backend(s)" );
104 s_backendQueries
= S
. getPointer ( "backend-queries" );
107 std :: unique_lock
< std :: mutex
> l ( d_mut
);
113 bool UeberBackend :: getDomainInfo ( const DNSName
& domain
, DomainInfo
& di
, bool getSerial
)
115 for ( auto backend
: backends
)
116 if ( backend
-> getDomainInfo ( domain
, di
, getSerial
))
121 bool UeberBackend :: createDomain ( const DNSName
& domain
, const DomainInfo :: DomainKind kind
, const vector
< ComboAddress
> & masters
, const string
& account
)
123 for ( DNSBackend
* mydb
: backends
) {
124 if ( mydb
-> createDomain ( domain
, kind
, masters
, account
)) {
131 bool UeberBackend :: doesDNSSEC ()
133 for ( auto * db
: backends
) {
140 bool UeberBackend :: addDomainKey ( const DNSName
& name
, const DNSBackend :: KeyData
& key
, int64_t & id
)
143 for ( DNSBackend
* db
: backends
) {
144 if ( db
-> addDomainKey ( name
, key
, id
))
149 bool UeberBackend :: getDomainKeys ( const DNSName
& name
, std :: vector
< DNSBackend :: KeyData
>& keys
)
151 for ( DNSBackend
* db
: backends
) {
152 if ( db
-> getDomainKeys ( name
, keys
))
158 bool UeberBackend :: getAllDomainMetadata ( const DNSName
& name
, std :: map
< std :: string
, std :: vector
< std :: string
> >& meta
)
160 for ( DNSBackend
* db
: backends
) {
161 if ( db
-> getAllDomainMetadata ( name
, meta
))
167 bool UeberBackend :: getDomainMetadata ( const DNSName
& name
, const std :: string
& kind
, std :: vector
< std :: string
>& meta
)
169 for ( DNSBackend
* db
: backends
) {
170 if ( db
-> getDomainMetadata ( name
, kind
, meta
))
176 bool UeberBackend :: getDomainMetadata ( const DNSName
& name
, const std :: string
& kind
, std :: string
& meta
)
180 std :: vector
< string
> tmp
;
181 if (( ret
= getDomainMetadata ( name
, kind
, tmp
)) && ! tmp
. empty ()) {
187 bool UeberBackend :: setDomainMetadata ( const DNSName
& name
, const std :: string
& kind
, const std :: vector
< std :: string
>& meta
)
189 for ( DNSBackend
* db
: backends
) {
190 if ( db
-> setDomainMetadata ( name
, kind
, meta
))
196 bool UeberBackend :: setDomainMetadata ( const DNSName
& name
, const std :: string
& kind
, const std :: string
& meta
)
198 std :: vector
< string
> tmp
;
202 return setDomainMetadata ( name
, kind
, tmp
);
205 bool UeberBackend :: activateDomainKey ( const DNSName
& name
, unsigned int id
)
207 for ( DNSBackend
* db
: backends
) {
208 if ( db
-> activateDomainKey ( name
, id
))
214 bool UeberBackend :: deactivateDomainKey ( const DNSName
& name
, unsigned int id
)
216 for ( DNSBackend
* db
: backends
) {
217 if ( db
-> deactivateDomainKey ( name
, id
))
223 bool UeberBackend :: publishDomainKey ( const DNSName
& name
, unsigned int id
)
225 for ( DNSBackend
* db
: backends
) {
226 if ( db
-> publishDomainKey ( name
, id
))
232 bool UeberBackend :: unpublishDomainKey ( const DNSName
& name
, unsigned int id
)
234 for ( DNSBackend
* db
: backends
) {
235 if ( db
-> unpublishDomainKey ( name
, id
))
242 bool UeberBackend :: removeDomainKey ( const DNSName
& name
, unsigned int id
)
244 for ( DNSBackend
* db
: backends
) {
245 if ( db
-> removeDomainKey ( name
, id
))
253 void UeberBackend :: reload ()
255 for ( auto & backend
: backends
)
261 void UeberBackend :: updateZoneCache () {
262 if (! g_zoneCache
. isEnabled ()) {
266 vector
< std :: tuple
< DNSName
, int >> zone_indices
;
267 g_zoneCache
. setReplacePending ();
269 for ( vector
< DNSBackend
*>:: iterator i
= backends
. begin (); i
!= backends
. end (); ++ i
)
271 vector
< DomainInfo
> zones
;
272 (* i
)-> getAllDomains (& zones
, false , true );
273 for ( auto & di
: zones
) {
274 zone_indices
. emplace_back ( std :: move ( di
. zone
), ( int ) di
. id
); // this cast should not be necessary
277 g_zoneCache
. replace ( zone_indices
);
280 void UeberBackend :: rediscover ( string
* status
)
282 for ( vector
< DNSBackend
* >:: iterator i
= backends
. begin (); i
!= backends
. end (); ++ i
)
285 ( * i
)-> rediscover (& tmpstr
);
287 * status
+= tmpstr
+ ( i
!= backends
. begin () ? " \n " : "" );
294 void UeberBackend :: getUnfreshSlaveInfos ( vector
< DomainInfo
>* domains
)
296 for ( auto & backend
: backends
)
298 backend
-> getUnfreshSlaveInfos ( domains
);
302 void UeberBackend :: getUpdatedMasters ( vector
< DomainInfo
>& domains
, std :: unordered_set
< DNSName
>& catalogs
, CatalogHashMap
& catalogHashes
)
304 for ( auto & backend
: backends
)
306 backend
-> getUpdatedMasters ( domains
, catalogs
, catalogHashes
);
310 bool UeberBackend :: inTransaction ()
312 for ( auto * b
: backends
)
314 if ( b
-> inTransaction ())
320 bool UeberBackend :: getAuth ( const DNSName
& target
, const QType
& qtype
, SOAData
* sd
, bool cachedOk
)
322 // A backend can respond to our authority request with the 'best' match it
323 // has. For example, when asked for a.b.c.example.com. it might respond with
324 // com. We then store that and keep querying the other backends in case one
325 // of them has a more specific zone but don't bother asking this specific
326 // backend again for b.c.example.com., c.example.com. and example.com.
327 // If a backend has no match it may respond with an empty qname.
330 DNSName
shorter ( target
);
331 vector
< pair
< size_t , SOAData
> > bestmatch ( backends
. size (), pair ( target
. wirelength ()+ 1 , SOAData ()));
334 if ( cachedOk
&& g_zoneCache
. isEnabled ()) {
335 if ( g_zoneCache
. getEntry ( shorter
, zoneId
)) {
336 // Zone exists in zone cache, directly look up SOA.
338 lookup ( QType ( QType :: SOA
), shorter
, zoneId
, nullptr );
340 DLOG ( g_log
<< Logger :: Info
<< "Backend returned no SOA for zone '" << shorter
. toLogString () << "', which it reported as existing " << endl
);
343 if ( zr
. dr
. d_name
!= shorter
) {
344 throw PDNSException ( "getAuth() returned an SOA for the wrong zone. Zone '" + zr
. dr
. d_name
. toLogString ()+ "' is not equal to looked up zone '" + shorter
. toLogString ()+ "'" );
347 sd
-> qname
= zr
. dr
. d_name
;
349 fillSOAData ( zr
, * sd
);
352 g_log
<< Logger :: Warning
<< "Backend returned a broken SOA for zone '" << shorter
. toLogString () << "'" << endl
;
357 if ( backends
. size () == 1 ) {
358 sd
-> db
= * backends
. begin ();
363 // leave database handle in a consistent state
368 // zone does not exist, try again with shorter name
372 d_question
. qtype
= QType :: SOA
;
373 d_question
. qname
= shorter
;
374 d_question
. zoneId
= zoneId
;
377 if ( cachedOk
&& ( d_cache_ttl
|| d_negcache_ttl
)) {
378 cstat
= cacheHas ( d_question
, d_answers
);
380 if ( cstat
== 1 && ! d_answers
. empty () && d_cache_ttl
) {
381 DLOG ( g_log
<< Logger :: Error
<< "has pos cache entry: " << shorter
<< endl
);
382 fillSOAData ( d_answers
[ 0 ], * sd
);
384 if ( backends
. size () == 1 ) {
385 sd
-> db
= * backends
. begin ();
391 } else if ( cstat
== 0 && d_negcache_ttl
) {
392 DLOG ( g_log
<< Logger :: Error
<< "has neg cache entry: " << shorter
<< endl
);
399 vector
< DNSBackend
*>:: const_iterator i
= backends
. begin ();
400 vector
< pair
< size_t , SOAData
> >:: iterator j
= bestmatch
. begin ();
401 for (; i
!= backends
. end () && j
!= bestmatch
. end (); ++ i
, ++ j
) {
403 DLOG ( g_log
<< Logger :: Error
<< "backend: " << i
- backends
. begin ()<< ", qname: " << shorter
<< endl
);
405 if ( j
-> first
< shorter
. wirelength ()) {
406 DLOG ( g_log
<< Logger :: Error
<< "skipped, we already found a shorter best match in this backend: " << j
-> second
. qname
<< endl
);
408 } else if ( j
-> first
== shorter
. wirelength ()) {
409 DLOG ( g_log
<< Logger :: Error
<< "use shorter best match: " << j
-> second
. qname
<< endl
);
413 DLOG ( g_log
<< Logger :: Error
<< "lookup: " << shorter
<< endl
);
414 if ((* i
)-> getAuth ( shorter
, sd
)) {
415 DLOG ( g_log
<< Logger :: Error
<< "got: " << sd
-> qname
<< endl
);
416 if (! sd
-> qname
. empty () && ! shorter
. isPartOf ( sd
-> qname
)) {
417 throw PDNSException ( "getAuth() returned an SOA for the wrong zone. Zone '" + sd
-> qname
. toLogString ()+ "' is not part of '" + shorter
. toLogString ()+ "'" );
419 j
-> first
= sd
-> qname
. wirelength ();
421 if ( sd
-> qname
== shorter
) {
425 DLOG ( g_log
<< Logger :: Error
<< "no match for: " << shorter
<< endl
);
431 if ( i
== backends
. end ()) {
433 DLOG ( g_log
<< Logger :: Error
<< "add neg cache entry:" << shorter
<< endl
);
434 d_question
. qname
= shorter
;
435 addNegCache ( d_question
);
438 } else if ( d_cache_ttl
) {
439 DLOG ( g_log
<< Logger :: Error
<< "add pos cache entry: " << sd
-> qname
<< endl
);
440 d_question
. qtype
= QType :: SOA
;
441 d_question
. qname
= sd
-> qname
;
442 d_question
. zoneId
= zoneId
;
445 rr
. dr
. d_name
= sd
-> qname
;
446 rr
. dr
. d_type
= QType :: SOA
;
447 rr
. dr
. d_content
= makeSOAContent (* sd
);
448 rr
. dr
. d_ttl
= sd
-> ttl
;
449 rr
. domain_id
= sd
-> domain_id
;
451 addCache ( d_question
, { rr
});
456 if ( found
== ( qtype
== QType :: DS
) || target
!= shorter
) {
457 DLOG ( g_log
<< Logger :: Error
<< "found: " << sd
-> qname
<< endl
);
460 DLOG ( g_log
<< Logger :: Error
<< "chasing next: " << sd
-> qname
<< endl
);
464 } while ( shorter
. chopOff ());
468 bool UeberBackend :: getSOAUncached ( const DNSName
& domain
, SOAData
& sd
)
470 d_question
. qtype
= QType :: SOA
;
471 d_question
. qname
= domain
;
472 d_question
. zoneId
=- 1 ;
474 for ( auto backend
: backends
)
475 if ( backend
-> getSOA ( domain
, sd
)) {
476 if ( domain
!= sd
. qname
) {
477 throw PDNSException ( "getSOA() returned an SOA for the wrong zone. Question: '" + domain
. toLogString ()+ "', answer: '" + sd
. qname
. toLogString ()+ "'" );
481 rr
. dr
. d_name
= sd
. qname
;
482 rr
. dr
. d_type
= QType :: SOA
;
483 rr
. dr
. d_content
= makeSOAContent ( sd
);
484 rr
. dr
. d_ttl
= sd
. ttl
;
485 rr
. domain_id
= sd
. domain_id
;
487 addCache ( d_question
, { rr
});
494 addNegCache ( d_question
);
498 bool UeberBackend :: superMasterAdd ( const AutoPrimary
& primary
)
500 for ( auto backend
: backends
)
501 if ( backend
-> superMasterAdd ( primary
))
506 bool UeberBackend :: autoPrimaryRemove ( const AutoPrimary
& primary
)
508 for ( auto backend
: backends
)
509 if ( backend
-> autoPrimaryRemove ( primary
))
514 bool UeberBackend :: autoPrimariesList ( std :: vector
< AutoPrimary
>& primaries
)
516 for ( auto backend
: backends
)
517 if ( backend
-> autoPrimariesList ( primaries
))
522 bool UeberBackend :: superMasterBackend ( const string
& ip
, const DNSName
& domain
, const vector
< DNSResourceRecord
>& nsset
, string
* nameserver
, string
* account
, DNSBackend
** db
)
524 for ( auto backend
: backends
)
525 if ( backend
-> superMasterBackend ( ip
, domain
, nsset
, nameserver
, account
, db
))
530 UeberBackend :: UeberBackend ( const string
& pname
)
533 d_instances
. lock ()-> push_back ( this ); // report to the static list of ourself
538 d_cache_ttl
= :: arg (). asNum ( "query-cache-ttl" );
539 d_negcache_ttl
= :: arg (). asNum ( "negquery-cache-ttl" );
543 backends
= BackendMakers (). all ( pname
== "key-only" );
546 static void del ( DNSBackend
* d
)
551 void UeberBackend :: cleanup ()
554 auto instances
= d_instances
. lock ();
555 remove ( instances
-> begin (), instances
-> end (), this );
556 instances
-> resize ( instances
-> size ()- 1 );
559 for_each ( backends
. begin (), backends
. end (), del
);
562 // returns -1 for miss, 0 for negative match, 1 for hit
563 int UeberBackend :: cacheHas ( const Question
& q
, vector
< DNSZoneRecord
> & rrs
)
565 extern AuthQueryCache QC
;
567 if (! d_cache_ttl
&& ! d_negcache_ttl
) {
572 // g_log<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
574 bool ret
= QC
. getEntry ( q
. qname
, q
. qtype
, rrs
, q
. zoneId
); // think about lowercasing here
578 if ( rrs
. empty ()) // negatively cached
584 void UeberBackend :: addNegCache ( const Question
& q
)
586 extern AuthQueryCache QC
;
589 // we should also not be storing negative answers if a pipebackend does scopeMask, but we can't pass a negative scopeMask in an empty set!
590 QC
. insert ( q
. qname
, q
. qtype
, vector
< DNSZoneRecord
>(), d_negcache_ttl
, q
. zoneId
);
593 void UeberBackend :: addCache ( const Question
& q
, vector
< DNSZoneRecord
> && rrs
)
595 extern AuthQueryCache QC
;
600 for ( const auto & rr
: rrs
) {
605 QC
. insert ( q
. qname
, q
. qtype
, std :: move ( rrs
), d_cache_ttl
, q
. zoneId
);
608 void UeberBackend :: alsoNotifies ( const DNSName
& domain
, set
< string
> * ips
)
610 for ( auto & backend
: backends
)
611 backend
-> alsoNotifies ( domain
, ips
);
614 UeberBackend ::~ UeberBackend ()
616 DLOG ( g_log
<< Logger :: Error
<< "UeberBackend destructor called, removing ourselves from instances, and deleting our backends" << endl
);
620 // this handle is more magic than most
621 void UeberBackend :: lookup ( const QType
& qtype
, const DNSName
& qname
, int zoneId
, DNSPacket
* pkt_p
)
624 g_log
<< Logger :: Error
<< "Stale ueberbackend received question, signalling that we want to be recycled" << endl
;
625 throw PDNSException ( "We are stale, please recycle" );
628 DLOG ( g_log
<< "UeberBackend received question for " << qtype
<< " of " << qname
<< endl
);
630 g_log
<< Logger :: Error
<< "UeberBackend is blocked, waiting for 'go'" << endl
;
631 std :: unique_lock
< std :: mutex
> l ( d_mut
);
632 d_cond
. wait ( l
, []{ return d_go
== true ; });
633 g_log
<< Logger :: Error
<< "Broadcast received, unblocked" << endl
;
636 d_qtype
= qtype
. getCode ();
639 d_handle
. qtype
= s_doANYLookupsOnly
? QType :: ANY
: qtype
;
640 d_handle
. qname
= qname
;
641 d_handle
. zoneId
= zoneId
;
642 d_handle
. pkt_p
= pkt_p
;
644 if (! backends
. size ()) {
645 g_log
<< Logger :: Error
<< "No database backends available - unable to answer questions." << endl
;
646 d_stale
= true ; // please recycle us!
647 throw PDNSException ( "We are stale, please recycle" );
650 d_question
. qtype
= d_handle
. qtype
;
651 d_question
. qname
= qname
;
652 d_question
. zoneId
= d_handle
. zoneId
;
654 int cstat
= cacheHas ( d_question
, d_answers
);
655 if ( cstat
< 0 ) { // nothing
656 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
657 d_negcached
= d_cached
= false ;
659 ( d_handle
. d_hinterBackend
= backends
[ d_handle
. i
++])-> lookup ( d_handle
. qtype
, d_handle
. qname
, d_handle
. zoneId
, d_handle
. pkt_p
);
660 ++(* s_backendQueries
);
663 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
669 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
672 d_cachehandleiter
= d_answers
. begin ();
676 d_handle
. parent
= this ;
679 void UeberBackend :: getAllDomains ( vector
< DomainInfo
>* domains
, bool getSerial
, bool include_disabled
)
681 for ( auto & backend
: backends
)
683 backend
-> getAllDomains ( domains
, getSerial
, include_disabled
);
687 bool UeberBackend :: get ( DNSZoneRecord
& rr
)
689 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
695 while ( d_cachehandleiter
!= d_answers
. end ()) {
696 rr
=* d_cachehandleiter
++;;
697 if (( d_qtype
== QType :: ANY
|| rr
. dr
. d_type
== d_qtype
)) {
704 while ( d_handle
. get ( rr
)) {
705 rr
. dr
. d_place
= DNSResourceRecord :: ANSWER
;
706 d_answers
. push_back ( rr
);
707 if (( d_qtype
== QType :: ANY
|| rr
. dr
. d_type
== d_qtype
)) {
712 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
713 if ( d_answers
. empty ()) {
714 // cout<<"adding negcache"<<endl;
715 addNegCache ( d_question
);
718 // cout<<"adding query cache"<<endl;
719 addCache ( d_question
, std :: move ( d_answers
));
727 bool UeberBackend :: setTSIGKey ( const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
729 for ( auto * b
: backends
) {
730 if ( b
-> setTSIGKey ( name
, algorithm
, content
)) {
737 bool UeberBackend :: getTSIGKey ( const DNSName
& name
, DNSName
& algorithm
, string
& content
)
742 for ( auto * b
: backends
) {
743 if ( b
-> getTSIGKey ( name
, algorithm
, content
)) {
747 return (! algorithm
. empty () && ! content
. empty ());
750 bool UeberBackend :: getTSIGKeys ( std :: vector
< struct TSIGKey
>& keys
)
754 for ( auto * b
: backends
) {
755 if ( b
-> getTSIGKeys ( keys
)) {
762 bool UeberBackend :: deleteTSIGKey ( const DNSName
& name
)
764 for ( auto * b
: backends
) {
765 if ( b
-> deleteTSIGKey ( name
)) {
774 bool UeberBackend :: searchRecords ( const string
& pattern
, int maxResults
, vector
< DNSResourceRecord
>& result
)
777 for ( vector
< DNSBackend
* >:: iterator i
= backends
. begin (); result
. size () < static_cast < vector
< DNSResourceRecord
>:: size_type
>( maxResults
) && i
!= backends
. end (); ++ i
)
778 if ((* i
)-> searchRecords ( pattern
, maxResults
- result
. size (), result
)) rc
= true ;
782 bool UeberBackend :: searchComments ( const string
& pattern
, int maxResults
, vector
< Comment
>& result
)
785 for ( vector
< DNSBackend
* >:: iterator i
= backends
. begin (); result
. size () < static_cast < vector
< Comment
>:: size_type
>( maxResults
) && i
!= backends
. end (); ++ i
)
786 if ((* i
)-> searchComments ( pattern
, maxResults
- result
. size (), result
)) rc
= true ;
790 AtomicCounter
UeberBackend :: handle :: instances ( 0 );
792 UeberBackend :: handle :: handle ()
794 // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl;
797 d_hinterBackend
= nullptr ;
803 UeberBackend :: handle ::~ handle ()
808 bool UeberBackend :: handle :: get ( DNSZoneRecord
& r
)
810 DLOG ( g_log
<< "Ueber get() was called for a " << qtype
<< " record" << endl
);
812 while ( d_hinterBackend
&& !( isMore
= d_hinterBackend
-> get ( r
))) { // this backend out of answers
813 if ( i
< parent
-> backends
. size ()) {
814 DLOG ( g_log
<< "Backend #" << i
<< " of " << parent
-> backends
. size ()
815 << " out of answers, taking next" << endl
);
817 d_hinterBackend
= parent
-> backends
[ i
++];
818 d_hinterBackend
-> lookup ( qtype
, qname
, zoneId
, pkt_p
);
819 ++(* s_backendQueries
);
824 DLOG ( g_log
<< "Now asking backend #" << i
<< endl
);
827 if (! isMore
&& i
== parent
-> backends
. size ()) {
828 DLOG ( g_log
<< "UeberBackend reached end of backends" << endl
);
832 DLOG ( g_log
<< "Found an answering backend - will not try another one" << endl
);
833 i
= parent
-> backends
. size (); // don't go on to the next backend