/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2017 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#endif
#include "dns_sd.h" // for kDNSServiceFlags* definitions
+#include "dns_sd_internal.h"
#if APPLE_OSX_mDNSResponder
#include <WebFilterDNS/WebFilterDNS.h>
#define NO_WCF 1
#endif // APPLE_OSX_mDNSResponder
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
#include "Metrics.h"
#endif
+#if USE_DNS64
+#include "DNS64.h"
+#endif
+
+#ifdef UNIT_TEST
+#include "unittest.h"
+#endif
+
// Forward declarations
mDNSlocal void BeginSleepProcessing(mDNS *const m);
mDNSlocal void RetrySPSRegistrations(mDNS *const m);
mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly);
-mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
-mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q);
mDNSlocal void mDNS_SendKeepalives(mDNS *const m);
mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth,
#define NR_AnswerMulticast (mDNSu8*)~0
#define NR_AnswerUnicast (mDNSu8*)~1
+// Question default timeout values
+#define DEFAULT_MCAST_TIMEOUT 5
+#define DEFAULT_LO_OR_P2P_TIMEOUT 5
+
// The code (see SendQueries() and BuildQuestion()) needs to have the
// RequestUnicast value set to a value one greater than the number of times you want the query
// sent with the "request unicast response" (QU) bit set.
return(e);
}
-mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 namehash, const domainname *const name)
{
AuthGroup *ag;
+ const mDNSu32 slot = namehash % AUTH_HASH_SLOTS;
+
for (ag = r->rrauth_hash[slot]; ag; ag=ag->next)
if (ag->namehash == namehash && SameDomainName(ag->name, name))
break;
return(ag);
}
-mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const ResourceRecord *const rr)
{
- return(AuthGroupForName(r, slot, rr->namehash, rr->name));
+ return(AuthGroupForName(r, rr->namehash, rr->name));
}
-mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const ResourceRecord *const rr)
{
mDNSu16 namelen = DomainNameLength(rr->name);
AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL);
+ const mDNSu32 slot = rr->namehash % AUTH_HASH_SLOTS;
if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
ag->next = r->rrauth_hash[slot];
ag->namehash = rr->namehash;
}
AssignDomainName(ag->name, rr->name);
- if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
+ if (AuthGroupForRecord(r, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
r->rrauth_hash[slot] = ag;
- if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
+ if (AuthGroupForRecord(r, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
return(ag);
}
// Returns the AuthGroup in which the AuthRecord was inserted
mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
{
- (void)m;
AuthGroup *ag;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- ag = AuthGroupForRecord(r, slot, &rr->resrec);
- if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now
+
+ (void)m;
+ ag = AuthGroupForRecord(r, &rr->resrec);
+ if (!ag) ag = GetAuthGroup(r, &rr->resrec); // If we don't have a AuthGroup for this name, make one now
if (ag)
{
*(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list
{
AuthGroup *a;
AuthRecord **rp;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- a = AuthGroupForRecord(r, slot, &rr->resrec);
+ a = AuthGroupForRecord(r, &rr->resrec);
if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; }
rp = &a->members;
while (*rp)
return a;
}
-mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 namehash, const domainname *const name)
{
CacheGroup *cg;
+ mDNSu32 slot = HashSlotFromNameHash(namehash);
for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
if (cg->namehash == namehash && SameDomainName(cg->name, name))
break;
return(cg);
}
-mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
+mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const ResourceRecord *const rr)
{
- return(CacheGroupForName(m, slot, rr->namehash, rr->name));
+ return(CacheGroupForName(m, rr->namehash, rr->name));
}
mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value
UDPSocket *sock = q->LocalSocket;
mDNSOpaque16 id = q->TargetQID;
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
uDNSMetrics metrics;
#endif
LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s",
q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr));
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
if ((q->CNAMEReferrals == 0) && !q->metrics.originalQName)
{
domainname * qName;
// Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
// because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
q->CNAMEReferrals = c;
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
q->metrics = metrics;
#endif
if (sock)
(X) &kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0)
#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
-#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
}
rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
}
- // Skip kDNSRecordTypeKnownUnique records here and set their LastAPTime in the "else" block below so that they get announced immediately,
- // otherwise, their announcement would be delayed until all other record probes complete.
- else if ((rr->resrec.RecordType != kDNSRecordTypeKnownUnique) && m->SuppressProbes && m->SuppressProbes - m->timenow >= 0)
+ // Skip kDNSRecordTypeKnownUnique and kDNSRecordTypeShared records here and set their LastAPTime in the "else" block below so
+ // that they get announced immediately, otherwise, their announcement would be delayed until the based on the SuppressProbes value.
+ else if ((rr->resrec.RecordType != kDNSRecordTypeKnownUnique) && (rr->resrec.RecordType != kDNSRecordTypeShared) && m->SuppressProbes && (m->SuppressProbes - m->timenow >= 0))
rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
else
rr->LastAPTime = m->timenow - rr->ThisAPInterval;
{
const AuthGroup *a;
AuthRecord *rp;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- a = AuthGroupForRecord(r, slot, &rr->resrec);
+ a = AuthGroupForRecord(r, &rr->resrec);
if (!a) return mDNSNULL;
rp = a->members;
while (rp)
{
const AuthGroup *a;
const AuthRecord *rp;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- a = AuthGroupForRecord(r, slot, &rr->resrec);
+ a = AuthGroupForRecord(r, &rr->resrec);
if (!a) return mDNSfalse;
rp = a->members;
while (rp)
{
const AuthGroup *a;
AuthRecord *rp;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- a = AuthGroupForRecord(r, slot, &rr->resrec);
+ a = AuthGroupForRecord(r, &rr->resrec);
if (!a) return mDNSNULL;
rp = a->members;
while (rp)
{
if (rr->resrec.RecordType == kDNSRecordTypeUnique)
rr->resrec.RecordType = kDNSRecordTypeVerified;
- else
+ else if (rr->resrec.RecordType != kDNSRecordTypeKnownUnique)
{
- LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
+ LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique or kDNSRecordTypeKnownUnique",
rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
return(mStatus_Invalid);
}
getKeepaliveRaddr(m, rr, &raddr);
// This is an asynchronous call. Once the remote MAC address is available, helper will schedule an
// asynchronous task to update the resource record
- mDNSPlatformGetRemoteMacAddr(m, &raddr);
+ mDNSPlatformGetRemoteMacAddr(&raddr);
}
return(mStatus_NoError);
{
AuthGroup *a;
AuthRecord **rp;
- const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
- a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+ a = AuthGroupForRecord(&m->rrauth, &rr->resrec);
if (!a) return mDNSfalse;
rp = &a->members;
while (*rp && *rp != rr) rp=&(*rp)->next;
debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
}
+mDNSlocal void AddRRSetAdditionalsToResponseList(mDNS *const m, AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *additional, const mDNSInterfaceID InterfaceID)
+{
+ AuthRecord *rr2;
+ if (additional->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ {
+ for (rr2 = m->ResourceRecords; rr2; rr2 = rr2->next)
+ {
+ if ((rr2->resrec.namehash == additional->resrec.namehash) &&
+ (rr2->resrec.rrtype == additional->resrec.rrtype) &&
+ (rr2 != additional) &&
+ (rr2->resrec.RecordType & kDNSRecordTypeUniqueMask) &&
+ (rr2->resrec.rrclass == additional->resrec.rrclass) &&
+ ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) &&
+ SameDomainName(rr2->resrec.name, additional->resrec.name))
+ {
+ AddRecordToResponseList(nrpp, rr2, rr);
+ }
+ }
+ }
+}
+
mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID)
{
AuthRecord *rr, *rr2;
// (Note: This is an "if", not a "while". If we add a record, we'll find it again
// later in the "for" loop, and we will follow further "additional" links then.)
if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID))
+ {
AddRecordToResponseList(nrpp, rr->Additional1, rr);
+ AddRRSetAdditionalsToResponseList(m, nrpp, rr, rr->Additional1, InterfaceID);
+ }
if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID))
+ {
AddRecordToResponseList(nrpp, rr->Additional2, rr);
+ AddRRSetAdditionalsToResponseList(m, nrpp, rr, rr->Additional2, InterfaceID);
+ }
// For SRV records, automatically add the Address record(s) for the target host
if (rr->resrec.rrtype == kDNSType_SRV)
mDNSs32 maxExistingAnnounceInterval = 0;
const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
- m->NextScheduledResponse = m->timenow + 0x78000000;
+ m->NextScheduledResponse = m->timenow + FutureTime;
if (m->SleepState == SleepState_Transferring) RetrySPSRegistrations(m);
verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s",
(rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr));
}
- ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr));
+ ScheduleNextCacheCheckTime(m, HashSlotFromNameHash(rr->resrec.namehash), NextCacheCheckEvent(rr));
}
#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
#define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5)
#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 5)
-#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 5)
+
+// Delay before restarting questions on a flapping interface.
+#define kDefaultQueryDelayTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 3)
+// After kDefaultQueryDelayTimeForFlappingInterface seconds, allow enough time for up to three queries (0, 1, and 4 seconds)
+// plus three seconds for "response delay" before removing the reconfirmed records from the cache.
+#define kDefaultReconfirmTimeForFlappingInterface (kDefaultQueryDelayTimeForFlappingInterface + ((mDNSu32)mDNSPlatformOneSecond * 7))
mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
{
// Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
// For all the reconfirmations in a given batch, we want to use the same random value
// so that the reconfirmation questions can be grouped into a single query packet
- if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF);
+ if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(FutureTime);
interval += m->RandomReconfirmDelay % ((interval/3) + 1);
rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond;
else
{
mDNSu32 forecast = *answerforecast + anoninfo_space;
- const mDNSu32 slot = HashSlot(&q->qname);
- const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ const CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
CacheRecord *rr;
CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
// to get a AAAA response is not grounds to doubt the PTR/SRV chain that lead us to that name.
mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const domainname *const name, const mDNSu32 namehash)
{
- CacheGroup *const cg = CacheGroupForName(m, HashSlot(name), namehash, name);
+ CacheGroup *const cg = CacheGroupForName(m, namehash, name);
const CacheRecord *cr = cg ? cg->members : mDNSNULL;
while (cr && !RRTypeIsAddressType(cr->resrec.rrtype)) cr=cr->next;
return(cr);
mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1)
{
#ifndef SPC_DISABLED
- CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname);
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
const CacheRecord *cr, *bestcr = mDNSNULL;
mDNSu32 bestmetric = 1000000;
for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i);
IPAddr[len - i] = 0;
m->mDNSStats.WakeOnResolves++;
- mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
+ mDNSPlatformSendWakeupPacket(InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
return;
}
else if (d->c[i] == ':')
{
// We forecast: qname (n) type (2) class (2)
mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
- const mDNSu32 slot = HashSlot(&q->qname);
- const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ const CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
const CacheRecord *rr;
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet
const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data);
// If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time
- if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+ if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(zeroIPPort);
if (q->LocalSocket)
{
InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
// Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list,
// which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very
// next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value.
- m->NextScheduledQuery = m->timenow + 0x78000000;
+ m->NextScheduledQuery = m->timenow + FutureTime;
for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
{
if (mDNSOpaque16IsZero(q->TargetQID)
// 2. Scan our authoritative RR list to see what probes we might need to send
- m->NextScheduledProbe = m->timenow + 0x78000000;
+ m->NextScheduledProbe = m->timenow + FutureTime;
if (m->CurrentRecord)
LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
return;
}
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname)
{
const domainname * queryName;
responseLatencyMs = 0;
}
- MetricsUpdateUDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular);
+ MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular);
q->metrics.answered = mDNStrue;
}
if (q->metrics.querySendCount > 0)
{
- MetricsUpdateUDNSResolveStats(queryName, &rr->resrec, isForCellular);
+ MetricsUpdateDNSResolveStats(queryName, &rr->resrec, isForCellular);
}
}
#endif
if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
+#if USE_DNS64
+ // If DNS64StateMachine() returns true, then the question was restarted as a different question, so return.
+ if (!mDNSOpaque16IsZero(q->TargetQID) && DNS64StateMachine(m, q, &rr->resrec, AddRecord)) return;
+#endif
+
#ifdef USE_LIBIDN
if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) // If negative answer, check if we need to try Punycode conversion
{
q->QuestionCallback(m, q, &neg.resrec, AddRecord);
}
else
- q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ {
+#if USE_DNS64
+ if (DNS64ShouldAnswerQuestion(q, &rr->resrec))
+ {
+ DNS64AnswerQuestion(m, q, &rr->resrec, AddRecord);
+ }
+ else
+#endif
+ {
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ }
+ }
mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
}
// If this is an "Add" operation and this question needs validation, validate the response.
m->CurrentQuestion = mDNSNULL;
}
-mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot, mDNSBool *purge)
+mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash)
{
- const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second
+ const mDNSs32 threshold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second
const mDNSs32 start = m->timenow - 0x10000000;
mDNSs32 delay = start;
- CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
+ CacheGroup *cg = CacheGroupForName(m, namehash, name);
const CacheRecord *rr;
- if (purge)
- *purge = mDNSfalse;
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
{
- // If there are records that will expire soon, there are cases that need delayed
- // delivery of events:
- //
- // 1) A new cache entry is about to be added as a replacement. The caller needs to
- // deliver a RMV (for the current old entry) followed by ADD (for the new entry).
- // It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime),
- // so that the cache entry can be purged (purging causes the RMV followed by ADD)
- //
- // 2) A new question is about to be answered and the caller needs to know whether it's
- // scheduling should be delayed so that the question is not answered with this record.
- // Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD
- // (new entry), a single ADD can be delivered by delaying the scheduling of the question
- // immediately.
- //
- // When the unicast cache record is created, it's TTL has been extended beyond its value
- // given in the resource record (See RRAdjustTTL). If it is in the "extended" time, the
- // cache is already expired and we set "purge" to indicate that. When "purge" is set, the
- // return value of the function should be ignored by the callers.
- //
- // Note: For case (1), "purge" argument is NULL and hence the following checks are skipped.
- // It is okay to skip in that case because the cache records have been set to expire almost
- // immediately and the extended time does not apply.
- //
- // Also, if there is already an active question we don't try to optimize as purging the cache
- // would end up delivering RMV for the active question and hence we avoid that.
-
- if (purge && !rr->resrec.InterfaceID && !rr->CRActiveQuestion && rr->resrec.rroriginalttl)
- {
- mDNSu32 uTTL = RRUnadjustedTTL(rr->resrec.rroriginalttl);
- if (m->timenow - (rr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0)
- {
- LogInfo("CheckForSoonToExpireRecords: %s: rroriginalttl %u, unadjustedTTL %u, currentTTL %u",
- CRDisplayString(m, rr), rr->resrec.rroriginalttl, uTTL, (m->timenow - rr->TimeRcvd)/mDNSPlatformOneSecond);
- *purge = mDNStrue;
- continue;
- }
- }
- if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second
+ if (threshold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second
{
if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted
delay = RRExpireTime(rr);
mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
{
CacheGroup *cg;
- const mDNSu32 slot = HashSlot(r->resrec.name);
//LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
r->resrec.rdata = mDNSNULL;
- cg = CacheGroupForRecord(m, slot, &r->resrec);
+ cg = CacheGroupForRecord(m, &r->resrec);
if (!cg)
{
m->NextScheduledQuery = m->timenow;
// After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTimeForRecord(),
// which will correctly update m->NextCacheCheck for us.
- event = m->timenow + 0x3FFFFFFF;
+ event = m->timenow + FutureTime;
}
}
}
// returns true to indicate the same.
mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDNSBool checkOnly)
{
- mDNSu32 slot;
AuthRecord *lr;
AuthGroup *ag;
if (m->CurrentRecord)
LogMsg("AnswerQuestionWithLORecord ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
- slot = AuthHashSlot(&q->qname);
- ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ ag = AuthGroupForName(&m->rrauth, q->qnamehash, &q->qname);
if (ag)
{
m->CurrentRecord = ag->members;
// reasons for suppressing the query, this function should be updated.
mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q)
{
- mDNSBool SuppressQuery = q->SuppressQuery;
- mDNSBool DisallowPID = q->DisallowPID;
+ mDNSBool SuppressQuery;
+ mDNSBool DisallowPID;
+
+ // If the client did not set the kDNSServiceFlagsReturnIntermediates flag, then don't generate a negative response, just
+ // deactivate the DNSQuestion.
+ if (!q->ReturnIntermed)
+ {
+ q->ThisQInterval = 0;
+ return;
+ }
+
+ SuppressQuery = q->SuppressQuery;
+ DisallowPID = q->DisallowPID;
// make sure that QuerySuppressed() returns false
q->SuppressQuery = mDNSfalse;
{
mDNSBool ShouldQueryImmediately = mDNStrue;
DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer
- mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+#if USE_DNS64
+ if (!mDNSOpaque16IsZero(q->TargetQID)) DNS64HandleNewQuestion(m, q);
+#endif
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
mDNSBool AnsweredFromCache = mDNSfalse;
verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- if (cg) CheckCacheExpiration(m, slot, cg);
+ if (cg) CheckCacheExpiration(m, HashSlotFromNameHash(q->qnamehash), cg);
if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; }
m->NewQuestions = q->next;
// Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first
// appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
{
- mDNSu32 slot;
AuthGroup *ag;
DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer
- m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any)
mDNSBool retEv = mDNSfalse;
+ m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any)
debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
// 1. First walk the LocalOnly records answering the LocalOnly question
// 2. As LocalOnly questions should also be answered by any other Auth records local to the machine,
// walk the ResourceRecords list delivering the answers
- slot = AuthHashSlot(&q->qname);
- ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ ag = AuthGroupForName(&m->rrauth, q->qnamehash, &q->qname);
if (ag)
{
m->CurrentRecord = ag->members;
}
AssignDomainName(cg->name, rr->name);
- if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c);
+ if (CacheGroupForRecord(m, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c);
m->rrcache_hash[slot] = cg;
- if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c);
+ if (CacheGroupForRecord(m, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c);
return(cg);
}
// had its Sleep Proxy client list change, and defer to actual BPF reconfiguration to mDNS_Execute().
// (GetNextScheduledEvent() returns "now" when m->SPSProxyListChanged is set)
#define SetSPSProxyListChanged(X) do { \
- if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \
+ if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m->SPSProxyListChanged); \
m->SPSProxyListChanged = (X); } while(0)
// Called from mDNS_Execute() to expire stale proxy records
}
}
-mDNSlocal void TimeoutQuestions(mDNS *const m)
+mDNSlocal void TimeoutQuestions_internal(mDNS *const m, DNSQuestion* questions, mDNSInterfaceID InterfaceID)
{
- m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF;
if (m->CurrentQuestion)
LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c,
DNSTypeName(m->CurrentQuestion->qtype));
- m->CurrentQuestion = m->Questions;
+ m->CurrentQuestion = questions;
while (m->CurrentQuestion)
{
DNSQuestion *const q = m->CurrentQuestion;
if (m->timenow - q->StopTime >= 0)
{
LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime);
- GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse);
+ q->LOAddressAnswers = 0; // unset since timing out the question
+ GenerateNegativeResponse(m, InterfaceID, QC_forceresponse);
if (m->CurrentQuestion == q) q->StopTime = 0;
}
else
m->CurrentQuestion = mDNSNULL;
}
+mDNSlocal void TimeoutQuestions(mDNS *const m)
+{
+ m->NextScheduledStopTime = m->timenow + FutureTime; // push reschedule of TimeoutQuestions to way off into the future
+ TimeoutQuestions_internal(m, m->Questions, mDNSInterface_Any);
+ TimeoutQuestions_internal(m, m->LocalOnlyQuestions, mDNSInterface_LocalOnly);
+}
+
mDNSlocal void mDNSCoreFreeProxyRR(mDNS *const m)
{
AuthRecord *rrPtr = m->SPSRRSet, *rrNext = mDNSNULL;
{
mDNS_Lock(m); // Must grab lock before trying to read m->timenow
-#if APPLE_OSX_mDNSResponder
- mDNSLogStatistics(m);
-#endif // APPLE_OSX_mDNSResponder
-
if (m->timenow - m->NextScheduledEvent >= 0)
{
int i;
if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
{
mDNSu32 numchecked = 0;
- m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
+ m->NextCacheCheck = m->timenow + FutureTime;
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
{
if (m->timenow - m->rrcache_nextcheck[slot] >= 0)
{
CacheGroup **cp = &m->rrcache_hash[slot];
- m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF;
+ m->rrcache_nextcheck[slot] = m->timenow + FutureTime;
while (*cp)
{
debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL");
if (m->timenow - m->NextScheduledSPS >= 0)
{
- m->NextScheduledSPS = m->timenow + 0x3FFFFFFF;
+ m->NextScheduledSPS = m->timenow + FutureTime;
CheckProxyRecords(m, m->DuplicateRecords); // Clear m->DuplicateRecords first, then m->ResourceRecords
CheckProxyRecords(m, m->ResourceRecords);
}
// as records could have expired during that check
if (m->timenow - m->NextScheduledKA >= 0)
{
- m->NextScheduledKA = m->timenow + 0x3FFFFFFF;
+ m->NextScheduledKA = m->timenow + FutureTime;
mDNS_SendKeepalives(m);
}
m->RandomQueryDelay = 0;
m->RandomReconfirmDelay = 0;
+ // See if any questions (or local-only questions) have timed out
if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m);
#ifndef UNICAST_DISABLED
if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m);
if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m);
if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m);
#endif
-#if APPLE_OSX_mDNSResponder
+#if APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR
extern void serviceBLE();
if (m->NextBLEServiceTime && (m->timenow - m->NextBLEServiceTime >= 0)) serviceBLE();
-#endif // APPLE_OSX_mDNSResponder
+#endif // APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR
}
// Note about multi-threaded systems:
mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
{
AuthRecord *rr;
- mDNSu32 slot;
AuthGroup *ag;
- slot = AuthHashSlot(&q->qname);
- ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ ag = AuthGroupForName(&m->rrauth, q->qnamehash, &q->qname);
if (ag)
{
for (rr = ag->members; rr; rr=rr->next)
!SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback)
{
question->NoAnswer = NoAnswer_Suspended;
- AddNewClientTunnel(m, question);
+ AddNewClientTunnel(question);
return;
}
#endif // APPLE_OSX_mDNSResponder
}
// Call the platform code to enable/disable sleep
- mDNSPlatformSetAllowSleep(m, allowSleep, reason);
+ mDNSPlatformSetAllowSleep(allowSleep, reason);
#else
(void) m;
#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
// now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees
// this DNS NULL record, it does not send any keepalives as it does not have all the information
mDNSPlatformMemZero(&mti, sizeof (mDNSTCPInfo));
- ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti);
+ ret = mDNSPlatformRetrieveTCPInfo(&laddr, &lport, &raddr, &rport, &mti);
if (ret != mStatus_NoError)
{
LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret);
m->NextScheduledSPRetry = m->timenow;
+ // Clear out the SCDynamic entry that stores the external SPS information
+ mDNSPlatformClearSPSData();
+
if (!m->SystemWakeOnLANEnabled) LogSPS("BeginSleepProcessing: m->SystemWakeOnLANEnabled is false");
else if (!mDNSCoreHaveAdvertisedMulticastServices(m)) LogSPS("BeginSleepProcessing: No advertised services");
else // If we have at least one advertised service
{
NetworkInterfaceInfo *intf;
-
- // Clear out the SCDynamic entry that stores the external SPS information
- mDNSPlatformClearSPSData();
-
for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
{
// Intialize it to false. These values make sense only when SleepState is set to Sleeping.
else if (SupportsInNICProxy(intf))
{
mDNSBool keepaliveOnly = mDNSfalse;
- if (ActivateLocalProxy(m, intf, &keepaliveOnly) == mStatus_NoError)
+ if (ActivateLocalProxy(intf, &keepaliveOnly) == mStatus_NoError)
{
SendGoodbyesForWakeOnlyService(m, &WakeOnlyService);
#endif
mDNS_ReclaimLockAfterCallback();
}
-
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ if (m->SSDPSocket)
+ {
+ mDNSPlatformUDPClose(m->SSDPSocket);
+ m->SSDPSocket = mDNSNULL;
+ }
+#endif
m->SleepState = SleepState_Transferring;
if (m->SystemWakeOnLANEnabled && m->DelaySleep)
{
// 2. Re-validate our cache records
currtime = mDNSPlatformUTC();
-#if APPLE_OSX_mDNSResponder
- // start time of this statistics gathering interval
- m->StatStartTime = currtime;
-#endif // APPLE_OSX_mDNSResponder
-
diff = currtime - m->TimeSlept;
FORALL_CACHERECORDS(slot, cg, cr)
{
if (rr->state == regState_Refresh && rr->tcp)
{ LogSPS("mDNSCoreReadyForSleep: waiting for Record updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; }
#if APPLE_OSX_mDNSResponder
- if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; }
+ if (!RecordReadyForSleep(rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; }
#endif
}
mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const ResourceRecord *const pktrr)
{
- mDNSu32 slot = HashSlot(pktrr->name);
- CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
+ CacheGroup *cg = CacheGroupForRecord(m, pktrr);
CacheRecord *rr;
mDNSBool match;
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
// We only mark this question for sending if it is at least one second since the last time we multicast it
// on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
// This is to guard against the case where someone blasts us with queries as fast as they can.
- if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
+ if ((mDNSu32)(m->timenow - rr->LastMCTime) >= (mDNSu32)mDNSPlatformOneSecond ||
(rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
rr->NR_AnswerTo = NR_AnswerMulticast;
}
if (query->h.flags.b[0] & kDNSFlag0_TC)
m->mDNSStats.KnownAnswerMultiplePkts++;
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- if (QuestionNeedsMulticastResponse)
-#else
// We only do the following accelerated cache expiration and duplicate question suppression processing
// for non-truncated multicast queries with multicast responses.
// For any query generating a unicast response we don't do this because we can't assume we will see the response.
// For truncated queries we don't do this because a response we're expecting might be suppressed by a subsequent
// known-answer packet, and when there's packet loss we can't safely assume we'll receive *all* known-answer packets.
if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC))
-#endif
{
#if POOF_ENABLED
- const mDNSu32 slot = HashSlot(&pktq.qname);
- CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
+ CacheGroup *cg = CacheGroupForName(m, pktq.qnamehash, &pktq.qname);
CacheRecord *cr;
// Make a list indicating which of our own cache records we expect to see updated as a result of this query
// Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- if (!(query->h.flags.b[0] & kDNSFlag0_TC))
-#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit)
if (!cr->NextInKAList && eap != &cr->NextInKAList)
{
*eap = cr;
eap = &cr->NextInKAList;
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- if (cr->MPUnansweredQ == 0 || m->timenow - cr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
- {
- // Although MPUnansweredQ is only really used for multi-packet query processing,
- // we increment it for both single-packet and multi-packet queries, so that it stays in sync
- // with the MPUnansweredKA value, which by necessity is incremented for both query types.
- cr->MPUnansweredQ++;
- cr->MPLastUnansweredQT = m->timenow;
- cr->MPExpectingKA = mDNStrue;
- }
-#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
}
#endif // POOF_ENABLED
// We only do this for non-truncated queries. Right now it would be too complicated to try
// to keep track of duplicate suppression state between multiple packets, especially when we
// can't guarantee to receive all of the Known Answer packets that go with a particular query.
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- if (!(query->h.flags.b[0] & kDNSFlag0_TC))
-#endif
// For anonymous question, the duplicate suppressesion should happen if the
// question belongs in the same group. As the group is expected to be
// small, we don't do the optimization for now.
ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
- #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
- // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list).
- if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
- {
- ourcacherr->MPUnansweredKA++;
- ourcacherr->MPExpectingKA = mDNSfalse;
- }
- #endif
-
#if POOF_ENABLED
// Having built our ExpectedAnswers list from the questions in this packet, we then remove
// any records that are suppressed by the Known Answer list in this packet.
mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response
mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response)
-#if !TARGET_OS_EMBEDDED
- // always honor kDNSQClass_UnicastResponse in embedded environment to increase reliability
- // in high multicast packet loss environments.
-
// If it's been one TTL/4 since we multicast this, then send a multicast response
// for conflict detection, etc.
- if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
+ if ((mDNSu32)(m->timenow - rr->LastMCTime) >= (mDNSu32)TicksTTL(rr)/4)
{
SendMulticastResponse = mDNStrue;
// If this record was marked for modern (delayed) unicast response, then mark it as promoted to
rr->NR_AnswerTo = NR_AnswerMulticast;
}
}
-#endif // !TARGET_OS_EMBEDDED
// If the client insists on a multicast response, then we'd better send one
if (rr->NR_AnswerTo == NR_AnswerMulticast)
cr->UnansweredQueries++;
cr->LastUnansweredTime = m->timenow;
if (cr->UnansweredQueries > 1)
- #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
- cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
- #else
debugf("ProcessQuery: UnansweredQueries %lu %s", cr->UnansweredQueries, CRDisplayString(m, cr));
- #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
SetNextCacheCheckTimeForRecord(m, cr);
}
{
// Only show debugging message if this record was not about to expire anyway
if (RRExpireTime(cr) - m->timenow > (mDNSs32) kDefaultReconfirmTimeForNoAnswer * 4 / 3 + mDNSPlatformOneSecond)
- #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
- cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
- #else
- LogInfo("ProcessQuery: UnansweredQueries %lu TTL %lu mDNS_Reconfirm() for %s",
- cr->UnansweredQueries, (RRExpireTime(cr) - m->timenow + mDNSPlatformOneSecond-1) / mDNSPlatformOneSecond, CRDisplayString(m, cr));
- #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
+ LogInfo("ProcessQuery: UnansweredQueries %lu interface %lu TTL %lu mDNS_Reconfirm() for %s",
+ cr->UnansweredQueries, InterfaceID, (RRExpireTime(cr) - m->timenow + mDNSPlatformOneSecond-1) / mDNSPlatformOneSecond, CRDisplayString(m, cr));
m->mDNSStats.PoofCacheDeletions++;
mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
}
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- // Make a guess, based on the multi-packet query / known answer counts, whether we think we
- // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
- // possible packet loss of up to 20% of the additional KA packets.)
- else if (cr->MPUnansweredQ * 4 > cr->MPUnansweredKA * 5 + 8)
- {
- // We want to do this conservatively.
- // If there are so many machines on the network that they have to use multi-packet known-answer lists,
- // then we don't want them to all hit the network simultaneously with their final expiration queries.
- // By setting the record to expire in four minutes, we achieve two things:
- // (a) the 90-95% final expiration queries will be less bunched together
- // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
- mDNSu32 remain = (mDNSu32)(RRExpireTime(cr) - m->timenow) / 4;
- if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond)
- remain = 240 * (mDNSu32)mDNSPlatformOneSecond;
-
- // Only show debugging message if this record was not about to expire anyway
- if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond)
- debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
- cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
-
- if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
- cr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query
- cr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics
- cr->MPUnansweredKA = 0;
- cr->MPExpectingKA = mDNSfalse;
-
- if (remain < kDefaultReconfirmTimeForNoAnswer)
- remain = kDefaultReconfirmTimeForNoAnswer;
- mDNS_Reconfirm_internal(m, cr, remain);
- }
-#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
}
#endif // POOF_ENABLED
rr->TimeRcvd = m->timenow;
rr->resrec.rroriginalttl = ttl;
rr->UnansweredQueries = 0;
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- rr->MPUnansweredQ = 0;
- rr->MPUnansweredKA = 0;
- rr->MPExpectingKA = mDNSfalse;
-#endif
SetNextCacheCheckTimeForRecord(m, rr);
}
mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease)
{
CacheRecord *rr;
- const mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ CacheGroup *cg = CacheGroupForName(m, q->qnamehash, &q->qname);
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
if (rr->CRActiveQuestion == q)
{
if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
{
CacheRecord *rr, *neg = mDNSNULL;
- mDNSu32 slot = HashSlot(&q.qname);
- CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+ CacheGroup *cg = CacheGroupForName(m, q.qnamehash, &q.qname);
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
{
ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA)
{
- const mDNSu32 s = HashSlot(m->rec.r.resrec.name);
- CacheGroup *cgSOA = CacheGroupForRecord(m, s, &m->rec.r.resrec);
+ CacheGroup *cgSOA = CacheGroupForRecord(m, &m->rec.r.resrec);
const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data;
mDNSu32 ttl_s = soa->min;
// We use the lesser of the SOA.MIN field and the SOA record's TTL, *except*
// Create the SOA record as we may have to return this to the questions
// that we are acting as a proxy for currently or in the future.
- SOARecord = CreateNewCacheEntry(m, s, cgSOA, 1, mDNSfalse, mDNSNULL);
+ SOARecord = CreateNewCacheEntry(m, HashSlotFromNameHash(m->rec.r.resrec.namehash), cgSOA, 1, mDNSfalse, mDNSNULL);
// Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
// with an Authority Section SOA record for d.com, then this is a hint that the authority
// been NULL. If we pass NULL cg to new cache entries that we create below,
// it will create additional cache groups for the same name. To avoid that,
// look up the cache group again to re-initialize cg again.
- cg = CacheGroupForName(m, slot, hash, name);
+ cg = CacheGroupForName(m, hash, name);
if (NSECRecords && DNSSECQuestion(qptr))
{
// Create the cache entry with delay and then add the NSEC records
// to it and add it immediately.
- negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL);
+ negcr = CreateNewCacheEntry(m, HashSlotFromNameHash(hash), cg, 1, mDNStrue, mDNSNULL);
if (negcr)
{
negcr->CRDNSSECQuestion = 0;
else
{
// Need to add with a delay so that we can tag the SOA record
- negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL);
+ negcr = CreateNewCacheEntry(m, HashSlotFromNameHash(hash), cg, 1, mDNStrue, mDNSNULL);
if (negcr)
{
negcr->CRDNSSECQuestion = 0;
repeat--;
name = (const domainname *)(name->c + 1 + name->c[0]);
hash = DomainNameHashValue(name);
- slot = HashSlot(name);
- // For now, we don't need to update cg here, because we'll do it again immediately, back up at the start of this loop
- //cg = CacheGroupForName(m, slot, hash, name);
}
}
}
}
for (i = 0; i < response->h.numAuthorities && ptr && ptr < end; i++)
{
- mDNSu32 slot;
CacheGroup *cg;
ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
m->rec.r.resrec.RecordType = 0;
continue;
}
- slot = HashSlot(m->rec.r.resrec.name);
- cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
+ cg = CacheGroupForRecord(m, &m->rec.r.resrec);
// Create the cache entry but don't add it to the cache it. We need
// to cache this along with the main cache record.
- rr = CreateNewCacheEntry(m, slot, cg, 0, mDNSfalse, mDNSNULL);
+ rr = CreateNewCacheEntry(m, HashSlotFromNameHash(m->rec.r.resrec.namehash), cg, 0, mDNSfalse, mDNSNULL);
if (rr)
{
debugf("mDNSParseNSEC3Records: %s", CRDisplayString(m, rr));
response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
response->h.numAdditionals, response->h.numAdditionals == 1 ? " " : "s", end - response->data, LLQType);
+#if AWD_METRICS
+ if (mDNSSameIPPort(srcport, UnicastDNSPort))
+ {
+ MetricsUpdateDNSResponseSize((mDNSu32)(end - (mDNSu8 *)response));
+ }
+#endif
+
// According to RFC 2181 <http://www.ietf.org/rfc/rfc2181.txt>
// When a DNS client receives a reply with TC
// set, it should ignore that response, and query again, using a
// abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache.
// Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already,
// and not even do the TCP query.
- // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet.
- if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return;
+ // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the
+ // entire RRSet, with the following exception. If the response contains an answer section and one or more records in
+ // either the authority section or additional section, then that implies that truncation occurred beyond the answer
+ // section, and the answer section is therefore assumed to be complete.
+ //
+ // From section 6.2 of RFC 1035 <https://tools.ietf.org/html/rfc1035>:
+ // When a response is so long that truncation is required, the truncation
+ // should start at the end of the response and work forward in the
+ // datagram. Thus if there is any data for the authority section, the
+ // answer section is guaranteed to be unique.
+ if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC) &&
+ ((response->h.numAnswers == 0) || ((response->h.numAuthorities == 0) && (response->h.numAdditionals == 0)))) return;
if (LLQType == uDNS_LLQ_Ignore) return;
// 1. We ignore questions (if any) in mDNS response packets
// 2. If this is an LLQ response, we handle it much the same
- // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this
- // answer as being the authoritative complete RRSet, and respond by deleting all other
- // matching cache records that don't appear in this packet.
// Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged
- if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC))
+ if (ResponseMCast || LLQType == uDNS_LLQ_Events)
ptr = LocateAnswers(response, end);
// Otherwise, for one-shot queries, any answers in our cache that are not also contained
// in this response packet are immediately deemed to be invalid.
CacheRecord *rr;
// Remember the unicast question that we found, which we use to make caching
// decisions later on in this function
- const mDNSu32 slot = HashSlot(&q.qname);
- CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+ CacheGroup *cg = CacheGroupForName(m, q.qnamehash, &q.qname);
if (!mDNSOpaque16IsZero(response->h.id))
{
unicastQuestion = qptr;
// have any record(s) of the same type that we should re-assert to rescue them
// (see note about "multi-homing and bridged networks" at the end of this function).
else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype)
- if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
+ if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (mDNSu32)(m->timenow - rr->LastMCTime) > (mDNSu32)mDNSPlatformOneSecond/2)
{ rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
}
}
if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r));
if (m->rrcache_size && AcceptableResponse)
{
- const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
- CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
+ const mDNSu32 slot = HashSlotFromNameHash(m->rec.r.resrec.namehash);
+ CacheGroup *cg = CacheGroupForRecord(m, &m->rec.r.resrec);
CacheRecord *rr = mDNSNULL;
if (McastNSEC3Records)
if (AddToCFList)
delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond);
else
- delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot, mDNSNULL);
+ delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash);
// If unique, assume we may have to delay delivery of this 'add' event.
// Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd()
while (CacheFlushRecords != (CacheRecord*)1)
{
CacheRecord *r1 = CacheFlushRecords, *r2;
- const mDNSu32 slot = HashSlot(r1->resrec.name);
- const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
+ const mDNSu32 slot = HashSlotFromNameHash(r1->resrec.namehash);
+ const CacheGroup *cg = CacheGroupForRecord(m, &r1->resrec);
+ mDNSBool purgedRecords = mDNSfalse;
CacheFlushRecords = CacheFlushRecords->NextInCFList;
r1->NextInCFList = mDNSNULL;
r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
}
r2->TimeRcvd = m->timenow;
+ SetNextCacheCheckTimeForRecord(m, r2);
}
- else // else, if record is old, mark it to be flushed
+ else if (r2->resrec.InterfaceID) // else, if record is old, mark it to be flushed
{
verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1));
verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2));
// We use (m->timenow - 1) instead of m->timenow, because we use that to identify records
// that we marked for deletion via an explicit DE record
}
+ SetNextCacheCheckTimeForRecord(m, r2);
+ }
+ else
+ {
+ // Old uDNS records are scheduled to be purged instead of given at most one second to live.
+ mDNS_PurgeCacheResourceRecord(m, r2);
+ purgedRecords = mDNStrue;
}
- SetNextCacheCheckTimeForRecord(m, r2);
}
}
NSECRecords = mDNSNULL;
NSECCachePtr = mDNSNULL;
}
- r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot, mDNSNULL);
+ if (r1->resrec.InterfaceID)
+ {
+ r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash);
+ }
+ else
+ {
+ // If uDNS records from an older RRset were scheduled to be purged, then delay delivery slightly to allow
+ // them to be deleted before any ADD events for this record.
+ r1->DelayDelivery = purgedRecords ? NonZeroTime(m->timenow) : 0;
+ }
// If no longer delaying, deliver answer now, else schedule delivery for the appropriate time
if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery);
mDNSu8 *end = mDNSNULL;
mDNSu32 length = 0;
AuthRecord opt;
+ NetworkInterfaceInfo *intf;
mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt.resrec.rrclass = NormalMaxDNSMessageData;
opt.resrec.rdlength = sizeof(rdataOPT);
opt.resrec.rdestimate = sizeof(rdataOPT);
- NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+ intf = FirstInterfaceForID(m, InterfaceID);
SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
LogSPS("Generated OPT record : %s", ARDisplayString(m, &opt));
return length;
}
+// Note that this routine is called both for Sleep Proxy Registrations, and for Standard Dynamic
+// DNS registrations, but (currently) only has to handle the Sleep Proxy Registration reply case,
+// and should ignore Standard Dynamic DNS registration replies, because those are handled elsewhere.
+// Really, both should be unified and handled in one place.
mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
{
if (InterfaceID)
{
- mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour
- const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
- mDNSAddr spsaddr;
- char *ifname;
- if (ptr)
- {
- ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
- if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
- {
- const rdataOPT *o;
- const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
- for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
- if (o->opt == kDNSOpt_Lease)
- {
- updatelease = o->u.updatelease;
- LogSPS("Sleep Proxy granted lease time %4d seconds, updateid %d, InterfaceID %p", updatelease, mDNSVal16(msg->h.id), InterfaceID);
- }
- }
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- }
+ mDNSu32 pktlease = 0, spsupdates = 0;
+ const mDNSBool gotlease = GetPktLease(m, msg, end, &pktlease);
+ const mDNSu32 updatelease = gotlease ? pktlease : 60 * 60; // If SPS fails to indicate lease time, assume one hour
+ if (gotlease) LogSPS("DNS Update response contains lease option granting %4d seconds, updateid %d, InterfaceID %p", updatelease, mDNSVal16(msg->h.id), InterfaceID);
if (m->CurrentRecord)
LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
if (mDNSOpaque64IsZero(&rr->updateIntID))
rr->updateid = zeroID;
rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond);
- LogSPS("Sleep Proxy %s record %5d 0x%x 0x%x (%d) %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr));
+ spsupdates++;
+ LogSPS("Sleep Proxy %s record %2d %5d 0x%x 0x%x (%d) %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", spsupdates, updatelease, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr));
if (rr->WakeUp.HMAC.l[0])
{
rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
m->CurrentRecord = rr->next;
}
-
- // Update the dynamic store with the IP Address and MAC address of the sleep proxy
- ifname = InterfaceNameForID(m, InterfaceID);
- mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr));
- mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname);
-
- // Store the Owner OPT record for this interface.
- // Configd may use the OPT record if it detects a conflict with the BSP when the system wakes up
- DNSMessage optMsg;
- int length = 0;
- InitializeDNSMessage(&optMsg.h, zeroID, ResponseFlags);
- length = mDNSGenerateOwnerOptForInterface(m, InterfaceID, &optMsg);
- if (length != 0)
+ if (spsupdates) // Only do this dynamic store stuff if this was, in fact, a Sleep Proxy Update response
{
- length += sizeof(DNSMessageHeader);
- mDNSPlatformStoreOwnerOptRecord(ifname, &optMsg, length);
+ char *ifname;
+ mDNSAddr spsaddr;
+ DNSMessage optMsg;
+ int length;
+ // Update the dynamic store with the IP Address and MAC address of the sleep proxy
+ ifname = InterfaceNameForID(m, InterfaceID);
+ mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr));
+ mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname);
+
+ // Store the Owner OPT record for this interface.
+ // Configd may use the OPT record if it detects a conflict with the BSP when the system wakes up
+ InitializeDNSMessage(&optMsg.h, zeroID, ResponseFlags);
+ length = mDNSGenerateOwnerOptForInterface(m, InterfaceID, &optMsg);
+ if (length != 0)
+ {
+ length += sizeof(DNSMessageHeader);
+ mDNSPlatformStoreOwnerOptRecord(ifname, &optMsg, length);
+ }
}
}
// If we were waiting to go to sleep, then this SPS registration or wide-area record deletion
cr->CRActiveQuestion = mDNSNULL;
cr->UnansweredQueries = 0;
cr->LastUnansweredTime = 0;
-#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
- cr->MPUnansweredQ = 0;
- cr->MPLastUnansweredQT = 0;
- cr->MPUnansweredKA = 0;
- cr->MPExpectingKA = mDNSfalse;
-#endif
cr->NextInCFList = mDNSNULL;
cr->nsec = mDNSNULL;
cr->soa = mDNSNULL;
// Track the number of multicast packets received from a source outside our subnet.
// Check the destination address to avoid accounting for spurious packets that
// comes in with message id zero.
- if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr) &&
+ if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr) && dstaddr &&
mDNSAddressIsAllDNSLinkGroup(dstaddr))
{
m->RemoteSubnet++;
{
static int msgCount = 0;
if (msgCount < 1000) {
- msgCount++;
int i = 0;
+ msgCount++;
LogInfo("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)",
msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - pkt), InterfaceID);
while (i < (int)(end - pkt))
#define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
(mDNSSameAddress(& (A)->Target, & (B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
+// SameQuestionKind is true if *both* questions are either multicast or unicast
+// TargetQID is used for this determination.
+#define SameQuestionKind(A,B) ((mDNSOpaque16IsZero(A) && mDNSOpaque16IsZero(B)) || \
+ ((!mDNSOpaque16IsZero(A)) && (!mDNSOpaque16IsZero(B))))
+
// Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the
// circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV"
// and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails
(q->DisallowPID == question->DisallowPID) && // Disallowing a PID should not affect a PID that is allowed
(q->BrowseThreshold == question->BrowseThreshold) && // browse thresholds must match
q->qnamehash == question->qnamehash &&
- (IsAWDLIncluded(q) == IsAWDLIncluded(question)) && // Inclusion of AWDL interface must match
- SameDomainName(&q->qname, &question->qname)) // and name
+ (IsAWDLIncluded(q) == IsAWDLIncluded(question)) && // Inclusion of AWDL interface must match
+ SameQuestionKind(q->TargetQID, question->TargetQID) && // mDNS or uDNS must match
+ SameDomainName(&q->qname, &question->qname)) // and name
return(q);
return(mDNSNULL);
}
mDNSu32 timeout = 0;
mDNSBool DEQuery;
- question->validDNSServers = zeroOpaque64;
+ question->validDNSServers = zeroOpaque128;
DEQuery = DomainEnumQuery(&question->qname);
for (curr = m->DNSServers; curr; curr = curr->next)
{
currcount = CountLabels(&curr->domain);
if ((!curr->cellIntf || (!DEQuery && !(question->flags & kDNSServiceFlagsDenyCellular))) &&
+ (!curr->isExpensive || !(question->flags & kDNSServiceFlagsDenyExpensive)) &&
DNSServerMatch(curr, question->InterfaceID, question->ServiceID))
{
bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
if (bettermatch)
{
debugf("SetValidDNSServers: Resetting all the bits");
- question->validDNSServers = zeroOpaque64;
+ question->validDNSServers = zeroOpaque128;
timeout = 0;
}
debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
timeout += curr->timeout;
if (DEQuery)
debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf);
- bit_set_opaque64(question->validDNSServers, index);
+ bit_set_opaque128(question->validDNSServers, index);
}
}
index++;
}
question->noServerResponse = 0;
- debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)",
- question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype));
+ debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x%x%x for question %p %##s (%s)",
+ question->validDNSServers.l[3], question->validDNSServers.l[2], question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype));
// If there are no matching resolvers, then use the default timeout value.
// For ProxyQuestion, shorten the timeout so that dig does not timeout on us in case of no response.
return ((question->ProxyQuestion || question->ValidatingResponse) ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
// 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 *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID, mDNSOpaque64 validBits,
+mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID, mDNSOpaque128 validBits,
int *selected, mDNSBool nameMatch)
{
DNSServer *curmatch = mDNSNULL;
{
DNSServer *curmatch = mDNSNULL;
char *ifname = mDNSNULL; // for logging purposes only
- mDNSOpaque64 allValid;
+ mDNSOpaque128 allValid;
if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly))
InterfaceID = mDNSNULL;
if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID);
// By passing in all ones, we make sure that every DNS server is considered
- allValid.l[0] = allValid.l[1] = 0xFFFFFFFF;
+ allValid.l[0] = allValid.l[1] = allValid.l[2] = allValid.l[3] = 0xFFFFFFFF;
curmatch = GetBestServer(m, name, InterfaceID, ServiceID, allValid, mDNSNULL, mDNStrue);
if (InterfaceID)
ifname = InterfaceNameForID(m, InterfaceID);
- if (!mDNSOpaque64IsZero(&question->validDNSServers))
+ if (!mDNSOpaque128IsZero(&question->validDNSServers))
{
curmatch = GetBestServer(m, name, InterfaceID, question->ServiceID, question->validDNSServers, &currindex, mDNSfalse);
if (currindex != -1)
- bit_clr_opaque64(question->validDNSServers, currindex);
+ bit_clr_opaque128(question->validDNSServers, currindex);
}
if (curmatch != mDNSNULL)
DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port));
return mDNSfalse;
}
+#if USE_DNS64
+ if (DNS64IsQueryingARecord(q->dns64.state))
+ {
+ LogInfo("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype));
+ return mDNSfalse;
+ }
+#endif
LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, since DNS Configuration does not allow (req_A is %s and req_AAAA is %s)",
q->qname.c, DNSTypeName(q->qtype), d->req_A ? "true" : "false", d->req_AAAA ? "true" : "false");
mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
{
CacheRecord *rr;
- mDNSu32 slot;
CacheGroup *cg;
- slot = HashSlot(&q->qname);
- cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ cg = CacheGroupForName(m, q->qnamehash, &q->qname);
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
{
// Don't deliver RMV events for negative records
mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
{
AuthRecord *rr;
- mDNSu32 slot;
AuthGroup *ag;
if (m->CurrentQuestion)
return mDNStrue;
}
m->CurrentQuestion = q;
- slot = AuthHashSlot(&q->qname);
- ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ ag = AuthGroupForName(&m->rrauth, q->qnamehash, &q->qname);
if (ag)
{
for (rr = ag->members; rr; rr=rr->next)
// Returns false if the question got deleted while delivering the RMV events
// The caller should handle the case
-mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+mDNSexport mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
{
if (m->CurrentQuestion)
LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
}
// InitDNSConfig() is called by InitCommonState() to initialize the DNS configuration of the Question.
-// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeForQuestion()
+// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeBeforeResolve()
mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question)
{
// First reset all DNS Configuration
question->qDNSServer = mDNSNULL;
- question->validDNSServers = zeroOpaque64;
+ question->validDNSServers = zeroOpaque128;
question->triedAllServersOnce = 0;
question->noServerResponse = 0;
- question->StopTime = 0;
-#if TARGET_OS_EMBEDDED
+ question->StopTime = (question->TimeoutQuestion) ? question->StopTime : 0;
+#if AWD_METRICS
mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics));
#endif
- // Need not initialize the DNS Configuration for Local Only OR P2P Questions
- if (LocalOnlyOrP2PInterface(question->InterfaceID))
+ // Need not initialize the DNS Configuration for Local Only OR P2P Questions when timeout not specified
+ if (LocalOnlyOrP2PInterface(question->InterfaceID) && !question->TimeoutQuestion)
return;
// Proceed to initialize DNS Configuration (some are set in SetValidDNSServers())
if (!mDNSOpaque16IsZero(question->TargetQID))
{
mDNSu32 timeout = SetValidDNSServers(m, question);
- // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have
- // a networking change/search domain change that calls this function again we keep
- // reinitializing the timeout value which means it may never timeout. If this becomes
- // a common case in the future, we can easily fix this by adding extra state that
- // indicates that we have already set the StopTime.
- //
- // Note that we set the timeout for all questions. If this turns out to be a duplicate,
+ // We set the timeout value the first time mDNS_StartQuery_internal is called for a question.
+ // So if a question is restarted when a network change occurs, the StopTime is not reset.
+ // Note that we set the timeout for all questions. If this turns out to be a duplicate,
// it gets a full timeout value even if the original question times out earlier.
- if (question->TimeoutQuestion)
+ if (question->TimeoutQuestion && !question->StopTime)
{
question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
- LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype));
+ LogInfo("InitDNSConfig: Setting StopTime on the uDNS question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype));
}
question->qDNSServer = GetServerForQuestion(m, question);
question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
}
- else
+ else if (question->TimeoutQuestion && !question->StopTime)
{
- if (question->TimeoutQuestion)
- question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+ // If the question is to be timed out and its a multicast, local-only or P2P case,
+ // then set it's stop time.
+ mDNSu32 timeout = LocalOnlyOrP2PInterface(question->InterfaceID) ?
+ DEFAULT_LO_OR_P2P_TIMEOUT : GetTimeoutForMcastQuestion(m, question);
+ question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
+ LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype));
}
// Set StopTime here since it is a part of DNS Configuration
if (question->StopTime)
SetNextQueryStopTime(m, question);
- // SetNextQueryTime() need not be initialized for LocalOnly OR P2P Questions since those questions
- // will never be transmitted on the wire. Hence we call SetNextQueryTime() here.
- SetNextQueryTime(m,question);
+ // Don't call SetNextQueryTime() if a LocalOnly OR P2P Question since those questions
+ // will never be transmitted on the wire.
+ if (!(LocalOnlyOrP2PInterface(question->InterfaceID)))
+ SetNextQueryTime(m,question);
}
// InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal
// state fields of the DNS Question. These are independent of the Client layer.
-mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question)
+mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question)
{
- mDNSBool purge;
int i;
mDNSBool isBlocked = mDNSfalse;
// turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC.
question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question
question->qnamehash = DomainNameHashValue(&question->qname);
- question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname), &purge);
+ question->DelayAnswering = mDNSOpaque16IsZero(question->TargetQID) ? CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash) : 0;
question->LastQTime = m->timenow;
question->ExpectUnicastResp = 0;
question->LastAnswerPktNum = m->PktNum;
if (!(question->flags & kDNSServiceFlagsServiceIndex))
{
#if APPLE_OSX_mDNSResponder
- mDNSPlatformGetDNSRoutePolicy(m, question, &isBlocked);
+ mDNSPlatformGetDNSRoutePolicy(question, &isBlocked);
#else
question->ServiceID = -1;
#endif
if (question->WakeOnResolve)
{
question->WakeOnResolveCount = InitialWakeOnResolveCount;
- purge = mDNStrue;
}
for (i=0; i<DupSuppressInfoSize; i++)
if (question->DelayAnswering)
LogInfo("InitCommonState: Delaying answering for %d ticks while cache stabilizes for %##s (%s)",
question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype));
-
- return(purge);
}
// Excludes the DNS Config fields which are already handled by InitDNSConfig()
question->id = zeroOpaque64;
}
+#ifdef DNS_PUSH_ENABLED
mDNSlocal void InitDNSPNState(DNSQuestion *const question)
{
question->dnsPushState = DNSPUSH_INIT;
}
+#endif // DNS_PUSH_ENABLED
// InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize
// DNSSEC & DNS Proxy fields of the DNS Question.
// Once the question is completely initialized including the duplicate logic, this function
// is called to finalize the unicast question which requires flushing the cache if needed,
// activating the query etc.
-mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDNSBool purge)
+mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question)
{
// Ensure DNS related info of duplicate question is same as the orig question
if (question->DuplicateOf)
{
question->validDNSServers = question->DuplicateOf->validDNSServers;
+ // If current(dup) question has DNS Server assigned but the original question has no DNS Server assigned to it,
+ // then we log a line as it could indicate an issue
+ if (question->DuplicateOf->qDNSServer == mDNSNULL)
+ {
+ if (question->qDNSServer)
+ LogInfo("FinalizeUnicastQuestion: Current(dup) question %p has DNSServer(%#a:%d) but original question(%p) has no DNS Server! %##s (%s)",
+ question, question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
+ mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort),
+ question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype));
+ }
question->qDNSServer = question->DuplicateOf->qDNSServer;
LogInfo("FinalizeUnicastQuestion: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d",
question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype),
ActivateUnicastQuery(m, question, mDNSfalse);
- // If purge was set above, flush the cache. Need to do this after we set the
- // DNS server on the question
- if (purge)
- {
- question->DelayAnswering = 0;
- mDNS_PurgeForQuestion(m, question);
- }
- else if (!question->DuplicateOf && DNSSECQuestion(question))
+ if (!question->DuplicateOf && DNSSECQuestion(question))
{
// For DNSSEC questions, we need to have the RRSIGs also for verification.
CheckForDNSSECRecords(m, question);
{
DNSQuestion **q;
mStatus vStatus;
- mDNSBool purge;
// First check for cache space (can't do queries if there is no cache space allocated)
if (m->rrcache_size == 0)
// InitCommonState -> InitDNSConfig) as DNS server selection affects DNSSEC
// validation.
- purge = InitCommonState(m, question);
+ InitCommonState(m, question);
InitWABState(question);
InitLLQState(question);
+#ifdef DNS_PUSH_ENABLED
InitDNSPNState(question);
+#endif // DNS_PUSH_ENABLED
InitDNSSECProxyState(m, question);
// FindDuplicateQuestion should be called last after all the intialization
// this routine with the question list data structures in an inconsistent state.
if (!mDNSOpaque16IsZero(question->TargetQID))
{
- FinalizeUnicastQuestion(m, question, purge);
+ FinalizeUnicastQuestion(m, question);
}
else
{
}
}
#endif // BONJOUR_ON_DEMAND
- if (purge)
+ if (question->WakeOnResolve)
{
LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c);
- mDNS_PurgeForQuestion(m, question);
+ mDNS_PurgeBeforeResolve(m, question);
}
}
}
mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
{
- const mDNSu32 slot = HashSlot(&question->qname);
- CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
+ CacheGroup *cg = CacheGroupForName(m, question->qnamehash, &question->qname);
CacheRecord *rr;
DNSQuestion **qp = &m->Questions;
}
#endif // BONJOUR_ON_DEMAND
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
if (Question_uDNS(question) && !question->metrics.answered && (question->metrics.querySendCount > 0))
{
const domainname * queryName;
mDNSBool isForCell;
mDNSu32 durationMs;
- queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname;
- isForCell = (question->qDNSServer && question->qDNSServer->cellIntf);
-
- if (question->metrics.querySendCount > 0)
- {
- durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond;
- }
- else
- {
- durationMs = 0;
- }
- MetricsUpdateUDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell);
+ queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname;
+ isForCell = (question->qDNSServer && question->qDNSServer->cellIntf);
+ durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond;
+ MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell);
}
#endif
// Take care to cut question from list *before* calling UpdateQuestionDuplicates
question->tcp = mDNSNULL;
}
}
+#ifdef DNS_PUSH_ENABLED
else if (question->dnsPushState == DNSPUSH_ESTABLISHED)
{
if (question->tcp)
question->tcp = mDNSNULL;
}
}
+#endif // DNS_PUSH_ENABLED
#if APPLE_OSX_mDNSResponder
UpdateAutoTunnelDomainStatuses(m);
#endif
FreeAnonInfo(question->AnonInfo);
question->AnonInfo = mDNSNULL;
}
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
if (question->metrics.originalQName)
{
mDNSPlatformMemFree(question->metrics.originalQName);
}
#endif
+#if USE_DNS64
+ DNS64ResetState(question);
+#endif
+
return(mStatus_NoError);
}
if (status == mStatus_NoError && !qq)
{
const CacheRecord *rr;
- const mDNSu32 slot = HashSlot(&question->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
+ CacheGroup *const cg = CacheGroupForName(m, question->qnamehash, &question->qname);
LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question))
return(intf);
}
+// The parameter "set" here refers to the set of AuthRecords used to advertise this interface.
+// (It's a set of records, not a set of interfaces.)
mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
{
char buffer[MAX_REVERSE_MAPPING_NAME];
primary = FindFirstAdvertisedInterface(m);
if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
+ // We should never have primary be NULL, because even if there is
+ // no other interface yet, we should always find ourself in the list.
// If interface is marked as a direct link, we can assume the address record is unique
// and does not need to go through the probe phase of the probe/announce packet sequence.
LogInfo("AdvertiseInterface: Marking address record as kDNSRecordTypeKnownUnique for %s", set->ifname);
// Send dynamic update for non-linklocal IPv4 Addresses
- mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, recordType, AuthRecordAny, mDNS_HostNameCallback, set);
- mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
- mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, recordType, AuthRecordAny, mDNS_HostNameCallback, set);
+ mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
#if ANSWER_REMOTE_HOSTNAME_QUERIES
set->RR_A.AllowRemoteQuery = mDNStrue;
}
}
-mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, InterfaceActivationSpeed activationSpeed)
{
AuthRecord *rr;
mDNSBool FirstOfType = mDNStrue;
// We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent,
// and think it's a conflicting answer to our probe.
// In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet.
- const mDNSs32 probedelay = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2;
- const mDNSu8 numannounce = flapping ? (mDNSu8)1 : InitialAnnounceCount;
+ mDNSs32 probedelay;
+ mDNSu8 numannounce;
+ switch (activationSpeed)
+ {
+ case FastActivation:
+ probedelay = (mDNSs32)0;
+ numannounce = InitialAnnounceCount;
+ LogMsg("mDNS_RegisterInterface: Using fast activation for DirectLink interface %s (%#a)", set->ifname, &set->ip);
+ break;
- // Use a small amount of randomness:
- // In the case of a network administrator turning on an Ethernet hub so that all the
- // connected machines establish link at exactly the same time, we don't want them all
- // to go and hit the network with identical queries at exactly the same moment.
- // We set a random delay of up to InitialQuestionInterval (1/3 second).
- // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way
- // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because
- // suppressing packet sending for more than about 1/3 second can cause protocol correctness
- // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts).
- // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
- if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+ case SlowActivation:
+ probedelay = mDNSPlatformOneSecond * 5;
+ numannounce = (mDNSu8)1;
+ LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a), doing slow activation", set->ifname, &set->ip);
+ m->mDNSStats.InterfaceUpFlap++;
+ break;
- if (flapping)
- {
- LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
- m->mDNSStats.InterfaceUpFlap++;
+ case NormalActivation:
+ default:
+ probedelay = mDNSPlatformOneSecond / 2;
+ numannounce = InitialAnnounceCount;
+ break;
}
LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
- if (m->SuppressProbes == 0 ||
- m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0)
- m->SuppressProbes = NonZeroTime(m->timenow + probedelay);
+ // No probe or sending suppression on DirectLink type interfaces.
+ if (activationSpeed == FastActivation)
+ {
+ m->SuppressSending = 0;
+ m->SuppressProbes = 0;
+ }
+ else
+ {
+ // Use a small amount of randomness:
+ // In the case of a network administrator turning on an Ethernet hub so that all the
+ // connected machines establish link at exactly the same time, we don't want them all
+ // to go and hit the network with identical queries at exactly the same moment.
+ // We set a random delay of up to InitialQuestionInterval (1/3 second).
+ // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way
+ // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because
+ // suppressing packet sending for more than about 1/3 second can cause protocol correctness
+ // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts).
+ // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
+ if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+
+ if (m->SuppressProbes == 0 ||
+ m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0)
+ m->SuppressProbes = NonZeroTime(m->timenow + probedelay);
+ }
// Include OWNER option in packets for 60 seconds after connecting to the network. Setting
// it here also handles the wake up case as the network link comes UP after waking causing
if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface,
{ // then reactivate this question
// If flapping, delay between first and second queries is nine seconds instead of one second
- mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID);
+ mDNSBool dodelay = (activationSpeed == SlowActivation) && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID);
mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval;
- mDNSs32 qdelay = dodelay ? mDNSPlatformOneSecond * 5 : 0;
- if (dodelay) LogInfo("No cache records expired for %##s (%s); okay to delay questions a little", q->qname.c, DNSTypeName(q->qtype));
+ mDNSs32 qdelay = dodelay ? kDefaultQueryDelayTimeForFlappingInterface : 0;
+ if (dodelay) LogInfo("No cache records expired for %##s (%s); delaying questions by %d seconds", q->qname.c, DNSTypeName(q->qtype), qdelay);
if (!q->ThisQInterval || q->ThisQInterval > initial)
{
// Note: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, InterfaceActivationSpeed activationSpeed)
{
NetworkInterfaceInfo **p = &m->HostInterfaces;
mDNSBool revalidate = mDNSfalse;
m->mDNSStats.InterfaceDown++;
- if (set->McastTxRx && flapping)
+ if (set->McastTxRx && (activationSpeed == SlowActivation))
{
LogMsg("mDNS_DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
m->mDNSStats.InterfaceDownFlap++;
{
// If this interface is deemed flapping,
// postpone deleting the cache records in case the interface comes back again
- if (set->McastTxRx && flapping)
+ if (set->McastTxRx && (activationSpeed == SlowActivation))
{
- // For a flapping interface we want these record to go away after 30 seconds
+ // For a flapping interface we want these records to go away after
+ // kDefaultReconfirmTimeForFlappingInterface seconds if they are not reconfirmed.
mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
// We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them --
// if the interface does come back, any relevant questions will be reactivated anyway
}
-// Derive AuthRecType from the coreFlag* values.
-// Note, this is not using the external flags values, kDNSServiceFlags*, defined in dns_sd.h.
-// It should be changed to do so once the use of coreFlag* is completely replaced with
-// the use the kDNSServiceFlags* definitions within mDNSResponder.
+// Derive AuthRecType from the kDNSServiceFlags* values.
mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags)
{
AuthRecType artype;
artype = AuthRecordLocalOnly;
else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE)
artype = AuthRecordP2P;
- else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P)
- && (flags & coreFlagIncludeAWDL))
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)
+ && (flags & kDNSServiceFlagsIncludeAWDL))
artype = AuthRecordAnyIncludeAWDLandP2P;
- else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P))
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
artype = AuthRecordAnyIncludeP2P;
- else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL))
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL))
artype = AuthRecordAnyIncludeAWDL;
else
artype = AuthRecordAny;
mDNSu32 i;
mDNSu32 hostTTL;
AuthRecType artype;
- mDNSu8 recordType = (flags & coreFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique;
+ mDNSu8 recordType = (flags & kDNSServiceFlagsKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique;
sr->ServiceCallback = Callback;
sr->ServiceContext = Context;
mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr);
mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr);
- if (flags & coreFlagWakeOnly)
+ if (flags & kDNSServiceFlagsWakeOnlyService)
{
sr->RR_PTR.AuthFlags = AuthFlagsWakeOnly;
}
hostTTL = kHostNameTTL;
mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr);
// If port number is zero, that means the client is really trying to do a RegisterNoSuchService
if (mDNSIPPortIsZero(port))
}
else if (msg == msg3)
{
- mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+ mDNSPlatformSetLocalAddressCacheEntry(&rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
}
else if (msg == msg4)
{
LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr));
}
else if (msg == msg3)
- mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+ mDNSPlatformSetLocalAddressCacheEntry(&rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
else if (msg == msg4)
SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha);
else if (msg == msg5)
{
if (!m->SPSSocket)
{
- m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+ m->SPSSocket = mDNSPlatformUDPSocket(zeroIPPort);
if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; }
}
#ifndef SPC_DISABLED
mDNS_Unlock(m);
}
-mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
- CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
- mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
+mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p,
+ CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
{
mDNSu32 slot;
mDNSs32 timenow;
m->timenow_last = timenow;
m->NextScheduledEvent = timenow;
m->SuppressSending = timenow;
- m->NextCacheCheck = timenow + 0x78000000;
- m->NextScheduledQuery = timenow + 0x78000000;
- m->NextScheduledProbe = timenow + 0x78000000;
- m->NextScheduledResponse = timenow + 0x78000000;
- m->NextScheduledNATOp = timenow + 0x78000000;
- m->NextScheduledSPS = timenow + 0x78000000;
- m->NextScheduledKA = timenow + 0x78000000;
- m->NextScheduledStopTime = timenow + 0x78000000;
+ m->NextCacheCheck = timenow + FutureTime;
+ m->NextScheduledQuery = timenow + FutureTime;
+ m->NextScheduledProbe = timenow + FutureTime;
+ m->NextScheduledResponse = timenow + FutureTime;
+ m->NextScheduledNATOp = timenow + FutureTime;
+ m->NextScheduledSPS = timenow + FutureTime;
+ m->NextScheduledKA = timenow + FutureTime;
+ m->NextScheduledStopTime = timenow + FutureTime;
m->NextBLEServiceTime = 0; // zero indicates inactive
#if BONJOUR_ON_DEMAND
m->SleepLimit = 0;
#if APPLE_OSX_mDNSResponder
- m->StatStartTime = mDNSPlatformUTC();
- m->NextStatLogTime = m->StatStartTime + kDefaultNextStatsticsLogTime;
- m->ActiveStatTime = 0;
m->UnicastPacketsSent = 0;
m->MulticastPacketsSent = 0;
m->RemoteSubnet = 0;
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
{
m->rrcache_hash[slot] = mDNSNULL;
- m->rrcache_nextcheck[slot] = timenow + 0x78000000;;
+ m->rrcache_nextcheck[slot] = timenow + FutureTime;;
}
mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
m->SuppressProbes = 0;
#ifndef UNICAST_DISABLED
- m->NextuDNSEvent = timenow + 0x78000000;
- m->NextSRVUpdate = timenow + 0x78000000;
+ m->NextuDNSEvent = timenow + FutureTime;
+ m->NextSRVUpdate = timenow + FutureTime;
m->DNSServers = mDNSNULL;
m->NATTraversals = mDNSNULL;
m->CurrentNATTraversal = mDNSNULL;
m->retryIntervalGetAddr = 0; // delta between time sent and retry
- m->retryGetAddr = timenow + 0x78000000; // absolute time when we retry
+ m->retryGetAddr = timenow + FutureTime; // absolute time when we retry
m->ExtAddress = zerov4Addr;
m->PCPNonce[0] = mDNSRandom(-1);
m->PCPNonce[1] = mDNSRandom(-1);
#endif
+ return(result);
+}
+
+mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
+ CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
+{
+ mStatus result = mDNS_InitStorage(m, p, rrcachestorage, rrcachesize, AdvertiseLocalAddresses, Callback, Context);
+ if (result != mStatus_NoError)
+ return(result);
+
result = mDNSPlatformInit(m);
#ifndef UNICAST_DISABLED
}
}
-mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q)
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q)
{
- const mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
CacheRecord *rp;
mDNSu8 validatingResponse = 0;
{
if (SameNameRecordAnswersQuestion(&rp->resrec, q))
{
- LogInfo("mDNS_PurgeForQuestion: Flushing %s", CRDisplayString(m, rp));
+ LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp));
mDNS_PurgeCacheResourceRecord(m, rp);
}
}
// them.
mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q)
{
- const mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
CacheRecord *rp;
for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
mDNSexport mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype)
{
DNSQuestion question;
- const mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
CacheRecord *rp;
// Create an identical question but with qtype
// hence we are ready to ack the configuration as this is the last call to mDNSPlatformSetConfig
// for the dns configuration change notification.
SetConfigState(m, mDNStrue);
- if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue))
+ if (!mDNSPlatformSetDNSConfig(mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue))
{
SetDynDNSHostNameIfChanged(m, &fqdn);
SetConfigState(m, mDNSfalse);
// cache records and as the resGroupID is different, you can't use the cache record from the scoped DNSServer to answer the
// non-scoped question and vice versa.
//
+#if USE_DNS64
+ DNS64RestartQuestions(m);
+#endif
for (q = m->Questions; q; q=q->next)
{
if (!mDNSOpaque16IsZero(q->TargetQID))
{
LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) pointing to DNSServer Address %#a"
" to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr);
- qptr->validDNSServers = zeroOpaque64;
+ qptr->validDNSServers = zeroOpaque128;
qptr->qDNSServer = mDNSNULL;
cr->resrec.rDNSServer = mDNSNULL;
}
v4 = v6 = r = zeroAddr;
v4.type = r.type = mDNSAddrType_IPv4;
- if (mDNSPlatformGetPrimaryInterface(m, &v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4))
+ if (mDNSPlatformGetPrimaryInterface(&v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4))
{
mDNS_SetPrimaryInterfaceInfo(m,
!mDNSIPv4AddressIsZero(v4.ip.v4) ? &v4 : mDNSNULL,
LogInfo("mDNS_FinalExit: done");
}
+
+#ifdef UNIT_TEST
+#include "../unittests/mdns_ut.c"
+#endif