+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;
+ }
+
+
+ debugf("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;
+ }
+
+
+//Checks to see whether the newname is a better match for the name, given the best one we have
+//seen so far (given in bestcount).
+//Returns -1 if the newname is not a better match
+//Returns 0 if the newname is the same as the old match
+//Returns 1 if the newname is a better match
+mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount,
+ int bestcount)
+ {
+ // If the name contains fewer labels than the new server's domain or the new name
+ // contains fewer labels than the current best, then it can't possibly be a better match
+ if (namecount < newcount || newcount < bestcount) return -1;
+
+ // If there is no match, return -1 and the caller will skip this newname for
+ // selection
+ //
+ // If we find a match and the number of labels is the same as bestcount, then
+ // we return 0 so that the caller can do additional logic to pick one of
+ // the best based on some other factors e.g., penaltyTime
+ //
+ // If we find a match and the number of labels is more than bestcount, then we
+ // return 1 so that the caller can pick this over the old one.
+ //
+ // NOTE: newcount can either be equal or greater than bestcount beause of the
+ // check above.
+
+ if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname))
+ return bestcount == newcount ? 0 : 1;
+ else
+ return -1;
+ }
+
+// 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 bestmatchlen = -1, namecount = name ? CountLabels(name) : 0;
+ DNSServer *curr;
+ mDNSs32 bestPenaltyTime;
+ int bettermatch;
+
+ bestmatchlen = -1;
+ bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1;
+ for (curr = m->DNSServers; curr; curr = curr->next)
+ {
+ int currcount = CountLabels(&curr->domain);
+ mDNSs32 currPenaltyTime = PenaltyTimeForServer(m, curr);
+
+ debugf("GetAnyBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d",
+ &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime);
+
+
+ // 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. BetterMatchForName walks through all best matches and
+ // "currPenaltyTime < bestPenaltyTime" 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))
+ {
+
+ bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen);
+
+ // If we found a better match (bettermatch == 1) then we don't need to
+ // compare penalty times. But if we found an equal match, then we compare
+ // the penalty times to pick a better match
+
+ if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime))
+ { curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime;}
+ }
+ }
+
+ return curmatch;
+ }
+