<rdar://problem/6930335> Sleep Proxy: SPS needs to wake all clients before shutting...
authorcheshire@apple.com <cheshire@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Tue, 24 Nov 2009 04:03:25 +0000 (04:03 +0000)
committercheshire@apple.com <cheshire@apple.com@214c2c4a-bf3b-4dcf-9390-e4dd3010487d>
Tue, 24 Nov 2009 04:03:25 +0000 (04:03 +0000)
git-svn-id: http://svn.macosforge.org/repository/mDNSResponder/trunk@6848 214c2c4a-bf3b-4dcf-9390-e4dd3010487d

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

index 304ce41..31284f4 100755 (executable)
@@ -57,6 +57,7 @@
 // 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);
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -813,6 +814,12 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
                return(mStatus_BadReferenceErr);
                }
 
+       if (rr->WakeUp.HMAC.l[0])
+               {
+               LogSPS("mDNS_Deregister_internal: Sending wakeup for %.6a %s", &rr->WakeUp.HMAC, ARDisplayString(m, rr));
+               SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password);
+               }
+
        // If this is a shared record and we've announced it at least once,
        // we need to retract that announcement before we delete the record
 
@@ -3126,14 +3133,16 @@ mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list)
                AuthRecord *rr = m->CurrentRecord;
                if (rr->WakeUp.HMAC.l[0])
                        {
-                       if (m->timenow - rr->TimeExpire < 0)            // If proxy record not expired yet, update m->NextScheduledSPS
+                       // If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more,
+                       // so we need to cease proxying for *all* records we may have, expired or not.
+                       if (m->SPSSocket && m->timenow - rr->TimeExpire < 0)    // If proxy record not expired yet, update m->NextScheduledSPS
                                {
                                if (m->NextScheduledSPS - rr->TimeExpire > 0)
                                        m->NextScheduledSPS = rr->TimeExpire;
                                }
-                       else                                                                            // else proxy record expired, so remove it
+                       else                                                                                                    // else proxy record expired, so remove it
                                {
-                               LogSPS("mDNS_Execute: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
+                               LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
                                        m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
                                SetSPSProxyListChanged(rr->resrec.InterfaceID);
                                mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
@@ -3733,10 +3742,8 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
 
                if (m->SPSState == 3)
                        {
-                       mDNS_DropLockBeforeCallback();          // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here
                        m->SPSState = 0;
-                       mDNSCoreBeSleepProxyServer(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower);
-                       mDNS_ReclaimLockAfterCallback();
+                       mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower);
                        }
 
                // In case we gave up waiting and went to sleep before we got an ack from the Sleep Proxy,
@@ -4194,6 +4201,8 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const Res
 // Called from ProcessQuery when we get an mDNS packet with an owner record in it
 mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist)
        {
+       if (m->CurrentRecord)
+               LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
        m->CurrentRecord = thelist;
        while (m->CurrentRecord)
                {
@@ -4203,6 +4212,8 @@ mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner,
                                {
                                LogSPS("ClearProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s",
                                        m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
+                               rr->WakeUp.HMAC = zeroEthAddr;  // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+                               rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it, since real host is now back and functional
                                mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
                                SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID);
                                }
@@ -5082,9 +5093,23 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                if (!ptr) goto exit;            // Break out of the loop and clean up our CacheFlushRecords list before exiting
 
                // Don't want to cache OPT or TSIG pseudo-RRs
-               if (m->rec.r.resrec.rrtype == kDNSType_OPT || m->rec.r.resrec.rrtype == kDNSType_TSIG)
-                       { m->rec.r.resrec.RecordType = 0; continue; }
-                       
+               if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; }
+               if (m->rec.r.resrec.rrtype == kDNSType_OPT)
+                       {
+                       const rdataOPT *opt;
+                       const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+                       // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently
+                       // delete all our own AuthRecords (which are identified by having zero MAC tags on them).
+                       for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++)
+                               if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0])
+                                       {
+                                       ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
+                                       ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
+                                       }
+                       m->rec.r.resrec.RecordType = 0;
+                       continue;
+                       }
+
                // if a CNAME record points to itself, then don't add it to the cache
                if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name))
                        { 
@@ -8368,8 +8393,12 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s
                }
        }
 
-mDNSexport void mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower)
+// Called with lock held
+mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower)
        {
+       // This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context
+       mDNS_DropLockBeforeCallback();
+
        // If turning off SPS, close our socket
        // (Do this first, BEFORE calling mDNS_DeregisterService below)
        if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; }
@@ -8390,10 +8419,17 @@ mDNSexport void mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 por
                if (!m->SPSSocket)
                        {
                        m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
-                       if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); return; }
+                       if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; }
                        }
                if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree);
                }
+       else if (m->SPSState)
+               {
+               LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState);
+               m->NextScheduledSPS = m->timenow;
+               }
+fail:
+       mDNS_ReclaimLockAfterCallback();
        }
 
 // ***************************************************************************
@@ -9065,9 +9101,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m)
 
        m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
 
-       mDNS_DropLockBeforeCallback();          // mDNSCoreBeSleepProxyServer expects to be called without the lock held, so we emulate that here
-       mDNSCoreBeSleepProxyServer(m, 0, 0, 0, 0);
-       mDNS_ReclaimLockAfterCallback();
+       mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0);
 
 #ifndef UNICAST_DISABLED
        {
index e469afe..0774778 100755 (executable)
@@ -1076,7 +1076,7 @@ struct AuthRecord_struct
        mDNSu8          AllowRemoteQuery;       // Set if we allow hosts not on the local link to query this record
        mDNSu8          ForceMCast;                     // Set by client to advertise solely via multicast, even for apparently unicast names
 
-       OwnerOptData    WakeUp;                         // Fpr Sleep Proxy records, MAC address of original owner (so we can wake it)
+       OwnerOptData    WakeUp;                         // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record
        mDNSAddr        AddressProxy;           // For reverse-mapping Sleep Proxy PTR records, address in question
        mDNSs32         TimeRcvd;                       // In platform time units
        mDNSs32         TimeExpire;                     // In platform time units
@@ -2491,7 +2491,9 @@ extern void     mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake);
 extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now);
 extern mDNSs32  mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now);
 
-extern void     mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower);
+extern void     mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower);
+#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \
+       do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0)
 extern void     mDNSCoreReceiveRawPacket  (mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID);
 
 extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip);
index 8f41255..3db4538 100644 (file)
@@ -1680,6 +1680,7 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID
                // Schedule check to see if we can close this BPF_fd now
                if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2);
                // prog.bf_len = 0; This seems to panic the kernel
+               if (x->BPF_fd < 0) return;              // If we've already closed our BPF_fd, no need to generate an error message below
                }
 
        if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno));