<rdar://problem/7252523> The solution to the problem consists of many parts.
authormparthasarathy@apple.com <mparthasarathy@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Fri, 25 Sep 2009 23:18:47 +0000 (23:18 +0000)
committermparthasarathy@apple.com <mparthasarathy@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Fri, 25 Sep 2009 23:18:47 +0000 (23:18 +0000)
1) Now we don't move the server to the end. Instead, it penalizes the server for 60 seconds so that new questions pick a different unpenalized server if any.

2) If all servers are penalized, it picks the one that is least penalized. Also, we don't penalize a server if it fails to respond to PTR queries as this was one of the common things seen with WAB queries.

3)To avoid doing any sort of learning, "-StrictUnicastOrdering" can now be added to the plist which will prevent penalizing servers but then move on with the next server on the list.

git-svn-id: http://svn.macosforge.org/repository/mDNSResponder/trunk@6696 214c2c4a-bf3b-4dcf-9390-e4dd3010487d

mDNSCore/mDNS.c
mDNSCore/mDNSEmbeddedAPI.h
mDNSCore/uDNS.c
mDNSCore/uDNS.h
mDNSMacOSX/daemon.c

index 1d4b5dc..5af01d6 100755 (executable)
@@ -3081,6 +3081,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
                {
                int i;
 
+               // If there are DNS servers that will come out of the Penalty box, we should do that now
+               // so that any questions that we send below can start using that
+               ResetDNSServerPenalties(m);
+
                verbosedebugf("mDNS_Execute");
                if (m->CurrentQuestion)
                        LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
@@ -4932,7 +4936,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                                        if (qptr)
                                                {
                                                LogInfo("Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
-                                               PushDNSServerToEnd(m, qptr);
+                                               PenalizeDNSServer(m, qptr, mDNSfalse);
                                                }
                                        returnEarly = mDNStrue;
                                        }
@@ -5840,19 +5844,150 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
                                }
        }
 
+mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server)
+       {
+       mDNSs32 ptime = 0;
+       if (server->penaltyTime != 0)
+               {
+               ptime = server->penaltyTime - m->timenow;
+               if (ptime < 0)
+                       {
+                       // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME
+                       // If it does not get reset in ResetDNSServerPenalties for some reason, we do it
+                       // here
+                       LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", ptime, server->penaltyTime, m->timenow);
+                       server->penaltyTime = 0;
+                       ptime = 0;
+                       }
+               }
+       return ptime;
+       }
+
+// Return the next server to "prev" if it is a match and unpenalized
+mDNSlocal DNSServer *GetNextUnPenalizedServer(mDNS *m, DNSServer *prev)
+       {
+       int curmatchlen = -1;
+       DNSServer *curr = m->DNSServers;
+
+       if (prev == mDNSNULL) return mDNSNULL;
+
+       while (curr != mDNSNULL && curr != prev)
+               curr = curr->next;
+
+       if (curr == mDNSNULL)
+               return mDNSNULL;
+
+
+       // We need to set the curmatchlen as though we are walking the list
+       // from the beginning. Otherwise, we may not pick the best match.
+       // For example, if we are looking up xxx.com, and we used the "xxx.com"
+       // entry the previous time and the next one is "com", we should not pick
+       // "com" now
+       curmatchlen = CountLabels(&curr->domain);
+       curr = curr->next;
+       while (curr != mDNSNULL)
+               {
+               int scount = CountLabels(&curr->domain);
+
+               // Should not be delete because it is marked temporarily for cleaning up
+               // entries during configuration change and we pass NULL as the last argument
+               // to GetServerForName 
+               if (curr->flags & DNSServer_FlagDelete)
+                       {
+                       LogInfo("GetServerForName: DNS Server is marked delete, cannot happen");
+                       curr = curr->next;
+                       continue;
+                       }
+
+
+               LogInfo("GetNextUnPenalizedServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", &curr->addr, curr->domain.c, curr->penaltyTime, PenaltyTimeForServer(m,curr));
+
+               // Note the "==" in comparing scount and curmatchlen. When we picked a match
+               // for the question the first time, we already made sure that prev is the best match.
+               // Any other match is as good if we can find another entry with same number of
+               // labels. There can't be better matches that have more labels, because
+               // we would have picked that in the first place. Also we don't care what the
+               // name in the question is as we picked the best server for the question first
+               // time and the domain name is in prev now
+
+               if ((curr->penaltyTime == 0) && (scount == curmatchlen) && SameDomainName(&prev->domain, &curr->domain))
+                               return curr;
+               curr = curr->next;
+               }
+       return mDNSNULL;
+       }
+
+
+// Get the Best server that matches a name. If you find penalized servers, look for the one
+// that will come out of the penalty box soon
+mDNSlocal DNSServer *GetAnyBestServer(mDNS *m, const domainname *name)
+       {
+       DNSServer *curmatch = mDNSNULL;
+       int curmatchlen = -1, ncount = name ? CountLabels(name) : 0;
+       DNSServer *curr;
+       mDNSs32 penaltyTime;
+
+       curmatchlen = -1;
+       penaltyTime = DNSSERVER_PENALTY_TIME + 1;
+       for (curr = m->DNSServers; curr; curr = curr->next)
+               {
+               int scount = CountLabels(&curr->domain);
+               mDNSs32 stime = PenaltyTimeForServer(m, curr);
+
+               LogInfo("GetAnyBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", &curr->addr, curr->domain.c, curr->penaltyTime, stime);
+
+               // If there are multiple best servers for a given question, we will pick the first one
+               // if none of them are penalized. If some of them are penalized in that list, we pick
+               // the least penalized one. "scount >= curmatchlen" lets us walk through all best
+               // matches and "stime < penaltyTime" check lets us either pick the first best server
+               // in the list when there are no penalized servers and least one among them when there
+               // are some penalized servers
+
+               if (!(curr->flags & DNSServer_FlagDelete) && ncount >= scount && scount >= curmatchlen && stime < penaltyTime)
+                       if (SameDomainName(SkipLeadingLabels(name, ncount - scount), &curr->domain))
+                               { curmatch = curr; curmatchlen = scount; penaltyTime = stime;}
+               }
+       return curmatch;
+       }
+
 // Look up a DNS Server, matching by name in split-dns configurations.
-mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name)
+mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, DNSServer *prev)
     {
-       DNSServer *curmatch = mDNSNULL, *p;
-       int curmatchlen = -1, ncount = name ? CountLabels(name) : 0;
+       DNSServer *curmatch = mDNSNULL;
 
-       for (p = m->DNSServers; p; p = p->next)
+       // prev is the previous DNS server used by some question
+       if (prev != mDNSNULL)
                {
-               int scount = CountLabels(&p->domain);
-               if (!(p->flags & DNSServer_FlagDelete) && ncount >= scount && scount > curmatchlen)
-                       if (SameDomainName(SkipLeadingLabels(name, ncount - scount), &p->domain))
-                               { curmatch = p; curmatchlen = scount; }
+               curmatch = GetNextUnPenalizedServer(m, prev);
+               if (curmatch != mDNSNULL) 
+                       {
+                       LogInfo("GetServerForName: Good DNS server %#a:%d (Penalty Time Left %d) found", &curmatch->addr,
+                           mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0));
+                       return curmatch;
+                       }
                }
+       
+       // We are here for many reasons.
+       //
+       // 1. We are looking up the DNS server first time for this question
+       // 2. We reached the end of list looking for unpenalized servers
+       //
+       // In the case of (1) we want to find the best match for the name. If nothing is penalized,
+       // we want the first one in the list. If some are penalized, we want the one that will get
+       // out of penalty box sooner
+       //
+       // In the case of (2) we want to select the first server that matches the name if StrictUnicastOrdering
+       // is TRUE. As penaltyTime is zero for all of them in that case, we automatically achieve that below.
+       // If StrictUnicastOrdering is FALSE, we want to pick the least penalized server in the list
+
+       curmatch = GetAnyBestServer(m, name);
+
+       if (curmatch != mDNSNULL) 
+               LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) found", &curmatch->addr,
+                   mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0));
+       else
+               LogInfo("GetServerForName: no DNS server found");
+
        return(curmatch);
        }
 
