diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c | 956 |
1 files changed, 512 insertions, 444 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c index 2f9227eabd..69236e0b88 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -47,7 +47,6 @@ #include "dns_sd.h" // for kDNSServiceFlags* definitions #if APPLE_OSX_mDNSResponder - #include <WebFilterDNS/WebFilterDNS.h> #if !NO_WCF @@ -63,6 +62,10 @@ void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); #define NO_WCF 1 #endif // APPLE_OSX_mDNSResponder +#if TARGET_OS_EMBEDDED +#include "Metrics.h" +#endif + // Forward declarations mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); @@ -80,6 +83,7 @@ mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m); mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords); mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records); +mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth); // *************************************************************************** @@ -146,10 +150,6 @@ mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) { mDNS_CheckLock(m); -#if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; -#endif - if (m->NextScheduledStopTime - q->StopTime > 0) m->NextScheduledStopTime = q->StopTime; } @@ -158,10 +158,6 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) { mDNS_CheckLock(m); -#if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; -#endif - if (ActiveQuestion(q)) { // Depending on whether this is a multicast or unicast question we want to set either: @@ -352,7 +348,7 @@ mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slo return(CacheGroupForName(m, slot, rr->namehash, rr->name)); } -mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself) +mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) { NetworkInterfaceInfo *intf; @@ -363,44 +359,19 @@ mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterface for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) - { - if (myself) - { - if (mDNSSameIPv4Address(intf->ip.ip.v4, addr->ip.v4)) - *myself = mDNStrue; - else - *myself = mDNSfalse; - if (*myself) - debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning true", addr); - else - debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning false", addr); - } return(mDNStrue); - } } if (addr->type == mDNSAddrType_IPv6) { + if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) - { - if (myself) - { - if (mDNSSameIPv6Address(intf->ip.ip.v6, addr->ip.v6)) - *myself = mDNStrue; - else - *myself = mDNSfalse; - if (*myself) - debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning true", addr); - else - debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning false", addr); - } return(mDNStrue); - } } return(mDNSfalse); @@ -475,11 +446,20 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re 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 + domainname *originalQName; +#endif // if there is a message waiting at the socket, we want to process that instead // of throwing it away. If we have a CNAME response that answers // both A and AAAA question and while answering it we don't want to throw // away the response where the actual addresses are present. + // This is a stupid hack and we should get rid of it. + // The chance of there being a second unicast UDP packet already waiting in the kernel before we’ve + // finished processing the previous one is virtually nil, and will only happen by luck on very rare + // occasions when running on a machine with a fast network connection and a slow or busy processor. + // The idea that we’d rely for correctness on this random chance event occurring is ridiculous. + // -- SC if (mDNSPlatformPeekUDP(m, q->LocalSocket)) { LogInfo("AnswerQuestionByFollowingCNAME: Preserving UDP socket for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -508,6 +488,31 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re 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 (q->metrics.originalQName) + { + originalQName = q->metrics.originalQName; + q->metrics.originalQName = mDNSNULL; + } + else + { + mDNSu16 qNameLen; + + qNameLen = DomainNameLength(&q->qname); + if ((qNameLen > 0) && (qNameLen <= MAX_DOMAIN_NAME)) + { + originalQName = mDNSPlatformMemAllocate(qNameLen); + if (originalQName) + { + mDNSPlatformMemCopy(originalQName->c, q->qname.c, qNameLen); + } + } + else + { + originalQName = mDNSNULL; + } + } +#endif mDNS_StopQuery_internal(m, q); // Stop old query AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname q->qnamehash = DomainNameHashValue(&q->qname); // and namehash @@ -524,6 +529,9 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // 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 + q->metrics.originalQName = originalQName; +#endif if (sock) { // We have a message waiting and that should answer this question. @@ -933,7 +941,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) { const domainname *const n = SetUnicastTargetToHostName(m, rr); if (n) newname = n; - else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } + else { if (target) target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } } if (target && SameDomainName(target, newname)) @@ -1133,26 +1141,61 @@ mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { + if (RRLocalOnly(rr)) + { + // A sanity check, this should be prevented in calling code. + LogInfo("DecrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr)); + return; + } + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) { - m->AutoTargetServices--; - LogInfo("DecrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); - if (!m->AutoTargetServices) + // If about to get rid of the last advertised service + if (m->AutoTargetServices == 1) DeadvertiseAllInterfaceRecords(m); + + m->AutoTargetServices--; + LogInfo("DecrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); + } + +#if TARGET_OS_WATCH + if (!AuthRecord_uDNS(rr)) + { + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NetworkChanged = m->timenow; + m->NumAllInterfaceRecords--; + LogInfo("DecrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); } +#endif } mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { - if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + if (RRLocalOnly(rr)) + { + // A sanity check, this should be prevented in calling code. + LogInfo("IncrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr)); + return; + } + +#if TARGET_OS_WATCH + if (!AuthRecord_uDNS(rr)) { - int count = m->AutoTargetServices; + m->NumAllInterfaceRecords++; + LogInfo("IncrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NetworkChanged = m->timenow; + } +#endif - // Bump up before calling AdvertiseAllInterfaceRecords. AdvertiseInterface - // returns without doing anything if the count is zero. + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { m->AutoTargetServices++; - LogInfo("IncrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); - if (!count) + LogInfo("IncrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); + // If this is the first advertised service + if (m->AutoTargetServices == 1) AdvertiseAllInterfaceRecords(m); } } @@ -1378,6 +1421,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more // complications and not clear whether there are any benefits. See rdar:9304275 for details. // Hence, just bail out. + // This comment is doesn’t make any sense. -- SC if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) { if (CheckAuthRecordConflict(&m->rrauth, rr)) @@ -1443,11 +1487,12 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) { AuthGroup *ag; ag = InsertAuthRecord(m, &m->rrauth, rr); - if (ag && !ag->NewLocalOnlyRecords) { + if (ag && !ag->NewLocalOnlyRecords) + { m->NewLocalOnlyRecords = mDNStrue; ag->NewLocalOnlyRecords = rr; } - // No probing for LocalOnly records, Acknowledge them right away + // No probing for LocalOnly records; acknowledge them right away if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; AcknowledgeRecord(m, rr); return(mStatus_NoError); @@ -1459,15 +1504,15 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } } - // If this is a keepalive record, fetch the MAC address of the remote host. + // If this is a non-sleep proxy keepalive record, fetch the MAC address of the remote host. // This is used by the in-NIC proxy to send the keepalive packets. - if (mDNS_KeepaliveRecord(&rr->resrec)) + if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) { + mDNSAddr raddr; // Set the record type to known unique to prevent probing keep alive records. // Also make sure we do not announce the keepalive records. rr->resrec.RecordType = kDNSRecordTypeKnownUnique; rr->AnnounceCount = 0; - mDNSAddr raddr; 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 @@ -1476,8 +1521,9 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above { - // We have inserted the record in the list. See if we have to advertise the A/AAAA,HINFO,PTR records. + // We have inserted the record in the list. See if we have to advertise the A/AAAA, HINFO, PTR records. IncrementAutoTargetServices(m, rr); + // For records that are not going to probe, acknowledge them right away if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) AcknowledgeRecord(m, rr); @@ -2373,8 +2419,8 @@ mDNSlocal void SendResponses(mDNS *const m) LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); for (r2 = rr; r2; r2=r2->next) - if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && - !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) + if ((r2->resrec.RecordType == kDNSRecordTypeDeregistering) && r2->AnnounceCount && (r2->resrec.InterfaceID == rr->resrec.InterfaceID) && + mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) { // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. @@ -2544,7 +2590,7 @@ mDNSlocal void SendResponses(mDNS *const m) if ((rr->SendRNow == intf->InterfaceID) && ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) { - LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); + // LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); rr->SendRNow = GetNextActiveInterfaceID(intf); } else if (rr->SendRNow == intf->InterfaceID) @@ -2929,10 +2975,10 @@ mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. // It also appends to the list of known answer records that need to be included, // and updates the forcast for the size of the known answer section. -mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, - CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) +mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf, DNSMessage *query, mDNSu8 **queryptr, + DNSQuestion *q, CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) { - mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; + mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; mDNSu8 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0; @@ -3072,27 +3118,27 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c #endif // SPC_DISABLED } -mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2) +mDNSlocal void CheckAndSwapSPS(const CacheRecord **sps1, const CacheRecord **sps2) { const CacheRecord *swap_sps; mDNSu32 metric1, metric2; - if (!sps1 || !sps2) return; - metric1 = SPSMetric(sps1->resrec.rdata->u.name.c); - metric2 = SPSMetric(sps2->resrec.rdata->u.name.c); - if (!SPSFeatures(sps1->resrec.rdata->u.name.c) && SPSFeatures(sps2->resrec.rdata->u.name.c) && (metric2 >= metric1)) + if (!(*sps1) || !(*sps2)) return; + metric1 = SPSMetric((*sps1)->resrec.rdata->u.name.c); + metric2 = SPSMetric((*sps2)->resrec.rdata->u.name.c); + if (!SPSFeatures((*sps1)->resrec.rdata->u.name.c) && SPSFeatures((*sps2)->resrec.rdata->u.name.c) && (metric2 >= metric1)) { - swap_sps = sps1; - sps1 = sps2; - sps2 = swap_sps; + swap_sps = *sps1; + *sps1 = *sps2; + *sps2 = swap_sps; } } mDNSlocal void ReorderSPSByFeature(const CacheRecord *sps[3]) { - CheckAndSwapSPS(sps[0], sps[1]); - CheckAndSwapSPS(sps[0], sps[2]); - CheckAndSwapSPS(sps[1], sps[2]); + CheckAndSwapSPS(&sps[0], &sps[1]); + CheckAndSwapSPS(&sps[0], &sps[2]); + CheckAndSwapSPS(&sps[1], &sps[2]); } @@ -3136,7 +3182,7 @@ mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressI return(mDNSfalse); } -mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) +mDNSlocal void RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) { int i, j; @@ -3156,8 +3202,6 @@ mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDN ds[i].Time = Time; ds[i].InterfaceID = InterfaceID; ds[i].Type = Type; - - return(i); } mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) @@ -3175,7 +3219,6 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) // Split MAC@IPAddress and pass them separately len = d->c[0]; - i = 1; cnt = 0; for (i = 1; i < len; i++) { @@ -3570,7 +3613,7 @@ mDNSlocal void SendQueries(mDNS *const m) } // If we're suppressing this question, or we successfully put it, update its SendQNow state else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) || - BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) + BuildQuestion(m, intf, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) { // We successfully added the question to the packet. Make sure that // we also send the NSEC3 record if required. BuildQuestion accounted for @@ -3594,7 +3637,7 @@ mDNSlocal void SendQueries(mDNS *const m) q->WakeOnResolveCount--; } - // use brackground traffic class if any included question requires it + // use background traffic class if any included question requires it if (q->UseBackgroundTrafficClass) { useBackgroundTrafficClass = mDNStrue; @@ -3607,7 +3650,7 @@ mDNSlocal void SendQueries(mDNS *const m) for (ar = m->ResourceRecords; ar; ar=ar->next) if (ar->SendRNow == intf->InterfaceID) { - mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) @@ -3905,6 +3948,31 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco return; } +#if TARGET_OS_EMBEDDED + if ((AddRecord == QC_add) && Question_uDNS(q) && (!q->metrics.answered || (q->metrics.querySendCount > 0))) + { + uDNSMetrics * metrics; + const domainname * queryName; + mDNSu32 responseLatencyMs; + mDNSBool isForCellular; + + metrics = &q->metrics; + queryName = metrics->originalQName ? metrics->originalQName : &q->qname; + if (metrics->querySendCount > 0) + { + responseLatencyMs = ((m->timenow - metrics->firstQueryTime) * 1000) / mDNSPlatformOneSecond; + } + else + { + responseLatencyMs = 0; + } + isForCellular = (q->qDNSServer && q->qDNSServer->cellIntf); + + MetricsUpdateUDNSStats(queryName, mDNStrue, metrics->querySendCount, responseLatencyMs, isForCellular); + metrics->answered = mDNStrue; + metrics->querySendCount = 0; + } +#endif // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) // may be called twice, once when the record is received, and again when it's time to notify local clients. // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. @@ -4086,6 +4154,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) { if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { + mDNSIPPort zp = zeroIPPort; // If this question is one that's actively sending queries, and it's received ten answers within one // second of sending the last query packet, then that indicates some radical network topology change, // so reset its exponential backoff back to the start. We must be at least at the eight-second interval @@ -4108,7 +4177,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? - rr->resrec.rDNSServer->port : zeroIPPort), q); + rr->resrec.rDNSServer->port : zp), q); q->CurrentAnswers++; q->unansweredQueries = 0; @@ -4898,7 +4967,7 @@ mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) // 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, m->SPSProxyListChanged); \ m->SPSProxyListChanged = (X); } while(0) // Called from mDNS_Execute() to expire stale proxy records @@ -5572,26 +5641,34 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) break; } - // Disallow sleep if there is no sleep proxy server - const CacheRecord *cr = FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL); - if ( cr == mDNSNULL) - { - allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); - LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); - break; - } - else if (m->SPSType != 0) + // If the interface can be an in-NIC Proxy, we should check if it can accomodate all the records + // that will be offloaded. If not, we should prevent sleep. + // This check will be possible once the lower layers provide an API to query the space available for offloads on the NIC. +#if APPLE_OSX_mDNSResponder + if (!SupportsInNICProxy(intf)) +#endif { - mDNSu32 mymetric = LocalSPSMetric(m); - mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); - if (metric >= mymetric) + // Disallow sleep if there is no sleep proxy server + const CacheRecord *cr = FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL); + if ( cr == mDNSNULL) { allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server with better metric on %s", intf->ifname); - LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server with a better metric", intf->ifname); + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); break; } + else if (m->SPSType != 0) + { + mDNSu32 mymetric = LocalSPSMetric(m); + mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); + if (metric >= mymetric) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server with better metric on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server with a better metric", intf->ifname); + break; + } + } } } } @@ -5622,7 +5699,7 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte // updateid and we should have returned from above. // // Note: scopeid is the same as intf->InterfaceID. It is passed in so that we don't have to call the - // platform function to extract the value from "intf" everytime. + // platform function to extract the value from "intf" every time. if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) @@ -5633,35 +5710,41 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte mDNSexport void UpdateRMACCallback(mDNS *const m, void *context) { - IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; - m->CurrentRecord = m->ResourceRecords; - - if (!addrmap) - { - LogMsg("UpdateRMACCallback: Address mapping is NULL"); - return; - } - - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - // If this is a keepalive record and the remote IP address matches, update the RData - if (mDNS_KeepaliveRecord(&rr->resrec)) - { - mDNSAddr raddr; - getKeepaliveRaddr(m, rr, &raddr); - if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) - { - UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); - } - } - m->CurrentRecord = rr->next; - } - - if (addrmap) - { - mDNSPlatformMemFree(addrmap); - } + IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; + m->CurrentRecord = m->ResourceRecords; + + if (!addrmap) + { + LogMsg("UpdateRMACCallback: Address mapping is NULL"); + return; + } + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + // If this is a non-sleep proxy keepalive record and the remote IP address matches, update the RData + if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) + { + // Update the MAC address only if it is not a zero MAC address + mDNSEthAddr macAddr; + mDNSu8 *ptr = GetValueForMACAddr((mDNSu8 *)(addrmap->ethaddr), (mDNSu8 *) (addrmap->ethaddr + sizeof(addrmap->ethaddr)), &macAddr); + if (ptr != mDNSNULL && !mDNSEthAddressIsZero(macAddr)) + { + UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); + } + } + } + m->CurrentRecord = rr->next; + } + + if (addrmap) + { + mDNSPlatformMemFree(addrmap); + } } mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr) @@ -6128,29 +6211,49 @@ mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) return mDNSfalse; } +#define WAKE_ONLY_SERVICE 1 +#define AC_ONLY_SERVICE 2 + #ifdef APPLE_OSX_mDNSResponder -// This function is used only in the case of local NIC proxy. For external -// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we -// walk the resource records. -mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) +mDNSlocal void SendGoodbyesForSelectServices(mDNS *const m, mDNSBool *servicePresent, mDNSu32 serviceType) { AuthRecord *rr; - - *WakeOnlyService = mDNSfalse; + *servicePresent = mDNSfalse; // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) { - if ((rr->AuthFlags & AuthFlagsWakeOnly) && - rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + // If the service type is wake only service and the auth flags match and requires a goodbye + // OR if the service type is AC only and it is not a keepalive record, + // mark the records we need to deregister and send them + if ((serviceType == WAKE_ONLY_SERVICE && (rr->AuthFlags & AuthFlagsWakeOnly) && + rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) || + (serviceType == AC_ONLY_SERVICE && !mDNS_KeepaliveRecord(&rr->resrec))) { rr->ImmedAnswer = mDNSInterfaceMark; - *WakeOnlyService = mDNStrue; + *servicePresent = mDNStrue; } } } +#endif + +#ifdef APPLE_OSX_mDNSResponder +// This function is used only in the case of local NIC proxy. For external +// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we +// walk the resource records. +mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) +{ + return SendGoodbyesForSelectServices(m, WakeOnlyService, WAKE_ONLY_SERVICE); +} #endif // APPLE_OSx_mDNSResponder +#ifdef APPLE_OSX_mDNSResponder +mDNSlocal void SendGoodbyesForACOnlyServices(mDNS *const m, mDNSBool *acOnlyService) +{ + return SendGoodbyesForSelectServices(m, acOnlyService, AC_ONLY_SERVICE); +} +#endif + mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast) { AuthRecord *rr; @@ -6246,6 +6349,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) { mDNSBool SendGoodbyes = mDNStrue; mDNSBool WakeOnlyService = mDNSfalse; + mDNSBool ACOnlyService = mDNSfalse; mDNSBool invokeKACallback = mDNStrue; const CacheRecord *sps[3] = { mDNSNULL }; mDNSOpaque64 updateIntID = zeroOpaque64; @@ -6302,9 +6406,12 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) #if APPLE_OSX_mDNSResponder else if (SupportsInNICProxy(intf)) { - if (ActivateLocalProxy(m, intf) == mStatus_NoError) + mDNSBool keepaliveOnly = mDNSfalse; + if (ActivateLocalProxy(m, intf, &keepaliveOnly) == mStatus_NoError) { SendGoodbyesForWakeOnlyService(m, &WakeOnlyService); + if (keepaliveOnly) + SendGoodbyesForACOnlyServices(m, &ACOnlyService); SendGoodbyes = mDNSfalse; invokeKACallback = mDNSfalse; LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); @@ -6348,9 +6455,9 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) { #if ForceAlerts if (intf->SPSAddr[i].type) - { LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; } + LogFatalError("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); if (intf->NetWakeResolve[i].ThisQInterval >= 0) - { LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; } + LogFatalError("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); #endif intf->SPSAddr[i].type = mDNSAddrType_None; if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]); @@ -6410,10 +6517,10 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server on all interfaces"); SendSleepGoodbyes(m, mDNSfalse, mDNSfalse); } - else if (WakeOnlyService) + else if (WakeOnlyService || ACOnlyService) { // If we saw WakeOnly service above, send the goodbyes now. - LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyServices"); + LogSPS("BeginSleepProcessing: Sending goodbyes for %s", WakeOnlyService? "WakeOnlyService" : "AC Only Service"); SendResponses(m); } } @@ -6487,11 +6594,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { m->SleepState = SleepState_Awake; m->SleepSeqNum++; - // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) - // then we enforce a minimum delay of 16 seconds before we begin sleep processing. - // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., - // before we make our determination of whether there's a Sleep Proxy out there we should register with. - m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16); + m->DelaySleep = 0; } if (m->SPSState == 3) @@ -7117,7 +7220,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; @@ -7667,9 +7770,9 @@ exit: DNSQuestion *q = DupQuestions; DupQuestions = q->NextInDQList; q->NextInDQList = mDNSNULL; - i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); - debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, - srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6"); } if (McastNSEC3Records) @@ -7687,7 +7790,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, { mDNSu8 *responseend = mDNSNULL; mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && - !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) { @@ -7779,10 +7882,10 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, mDNSIPPort srcp; if (!tcp) { - if (q->LocalSocket) - srcp = q->LocalSocket->port; + if (q->LocalSocket) + srcp = q->LocalSocket->port; else - srcp = zeroIPPort; + srcp = zeroIPPort; } else { @@ -7808,111 +7911,6 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, return(mDNSNULL); } -// Return a pointer to the primary service name, skipping subtype name if present. -mDNSlocal const domainname *getPrimaryServiceName(const domainname *domainName) -{ - const domainname *primaryName = domainName; - const domainname *subName = SkipLeadingLabels(domainName, 1); - - if (SameDomainLabel(subName->c, (const mDNSu8 *)mDNSSubTypeLabel)) - { - // skip "<sub type name>._sub" portion of name - primaryName = SkipLeadingLabels(domainName, 2); - debugf("getPrimaryServiceName: returning %##s for _sub type", primaryName); - } - - return primaryName; -} - -// This function is not called if the packet is from us, which implies that we accept all multicast packets coming from us. -mDNSlocal mDNSBool ExpectingMulticastResponseForRecord(mDNS *const m, CacheRecord *rr, const mDNSAddr *srcaddr, mDNSBool recordAccepted, - CacheRecord **McastNSEC3Records) -{ - DNSQuestion *q; - - // Accept A and AAAA if we accepted something before in the same packet as most likely related to the - // service records that we may have accepted. - if (recordAccepted && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA)) - { - LogInfo("ExpectingMulticastResponseForRecord:A:AAAA: accepting %s, from %#a due to same packet %d", CRDisplayString(m, rr), srcaddr, m->PktNum); - return mDNStrue; - } - for (q = m->Questions; q; q=q->next) - { - if (!q->DuplicateOf && mDNSOpaque16IsZero(q->TargetQID)) - { - mDNSBool ret; - // 1. If a resource record answers question, cache it. This also will cache NSECs if it asserts - // non-existence of q->qtype. If we have any matching NSEC3 Records for the question, send - // it along with the resource record. Do it only for questions that are expecting to - // discover only its peers (q->AnonInfo not NULL) - if (q->AnonInfo && McastNSEC3Records && !rr->resrec.AnonInfo) - { - InitializeAnonInfoForCR(m, McastNSEC3Records, rr); - } - ret = ResourceRecordAnswersQuestion(&rr->resrec, q); - if (ret) - { - // The record and the question belong to the same subset. Set the - // anonymous data in the cache record. - if (q->AnonInfo && rr->resrec.AnonInfo) - { - SetAnonData(q, &rr->resrec, mDNSfalse); - } - LogInfo("ExpectingMulticastResponseForRecord: Name and Type match, accepting %s, from %#a", CRDisplayString(m, rr), srcaddr); - if (rr->resrec.rrtype == kDNSType_NSEC) - LogInfo("ExpectingMulticastResponseForRecord: record %s, question %##s (%s)", CRDisplayString(m, rr), q->qname.c, DNSTypeName(q->qtype)); - return mDNStrue; - } - if (rr->resrec.rrtype == kDNSType_SRV || rr->resrec.rrtype == kDNSType_TXT) - { - // Point to the service type in the record name - const domainname *name = SkipLeadingLabels(rr->resrec.name, 1); - - // If question is for a sub type, just compare against the primary service type - const domainname *primaryName = getPrimaryServiceName(&q->qname); - - // 2. If the SRV or TXT record matches the service name, then cache it. If the TXT or SRV record is - // before the PTR record in the packet, PTR record may not be in the cache yet and hence the logic - // in (3) below will fail to cache it. - if (q->qtype == kDNSType_PTR && name && SameDomainName(primaryName, name)) - { - LogInfo("ExpectingMulticastResponseForRecord: Accepting %s due to PTR match, question %##s from %#a, pktnum %d", - CRDisplayString(m, rr), q->qname.c, srcaddr, m->PktNum); - return mDNStrue; - } - - if (name) - { - const mDNSu32 slot = HashSlot(name); - const mDNSu32 namehash = DomainNameHashValue(name); - CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); - CacheRecord *cr; - - // 3. Same as in (2), but look in the cache in case we don't have the PTR question. - - for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - { - if (cr->resrec.rrtype == kDNSType_PTR) - { - primaryName = getPrimaryServiceName(cr->resrec.name); - - if (SameDomainName(primaryName, name)) - { - LogInfo("ExpectingMulticastResponseForRecord: accepting %s, from %#a, pktnum %d", - CRDisplayString(m, rr), srcaddr, m->PktNum); - return mDNStrue; - } - } - } - } - } - } - } - debugf("ExpectingMulticastResponseForRecord: discarding %s, from %#a, pktnum %d", CRDisplayString(m, rr), srcaddr, m->PktNum); - return(mDNSfalse); -} - // Certain data types need more space for in-memory storage than their in-packet rdlength would imply // Currently this applies only to rdata types containing more than one domainname, // or types where the domainname is not the last item in the structure. @@ -8458,7 +8456,8 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * name = (const domainname *)(name->c + 1 + name->c[0]); hash = DomainNameHashValue(name); slot = HashSlot(name); - cg = CacheGroupForName(m, slot, hash, 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); } } } @@ -8660,7 +8659,7 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records) { - const mDNSu8 *ptr = response->data; + const mDNSu8 *ptr; CacheRecord *rr; int i; @@ -8709,24 +8708,6 @@ mDNSlocal void mDNSCoreResetRecord(mDNS *const m) } } -#define DEVICE_INFO_RECORD_LABELS 4 - -// Determine if the record is an instance of _device-info._tcp.local. -mDNSlocal mDNSBool IsDeviceInfoRecord(const domainname *d) -{ - const domainname *afterInstance; - - if (CountLabels(d) != DEVICE_INFO_RECORD_LABELS) - return mDNSfalse; - - // skip the instance name - afterInstance = SkipLeadingLabels(d, 1); - if (SameDomainName(afterInstance, &LocalDeviceInfoName)) - return mDNStrue; - - return mDNSfalse; -} - // Note: mDNSCoreReceiveResponse 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. @@ -8739,9 +8720,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const mDNSInterfaceID InterfaceID) { int i; - mDNSBool myself; mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); DNSQuestion *llqMatch = mDNSNULL; DNSQuestion *unicastQuestion = mDNSNULL; uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); @@ -8759,12 +8739,11 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, mDNSu8 rcode = '\0'; mDNSBool rrsigsCreated = mDNSfalse; mDNSBool DNSSECQuestion = mDNSfalse; - mDNSBool recordAccepted = mDNSfalse; NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); // All records in a DNS response packet are treated as equally valid statements of truth. If we want // to guard against spoof responses, then the only credible protection against that is cryptographic - // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record + // security, e.g. DNSSEC., not worrying about which section in the spoof packet contained the record. int firstauthority = response->h.numAnswers; int firstadditional = firstauthority + response->h.numAuthorities; int totalrecords = firstadditional + response->h.numAdditionals; @@ -9000,64 +8979,69 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that // we create. - if (!mDNSOpaque16IsZero(response->h.id)) - { - DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); - - // Initialize the DNS server on the resource record which will now filter what questions we answer with - // this record. - // - // We could potentially lookup the DNS server based on the source address, but that may not work always - // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came - // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based - // on the "id" and "source port", then this response answers the question and assume the response - // came from the same DNS server that we sent the query to. - - if (q != mDNSNULL) - { - AcceptableResponse = mDNStrue; - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; - } - else - LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - } - else - { - // If we can't find a matching question, we need to see whether we have seen records earlier that matched - // the question. The code below does that. So, make this record unacceptable for now - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; - } - } - } - else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) - { - recordAccepted = mDNStrue; - AcceptableResponse = mDNStrue; - LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r), - InterfaceID); - } - else if (IsDeviceInfoRecord(m->rec.r.resrec.name)) - { - recordAccepted = mDNStrue; - AcceptableResponse = mDNStrue; - LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p", - CRDisplayString(m, &m->rec.r), InterfaceID); - } + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + + // Initialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. + + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + else + LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + } + else + { + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } + } } } else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A) { - CacheRecord *const rr = &m->rec.r; - RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; - - // If we are supposed to ignore link-local addresses on this interface, drop - // all "A" records that have link-local address in them. + // There are some routers (rare, thankfully) that generate bogus ARP responses for + // any IPv4 address they don’t recognize, including RFC 3927 IPv4 link-local addresses. + // To work with these broken routers, client devices need to blacklist these broken + // routers and ignore their bogus ARP responses. Some devices implement a technique + // such as the one described in US Patent 7436783, which lets clients detect and + // ignore these broken routers: <https://www.google.com/patents/US7436783> + + // OS X and iOS do not implement this defensive mechanism, instead taking a simpler + // approach of just detecting these broken routers and completely disabling IPv4 + // link-local communication on interfaces where a broken router is detected. + // OS X and iOS set the IFEF_ARPLL interface flag on interfaces + // that are deemed “safe” for IPv4 link-local communication; + // the flag is cleared on interfaces where a broken router is detected. + + // OS X and iOS will not even try to communicate with an IPv4 + // link-local destination on an interface without the IFEF_ARPLL flag set. + // This can cause some badly written applications to freeze for a long time if they + // attempt to connect to an IPv4 link-local destination address and then wait for + // that connection attempt to time out before trying other candidate addresses. + + // To mask this client bug, we suppress acceptance of IPv4 link-local address + // records on interfaces where we know the OS will be unwilling even to attempt + // communication with those IPv4 link-local destination addresses. + // <rdar://problem/9400639> kSuppress IPv4LL answers on interfaces without IFEF_ARPLL + + const CacheRecord *const rr = &m->rec.r; + const RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; if (mDNSv4AddressIsLinkLocal(&rdb->ipv4)) { LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r)); @@ -9116,7 +9100,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, if (rr->ProbeCount > DefaultProbeCountForTypeUnique) LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr)); else if (rr->ProbeCount == DefaultProbeCountForTypeUnique) - LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); + LogInfo("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); else { LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r)); @@ -9202,27 +9186,6 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID); } - // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache - // everything that was received over multicast. Otherwise, we are selective about the caching. - // - // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call - // ExpectingMulticastResponseForRecord which decides whether to cache this record or not. - // - if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id)) - { - if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) - { - //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r)); - mDNSCoreResetRecord(m); - continue; - } - else - { - recordAccepted = mDNStrue; - } - } - - // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) if (!rr && m->rec.r.resrec.rroriginalttl > 0) @@ -9360,15 +9323,22 @@ exit: // goodbye announcement with the cache flush bit set (or a case-change on record rdata, // which we treat as a goodbye followed by an addition) and in that case it would be // inappropriate to synchronize all the other records to a TTL of 0 (or 1). + // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, // because certain early Bonjour devices are known to have this specific mismatch, and // there's no point filling syslog with messages about something we already know about. // We also don't log this for uDNS responses, since a caching name server is obliged // to give us an aged TTL to correct for how long it has held the record, // so our received TTLs are expected to vary in that case + + // We also suppress log message in the case of SRV records that are recieved + // with a TTL of 4500 that are already cached with a TTL of 120 seconds, since + // this behavior was observed for a number of discoveryd based AppleTV's in iOS 8 + // GM builds. if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) { if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && + !(r2->resrec.rroriginalttl == 120 && r1->resrec.rroriginalttl == 4500 && r2->resrec.rrtype == kDNSType_SRV) && mDNSOpaque16IsZero(response->h.id)) LogInfo("Correcting TTL from %4d to %4d for %s", r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); @@ -9555,7 +9525,7 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et int i; mDNSs8 hval = 0; int colons = 0; - mDNSu8 val = 0; + mDNSu16 val = 0; /* need to use 16 bit int to detect overflow */ for (i = 0; ptr < limit && *ptr != ' ' && i < 17; i++, ptr++) { @@ -9567,7 +9537,12 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et } else if (*ptr == ':') { - eth->b[colons] = val; + if (colons >=5 || val > 255) + { + LogMsg("GetValueForMACAddr: Address malformed colons %d val %d", colons, val); + return mDNSNULL; + } + eth->b[colons] = (mDNSs8)val; colons++; val = 0; } @@ -9577,7 +9552,7 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et LogMsg("GetValueForMACAddr: Address malformed colons %d", colons); return mDNSNULL; } - eth->b[colons] = val; + eth->b[colons] = (mDNSs8)val; return ptr; } @@ -9698,6 +9673,11 @@ mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4 val = val * 10 + *ptr - '0'; else if (*ptr == '.') { + if (val > 255 || dots >= 3) + { + LogMsg("GetValueForIPv4Addr: something wrong ptr(%p) %c, limit %p, dots %d", ptr, *ptr, limit, dots); + return mDNSNULL; + } v4->b[dots++] = val; val = 0; } @@ -9740,6 +9720,37 @@ mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *valu return ptr; } +mDNSexport mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr) +{ + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + if (!mDNS_KeepaliveRecord(&rr->resrec)) + { + return mDNSfalse; + } + + timeout = seq = ack = 0; + win = 0; + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + if (mDNSAddressIsZero(&laddr) || mDNSIPPortIsZero(lport) || + mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(rport) || + mDNSEthAddressIsZero(eth)) + { + return mDNSfalse; + } + + return mDNStrue; +} + + mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win) { @@ -9916,16 +9927,18 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m) mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) { - if (ar != mDNSNULL) - { - LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); - return; - } - mDNSu32 timeout, seq, ack; + mDNSu32 timeout, seq, ack, seqInc; mDNSu16 win; mDNSAddr laddr, raddr; mDNSEthAddr eth; mDNSIPPort lport, rport; + mDNSu8 *ptr; + + if (ar == mDNSNULL) + { + LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); + return; + } timeout = seq = ack = 0; win = 0; @@ -9940,6 +9953,17 @@ mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar)); return; } + + // To send a keepalive ACK, we need to add one to the sequence number from the keepalive + // record, which is the TCP connection's "next" sequence number minus one. Otherwise, the + // keepalive ACK also ends up being a keepalive probe. Also, seq is in network byte order, so + // it's converted to host byte order before incrementing it by one. + ptr = (mDNSu8 *)&seq; + seqInc = (mDNSu32)((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]) + 1; + ptr[0] = (mDNSu8)((seqInc >> 24) & 0xFF); + ptr[1] = (mDNSu8)((seqInc >> 16) & 0xFF); + ptr[2] = (mDNSu8)((seqInc >> 8) & 0xFF); + ptr[3] = (mDNSu8)((seqInc ) & 0xFF); LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); } @@ -10106,6 +10130,8 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg { 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); @@ -10155,8 +10181,7 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg } // Update the dynamic store with the IP Address and MAC address of the sleep proxy - char *ifname = InterfaceNameForID(m, InterfaceID); - mDNSAddr spsaddr; + ifname = InterfaceNameForID(m, InterfaceID); mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr)); mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname); } @@ -10169,12 +10194,7 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) { if (cr == &m->rec.r && m->rec.r.resrec.RecordType) - { - LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); -#if ForceAlerts - *(long*)0 = 0; -#endif - } + LogFatalError("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); // Create empty resource record cr->resrec.RecordType = kDNSRecordTypePacketNegative; @@ -10282,7 +10302,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co // 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, mDNSNULL) && + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr) && mDNSAddressIsAllDNSLinkGroup(dstaddr)) { m->RemoteSubnet++; @@ -10489,8 +10509,8 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname { if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) { - if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); - (*p)->flags &= ~DNSServer_FlagDelete; + if (!((*p)->flags & McastResolver_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); + (*p)->flags &= ~McastResolver_FlagDelete; tmp = *p; *p = tmp->next; tmp->next = mDNSNULL; @@ -10508,7 +10528,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname else { (*p)->interface = interface; - (*p)->flags = DNSServer_FlagNew; + (*p)->flags = McastResolver_FlagNew; (*p)->timeout = timeout; AssignDomainName(&(*p)->domain, d); (*p)->next = mDNSNULL; @@ -10714,9 +10734,10 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout - if (curr->scoped && curr->interface == mDNSInterface_Any) + // Skip DNSServers that are InterfaceID Scoped but have no valid interfaceid set OR DNSServers that are ServiceID Scoped but have no valid serviceid set + if ((curr->scoped == kScopeInterfaceID && curr->interface == mDNSInterface_Any) || (curr->scoped == kScopeServiceID && curr->serviceID <= 0)) { - debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); + LogInfo("SetValidDNSServers: ScopeType[%d] Skipping DNS server %#a (Domain %##s) Interface:[%p] Serviceid:[%d]", curr->scoped, &curr->addr, curr->domain.c, curr->interface, curr->serviceID); continue; } @@ -11367,6 +11388,9 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) question->triedAllServersOnce = 0; question->noServerResponse = 0; question->StopTime = 0; +#if TARGET_OS_EMBEDDED + mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics)); +#endif // Need not initialize the DNS Configuration for Local Only OR P2P Questions if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) @@ -11415,6 +11439,7 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) { mDNSBool purge; int i; + mDNSBool isCellBlocked = mDNSfalse; // Note: In the case where we already have the answer to this question in our cache, that may be all the client // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would @@ -11463,14 +11488,14 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) question->FlappingInterface1 = mDNSNULL; question->FlappingInterface2 = mDNSNULL; - // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetServiceID() + // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetDNSRoutePolicy() // since we would already have the question->ServiceID in that case. if (!(question->flags & kDNSServiceFlagsServiceIndex)) - question->ServiceID = mDNSPlatformGetServiceID(m, question); + mDNSPlatformGetDNSRoutePolicy(m, question, &isCellBlocked); else - LogInfo("InitCommonState: Query for %##s (%s), PID[%d], ServiceID %d is already set by client", question->qname.c, - DNSTypeName(question->qtype), question->pid, question->ServiceID); - + LogInfo("InitCommonState: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] is already set by client", question->qname.c, + DNSTypeName(question->qtype), question->pid, question->euid, question->ServiceID); + InitDNSConfig(m, question); question->AuthInfo = GetAuthInfoForQuestion(m, question); @@ -11480,10 +11505,10 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) // If ServiceID is 0 or the policy disallows making DNS requests, // set DisallowPID - question->DisallowPID = (question->ServiceID == 0 || (mDNSPlatformAllowPID(m, question) == 0)); + question->DisallowPID = (question->ServiceID == 0 || (isCellBlocked && question->qDNSServer && question->qDNSServer->cellIntf)); if (question->DisallowPID) LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c, - DNSTypeName(question->qtype), question->pid, question->ServiceID); + DNSTypeName(question->qtype), question->pid, question->ServiceID); question->NextInDQList = mDNSNULL; question->SendQNow = mDNSNULL; @@ -11602,7 +11627,7 @@ mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) { if (question->qDNSServer->cellIntf) { - LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); + debugf("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); question->ValidationRequired = mDNSfalse; } if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO)) @@ -11739,6 +11764,13 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } else { +#if TARGET_OS_WATCH + m->NumAllInterfaceQuestions++; + LogInfo("mDNS_StartQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NetworkChanged = m->timenow; +#endif if (purge) { LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); @@ -11783,14 +11815,35 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que #if !ForceAlerts if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active #endif - LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", - question->qname.c, DNSTypeName(question->qtype)); -#if ForceAlerts - *(long*)0 = 0; -#endif + LogFatalError("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", question->qname.c, DNSTypeName(question->qtype)); return(mStatus_BadReferenceErr); } +#if TARGET_OS_WATCH + if (question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_P2P && mDNSOpaque16IsZero(question->TargetQID)) + { + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NetworkChanged = m->timenow; + m->NumAllInterfaceQuestions--; + LogInfo("mDNS_StopQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + } +#endif + +#if TARGET_OS_EMBEDDED + if (Question_uDNS(question) && !question->metrics.answered) + { + uDNSMetrics * metrics; + const domainname * queryName; + mDNSBool isForCellular; + + metrics = &question->metrics; + queryName = metrics->originalQName ? metrics->originalQName : &question->qname; + isForCellular = (question->qDNSServer && question->qDNSServer->cellIntf); + + MetricsUpdateUDNSStats(queryName, mDNSfalse, metrics->querySendCount, 0, isForCellular); + } +#endif // Take care to cut question from list *before* calling UpdateQuestionDuplicates UpdateQuestionDuplicates(m, question); // But don't trash ThisQInterval until afterwards. @@ -11913,6 +11966,13 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que FreeAnonInfo(question->AnonInfo); question->AnonInfo = mDNSNULL; } +#if TARGET_OS_EMBEDDED + if (question->metrics.originalQName) + { + mDNSPlatformMemFree(question->metrics.originalQName); + question->metrics.originalQName = mDNSNULL; + } +#endif return(mStatus_NoError); } @@ -12365,6 +12425,7 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->qnameOrig = mDNSNULL; question->AnonInfo = mDNSNULL; question->pid = mDNSPlatformGetPID(); + question->euid = 0; question->QuestionCallback = Callback; question->QuestionContext = Context; if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); @@ -12481,26 +12542,20 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { char buffer[MAX_REVERSE_MAPPING_NAME]; NetworkInterfaceInfo *primary; + mDNSu8 recordType; - if (!set->McastTxRx) - { - LogInfo("AdvertiseInterface: Returning, not multicast capable %s", set->ifname); - return; - } -#if TARGET_OS_EMBEDDED - if (!m->AutoTargetServices) + if (m->AutoTargetServices == 0) { LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); return; } -#endif primary = FindFirstAdvertisedInterface(m); if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary // 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. - mDNSu8 recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); + recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); if (set->DirectLink) LogInfo("AdvertiseInterface: Marking address record as kDNSRecordTypeKnownUnique for %s", set->ifname); @@ -12575,22 +12630,19 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { - NetworkInterfaceInfo *intf; - - // If we still have address records referring to this one, update them - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->RR_A.RRSet == &set->RR_A) - intf->RR_A.RRSet = A; + if (m->AutoTargetServices == 0) + { + LogInfo("DeadvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); + return; + } // Unregister these records. // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). - if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); - if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_A .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); #if APPLE_OSX_mDNSResponder @@ -12614,7 +12666,6 @@ mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) { -#if TARGET_OS_EMBEDDED NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) { @@ -12624,15 +12675,11 @@ mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) DeadvertiseInterface(m, intf); } } -#else - (void) m; //unused -#endif } mDNSexport void mDNS_SetFQDN(mDNS *const m) { domainname newmname; - NetworkInterfaceInfo *intf; AuthRecord *rr; newmname.c[0] = 0; @@ -12645,14 +12692,8 @@ mDNSexport void mDNS_SetFQDN(mDNS *const m) else { AssignDomainName(&m->MulticastHostname, &newmname); - - // 1. Stop advertising our address records on all interfaces - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) DeadvertiseInterface(m, intf); - - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) AdvertiseInterface(m, intf); + DeadvertiseAllInterfaceRecords(m); + AdvertiseAllInterfaceRecords(m); } // 3. Make sure that any AutoTarget SRV records (and the like) get updated @@ -12971,6 +13012,9 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se { NetworkInterfaceInfo **p = &m->HostInterfaces; mDNSBool revalidate = mDNSfalse; + NetworkInterfaceInfo *primary; + NetworkInterfaceInfo *intf; + AuthRecord *A; mDNS_Lock(m); @@ -12987,14 +13031,13 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se if (!set->InterfaceActive) { // If this interface not the active member of its set, update the v4/v6Available flags for the active member - NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) UpdateInterfaceProtocols(m, intf); } else { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID); + intf = FirstInterfaceForID(m, set->InterfaceID); if (intf) { LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %d %s (%#a) exists;" @@ -13027,7 +13070,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se if (set->McastTxRx && flapping) { - LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + LogMsg("mDNS_DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); m->mDNSStats.InterfaceDownFlap++; } @@ -13068,6 +13111,15 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se } } + // If we still have address records referring to this one, update them. + // This is safe, because this NetworkInterfaceInfo has already been unlinked from the list, + // so the call to FindFirstAdvertisedInterface() won’t accidentally find it. + primary = FindFirstAdvertisedInterface(m); + A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + // If we were advertising on this interface, deregister those address and reverse-lookup records now if (set->Advertise) DeadvertiseInterface(m, set); @@ -13390,8 +13442,6 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, e = &sr->Extras; while (*e) e = &(*e)->next; - if (ttl == 0) ttl = kStandardTTL; - extra->r.DependentOn = &sr->RR_SRV; debugf("mDNS_AddRecordToService adding record to %##s %s %d", @@ -14045,11 +14095,13 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) { const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; + const mDNSu8 * transEnd = p + 14 + mDNSVal16(pkt->v4.totlen); + if (transEnd > end) transEnd = end; debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; - if (end >= trans + RequiredCapLen(pkt->v4.protocol)) - mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); + if (transEnd >= trans + RequiredCapLen(pkt->v4.protocol)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, transEnd, InterfaceID, 0); } // Is IPv6? Length must be at least 14 + 28 = 42 bytes else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) @@ -14180,6 +14232,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, if (!rrcachestorage) rrcachesize = 0; m->p = p; + m->NetworkChanged = 0; m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->DivertMulticastAdvertisements = mDNSfalse; @@ -14302,13 +14355,20 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->WABBrowseQueriesCount = 0; m->WABLBrowseQueriesCount = 0; m->WABRegQueriesCount = 0; -#if !TARGET_OS_EMBEDDED - m->mDNSOppCaching = mDNStrue; +#if TARGET_OS_EMBEDDED || TARGET_OS_WATCH + m->AutoTargetServices = 0; #else - m->mDNSOppCaching = mDNSfalse; + m->AutoTargetServices = 1; +#endif +#if TARGET_OS_WATCH + m->NumAllInterfaceRecords = 0; + m->NumAllInterfaceQuestions = 0; +#else + // Initialize to 1 for these targets to prevent not joining multicast group for interfaces when + // both of these values are zero. + m->NumAllInterfaceRecords = 1; + m->NumAllInterfaceQuestions = 1; #endif - m->AutoTargetServices = 0; - // NAT traversal fields m->LLQNAT.clientCallback = mDNSNULL; m->LLQNAT.clientContext = mDNSNULL; @@ -14545,6 +14605,10 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) ptr->penaltyTime = 0; NumUnicastDNSServers--; ptr->flags |= DNSServer_FlagDelete; +#if APPLE_OSX_mDNSResponder + if (ptr->flags & DNSServer_FlagUnreachable) + NumUnreachableDNSServers--; +#endif } // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at // mcast resolvers. Today we get both mcast and ucast configuration using the same @@ -14559,6 +14623,10 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) ptr->penaltyTime = 0; NumUnicastDNSServers++; ptr->flags &= ~DNSServer_FlagDelete; +#if APPLE_OSX_mDNSResponder + if (ptr->flags & DNSServer_FlagUnreachable) + NumUnreachableDNSServers++; +#endif } for (mr = m->McastResolvers; mr; mr = mr->next) mr->flags &= ~McastResolver_FlagDelete; @@ -14607,7 +14675,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // affecting them as they never change. while (*mres) { - if (((*mres)->flags & DNSServer_FlagDelete) != 0) + if (((*mres)->flags & McastResolver_FlagDelete) != 0) { mr = *mres; *mres = (*mres)->next; @@ -14668,6 +14736,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) tport = t->port; else tport = zeroIPPort; + if (s) sport = s->port; else @@ -14724,8 +14793,9 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) } else { + mDNSIPPort zp = zeroIPPort; debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", - q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable); + q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zp), q->DuplicateOf, q->SuppressUnusable); for (qptr = q->next ; qptr; qptr = qptr->next) if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } } @@ -14943,7 +15013,6 @@ mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start) mDNSexport void mDNS_StartExit(mDNS *const m) { - NetworkInterfaceInfo *intf; AuthRecord *rr; mDNS_Lock(m); @@ -14983,9 +15052,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m) } #endif - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - DeadvertiseInterface(m, intf); + DeadvertiseAllInterfaceRecords(m); // Shut down all our active NAT Traversals while (m->NATTraversals) @@ -15045,6 +15112,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m) mDNSexport void mDNS_FinalExit(mDNS *const m) { mDNSu32 rrcache_active = 0; + mDNSu32 rrcache_totalused = m->rrcache_totalused; mDNSu32 slot; AuthRecord *rr; @@ -15067,9 +15135,9 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) ReleaseCacheGroup(m, &m->rrcache_hash[slot]); } } - debugf("mDNS_FinalExit: RR Cache was using %ld records, %lu active", m->rrcache_totalused, rrcache_active); + debugf("mDNS_FinalExit: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); if (rrcache_active != m->rrcache_active) - LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); + LogMsg("*** ERROR *** rrcache_totalused %lu; rrcache_active %lu != m->rrcache_active %lu", rrcache_totalused, rrcache_active, m->rrcache_active); for (rr = m->ResourceRecords; rr; rr = rr->next) LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); |