@@ -6005,7 +6140,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                        // this routine with the question list data structures in an inconsistent state.
                        if (!mDNSOpaque16IsZero(question->TargetQID))
                                {
-                               question->qDNSServer = GetServerForName(m, &question->qname);
+                               question->qDNSServer = GetServerForName(m, &question->qname, mDNSNULL);
                                ActivateUnicastQuery(m, question, mDNSfalse);
 
                                // If long-lived query, and we don't have our NAT mapping active, start it now
@@ -8208,7 +8343,11 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
        // Let the platform layer get the current DNS information
        // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand
        // (no need to hit the network with domain enumeration queries until we actually need that information).
-       for (ptr = m->DNSServers; ptr; ptr = ptr->next) ptr->flags |= DNSServer_FlagDelete;
+       for (ptr = m->DNSServers; ptr; ptr = ptr->next)
+               {
+               ptr->penaltyTime = 0;
+               ptr->flags |= DNSServer_FlagDelete;
+               }
 
        mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL);
 
@@ -8216,7 +8355,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
        for (q = m->Questions; q; q=q->next)
                if (!mDNSOpaque16IsZero(q->TargetQID))
                        {
-                       DNSServer *s = GetServerForName(m, &q->qname);
+                       DNSServer *s = GetServerForName(m, &q->qname, mDNSNULL);
                        DNSServer *t = q->qDNSServer;
                        if (t != s)
                                {
@@ -8234,7 +8373,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
        // Flush all records that match a new resolver
        FORALL_CACHERECORDS(slot, cg, cr)
                {
-               ptr = GetServerForName(m, cr->resrec.name);
+               ptr = GetServerForName(m, cr->resrec.name, mDNSNULL);
                if (ptr && (ptr->flags & DNSServer_FlagNew) && !cr->resrec.InterfaceID)
                        PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse);
                }
@@ -8249,7 +8388,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                        ptr = *p;
                        ptr->flags &= ~DNSServer_FlagDelete;    // Clear del so GetServerForName will (temporarily) find this server again before it's finally deleted
                        FORALL_CACHERECORDS(slot, cg, cr)
-                               if (!cr->resrec.InterfaceID && GetServerForName(m, cr->resrec.name) == ptr)
+                               if (!cr->resrec.InterfaceID && GetServerForName(m, cr->resrec.name, mDNSNULL) == ptr)
                                        PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue);
                        *p = (*p)->next;
                        debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
index ad0424c..8c3ea07 100755 (executable)
@@ -1209,6 +1209,7 @@ typedef struct DNSServer
        mDNSu32         teststate;      // Have we sent bug-detection query to this server?
        mDNSs32         lasttest;       // Time we sent last bug-detection query to this server
        domainname      domain;         // name->server matching for "split dns"
+       mDNSs32                 penaltyTime; // amount of time this server is penalized                 
        } DNSServer;
 
 typedef struct ExtraResourceRecord_struct ExtraResourceRecord;
@@ -1818,6 +1819,8 @@ extern const mDNSOpaque16 UpdateRespFlags;
 
 extern const mDNSOpaque64 zeroOpaque64;
 
+extern mDNSBool StrictUnicastOrdering;
+
 #define localdomain           (*(const domainname *)"\x5" "local")
 #define DeviceInfoName        (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp")
 #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp")
@@ -2047,7 +2050,7 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT
 
 extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m);
                
-extern DNSServer *GetServerForName(mDNS *m, const domainname *name);
+extern DNSServer *GetServerForName(mDNS *m, const domainname *name, DNSServer *current);
 
 // ***************************************************************************
 #if 0
@@ -2246,7 +2249,7 @@ extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCa
 extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
 extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAddr *v6addr, const mDNSAddr *router);
 extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port);
-extern void PushDNSServerToEnd(mDNS *const m, DNSQuestion *q);
+extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSBool QueryFail);
 extern void mDNS_AddSearchDomain(const domainname *const domain);
 
 // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
index 1e5a5ba..cf28a83 100755 (executable)
@@ -186,44 +186,110 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
                        (*p)->next = mDNSNULL;
                        }
                }
+       (*p)->penaltyTime = 0;
        return(*p);
        }
 
-mDNSexport void PushDNSServerToEnd(mDNS *const m, DNSQuestion *q)
+// PenalizeDNSServer is called when the number of queries to the unicast
+// DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an
+// error e.g., SERV_FAIL from DNS server. QueryFail is TRUE if this function
+// is called when we exceed MAX_UCAST_UNANSWERED_QUERIES
+
+mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSBool QueryFail)
        {
        DNSServer *orig = q->qDNSServer;
-       DNSServer **p = &m->DNSServers;
        
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
-               LogMsg("PushDNSServerToEnd: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+               LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
        if (!q->qDNSServer)
                {
-               LogMsg("PushDNSServerToEnd: Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries);
+               LogMsg("PenalizeDNSServer: Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries);
                goto end;
                }
 
-       LogInfo("PushDNSServerToEnd: Pushing DNS server %#a:%d (%##s) due to %d unanswered queries for %##s (%s)",
+       if (QueryFail)
+               {
+                       LogInfo("PenalizeDNSServer: DNS server %#a:%d (%##s) %d unanswered queries for %##s (%s)",
                &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype));
-
-       while (*p)
+               }
+       else
                {
-               if (*p == q->qDNSServer) *p = q->qDNSServer->next;
-               else p=&(*p)->next;
+                       LogInfo("PenalizeDNSServer: DNS server %#a:%d (%##s) Server Error for %##s (%s)",
+               &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->qname.c, DNSTypeName(q->qtype));
                }
 
-       *p = q->qDNSServer;
-       q->qDNSServer->next = mDNSNULL;
+
+       // If strict ordering of unicast servers needs to be preserved, we just lookup
+       // the next best match server below
+       //
+       // If strict ordering is not required which is the default behavior, we penalize the server
+       // for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR
+       // in the future.
+
+       if (!StrictUnicastOrdering)
+               {
+               LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE");
+               // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME
+               // XXX Include other logic here to see if this server should really be penalized
+               //
+               if (q->qtype == kDNSType_PTR)
+                       {
+                       LogInfo("PenalizeDNSServer: Not Penalizing PTR question");
+                       }
+               else
+                       {
+                       LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype);
+                       q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME);
+                       }
+               }
+       else
+               {
+               LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE");
+               }
 
 end:
-       q->qDNSServer = GetServerForName(m, &q->qname);
+       q->qDNSServer = GetServerForName(m, &q->qname, q->qDNSServer);
 
-       if (q->qDNSServer != orig)
+       if ((q->qDNSServer != orig) && (QueryFail))
                {
-               if (q->qDNSServer) LogInfo("PushDNSServerToEnd: Server for %##s (%s) changed to %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c);
-               else               LogInfo("PushDNSServerToEnd: Server for %##s (%s) changed to <null>",        q->qname.c, DNSTypeName(q->qtype));
-               q->ThisQInterval = q->ThisQInterval / QuestionIntervalStep; // Decrease interval one step so we don't quickly bounce between servers for queries that will not be answered.
+               // We picked a new server. In the case where QueryFail is true, the code has already incremented the interval
+               // and to compensate that we decrease it here.  When two queries are sent, the QuestionIntervalStep is at 9. We just
+               // move it back to 3 here when we pick a new server. We can't start at 1 because if we have two servers failing, we will never
+               // backoff 
+               //
+               q->ThisQInterval = q->ThisQInterval / QuestionIntervalStep;
+               if (q->qDNSServer) LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s), Question Interval %u", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->ThisQInterval);
+               else               LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to <null>, Question Interval %u",        q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval);
+
                }
+       else 
+               {
+               // if we are here it means,
+               //
+               // 1) We picked the same server, QueryFail = false
+               // 2) We picked the same server, QueryFail = true 
+               // 3) We picked a different server, QueryFail = false
+               //
+               // For all these three cases, ThisQInterval is already set properly
+
+               if (q->qDNSServer) 
+                       {
+                       if (q->qDNSServer != orig)
+                               {
+                               LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c);
+                               }
+                               else
+                               {
+                               LogInfo("PenalizeDNSServer: Server for %##s (%s) remains the same at %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c);
+                               }
+                       }
+               else
+                       { 
+                       LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to <null>",        q->qname.c, DNSTypeName(q->qtype));
+                       }
+               }
+       q->unansweredQueries = 0;
        }
 
 // ***************************************************************************
@@ -3407,8 +3473,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                        DNSServer *orig = q->qDNSServer;
                        if (orig) LogInfo("Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c);
 
-                       PushDNSServerToEnd(m, q);
-                       q->unansweredQueries = 0;
+                       PenalizeDNSServer(m, q, mDNStrue);
                        }
 
                if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled)
@@ -3696,6 +3761,40 @@ mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m)
        return nextevent;
        }
 
+// This function is called early on in mDNS_Execute before any uDNS questions are
+// dispatched so that if there are some good servers, the uDNS questions can now
+// use it
+mDNSexport void ResetDNSServerPenalties(mDNS *m)
+       {
+       DNSServer *d;
+       for (d = m->DNSServers; d; d=d->next)
+               {
+               if (d->penaltyTime != 0)
+                       {
+                       if (d->penaltyTime - m->timenow <= 0)
+                               {
+                               LogInfo("ResetDNSServerPenalties: DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port));
+                               d->penaltyTime = 0;
+                               }
+                       }
+               }
+       }
+
+mDNSlocal mDNSs32 CheckDNSServerPenalties(mDNS *m)
+       {
+       mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
+       DNSServer *d;
+       for (d = m->DNSServers; d; d=d->next)
+               {
+               if (d->penaltyTime != 0)
+                       {
+                       if ((nextevent - d->penaltyTime) > 0)
+                               nextevent = d->penaltyTime;
+                       }
+               }
+       return nextevent;
+       }
+
 mDNSexport void uDNS_Execute(mDNS *const m)
        {
        mDNSs32 nexte;
@@ -3715,6 +3814,9 @@ mDNSexport void uDNS_Execute(mDNS *const m)
 
        nexte = CheckServiceRegistrations(m);
        if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
+
+       nexte = CheckDNSServerPenalties(m);
+       if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
        }
 
 // ***************************************************************************
index 209f398..bf25b18 100755 (executable)
@@ -33,6 +33,7 @@
 #define LLQ_POLL_INTERVAL       (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc.
 #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond)         // require server responses within one minute of request
 #define MAX_UCAST_UNANSWERED_QUERIES 2                       // the number of unanswered queries from any one uDNS server before trying another server
+#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server
 
 #define DEFAULT_UPDATE_LEASE 7200
 
@@ -86,6 +87,7 @@ extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *
 
 // returns time of next scheduled event
 extern void uDNS_Execute(mDNS *const m);
+extern void ResetDNSServerPenalties(mDNS *m);
 
 extern mStatus         uDNS_SetupDNSConfig(mDNS *const m);
 extern mStatus         uDNS_RegisterSearchDomains(mDNS *const m);
index 9807666..0778e52 100644 (file)
@@ -100,6 +100,7 @@ static int restarting_via_mach_init = 0;    // Used on Jaguar/Panther when daemon i
 static int started_via_launchdaemon = 0;       // Indicates we're running on Tiger or later, where daemon is managed by launchd
 static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
 
+mDNSBool StrictUnicastOrdering = mDNSfalse;
 //*************************************************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
@@ -1779,8 +1780,9 @@ mDNSlocal void INFOCallback(void)
                for (s = mDNSStorage.DNSServers; s; s = s->next)
                        {
                        NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)s->interface;
-                       LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %s",
+                       LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s",
                                s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
+                               s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0,
                                s->teststate == DNSServer_Untested ? "(Untested)" :
                                s->teststate == DNSServer_Passed   ? ""           :
                                s->teststate == DNSServer_Failed   ? "(Failed)"   :
@@ -2502,6 +2504,7 @@ mDNSexport int main(int argc, char **argv)
                if (!strcasecmp(argv[i], "-UnicastPacketLogging"     )) mDNS_PacketLoggingEnabled = mDNStrue;
                if (!strcasecmp(argv[i], "-OfferSleepProxyService"   ))
                        OfferSleepProxyService = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 80;
+               if (!strcasecmp(argv[i], "-StrictUnicastOrdering"     )) StrictUnicastOrdering = mDNStrue;
                }
        
        // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure